diff --git a/packages/trace-viewer/src/ui/consoleTab.css b/packages/trace-viewer/src/ui/consoleTab.css
index 50a881cb97..3c31395001 100644
--- a/packages/trace-viewer/src/ui/consoleTab.css
+++ b/packages/trace-viewer/src/ui/consoleTab.css
@@ -52,12 +52,34 @@
.console-time {
float: left;
- min-width: 30px;
+ min-width: 50px;
color: var(--vscode-editorCodeLens-foreground);
user-select: none;
}
.console-stack {
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);
}
diff --git a/packages/trace-viewer/src/ui/consoleTab.tsx b/packages/trace-viewer/src/ui/consoleTab.tsx
index c76a9d0783..94a3d7ac9d 100644
--- a/packages/trace-viewer/src/ui/consoleTab.tsx
+++ b/packages/trace-viewer/src/ui/consoleTab.tsx
@@ -25,13 +25,14 @@ import { msToString } from '@web/uiUtils';
import type * as trace from '@trace/trace';
type ConsoleEntry = {
- message?: trace.ConsoleMessageTraceEvent['initializer'];
- error?: channels.SerializedError;
+ browserMessage?: trace.ConsoleMessageTraceEvent['initializer'],
+ browserError?: channels.SerializedError;
nodeMessage?: {
text?: string;
base64?: string;
- isError: boolean;
},
+ isError: boolean;
+ isWarning: boolean;
timestamp: number;
};
@@ -52,13 +53,17 @@ export const ConsoleTab: React.FunctionComponent<{
if (event.method === 'console') {
const { guid } = event.params.message;
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,
});
}
if (event.method === 'pageError') {
entries.push({
- error: event.params.error,
+ browserError: event.params.error,
+ isError: true,
+ isWarning: false,
timestamp: event.time,
});
}
@@ -68,8 +73,9 @@ export const ConsoleTab: React.FunctionComponent<{
nodeMessage: {
text: event.text,
base64: event.base64,
- isError: event.type === 'stderr',
},
+ isError: event.type === 'stderr',
+ isWarning: false,
timestamp: event.timestamp,
});
}
@@ -86,69 +92,56 @@ export const ConsoleTab: React.FunctionComponent<{
return
!!entry.error || entry.message?.type === 'error' || entry.nodeMessage?.isError || false}
- isWarning={entry => entry.message?.type === 'warning'}
+ isError={entry => entry.isError}
+ isWarning={entry => entry.isWarning}
render={entry => {
- const { message, error, nodeMessage } = entry;
const timestamp = msToString(entry.timestamp - boundaries.minimum);
- if (message) {
- const text = message.args ? format(message.args) : message.text;
- const url = message.location.url;
+ const timestampElement = {timestamp};
+ const errorSuffix = entry.isError ? ' status-error' : entry.isWarning ? ' status-warning' : ' status-none';
+ const statusElement = entry.browserMessage || entry.browserError ? : ;
+ 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) : '';
- return
- {timestamp}
- {filename}:{message.location.lineNumber}
-
- {text}
-
;
+ locationText = `${filename}:${browserMessage.location.lineNumber}`;
+ messageBody = text;
}
- if (error) {
- const { error: errorObject, value } = error;
+
+ if (browserError) {
+ const { error: errorObject, value } = browserError;
if (errorObject) {
- return
-
{timestamp}
-
-
{errorObject.message}
-
{errorObject.stack}
-
;
+ messageBody = errorObject.message;
+ messageStack = errorObject.stack;
+ } else {
+ messageBody = String(value);
}
- return
- {timestamp}
-
- {String(value)}
-
;
}
- if (nodeMessage?.text) {
- return
- {timestamp}
-
-
-
;
- }
- if (nodeMessage?.base64) {
- return
-
-
-
;
- }
- return null;
+
+ if (nodeMessage?.text)
+ messageInnerHTML = ansi2htmlMarkup(nodeMessage.text.trim()) || '';
+
+ if (nodeMessage?.base64)
+ messageInnerHTML = ansi2htmlMarkup(atob(nodeMessage.base64).trim()) || '';
+
+ return
+ {timestampElement}
+ {statusElement}
+ {locationText &&
{locationText}}
+ {messageBody &&
{messageBody}}
+ {messageInnerHTML &&
}
+ {messageStack &&
{messageStack}
}
+
;
}}
/>
;
};
-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[] {
if (args.length === 1)
return [{args[0].preview}];
diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts
index e65e7ce22d..98daeb1b0b 100644
--- a/tests/library/trace-viewer.spec.ts
+++ b/tests/library/trace-viewer.spec.ts
@@ -147,12 +147,12 @@ test('should render console', async ({ showTraceViewer, browserName }) => {
await expect(traceViewer.consoleLineMessages.last()).toHaveText('Cheers!');
const icons = traceViewer.consoleLines.locator('.codicon');
- await expect(icons.nth(0)).toHaveClass('codicon codicon-blank');
- await expect(icons.nth(1)).toHaveClass('codicon codicon-warning');
- await expect(icons.nth(2)).toHaveClass('codicon codicon-error');
- await expect(icons.nth(3)).toHaveClass('codicon codicon-error');
+ await expect.soft(icons.nth(0)).toHaveClass('codicon codicon-browser status-none');
+ await expect.soft(icons.nth(1)).toHaveClass('codicon codicon-browser status-warning');
+ await expect.soft(icons.nth(2)).toHaveClass('codicon codicon-browser status-error');
+ await expect.soft(icons.nth(3)).toHaveClass('codicon codicon-browser status-error');
// 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 traceViewer.selectAction('page.evaluate');
diff --git a/tests/playwright-test/ui-mode-test-output.spec.ts b/tests/playwright-test/ui-mode-test-output.spec.ts
index 23f254707a..5590f90ae1 100644
--- a/tests/playwright-test/ui-mode-test-output.spec.ts
+++ b/tests/playwright-test/ui-mode-test-output.spec.ts
@@ -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([
- 'codicon codicon-blank',
- 'codicon codicon-blank',
- 'codicon codicon-error',
- 'codicon codicon-error',
- 'codicon codicon-blank',
+ 'codicon codicon-browser status-none',
+ 'codicon codicon-file status-none',
+ 'codicon codicon-browser status-error',
+ 'codicon codicon-file status-error',
+ 'codicon codicon-file status-none',
]);
await expect(page.getByText('RED', { exact: true })).toHaveCSS('color', 'rgb(204, 0, 0)');