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<
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> }> = {
_ctWorker: [{ page: undefined, context: undefined, hash: '', isolateTests: false }, { 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;
}
_ctWorker: [{ context: undefined, hash: '' }, { scope: 'worker' }],
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 hash = contextHash(defaultContextOptions);
if (!_ctWorker.page || _ctWorker.hash !== hash) {
if (!_ctWorker.context || _ctWorker.hash !== hash || isolateTests) {
if (_ctWorker.context)
await _ctWorker.context.close();
const context = await browser.newContext();
const page = await createPage(context);
_ctWorker.context = context;
_ctWorker.page = page;
// Context factory sets up video so we want to use that for isolated contexts.
// However, it closes the context after the test, so we don't want to use it
// for shared contexts.
_ctWorker.context = isolateTests ? await _contextFactory() : await browser.newContext();
_ctWorker.hash = hash;
await use(page.context());
return;
await _ctWorker.context.addInitScript('navigator.serviceWorker.register = () => {}');
await _ctWorker.context.exposeFunction('__pw_dispatch', (ordinal: number, args: any[]) => {
boundCallbacksForMount[ordinal](...args);
});
} else {
const page = _ctWorker.page;
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 (_ctWorker.context as any)._resetForReuse();
}
await use(_ctWorker.context);
},
page: async ({ context, _ctWorker }, use) => {
if (_ctWorker.isolateTests) {
await use(await createPage(context));
return;
}
await use(_ctWorker.page!);
page: async ({ context, viewport }, use) => {
let page = context.pages()[0];
await (context as any)._wrapApiCall(async () => {
if (!page) {
page = await context.newPage();
} 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) => {
@ -148,15 +144,3 @@ function contextHash(context: BrowserContextOptions): string {
};
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.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);
});