fix(ui): print config and global setup errors (#30531)

Fixes: https://github.com/microsoft/playwright/issues/30513
This commit is contained in:
Pavel Feldman 2024-04-24 18:54:48 -07:00 committed by GitHub
parent 59689c9c97
commit ff3d3ae8f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 60 additions and 26 deletions

View File

@ -124,9 +124,9 @@ export type JsonEvent = {
};
type TeleReporterReceiverOptions = {
mergeProjects: boolean;
mergeTestCases: boolean;
resolvePath: (rootDir: string, relativePath: string) => string;
mergeProjects?: boolean;
mergeTestCases?: boolean;
resolvePath?: (rootDir: string, relativePath: string) => string;
configOverrides?: Pick<reporterTypes.FullConfig, 'configFile' | 'quiet' | 'reportSlowTests' | 'reporter'>;
clearPreviousResultsWhenTestBegins?: boolean;
};
@ -140,7 +140,7 @@ export class TeleReporterReceiver {
private _rootDir!: string;
private _config!: reporterTypes.FullConfig;
constructor(reporter: Partial<ReporterV2>, options: TeleReporterReceiverOptions) {
constructor(reporter: Partial<ReporterV2>, options: TeleReporterReceiverOptions = {}) {
this._rootSuite = new TeleSuite('', 'root');
this._options = options;
this._reporter = reporter;
@ -388,7 +388,7 @@ export class TeleReporterReceiver {
private _absolutePath(relativePath?: string): string | undefined {
if (relativePath === undefined)
return;
return this._options.resolvePath(this._rootDir, relativePath);
return this._options.resolvePath ? this._options.resolvePath(this._rootDir, relativePath) : this._rootDir + '/' + relativePath;
}
}

View File

@ -8,4 +8,5 @@
../util.ts
../utilsBundle.ts
../isomorphic/folders.ts
../isomorphic/teleReceiver.ts
../fsWatcher.ts

View File

@ -39,6 +39,7 @@ import type { TraceViewerRedirectOptions, TraceViewerServerOptions } from 'playw
import type { TestRunnerPluginRegistration } from '../plugins';
import { serializeError } from '../util';
import { cacheDir } from '../transform/compilationCache';
import { baseFullConfig } from '../isomorphic/teleReceiver';
const originalStdoutWrite = process.stdout.write;
const originalStderrWrite = process.stderr.write;
@ -147,7 +148,10 @@ class TestServerDispatcher implements TestServerInterface {
const { reporter, report } = await this._collectingReporter();
const { config, error } = await this._loadConfig();
if (!config) {
// Produce dummy config when it has an error.
reporter.onConfigure(baseFullConfig);
reporter.onError(error!);
await reporter.onExit();
return { status: 'failed', report };
}

View File

@ -115,11 +115,7 @@ export class TeleSuiteUpdater {
this._options.onUpdate();
},
onError: (error: reporterTypes.TestError) => {
this.loadErrors.push(error);
this._options.onError?.(error);
this._options.onUpdate();
},
onError: (error: reporterTypes.TestError) => this._handleOnError(error),
printsToStdio: () => {
return false;
@ -133,6 +129,17 @@ export class TeleSuiteUpdater {
};
}
processGlobalReport(report: any[]) {
const receiver = new TeleReporterReceiver({
onConfigure: (c: reporterTypes.FullConfig) => {
this.config = c;
},
onError: (error: reporterTypes.TestError) => this._handleOnError(error)
});
for (const message of report)
receiver.dispatch(message);
}
processListReport(report: any[]) {
// Save test results and reset all projects, the results will be restored after
// new project structure is built.
@ -150,6 +157,12 @@ export class TeleSuiteUpdater {
this._receiver.dispatch(message)?.catch(() => {});
}
private _handleOnError(error: reporterTypes.TestError) {
this.loadErrors.push(error);
this._options.onError?.(error);
this._options.onUpdate();
}
asModel(): TestModel {
return {
rootSuite: this.rootSuite || new TeleSuite('', 'root'),

View File

@ -172,24 +172,29 @@ export const UIModeView: React.FC<{}> = ({
setIsLoading(true);
setWatchedTreeIds({ value: new Set() });
(async () => {
await testServerConnection.initialize({
interceptStdio: true,
watchTestDirs: true
});
const { status } = await testServerConnection.runGlobalSetup({});
if (status !== 'passed')
return;
const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args });
teleSuiteUpdater.processListReport(result.report);
try {
await testServerConnection.initialize({
interceptStdio: true,
watchTestDirs: true
});
const { status, report } = await testServerConnection.runGlobalSetup({});
teleSuiteUpdater.processGlobalReport(report);
if (status !== 'passed')
return;
testServerConnection.onListChanged(updateList);
testServerConnection.onReport(params => {
teleSuiteUpdater.processTestReportEvent(params);
});
setIsLoading(false);
const result = await testServerConnection.listTests({ projects: queryParams.projects, locations: queryParams.args });
teleSuiteUpdater.processListReport(result.report);
const { hasBrowsers } = await testServerConnection.checkBrowsers({});
setHasBrowsers(hasBrowsers);
testServerConnection.onListChanged(updateList);
testServerConnection.onReport(params => {
teleSuiteUpdater.processTestReportEvent(params);
});
const { hasBrowsers } = await testServerConnection.checkBrowsers({});
setHasBrowsers(hasBrowsers);
} finally {
setIsLoading(false);
}
})();
return () => {
clearTimeout(throttleTimer);

View File

@ -84,6 +84,17 @@ test('should teardown on sigint', async ({ runUITest, nodeVersion }) => {
]);
});
test('should show errors in config', async ({ runUITest }) => {
const { page } = await runUITest({
'playwright.config.ts': `
import { defineConfig, devices } from '@playwright/test';
throw new Error("URL is empty")
`,
});
await page.getByText('playwright.config.ts').click();
await expect(page.getByText('Error: URL is empty')).toBeInViewport();
});
const testsWithSetup = {
'playwright.config.ts': `
import { defineConfig } from '@playwright/test';