Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 3 additions & 14 deletions packages/playwright/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ import fs from 'fs';
import path from 'path';

import * as playwrightLibrary from 'playwright-core';
import { setBoxedStackPrefixes, createGuid, currentZone, debugMode, jsonStringifyForceASCII, asLocatorDescription, renderTitleForCall, getActionGroup } from 'playwright-core/lib/utils';
import { createGuid, currentZone, debugMode, jsonStringifyForceASCII, asLocatorDescription, renderTitleForCall, getActionGroup } from 'playwright-core/lib/utils';

import { currentTestInfo } from './common/globals';
import { rootTestType } from './common/testType';
import { runBrowserBackendAtEnd } from './mcp/test/browserBackend';
import { initPlaywrightTest } from './util';

import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, VideoMode } from '../types/test';
import type { ContextReuseMode } from './common/config';
Expand All @@ -39,19 +40,7 @@ import type { BrowserContext, BrowserContextOptions, LaunchOptions, Page, Tracin
export { expect } from './matchers/expect';
export const _baseTest: TestType<{}, {}> = rootTestType.test;

setBoxedStackPrefixes([path.dirname(require.resolve('../package.json'))]);

if ((process as any)['__pw_initiator__']) {
const originalStackTraceLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 200;
try {
throw new Error('Requiring @playwright/test second time, \nFirst:\n' + (process as any)['__pw_initiator__'] + '\n\nSecond: ');
} finally {
Error.stackTraceLimit = originalStackTraceLimit;
}
} else {
(process as any)['__pw_initiator__'] = new Error().stack;
}
initPlaywrightTest('Importing @playwright/test');

type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
_combinedContextOptions: BrowserContextOptions,
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright/src/mcp/test/testContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class TestContext {
async createTestRunner(): Promise<TestRunner> {
if (this._testRunner)
await this._testRunner.stopTests();
const testRunner = new TestRunner(this.configLocation!, {});
const testRunner = new TestRunner(this.configLocation!, {}, 'Playwright Agent');
await testRunner.initialize({});
this._testRunner = testRunner;
testRunner.on(TestRunnerEvent.TestFilesChanged, testFiles => {
Expand Down
9 changes: 7 additions & 2 deletions packages/playwright/src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { ensureSeedTest, seedProject } from './mcp/test/seed';
import { decorateCommand } from './mcp/program';
import { setupExitWatchdog } from './mcp/browser/watchdog';
import { initClaudeCodeRepo, initOpencodeRepo, initVSCodeRepo } from './agents/generateAgents';
import { initPlaywrightTest } from './util';

import type { ConfigCLIOverrides } from './common/ipc';
import type { TraceMode } from '../types/test';
Expand Down Expand Up @@ -86,7 +87,7 @@ function addClearCacheCommand(program: Command) {
command.description('clears build and test caches');
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
command.action(async opts => {
const runner = new TestRunner(resolveConfigLocation(opts.config), {});
const runner = new TestRunner(resolveConfigLocation(opts.config), {}, 'Playwright CLI');
const { status } = await runner.clearCache(createErrorCollectingReporter(terminalScreen));
const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);
gracefullyProcessExitDoNotHang(exitCode);
Expand All @@ -98,7 +99,7 @@ function addDevServerCommand(program: Command) {
command.description('start dev server');
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
command.action(async options => {
const runner = new TestRunner(resolveConfigLocation(options.config), {});
const runner = new TestRunner(resolveConfigLocation(options.config), {}, 'Playwright CLI');
await runner.startDevServer(createErrorCollectingReporter(terminalScreen), 'in-process');
});
}
Expand Down Expand Up @@ -163,6 +164,7 @@ function addTestMCPServerCommand(program: Command) {
command.option('--port <port>', 'port to listen on for SSE transport.');
command.action(async options => {
setupExitWatchdog();
initPlaywrightTest('Playwright Agent');
const backendFactory: ServerBackendFactory = {
name: 'Playwright Test Runner',
nameInConfig: 'playwright-test-runner',
Expand Down Expand Up @@ -206,6 +208,7 @@ function addInitAgentsCommand(program: Command) {
}

async function runTests(args: string[], opts: { [key: string]: any }) {
initPlaywrightTest('Playwright CLI');
await startProfiling();
const cliOverrides = overridesFromOptions(opts);

Expand Down Expand Up @@ -268,6 +271,7 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
}

async function runTestServer(opts: { [key: string]: any }) {
initPlaywrightTest('Playwright CLI');
const host = opts.host || 'localhost';
const port = opts.port ? +opts.port : 0;
const status = await testServer.runTestServer(opts.config, { }, { host, port });
Expand All @@ -276,6 +280,7 @@ async function runTestServer(opts: { [key: string]: any }) {
}

async function mergeReports(reportDir: string | undefined, opts: { [key: string]: any }) {
initPlaywrightTest('Playwright CLI');
const configFile = opts.config;
const config = configFile ? await loadConfigFromFile(configFile) : await loadEmptyConfigForMergeReports();

Expand Down
5 changes: 3 additions & 2 deletions packages/playwright/src/runner/testRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { webServerPluginsForConfig } from '../plugins/webServerPlugin';
import { internalScreen } from '../reporters/base';
import { InternalReporter } from '../reporters/internalReporter';
import { affectedTestFiles, collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache';
import { serializeError } from '../util';
import { initPlaywrightTest, serializeError } from '../util';
import { createErrorCollectingReporter, createReporters } from './reporters';
import { TestRun, createApplyRebaselinesTask, createClearCacheTask, createGlobalSetupTasks, createListFilesTask, createLoadTask, createPluginSetupTasks, createReportBeginTask, createRunTestsTasks, createStartDevServerTask, runTasks, runTasksDeferCleanup } from './tasks';
import { LastRunReporter } from './lastRun';
Expand Down Expand Up @@ -97,7 +97,8 @@ export class TestRunner extends EventEmitter<TestRunnerEventMap> {
private _watchTestDirs = false;
private _populateDependenciesOnList = false;

constructor(configLocation: ConfigLocation, configCLIOverrides: ConfigCLIOverrides) {
constructor(configLocation: ConfigLocation, configCLIOverrides: ConfigCLIOverrides, initiator: string) {
initPlaywrightTest(initiator);
super();
this.configLocation = configLocation;
this._configCLIOverrides = configCLIOverrides;
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright/src/runner/testServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export class TestServerDispatcher implements TestServerInterface {
readonly _dispatchEvent: TestServerInterfaceEventEmitters['dispatchEvent'];

constructor(configLocation: ConfigLocation, configCLIOverrides: ConfigCLIOverrides) {
this._testRunner = new TestRunner(configLocation, configCLIOverrides);
this._testRunner = new TestRunner(configLocation, configCLIOverrides, 'VSCode extension');
this.transport = {
onconnect: () => {},
dispatch: (method, params) => (this as any)[method](params),
Expand Down
31 changes: 30 additions & 1 deletion packages/playwright/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import path from 'path';
import url from 'url';
import util from 'util';

import { parseStackFrame, sanitizeForFilePath, calculateSha1, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils';
import { setBoxedStackPrefixes, parseStackFrame, sanitizeForFilePath, calculateSha1, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils';
import { debug, mime, minimatch } from 'playwright-core/lib/utilsBundle';

import type { Location } from './../types/testReporter';
Expand All @@ -31,6 +31,35 @@ import type { TestCase } from './common/test';
const PLAYWRIGHT_TEST_PATH = path.join(__dirname, '..');
const PLAYWRIGHT_CORE_PATH = path.dirname(require.resolve('playwright-core/package.json'));

export function initPlaywrightTest(initiator: string) {
setBoxedStackPrefixes([path.dirname(require.resolve('../package.json'))]);

initiator = ` ${initiator}\n v${require('../package.json').version} at ${PLAYWRIGHT_TEST_PATH}`;

if ((process as any)['__pw_initiator_path__'] === PLAYWRIGHT_TEST_PATH) {
// Same version of Playwright, update the initiator.
(process as any)['__pw_initiator__'] = initiator;
return;
}

if ((process as any)['__pw_initiator__']) {
// Different version of Playwright, throw.
const error = new Error([
'Mixing two versions of Playwright:',
'',
(process as any)['__pw_initiator__'],
'',
initiator,
].join('\n'));
error.stack = 'Error: ' + error.message;
throw error;
}

// First-time import, set up the initiator.
(process as any)['__pw_initiator_path__'] = PLAYWRIGHT_TEST_PATH;
(process as any)['__pw_initiator__'] = initiator;
}

export function filterStackTrace(e: Error): { message: string, stack: string, cause?: ReturnType<typeof filterStackTrace> } {
const name = e.name ? e.name + ': ' : '';
const cause = e.cause instanceof Error ? filterStackTrace(e.cause) : undefined;
Expand Down