Skip to content

Commit 8b9a2af

Browse files
authored
feat(inspector): render errors (#5459)
1 parent ae2ffb3 commit 8b9a2af

File tree

17 files changed

+191
-80
lines changed

17 files changed

+191
-80
lines changed

index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,4 @@
1414
* limitations under the License.
1515
*/
1616

17-
const { setUnderTest } = require('./lib/utils/utils');
1817
module.exports = require('./lib/inprocess');

src/dispatchers/dispatcher.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ export class DispatcherConnection {
214214
this.onmessage({ id, result: this._replaceDispatchersWithGuids(result) });
215215
} catch (e) {
216216
// Dispatching error
217+
callMetadata.error = e.message;
217218
if (callMetadata.log.length)
218219
rewriteErrorMessage(e, e.message + formatLogRecording(callMetadata.log) + kLoggingNote);
219220
this.onmessage({ id, error: serializeError(e) });

src/server/browserContext.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,8 @@ export abstract class BrowserContext extends SdkObject {
345345
}
346346
}
347347

348-
async extendInjectedScript(source: string) {
349-
const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source).catch(e => {});
348+
async extendInjectedScript(source: string, arg?: any) {
349+
const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source, arg).catch(() => {});
350350
const installInPage = (page: Page) => {
351351
page.on(Page.Events.InternalFrameNavigatedToNewDocument, installInFrame);
352352
return Promise.all(page.frames().map(installInFrame));

src/server/instrumentation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export type CallMetadata = {
3939
params: any;
4040
stack?: StackFrame[];
4141
log: string[];
42-
error?: Error;
42+
error?: string;
4343
point?: Point;
4444
};
4545

src/server/supplements/injected/recorder.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@ export class Recorder {
5151
private _actionPointElement: HTMLElement;
5252
private _actionPoint: Point | undefined;
5353
private _actionSelector: string | undefined;
54+
private _params: { isUnderTest: boolean; };
5455

55-
constructor(injectedScript: InjectedScript) {
56+
constructor(injectedScript: InjectedScript, params: { isUnderTest: boolean }) {
57+
this._params = params;
5658
this._injectedScript = injectedScript;
5759
this._outerGlassPaneElement = html`
5860
<x-pw-glass style="
@@ -76,7 +78,7 @@ export class Recorder {
7678
</x-pw-glass-inner>`;
7779

7880
// Use a closed shadow root to prevent selectors matching our internal previews.
79-
this._glassPaneShadow = this._outerGlassPaneElement.attachShadow({ mode: 'closed' });
81+
this._glassPaneShadow = this._outerGlassPaneElement.attachShadow({ mode: this._params.isUnderTest ? 'open' : 'closed' });
8082
this._glassPaneShadow.appendChild(this._innerGlassPaneElement);
8183
this._glassPaneShadow.appendChild(this._actionPointElement);
8284
this._glassPaneShadow.appendChild(html`

src/server/supplements/recorder/recorderApp.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { internalCallMetadata } from '../../instrumentation';
2626
import type { CallLog, EventData, Mode, Source } from './recorderTypes';
2727
import { BrowserContext } from '../../browserContext';
2828
import { isUnderTest } from '../../../utils/utils';
29+
import { RecentLogsCollector } from '../../../utils/debugLogger';
2930

3031
const readFileAsync = util.promisify(fs.readFile);
3132

@@ -41,11 +42,13 @@ declare global {
4142

4243
export class RecorderApp extends EventEmitter {
4344
private _page: Page;
45+
readonly wsEndpoint: string | undefined;
4446

45-
constructor(page: Page) {
47+
constructor(page: Page, wsEndpoint: string | undefined) {
4648
super();
4749
this.setMaxListeners(0);
4850
this._page = page;
51+
this.wsEndpoint = wsEndpoint;
4952
}
5053

5154
async close() {
@@ -90,28 +93,45 @@ export class RecorderApp extends EventEmitter {
9093

9194
static async open(inspectedContext: BrowserContext): Promise<RecorderApp> {
9295
const recorderPlaywright = createPlaywright(true);
96+
const args = [
97+
'--app=data:text/html,',
98+
'--window-size=600,600',
99+
'--window-position=1280,10',
100+
];
101+
if (isUnderTest())
102+
args.push(`--remote-debugging-port=0`);
93103
const context = await recorderPlaywright.chromium.launchPersistentContext(internalCallMetadata(), '', {
94104
sdkLanguage: inspectedContext._options.sdkLanguage,
95-
args: [
96-
'--app=data:text/html,',
97-
'--window-size=600,600',
98-
'--window-position=1280,10',
99-
],
105+
args,
100106
noDefaultViewport: true,
101107
headless: isUnderTest() && !inspectedContext._browser.options.headful
102108
});
103-
109+
const wsEndpoint = isUnderTest() ? await this._parseWsEndpoint(context._browser.options.browserLogsCollector) : undefined;
104110
const controller = new ProgressController(internalCallMetadata(), context._browser);
105111
await controller.run(async progress => {
106112
await context._browser._defaultContext!._loadDefaultContextAsIs(progress);
107113
});
108114

109115
const [page] = context.pages();
110-
const result = new RecorderApp(page);
116+
const result = new RecorderApp(page, wsEndpoint);
111117
await result._init();
112118
return result;
113119
}
114120

121+
private static async _parseWsEndpoint(recentLogs: RecentLogsCollector): Promise<string> {
122+
let callback: ((log: string) => void) | undefined;
123+
const result = new Promise<string>(f => callback = f);
124+
const check = (log: string) => {
125+
const match = log.match(/DevTools listening on (.*)/);
126+
if (match)
127+
callback!(match[1]);
128+
};
129+
for (const log of recentLogs.recentLogs())
130+
check(log);
131+
recentLogs.on('log', check);
132+
return result;
133+
}
134+
115135
async setMode(mode: 'none' | 'recording' | 'inspecting'): Promise<void> {
116136
await this._page.mainFrame()._evaluateExpression(((mode: Mode) => {
117137
window.playwrightSetMode(mode);

src/server/supplements/recorder/recorderTypes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ export type CallLog = {
3434
title: string;
3535
messages: string[];
3636
status: 'in-progress' | 'done' | 'error' | 'paused';
37+
error?: string;
3738
};
3839

3940
export type SourceHighlight = {
4041
line: number;
41-
type: 'running' | 'paused';
42+
type: 'running' | 'paused' | 'error';
4243
};
4344

4445
export type Source = {

src/server/supplements/recorderSupplement.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { RecorderApp } from './recorder/recorderApp';
3333
import { CallMetadata, internalCallMetadata, SdkObject } from '../instrumentation';
3434
import { Point } from '../../common/types';
3535
import { CallLog, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
36+
import { isUnderTest } from '../../utils/utils';
3637

3738
type BindingSource = { frame: Frame, page: Page };
3839

@@ -179,7 +180,7 @@ export class RecorderSupplement {
179180
this._resume(false).catch(() => {});
180181
});
181182

182-
await this._context.extendInjectedScript(recorderSource.source);
183+
await this._context.extendInjectedScript(recorderSource.source, { isUnderTest: isUnderTest() });
183184
await this._context.extendInjectedScript(consoleApiSource.source);
184185

185186
(this._context as any).recorderAppForTest = recorderApp;
@@ -332,7 +333,8 @@ export class RecorderSupplement {
332333
}
333334

334335
async onAfterCall(metadata: CallMetadata): Promise<void> {
335-
this._currentCallsMetadata.delete(metadata);
336+
if (!metadata.error)
337+
this._currentCallsMetadata.delete(metadata);
336338
this._pausedCallsMetadata.delete(metadata);
337339
this._updateUserSources();
338340
this.updateCallLog([metadata]);
@@ -357,7 +359,7 @@ export class RecorderSupplement {
357359
}
358360
if (line) {
359361
const paused = this._pausedCallsMetadata.has(metadata);
360-
source.highlight.push({ line, type: paused ? 'paused' : 'running' });
362+
source.highlight.push({ line, type: metadata.error ? 'error' : (paused ? 'paused' : 'running') });
361363
if (paused)
362364
source.revealLine = line;
363365
}
@@ -387,7 +389,7 @@ export class RecorderSupplement {
387389
status = 'paused';
388390
if (metadata.error)
389391
status = 'error';
390-
logs.push({ id: metadata.id, messages: metadata.log, title, status });
392+
logs.push({ id: metadata.id, messages: metadata.log, title, status, error: metadata.error });
391393
}
392394
this._recorderApp?.updateCallLogs(logs);
393395
}

src/trace/tracer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class ContextTracer implements SnapshotterDelegate {
180180
startTime: metadata.startTime,
181181
endTime: metadata.endTime,
182182
logs: metadata.log.slice(),
183-
error: metadata.error ? metadata.error.stack : undefined,
183+
error: metadata.error,
184184
snapshots: snapshotsForMetadata(metadata),
185185
};
186186
this._appendTraceEvent(event);

src/utils/debugLogger.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import debug from 'debug';
1818
import fs from 'fs';
19+
import { EventEmitter } from 'events';
1920

2021
const debugLoggerColorMap = {
2122
'api': 45, // cyan
@@ -63,10 +64,11 @@ class DebugLogger {
6364
export const debugLogger = new DebugLogger();
6465

6566
const kLogCount = 50;
66-
export class RecentLogsCollector {
67+
export class RecentLogsCollector extends EventEmitter {
6768
private _logs: string[] = [];
6869

6970
log(message: string) {
71+
this.emit('log', message);
7072
this._logs.push(message);
7173
if (this._logs.length === kLogCount * 2)
7274
this._logs.splice(0, kLogCount);

0 commit comments

Comments
 (0)