chore: create artifacts dir in the test runner (#26594)

This commit is contained in:
Pavel Feldman 2023-08-21 19:41:10 -07:00 committed by GitHub
parent fe7b956c3b
commit 1ceaa923ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 27 additions and 47 deletions

View file

@ -62,6 +62,7 @@ export type WorkerInitParams = {
repeatEachIndex: number;
projectId: string;
config: SerializedConfig;
artifactsDir: string;
};
export type TestBeginPayload = {

View file

@ -18,12 +18,11 @@ import * as fs from 'fs';
import * as path from 'path';
import type { APIRequestContext, BrowserContext, Browser, BrowserContextOptions, LaunchOptions, Page, Tracing, Video } from 'playwright-core';
import * as playwrightLibrary from 'playwright-core';
import { createGuid, debugMode, addInternalStackPrefix, mergeTraceFiles, saveTraceFile, removeFolders, isString, asLocator, jsonStringifyForceASCII } from 'playwright-core/lib/utils';
import { createGuid, debugMode, addInternalStackPrefix, mergeTraceFiles, saveTraceFile, isString, asLocator, jsonStringifyForceASCII } from 'playwright-core/lib/utils';
import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, TraceMode, VideoMode } from '../types/test';
import type { TestInfoImpl } from './worker/testInfo';
import { rootTestType } from './common/testType';
import type { ContextReuseMode } from './common/config';
import { artifactsFolderName } from './isomorphic/folders';
import type { ClientInstrumentation, ClientInstrumentationListener } from '../../playwright-core/src/client/clientInstrumentation';
import type { ParsedStackTrace } from '../../playwright-core/src/utils/stackTrace';
import { currentTestInfo } from './common/globals';
@ -55,7 +54,7 @@ type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
};
type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
_browserOptions: LaunchOptions;
_artifactsDir: () => string;
_artifactsDir: string;
};
const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
@ -78,17 +77,8 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
video: ['off', { scope: 'worker', option: true }],
trace: ['off', { scope: 'worker', option: true }],
_artifactsDir: [async ({}, use, workerInfo) => {
let dir: string | undefined;
await use(() => {
if (!dir) {
dir = path.join(workerInfo.project.outputDir, artifactsFolderName(workerInfo.workerIndex));
fs.mkdirSync(dir, { recursive: true });
}
return dir;
});
if (dir)
await removeFolders([dir]);
_artifactsDir: [async ({}, use) => {
await use(process.env.TEST_ARTIFACTS_DIR!);
}, { scope: 'worker', _title: 'playwright configuration' } as any],
_browserOptions: [async ({ playwright, headless, channel, launchOptions, connectOptions, _artifactsDir }, use) => {
@ -100,7 +90,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
options.headless = headless;
if (channel !== undefined)
options.channel = channel;
options.tracesDir = path.join(_artifactsDir(), 'traces');
options.tracesDir = path.join(_artifactsDir, 'traces');
for (const browserType of [playwright.chromium, playwright.firefox, playwright.webkit]) {
(browserType as any)._defaultLaunchOptions = options;
@ -256,7 +246,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
(browserType as any)._defaultContextNavigationTimeout = navigationTimeout || 0;
}
(playwright.request as any)._defaultContextOptions = { ..._combinedContextOptions };
(playwright.request as any)._defaultContextOptions.tracesDir = path.join(_artifactsDir(), 'traces');
(playwright.request as any)._defaultContextOptions.tracesDir = path.join(_artifactsDir, 'traces');
(playwright.request as any)._defaultContextOptions.timeout = actionTimeout || 0;
await use();
(playwright.request as any)._defaultContextOptions = undefined;
@ -268,7 +258,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
}, { auto: 'all-hooks-included', _title: 'context configuration' } as any],
_setupArtifacts: [async ({ playwright, _artifactsDir, trace, screenshot }, use, testInfo) => {
const artifactsRecorder = new ArtifactsRecorder(playwright, _artifactsDir(), trace, screenshot);
const artifactsRecorder = new ArtifactsRecorder(playwright, _artifactsDir, trace, screenshot);
await artifactsRecorder.willStartTest(testInfo as TestInfoImpl);
const csiListener: ClientInstrumentationListener = {
onApiCallBegin: (apiName: string, params: Record<string, any>, stackTrace: ParsedStackTrace | null, wallTime: number, userData: any) => {
@ -337,7 +327,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
}
const videoOptions: BrowserContextOptions = captureVideo ? {
recordVideo: {
dir: _artifactsDir(),
dir: _artifactsDir,
size: typeof video === 'string' ? undefined : video.size,
}
} : {};

View file

@ -464,7 +464,9 @@ export class Dispatcher {
}
_createWorker(testGroup: TestGroup, parallelIndex: number, loaderData: SerializedConfig) {
const worker = new WorkerHost(testGroup, parallelIndex, loaderData, this._extraEnvByProjectId.get(testGroup.projectId) || {});
const projectConfig = this._config.projects.find(p => p.id === testGroup.projectId)!;
const outputDir = projectConfig.project.outputDir;
const worker = new WorkerHost(testGroup, parallelIndex, loaderData, this._extraEnvByProjectId.get(testGroup.projectId) || {}, outputDir);
const handleOutput = (params: TestOutputPayload) => {
const chunk = chunkFromParams(params);
if (worker.didFail()) {

View file

@ -14,9 +14,13 @@
* limitations under the License.
*/
import fs from 'fs';
import path from 'path';
import type { TestGroup } from './testGroups';
import type { RunPayload, SerializedConfig, WorkerInitParams } from '../common/ipc';
import { ProcessHost } from './processHost';
import { artifactsFolderName } from '../isomorphic/folders';
import { removeFolders } from 'playwright-core/lib/utils';
let lastWorkerIndex = 0;
@ -27,7 +31,7 @@ export class WorkerHost extends ProcessHost {
currentTestId: string | null = null;
private _params: WorkerInitParams;
constructor(testGroup: TestGroup, parallelIndex: number, config: SerializedConfig, extraEnv: Record<string, string | undefined>) {
constructor(testGroup: TestGroup, parallelIndex: number, config: SerializedConfig, extraEnv: Record<string, string | undefined>, outputDir: string) {
const workerIndex = lastWorkerIndex++;
super(require.resolve('../worker/workerMain.js'), `worker-${workerIndex}`, {
...extraEnv,
@ -44,13 +48,20 @@ export class WorkerHost extends ProcessHost {
repeatEachIndex: testGroup.repeatEachIndex,
projectId: testGroup.projectId,
config,
artifactsDir: path.join(outputDir, artifactsFolderName(workerIndex))
};
}
async start() {
await fs.promises.mkdir(this._params.artifactsDir, { recursive: true });
await this.startRunner(this._params, false);
}
override async stop(didFail?: boolean) {
await super.stop(didFail);
await removeFolders([this._params.artifactsDir]);
}
runTestGroup(runPayload: RunPayload) {
this.sendMessageNoReply({ method: 'runTestGroup', params: runPayload });
}

View file

@ -63,6 +63,7 @@ export class WorkerMain extends ProcessRunner {
super();
process.env.TEST_WORKER_INDEX = String(params.workerIndex);
process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex);
process.env.TEST_ARTIFACTS_DIR = params.artifactsDir;
setIsWorkerProcess();
this._params = params;

View file

@ -801,28 +801,3 @@ test('should use actionTimeout for APIRequestContext', async ({ runInlineTest, s
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(3);
});
test('should cancel apiRequests if test will timeout', async ({ runInlineTest, server }) => {
server.setRoute('/stall', (req, res) => {});
const result = await runInlineTest({
'playwright.config.js': `
module.exports = {
timeout: 1000,
use: {
baseURL: '${server.PREFIX}',
}
};
`,
'a.test.ts': `
import { test, expect } from '@playwright/test';
test('pass', async ({ request }) => {
await request.get('/stall')
});
`,
}, { workers: 1 });
expect(result.exitCode).toBe(1);
expect(result.passed).toBe(0);
expect(result.failed).toBe(1);
expect(result.output).toContain('apiRequestContext.get: Request context disposed.');
expect(result.output).toContain('Test timeout of 1000ms exceeded.');
});

View file

@ -424,7 +424,7 @@ for (const mode of ['off', 'retain-on-failure', 'on-first-retry', 'on-all-retrie
// Override locale fixture to check in teardown that no temporary trace zip was created.
locale: [async ({ locale, _artifactsDir }, use) => {
await use(locale);
const entries = fs.readdirSync(_artifactsDir());
const entries = fs.readdirSync(_artifactsDir);
expect(entries.filter(e => e.endsWith('.zip'))).toEqual([]);
}, { option: true }],
});
@ -449,7 +449,7 @@ for (const mode of ['off', 'retain-on-failure', 'on-first-retry', 'on-all-retrie
// Override locale fixture to check in teardown that no temporary trace zip was created.
locale: [async ({ locale, _artifactsDir }, use) => {
await use(locale);
const entries = fs.readdirSync(_artifactsDir());
const entries = fs.readdirSync(_artifactsDir);
expect(entries.filter(e => e.endsWith('.zip'))).toEqual([]);
}, { option: true }],
});