fix: enable util world bindings in firefox (#6546)
This commit is contained in:
parent
dc7f7f9a8c
commit
41df6607b0
|
|
@ -23,7 +23,7 @@ import { Page, PageBinding, PageDelegate } from '../page';
|
||||||
import { ConnectionTransport } from '../transport';
|
import { ConnectionTransport } from '../transport';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { ConnectionEvents, FFConnection } from './ffConnection';
|
import { ConnectionEvents, FFConnection } from './ffConnection';
|
||||||
import { FFPage } from './ffPage';
|
import { FFPage, UTILITY_WORLD_NAME } from './ffPage';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
|
|
||||||
export class FFBrowser extends Browser {
|
export class FFBrowser extends Browser {
|
||||||
|
|
@ -303,9 +303,8 @@ export class FFBrowserContext extends BrowserContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _doExposeBinding(binding: PageBinding) {
|
async _doExposeBinding(binding: PageBinding) {
|
||||||
if (binding.world !== 'main')
|
const worldName = binding.world === 'utility' ? UTILITY_WORLD_NAME : '';
|
||||||
throw new Error('Only main context bindings are supported in Firefox.');
|
await this._browser._connection.send('Browser.addBinding', { browserContextId: this._browserContextId, worldName, name: binding.name, script: binding.source });
|
||||||
await this._browser._connection.send('Browser.addBinding', { browserContextId: this._browserContextId, name: binding.name, script: binding.source });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _doUpdateRequestInterception(): Promise<void> {
|
async _doUpdateRequestInterception(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import { Progress } from '../progress';
|
||||||
import { splitErrorMessage } from '../../utils/stackTrace';
|
import { splitErrorMessage } from '../../utils/stackTrace';
|
||||||
import { debugLogger } from '../../utils/debugLogger';
|
import { debugLogger } from '../../utils/debugLogger';
|
||||||
|
|
||||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
export const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
|
|
||||||
export class FFPage implements PageDelegate {
|
export class FFPage implements PageDelegate {
|
||||||
readonly cspErrorsAsynchronousForInlineScipts = true;
|
readonly cspErrorsAsynchronousForInlineScipts = true;
|
||||||
|
|
@ -317,9 +317,8 @@ export class FFPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async exposeBinding(binding: PageBinding) {
|
async exposeBinding(binding: PageBinding) {
|
||||||
if (binding.world !== 'main')
|
const worldName = binding.world === 'utility' ? UTILITY_WORLD_NAME : '';
|
||||||
throw new Error('Only main context bindings are supported in Firefox.');
|
await this._session.send('Page.addBinding', { name: binding.name, script: binding.source, worldName });
|
||||||
await this._session.send('Page.addBinding', { name: binding.name, script: binding.source });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
didClose() {
|
didClose() {
|
||||||
|
|
|
||||||
|
|
@ -581,36 +581,36 @@ export class PageBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
function takeHandle(arg: { name: string, seq: number }) {
|
function takeHandle(arg: { name: string, seq: number }) {
|
||||||
const handle = (window as any)[arg.name]['handles'].get(arg.seq);
|
const handle = (globalThis as any)[arg.name]['handles'].get(arg.seq);
|
||||||
(window as any)[arg.name]['handles'].delete(arg.seq);
|
(globalThis as any)[arg.name]['handles'].delete(arg.seq);
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deliverResult(arg: { name: string, seq: number, result: any }) {
|
function deliverResult(arg: { name: string, seq: number, result: any }) {
|
||||||
(window as any)[arg.name]['callbacks'].get(arg.seq).resolve(arg.result);
|
(globalThis as any)[arg.name]['callbacks'].get(arg.seq).resolve(arg.result);
|
||||||
(window as any)[arg.name]['callbacks'].delete(arg.seq);
|
(globalThis as any)[arg.name]['callbacks'].delete(arg.seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deliverError(arg: { name: string, seq: number, message: string, stack: string | undefined }) {
|
function deliverError(arg: { name: string, seq: number, message: string, stack: string | undefined }) {
|
||||||
const error = new Error(arg.message);
|
const error = new Error(arg.message);
|
||||||
error.stack = arg.stack;
|
error.stack = arg.stack;
|
||||||
(window as any)[arg.name]['callbacks'].get(arg.seq).reject(error);
|
(globalThis as any)[arg.name]['callbacks'].get(arg.seq).reject(error);
|
||||||
(window as any)[arg.name]['callbacks'].delete(arg.seq);
|
(globalThis as any)[arg.name]['callbacks'].delete(arg.seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deliverErrorValue(arg: { name: string, seq: number, error: any }) {
|
function deliverErrorValue(arg: { name: string, seq: number, error: any }) {
|
||||||
(window as any)[arg.name]['callbacks'].get(arg.seq).reject(arg.error);
|
(globalThis as any)[arg.name]['callbacks'].get(arg.seq).reject(arg.error);
|
||||||
(window as any)[arg.name]['callbacks'].delete(arg.seq);
|
(globalThis as any)[arg.name]['callbacks'].delete(arg.seq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPageBinding(bindingName: string, needsHandle: boolean) {
|
function addPageBinding(bindingName: string, needsHandle: boolean) {
|
||||||
const binding = (window as any)[bindingName];
|
const binding = (globalThis as any)[bindingName];
|
||||||
if (binding.__installed)
|
if (binding.__installed)
|
||||||
return;
|
return;
|
||||||
(window as any)[bindingName] = (...args: any[]) => {
|
(globalThis as any)[bindingName] = (...args: any[]) => {
|
||||||
const me = (window as any)[bindingName];
|
const me = (globalThis as any)[bindingName];
|
||||||
if (needsHandle && args.slice(1).some(arg => arg !== undefined))
|
if (needsHandle && args.slice(1).some(arg => arg !== undefined))
|
||||||
throw new Error(`exposeBindingHandle supports a single argument, ${args.length} received`);
|
throw new Error(`exposeBindingHandle supports a single argument, ${args.length} received`);
|
||||||
let callbacks = me['callbacks'];
|
let callbacks = me['callbacks'];
|
||||||
|
|
@ -634,5 +634,5 @@ function addPageBinding(bindingName: string, needsHandle: boolean) {
|
||||||
}
|
}
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
(window as any)[bindingName].__installed = true;
|
(globalThis as any)[bindingName].__installed = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,13 @@ import { generateSelector, querySelector } from './selectorGenerator';
|
||||||
import type { Point } from '../../../common/types';
|
import type { Point } from '../../../common/types';
|
||||||
import type { UIState } from '../recorder/recorderTypes';
|
import type { UIState } from '../recorder/recorderTypes';
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
declare module globalThis {
|
||||||
_playwrightRecorderPerformAction: (action: actions.Action) => Promise<void>;
|
let _playwrightRecorderPerformAction: (action: actions.Action) => Promise<void>;
|
||||||
_playwrightRecorderRecordAction: (action: actions.Action) => Promise<void>;
|
let _playwrightRecorderRecordAction: (action: actions.Action) => Promise<void>;
|
||||||
_playwrightRecorderState: () => Promise<UIState>;
|
let _playwrightRecorderState: () => Promise<UIState>;
|
||||||
_playwrightRecorderSetSelector: (selector: string) => Promise<void>;
|
let _playwrightRecorderSetSelector: (selector: string) => Promise<void>;
|
||||||
_playwrightRefreshOverlay: () => void;
|
let _playwrightRefreshOverlay: () => void;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const scriptSymbol = Symbol('scriptSymbol');
|
const scriptSymbol = Symbol('scriptSymbol');
|
||||||
|
|
@ -125,15 +124,15 @@ export class Recorder {
|
||||||
this._refreshListenersIfNeeded();
|
this._refreshListenersIfNeeded();
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this._refreshListenersIfNeeded();
|
this._refreshListenersIfNeeded();
|
||||||
if ((window as any)._recorderScriptReadyForTest) {
|
if (params.isUnderTest && !(this as any)._reportedReadyForTest) {
|
||||||
(window as any)._recorderScriptReadyForTest();
|
(this as any)._reportedReadyForTest = true;
|
||||||
delete (window as any)._recorderScriptReadyForTest;
|
console.error('Recorder script ready for test');
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
window._playwrightRefreshOverlay = () => {
|
globalThis._playwrightRefreshOverlay = () => {
|
||||||
this._pollRecorderMode().catch(e => console.log(e)); // eslint-disable-line no-console
|
this._pollRecorderMode().catch(e => console.log(e)); // eslint-disable-line no-console
|
||||||
};
|
};
|
||||||
window._playwrightRefreshOverlay();
|
globalThis._playwrightRefreshOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _refreshListenersIfNeeded() {
|
private _refreshListenersIfNeeded() {
|
||||||
|
|
@ -186,7 +185,7 @@ export class Recorder {
|
||||||
const pollPeriod = 1000;
|
const pollPeriod = 1000;
|
||||||
if (this._pollRecorderModeTimer)
|
if (this._pollRecorderModeTimer)
|
||||||
clearTimeout(this._pollRecorderModeTimer);
|
clearTimeout(this._pollRecorderModeTimer);
|
||||||
const state = await window._playwrightRecorderState().catch(e => null);
|
const state = await globalThis._playwrightRecorderState().catch(e => null);
|
||||||
if (!state) {
|
if (!state) {
|
||||||
this._pollRecorderModeTimer = setTimeout(() => this._pollRecorderMode(), pollPeriod);
|
this._pollRecorderModeTimer = setTimeout(() => this._pollRecorderMode(), pollPeriod);
|
||||||
return;
|
return;
|
||||||
|
|
@ -267,7 +266,7 @@ export class Recorder {
|
||||||
|
|
||||||
private _onClick(event: MouseEvent) {
|
private _onClick(event: MouseEvent) {
|
||||||
if (this._mode === 'inspecting')
|
if (this._mode === 'inspecting')
|
||||||
window._playwrightRecorderSetSelector(this._hoveredModel ? this._hoveredModel.selector : '');
|
globalThis._playwrightRecorderSetSelector(this._hoveredModel ? this._hoveredModel.selector : '');
|
||||||
if (this._shouldIgnoreMouseEvent(event))
|
if (this._shouldIgnoreMouseEvent(event))
|
||||||
return;
|
return;
|
||||||
if (this._actionInProgress(event))
|
if (this._actionInProgress(event))
|
||||||
|
|
@ -349,8 +348,8 @@ export class Recorder {
|
||||||
const activeElement = this._deepActiveElement(document);
|
const activeElement = this._deepActiveElement(document);
|
||||||
const result = activeElement ? generateSelector(this._injectedScript, activeElement) : null;
|
const result = activeElement ? generateSelector(this._injectedScript, activeElement) : null;
|
||||||
this._activeModel = result && result.selector ? result : null;
|
this._activeModel = result && result.selector ? result : null;
|
||||||
if ((window as any)._highlightUpdatedForTest)
|
if (this._params.isUnderTest)
|
||||||
(window as any)._highlightUpdatedForTest(result ? result.selector : null);
|
console.error('Highlight updated for test: ' + (result ? result.selector : null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateModelForHoveredElement() {
|
private _updateModelForHoveredElement() {
|
||||||
|
|
@ -365,8 +364,8 @@ export class Recorder {
|
||||||
return;
|
return;
|
||||||
this._hoveredModel = selector ? { selector, elements } : null;
|
this._hoveredModel = selector ? { selector, elements } : null;
|
||||||
this._updateHighlight();
|
this._updateHighlight();
|
||||||
if ((window as any)._highlightUpdatedForTest)
|
if (this._params.isUnderTest)
|
||||||
(window as any)._highlightUpdatedForTest(selector);
|
console.error('Highlight updated for test: ' + selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateHighlight() {
|
private _updateHighlight() {
|
||||||
|
|
@ -455,7 +454,7 @@ export class Recorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elementType === 'file') {
|
if (elementType === 'file') {
|
||||||
window._playwrightRecorderRecordAction({
|
globalThis._playwrightRecorderRecordAction({
|
||||||
name: 'setInputFiles',
|
name: 'setInputFiles',
|
||||||
selector: this._activeModel!.selector,
|
selector: this._activeModel!.selector,
|
||||||
signals: [],
|
signals: [],
|
||||||
|
|
@ -467,7 +466,7 @@ export class Recorder {
|
||||||
// Non-navigating actions are simply recorded by Playwright.
|
// Non-navigating actions are simply recorded by Playwright.
|
||||||
if (this._consumedDueWrongTarget(event))
|
if (this._consumedDueWrongTarget(event))
|
||||||
return;
|
return;
|
||||||
window._playwrightRecorderRecordAction({
|
globalThis._playwrightRecorderRecordAction({
|
||||||
name: 'fill',
|
name: 'fill',
|
||||||
selector: this._activeModel!.selector,
|
selector: this._activeModel!.selector,
|
||||||
signals: [],
|
signals: [],
|
||||||
|
|
@ -564,7 +563,7 @@ export class Recorder {
|
||||||
|
|
||||||
private async _performAction(action: actions.Action) {
|
private async _performAction(action: actions.Action) {
|
||||||
this._performingAction = true;
|
this._performingAction = true;
|
||||||
await window._playwrightRecorderPerformAction(action).catch(() => {});
|
await globalThis._playwrightRecorderPerformAction(action).catch(() => {});
|
||||||
this._performingAction = false;
|
this._performingAction = false;
|
||||||
|
|
||||||
// Action could have changed DOM, update hovered model selectors.
|
// Action could have changed DOM, update hovered model selectors.
|
||||||
|
|
@ -572,11 +571,13 @@ export class Recorder {
|
||||||
// If that was a keyboard action, it similarly requires new selectors for active model.
|
// If that was a keyboard action, it similarly requires new selectors for active model.
|
||||||
this._onFocus();
|
this._onFocus();
|
||||||
|
|
||||||
if ((window as any)._actionPerformedForTest) {
|
if (this._params.isUnderTest) {
|
||||||
(window as any)._actionPerformedForTest({
|
// Serialize all to string as we cannot attribute console message to isolated world
|
||||||
|
// in Firefox.
|
||||||
|
console.error('Action performed for test: ' + JSON.stringify({
|
||||||
hovered: this._hoveredModel ? this._hoveredModel.selector : null,
|
hovered: this._hoveredModel ? this._hoveredModel.selector : null,
|
||||||
active: this._activeModel ? this._activeModel.selector : null,
|
active: this._activeModel ? this._activeModel.selector : null,
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ test.describe('cli codegen', () => {
|
||||||
expect(selector).toBe('text=Submit');
|
expect(selector).toBe('text=Submit');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'click'),
|
recorder.waitForOutput('<javascript>', 'click'),
|
||||||
page.dispatchEvent('button', 'click', { detail: 1 })
|
page.dispatchEvent('button', 'click', { detail: 1 })
|
||||||
]);
|
]);
|
||||||
|
|
@ -77,7 +77,7 @@ await page.ClickAsync("text=Submit");`);
|
||||||
expect(selector).toBe('text=Submit');
|
expect(selector).toBe('text=Submit');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'click'),
|
recorder.waitForOutput('<javascript>', 'click'),
|
||||||
page.dispatchEvent('button', 'click', { detail: 1 })
|
page.dispatchEvent('button', 'click', { detail: 1 })
|
||||||
]);
|
]);
|
||||||
|
|
@ -103,7 +103,7 @@ await page.ClickAsync("text=Submit");`);
|
||||||
expect(selector).toBe('text=Submit');
|
expect(selector).toBe('text=Submit');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'click'),
|
recorder.waitForOutput('<javascript>', 'click'),
|
||||||
page.dispatchEvent('button', 'click', { detail: 1 })
|
page.dispatchEvent('button', 'click', { detail: 1 })
|
||||||
]);
|
]);
|
||||||
|
|
@ -155,7 +155,7 @@ await page.ClickAsync("text=Submit");`);
|
||||||
expect(divContents).toBe(`<div onclick="console.log('click')"> Some long text here </div>`);
|
expect(divContents).toBe(`<div onclick="console.log('click')"> Some long text here </div>`);
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'click'),
|
recorder.waitForOutput('<javascript>', 'click'),
|
||||||
page.dispatchEvent('div', 'click', { detail: 1 })
|
page.dispatchEvent('div', 'click', { detail: 1 })
|
||||||
]);
|
]);
|
||||||
|
|
@ -173,7 +173,7 @@ await page.ClickAsync("text=Submit");`);
|
||||||
expect(selector).toBe('input[name="name"]');
|
expect(selector).toBe('input[name="name"]');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'fill'),
|
recorder.waitForOutput('<javascript>', 'fill'),
|
||||||
page.fill('input', 'John')
|
page.fill('input', 'John')
|
||||||
]);
|
]);
|
||||||
|
|
@ -208,7 +208,7 @@ await page.FillAsync(\"input[name=\\\"name\\\"]\", \"John\");`);
|
||||||
expect(selector).toBe('textarea[name="name"]');
|
expect(selector).toBe('textarea[name="name"]');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'fill'),
|
recorder.waitForOutput('<javascript>', 'fill'),
|
||||||
page.fill('textarea', 'John')
|
page.fill('textarea', 'John')
|
||||||
]);
|
]);
|
||||||
|
|
@ -322,7 +322,8 @@ await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
||||||
|
|
||||||
const messages: any[] = [];
|
const messages: any[] = [];
|
||||||
page.on('console', message => {
|
page.on('console', message => {
|
||||||
messages.push(message);
|
if (message.type() !== 'error')
|
||||||
|
messages.push(message);
|
||||||
});
|
});
|
||||||
const [, sources] = await Promise.all([
|
const [, sources] = await Promise.all([
|
||||||
recorder.waitForActionPerformed(),
|
recorder.waitForActionPerformed(),
|
||||||
|
|
@ -346,7 +347,7 @@ await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
||||||
expect(selector).toBe('input[name="accept"]');
|
expect(selector).toBe('input[name="accept"]');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'check'),
|
recorder.waitForOutput('<javascript>', 'check'),
|
||||||
page.click('input')
|
page.click('input')
|
||||||
]);
|
]);
|
||||||
|
|
@ -383,7 +384,7 @@ await page.CheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||||
expect(selector).toBe('input[name="accept"]');
|
expect(selector).toBe('input[name="accept"]');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'check'),
|
recorder.waitForOutput('<javascript>', 'check'),
|
||||||
page.keyboard.press('Space')
|
page.keyboard.press('Space')
|
||||||
]);
|
]);
|
||||||
|
|
@ -403,7 +404,7 @@ await page.CheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||||
expect(selector).toBe('input[name="accept"]');
|
expect(selector).toBe('input[name="accept"]');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'uncheck'),
|
recorder.waitForOutput('<javascript>', 'uncheck'),
|
||||||
page.click('input')
|
page.click('input')
|
||||||
]);
|
]);
|
||||||
|
|
@ -440,7 +441,7 @@ await page.UncheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||||
expect(selector).toBe('select');
|
expect(selector).toBe('select');
|
||||||
|
|
||||||
const [message, sources] = await Promise.all([
|
const [message, sources] = await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() !== 'error'),
|
||||||
recorder.waitForOutput('<javascript>', 'select'),
|
recorder.waitForOutput('<javascript>', 'select'),
|
||||||
page.selectOption('select', '2')
|
page.selectOption('select', '2')
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -442,7 +442,10 @@ await page1.GoToAsync("about:blank?foo");`);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const messages: any[] = [];
|
const messages: any[] = [];
|
||||||
page.on('console', message => messages.push(message.text()));
|
page.on('console', message => {
|
||||||
|
if (message.type() !== 'error')
|
||||||
|
messages.push(message.text());
|
||||||
|
});
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.click('button'),
|
page.click('button'),
|
||||||
recorder.waitForOutput('<javascript>', 'page.click')
|
recorder.waitForOutput('<javascript>', 'page.click')
|
||||||
|
|
|
||||||
|
|
@ -93,12 +93,17 @@ class Recorder {
|
||||||
let callback;
|
let callback;
|
||||||
const result = new Promise(f => callback = f);
|
const result = new Promise(f => callback = f);
|
||||||
await page.goto(url);
|
await page.goto(url);
|
||||||
const frames = new Set<any>();
|
let msgCount = 0;
|
||||||
await page.exposeBinding('_recorderScriptReadyForTest', (source, arg) => {
|
const listener = msg => {
|
||||||
frames.add(source.frame);
|
if (msg.text() === 'Recorder script ready for test') {
|
||||||
if (frames.size === frameCount)
|
++msgCount;
|
||||||
callback(arg);
|
if (msgCount === frameCount) {
|
||||||
});
|
page.off('console', listener);
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
page.on('console', listener);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
result,
|
result,
|
||||||
page.setContent(content)
|
page.setContent(content)
|
||||||
|
|
@ -128,23 +133,35 @@ class Recorder {
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForHighlight(action: () => Promise<void>): Promise<string> {
|
async waitForHighlight(action: () => Promise<void>): Promise<string> {
|
||||||
if (!this._highlightInstalled) {
|
let callback;
|
||||||
this._highlightInstalled = true;
|
const result = new Promise<string>(f => callback = f);
|
||||||
await this.page.exposeBinding('_highlightUpdatedForTest', (source, arg) => this._highlightCallback(arg));
|
const listener = async msg => {
|
||||||
}
|
const prefix = 'Highlight updated for test: ';
|
||||||
|
if (msg.text().startsWith(prefix)) {
|
||||||
|
this.page.off('console', listener);
|
||||||
|
callback(msg.text().substr(prefix.length));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.page.on('console', listener);
|
||||||
const [ generatedSelector ] = await Promise.all([
|
const [ generatedSelector ] = await Promise.all([
|
||||||
new Promise<string>(f => this._highlightCallback = f),
|
result,
|
||||||
action()
|
action()
|
||||||
]);
|
]);
|
||||||
return generatedSelector;
|
return generatedSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForActionPerformed(): Promise<{ hovered: string | null, active: string | null }> {
|
async waitForActionPerformed(): Promise<{ hovered: string | null, active: string | null }> {
|
||||||
if (!this._actionReporterInstalled) {
|
let callback;
|
||||||
this._actionReporterInstalled = true;
|
const listener = async msg => {
|
||||||
await this.page.exposeBinding('_actionPerformedForTest', (source, arg) => this._actionPerformedCallback(arg));
|
const prefix = 'Action performed for test: ';
|
||||||
}
|
if (msg.text().startsWith(prefix)) {
|
||||||
return await new Promise(f => this._actionPerformedCallback = f);
|
this.page.off('console', listener);
|
||||||
|
const arg = JSON.parse(msg.text().substr(prefix.length));
|
||||||
|
callback(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.page.on('console', listener);
|
||||||
|
return new Promise(f => callback = f);
|
||||||
}
|
}
|
||||||
|
|
||||||
async hoverOverElement(selector: string): Promise<string> {
|
async hoverOverElement(selector: string): Promise<string> {
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ it.describe('pause', () => {
|
||||||
const scriptPromise = (async () => {
|
const scriptPromise = (async () => {
|
||||||
await page.pause();
|
await page.pause();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForEvent('console'),
|
page.waitForEvent('console', msg => msg.type() === 'log' && msg.text() === '1'),
|
||||||
page.click('button'),
|
page.click('button'),
|
||||||
]);
|
]);
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue