INTERNAL ERROR: missing context with id = 2
See original GitHub issueTLDR. I’m desperately hoping someone can help with this. I’ve been at it for 2 days and it’s driving me crazy. I’m working on creating a simplified repro case but I’m hoping someone might be able to point me in the right direction. It feels like puppeteer is possibly getting a bit confused about context when running in a highly parallel environment, but I’m probably wrong.
This problem occurs both locally running Docker and on CircleCI.
Steps to reproduce
FAIL __tests__/visual-regression/cta.test.js (79.643s)
● 99-visual-regression-cta-cta - mobile - ltr
INTERNAL ERROR: missing context with id = 2
at assert (node_modules/puppeteer/lib/helper.js:251:11)
at FrameManager.createJSHandle (node_modules/puppeteer/lib/FrameManager.js:223:5)
at ExecutionContext.evaluateHandle (node_modules/puppeteer/lib/ExecutionContext.js:89:17)
We an enormous visual regression test suite, a hundred or so prebuilt html files each containing a html component. We test each in 3 different viewports both right to left and left to right, so 6 visual regression tests per file using jest-image-snapshot. We have the tests broken down into a bunch of different files so jest is able to run them in parallel.
Tell us about your environment:
- Puppeteer version: 1.5.1
- Platform / OS version: circleci/node:8-browsers
- Node.js version: 8
- Jest: 22.4.3
- Jest-image-snapshot: 2.4.0
Sample
Example runner
require('../_lib/visual-regression').performVisualRegression('foo/bar/');
Simplified visual-regression.js.
function performVisualRegression(dir, viewports = viewportSpecs.map((v) => v.id)) {
const slugs = getSlugs(dir);
for (const slug of slugs) {
for (const viewport of viewportSpecs.filter((v) => viewports.includes(v.id))) {
for (const direction of ['ltr', 'rtl']) {
// test.concurrent
test(`${slug} - ${viewport.id} - ${direction}`, performTest(slug, viewport, customSnapshotsDir, direction), 30000);
}
}
}
}
function performTest(slug, viewport, customSnapshotsDir, direction = 'ltr') {
const url = `${global.BASE_URL}/patterns/${slug}/${slug}.rendered.html`;
return async() => {
let page;
try {
page = await browser.newPage();
await page.setCacheEnabled(false);
await page.setViewport(viewport);
const response = await page.goto(url, { timeout: 15000, waitUntil: ['load', 'networkidle2'] });
expect(await response.ok()).toBe(true);
await page.$eval('html', (div, dir) => div.setAttribute('dir', dir), direction);
const elm = await page.$('body > *:not(script):not(style):not(link):not(meta)');
expect(elm).not.toBeNull();
await page.waitFor(250);
const screenshot = await elm.screenshot();
expect(screenshot).toBeTruthy();
expect(screenshot instanceof Buffer).toBe(true);
let snapshotId = `${name}~~${viewport.id}~~${direction}`;
const config = Object.assign({}, defaultSnapshotConfig, { customSnapshotsDir, customSnapshotIdentifier: snapshotId });
expect(screenshot).toMatchImageSnapshot(config);
} finally {
if (page) {
await page.close();
}
}
};
}
Jest environment.
const NodeEnvironment = require('jest-environment-node');
const puppeteer = require('puppeteer');
class PuppeteerEnvironment extends NodeEnvironment {
constructor(config) {
super(config);
this.browser = puppeteer.launch({
headless: true,
dumpio: false,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
]
});
}
async setup() {
await super.setup();
const browser = await this.browser;
browser.on('targetcreated', async(target) => {
if (target.type() !== 'page') {
return;
}
const page = await target.page();
page.evaluateOnNewDocument(() => {
window.env = 'test';
});
page.on('domcontentloaded', async() => {
await page.evaluate(async() => {
window.env = 'test';
// Stop videos from mucking up visual regression tests
document.querySelectorAll('video').forEach((v) => {
v.removeAttribute('autoplay');
v.setAttribute('preload', 'auto');
v.pause();
v.currentTime = 0;
v.load();
});
});
});
});
this.global.BROWSER = browser;
}
async teardown() {
await super.teardown();
const b = await this.browser;
await b.close();
this.browser = null;
}
dispose() {
}
runScript(script) {
return super.runScript(script);
}
}
module.exports = PuppeteerEnvironment;
I should add we’ve also been having other extremely weird stuff going on. Left is the reference, middle is the diff, and right is what was produced during the test run.
Viewport not being honoured, completely the wrong size.
Completely the wrong component
Issue Analytics
- State:
- Created 5 years ago
- Comments:8 (6 by maintainers)
Top GitHub Comments
I can’t repro the error with the test suite you sent. But I think I know what is going on.
In your setup script, you run
page.evaluate
ondomcontentloaded
. Then when your test is done, you close the page. But you don’t await thepage.evaluate
before callingpage.close
. So the page closes before the evaluation finishes, and when the evaluation does finish, the page has already closed and we fail to return the result of the evaluation with this error.It is pretty much always wrong to not await code being run on events. You have another race in this code, which might no be manifesting. On
domcontentloaded
you setwindow.env = 'test';
. But you don’t wait for this eval to run before moving on with your test. An indefinite amount of time could pass between thedomcontentloaded
event firing in Chromium, thedomcontentloaded
event firing in node, and then running the code in Chromium again via your evaluate. In your setup function, it should look something like this: