chore: simplify context/page reuse in ct (#14565)

This commit is contained in:
Dmitry Gozman 2022-06-02 12:16:07 -07:00 committed by GitHub
parent 8801f79742
commit 3a3aa023ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 44 deletions

View file

@ -21,51 +21,47 @@ let boundCallbacksForMount: Function[] = [];
export const fixtures: Fixtures< export const fixtures: Fixtures<
PlaywrightTestArgs & PlaywrightTestOptions & { mount: (component: any, options: any) => Promise<Locator> }, PlaywrightTestArgs & PlaywrightTestOptions & { mount: (component: any, options: any) => Promise<Locator> },
PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _ctWorker: { page: Page | undefined, context: BrowserContext | undefined, hash: string, isolateTests: boolean } }, PlaywrightWorkerArgs & PlaywrightWorkerOptions & { _ctWorker: { context: BrowserContext | undefined, hash: string } },
{ _contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext> }> = { { _contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext> }> = {
_ctWorker: [{ page: undefined, context: undefined, hash: '', isolateTests: false }, { scope: 'worker' }], _ctWorker: [{ context: undefined, hash: '' }, { scope: 'worker' }],
context: async ({ _contextFactory, playwright, browser, _ctWorker, video, trace, viewport }, use, testInfo) => {
_ctWorker.isolateTests = shouldCaptureVideo(normalizeVideoMode(video), testInfo) || shouldCaptureTrace(normalizeTraceMode(trace), testInfo);
if (_ctWorker.isolateTests) {
await use(await _contextFactory());
return;
}
context: async ({ playwright, browser, _ctWorker, _contextFactory, video, trace }, use, testInfo) => {
const isolateTests = shouldCaptureVideo(normalizeVideoMode(video), testInfo) || shouldCaptureTrace(normalizeTraceMode(trace), testInfo);
const defaultContextOptions = (playwright.chromium as any)._defaultContextOptions as BrowserContextOptions; const defaultContextOptions = (playwright.chromium as any)._defaultContextOptions as BrowserContextOptions;
const hash = contextHash(defaultContextOptions); const hash = contextHash(defaultContextOptions);
if (!_ctWorker.page || _ctWorker.hash !== hash) { if (!_ctWorker.context || _ctWorker.hash !== hash || isolateTests) {
if (_ctWorker.context) if (_ctWorker.context)
await _ctWorker.context.close(); await _ctWorker.context.close();
// Context factory sets up video so we want to use that for isolated contexts.
const context = await browser.newContext(); // However, it closes the context after the test, so we don't want to use it
const page = await createPage(context); // for shared contexts.
_ctWorker.context = context; _ctWorker.context = isolateTests ? await _contextFactory() : await browser.newContext();
_ctWorker.page = page;
_ctWorker.hash = hash; _ctWorker.hash = hash;
await use(page.context()); await _ctWorker.context.addInitScript('navigator.serviceWorker.register = () => {}');
return; await _ctWorker.context.exposeFunction('__pw_dispatch', (ordinal: number, args: any[]) => {
boundCallbacksForMount[ordinal](...args);
});
} else { } else {
const page = _ctWorker.page; await (_ctWorker.context as any)._resetForReuse();
await (page as any)._wrapApiCall(async () => {
await (page as any)._resetForReuse();
await (page.context() as any)._resetForReuse();
await page.goto('about:blank');
await page.setViewportSize(viewport || { width: 1280, height: 800 });
await page.goto(process.env.PLAYWRIGHT_VITE_COMPONENTS_BASE_URL!);
}, true);
await use(page.context());
} }
await use(_ctWorker.context);
}, },
page: async ({ context, _ctWorker }, use) => { page: async ({ context, viewport }, use) => {
if (_ctWorker.isolateTests) { let page = context.pages()[0];
await use(await createPage(context)); await (context as any)._wrapApiCall(async () => {
return; if (!page) {
} page = await context.newPage();
await use(_ctWorker.page!); } else {
await (page as any)._resetForReuse();
await page.goto('about:blank');
await page.setViewportSize(viewport || { width: 1280, height: 800 });
}
await page.goto(process.env.PLAYWRIGHT_VITE_COMPONENTS_BASE_URL!);
}, true);
await use(page);
}, },
mount: async ({ page }, use) => { mount: async ({ page }, use) => {
@ -148,15 +144,3 @@ function contextHash(context: BrowserContextOptions): string {
}; };
return JSON.stringify(hash); return JSON.stringify(hash);
} }
function createPage(context: BrowserContext): Promise<Page> {
return (context as any)._wrapApiCall(async () => {
const page = await context.newPage();
await page.addInitScript('navigator.serviceWorker.register = () => {}');
await page.exposeFunction('__pw_dispatch', (ordinal: number, args: any[]) => {
boundCallbacksForMount[ordinal](...args);
});
await page.goto(process.env.PLAYWRIGHT_VITE_COMPONENTS_BASE_URL!);
return page;
}, true);
}

View file

@ -111,3 +111,47 @@ test('should not reuse context with trace', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
expect(result.passed).toBe(2); expect(result.passed).toBe(2);
}); });
test('should work with manually closed pages', async ({ runInlineTest }) => {
const result = await runInlineTest({
'playwright/index.html': `<script type="module" src="/playwright/index.ts"></script>`,
'playwright/index.ts': `
//@no-header
`,
'src/button.test.tsx': `
//@no-header
import { test, expect } from '@playwright/experimental-ct-react';
test('closes page', async ({ mount, page }) => {
let hadEvent = false;
const component = await mount(<button onClick={e => hadEvent = true}>Submit</button>);
await expect(component).toHaveText('Submit');
await component.click();
expect(hadEvent).toBe(true);
await page.close();
});
test('creates a new page', async ({ mount, page, context }) => {
let hadEvent = false;
const component = await mount(<button onClick={e => hadEvent = true}>Submit</button>);
await expect(component).toHaveText('Submit');
await component.click();
expect(hadEvent).toBe(true);
await page.close();
await context.newPage();
});
test('still works', async ({ mount }) => {
let hadEvent = false;
const component = await mount(<button onClick={e => hadEvent = true}>Submit</button>);
await expect(component).toHaveText('Submit');
await component.click();
expect(hadEvent).toBe(true);
});
`,
}, { workers: 1 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(3);
});