Skip to content

Commit 273122b

Browse files
authored
chore: align internal test harness w/ @playwright/test (microsoft#9796)
1 parent 7927920 commit 273122b

18 files changed

+134
-97
lines changed

Diff for: packages/playwright-core/src/client/tracing.ts

+13-8
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,14 @@ export class Tracing implements api.Tracing {
9999

100100
// Add sources.
101101
if (sources) {
102-
for (const source of sources)
103-
zipFile.addFile(source, 'resources/src@' + calculateSha1(source) + '.txt');
102+
for (const source of sources) {
103+
try {
104+
if (fs.statSync(source).isFile())
105+
zipFile.addFile(source, 'resources/src@' + calculateSha1(source) + '.txt');
106+
} catch (e) {
107+
}
108+
}
104109
}
105-
106110
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
107111
if (skipCompress) {
108112
// Local scenario, compress the entries.
@@ -120,7 +124,7 @@ export class Tracing implements api.Tracing {
120124
await artifact.saveAs(tmpPath);
121125
await artifact.delete();
122126

123-
yauzl.open(filePath!, (err, inZipFile) => {
127+
yauzl.open(tmpPath!, (err, inZipFile) => {
124128
if (err) {
125129
promise.reject(err);
126130
return;
@@ -135,10 +139,11 @@ export class Tracing implements api.Tracing {
135139
}
136140
zipFile.addReadStream(readStream!, entry.fileName);
137141
if (--pendingEntries === 0) {
138-
zipFile.end();
139-
zipFile.outputStream.pipe(fs.createWriteStream(filePath)).on('close', () => {
140-
fs.promises.unlink(tmpPath).then(() => {
141-
promise.resolve();
142+
zipFile.end(undefined, () => {
143+
zipFile.outputStream.pipe(fs.createWriteStream(filePath)).on('close', () => {
144+
fs.promises.unlink(tmpPath).then(() => {
145+
promise.resolve();
146+
});
142147
});
143148
});
144149
}

Diff for: packages/playwright-core/src/utils/stackTrace.ts

+6
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const CLIENT_LIB = path.join(CORE_DIR, 'lib', 'client');
3535
const CLIENT_SRC = path.join(CORE_DIR, 'src', 'client');
3636
const TEST_DIR_SRC = path.resolve(CORE_DIR, '..', 'playwright-test');
3737
const TEST_DIR_LIB = path.resolve(CORE_DIR, '..', '@playwright', 'test');
38+
const WS_LIB = path.relative(process.cwd(), path.dirname(require.resolve('ws')));
3839

3940
export type ParsedStackTrace = {
4041
allFrames: StackFrame[];
@@ -66,6 +67,11 @@ export function captureStackTrace(): ParsedStackTrace {
6667
// EventEmitter.emit has 'events.js' file.
6768
if (frame.file === 'events.js' && frame.function?.endsWith('.emit'))
6869
return null;
70+
// Node 12
71+
if (frame.file === '_stream_readable.js' || frame.file === '_stream_writable.js')
72+
return null;
73+
if (frame.file.startsWith(WS_LIB))
74+
return null;
6975
const fileName = path.resolve(process.cwd(), frame.file);
7076
if (isTesting && fileName.includes(path.join('playwright', 'tests', 'config', 'coverage.js')))
7177
return null;

Diff for: packages/playwright-test/src/index.ts

+54-26
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
3131
};
3232
type WorkerAndFileFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
3333
_browserType: BrowserType;
34+
_browserOptions: LaunchOptions;
3435
_artifactsDir: () => string,
3536
_reuseBrowserContext: ReuseBrowserContextStorage,
3637
};
@@ -150,31 +151,9 @@ export const test = _baseTest.extend<TestFixtures, WorkerAndFileFixtures>({
150151
await removeFolders([dir]);
151152
}, { scope: 'worker' }],
152153

153-
_browserType: [async ({ playwright, browserName, headless, channel, launchOptions }, use) => {
154-
if (!['chromium', 'firefox', 'webkit'].includes(browserName))
155-
throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`);
156-
const browserType = playwright[browserName];
157-
158-
const options: LaunchOptions = {
159-
handleSIGINT: false,
160-
timeout: 0,
161-
...launchOptions,
162-
};
163-
if (headless !== undefined)
164-
options.headless = headless;
165-
if (channel !== undefined)
166-
options.channel = channel;
167-
168-
(browserType as any)._defaultLaunchOptions = options;
169-
await use(browserType);
170-
(browserType as any)._defaultLaunchOptions = undefined;
171-
}, { scope: 'worker' }],
172-
173-
browser: [ async ({ _browserType }, use) => {
174-
const browser = await _browserType.launch();
175-
await use(browser);
176-
await browser.close();
177-
}, { scope: 'worker' } ],
154+
_browserOptions: [browserOptionsWorkerFixture, { scope: 'worker' }],
155+
_browserType: [browserTypeWorkerFixture, { scope: 'worker' }],
156+
browser: [browserWorkerFixture, { scope: 'worker' } ],
178157

179158
acceptDownloads: undefined,
180159
bypassCSP: undefined,
@@ -480,7 +459,54 @@ export const test = _baseTest.extend<TestFixtures, WorkerAndFileFixtures>({
480459

481460
});
482461

483-
export default test;
462+
export async function browserOptionsWorkerFixture(
463+
{
464+
headless,
465+
channel,
466+
launchOptions
467+
}: {
468+
headless: boolean | undefined,
469+
channel: string | undefined,
470+
launchOptions: LaunchOptions
471+
}, use: (options: LaunchOptions) => Promise<void>) {
472+
const options: LaunchOptions = {
473+
handleSIGINT: false,
474+
timeout: 0,
475+
...launchOptions,
476+
};
477+
if (headless !== undefined)
478+
options.headless = headless;
479+
if (channel !== undefined)
480+
options.channel = channel;
481+
await use(options);
482+
}
483+
484+
export async function browserTypeWorkerFixture(
485+
{
486+
playwright,
487+
browserName,
488+
_browserOptions
489+
}: {
490+
playwright: any,
491+
browserName: string,
492+
_browserOptions: LaunchOptions
493+
}, use: (browserType: BrowserType) => Promise<void>) {
494+
if (!['chromium', 'firefox', 'webkit'].includes(browserName))
495+
throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`);
496+
const browserType = playwright[browserName];
497+
(browserType as any)._defaultLaunchOptions = _browserOptions;
498+
await use(browserType);
499+
(browserType as any)._defaultLaunchOptions = undefined;
500+
}
501+
502+
export async function browserWorkerFixture(
503+
{ _browserType }: { _browserType: BrowserType },
504+
use: (browser: Browser) => Promise<void>) {
505+
const browser = await _browserType.launch();
506+
await use(browser);
507+
await browser.close();
508+
}
509+
484510

485511
function formatPendingCalls(calls: ParsedStackTrace[]) {
486512
if (!calls.length)
@@ -517,3 +543,5 @@ type ParsedStackTrace = {
517543
};
518544

519545
const kTracingStarted = Symbol('kTracingStarted');
546+
547+
export default test;

Diff for: tests/browsercontext-proxy.spec.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,15 @@
1616

1717
import { browserTest as it, expect } from './config/browserTest';
1818

19-
it.use({ proxy: { server: 'per-context' } });
19+
it.use({
20+
launchOptions: async ({ launchOptions }, use) => {
21+
await use({
22+
...launchOptions,
23+
proxy: { server: 'per-context' }
24+
});
25+
}
26+
});
27+
2028

2129
it.beforeEach(({ server }) => {
2230
server.setRoute('/target.html', async (req, res) => {

Diff for: tests/channels.spec.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import { playwrightTest as it, expect } from './config/browserTest';
2020

2121
// Use something worker-scoped (e.g. launch args) to force a new worker for this file.
2222
// Otherwise, a browser launched for other tests in this worker will affect the expectations.
23-
it.use({ args: [] });
23+
it.use({
24+
launchOptions: async ({ launchOptions }, use) => {
25+
await use({ ...launchOptions, args: [] });
26+
}
27+
});
2428

2529
it('should scope context handles', async ({ browserType, browserOptions, server }) => {
2630
const browser = await browserType.launch(browserOptions);

Diff for: tests/chromium/js-coverage.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { contextTest as it, expect } from '../config/browserTest';
1818

19-
it.skip(({ trace }) => !!trace);
19+
it.skip(({ trace }) => trace === 'on');
2020

2121
it('should work', async function({ page, server }) {
2222
await page.coverage.startJSCoverage();

Diff for: tests/chromium/oopif.spec.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616

1717
import { contextTest as it, expect } from '../config/browserTest';
1818

19-
it.use({ args: ['--site-per-process'] });
19+
it.use({
20+
launchOptions: async ({ launchOptions }, use) => {
21+
await use({ ...launchOptions, args: ['--site-per-process'] });
22+
}
23+
});
2024

2125
it('should report oopif frames', async function({ page, browser, server }) {
2226
await page.goto(server.PREFIX + '/dynamic-oopif.html');

Diff for: tests/config/android.config.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import type { Config } from '@playwright/test';
1818
import * as path from 'path';
1919
import { test as pageTest } from '../page/pageTest';
2020
import { androidFixtures } from '../android/androidTest';
21-
import { PlaywrightOptions } from './browserTest';
21+
import { PlaywrightOptionsEx } from './browserTest';
2222
import { CommonOptions } from './baseTest';
2323

2424
const outputDir = path.join(__dirname, '..', '..', 'test-results');
2525
const testDir = path.join(__dirname, '..');
26-
const config: Config<CommonOptions & PlaywrightOptions> = {
26+
const config: Config<CommonOptions & PlaywrightOptionsEx> = {
2727
testDir,
2828
outputDir,
2929
timeout: 120000,

Diff for: tests/config/baseTest.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Fixtures, _baseTest } from '@playwright/test';
17+
import { Fixtures, VideoMode, _baseTest } from '@playwright/test';
1818
import * as path from 'path';
1919
import * as fs from 'fs';
2020
import { installCoverageHooks } from './coverage';
2121
import { start } from '../../packages/playwright-core/lib/outofprocess';
2222
import { GridClient } from 'playwright-core/lib/grid/gridClient';
23-
import type { LaunchOptions } from 'playwright-core';
23+
import type { LaunchOptions, ViewportSize } from 'playwright-core';
2424
import { commonFixtures, CommonFixtures, serverFixtures, ServerFixtures, ServerOptions } from './commonFixtures';
2525

2626
export type BrowserName = 'chromium' | 'firefox' | 'webkit';
@@ -29,8 +29,8 @@ type BaseOptions = {
2929
mode: Mode;
3030
browserName: BrowserName;
3131
channel: LaunchOptions['channel'];
32-
video: boolean | undefined;
33-
trace: boolean | undefined;
32+
video: VideoMode | { mode: VideoMode, size: ViewportSize };
33+
trace: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | /** deprecated */ 'retry-with-trace';
3434
headless: boolean | undefined;
3535
};
3636
type BaseFixtures = {

Diff for: tests/config/browserTest.ts

+15-37
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
* limitations under the License.
1515
*/
1616

17-
import type { Fixtures } from '@playwright/test';
17+
import type { Fixtures, PlaywrightTestOptions, PlaywrightWorkerOptions } from '@playwright/test';
1818
import type { Browser, BrowserContext, BrowserContextOptions, BrowserType, LaunchOptions, Page } from 'playwright-core';
1919
import { removeFolders } from 'playwright-core/lib/utils/utils';
20-
import { ReuseBrowserContextStorage } from '@playwright/test/src/index';
20+
import { browserOptionsWorkerFixture, browserTypeWorkerFixture, browserWorkerFixture, ReuseBrowserContextStorage } from '@playwright/test/src/index';
2121
import * as path from 'path';
2222
import * as fs from 'fs';
2323
import * as os from 'os';
@@ -26,21 +26,16 @@ import { baseTest, CommonWorkerFixtures } from './baseTest';
2626
import { CommonFixtures } from './commonFixtures';
2727
import type { ParsedStackTrace } from 'playwright-core/lib/utils/stackTrace';
2828

29-
type PlaywrightWorkerOptions = {
30-
executablePath: LaunchOptions['executablePath'];
31-
proxy: LaunchOptions['proxy'];
32-
args: LaunchOptions['args'];
33-
};
3429
export type PlaywrightWorkerFixtures = {
30+
_browserType: BrowserType;
31+
_browserOptions: LaunchOptions;
3532
browserType: BrowserType;
3633
browserOptions: LaunchOptions;
3734
browser: Browser;
3835
browserVersion: string;
39-
_reuseBrowserContext: ReuseBrowserContextStorage,
40-
};
41-
type PlaywrightTestOptions = {
42-
hasTouch: BrowserContextOptions['hasTouch'];
36+
_reuseBrowserContext: ReuseBrowserContextStorage;
4337
};
38+
4439
type PlaywrightTestFixtures = {
4540
createUserDataDir: () => Promise<string>;
4641
launchPersistent: (options?: Parameters<BrowserType['launchPersistentContext']>[1]) => Promise<{ context: BrowserContext, page: Page }>;
@@ -50,35 +45,18 @@ type PlaywrightTestFixtures = {
5045
context: BrowserContext;
5146
page: Page;
5247
};
53-
export type PlaywrightOptions = PlaywrightWorkerOptions & PlaywrightTestOptions;
48+
export type PlaywrightOptionsEx = PlaywrightWorkerOptions & PlaywrightTestOptions;
5449

5550
export const playwrightFixtures: Fixtures<PlaywrightTestOptions & PlaywrightTestFixtures, PlaywrightWorkerOptions & PlaywrightWorkerFixtures, CommonFixtures, CommonWorkerFixtures> = {
56-
executablePath: [ undefined, { scope: 'worker' } ],
57-
proxy: [ undefined, { scope: 'worker' } ],
58-
args: [ undefined, { scope: 'worker' } ],
5951
hasTouch: undefined,
6052

61-
browserType: [async ({ playwright, browserName }, run) => {
62-
await run(playwright[browserName]);
63-
}, { scope: 'worker' } ],
64-
65-
browserOptions: [async ({ headless, channel, executablePath, proxy, args }, run) => {
66-
await run({
67-
headless,
68-
channel,
69-
executablePath,
70-
proxy,
71-
args,
72-
handleSIGINT: false,
73-
devtools: process.env.DEVTOOLS === '1',
74-
});
75-
}, { scope: 'worker' } ],
53+
_browserType: [browserTypeWorkerFixture, { scope: 'worker' } ],
54+
_browserOptions: [browserOptionsWorkerFixture, { scope: 'worker' } ],
7655

77-
browser: [async ({ browserType, browserOptions }, run) => {
78-
const browser = await browserType.launch(browserOptions);
79-
await run(browser);
80-
await browser.close();
81-
}, { scope: 'worker' } ],
56+
launchOptions: [ {}, { scope: 'worker' } ],
57+
browserType: [async ({ _browserType }, use) => use(_browserType), { scope: 'worker' } ],
58+
browserOptions: [async ({ _browserOptions }, use) => use(_browserOptions), { scope: 'worker' } ],
59+
browser: [browserWorkerFixture, { scope: 'worker' } ],
8260

8361
browserVersion: [async ({ browser }, run) => {
8462
await run(browser.version());
@@ -132,7 +110,7 @@ export const playwrightFixtures: Fixtures<PlaywrightTestOptions & PlaywrightTest
132110
contextOptions: async ({ video, hasTouch }, run, testInfo) => {
133111
const debugName = path.relative(testInfo.project.outputDir, testInfo.outputDir).replace(/[\/\\]/g, '-');
134112
const contextOptions = {
135-
recordVideo: video ? { dir: testInfo.outputPath('') } : undefined,
113+
recordVideo: video === 'on' ? { dir: testInfo.outputPath('') } : undefined,
136114
_debugName: debugName,
137115
hasTouch,
138116
} as BrowserContextOptions;
@@ -145,7 +123,7 @@ export const playwrightFixtures: Fixtures<PlaywrightTestOptions & PlaywrightTest
145123
const context = await browser.newContext({ ...contextOptions, ...options });
146124
contexts.set(context, { closed: false });
147125
context.on('close', () => contexts.get(context).closed = true);
148-
if (trace)
126+
if (trace === 'on')
149127
await context.tracing.start({ screenshots: true, snapshots: true, sources: true } as any);
150128
(context as any)._instrumentation.addListener({
151129
onApiCallBegin: (apiCall: string, stackTrace: ParsedStackTrace | null, userData: any) => {

0 commit comments

Comments
 (0)