test: remove unnecessary folio.extend calls before test migration (#6030)

This commit is contained in:
Dmitry Gozman 2021-04-01 13:18:04 -07:00 committed by GitHub
parent 8f71f5971b
commit 2290d8f81f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 225 additions and 295 deletions

View file

@ -18,18 +18,7 @@
import domain from 'domain';
import { folio } from './fixtures';
import type { ChromiumBrowser } from '..';
const fixtures = folio.extend<{}, { domain: any }>();
fixtures.domain.init(async ({ }, run) => {
const local = domain.create();
local.run(() => { });
let err;
local.on('error', e => err = e);
await run(null);
if (err)
throw err;
}, { scope: 'worker' });
const { it, expect } = fixtures.build();
const { it, expect } = folio;
it('should work', async ({browser}) => {
expect(!!browser['_connection']).toBeTruthy();
@ -168,7 +157,12 @@ it('should scope browser handles', async ({browserType, browserOptions}) => {
await expectScopeState(browserType, GOLDEN_PRECONDITION);
});
it('should work with the domain module', async ({ domain, browserType, browserOptions, server, isFirefox }) => {
it('should work with the domain module', async ({ browserType, browserOptions, server, isFirefox }) => {
const local = domain.create();
local.run(() => { });
let err;
local.on('error', e => err = e);
const browser = await browserType.launch(browserOptions);
const page = await browser.newPage();
@ -189,6 +183,9 @@ it('should work with the domain module', async ({ domain, browserType, browserOp
expect(message).toContain(': 400');
await browser.close();
if (err)
throw err;
});
async function expectScopeState(object, golden) {

View file

@ -18,20 +18,13 @@ import { folio } from '../fixtures';
import fs from 'fs';
import path from 'path';
import type { ChromiumBrowser } from '../..';
type TestState = {
outputTraceFile: string;
};
const fixtures = folio.extend<TestState>();
fixtures.outputTraceFile.init(async ({ testInfo }, run) => {
await run(testInfo.outputPath(path.join(`trace.json`)));
});
const { it, expect, describe } = fixtures.build();
const { it, expect, describe } = folio;
describe('tracing', (suite, { browserName }) => {
suite.skip(browserName !== 'chromium');
}, () => {
it('should output a trace', async ({browser, page, server, outputTraceFile}) => {
it('should output a trace', async ({browser, page, server, testInfo}) => {
const outputTraceFile = testInfo.outputPath(path.join(`trace.json`));
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputTraceFile});
await page.goto(server.PREFIX + '/grid.html');
await (browser as ChromiumBrowser).stopTracing();
@ -46,7 +39,8 @@ describe('tracing', (suite, { browserName }) => {
expect(fs.existsSync(filePath)).toBe(true);
});
it('should run with custom categories if provided', async ({browser, page, outputTraceFile}) => {
it('should run with custom categories if provided', async ({browser, page, testInfo}) => {
const outputTraceFile = testInfo.outputPath(path.join(`trace.json`));
await (browser as ChromiumBrowser).startTracing(page, {path: outputTraceFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
await (browser as ChromiumBrowser).stopTracing();
@ -54,7 +48,8 @@ describe('tracing', (suite, { browserName }) => {
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires');
});
it('should throw if tracing on two pages', async ({browser, page, outputTraceFile}) => {
it('should throw if tracing on two pages', async ({browser, page, testInfo}) => {
const outputTraceFile = testInfo.outputPath(path.join(`trace.json`));
await (browser as ChromiumBrowser).startTracing(page, {path: outputTraceFile});
const newPage = await browser.newPage();
let error = null;
@ -64,7 +59,8 @@ describe('tracing', (suite, { browserName }) => {
await (browser as ChromiumBrowser).stopTracing();
});
it('should return a buffer', async ({browser, page, server, outputTraceFile}) => {
it('should return a buffer', async ({browser, page, server, testInfo}) => {
const outputTraceFile = testInfo.outputPath(path.join(`trace.json`));
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputTraceFile});
await page.goto(server.PREFIX + '/grid.html');
const trace = await (browser as ChromiumBrowser).stopTracing();

View file

@ -17,10 +17,11 @@
import * as http from 'http';
import path from 'path';
import { ChildProcess, spawn } from 'child_process';
import { folio as baseFolio } from '../recorder.fixtures';
import { folio as baseFolio } from '../fixtures';
import type { BrowserType, Browser, Page } from '../..';
export { config } from 'folio';
import type { Source } from '../../src/server/supplements/recorder/recorderTypes';
import { recorderPageGetter } from '../utils';
type WorkerFixtures = {
browserType: BrowserType<Browser>;
@ -35,9 +36,9 @@ type TestFixtures = {
export const fixtures = baseFolio.extend<TestFixtures, WorkerFixtures>();
fixtures.recorder.init(async ({ page, recorderPageGetter }, runTest) => {
fixtures.recorder.init(async ({ page, context, toImpl }, runTest) => {
await (page.context() as any)._enableRecorder({ language: 'javascript', startRecording: true });
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await runTest(new Recorder(page, recorderPage));
});

View file

@ -15,109 +15,87 @@
*/
import { folio } from './fixtures';
import fs from 'fs';
import type { Browser, BrowserContext } from '..';
const { it, expect, describe, beforeEach } = folio;
type TestState = {
downloadsBrowser: Browser;
persistentDownloadsContext: BrowserContext;
};
const fixtures = folio.extend<TestState>();
fixtures.downloadsBrowser.init(async ({ server, browserType, browserOptions, testInfo }, test) => {
server.setRoute('/download', (req, res) => {
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
res.end(`Hello world`);
describe('downloads path', () => {
beforeEach(async ({server}) => {
server.setRoute('/download', (req, res) => {
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
res.end(`Hello world`);
});
});
const browser = await browserType.launch({
...browserOptions,
downloadsPath: testInfo.outputPath(''),
it('should keep downloadsPath folder', async ({browserType, browserOptions, testInfo, server}) => {
const downloadsBrowser = await browserType.launch({ ...browserOptions, downloadsPath: testInfo.outputPath('') });
const page = await downloadsBrowser.newPage();
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a')
]);
expect(download.url()).toBe(`${server.PREFIX}/download`);
expect(download.suggestedFilename()).toBe(`file.txt`);
await download.path().catch(e => void 0);
await page.close();
await downloadsBrowser.close();
expect(fs.existsSync(testInfo.outputPath(''))).toBeTruthy();
});
await test(browser);
await browser.close();
});
fixtures.persistentDownloadsContext.init(async ({ server, launchPersistent, testInfo, browserChannel }, test) => {
server.setRoute('/download', (req, res) => {
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
res.end(`Hello world`);
it('should delete downloads when context closes', async ({browserType, browserOptions, server, testInfo}) => {
const downloadsBrowser = await browserType.launch({ ...browserOptions, downloadsPath: testInfo.outputPath('') });
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a')
]);
const path = await download.path();
expect(fs.existsSync(path)).toBeTruthy();
await page.close();
expect(fs.existsSync(path)).toBeFalsy();
await downloadsBrowser.close();
});
const { context, page } = await launchPersistent(
{
downloadsPath: testInfo.outputPath(''),
acceptDownloads: true,
}
);
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
await test(context);
await context.close();
});
const { it, expect } = fixtures.build();
it('should report downloads in downloadsPath folder', async ({browserType, browserOptions, testInfo, server}) => {
const downloadsBrowser = await browserType.launch({ ...browserOptions, downloadsPath: testInfo.outputPath('') });
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a')
]);
const path = await download.path();
expect(path.startsWith(testInfo.outputPath(''))).toBeTruthy();
await page.close();
await downloadsBrowser.close();
});
it('should keep downloadsPath folder', async ({downloadsBrowser, testInfo, server}) => {
const page = await downloadsBrowser.newPage();
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a')
]);
expect(download.url()).toBe(`${server.PREFIX}/download`);
expect(download.suggestedFilename()).toBe(`file.txt`);
await download.path().catch(e => void 0);
await page.close();
await downloadsBrowser.close();
expect(fs.existsSync(testInfo.outputPath(''))).toBeTruthy();
});
it('should accept downloads in persistent context', async ({launchPersistent, testInfo, server}) => {
const { context, page } = await launchPersistent({ acceptDownloads: true, downloadsPath: testInfo.outputPath('') });
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a'),
]);
expect(download.url()).toBe(`${server.PREFIX}/download`);
expect(download.suggestedFilename()).toBe(`file.txt`);
const path = await download.path();
expect(path.startsWith(testInfo.outputPath(''))).toBeTruthy();
await context.close();
});
it('should delete downloads when context closes', async ({downloadsBrowser, server}) => {
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a')
]);
const path = await download.path();
expect(fs.existsSync(path)).toBeTruthy();
await page.close();
expect(fs.existsSync(path)).toBeFalsy();
});
it('should report downloads in downloadsPath folder', async ({downloadsBrowser, testInfo, server}) => {
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a')
]);
const path = await download.path();
expect(path.startsWith(testInfo.outputPath(''))).toBeTruthy();
await page.close();
});
it('should accept downloads in persistent context', async ({persistentDownloadsContext, testInfo, server}) => {
const page = persistentDownloadsContext.pages()[0];
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a'),
]);
expect(download.url()).toBe(`${server.PREFIX}/download`);
expect(download.suggestedFilename()).toBe(`file.txt`);
const path = await download.path();
expect(path.startsWith(testInfo.outputPath(''))).toBeTruthy();
});
it('should delete downloads when persistent context closes', async ({persistentDownloadsContext}) => {
const page = persistentDownloadsContext.pages()[0];
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a'),
]);
const path = await download.path();
expect(fs.existsSync(path)).toBeTruthy();
await persistentDownloadsContext.close();
expect(fs.existsSync(path)).toBeFalsy();
it('should delete downloads when persistent context closes', async ({launchPersistent, server, testInfo}) => {
const { context, page } = await launchPersistent({ acceptDownloads: true, downloadsPath: testInfo.outputPath('') });
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('a'),
]);
const path = await download.path();
expect(fs.existsSync(path)).toBeTruthy();
await context.close();
expect(fs.existsSync(path)).toBeFalsy();
});
});

View file

@ -15,65 +15,53 @@
* limitations under the License.
*/
import { folio as baseFolio } from './fixtures';
import { folio } from './fixtures';
import fs from 'fs';
import type * as har from '../src/server/supplements/har/har';
import type { BrowserContext, Page } from '../index';
import type { BrowserContext, BrowserContextOptions } from '../index';
import { TestInfo } from 'folio';
const { expect, it } = folio;
const builder = baseFolio.extend<{
pageWithHar: {
page: Page,
context: BrowserContext,
path: string,
log: () => Promise<har.Log>
}
}>();
builder.pageWithHar.init(async ({ contextFactory, testInfo }, run) => {
async function pageWithHar(contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>, testInfo: TestInfo) {
const harPath = testInfo.outputPath('test.har');
const context = await contextFactory({ recordHar: { path: harPath }, ignoreHTTPSErrors: true });
const page = await context.newPage();
await run({
path: harPath,
return {
page,
context,
log: async () => {
getLog: async () => {
await context.close();
return JSON.parse(fs.readFileSync(harPath).toString())['log'];
}
});
});
const { expect, it } = builder.build();
};
}
it('should throw without path', async ({ browser }) => {
const error = await browser.newContext({ recordHar: {} as any }).catch(e => e);
expect(error.message).toContain('recordHar.path: expected string, got undefined');
});
it('should have version and creator', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should have version and creator', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.EMPTY_PAGE);
const log = await pageWithHar.log();
const log = await getLog();
expect(log.version).toBe('1.2');
expect(log.creator.name).toBe('Playwright');
expect(log.creator.version).toBe(require('../package.json')['version']);
});
it('should have browser', async ({ browserName, browser, pageWithHar, server }) => {
const { page } = pageWithHar;
it('should have browser', async ({ browserName, browser, contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.EMPTY_PAGE);
const log = await pageWithHar.log();
const log = await getLog();
expect(log.browser.name.toLowerCase()).toBe(browserName);
expect(log.browser.version).toBe(browser.version());
});
it('should have pages', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should have pages', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto('data:text/html,<title>Hello</title>');
// For data: load comes before domcontentloaded...
await page.waitForLoadState('domcontentloaded');
const log = await pageWithHar.log();
const log = await getLog();
expect(log.pages.length).toBe(1);
const pageEntry = log.pages[0];
expect(pageEntry.id).toBe('page_0');
@ -97,10 +85,10 @@ it('should have pages in persistent context', async ({ launchPersistent, testInf
expect(pageEntry.title).toBe('Hello');
});
it('should include request', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should include request', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.EMPTY_PAGE);
const log = await pageWithHar.log();
const log = await getLog();
expect(log.entries.length).toBe(1);
const entry = log.entries[0];
expect(entry.pageref).toBe('page_0');
@ -111,10 +99,10 @@ it('should include request', async ({ pageWithHar, server }) => {
expect(entry.request.headers.find(h => h.name.toLowerCase() === 'user-agent')).toBeTruthy();
});
it('should include response', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should include response', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.EMPTY_PAGE);
const log = await pageWithHar.log();
const log = await getLog();
const entry = log.entries[0];
expect(entry.response.status).toBe(200);
expect(entry.response.statusText).toBe('OK');
@ -123,29 +111,29 @@ it('should include response', async ({ pageWithHar, server }) => {
expect(entry.response.headers.find(h => h.name.toLowerCase() === 'content-type').value).toContain('text/html');
});
it('should include redirectURL', async ({ pageWithHar, server }) => {
it('should include redirectURL', async ({ contextFactory, testInfo, server }) => {
server.setRedirect('/foo.html', '/empty.html');
const { page } = pageWithHar;
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.PREFIX + '/foo.html');
const log = await pageWithHar.log();
const log = await getLog();
expect(log.entries.length).toBe(2);
const entry = log.entries[0];
expect(entry.response.status).toBe(302);
expect(entry.response.redirectURL).toBe(server.EMPTY_PAGE);
});
it('should include query params', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should include query params', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.PREFIX + '/har.html?name=value');
const log = await pageWithHar.log();
const log = await getLog();
expect(log.entries[0].request.queryString).toEqual([{ name: 'name', value: 'value' }]);
});
it('should include postData', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should include postData', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => fetch('./post', { method: 'POST', body: 'Hello' }));
const log = await pageWithHar.log();
const log = await getLog();
expect(log.entries[1].request.postData).toEqual({
mimeType: 'text/plain;charset=UTF-8',
params: [],
@ -153,13 +141,13 @@ it('should include postData', async ({ pageWithHar, server }) => {
});
});
it('should include binary postData', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should include binary postData', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.EMPTY_PAGE);
await page.evaluate(async () => {
await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(16).keys())) });
});
const log = await pageWithHar.log();
const log = await getLog();
expect(log.entries[1].request.postData).toEqual({
mimeType: 'application/octet-stream',
params: [],
@ -167,12 +155,12 @@ it('should include binary postData', async ({ pageWithHar, server }) => {
});
});
it('should include form params', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should include form params', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.EMPTY_PAGE);
await page.setContent(`<form method='POST' action='/post'><input type='text' name='foo' value='bar'><input type='number' name='baz' value='123'><input type='submit'></form>`);
await page.click('input[type=submit]');
const log = await pageWithHar.log();
const log = await getLog();
expect(log.entries[1].request.postData).toEqual({
mimeType: 'application/x-www-form-urlencoded',
params: [
@ -183,8 +171,9 @@ it('should include form params', async ({ pageWithHar, server }) => {
});
});
it('should include cookies', async ({ pageWithHar, server }) => {
const { page, context } = pageWithHar;
it('should include cookies', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
const context = page.context();
await context.addCookies([
{ name: 'name1', value: '"value1"', domain: 'localhost', path: '/', httpOnly: true },
{ name: 'name2', value: 'val"ue2', domain: 'localhost', path: '/', sameSite: 'Lax' },
@ -192,7 +181,7 @@ it('should include cookies', async ({ pageWithHar, server }) => {
{ name: 'name4', value: 'val,ue4', domain: 'localhost', path: '/' },
]);
await page.goto(server.EMPTY_PAGE);
const log = await pageWithHar.log();
const log = await getLog();
expect(log.entries[0].request.cookies).toEqual([
{ name: 'name1', value: '"value1"' },
{ name: 'name2', value: 'val"ue2' },
@ -203,8 +192,8 @@ it('should include cookies', async ({ pageWithHar, server }) => {
it('should include set-cookies', (test, { browserName, platform }) => {
test.fail(browserName === 'webkit' && platform === 'darwin', 'Does not work yet');
}, async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
}, async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', [
'name1=value1; HttpOnly',
@ -214,15 +203,15 @@ it('should include set-cookies', (test, { browserName, platform }) => {
res.end();
});
await page.goto(server.EMPTY_PAGE);
const log = await pageWithHar.log();
const log = await getLog();
const cookies = log.entries[0].response.cookies;
expect(cookies[0]).toEqual({ name: 'name1', value: 'value1', httpOnly: true });
expect(cookies[1]).toEqual({ name: 'name2', value: '"value2"' });
expect(new Date(cookies[2].expires).valueOf()).toBeGreaterThan(Date.now());
});
it('should include set-cookies with comma', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should include set-cookies with comma', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', [
'name1=val,ue1',
@ -230,13 +219,13 @@ it('should include set-cookies with comma', async ({ pageWithHar, server }) => {
res.end();
});
await page.goto(server.EMPTY_PAGE);
const log = await pageWithHar.log();
const log = await getLog();
const cookies = log.entries[0].response.cookies;
expect(cookies[0]).toEqual({ name: 'name1', value: 'val,ue1' });
});
it('should include secure set-cookies', async ({ pageWithHar, httpsServer }) => {
const { page } = pageWithHar;
it('should include secure set-cookies', async ({ contextFactory, testInfo, httpsServer }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
httpsServer.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', [
'name1=value1; Secure',
@ -244,15 +233,15 @@ it('should include secure set-cookies', async ({ pageWithHar, httpsServer }) =>
res.end();
});
await page.goto(httpsServer.EMPTY_PAGE);
const log = await pageWithHar.log();
const log = await getLog();
const cookies = log.entries[0].response.cookies;
expect(cookies[0]).toEqual({ name: 'name1', value: 'value1', secure: true });
});
it('should include content', async ({ pageWithHar, server }) => {
const { page } = pageWithHar;
it('should include content', async ({ contextFactory, testInfo, server }) => {
const { page, getLog } = await pageWithHar(contextFactory, testInfo);
await page.goto(server.PREFIX + '/har.html');
const log = await pageWithHar.log();
const log = await getLog();
const content1 = log.entries[0].response.content;
expect(content1.encoding).toBe('base64');

View file

@ -16,26 +16,27 @@
import { expect } from 'folio';
import { Page } from '..';
import { folio } from './recorder.fixtures';
import { folio } from './fixtures';
import { recorderPageGetter } from './utils';
const { afterEach, it, describe } = folio;
describe('pause', (suite, { mode }) => {
suite.skip(mode !== 'default');
}, () => {
afterEach(async ({ recorderPageGetter }) => {
afterEach(async ({ context, toImpl }) => {
try {
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
recorderPage.click('[title=Resume]').catch(() => {});
} catch (e) {
// Some tests close context.
}
});
it('should pause and resume the script', async ({ page, recorderPageGetter }) => {
it('should pause and resume the script', async ({ page, context, toImpl }) => {
const scriptPromise = (async () => {
await page.pause();
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title=Resume]');
await scriptPromise;
});
@ -52,33 +53,33 @@ describe('pause', (suite, { mode }) => {
await scriptPromise;
});
it('should pause after a navigation', async ({page, server, recorderPageGetter}) => {
it('should pause after a navigation', async ({page, server, context, toImpl}) => {
const scriptPromise = (async () => {
await page.goto(server.EMPTY_PAGE);
await page.pause();
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title=Resume]');
await scriptPromise;
});
it('should show source', async ({page, recorderPageGetter}) => {
it('should show source', async ({page, context, toImpl}) => {
const scriptPromise = (async () => {
await page.pause();
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
const source = await recorderPage.textContent('.source-line-paused .source-code');
expect(source).toContain('page.pause()');
await recorderPage.click('[title=Resume]');
await scriptPromise;
});
it('should pause on next pause', async ({page, recorderPageGetter}) => {
it('should pause on next pause', async ({page, context, toImpl}) => {
const scriptPromise = (async () => {
await page.pause(); // 1
await page.pause(); // 2
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
const source = await recorderPage.textContent('.source-line-paused');
expect(source).toContain('page.pause(); // 1');
await recorderPage.click('[title=Resume]');
@ -87,13 +88,13 @@ describe('pause', (suite, { mode }) => {
await scriptPromise;
});
it('should step', async ({page, recorderPageGetter}) => {
it('should step', async ({page, context, toImpl}) => {
await page.setContent('<button>Submit</button>');
const scriptPromise = (async () => {
await page.pause();
await page.click('button');
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
const source = await recorderPage.textContent('.source-line-paused');
expect(source).toContain('page.pause();');
@ -104,13 +105,13 @@ describe('pause', (suite, { mode }) => {
await scriptPromise;
});
it('should highlight pointer', async ({page, recorderPageGetter}) => {
it('should highlight pointer', async ({page, context, toImpl}) => {
await page.setContent('<button>Submit</button>');
const scriptPromise = (async () => {
await page.pause();
await page.click('button');
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Step over"]');
const point = await page.waitForSelector('x-pw-action-point');
@ -130,28 +131,28 @@ describe('pause', (suite, { mode }) => {
await scriptPromise;
});
it('should skip input when resuming', async ({page, recorderPageGetter}) => {
it('should skip input when resuming', async ({page, context, toImpl}) => {
await page.setContent('<button>Submit</button>');
const scriptPromise = (async () => {
await page.pause();
await page.click('button');
await page.pause(); // 2
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
await recorderPage.click('[title=Resume]');
await scriptPromise;
});
it('should populate log', async ({page, recorderPageGetter}) => {
it('should populate log', async ({page, context, toImpl}) => {
await page.setContent('<button>Submit</button>');
const scriptPromise = (async () => {
await page.pause();
await page.click('button');
await page.pause(); // 2
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
expect(await sanitizeLog(recorderPage)).toEqual([
@ -163,7 +164,7 @@ describe('pause', (suite, { mode }) => {
await scriptPromise;
});
it('should highlight waitForEvent', async ({page, recorderPageGetter}) => {
it('should highlight waitForEvent', async ({page, context, toImpl}) => {
await page.setContent('<button onclick="console.log(1)">Submit</button>');
const scriptPromise = (async () => {
await page.pause();
@ -172,7 +173,7 @@ describe('pause', (suite, { mode }) => {
page.click('button'),
]);
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Step over"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.click")');
await recorderPage.waitForSelector('.source-line-running:has-text("page.waitForEvent")');
@ -180,7 +181,7 @@ describe('pause', (suite, { mode }) => {
await scriptPromise;
});
it('should populate log with waitForEvent', async ({page, recorderPageGetter}) => {
it('should populate log with waitForEvent', async ({page, context, toImpl}) => {
await page.setContent('<button onclick="console.log(1)">Submit</button>');
const scriptPromise = (async () => {
await page.pause();
@ -190,7 +191,7 @@ describe('pause', (suite, { mode }) => {
]);
await page.pause(); // 2
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
expect(await sanitizeLog(recorderPage)).toEqual([
@ -203,13 +204,13 @@ describe('pause', (suite, { mode }) => {
await scriptPromise;
});
it('should populate log with error', async ({page, recorderPageGetter}) => {
it('should populate log with error', async ({page, context, toImpl}) => {
await page.setContent('<button onclick="console.log(1)">Submit</button>');
const scriptPromise = (async () => {
await page.pause();
await page.isChecked('button');
})().catch(e => e);
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-error');
expect(await sanitizeLog(recorderPage)).toEqual([
@ -223,7 +224,7 @@ describe('pause', (suite, { mode }) => {
expect(error.message).toContain('Not a checkbox or radio button');
});
it('should populate log with error in waitForEvent', async ({page, recorderPageGetter}) => {
it('should populate log with error in waitForEvent', async ({page, context, toImpl}) => {
await page.setContent('<button>Submit</button>');
const scriptPromise = (async () => {
await page.pause();
@ -232,7 +233,7 @@ describe('pause', (suite, { mode }) => {
page.click('button'),
]);
})().catch(() => {});
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Step over"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.click")');
await recorderPage.waitForSelector('.source-line-error:has-text("page.waitForEvent")');
@ -247,36 +248,36 @@ describe('pause', (suite, { mode }) => {
await scriptPromise;
});
it('should pause on page close', async ({ page, recorderPageGetter }) => {
it('should pause on page close', async ({ page, context, toImpl }) => {
const scriptPromise = (async () => {
await page.pause();
await page.close();
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Step over"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.close();")');
await recorderPage.click('[title=Resume]');
await scriptPromise;
});
it('should pause on context close', async ({ page, recorderPageGetter }) => {
it('should pause on context close', async ({ page, context, toImpl }) => {
const scriptPromise = (async () => {
await page.pause();
await page.context().close();
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
await recorderPage.click('[title="Step over"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.context().close();")');
await recorderPage.click('[title=Resume]');
await scriptPromise;
});
it('should highlight on explore', async ({ page, recorderPageGetter }) => {
it('should highlight on explore', async ({ page, context, toImpl }) => {
await page.setContent('<button>Submit</button>');
const scriptPromise = (async () => {
await page.pause();
})();
const recorderPage = await recorderPageGetter();
const recorderPage = await recorderPageGetter(context, toImpl);
const [element] = await Promise.all([
page.waitForSelector('x-pw-highlight:visible'),
recorderPage.fill('input[placeholder="Playwright Selector"]', 'text=Submit'),

View file

@ -1,36 +0,0 @@
/**
* Copyright Microsoft Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { folio as baseFolio } from './fixtures';
import { Page } from '..';
import { chromium } from '../index';
const fixtures = baseFolio.extend<{
recorderPageGetter: () => Promise<Page>,
}>();
fixtures.recorderPageGetter.init(async ({context, toImpl}, runTest) => {
await runTest(async () => {
while (!toImpl(context).recorderAppForTest)
await new Promise(f => setTimeout(f, 100));
const wsEndpoint = toImpl(context).recorderAppForTest.wsEndpoint;
const browser = await chromium.connectOverCDP({ wsEndpoint });
const c = browser.contexts()[0];
return c.pages()[0] || await c.waitForEvent('page');
});
});
export const folio = fixtures.build();

View file

@ -14,46 +14,40 @@
* limitations under the License.
*/
import { folio as baseFolio } from './fixtures';
import { folio } from './fixtures';
import { InMemorySnapshotter } from '../lib/server/snapshot/inMemorySnapshotter';
import { HttpServer } from '../lib/utils/httpServer';
import { SnapshotServer } from '../lib/server/snapshot/snapshotServer';
type TestFixtures = {
snapshotter: any;
snapshotPort: number;
};
export const fixtures = baseFolio.extend<TestFixtures>();
fixtures.snapshotter.init(async ({ context, toImpl }, runTest) => {
const snapshotter = new InMemorySnapshotter(toImpl(context));
await snapshotter.initialize();
await runTest(snapshotter);
await snapshotter.dispose();
});
fixtures.snapshotPort.init(async ({ snapshotter, testWorkerIndex }, runTest) => {
const httpServer = new HttpServer();
new SnapshotServer(httpServer, snapshotter);
const port = 9700 + testWorkerIndex;
httpServer.start(port);
await runTest(port);
httpServer.stop();
});
const { it, describe, expect } = fixtures.build();
const { it, describe, expect, beforeEach, afterEach } = folio;
describe('snapshots', (suite, { mode }) => {
suite.skip(mode !== 'default');
}, () => {
let snapshotter: any;
let httpServer: any;
let snapshotPort: number;
it('should collect snapshot', async ({ snapshotter, page, toImpl }) => {
beforeEach(async ({ context, toImpl, testWorkerIndex }) => {
snapshotter = new InMemorySnapshotter(toImpl(context));
await snapshotter.initialize();
httpServer = new HttpServer();
new SnapshotServer(httpServer, snapshotter);
snapshotPort = 9700 + testWorkerIndex;
httpServer.start(snapshotPort);
});
afterEach(async () => {
await snapshotter.dispose();
httpServer.stop();
});
it('should collect snapshot', async ({ page, toImpl }) => {
await page.setContent('<button>Hello</button>');
const snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot');
expect(distillSnapshot(snapshot)).toBe('<BUTTON>Hello</BUTTON>');
});
it('should capture resources', async ({ snapshotter, page, toImpl, server }) => {
it('should capture resources', async ({ page, toImpl, server }) => {
await page.goto(server.EMPTY_PAGE);
await page.route('**/style.css', route => {
route.fulfill({ body: 'button { color: red; }', }).catch(() => {});
@ -65,7 +59,7 @@ describe('snapshots', (suite, { mode }) => {
expect(resources[cssHref]).toBeTruthy();
});
it('should collect multiple', async ({ snapshotter, page, toImpl }) => {
it('should collect multiple', async ({ page, toImpl }) => {
await page.setContent('<button>Hello</button>');
const snapshots = [];
snapshotter.on('snapshot', snapshot => snapshots.push(snapshot));
@ -74,7 +68,7 @@ describe('snapshots', (suite, { mode }) => {
expect(snapshots.length).toBe(2);
});
it('should only collect on change', async ({ snapshotter, page }) => {
it('should only collect on change', async ({ page }) => {
await page.setContent('<button>Hello</button>');
const snapshots = [];
snapshotter.on('snapshot', snapshot => snapshots.push(snapshot));
@ -89,7 +83,7 @@ describe('snapshots', (suite, { mode }) => {
expect(snapshots.length).toBe(2);
});
it('should respect inline CSSOM change', async ({ snapshotter, page }) => {
it('should respect inline CSSOM change', async ({ page }) => {
await page.setContent('<style>button { color: red; }</style><button>Hello</button>');
const snapshots = [];
snapshotter.on('snapshot', snapshot => snapshots.push(snapshot));
@ -108,7 +102,7 @@ describe('snapshots', (suite, { mode }) => {
expect(distillSnapshot(snapshots[1])).toBe('<style>button { color: blue; }</style><BUTTON>Hello</BUTTON>');
});
it('should respect subresource CSSOM change', async ({ snapshotter, page, server }) => {
it('should respect subresource CSSOM change', async ({ page, server }) => {
await page.goto(server.EMPTY_PAGE);
await page.route('**/style.css', route => {
route.fulfill({ body: 'button { color: red; }', }).catch(() => {});
@ -137,7 +131,7 @@ describe('snapshots', (suite, { mode }) => {
it('should capture iframe', (test, { browserName }) => {
test.skip(browserName === 'firefox');
}, async ({ contextFactory, snapshotter, page, server, snapshotPort, toImpl }) => {
}, async ({ contextFactory, page, server, toImpl }) => {
await page.route('**/empty.html', route => {
route.fulfill({
body: '<iframe src="iframe.html"></iframe>',
@ -176,7 +170,7 @@ describe('snapshots', (suite, { mode }) => {
expect(await button.textContent()).toBe('Hello iframe');
});
it('should capture snapshot target', async ({ snapshotter, page, toImpl }) => {
it('should capture snapshot target', async ({ page, toImpl }) => {
await page.setContent('<button>Hello</button><button>World</button>');
{
const handle = await page.$('text=Hello');
@ -190,7 +184,7 @@ describe('snapshots', (suite, { mode }) => {
}
});
it('should collect on attribute change', async ({ snapshotter, page, toImpl }) => {
it('should collect on attribute change', async ({ page, toImpl }) => {
await page.setContent('<button>Hello</button>');
{
const snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot');

View file

@ -15,7 +15,8 @@
*/
import { expect } from './fixtures';
import type { Frame, Page } from '../index';
import type { Frame, Page, BrowserContext } from '../index';
import { chromium } from '../index';
export async function attachFrame(page: Page, frameId: string, url: string): Promise<Frame> {
const handle = await page.evaluateHandle(async ({ frameId, url }) => {
@ -58,3 +59,12 @@ export function expectedSSLError(browserName: string): string {
}
return expectedSSLError;
}
export async function recorderPageGetter(context: BrowserContext, toImpl: (x: any) => any) {
while (!toImpl(context).recorderAppForTest)
await new Promise(f => setTimeout(f, 100));
const wsEndpoint = toImpl(context).recorderAppForTest.wsEndpoint;
const browser = await chromium.connectOverCDP({ wsEndpoint });
const c = browser.contexts()[0];
return c.pages()[0] || await c.waitForEvent('page');
}