123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports._baseTest = void 0;
- Object.defineProperty(exports, "_store", {
- enumerable: true,
- get: function () {
- return _store.store;
- }
- });
- Object.defineProperty(exports, "defineConfig", {
- enumerable: true,
- get: function () {
- return _configLoader.defineConfig;
- }
- });
- Object.defineProperty(exports, "expect", {
- enumerable: true,
- get: function () {
- return _expect.expect;
- }
- });
- Object.defineProperty(exports, "mergeExpects", {
- enumerable: true,
- get: function () {
- return _expect.mergeExpects;
- }
- });
- Object.defineProperty(exports, "mergeTests", {
- enumerable: true,
- get: function () {
- return _testType.mergeTests;
- }
- });
- exports.test = void 0;
- var fs = _interopRequireWildcard(require("fs"));
- var path = _interopRequireWildcard(require("path"));
- var playwrightLibrary = _interopRequireWildcard(require("playwright-core"));
- var _utils = require("playwright-core/lib/utils");
- var _testType = require("./common/testType");
- var _globals = require("./common/globals");
- var _testTracing = require("./worker/testTracing");
- var _expect = require("./matchers/expect");
- var _store = require("./store");
- var _configLoader = require("./common/configLoader");
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
- function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
- /**
- * Copyright (c) Microsoft Corporation.
- *
- * 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.
- */
- const _baseTest = exports._baseTest = _testType.rootTestType.test;
- (0, _utils.addInternalStackPrefix)(path.dirname(require.resolve('../package.json')));
- if (process['__pw_initiator__']) {
- const originalStackTraceLimit = Error.stackTraceLimit;
- Error.stackTraceLimit = 200;
- try {
- throw new Error('Requiring @playwright/test second time, \nFirst:\n' + process['__pw_initiator__'] + '\n\nSecond: ');
- } finally {
- Error.stackTraceLimit = originalStackTraceLimit;
- }
- } else {
- process['__pw_initiator__'] = new Error().stack;
- }
- const playwrightFixtures = {
- defaultBrowserType: ['chromium', {
- scope: 'worker',
- option: true
- }],
- browserName: [({
- defaultBrowserType
- }, use) => use(defaultBrowserType), {
- scope: 'worker',
- option: true
- }],
- playwright: [async ({}, use) => {
- await use(require('playwright-core'));
- }, {
- scope: 'worker',
- _hideStep: true
- }],
- headless: [({
- launchOptions
- }, use) => {
- var _launchOptions$headle;
- return use((_launchOptions$headle = launchOptions.headless) !== null && _launchOptions$headle !== void 0 ? _launchOptions$headle : true);
- }, {
- scope: 'worker',
- option: true
- }],
- channel: [({
- launchOptions
- }, use) => use(launchOptions.channel), {
- scope: 'worker',
- option: true
- }],
- launchOptions: [{}, {
- scope: 'worker',
- option: true
- }],
- connectOptions: [async ({}, use) => {
- await use(connectOptionsFromEnv());
- }, {
- scope: 'worker',
- option: true
- }],
- screenshot: ['off', {
- scope: 'worker',
- option: true
- }],
- video: ['off', {
- scope: 'worker',
- option: true
- }],
- trace: ['off', {
- scope: 'worker',
- option: true
- }],
- _artifactsDir: [async ({}, use) => {
- await use(process.env.TEST_ARTIFACTS_DIR);
- }, {
- scope: 'worker',
- _title: 'playwright configuration'
- }],
- _browserOptions: [async ({
- playwright,
- headless,
- channel,
- launchOptions,
- _artifactsDir
- }, use) => {
- const options = {
- handleSIGINT: false,
- ...launchOptions
- };
- if (headless !== undefined) options.headless = headless;
- if (channel !== undefined) options.channel = channel;
- options.tracesDir = path.join(_artifactsDir, 'traces');
- for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) browserType._defaultLaunchOptions = options;
- await use(options);
- for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) browserType._defaultLaunchOptions = undefined;
- }, {
- scope: 'worker',
- auto: true
- }],
- browser: [async ({
- playwright,
- browserName,
- _browserOptions,
- connectOptions
- }, use, testInfo) => {
- if (!['chromium', 'firefox', 'webkit'].includes(browserName)) throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`);
- if (connectOptions) {
- var _connectOptions$expos;
- const browser = await playwright[browserName].connect({
- ...connectOptions,
- exposeNetwork: (_connectOptions$expos = connectOptions.exposeNetwork) !== null && _connectOptions$expos !== void 0 ? _connectOptions$expos : connectOptions._exposeNetwork,
- headers: {
- ...(process.env.PW_TEST_REUSE_CONTEXT ? {
- 'x-playwright-reuse-context': '1'
- } : {}),
- // HTTP headers are ASCII only (not UTF-8).
- 'x-playwright-launch-options': (0, _utils.jsonStringifyForceASCII)(_browserOptions),
- ...connectOptions.headers
- }
- });
- await use(browser);
- await browser._wrapApiCall(async () => {
- await browser.close({
- reason: 'Test ended.'
- });
- }, true);
- return;
- }
- const browser = await playwright[browserName].launch();
- await use(browser);
- await browser._wrapApiCall(async () => {
- await browser.close({
- reason: 'Test ended.'
- });
- }, true);
- }, {
- scope: 'worker',
- timeout: 0
- }],
- acceptDownloads: [({
- contextOptions
- }, use) => {
- var _contextOptions$accep;
- return use((_contextOptions$accep = contextOptions.acceptDownloads) !== null && _contextOptions$accep !== void 0 ? _contextOptions$accep : true);
- }, {
- option: true
- }],
- bypassCSP: [({
- contextOptions
- }, use) => {
- var _contextOptions$bypas;
- return use((_contextOptions$bypas = contextOptions.bypassCSP) !== null && _contextOptions$bypas !== void 0 ? _contextOptions$bypas : false);
- }, {
- option: true
- }],
- colorScheme: [({
- contextOptions
- }, use) => use(contextOptions.colorScheme === undefined ? 'light' : contextOptions.colorScheme), {
- option: true
- }],
- deviceScaleFactor: [({
- contextOptions
- }, use) => use(contextOptions.deviceScaleFactor), {
- option: true
- }],
- extraHTTPHeaders: [({
- contextOptions
- }, use) => use(contextOptions.extraHTTPHeaders), {
- option: true
- }],
- geolocation: [({
- contextOptions
- }, use) => use(contextOptions.geolocation), {
- option: true
- }],
- hasTouch: [({
- contextOptions
- }, use) => {
- var _contextOptions$hasTo;
- return use((_contextOptions$hasTo = contextOptions.hasTouch) !== null && _contextOptions$hasTo !== void 0 ? _contextOptions$hasTo : false);
- }, {
- option: true
- }],
- httpCredentials: [({
- contextOptions
- }, use) => use(contextOptions.httpCredentials), {
- option: true
- }],
- ignoreHTTPSErrors: [({
- contextOptions
- }, use) => {
- var _contextOptions$ignor;
- return use((_contextOptions$ignor = contextOptions.ignoreHTTPSErrors) !== null && _contextOptions$ignor !== void 0 ? _contextOptions$ignor : false);
- }, {
- option: true
- }],
- isMobile: [({
- contextOptions
- }, use) => {
- var _contextOptions$isMob;
- return use((_contextOptions$isMob = contextOptions.isMobile) !== null && _contextOptions$isMob !== void 0 ? _contextOptions$isMob : false);
- }, {
- option: true
- }],
- javaScriptEnabled: [({
- contextOptions
- }, use) => {
- var _contextOptions$javaS;
- return use((_contextOptions$javaS = contextOptions.javaScriptEnabled) !== null && _contextOptions$javaS !== void 0 ? _contextOptions$javaS : true);
- }, {
- option: true
- }],
- locale: [({
- contextOptions
- }, use) => {
- var _contextOptions$local;
- return use((_contextOptions$local = contextOptions.locale) !== null && _contextOptions$local !== void 0 ? _contextOptions$local : 'en-US');
- }, {
- option: true
- }],
- offline: [({
- contextOptions
- }, use) => {
- var _contextOptions$offli;
- return use((_contextOptions$offli = contextOptions.offline) !== null && _contextOptions$offli !== void 0 ? _contextOptions$offli : false);
- }, {
- option: true
- }],
- permissions: [({
- contextOptions
- }, use) => use(contextOptions.permissions), {
- option: true
- }],
- proxy: [({
- contextOptions
- }, use) => use(contextOptions.proxy), {
- option: true
- }],
- storageState: [({
- contextOptions
- }, use) => use(contextOptions.storageState), {
- option: true
- }],
- timezoneId: [({
- contextOptions
- }, use) => use(contextOptions.timezoneId), {
- option: true
- }],
- userAgent: [({
- contextOptions
- }, use) => use(contextOptions.userAgent), {
- option: true
- }],
- viewport: [({
- contextOptions
- }, use) => use(contextOptions.viewport === undefined ? {
- width: 1280,
- height: 720
- } : contextOptions.viewport), {
- option: true
- }],
- actionTimeout: [0, {
- option: true
- }],
- testIdAttribute: ['data-testid', {
- option: true
- }],
- navigationTimeout: [0, {
- option: true
- }],
- baseURL: [async ({}, use) => {
- await use(process.env.PLAYWRIGHT_TEST_BASE_URL);
- }, {
- option: true
- }],
- serviceWorkers: [({
- contextOptions
- }, use) => {
- var _contextOptions$servi;
- return use((_contextOptions$servi = contextOptions.serviceWorkers) !== null && _contextOptions$servi !== void 0 ? _contextOptions$servi : 'allow');
- }, {
- option: true
- }],
- contextOptions: [{}, {
- option: true
- }],
- _combinedContextOptions: async ({
- acceptDownloads,
- bypassCSP,
- colorScheme,
- deviceScaleFactor,
- extraHTTPHeaders,
- hasTouch,
- geolocation,
- httpCredentials,
- ignoreHTTPSErrors,
- isMobile,
- javaScriptEnabled,
- locale,
- offline,
- permissions,
- proxy,
- storageState,
- viewport,
- timezoneId,
- userAgent,
- baseURL,
- contextOptions,
- serviceWorkers
- }, use) => {
- const options = {};
- if (acceptDownloads !== undefined) options.acceptDownloads = acceptDownloads;
- if (bypassCSP !== undefined) options.bypassCSP = bypassCSP;
- if (colorScheme !== undefined) options.colorScheme = colorScheme;
- if (deviceScaleFactor !== undefined) options.deviceScaleFactor = deviceScaleFactor;
- if (extraHTTPHeaders !== undefined) options.extraHTTPHeaders = extraHTTPHeaders;
- if (geolocation !== undefined) options.geolocation = geolocation;
- if (hasTouch !== undefined) options.hasTouch = hasTouch;
- if (httpCredentials !== undefined) options.httpCredentials = httpCredentials;
- if (ignoreHTTPSErrors !== undefined) options.ignoreHTTPSErrors = ignoreHTTPSErrors;
- if (isMobile !== undefined) options.isMobile = isMobile;
- if (javaScriptEnabled !== undefined) options.javaScriptEnabled = javaScriptEnabled;
- if (locale !== undefined) options.locale = locale;
- if (offline !== undefined) options.offline = offline;
- if (permissions !== undefined) options.permissions = permissions;
- if (proxy !== undefined) options.proxy = proxy;
- if (storageState !== undefined) options.storageState = storageState;
- if (timezoneId !== undefined) options.timezoneId = timezoneId;
- if (userAgent !== undefined) options.userAgent = userAgent;
- if (viewport !== undefined) options.viewport = viewport;
- if (baseURL !== undefined) options.baseURL = baseURL;
- if (serviceWorkers !== undefined) options.serviceWorkers = serviceWorkers;
- await use({
- ...contextOptions,
- ...options
- });
- },
- _setupContextOptions: [async ({
- playwright,
- _combinedContextOptions,
- _artifactsDir,
- actionTimeout,
- navigationTimeout,
- testIdAttribute
- }, use, testInfo) => {
- if (testIdAttribute) playwrightLibrary.selectors.setTestIdAttribute(testIdAttribute);
- testInfo.snapshotSuffix = process.platform;
- if ((0, _utils.debugMode)()) testInfo.setTimeout(0);
- for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) {
- browserType._defaultContextOptions = _combinedContextOptions;
- browserType._defaultContextTimeout = actionTimeout || 0;
- browserType._defaultContextNavigationTimeout = navigationTimeout || 0;
- }
- playwright.request._defaultContextOptions = {
- ..._combinedContextOptions
- };
- playwright.request._defaultContextOptions.tracesDir = path.join(_artifactsDir, 'traces');
- playwright.request._defaultContextOptions.timeout = actionTimeout || 0;
- await use();
- playwright.request._defaultContextOptions = undefined;
- for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) {
- browserType._defaultContextOptions = undefined;
- browserType._defaultContextTimeout = undefined;
- browserType._defaultContextNavigationTimeout = undefined;
- }
- }, {
- auto: 'all-hooks-included',
- _title: 'context configuration'
- }],
- _setupArtifacts: [async ({
- playwright,
- _artifactsDir,
- trace,
- screenshot
- }, use, testInfo) => {
- const artifactsRecorder = new ArtifactsRecorder(playwright, _artifactsDir, trace, screenshot);
- await artifactsRecorder.willStartTest(testInfo);
- const csiListener = {
- onApiCallBegin: (apiName, params, frames, wallTime, userData) => {
- const testInfo = (0, _globals.currentTestInfo)();
- if (!testInfo || apiName.includes('setTestIdAttribute')) return {
- userObject: null
- };
- const step = testInfo._addStep({
- location: frames[0],
- category: 'pw:api',
- title: renderApiCall(apiName, params),
- apiName,
- params,
- wallTime,
- laxParent: true
- });
- userData.userObject = step;
- },
- onApiCallEnd: (userData, error) => {
- const step = userData.userObject;
- step === null || step === void 0 ? void 0 : step.complete({
- error
- });
- },
- onWillPause: () => {
- var _currentTestInfo;
- (_currentTestInfo = (0, _globals.currentTestInfo)()) === null || _currentTestInfo === void 0 ? void 0 : _currentTestInfo.setTimeout(0);
- },
- onDidCreateBrowserContext: async context => {
- await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.didCreateBrowserContext(context));
- const testInfo = (0, _globals.currentTestInfo)();
- if (testInfo) attachConnectedHeaderIfNeeded(testInfo, context.browser());
- },
- onDidCreateRequestContext: async context => {
- await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.didCreateRequestContext(context));
- },
- onWillCloseBrowserContext: async context => {
- await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.willCloseBrowserContext(context));
- },
- onWillCloseRequestContext: async context => {
- await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.willCloseRequestContext(context));
- }
- };
- const clientInstrumentation = playwright._instrumentation;
- clientInstrumentation.addListener(csiListener);
- await use();
- clientInstrumentation.removeListener(csiListener);
- await (artifactsRecorder === null || artifactsRecorder === void 0 ? void 0 : artifactsRecorder.didFinishTest());
- }, {
- auto: 'all-hooks-included',
- _title: 'trace recording'
- }],
- _contextFactory: [async ({
- browser,
- video,
- _artifactsDir,
- _reuseContext
- }, use, testInfo) => {
- const testInfoImpl = testInfo;
- const videoMode = normalizeVideoMode(video);
- const captureVideo = shouldCaptureVideo(videoMode, testInfo) && !_reuseContext;
- const contexts = new Map();
- await use(async options => {
- const hook = hookType(testInfoImpl);
- if (hook) {
- throw new Error([`"context" and "page" fixtures are not supported in "${hook}" since they are created on a per-test basis.`, `If you would like to reuse a single page between tests, create context manually with browser.newContext(). See https://aka.ms/playwright/reuse-page for details.`, `If you would like to configure your page before each test, do that in beforeEach hook instead.`].join('\n'));
- }
- const videoOptions = captureVideo ? {
- recordVideo: {
- dir: _artifactsDir,
- size: typeof video === 'string' ? undefined : video.size
- }
- } : {};
- const context = await browser.newContext({
- ...videoOptions,
- ...options
- });
- const contextData = {
- pagesWithVideo: []
- };
- contexts.set(context, contextData);
- if (captureVideo) context.on('page', page => contextData.pagesWithVideo.push(page));
- return context;
- });
- let counter = 0;
- const closeReason = testInfo.status === 'timedOut' ? 'Test timeout of ' + testInfo.timeout + 'ms exceeded.' : 'Test ended.';
- await Promise.all([...contexts.keys()].map(async context => {
- context[kStartedContextTearDown] = true;
- await context._wrapApiCall(async () => {
- await context.close({
- reason: closeReason
- });
- }, true);
- const testFailed = testInfo.status !== testInfo.expectedStatus;
- const preserveVideo = captureVideo && (videoMode === 'on' || testFailed && videoMode === 'retain-on-failure' || videoMode === 'on-first-retry' && testInfo.retry === 1);
- if (preserveVideo) {
- const {
- pagesWithVideo: pagesForVideo
- } = contexts.get(context);
- const videos = pagesForVideo.map(p => p.video()).filter(Boolean);
- await Promise.all(videos.map(async v => {
- try {
- const savedPath = testInfo.outputPath(`video${counter ? '-' + counter : ''}.webm`);
- ++counter;
- await v.saveAs(savedPath);
- testInfo.attachments.push({
- name: 'video',
- path: savedPath,
- contentType: 'video/webm'
- });
- } catch (e) {
- // Silent catch empty videos.
- }
- }));
- }
- }));
- }, {
- scope: 'test',
- _title: 'context'
- }],
- _contextReuseMode: process.env.PW_TEST_REUSE_CONTEXT === 'when-possible' ? 'when-possible' : process.env.PW_TEST_REUSE_CONTEXT ? 'force' : 'none',
- _reuseContext: [async ({
- video,
- _contextReuseMode
- }, use, testInfo) => {
- const reuse = _contextReuseMode === 'force' || _contextReuseMode === 'when-possible' && !shouldCaptureVideo(normalizeVideoMode(video), testInfo);
- await use(reuse);
- }, {
- scope: 'test',
- _title: 'context'
- }],
- context: async ({
- playwright,
- browser,
- _reuseContext,
- _contextFactory
- }, use, testInfo) => {
- attachConnectedHeaderIfNeeded(testInfo, browser);
- if (!_reuseContext) {
- await use(await _contextFactory());
- return;
- }
- const defaultContextOptions = playwright.chromium._defaultContextOptions;
- const context = await browser._newContextForReuse(defaultContextOptions);
- context[kIsReusedContext] = true;
- await use(context);
- const closeReason = testInfo.status === 'timedOut' ? 'Test timeout of ' + testInfo.timeout + 'ms exceeded.' : 'Test ended.';
- await browser._stopPendingOperations(closeReason);
- },
- page: async ({
- context,
- _reuseContext
- }, use) => {
- if (!_reuseContext) {
- await use(await context.newPage());
- return;
- }
- // First time we are reusing the context, we should create the page.
- let [page] = context.pages();
- if (!page) page = await context.newPage();
- await use(page);
- },
- request: async ({
- playwright
- }, use) => {
- const request = await playwright.request.newContext();
- await use(request);
- request[kStartedContextTearDown] = true;
- await request.dispose();
- }
- };
- function hookType(testInfo) {
- const type = testInfo._timeoutManager.currentRunnableType();
- if (type === 'beforeAll' || type === 'afterAll') return type;
- }
- function normalizeVideoMode(video) {
- if (!video) return 'off';
- let videoMode = typeof video === 'string' ? video : video.mode;
- if (videoMode === 'retry-with-video') videoMode = 'on-first-retry';
- return videoMode;
- }
- function shouldCaptureVideo(videoMode, testInfo) {
- return videoMode === 'on' || videoMode === 'retain-on-failure' || videoMode === 'on-first-retry' && testInfo.retry === 1;
- }
- function normalizeTraceMode(trace) {
- if (!trace) return 'off';
- let traceMode = typeof trace === 'string' ? trace : trace.mode;
- if (traceMode === 'retry-with-trace') traceMode = 'on-first-retry';
- return traceMode;
- }
- function shouldCaptureTrace(traceMode, testInfo) {
- return traceMode === 'on' || traceMode === 'retain-on-failure' || traceMode === 'on-first-retry' && testInfo.retry === 1 || traceMode === 'on-all-retries' && testInfo.retry > 0;
- }
- function normalizeScreenshotMode(screenshot) {
- if (!screenshot) return 'off';
- return typeof screenshot === 'string' ? screenshot : screenshot.mode;
- }
- function attachConnectedHeaderIfNeeded(testInfo, browser) {
- const connectHeaders = browser === null || browser === void 0 ? void 0 : browser._connectHeaders;
- if (!connectHeaders) return;
- for (const header of connectHeaders) {
- if (header.name !== 'x-playwright-attachment') continue;
- const [name, value] = header.value.split('=');
- if (!name || !value) continue;
- if (testInfo.attachments.some(attachment => attachment.name === name)) continue;
- testInfo.attachments.push({
- name,
- contentType: 'text/plain',
- body: Buffer.from(value)
- });
- }
- }
- const kTracingStarted = Symbol('kTracingStarted');
- const kIsReusedContext = Symbol('kReusedContext');
- const kStartedContextTearDown = Symbol('kStartedContextTearDown');
- let traceOrdinal = 0;
- function connectOptionsFromEnv() {
- const wsEndpoint = process.env.PW_TEST_CONNECT_WS_ENDPOINT;
- if (!wsEndpoint) return undefined;
- const headers = process.env.PW_TEST_CONNECT_HEADERS ? JSON.parse(process.env.PW_TEST_CONNECT_HEADERS) : undefined;
- return {
- wsEndpoint,
- headers,
- exposeNetwork: process.env.PW_TEST_CONNECT_EXPOSE_NETWORK
- };
- }
- class ArtifactsRecorder {
- constructor(playwright, artifactsDir, trace, screenshot) {
- this._testInfo = void 0;
- this._playwright = void 0;
- this._artifactsDir = void 0;
- this._screenshotMode = void 0;
- this._traceMode = void 0;
- this._captureTrace = false;
- this._screenshotOptions = void 0;
- this._traceOptions = void 0;
- this._temporaryTraceFiles = [];
- this._temporaryScreenshots = [];
- this._temporaryArtifacts = [];
- this._reusedContexts = new Set();
- this._screenshotOrdinal = 0;
- this._screenshottedSymbol = void 0;
- this._startedCollectingArtifacts = void 0;
- this._playwright = playwright;
- this._artifactsDir = artifactsDir;
- this._screenshotMode = normalizeScreenshotMode(screenshot);
- this._screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot;
- this._traceMode = normalizeTraceMode(trace);
- const defaultTraceOptions = {
- screenshots: true,
- snapshots: true,
- sources: true,
- attachments: true,
- _live: false
- };
- this._traceOptions = typeof trace === 'string' ? defaultTraceOptions : {
- ...defaultTraceOptions,
- ...trace,
- mode: undefined
- };
- this._screenshottedSymbol = Symbol('screenshotted');
- this._startedCollectingArtifacts = Symbol('startedCollectingArtifacts');
- }
- _createTemporaryArtifact(...name) {
- const file = path.join(this._artifactsDir, ...name);
- this._temporaryArtifacts.push(file);
- return file;
- }
- async willStartTest(testInfo) {
- this._testInfo = testInfo;
- testInfo._onDidFinishTestFunction = () => this.didFinishTestFunction();
- this._captureTrace = shouldCaptureTrace(this._traceMode, testInfo) && !process.env.PW_TEST_DISABLE_TRACING;
- if (this._captureTrace) this._testInfo._tracing.start(this._createTemporaryArtifact('traces', `${this._testInfo.testId}-test.trace`), this._traceOptions);
- // Since beforeAll(s), test and afterAll(s) reuse the same TestInfo, make sure we do not
- // overwrite previous screenshots.
- this._screenshotOrdinal = testInfo.attachments.filter(a => a.name === 'screenshot').length;
- // Process existing contexts.
- for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) {
- const promises = [];
- const existingContexts = Array.from(browserType._contexts);
- for (const context of existingContexts) {
- if (context[kIsReusedContext]) this._reusedContexts.add(context);else promises.push(this.didCreateBrowserContext(context));
- }
- await Promise.all(promises);
- }
- {
- const existingApiRequests = Array.from(this._playwright.request._contexts);
- await Promise.all(existingApiRequests.map(c => this.didCreateRequestContext(c)));
- }
- }
- async didCreateBrowserContext(context) {
- await this._startTraceChunkOnContextCreation(context.tracing);
- }
- async willCloseBrowserContext(context) {
- // When reusing context, we get all previous contexts closed at the start of next test.
- // Do not record empty traces and useless screenshots for them.
- if (this._reusedContexts.has(context)) return;
- await this._stopTracing(context.tracing, context[kStartedContextTearDown]);
- if (this._screenshotMode === 'on' || this._screenshotMode === 'only-on-failure') {
- // Capture screenshot for now. We'll know whether we have to preserve them
- // after the test finishes.
- await Promise.all(context.pages().map(page => this._screenshotPage(page, true)));
- }
- }
- async didCreateRequestContext(context) {
- const tracing = context._tracing;
- await this._startTraceChunkOnContextCreation(tracing);
- }
- async willCloseRequestContext(context) {
- const tracing = context._tracing;
- await this._stopTracing(tracing, context[kStartedContextTearDown]);
- }
- async didFinishTestFunction() {
- const captureScreenshots = this._screenshotMode === 'on' || this._screenshotMode === 'only-on-failure' && this._testInfo._isFailure();
- if (captureScreenshots) await this._screenshotOnTestFailure();
- }
- async didFinishTest() {
- const captureScreenshots = this._screenshotMode === 'on' || this._screenshotMode === 'only-on-failure' && this._testInfo._isFailure();
- if (captureScreenshots) await this._screenshotOnTestFailure();
- const leftoverContexts = [];
- for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) leftoverContexts.push(...browserType._contexts);
- const leftoverApiRequests = Array.from(this._playwright.request._contexts);
- // Collect traces/screenshots for remaining contexts.
- await Promise.all(leftoverContexts.map(async context => {
- await this._stopTracing(context.tracing, true);
- }).concat(leftoverApiRequests.map(async context => {
- const tracing = context._tracing;
- await this._stopTracing(tracing, true);
- })));
- // Attach temporary screenshots for contexts closed before collecting the test trace.
- if (captureScreenshots) {
- for (const file of this._temporaryScreenshots) {
- try {
- const screenshotPath = this._createScreenshotAttachmentPath();
- await fs.promises.rename(file, screenshotPath);
- this._attachScreenshot(screenshotPath);
- } catch {}
- }
- }
- // Collect test trace.
- if (this._preserveTrace()) {
- const tracePath = this._createTemporaryArtifact((0, _utils.createGuid)() + '.zip');
- this._temporaryTraceFiles.push(tracePath);
- await this._testInfo._tracing.stop(tracePath);
- }
- // Either remove or attach temporary traces for contexts closed before the
- // test has finished.
- if (this._preserveTrace() && this._temporaryTraceFiles.length) {
- const tracePath = this._testInfo.outputPath(`trace.zip`);
- // This could be: beforeHooks, or beforeHooks + test, etc.
- const beforeHooksHadTrace = fs.existsSync(tracePath);
- if (beforeHooksHadTrace) {
- await fs.promises.rename(tracePath, tracePath + '.tmp');
- this._temporaryTraceFiles.unshift(tracePath + '.tmp');
- }
- await (0, _testTracing.mergeTraceFiles)(tracePath, this._temporaryTraceFiles);
- // Do not add attachment twice.
- if (!beforeHooksHadTrace) this._testInfo.attachments.push({
- name: 'trace',
- path: tracePath,
- contentType: 'application/zip'
- });
- }
- for (const file of this._temporaryArtifacts) await fs.promises.unlink(file).catch(() => {});
- }
- _createScreenshotAttachmentPath() {
- const testFailed = this._testInfo._isFailure();
- const index = this._screenshotOrdinal + 1;
- ++this._screenshotOrdinal;
- const screenshotPath = this._testInfo.outputPath(`test-${testFailed ? 'failed' : 'finished'}-${index}.png`);
- return screenshotPath;
- }
- async _screenshotPage(page, temporary) {
- if (page[this._screenshottedSymbol]) return;
- page[this._screenshottedSymbol] = true;
- try {
- const screenshotPath = temporary ? this._createTemporaryArtifact((0, _utils.createGuid)() + '.png') : this._createScreenshotAttachmentPath();
- // Pass caret=initial to avoid any evaluations that might slow down the screenshot
- // and let the page modify itself from the problematic state it had at the moment of failure.
- await page.screenshot({
- ...this._screenshotOptions,
- timeout: 5000,
- path: screenshotPath,
- caret: 'initial'
- });
- if (temporary) this._temporaryScreenshots.push(screenshotPath);else this._attachScreenshot(screenshotPath);
- } catch {
- // Screenshot may fail, just ignore.
- }
- }
- _attachScreenshot(screenshotPath) {
- this._testInfo.attachments.push({
- name: 'screenshot',
- path: screenshotPath,
- contentType: 'image/png'
- });
- }
- async _screenshotOnTestFailure() {
- const contexts = [];
- for (const browserType of [this._playwright.chromium, this._playwright.firefox, this._playwright.webkit]) contexts.push(...browserType._contexts);
- const pages = contexts.map(ctx => ctx.pages()).flat();
- await Promise.all(pages.map(page => this._screenshotPage(page, false)));
- }
- async _startTraceChunkOnContextCreation(tracing) {
- if (this._captureTrace) {
- const title = [path.relative(this._testInfo.project.testDir, this._testInfo.file) + ':' + this._testInfo.line, ...this._testInfo.titlePath.slice(1)].join(' › ');
- const ordinalSuffix = traceOrdinal ? `-context${traceOrdinal}` : '';
- ++traceOrdinal;
- const retrySuffix = this._testInfo.retry ? `-retry${this._testInfo.retry}` : '';
- // Note that trace name must start with testId for live tracing to work.
- const name = `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`;
- if (!tracing[kTracingStarted]) {
- await tracing.start({
- ...this._traceOptions,
- title,
- name
- });
- tracing[kTracingStarted] = true;
- } else {
- await tracing.startChunk({
- title,
- name
- });
- }
- } else {
- if (tracing[kTracingStarted]) {
- tracing[kTracingStarted] = false;
- await tracing.stop();
- }
- }
- }
- _preserveTrace() {
- const testFailed = this._testInfo.status !== this._testInfo.expectedStatus;
- return this._captureTrace && (this._traceMode === 'on' || testFailed && this._traceMode === 'retain-on-failure' || this._traceMode === 'on-first-retry' && this._testInfo.retry === 1 || this._traceMode === 'on-all-retries' && this._testInfo.retry > 0);
- }
- async _stopTracing(tracing, contextTearDownStarted) {
- if (tracing[this._startedCollectingArtifacts]) return;
- tracing[this._startedCollectingArtifacts] = true;
- if (this._captureTrace) {
- let tracePath;
- // Create a trace file if we know that:
- // - it is's going to be used due to the config setting and the test status or
- // - we are inside a test or afterEach and the user manually closed the context.
- if (this._preserveTrace() || !contextTearDownStarted) {
- tracePath = this._createTemporaryArtifact((0, _utils.createGuid)() + '.zip');
- this._temporaryTraceFiles.push(tracePath);
- }
- await tracing.stopChunk({
- path: tracePath
- });
- }
- }
- }
- const paramsToRender = ['url', 'selector', 'text', 'key'];
- function renderApiCall(apiName, params) {
- const paramsArray = [];
- if (params) {
- for (const name of paramsToRender) {
- if (!(name in params)) continue;
- let value;
- if (name === 'selector' && (0, _utils.isString)(params[name]) && params[name].startsWith('internal:')) {
- const getter = (0, _utils.asLocator)('javascript', params[name]);
- apiName = apiName.replace(/^locator\./, 'locator.' + getter + '.');
- apiName = apiName.replace(/^page\./, 'page.' + getter + '.');
- apiName = apiName.replace(/^frame\./, 'frame.' + getter + '.');
- } else {
- value = params[name];
- paramsArray.push(value);
- }
- }
- }
- const paramsText = paramsArray.length ? '(' + paramsArray.join(', ') + ')' : '';
- return apiName + paramsText;
- }
- const test = exports.test = _baseTest.extend(playwrightFixtures);
|