Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow puppeteer to connect to a remote chrome instance #1567

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -747,11 +747,12 @@ The [storageState](https://playwright.dev/docs/api/class-browsercontext#browser-
<!-- omit from toc -->
### Setting Puppeteer And Playwright Option Flags

Backstop sets two defaults for both Puppeteer and Playwright:
Backstop sets three defaults for both Puppeteer and Playwright:

```json
ignoreHTTPSErrors: true,
headless: <!!!config.debugWindow>
headless: <!!!config.debugWindow>,
remote: false
```

You can add more settings (or override the defaults) with the `engineOptions` property. (properties are merged). This is where headless mode can also be set to 'new', until "new headless mode" is less hacky and more supported by Playwright.
Expand All @@ -772,6 +773,53 @@ More info here:
* [Puppeteer on github](https://github.com/GoogleChrome/puppeteer).
* [Playwright on github](https://github.com/microsoft/playwright).

<!-- omit from toc -->
#### Running Puppeteer on an already running Chrome instance
```json
"engineOptions": {
"remote": true,
"remoteOptions": {
// Note: This ignoreHTTPSErrors is separate from the one in engineOptions, and no default is set for it by BackstopJS.
"ignoreHTTPSErrors": false,
"browserWSEndpoint": "ws://myremotechrome:9222/devtools/browser/e6447a74-d83f-4f4e-a8e9-388b5216e0c2"
}
}
```

For more available remoteOptions, check BrowserOptions and ConnectOptions from puppeteer.

Notes for remote mode:
- Anything outside of remoteOptions is ignored.
- Chrome has to be launched separately, and BackstopJS has to be able to connect to it.
- Args, headless, etc. have to be set for chrome when launching chrome.

<!-- omit from toc -->
#### Example setup with docker

1. Start a dockerized chrome instance
1. E.g `docker run -d -p 9222:9222 --cap-add=SYS_ADMIN justinribeiro/chrome-headless`
2. Note the output, it's the docker container ID, e.g `8d203d9598f89028f98389f50a92aab33e18699e502f3813ab584ee654a8ca8a`
2. Get the devtools web socket
1. Use `docker logs <container name or id>` (list these with `docker ps`)
1. E.g `docker logs 8d203d9598f89028f98389f50a92aab33e18699e502f3813ab584ee654a8ca8a`
2. E.g `docker logs loving_dijkstra`
2. Copy the web socket URL from the logs
1. E.g, if the logs say `DevTools listening on ws://0.0.0.0:9222/devtools/browser/e6447a74-d83f-4f4e-a8e9-388b5216e0c2`, then the URl is `ws://0.0.0.0:9222/devtools/browser/e6447a74-d83f-4f4e-a8e9-388b5216e0c2`
3. Create a new or edit an existing BackstopJS config and add these:
```json
"engineOptions": {
"remote": true,
"remoteOptions": {
"browserWSEndpoint": "ws://localhost:9222/devtools/browser/e6447a74-d83f-4f4e-a8e9-388b5216e0c2"
}
},
```

If everything went OK, now you should be able to use backstop commands as usual.
Notes:
- Since in this example chrome is running in a docker container, it won't see any files on your system, unless they are mounted to it.
This means `url: path/to/index.html` scenarios won't work. But making these work is more of a docker topic, not a BackstopJS one.

<!-- omit from toc -->
### Using Docker For Testing Across Different Environments

Expand Down Expand Up @@ -1195,6 +1243,11 @@ For all engines there is also the `debug` setting. This enables verbose console
"debug": true
```

Note, this does not work when using
```json
"remote": true
```

<!-- omit from toc -->
### Issues with Chrome-Headless in Docker

Expand Down
67 changes: 55 additions & 12 deletions core/util/runPuppet.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,54 @@ function loggerAction (action, color, message, ...rest) {
console[action](chalk[color](message), ...rest);
}

/**
*
* Launch the browser, or connect to it.
*
* @param {Object} puppeteerArgs
* @returns {Promise<Puppeteer.Browser>}
*/
async function obtainBrowser (puppeteerArgs) {
if (puppeteerArgs.remote === true) {
return puppeteer.connect(puppeteerArgs.remoteOptions);
}

return puppeteer.launch(puppeteerArgs);
}

/**
* Close the browser, or disconnect from it.
*
* @param {Puppeteer.Browser} browser
* @param {Object} puppeteerArgs
* @returns {Promise<*>}
*/
async function releaseBrowser (browser, puppeteerArgs) {
if (puppeteerArgs.remote === true) {
return browser.disconnect();
}

return browser.close();
}

/**
* Build the puppeteer args object.
*
* @param {Object} config
* @returns {Object}
*/
function buildPuppeteerArgs (config) {
return Object.assign(
{},
{
ignoreHTTPSErrors: true,
headless: config.debugWindow ? false : config?.engineOptions?.headless || 'new',
remote: false
},
config.engineOptions
);
}

async function processScenarioView (scenario, variantOrScenarioLabelSafe, scenarioLabelSafe, viewport, config, logger) {
const { scenarioDefaults = {} } = config;

Expand Down Expand Up @@ -76,16 +124,9 @@ async function processScenarioView (scenario, variantOrScenarioLabelSafe, scenar
const VP_W = viewport.width || viewport.viewport.width;
const VP_H = viewport.height || viewport.viewport.height;

const puppeteerArgs = Object.assign(
{},
{
ignoreHTTPSErrors: true,
headless: config.debugWindow ? false : config?.engineOptions?.headless || 'new'
},
config.engineOptions
);
const puppeteerArgs = buildPuppeteerArgs(config);

const browser = await puppeteer.launch(puppeteerArgs);
const browser = await obtainBrowser(puppeteerArgs);
const page = await browser.newPage();

await page.setViewport({ width: VP_W, height: VP_H });
Expand Down Expand Up @@ -286,7 +327,7 @@ async function processScenarioView (scenario, variantOrScenarioLabelSafe, scenar
error = e;
}
} else {
await browser.close();
await releaseBrowser(browser, puppeteerArgs);
}

if (error) {
Expand Down Expand Up @@ -355,6 +396,8 @@ async function delegateSelectors (
captureJobs.push(function () { return captureScreenshot(page, browser, null, selectorMap, config, captureList, viewport, logger); });
}

const puppeteerArgs = buildPuppeteerArgs(config);

return new Promise(function (resolve, reject) {
let job = null;
const errors = [];
Expand All @@ -378,10 +421,10 @@ async function delegateSelectors (
next();
}).then(async () => {
logger.log('green', 'x Close Browser');
await browser.close();
await releaseBrowser(browser, puppeteerArgs);
}).catch(async (err) => {
logger.log('red', err);
await browser.close();
await releaseBrowser(browser, puppeteerArgs);
}).then(_ => compareConfig);
}

Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@babel/core": "^7.23.6",
"@babel/preset-env": "^7.23.6",
"@babel/preset-react": "^7.23.3",
"@types/puppeteer": "^7.0.4",
"assert": "^2.1.0",
"babel-loader": "^9.1.3",
"backstop-twentytwenty": "^1.1.0",
Expand Down