Skip to content

Commit d7abd99

Browse files
committed
re-iterate
1 parent 2776191 commit d7abd99

File tree

6 files changed

+96
-26
lines changed

6 files changed

+96
-26
lines changed

packages/playwright/src/common/testLoader.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@
1515
*/
1616

1717
import path from 'path';
18-
import util from 'util';
1918

2019
import * as esmLoaderHost from './esmLoaderHost';
2120
import { isWorkerProcess, setCurrentlyLoadingFileSuite } from './globals';
2221
import { Suite } from './test';
2322
import { startCollectingFileDeps, stopCollectingFileDeps } from '../transform/compilationCache';
2423
import { requireOrImport } from '../transform/transform';
25-
import { filterStackTrace } from '../util';
24+
import { serializeLoadError } from '../util';
2625

2726
import type { TestError } from '../../types/testReporter';
2827

@@ -50,7 +49,7 @@ export async function loadTestFile(file: string, rootDir: string, testErrors?: T
5049
} catch (e) {
5150
if (!testErrors)
5251
throw e;
53-
testErrors.push(serializeLoadError(file, e));
52+
testErrors.push(serializeLoadError(e, file));
5453
} finally {
5554
setCurrentlyLoadingFileSuite(undefined);
5655
if (!isWorkerProcess()) {
@@ -81,18 +80,3 @@ export async function loadTestFile(file: string, rootDir: string, testErrors?: T
8180

8281
return suite;
8382
}
84-
85-
export function serializeLoadError(file: string, error: Error | any): TestError {
86-
if (error instanceof Error) {
87-
const result: TestError = filterStackTrace(error);
88-
// Babel parse errors have location.
89-
const loc = (error as any).loc;
90-
result.location = loc ? {
91-
file,
92-
line: loc.line || 0,
93-
column: loc.column || 0,
94-
} : undefined;
95-
return result;
96-
}
97-
return { value: util.inspect(error) };
98-
}

packages/playwright/src/program.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ import { filterProjects } from './runner/projectUtils';
3232
import { Runner } from './runner/runner';
3333
import * as testServer from './runner/testServer';
3434
import { runWatchModeLoop } from './runner/watchMode';
35-
import { serializeError } from './util';
35+
import { serializeLoadError } from './util';
3636

37-
import type { TestError } from '../types/testReporter';
3837
import type { ConfigCLIOverrides } from './common/ipc';
3938
import type { TraceMode } from '../types/test';
4039
import type { ReporterDescription } from '../types/test';
@@ -249,8 +248,7 @@ export async function withRunnerAndMutedWrite(configFile: string | undefined, ca
249248
gracefullyProcessExitDoNotHang(0);
250249
});
251250
} catch (e) {
252-
const error: TestError = serializeError(e);
253-
error.location = prepareErrorStack(e.stack).location;
251+
const error = serializeLoadError(e, resolveConfigLocation(configFile).resolvedConfigFile);
254252
stdoutWrite(JSON.stringify({ error }, undefined, 2), () => {
255253
gracefullyProcessExitDoNotHang(0);
256254
});

packages/playwright/src/runner/testServer.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { internalScreen } from '../reporters/base';
3333
import { InternalReporter } from '../reporters/internalReporter';
3434
import ListReporter from '../reporters/list';
3535
import { affectedTestFiles, collectAffectedTestFiles, dependenciesForTestFile } from '../transform/compilationCache';
36-
import { serializeError } from '../util';
36+
import { serializeLoadError } from '../util';
3737

3838
import type * as reporterTypes from '../../types/testReporter';
3939
import type { ConfigLocation, FullConfigInternal } from '../common/config';
@@ -43,7 +43,6 @@ import type { TestRunnerPluginRegistration } from '../plugins';
4343
import type { ReporterV2 } from '../reporters/reporterV2';
4444
import type { TraceViewerRedirectOptions, TraceViewerServerOptions } from 'playwright-core/lib/server/trace/viewer/traceViewer';
4545
import type { HttpServer, Transport } from 'playwright-core/lib/utils';
46-
import { serializeLoadError } from '../common/testLoader';
4746

4847
const originalStdoutWrite = process.stdout.write;
4948
const originalStderrWrite = process.stderr.write;
@@ -419,7 +418,7 @@ export class TestServerDispatcher implements TestServerInterface {
419418
}
420419
return { config };
421420
} catch (e) {
422-
return { config: null, error: this._configLocation.resolvedConfigFile ? serializeLoadError(this._configLocation.resolvedConfigFile, e) : serializeError(e) };
421+
return { config: null, error: serializeLoadError(e, this._configLocation.resolvedConfigFile) };
423422
}
424423
}
425424

packages/playwright/src/util.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import util from 'util';
2222
import { parseStackFrame, sanitizeForFilePath, calculateSha1, isRegExp, isString, stringifyStackFrames } from 'playwright-core/lib/utils';
2323
import { colors, debug, mime, minimatch } from 'playwright-core/lib/utilsBundle';
2424

