Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(trace-viewer): test harness for embedded trace viewer #514

Merged
Merged
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
2 changes: 2 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const config: PlaywrightTestConfig<WorkerOptions> = {
name: 'default',
use: {
showBrowser: false,
traceViewerMode: 'spawn',
}
},
{
Expand All @@ -46,6 +47,7 @@ const config: PlaywrightTestConfig<WorkerOptions> = {
use: {
overridePlaywrightVersion: 1.43,
showBrowser: false,
traceViewerMode: 'spawn',
}
},
{
Expand Down
32 changes: 29 additions & 3 deletions tests/mock/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -960,9 +960,16 @@ export class VSCode {
this.window.createWebviewPanel = (viewType: string) => {
const { webview, pagePromise } = this._createWebviewAndPage();
const didDispose = new EventEmitter<void>();
const didChangeViewState = new EventEmitter<{ webviewPanel: any }>();
const panel: any = {};
panel.onDidDispose = didDispose.event;
panel.onDidChangeViewState = didChangeViewState.event;
panel.webview = webview;
panel.visible = true;
webview.onDidChangeVisibility(visibilityState => {
panel.visible = visibilityState === 'visible';
didChangeViewState.fire({ webviewPanel: panel });
});
pagePromise.then(webview => {
webview.on('close', () => {
panel.dispose();
Expand Down Expand Up @@ -1067,6 +1074,7 @@ export class VSCode {
'playwright.env': {},
'playwright.reuseBrowser': false,
'playwright.showTrace': false,
'playwright.embeddedTraceViewer': false,
'workbench.colorTheme': 'Dark Modern',
};
this.workspace.getConfiguration = scope => {
Expand Down Expand Up @@ -1108,12 +1116,21 @@ export class VSCode {
return webviews ? [...webviews] : [];
}

async changeVisibility(webview: Page, state: 'visible' | 'hidden') {
if (state === 'visible')
await webview.goto('http://localhost');
else
await webview.goto('http://localhost/hidden');
}

private _createWebviewAndPage() {
let initializedPage: Page | undefined = undefined;
const webview: any = {};
webview.asWebviewUri = uri => path.relative(this.context.extensionUri.fsPath, uri.fsPath).replace(/\\/g, '/');
const eventEmitter = new EventEmitter<any>();
webview.onDidReceiveMessage = eventEmitter.event;
const didReceiveMessage = new EventEmitter<any>();
const didChangeVisibility = new EventEmitter<'visible' | 'hidden'>();
webview.onDidReceiveMessage = didReceiveMessage.event;
webview.onDidChangeVisibility = didChangeVisibility.event;
webview.cspSource = 'http://localhost/';
const pendingMessages: any[] = [];
webview.postMessage = (data: any) => {
Expand All @@ -1136,17 +1153,26 @@ export class VSCode {
route.continue();
} else if (url === 'http://localhost/') {
route.fulfill({ body: webview.html });
} else if (url === 'http://localhost/hidden') {
route.fulfill({ body: 'hidden webview' });
} else {
const suffix = url.substring('http://localhost/'.length);
const buffer = fs.readFileSync(path.join(this.context.extensionUri.fsPath, suffix));
route.fulfill({ body: buffer });
}
});
page.on('load', () => {
const url = page.url();
if (url === 'http://localhost/')
didChangeVisibility.fire('visible');
else if (url === 'http://localhost/hidden')
didChangeVisibility.fire('hidden');
});
await page.addInitScript(() => {
globalThis.acquireVsCodeApi = () => globalThis;
});
await page.goto('http://localhost');
await page.exposeFunction('postMessage', (data: any) => eventEmitter.fire(data));
await page.exposeFunction('postMessage', (data: any) => didReceiveMessage.fire(data));
this.context.subscriptions.push({
dispose: () => {
context.close().catch(() => {});
Expand Down
34 changes: 14 additions & 20 deletions tests/spawn-trace-viewer.spec.ts → tests/trace-viewer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,9 @@

import { enableConfigs, expect, selectConfig, selectTestItem, test, traceViewerInfo } from './utils';

test.beforeEach(({ showBrowser }) => {
test.skip(showBrowser);
// prevents spawn trace viewer process from opening in browser
process.env.PWTEST_UNDER_TEST = '1';
});

test.use({ showTrace: true, envRemoteName: 'ssh-remote' });
test.skip(({ traceViewerMode }) => !traceViewerMode);

test('@smoke should open trace viewer', async ({ activate }) => {
test('@smoke should open trace viewer', async ({ activate, traceViewerMode }) => {
const { vscode, testController } = await activate({
'playwright.config.js': `module.exports = { testDir: 'tests' }`,
'tests/test.spec.ts': `
Expand All @@ -38,12 +32,12 @@ test('@smoke should open trace viewer', async ({ activate }) => {
selectTestItem(testController.findTestItems(/pass/)[0]);

await expect.poll(() => traceViewerInfo(vscode)).toMatchObject({
type: 'spawn',
type: traceViewerMode,
traceFile: expect.stringContaining('pass'),
});
});

test('should change opened file in trace viewer', async ({ activate }) => {
test('should change opened file in trace viewer', async ({ activate, traceViewerMode }) => {
const { vscode, testController } = await activate({
'playwright.config.js': `module.exports = { testDir: 'tests' }`,
'tests/test.spec.ts': `
Expand All @@ -59,14 +53,14 @@ test('should change opened file in trace viewer', async ({ activate }) => {
selectTestItem(testController.findTestItems(/one/)[0]);

await expect.poll(() => traceViewerInfo(vscode)).toMatchObject({
type: 'spawn',
type: traceViewerMode,
traceFile: expect.stringContaining('one'),
});

selectTestItem(testController.findTestItems(/two/)[0]);

await expect.poll(() => traceViewerInfo(vscode)).toMatchObject({
type: 'spawn',
type: traceViewerMode,
traceFile: expect.stringContaining('two'),
});
});
Expand All @@ -86,7 +80,7 @@ test('should not open trace viewer if test did not run', async ({ activate }) =>
await expect.poll(() => traceViewerInfo(vscode)).toBeUndefined();
});

test('should refresh trace viewer while test is running', async ({ activate, createLatch }) => {
test('should refresh trace viewer while test is running', async ({ activate, createLatch, traceViewerMode }) => {
const latch = createLatch();

const { vscode, testController } = await activate({
Expand All @@ -102,20 +96,20 @@ test('should refresh trace viewer while test is running', async ({ activate, cre

const testRunPromise = testController.run();
await expect.poll(() => traceViewerInfo(vscode)).toMatchObject({
type: 'spawn',
type: traceViewerMode,
traceFile: expect.stringMatching(/\.json$/),
});

latch.open();
await testRunPromise;

await expect.poll(() => traceViewerInfo(vscode)).toMatchObject({
type: 'spawn',
type: traceViewerMode,
traceFile: expect.stringMatching(/\.zip$/),
});
});

test('should close trace viewer if test configs refreshed', async ({ activate }) => {
test('should close trace viewer if test configs refreshed', async ({ activate, traceViewerMode }) => {
const { vscode, testController } = await activate({
'playwright.config.js': `module.exports = { testDir: 'tests' }`,
'tests/test.spec.ts': `
Expand All @@ -129,7 +123,7 @@ test('should close trace viewer if test configs refreshed', async ({ activate })
selectTestItem(testController.findTestItems(/pass/)[0]);

await expect.poll(() => traceViewerInfo(vscode)).toMatchObject({
type: 'spawn',
type: traceViewerMode,
traceFile: expect.stringContaining('pass'),
});

Expand All @@ -138,7 +132,7 @@ test('should close trace viewer if test configs refreshed', async ({ activate })
await expect.poll(() => traceViewerInfo(vscode)).toBeUndefined();
});

test('should open new trace viewer when another test config is selected', async ({ activate }) => {
test('should open new trace viewer when another test config is selected', async ({ activate, traceViewerMode }) => {
const { vscode, testController } = await activate({
'playwright1.config.js': `module.exports = { testDir: 'tests1' }`,
'playwright2.config.js': `module.exports = { testDir: 'tests2' }`,
Expand All @@ -162,7 +156,7 @@ test('should open new trace viewer when another test config is selected', async
selectTestItem(testItems[0]);

await expect.poll(() => traceViewerInfo(vscode)).toMatchObject({
type: 'spawn',
type: traceViewerMode,
serverUrlPrefix: expect.stringContaining('http'),
testConfigFile: expect.stringContaining('playwright1.config.js'),
});
Expand All @@ -177,7 +171,7 @@ test('should open new trace viewer when another test config is selected', async
selectTestItem(testItems[0]);

await expect.poll(() => traceViewerInfo(vscode)).toMatchObject({
type: 'spawn',
type: traceViewerMode,
serverUrlPrefix: expect.stringContaining('http'),
testConfigFile: expect.stringContaining('playwright2.config.js'),
});
Expand Down
19 changes: 11 additions & 8 deletions tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,14 @@ type Latch = {
type TestFixtures = {
vscode: VSCode,
activate: (files: { [key: string]: string }, options?: { rootDir?: string, workspaceFolders?: [string, any][], env?: Record<string, any> }) => Promise<ActivateResult>;
showTrace: boolean;
envRemoteName?: string;
createLatch: () => Latch;
};

export type WorkerOptions = {
overridePlaywrightVersion?: number;
showBrowser: boolean;
vsCodeVersion: number;
traceViewerMode?: 'spawn' | 'embedded';
};

// Make sure connect tests work with the locally-rolled version.
Expand Down Expand Up @@ -125,14 +124,13 @@ export const test = baseTest.extend<TestFixtures, WorkerOptions>({
overridePlaywrightVersion: [undefined, { option: true, scope: 'worker' }],
showBrowser: [false, { option: true, scope: 'worker' }],
vsCodeVersion: [1.86, { option: true, scope: 'worker' }],
showTrace: false,
envRemoteName: undefined,
traceViewerMode: [undefined, { option: true, scope: 'worker' }],

vscode: async ({ browser, vsCodeVersion }, use) => {
await use(new VSCode(vsCodeVersion, path.resolve(__dirname, '..'), browser));
},

activate: async ({ vscode, showBrowser, showTrace, envRemoteName, overridePlaywrightVersion }, use, testInfo) => {
activate: async ({ vscode, showBrowser, overridePlaywrightVersion, traceViewerMode }, use, testInfo) => {
const instances: VSCode[] = [];
await use(async (files: { [key: string]: string }, options?: { rootDir?: string, workspaceFolders?: [string, any][], env?: Record<string, any> }) => {
if (options?.workspaceFolders) {
Expand All @@ -147,10 +145,15 @@ export const test = baseTest.extend<TestFixtures, WorkerOptions>({
configuration.update('env', options.env);
if (showBrowser)
configuration.update('reuseBrowser', true);
if (showTrace)
if (traceViewerMode) {
configuration.update('showTrace', true);
if (envRemoteName)
vscode.env.remoteName = envRemoteName;

// prevents spawn trace viewer process from opening app and browser
vscode.env.remoteName = 'ssh-remote';
process.env.PWTEST_UNDER_TEST = '1';
}
if (traceViewerMode === 'embedded')
configuration.update('embeddedTraceViewer', true);

const extension = new Extension(vscode, vscode.context);
if (overridePlaywrightVersion)
Expand Down