fix(trace-viewer): show source files in local version (#9732)
This commit is contained in:
parent
c890510d86
commit
f08c22b467
|
|
@ -27,17 +27,16 @@ import { ProgressController } from '../../progress';
|
|||
|
||||
export async function showTraceViewer(traceUrl: string, browserName: string, headless = false, port?: number): Promise<BrowserContext | undefined> {
|
||||
const server = new HttpServer();
|
||||
server.routePath('/file', (request, response) => {
|
||||
try {
|
||||
const path = new URL('http://localhost' + request.url!).searchParams.get('path')!;
|
||||
return server.serveFile(response, path);
|
||||
} catch (e) {
|
||||
return false;
|
||||
server.routePrefix('/trace', (request, response) => {
|
||||
const url = new URL('http://localhost' + request.url!);
|
||||
const relativePath = url.pathname.slice('/trace'.length);
|
||||
if (relativePath.startsWith('/file')) {
|
||||
try {
|
||||
return server.serveFile(response, url.searchParams.get('path')!);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.routePrefix('/', (request, response) => {
|
||||
const relativePath = new URL('http://localhost' + request.url!).pathname.slice('/trace'.length);
|
||||
const absolutePath = path.join(__dirname, '..', '..', '..', 'webpack', 'traceViewer', ...relativePath.split('/'));
|
||||
return server.serveFile(response, absolutePath);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async function loadTrace(trace: string, clientId: string, progress: (done: numbe
|
|||
if (entry)
|
||||
return entry.traceModel;
|
||||
const traceModel = new TraceModel();
|
||||
let url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `/file?path=${trace}`;
|
||||
let url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `file?path=${trace}`;
|
||||
// Dropbox does not support cors.
|
||||
if (url.startsWith('https://www.dropbox.com/'))
|
||||
url = 'https://dl.dropboxusercontent.com/' + url.substring('https://www.dropbox.com/'.length);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export const SourceTab: React.FunctionComponent<{
|
|||
} else {
|
||||
const filePath = stackInfo.frames[selectedFrame].file;
|
||||
if (!stackInfo.fileContent.has(filePath))
|
||||
stackInfo.fileContent.set(filePath, await fetch(`/file?${filePath}`).then(response => response.text()).catch(e => `<Unable to read "${filePath}">`));
|
||||
stackInfo.fileContent.set(filePath, await fetch(`file?path=${filePath}`).then(response => response.text()).catch(e => `<Unable to read "${filePath}">`));
|
||||
value = stackInfo.fileContent.get(filePath)!;
|
||||
}
|
||||
return value;
|
||||
|
|
|
|||
|
|
@ -97,6 +97,15 @@ export const Workbench: React.FunctionComponent<{
|
|||
const consoleCount = errors + warnings;
|
||||
const networkCount = selectedAction ? modelUtil.resourcesForAction(selectedAction).length : 0;
|
||||
|
||||
const tabs = [
|
||||
{ id: 'logs', title: 'Call', count: 0, render: () => <CallTab action={selectedAction} /> },
|
||||
{ id: 'console', title: 'Console', count: consoleCount, render: () => <ConsoleTab action={selectedAction} /> },
|
||||
{ id: 'network', title: 'Network', count: networkCount, render: () => <NetworkTab action={selectedAction} /> },
|
||||
];
|
||||
|
||||
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')
|
||||
tabs.push({ id: 'source', title: 'Source', count: 0, render: () => <SourceTab action={selectedAction} /> });
|
||||
|
||||
return <div className='vbox workbench'
|
||||
onDragOver={event => { event.preventDefault(); }}
|
||||
onDrop={event => handleDropEvent(event)}>
|
||||
|
|
@ -118,12 +127,7 @@ export const Workbench: React.FunctionComponent<{
|
|||
<SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
|
||||
<SplitView sidebarSize={300} orientation='horizontal'>
|
||||
<SnapshotTab action={selectedAction} defaultSnapshotSize={defaultSnapshotSize} />
|
||||
<TabbedPane tabs={[
|
||||
{ id: 'logs', title: 'Call', count: 0, render: () => <CallTab action={selectedAction} /> },
|
||||
{ id: 'console', title: 'Console', count: consoleCount, render: () => <ConsoleTab action={selectedAction} /> },
|
||||
{ id: 'network', title: 'Network', count: networkCount, render: () => <NetworkTab action={selectedAction} /> },
|
||||
{ id: 'source', title: 'Source', count: 0, render: () => <SourceTab action={selectedAction} /> },
|
||||
]} selectedTab={selectedTab} setSelectedTab={setSelectedTab}/>
|
||||
<TabbedPane tabs={tabs} selectedTab={selectedTab} setSelectedTab={setSelectedTab}/>
|
||||
</SplitView>
|
||||
<ActionList
|
||||
actions={contextEntry.actions}
|
||||
|
|
|
|||
|
|
@ -159,14 +159,7 @@ export async function showHTMLReport(reportFolder: string | undefined) {
|
|||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
const server = new HttpServer();
|
||||
server.routePrefix('/', (request, response) => {
|
||||
let relativePath = new URL('http://localhost' + request.url).pathname;
|
||||
if (relativePath === '/')
|
||||
relativePath = '/index.html';
|
||||
const absolutePath = path.join(folder, ...relativePath.split('/'));
|
||||
return server.serveFile(response, absolutePath);
|
||||
});
|
||||
const server = startHtmlReportServer(folder);
|
||||
const url = await server.start(9323);
|
||||
console.log('');
|
||||
console.log(colors.cyan(` Serving HTML report at ${url}. Press Ctrl+C to quit.`));
|
||||
|
|
@ -175,6 +168,26 @@ export async function showHTMLReport(reportFolder: string | undefined) {
|
|||
await new Promise(() => {});
|
||||
}
|
||||
|
||||
export function startHtmlReportServer(folder: string): HttpServer {
|
||||
const server = new HttpServer();
|
||||
server.routePrefix('/', (request, response) => {
|
||||
let relativePath = new URL('http://localhost' + request.url).pathname;
|
||||
if (relativePath.startsWith('/trace/file')) {
|
||||
const url = new URL('http://localhost' + request.url!);
|
||||
try {
|
||||
return server.serveFile(response, url.searchParams.get('path')!);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (relativePath === '/')
|
||||
relativePath = '/index.html';
|
||||
const absolutePath = path.join(folder, ...relativePath.split('/'));
|
||||
return server.serveFile(response, absolutePath);
|
||||
});
|
||||
return server;
|
||||
}
|
||||
|
||||
class HtmlBuilder {
|
||||
private _reportFolder: string;
|
||||
private _tests = new Map<string, JsonTestCase>();
|
||||
|
|
|
|||
|
|
@ -15,22 +15,16 @@
|
|||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { test as baseTest, expect } from './playwright-test-fixtures';
|
||||
import { HttpServer } from 'playwright-core/lib/utils/httpServer';
|
||||
import { startHtmlReportServer } from '../../packages/playwright-test/lib/reporters/html';
|
||||
|
||||
const test = baseTest.extend<{ showReport: () => Promise<void> }>({
|
||||
showReport: async ({ page }, use, testInfo) => {
|
||||
const server = new HttpServer();
|
||||
let server: HttpServer;
|
||||
await use(async () => {
|
||||
const reportFolder = testInfo.outputPath('playwright-report');
|
||||
server.routePrefix('/', (request, response) => {
|
||||
let relativePath = new URL('http://localhost' + request.url).pathname;
|
||||
if (relativePath === '/')
|
||||
relativePath = '/index.html';
|
||||
const absolutePath = path.join(reportFolder, ...relativePath.split('/'));
|
||||
return server.serveFile(response, absolutePath);
|
||||
});
|
||||
server = startHtmlReportServer(reportFolder);
|
||||
const location = await server.start();
|
||||
await page.goto(location);
|
||||
});
|
||||
|
|
@ -263,3 +257,37 @@ test('should highlight error', async ({ runInlineTest, page, showReport }) => {
|
|||
await page.click('text=fails');
|
||||
await expect(page.locator('.error-message span:has-text("received")').nth(1)).toHaveCSS('color', 'rgb(204, 0, 0)');
|
||||
});
|
||||
|
||||
test('should show trace source', async ({ runInlineTest, page, showReport }) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.js': `
|
||||
module.exports = { use: { trace: 'on' } };
|
||||
`,
|
||||
'a.test.js': `
|
||||
const { test } = pwt;
|
||||
test('passes', async ({ page }) => {
|
||||
await page.evaluate('2 + 2');
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'dot,html' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
|
||||
await showReport();
|
||||
await page.click('text=passes');
|
||||
await page.click('img');
|
||||
await page.click('.action-title >> text=page.evaluate');
|
||||
await page.click('text=Source');
|
||||
|
||||
await expect(page.locator('.source-line')).toContainText([
|
||||
/const.*pwt;/,
|
||||
/page\.evaluate/
|
||||
]);
|
||||
await expect(page.locator('.source-line-running')).toContainText('page.evaluate');
|
||||
|
||||
await expect(page.locator('.stack-trace-frame')).toContainText([
|
||||
/a.test.js:[\d]+/,
|
||||
/fixtures.[tj]s:[\d]+/,
|
||||
]);
|
||||
await expect(page.locator('.stack-trace-frame.selected')).toContainText('a.test.js');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -277,8 +277,6 @@ test('should have network requests', async ({ showTraceViewer }) => {
|
|||
});
|
||||
|
||||
test('should capture iframe', async ({ page, server, browserName, runAndTrace }) => {
|
||||
test.skip(browserName === 'firefox');
|
||||
|
||||
await page.route('**/empty.html', route => {
|
||||
route.fulfill({
|
||||
body: '<iframe src="iframe.html"></iframe>',
|
||||
|
|
@ -394,8 +392,6 @@ test('should work with adopted style sheets and replace/replaceSync', async ({ p
|
|||
});
|
||||
|
||||
test('should restore scroll positions', async ({ page, runAndTrace, browserName }) => {
|
||||
test.skip(browserName === 'firefox');
|
||||
|
||||
const traceViewer = await runAndTrace(async () => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
|
|
@ -428,8 +424,6 @@ test('should restore scroll positions', async ({ page, runAndTrace, browserName
|
|||
});
|
||||
|
||||
test('should work with meta CSP', async ({ page, runAndTrace, browserName }) => {
|
||||
test.skip(browserName === 'firefox');
|
||||
|
||||
const traceViewer = await runAndTrace(async () => {
|
||||
await page.setContent(`
|
||||
<head>
|
||||
|
|
@ -455,8 +449,6 @@ test('should work with meta CSP', async ({ page, runAndTrace, browserName }) =>
|
|||
});
|
||||
|
||||
test('should handle multiple headers', async ({ page, server, runAndTrace, browserName }) => {
|
||||
test.skip(browserName === 'firefox');
|
||||
|
||||
server.setRoute('/foo.css', (req, res) => {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('vary', ['accepts-encoding', 'accepts-encoding']);
|
||||
|
|
@ -499,8 +491,6 @@ test('should handle src=blob', async ({ page, server, runAndTrace, browserName }
|
|||
});
|
||||
|
||||
test('should highlight target elements', async ({ page, runAndTrace, browserName }) => {
|
||||
test.skip(browserName === 'firefox');
|
||||
|
||||
const traceViewer = await runAndTrace(async () => {
|
||||
await page.setContent(`
|
||||
<div>hello</div>
|
||||
|
|
@ -537,3 +527,17 @@ test('should highlight target elements', async ({ page, runAndTrace, browserName
|
|||
const frameExpect2 = await traceViewer.snapshotFrame('expect.toHaveText', 1);
|
||||
await expect(frameExpect2.locator('[__playwright_target__]')).toHaveText(['hello', 'world']);
|
||||
});
|
||||
|
||||
test('should show action source', async ({ showTraceViewer }) => {
|
||||
const traceViewer = await showTraceViewer(traceFile);
|
||||
await traceViewer.selectAction('page.click');
|
||||
const page = traceViewer.page;
|
||||
|
||||
await page.click('text=Source');
|
||||
await expect(page.locator('.source-line')).toContainText([
|
||||
/async.*function.*doClick/,
|
||||
/page\.click/
|
||||
]);
|
||||
await expect(page.locator('.source-line-running')).toContainText('page.click');
|
||||
await expect(page.locator('.stack-trace-frame.selected')).toHaveText(/doClick.*trace-viewer\.spec\.ts:[\d]+/);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue