mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: decorate console message sources (#26588)
This commit is contained in:
parent
1515d4efdc
commit
c3c3c7f53c
@ -52,12 +52,34 @@
|
|||||||
|
|
||||||
.console-time {
|
.console-time {
|
||||||
float: left;
|
float: left;
|
||||||
min-width: 30px;
|
min-width: 50px;
|
||||||
color: var(--vscode-editorCodeLens-foreground);
|
color: var(--vscode-editorCodeLens-foreground);
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.console-stack {
|
.console-stack {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
margin: 3px;
|
margin-left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-line .codicon.status-none::after,
|
||||||
|
.console-line .codicon.status-error::after,
|
||||||
|
.console-line .codicon.status-warning::after {
|
||||||
|
display: inline-block;
|
||||||
|
content: 'a';
|
||||||
|
color: transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
position: relative;
|
||||||
|
top: 8px;
|
||||||
|
left: -7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-line .codicon.status-error::after {
|
||||||
|
background-color: var(--vscode-errorForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.console-line .codicon.status-warning::after {
|
||||||
|
background-color: var(--vscode-list-warningForeground);
|
||||||
}
|
}
|
||||||
|
@ -25,13 +25,14 @@ import { msToString } from '@web/uiUtils';
|
|||||||
import type * as trace from '@trace/trace';
|
import type * as trace from '@trace/trace';
|
||||||
|
|
||||||
type ConsoleEntry = {
|
type ConsoleEntry = {
|
||||||
message?: trace.ConsoleMessageTraceEvent['initializer'];
|
browserMessage?: trace.ConsoleMessageTraceEvent['initializer'],
|
||||||
error?: channels.SerializedError;
|
browserError?: channels.SerializedError;
|
||||||
nodeMessage?: {
|
nodeMessage?: {
|
||||||
text?: string;
|
text?: string;
|
||||||
base64?: string;
|
base64?: string;
|
||||||
isError: boolean;
|
|
||||||
},
|
},
|
||||||
|
isError: boolean;
|
||||||
|
isWarning: boolean;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,13 +53,17 @@ export const ConsoleTab: React.FunctionComponent<{
|
|||||||
if (event.method === 'console') {
|
if (event.method === 'console') {
|
||||||
const { guid } = event.params.message;
|
const { guid } = event.params.message;
|
||||||
entries.push({
|
entries.push({
|
||||||
message: modelUtil.context(event).initializers[guid],
|
browserMessage: modelUtil.context(event).initializers[guid],
|
||||||
|
isError: modelUtil.context(event).initializers[guid]?.type === 'error',
|
||||||
|
isWarning: modelUtil.context(event).initializers[guid]?.type === 'warning',
|
||||||
timestamp: event.time,
|
timestamp: event.time,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (event.method === 'pageError') {
|
if (event.method === 'pageError') {
|
||||||
entries.push({
|
entries.push({
|
||||||
error: event.params.error,
|
browserError: event.params.error,
|
||||||
|
isError: true,
|
||||||
|
isWarning: false,
|
||||||
timestamp: event.time,
|
timestamp: event.time,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -68,8 +73,9 @@ export const ConsoleTab: React.FunctionComponent<{
|
|||||||
nodeMessage: {
|
nodeMessage: {
|
||||||
text: event.text,
|
text: event.text,
|
||||||
base64: event.base64,
|
base64: event.base64,
|
||||||
isError: event.type === 'stderr',
|
|
||||||
},
|
},
|
||||||
|
isError: event.type === 'stderr',
|
||||||
|
isWarning: false,
|
||||||
timestamp: event.timestamp,
|
timestamp: event.timestamp,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -86,69 +92,56 @@ export const ConsoleTab: React.FunctionComponent<{
|
|||||||
return <div className='console-tab'>
|
return <div className='console-tab'>
|
||||||
<ConsoleListView
|
<ConsoleListView
|
||||||
items={filteredEntries}
|
items={filteredEntries}
|
||||||
isError={entry => !!entry.error || entry.message?.type === 'error' || entry.nodeMessage?.isError || false}
|
isError={entry => entry.isError}
|
||||||
isWarning={entry => entry.message?.type === 'warning'}
|
isWarning={entry => entry.isWarning}
|
||||||
render={entry => {
|
render={entry => {
|
||||||
const { message, error, nodeMessage } = entry;
|
|
||||||
const timestamp = msToString(entry.timestamp - boundaries.minimum);
|
const timestamp = msToString(entry.timestamp - boundaries.minimum);
|
||||||
if (message) {
|
const timestampElement = <span className='console-time'>{timestamp}</span>;
|
||||||
const text = message.args ? format(message.args) : message.text;
|
const errorSuffix = entry.isError ? ' status-error' : entry.isWarning ? ' status-warning' : ' status-none';
|
||||||
const url = message.location.url;
|
const statusElement = entry.browserMessage || entry.browserError ? <span className={'codicon codicon-browser' + errorSuffix}></span> : <span className={'codicon codicon-file' + errorSuffix}></span>;
|
||||||
|
let locationText: string | undefined;
|
||||||
|
let messageBody: JSX.Element[] | string | undefined;
|
||||||
|
let messageInnerHTML: string | undefined;
|
||||||
|
let messageStack: JSX.Element[] | string | undefined;
|
||||||
|
|
||||||
|
const { browserMessage, browserError, nodeMessage } = entry;
|
||||||
|
if (browserMessage) {
|
||||||
|
const text = browserMessage.args ? format(browserMessage.args) : browserMessage.text;
|
||||||
|
const url = browserMessage.location.url;
|
||||||
const filename = url ? url.substring(url.lastIndexOf('/') + 1) : '<anonymous>';
|
const filename = url ? url.substring(url.lastIndexOf('/') + 1) : '<anonymous>';
|
||||||
return <div className='console-line'>
|
locationText = `${filename}:${browserMessage.location.lineNumber}`;
|
||||||
<span className='console-time'>{timestamp}</span>
|
messageBody = text;
|
||||||
<span className='console-location'>{filename}:{message.location.lineNumber}</span>
|
|
||||||
<span className={'codicon codicon-' + iconClass(message)}></span>
|
|
||||||
<span className='console-line-message'>{text}</span>
|
|
||||||
</div>;
|
|
||||||
}
|
}
|
||||||
if (error) {
|
|
||||||
const { error: errorObject, value } = error;
|
if (browserError) {
|
||||||
|
const { error: errorObject, value } = browserError;
|
||||||
if (errorObject) {
|
if (errorObject) {
|
||||||
return <div className='console-line'>
|
messageBody = errorObject.message;
|
||||||
<span className='console-time'>{timestamp}</span>
|
messageStack = errorObject.stack;
|
||||||
<span className={'codicon codicon-error'}></span>
|
} else {
|
||||||
<span className='console-line-message'>{errorObject.message}</span>
|
messageBody = String(value);
|
||||||
<div className='console-stack'>{errorObject.stack}</div>
|
|
||||||
</div>;
|
|
||||||
}
|
}
|
||||||
return <div className='console-line'>
|
|
||||||
<span className='console-time'>{timestamp}</span>
|
|
||||||
<span className={'codicon codicon-error'}></span>
|
|
||||||
<span className='console-line-message'>{String(value)}</span>
|
|
||||||
</div>;
|
|
||||||
}
|
}
|
||||||
if (nodeMessage?.text) {
|
|
||||||
return <div className='console-line'>
|
if (nodeMessage?.text)
|
||||||
<span className='console-time'>{timestamp}</span>
|
messageInnerHTML = ansi2htmlMarkup(nodeMessage.text.trim()) || '';
|
||||||
<span className={'codicon codicon-' + stdioClass(nodeMessage.isError)}></span>
|
|
||||||
<span className='console-line-message' dangerouslySetInnerHTML={{ __html: ansi2htmlMarkup(nodeMessage.text.trim()) || '' }}></span>
|
if (nodeMessage?.base64)
|
||||||
</div>;
|
messageInnerHTML = ansi2htmlMarkup(atob(nodeMessage.base64).trim()) || '';
|
||||||
}
|
|
||||||
if (nodeMessage?.base64) {
|
return <div className='console-line'>
|
||||||
return <div className='console-line'>
|
{timestampElement}
|
||||||
<span className={'codicon codicon-' + stdioClass(nodeMessage.isError)}></span>
|
{statusElement}
|
||||||
<span className='console-line-message' dangerouslySetInnerHTML={{ __html: ansi2htmlMarkup(atob(nodeMessage.base64).trim()) || '' }}></span>
|
{locationText && <span className='console-location'>{locationText}</span>}
|
||||||
</div>;
|
{messageBody && <span className='console-line-message'>{messageBody}</span>}
|
||||||
}
|
{messageInnerHTML && <span className='console-line-message' dangerouslySetInnerHTML={{ __html: messageInnerHTML }}></span>}
|
||||||
return null;
|
{messageStack && <div className='console-stack'>{messageStack}</div>}
|
||||||
|
</div>;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
function iconClass(message: trace.ConsoleMessageTraceEvent['initializer']): string {
|
|
||||||
switch (message.type) {
|
|
||||||
case 'error': return 'error';
|
|
||||||
case 'warning': return 'warning';
|
|
||||||
}
|
|
||||||
return 'blank';
|
|
||||||
}
|
|
||||||
|
|
||||||
function stdioClass(isError: boolean): string {
|
|
||||||
return isError ? 'error' : 'blank';
|
|
||||||
}
|
|
||||||
|
|
||||||
function format(args: { preview: string, value: any }[]): JSX.Element[] {
|
function format(args: { preview: string, value: any }[]): JSX.Element[] {
|
||||||
if (args.length === 1)
|
if (args.length === 1)
|
||||||
return [<span>{args[0].preview}</span>];
|
return [<span>{args[0].preview}</span>];
|
||||||
|
@ -147,12 +147,12 @@ test('should render console', async ({ showTraceViewer, browserName }) => {
|
|||||||
await expect(traceViewer.consoleLineMessages.last()).toHaveText('Cheers!');
|
await expect(traceViewer.consoleLineMessages.last()).toHaveText('Cheers!');
|
||||||
|
|
||||||
const icons = traceViewer.consoleLines.locator('.codicon');
|
const icons = traceViewer.consoleLines.locator('.codicon');
|
||||||
await expect(icons.nth(0)).toHaveClass('codicon codicon-blank');
|
await expect.soft(icons.nth(0)).toHaveClass('codicon codicon-browser status-none');
|
||||||
await expect(icons.nth(1)).toHaveClass('codicon codicon-warning');
|
await expect.soft(icons.nth(1)).toHaveClass('codicon codicon-browser status-warning');
|
||||||
await expect(icons.nth(2)).toHaveClass('codicon codicon-error');
|
await expect.soft(icons.nth(2)).toHaveClass('codicon codicon-browser status-error');
|
||||||
await expect(icons.nth(3)).toHaveClass('codicon codicon-error');
|
await expect.soft(icons.nth(3)).toHaveClass('codicon codicon-browser status-error');
|
||||||
// Firefox can insert layout error here.
|
// Firefox can insert layout error here.
|
||||||
await expect(icons.last()).toHaveClass('codicon codicon-blank');
|
await expect.soft(icons.last()).toHaveClass('codicon codicon-browser status-none');
|
||||||
await expect(traceViewer.consoleStacks.first()).toContainText('Error: Unhandled exception');
|
await expect(traceViewer.consoleStacks.first()).toContainText('Error: Unhandled exception');
|
||||||
|
|
||||||
await traceViewer.selectAction('page.evaluate');
|
await traceViewer.selectAction('page.evaluate');
|
||||||
|
@ -101,11 +101,11 @@ test('should show console messages for test', async ({ runUITest }, testInfo) =>
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
await expect(page.locator('.console-tab .list-view-entry .codicon')).toHaveClass([
|
await expect(page.locator('.console-tab .list-view-entry .codicon')).toHaveClass([
|
||||||
'codicon codicon-blank',
|
'codicon codicon-browser status-none',
|
||||||
'codicon codicon-blank',
|
'codicon codicon-file status-none',
|
||||||
'codicon codicon-error',
|
'codicon codicon-browser status-error',
|
||||||
'codicon codicon-error',
|
'codicon codicon-file status-error',
|
||||||
'codicon codicon-blank',
|
'codicon codicon-file status-none',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await expect(page.getByText('RED', { exact: true })).toHaveCSS('color', 'rgb(204, 0, 0)');
|
await expect(page.getByText('RED', { exact: true })).toHaveCSS('color', 'rgb(204, 0, 0)');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user