fix(cli): do not extend injected script on same-document navigations (#5002)
Otherwise, the injected script has to be ready for reentrancy.
This commit is contained in:
parent
a35617db47
commit
5c3f483659
|
|
@ -372,7 +372,7 @@ export class Recorder {
|
||||||
if (event.key === '@' && event.code === 'KeyL')
|
if (event.key === '@' && event.code === 'KeyL')
|
||||||
return false;
|
return false;
|
||||||
// Allow and ignore common used shortcut for pasting.
|
// Allow and ignore common used shortcut for pasting.
|
||||||
if (process.platform === 'darwin') {
|
if (navigator.platform.includes('Mac')) {
|
||||||
if (event.key === 'v' && event.metaKey)
|
if (event.key === 'v' && event.metaKey)
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -382,7 +382,7 @@ export abstract class BrowserContext extends EventEmitter {
|
||||||
async extendInjectedScript(source: string, arg?: any) {
|
async extendInjectedScript(source: string, arg?: any) {
|
||||||
const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source, arg).catch(e => {});
|
const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source, arg).catch(e => {});
|
||||||
const installInPage = (page: Page) => {
|
const installInPage = (page: Page) => {
|
||||||
page.on(Page.Events.FrameNavigated, installInFrame);
|
page.on(Page.Events.InternalFrameNavigatedToNewDocument, installInFrame);
|
||||||
return Promise.all(page.frames().map(installInFrame));
|
return Promise.all(page.frames().map(installInFrame));
|
||||||
};
|
};
|
||||||
this.on(BrowserContext.Events.Page, installInPage);
|
this.on(BrowserContext.Events.Page, installInPage);
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ export class FrameManager {
|
||||||
frame.emit(Frame.Events.Navigation, navigationEvent);
|
frame.emit(Frame.Events.Navigation, navigationEvent);
|
||||||
if (!initial) {
|
if (!initial) {
|
||||||
debugLogger.log('api', ` navigated to "${url}"`);
|
debugLogger.log('api', ` navigated to "${url}"`);
|
||||||
this._page.frameNavigated(frame);
|
this._page.frameNavigatedToNewDocument(frame);
|
||||||
}
|
}
|
||||||
// Restore pending if any - see comments above about keepPending.
|
// Restore pending if any - see comments above about keepPending.
|
||||||
frame._pendingDocument = keepPending;
|
frame._pendingDocument = keepPending;
|
||||||
|
|
@ -213,7 +213,6 @@ export class FrameManager {
|
||||||
const navigationEvent: NavigationEvent = { url, name: frame._name };
|
const navigationEvent: NavigationEvent = { url, name: frame._name };
|
||||||
frame.emit(Frame.Events.Navigation, navigationEvent);
|
frame.emit(Frame.Events.Navigation, navigationEvent);
|
||||||
debugLogger.log('api', ` navigated to "${url}"`);
|
debugLogger.log('api', ` navigated to "${url}"`);
|
||||||
this._page.frameNavigated(frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frameAbortedNavigation(frameId: string, errorText: string, documentId?: string) {
|
frameAbortedNavigation(frameId: string, errorText: string, documentId?: string) {
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ export class Page extends EventEmitter {
|
||||||
RequestFinished: 'requestfinished',
|
RequestFinished: 'requestfinished',
|
||||||
FrameAttached: 'frameattached',
|
FrameAttached: 'frameattached',
|
||||||
FrameDetached: 'framedetached',
|
FrameDetached: 'framedetached',
|
||||||
FrameNavigated: 'framenavigated',
|
InternalFrameNavigatedToNewDocument: 'internalframenavigatedtonewdocument',
|
||||||
Load: 'load',
|
Load: 'load',
|
||||||
Popup: 'popup',
|
Popup: 'popup',
|
||||||
WebSocket: 'websocket',
|
WebSocket: 'websocket',
|
||||||
|
|
@ -457,8 +457,8 @@ export class Page extends EventEmitter {
|
||||||
this.emit(Page.Events.VideoStarted, video);
|
this.emit(Page.Events.VideoStarted, video);
|
||||||
}
|
}
|
||||||
|
|
||||||
frameNavigated(frame: frames.Frame) {
|
frameNavigatedToNewDocument(frame: frames.Frame) {
|
||||||
this.emit(Page.Events.FrameNavigated, frame);
|
this.emit(Page.Events.InternalFrameNavigatedToNewDocument, frame);
|
||||||
const url = frame.url();
|
const url = frame.url();
|
||||||
if (!url.startsWith('http'))
|
if (!url.startsWith('http'))
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -82,3 +82,26 @@ it('exposeBindingHandle should work', async ({context}) => {
|
||||||
expect(await target.evaluate(x => x.foo)).toBe(42);
|
expect(await target.evaluate(x => x.foo)).toBe(42);
|
||||||
expect(result).toEqual(17);
|
expect(result).toEqual(17);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('extendInjectedScript should work', async ({ context, server }) => {
|
||||||
|
await (context as any)._extendInjectedScript(`var pwExport = (() => {
|
||||||
|
class Foo {
|
||||||
|
constructor() {
|
||||||
|
window._counter = (window._counter || 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Foo;
|
||||||
|
})()`);
|
||||||
|
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.waitForFunction(() => (window as any)._counter === 1);
|
||||||
|
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.waitForFunction(() => (window as any)._counter === 1);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation(),
|
||||||
|
page.evaluate(() => history.pushState({}, '', '/url.html'))
|
||||||
|
]);
|
||||||
|
expect(await page.evaluate(() => (window as any)._counter)).toBe(1);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,34 @@ describe('cli codegen', (test, { browserName, headful }) => {
|
||||||
expect(message.text()).toBe('click');
|
expect(message.text()).toBe('click');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should click after same-document navigation', async ({ page, recorder, httpServer }) => {
|
||||||
|
httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||||
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||||
|
res.end('');
|
||||||
|
});
|
||||||
|
await recorder.setContentAndWait(`<button onclick="console.log('click')">Submit</button>`, httpServer.PREFIX + '/foo.html');
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation(),
|
||||||
|
page.evaluate(() => history.pushState({}, '', '/url.html')),
|
||||||
|
]);
|
||||||
|
// This is the only way to give recorder a chance to install
|
||||||
|
// the second unnecessary copy of the recorder script.
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
const selector = await recorder.hoverOverElement('button');
|
||||||
|
expect(selector).toBe('text="Submit"');
|
||||||
|
|
||||||
|
const [message] = await Promise.all([
|
||||||
|
page.waitForEvent('console'),
|
||||||
|
recorder.waitForOutput('click'),
|
||||||
|
page.dispatchEvent('button', 'click', { detail: 1 })
|
||||||
|
]);
|
||||||
|
expect(recorder.output()).toContain(`
|
||||||
|
// Click text="Submit"
|
||||||
|
await page.click('text="Submit"');`);
|
||||||
|
expect(message.text()).toBe('click');
|
||||||
|
});
|
||||||
|
|
||||||
it('should not target selector preview by text regexp', async ({ page, recorder }) => {
|
it('should not target selector preview by text regexp', async ({ page, recorder }) => {
|
||||||
await recorder.setContentAndWait(`<span>dummy</span>`);
|
await recorder.setContentAndWait(`<span>dummy</span>`);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue