Skip to content

Commit 4bc8cf0

Browse files
authored
feat(recorder): display primary page URL in recorder title (#34637)
1 parent 893e7bb commit 4bc8cf0

File tree

6 files changed

+115
-25
lines changed

6 files changed

+115
-25
lines changed

packages/playwright-core/src/server/recorder.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import type * as actions from '@recorder/actions';
3232
import { stringifySelector } from '../utils/isomorphic/selectorParser';
3333
import type { Frame } from './frames';
3434
import type { AriaTemplateNode } from '@isomorphic/ariaSnapshot';
35+
import type { Page } from './page';
3536

3637
const recorderSymbol = Symbol('recorderSymbol');
3738

@@ -148,6 +149,7 @@ export class Recorder implements InstrumentationListener, IRecorder {
148149
this._context.instrumentation.removeListener(this);
149150
this._recorderApp?.close().catch(() => {});
150151
});
152+
151153
this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[], actions: actions.ActionInContext[] }) => {
152154
this._recorderSources = data.sources;
153155
recorderApp.setActions(data.actions, data.sources);
@@ -346,7 +348,8 @@ export class Recorder implements InstrumentationListener, IRecorder {
346348
}
347349

348350
private _pushAllSources() {
349-
this._recorderApp?.setSources([...this._recorderSources, ...this._userSources.values()]);
351+
const primaryPage: Page | undefined = this._context.pages()[0];
352+
this._recorderApp?.setSources([...this._recorderSources, ...this._userSources.values()], primaryPage?.mainFrame().url());
350353
}
351354

352355
async onBeforeInputAction(sdkObject: SdkObject, metadata: CallMetadata) {

packages/playwright-core/src/server/recorder/recorderApp.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class EmptyRecorderApp extends EventEmitter implements IRecorderApp {
3737
async setRunningFile(file: string | undefined): Promise<void> {}
3838
async elementPicked(elementInfo: ElementInfo, userGesture?: boolean): Promise<void> {}
3939
async updateCallLogs(callLogs: CallLog[]): Promise<void> {}
40-
async setSources(sources: Source[]): Promise<void> {}
40+
async setSources(sources: Source[], primaryPageURL: string | undefined): Promise<void> {}
4141
async setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void> {}
4242
}
4343

@@ -143,10 +143,10 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
143143
}).toString(), { isFunction: true }, paused).catch(() => {});
144144
}
145145

146-
async setSources(sources: Source[]): Promise<void> {
147-
await this._page.mainFrame().evaluateExpression(((sources: Source[]) => {
148-
window.playwrightSetSources(sources);
149-
}).toString(), { isFunction: true }, sources).catch(() => {});
146+
async setSources(sources: Source[], primaryPageURL: string | undefined): Promise<void> {
147+
await this._page.mainFrame().evaluateExpression((({ sources, primaryPageURL }: { sources: Source[], primaryPageURL: string | undefined }) => {
148+
window.playwrightSetSources(sources, primaryPageURL);
149+
}).toString(), { isFunction: true }, { sources, primaryPageURL }).catch(() => {});
150150

151151
// Testing harness for runCLI mode.
152152
if (process.env.PWTEST_CLI_IS_UNDER_TEST && sources.length) {

packages/playwright-core/src/server/recorder/recorderFrontend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export interface IRecorderApp extends EventEmitter {
3232
setRunningFile(file: string | undefined): Promise<void>;
3333
elementPicked(elementInfo: ElementInfo, userGesture?: boolean): Promise<void>;
3434
updateCallLogs(callLogs: CallLog[]): Promise<void>;
35-
setSources(sources: Source[]): Promise<void>;
35+
setSources(sources: Source[], primaryPageURL: string | undefined): Promise<void>;
3636
setActions(actions: actions.ActionInContext[], sources: Source[]): Promise<void>;
3737
}
3838

packages/recorder/src/main.tsx

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,33 @@ import * as React from 'react';
1919
import { Recorder } from './recorder';
2020
import './recorder.css';
2121

22-
export const Main: React.FC = ({
23-
}) => {
22+
export const Main: React.FC = ({}) => {
2423
const [sources, setSources] = React.useState<Source[]>([]);
2524
const [paused, setPaused] = React.useState(false);
2625
const [log, setLog] = React.useState(new Map<string, CallLog>());
2726
const [mode, setMode] = React.useState<Mode>('none');
2827

29-
window.playwrightSetMode = setMode;
30-
window.playwrightSetSources = React.useCallback((sources: Source[]) => {
31-
setSources(sources);
32-
window.playwrightSourcesEchoForTest = sources;
28+
React.useLayoutEffect(() => {
29+
window.playwrightSetMode = setMode;
30+
window.playwrightSetSources = (sources, primaryPageURL) => {
31+
setSources(sources);
32+
window.playwrightSourcesEchoForTest = sources;
33+
document.title = primaryPageURL
34+
? `Playwright Inspector - ${primaryPageURL}`
35+
: `Playwright Inspector`;
36+
};
37+
window.playwrightSetPaused = setPaused;
38+
window.playwrightUpdateLogs = callLogs => {
39+
setLog(log => {
40+
const newLog = new Map<string, CallLog>(log);
41+
for (const callLog of callLogs) {
42+
callLog.reveal = !log.has(callLog.id);
43+
newLog.set(callLog.id, callLog);
44+
}
45+
return newLog;
46+
});
47+
};
3348
}, []);
34-
window.playwrightSetPaused = setPaused;
35-
window.playwrightUpdateLogs = callLogs => {
36-
setLog(log => {
37-
const newLog = new Map<string, CallLog>(log);
38-
for (const callLog of callLogs) {
39-
callLog.reveal = !log.has(callLog.id);
40-
newLog.set(callLog.id, callLog);
41-
}
42-
return newLog;
43-
});
44-
};
4549

4650
return <Recorder sources={sources} paused={paused} log={log} mode={mode} />;
4751
};

packages/recorder/src/recorderTypes.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ declare global {
101101
interface Window {
102102
playwrightSetMode: (mode: Mode) => void;
103103
playwrightSetPaused: (paused: boolean) => void;
104-
playwrightSetSources: (sources: Source[]) => void;
104+
playwrightSetSources: (sources: Source[], primaryPageURL: string | undefined) => void;
105105
playwrightSetOverlayVisible: (visible: boolean) => void;
106106
playwrightUpdateLogs: (callLogs: CallLog[]) => void;
107107
playwrightSetRunningFile: (file: string | undefined) => void;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { test, expect } from './inspectorTest';
18+
19+
test('should reflect formatted URL of the page', async ({
20+
openRecorder,
21+
server,
22+
}) => {
23+
const { recorder } = await openRecorder();
24+
await recorder.setContentAndWait('');
25+
await expect(recorder.recorderPage).toHaveTitle(
26+
'Playwright Inspector - about:blank',
27+
);
28+
29+
await recorder.setContentAndWait('', server.EMPTY_PAGE);
30+
await expect(recorder.recorderPage).toHaveTitle(
31+
`Playwright Inspector - ${server.EMPTY_PAGE}`,
32+
);
33+
});
34+
35+
test('should update primary page URL when original primary closes', async ({
36+
context,
37+
openRecorder,
38+
server,
39+
}) => {
40+
const { recorder } = await openRecorder();
41+
await recorder.setContentAndWait(
42+
'',
43+
`${server.PREFIX}/background-color.html`,
44+
);
45+
await expect(recorder.recorderPage).toHaveTitle(
46+
`Playwright Inspector - ${server.PREFIX}/background-color.html`,
47+
);
48+
49+
const page2 = await context.newPage();
50+
await page2.goto(`${server.PREFIX}/empty.html`);
51+
await expect(recorder.recorderPage).toHaveTitle(
52+
`Playwright Inspector - ${server.PREFIX}/background-color.html`,
53+
);
54+
55+
const page3 = await context.newPage();
56+
await page3.goto(`${server.PREFIX}/dom.html`);
57+
await expect(recorder.recorderPage).toHaveTitle(
58+
`Playwright Inspector - ${server.PREFIX}/background-color.html`,
59+
);
60+
61+
const page4 = await context.newPage();
62+
await page4.goto(`${server.PREFIX}/grid.html`);
63+
await expect(recorder.recorderPage).toHaveTitle(
64+
`Playwright Inspector - ${server.PREFIX}/background-color.html`,
65+
);
66+
67+
await page2.close();
68+
await expect(recorder.recorderPage).toHaveTitle(
69+
`Playwright Inspector - ${server.PREFIX}/background-color.html`,
70+
);
71+
72+
await recorder.page.close();
73+
// URL will not update without performing some action
74+
await page3.getByRole('checkbox').click();
75+
await expect(recorder.recorderPage).toHaveTitle(
76+
`Playwright Inspector - ${server.PREFIX}/dom.html`,
77+
);
78+
79+
await page3.close();
80+
await expect(recorder.recorderPage).toHaveTitle(
81+
`Playwright Inspector - ${server.PREFIX}/grid.html`,
82+
);
83+
});

0 commit comments

Comments
 (0)