25-
import type { Location } from './../types/testReporter';
25+
import { prepareErrorStack } from './reporters/base';
26+
27+
import type { Location, TestError } from './../types/testReporter';
2628
import type { TestInfoErrorImpl } from './common/ipc';
2729
import type { StackFrame } from '@protocol/channels';
2830
import type { RawStack } from 'playwright-core/lib/utils';
@@ -73,6 +75,25 @@ export function serializeError(error: Error | any): TestInfoErrorImpl {
7375
};
7476
}
7577

78+
export function serializeLoadError(e: Error | any, resolvedConfigFile: string | undefined): TestError {
79+
const error: TestError = serializeError(e);
80+
if (e instanceof Error && e.name === 'SyntaxError' && (e as NodeJS.ErrnoException).code === 'BABEL_PARSE_ERROR' && resolvedConfigFile) {
81+
// Babel parse errors are:
82+
// - always SyntaxError
83+
// - always have '.loc' property
84+
// - always have error code BABEL_PARSE_ERROR
85+
// @see https://github.com/babel/babel/blob/5c350eab83dd12268add44cce0eeda6c898211e3/packages/babel-parser/src/parse-error.ts#L97
86+
error.location = {
87+
file: resolvedConfigFile,
88+
line: (e as any).loc?.line,
89+
column: (e as any).loc?.column,
90+
};
91+
} else {
92+
error.location = prepareErrorStack(e.stack).location;
93+
}
94+
return error;
95+
}
96+
7697
export type Matcher = (value: string) => boolean;
7798

7899
export type TestFileFilter = {

tests/playwright-test/list-files.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import path from 'path';
1718
import { test, expect } from './playwright-test-fixtures';
1819

1920
test('should list files', async ({ runCLICommand }) => {
@@ -99,3 +100,25 @@ test('should report error', async ({ runCLICommand }) => {
99100
}
100101
});
101102
});
103+
104+
test('should report SyntaxErrors', async ({ runCLICommand }) => {
105+
const result = await runCLICommand({
106+
'playwright.config.ts': `
107+
!?NotAValidConfig
108+
`,
109+
}, 'list-files');
110+
expect(result.exitCode).toBe(0);
111+
112+
const data = JSON.parse(result.stdout);
113+
expect(data).toEqual({
114+
error: {
115+
location: {
116+
file: expect.stringContaining('playwright.config.ts'),
117+
line: 2,
118+
column: 7,
119+
},
120+
message: expect.stringContaining(`SyntaxError: ${path.join(test.info().outputDir, 'playwright.config.ts')}: Unexpected token (2:7)`),
121+
stack: expect.stringContaining(`SyntaxError: ${path.join(test.info().outputDir, 'playwright.config.ts')}: Unexpected token (2:7)`),
122+
}
123+
});
124+
});

tests/playwright-test/test-server.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,51 @@ test('should list tests with testIdAttribute', async ({ startTestServer, writeFi
145145
expect(onProject.use.testIdAttribute).toBe('testId');
146146
});
147147

148+
test('should serialize exception if config throws error', async ({ startTestServer, writeFiles }) => {
149+
await writeFiles({
150+
'playwright.config.ts': `
151+
throw new Error('Config error');
152+
`,
153+
});
154+
155+
const testServerConnection = await startTestServer();
156+
const events = await testServerConnection.listFiles({});
157+
const error = events.report.find(e => e.method === 'onError').params.error;
158+
expect(error).toEqual({
159+
message: expect.stringContaining('Config error'),
160+
location: {
161+
file: expect.stringContaining('playwright.config.ts'),
162+
line: 2,
163+
column: 13,
164+
},
165+
snippet: expect.anything(),
166+
stack: expect.stringContaining('Error: Config error'),
167+
});
168+
});
169+
170+
171+
test('should serialize exception if config has SyntaxError', async ({ startTestServer, writeFiles }) => {
172+
await writeFiles({
173+
'playwright.config.ts': `
174+
Invalid syntax
175+
`,
176+
});
177+
178+
const testServerConnection = await startTestServer();
179+
const events = await testServerConnection.listFiles({});
180+
const error = events.report.find(e => e.method === 'onError').params.error;
181+
expect(error).toEqual({
182+
message: expect.stringMatching(/SyntaxError: .*playwright\.config\.ts: Missing semicolon/),
183+
location: {
184+
file: expect.stringContaining('playwright.config.ts'),
185+
line: 2,
186+
column: 13,
187+
},
188+
snippet: expect.anything(),
189+
stack: expect.stringMatching(/SyntaxError: .*playwright\.config\.ts: Missing semicolon/),
190+
});
191+
});
192+
148193
test('stdio interception', async ({ startTestServer, writeFiles }) => {
149194
const testServerConnection = await startTestServer();
150195
await testServerConnection.initialize({ interceptStdio: true });

0 commit comments

Comments
 (0)