mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-11 12:33:45 +03:00
chore: use test project output dir when showing live trace (#21735)
This commit is contained in:
parent
eac910db82
commit
e066ca0713
@ -43,6 +43,7 @@ export const ActionList: React.FC<ActionListProps> = ({
|
||||
revealConsole = () => {},
|
||||
}) => {
|
||||
return <ActionListView
|
||||
dataTestId='action-list'
|
||||
items={actions}
|
||||
id={action => action.callId}
|
||||
selectedItem={selectedAction}
|
||||
|
@ -99,15 +99,6 @@ export const WatchModeView: React.FC<{}> = ({
|
||||
setProgress(newProgress);
|
||||
};
|
||||
|
||||
const outputDir = React.useMemo(() => {
|
||||
let outputDir = '';
|
||||
for (const p of rootSuite.value?.suites || []) {
|
||||
outputDir = p.project()?.outputDir || '';
|
||||
break;
|
||||
}
|
||||
return outputDir;
|
||||
}, [rootSuite]);
|
||||
|
||||
const runTests = (testIds: string[]) => {
|
||||
// Clear test results.
|
||||
{
|
||||
@ -130,6 +121,7 @@ export const WatchModeView: React.FC<{}> = ({
|
||||
|
||||
const isRunningTest = !!runningState;
|
||||
const result = selectedTest?.results[0];
|
||||
const outputDir = selectedTest ? outputDirForTestCase(selectedTest) : undefined;
|
||||
|
||||
return <div className='vbox watch-mode'>
|
||||
<SplitView sidebarSize={250} orientation='horizontal' sidebarIsFirst={true}>
|
||||
@ -388,7 +380,7 @@ const TestList: React.FC<{
|
||||
};
|
||||
|
||||
const TraceView: React.FC<{
|
||||
outputDir: string,
|
||||
outputDir: string | undefined,
|
||||
testCase: TestCase | undefined,
|
||||
result: TestResult | undefined,
|
||||
}> = ({ outputDir, testCase, result }) => {
|
||||
@ -412,6 +404,11 @@ const TraceView: React.FC<{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!outputDir) {
|
||||
setModel(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const traceLocation = `${outputDir}/${artifactsFolderName(result!.workerIndex)}/traces/${testCase?.id}.json`;
|
||||
// Start polling running test.
|
||||
pollTimer.current = setTimeout(async () => {
|
||||
@ -535,6 +532,14 @@ const sendMessageNoReply = (method: string, params?: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
const outputDirForTestCase = (testCase: TestCase): string | undefined => {
|
||||
for (let suite: Suite | undefined = testCase.parent; suite; suite = suite.parent) {
|
||||
if (suite.project())
|
||||
return suite.project()?.outputDir;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const fileNameForTreeItem = (treeItem?: TreeItem): string | undefined => {
|
||||
return treeItem?.location.file;
|
||||
};
|
||||
|
@ -53,7 +53,7 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
|
||||
}) => {
|
||||
const codemirrorElement = React.useRef<HTMLDivElement>(null);
|
||||
const [modulePromise] = React.useState<Promise<CodeMirror>>(import('./codeMirrorModule').then(m => m.default));
|
||||
const codemirrorRef = React.useRef<{ cm: CodeMirror.Editor, highlight: SourceHighlight[], widgets: CodeMirror.LineWidget[] } | null>(null);
|
||||
const codemirrorRef = React.useRef<{ cm: CodeMirror.Editor, highlight?: SourceHighlight[], widgets?: CodeMirror.LineWidget[] } | null>(null);
|
||||
const [codemirror, setCodemirror] = React.useState<CodeMirror.Editor>();
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -92,7 +92,7 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
|
||||
lineNumbers,
|
||||
lineWrapping: wrapLines,
|
||||
});
|
||||
codemirrorRef.current = { cm, highlight: [], widgets: [] };
|
||||
codemirrorRef.current = { cm };
|
||||
setCodemirror(cm);
|
||||
return cm;
|
||||
})();
|
||||
@ -108,43 +108,47 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
|
||||
codemirror.on('change', (codemirror as any)[listenerSymbol]);
|
||||
}
|
||||
|
||||
let valueChanged = false;
|
||||
if (codemirror.getValue() !== text) {
|
||||
codemirror.setValue(text);
|
||||
valueChanged = true;
|
||||
if (focusOnChange) {
|
||||
codemirror.execCommand('selectAll');
|
||||
codemirror.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Line highlight.
|
||||
for (const h of codemirrorRef.current!.highlight)
|
||||
codemirror.removeLineClass(h.line - 1, 'wrap');
|
||||
for (const h of highlight || [])
|
||||
codemirror.addLineClass(h.line - 1, 'wrap', `source-line-${h.type}`);
|
||||
codemirrorRef.current!.highlight = highlight || [];
|
||||
if (valueChanged || JSON.stringify(highlight) !== JSON.stringify(codemirrorRef.current!.highlight)) {
|
||||
// Line highlight.
|
||||
for (const h of codemirrorRef.current!.highlight || [])
|
||||
codemirror.removeLineClass(h.line - 1, 'wrap');
|
||||
for (const h of highlight || [])
|
||||
codemirror.addLineClass(h.line - 1, 'wrap', `source-line-${h.type}`);
|
||||
|
||||
// Error widgets.
|
||||
for (const w of codemirrorRef.current!.widgets)
|
||||
codemirror.removeLineWidget(w);
|
||||
const widgets: CodeMirror.LineWidget[] = [];
|
||||
for (const h of highlight || []) {
|
||||
if (h.type !== 'error')
|
||||
continue;
|
||||
// Error widgets.
|
||||
for (const w of codemirrorRef.current!.widgets || [])
|
||||
codemirror.removeLineWidget(w);
|
||||
const widgets: CodeMirror.LineWidget[] = [];
|
||||
for (const h of highlight || []) {
|
||||
if (h.type !== 'error')
|
||||
continue;
|
||||
|
||||
const line = codemirrorRef.current?.cm.getLine(h.line - 1);
|
||||
if (line) {
|
||||
const underlineWidgetElement = document.createElement('div');
|
||||
underlineWidgetElement.className = 'source-line-error-underline';
|
||||
underlineWidgetElement.innerHTML = ' '.repeat(line.length || 1);
|
||||
widgets.push(codemirror.addLineWidget(h.line, underlineWidgetElement, { above: true, coverGutter: false }));
|
||||
const line = codemirrorRef.current?.cm.getLine(h.line - 1);
|
||||
if (line) {
|
||||
const underlineWidgetElement = document.createElement('div');
|
||||
underlineWidgetElement.className = 'source-line-error-underline';
|
||||
underlineWidgetElement.innerHTML = ' '.repeat(line.length || 1);
|
||||
widgets.push(codemirror.addLineWidget(h.line, underlineWidgetElement, { above: true, coverGutter: false }));
|
||||
}
|
||||
|
||||
const errorWidgetElement = document.createElement('div');
|
||||
errorWidgetElement.innerHTML = ansi2htmlMarkup(h.message || '');
|
||||
errorWidgetElement.className = 'source-line-error-widget';
|
||||
widgets.push(codemirror.addLineWidget(h.line, errorWidgetElement, { above: true, coverGutter: false }));
|
||||
}
|
||||
|
||||
const errorWidgetElement = document.createElement('div');
|
||||
errorWidgetElement.innerHTML = ansi2htmlMarkup(h.message || '');
|
||||
errorWidgetElement.className = 'source-line-error-widget';
|
||||
widgets.push(codemirror.addLineWidget(h.line, errorWidgetElement, { above: true, coverGutter: false }));
|
||||
codemirrorRef.current!.highlight = highlight;
|
||||
codemirrorRef.current!.widgets = widgets;
|
||||
}
|
||||
codemirrorRef.current!.widgets = widgets;
|
||||
|
||||
if (revealLine && codemirrorRef.current!.cm.lineCount() >= revealLine)
|
||||
codemirror.scrollIntoView({ line: revealLine - 1, ch: 0 }, 50);
|
||||
|
111
tests/playwright-test/ui-mode-test-progress.spec.ts
Normal file
111
tests/playwright-test/ui-mode-test-progress.spec.ts
Normal file
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ManualPromise } from '../../packages/playwright-core/lib/utils/manualPromise';
|
||||
import { test, expect } from './ui-mode-fixtures';
|
||||
test.describe.configure({ mode: 'parallel' });
|
||||
|
||||
test('should update trace live', async ({ runUITest, server }) => {
|
||||
const onePromise = new ManualPromise();
|
||||
|
||||
server.setRoute('/one.html', async (req, res) => {
|
||||
await onePromise;
|
||||
res.end('<html>One</html>');
|
||||
});
|
||||
|
||||
const twoPromise = new ManualPromise();
|
||||
server.setRoute('/two.html', async (req, res) => {
|
||||
await twoPromise;
|
||||
res.end('<html>Two</html>');
|
||||
});
|
||||
|
||||
const page = await runUITest({
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('live test', async ({ page }) => {
|
||||
await page.goto('${server.PREFIX}/one.html');
|
||||
await page.goto('${server.PREFIX}/two.html');
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
// Start test.
|
||||
await page.getByText('live test').dblclick();
|
||||
|
||||
// It should halt on loading one.html.
|
||||
const listItem = page.getByTestId('action-list').getByRole('listitem');
|
||||
await expect(
|
||||
listItem,
|
||||
'action list'
|
||||
).toHaveText([
|
||||
/browserContext.newPage[\d.]+m?s/,
|
||||
/page.gotohttp:\/\/localhost:\d+\/one.html/
|
||||
]);
|
||||
|
||||
await expect(
|
||||
listItem.locator(':scope.selected'),
|
||||
'last action to be selected'
|
||||
).toHaveText(/page.goto/);
|
||||
await expect(
|
||||
listItem.locator(':scope.selected .codicon.codicon-loading'),
|
||||
'spinner'
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.locator('.CodeMirror .source-line-running'),
|
||||
'check source tab',
|
||||
).toHaveText(/4 await page.goto\('http:\/\/localhost:\d+\/one.html/);
|
||||
|
||||
// Unlock the navigation step.
|
||||
onePromise.resolve();
|
||||
|
||||
await expect(
|
||||
page.frameLocator('id=snapshot').locator('body'),
|
||||
'verify snapshot'
|
||||
).toHaveText('One');
|
||||
await expect(listItem).toHaveText([
|
||||
/browserContext.newPage[\d.]+m?s/,
|
||||
/page.gotohttp:\/\/localhost:\d+\/one.html[\d.]+m?s/,
|
||||
/page.gotohttp:\/\/localhost:\d+\/two.html/
|
||||
]);
|
||||
await expect(
|
||||
listItem.locator(':scope.selected'),
|
||||
'last action to be selected'
|
||||
).toHaveText(/page.goto/);
|
||||
await expect(
|
||||
listItem.locator(':scope.selected .codicon.codicon-loading'),
|
||||
'spinner'
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.locator('.CodeMirror .source-line-running'),
|
||||
'check source tab',
|
||||
).toHaveText(/5 await page.goto\('http:\/\/localhost:\d+\/two.html/);
|
||||
|
||||
// Unlock the navigation step.
|
||||
twoPromise.resolve();
|
||||
|
||||
await expect(
|
||||
page.frameLocator('id=snapshot').locator('body'),
|
||||
'verify snapshot'
|
||||
).toHaveText('Two');
|
||||
|
||||
await expect(listItem).toHaveText([
|
||||
/browserContext.newPage[\d.]+m?s/,
|
||||
/page.gotohttp:\/\/localhost:\d+\/one.html[\d.]+m?s/,
|
||||
/page.gotohttp:\/\/localhost:\d+\/two.html[\d.]+m?s/
|
||||
]);
|
||||
});
|
Loading…
Reference in New Issue
Block a user