mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
test: unflake recorder tests (#2808)
We ensure that recorder is installed in the main frame before running the test.
This commit is contained in:
parent
baaa65492b
commit
35cb20d5ad
@ -18,26 +18,37 @@ import { Writable } from 'stream';
|
||||
import { BrowserContextBase } from '../browserContext';
|
||||
import { Events } from '../events';
|
||||
import * as frames from '../frames';
|
||||
import * as js from '../javascript';
|
||||
import { Page } from '../page';
|
||||
import { RecorderController } from './recorderController';
|
||||
import DebugScript from './injected/debugScript';
|
||||
|
||||
export class DebugController {
|
||||
private _options: { recorderOutput?: Writable | undefined };
|
||||
|
||||
constructor(context: BrowserContextBase, options: { recorderOutput?: Writable | undefined }) {
|
||||
const installInFrame = async (frame: frames.Frame) => {
|
||||
try {
|
||||
const mainContext = await frame._mainContext();
|
||||
await mainContext.createDebugScript({ console: true, record: !!options.recorderOutput });
|
||||
} catch (e) {
|
||||
}
|
||||
};
|
||||
this._options = options;
|
||||
|
||||
if (options.recorderOutput)
|
||||
new RecorderController(context, options.recorderOutput);
|
||||
|
||||
context.on(Events.BrowserContext.Page, (page: Page) => {
|
||||
for (const frame of page.frames())
|
||||
installInFrame(frame);
|
||||
page.on(Events.Page.FrameNavigated, installInFrame);
|
||||
this.ensureInstalledInFrame(frame);
|
||||
page.on(Events.Page.FrameNavigated, frame => this.ensureInstalledInFrame(frame));
|
||||
});
|
||||
}
|
||||
|
||||
private async ensureInstalledInFrame(frame: frames.Frame): Promise<js.JSHandle<DebugScript> | undefined> {
|
||||
try {
|
||||
const mainContext = await frame._mainContext();
|
||||
return await mainContext.createDebugScript({ console: true, record: !!this._options.recorderOutput });
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
async ensureInstalledInFrameForTest(frame: frames.Frame): Promise<void> {
|
||||
const handle = await this.ensureInstalledInFrame(frame);
|
||||
await handle!.evaluate(debugScript => debugScript.recorder!.refreshListeners());
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,6 @@ export default class DebugScript {
|
||||
consoleAPI: ConsoleAPI | undefined;
|
||||
recorder: Recorder | undefined;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
initialize(injectedScript: InjectedScript, options: { console?: boolean, record?: boolean }) {
|
||||
if (options.console)
|
||||
this.consoleAPI = new ConsoleAPI(injectedScript);
|
||||
|
@ -28,13 +28,27 @@ declare global {
|
||||
export class Recorder {
|
||||
private _injectedScript: InjectedScript;
|
||||
private _performingAction = false;
|
||||
readonly refreshListeners: () => void;
|
||||
|
||||
constructor(injectedScript: InjectedScript) {
|
||||
this._injectedScript = injectedScript;
|
||||
|
||||
document.addEventListener('click', event => this._onClick(event), true);
|
||||
document.addEventListener('input', event => this._onInput(event), true);
|
||||
document.addEventListener('keydown', event => this._onKeyDown(event), true);
|
||||
const onClick = this._onClick.bind(this);
|
||||
const onInput = this._onInput.bind(this);
|
||||
const onKeyDown = this._onKeyDown.bind(this);
|
||||
this.refreshListeners = () => {
|
||||
document.removeEventListener('click', onClick, true);
|
||||
document.removeEventListener('input', onInput, true);
|
||||
document.removeEventListener('keydown', onKeyDown, true);
|
||||
document.addEventListener('click', onClick, true);
|
||||
document.addEventListener('input', onInput, true);
|
||||
document.addEventListener('keydown', onKeyDown, true);
|
||||
};
|
||||
this.refreshListeners();
|
||||
// Document listeners are cleared upon document.open,
|
||||
// so we refresh them periodically in a best-effort manner.
|
||||
// Note: keep in sync with the same constant in the test.
|
||||
setInterval(this.refreshListeners, 1000);
|
||||
}
|
||||
|
||||
private async _onClick(event: MouseEvent) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const {USES_HOOKS} = require('./utils').testOptions(browserType);
|
||||
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = require('./utils').testOptions(browserType);
|
||||
|
||||
class WritableBuffer {
|
||||
constructor() {
|
||||
@ -56,16 +56,20 @@ describe.skip(USES_HOOKS)('Recorder', function() {
|
||||
state.context = await state.browser.newContext();
|
||||
state.output = new WritableBuffer();
|
||||
const debugController = state.context._initDebugModeForTest({ recorderOutput: state.output });
|
||||
state.page = await state.context.newPage();
|
||||
state.setContent = async (content) => {
|
||||
await state.page.setContent(content);
|
||||
await debugController.ensureInstalledInFrameForTest(state.page.mainFrame());
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
afterEach(async state => {
|
||||
await state.context.close();
|
||||
});
|
||||
|
||||
it('should click', async function({context, output, server}) {
|
||||
const page = await context.newPage();
|
||||
|
||||
it('should click', async function({page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<button onclick="console.log('click')">Submit</button>`);
|
||||
await setContent(`<button onclick="console.log('click')">Submit</button>`);
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
output.waitFor('click'),
|
||||
@ -77,10 +81,30 @@ describe.skip(USES_HOOKS)('Recorder', function() {
|
||||
expect(message.text()).toBe('click');
|
||||
});
|
||||
|
||||
it('should fill', async function({context, output, server}) {
|
||||
const page = await context.newPage();
|
||||
it('should click after document.open', async function({page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<input id="input" name="name" oninput="console.log(input.value)"></input>`);
|
||||
await setContent(``);
|
||||
await page.evaluate(() => {
|
||||
document.open();
|
||||
document.write(`<button onclick="console.log('click')">Submit</button>`);
|
||||
document.close();
|
||||
// Give it time to refresh. See Recorder for details.
|
||||
return new Promise(f => setTimeout(f, 1000));
|
||||
});
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
output.waitFor('click'),
|
||||
page.dispatchEvent('button', 'click', { detail: 1 })
|
||||
]);
|
||||
expect(output.text()).toContain(`
|
||||
// Click text="Submit"
|
||||
await page.click('text="Submit"');`);
|
||||
expect(message.text()).toBe('click');
|
||||
});
|
||||
|
||||
it('should fill', async function({page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await setContent(`<input id="input" name="name" oninput="console.log(input.value)"></input>`);
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
output.waitFor('fill'),
|
||||
@ -92,10 +116,9 @@ describe.skip(USES_HOOKS)('Recorder', function() {
|
||||
expect(message.text()).toBe('John');
|
||||
});
|
||||
|
||||
it('should press', async function({context, output, server}) {
|
||||
const page = await context.newPage();
|
||||
it('should press', async function({page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<input name="name" onkeypress="console.log('press')"></input>`);
|
||||
await setContent(`<input name="name" onkeypress="console.log('press')"></input>`);
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
output.waitFor('press'),
|
||||
@ -107,10 +130,9 @@ describe.skip(USES_HOOKS)('Recorder', function() {
|
||||
expect(message.text()).toBe('press');
|
||||
});
|
||||
|
||||
it('should check', async function({context, output, server}) {
|
||||
const page = await context.newPage();
|
||||
it('should check', async function({page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<input id="checkbox" type="checkbox" name="accept" onchange="console.log(checkbox.checked)"></input>`);
|
||||
await setContent(`<input id="checkbox" type="checkbox" name="accept" onchange="console.log(checkbox.checked)"></input>`);
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
output.waitFor('check'),
|
||||
@ -123,10 +145,9 @@ describe.skip(USES_HOOKS)('Recorder', function() {
|
||||
expect(message.text()).toBe("true");
|
||||
});
|
||||
|
||||
it('should uncheck', async function({context, output, server}) {
|
||||
const page = await context.newPage();
|
||||
it('should uncheck', async function({page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<input id="checkbox" type="checkbox" checked name="accept" onchange="console.log(checkbox.checked)"></input>`);
|
||||
await setContent(`<input id="checkbox" type="checkbox" checked name="accept" onchange="console.log(checkbox.checked)"></input>`);
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
output.waitFor('uncheck'),
|
||||
@ -138,10 +159,9 @@ describe.skip(USES_HOOKS)('Recorder', function() {
|
||||
expect(message.text()).toBe("false");
|
||||
});
|
||||
|
||||
it('should select', async function({context, output, server}) {
|
||||
const page = await context.newPage();
|
||||
it('should select', async function({page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<select id="age" onchange="console.log(age.selectedOptions[0].value)"><option value="1"><option value="2"></select>');
|
||||
await setContent('<select id="age" onchange="console.log(age.selectedOptions[0].value)"><option value="1"><option value="2"></select>');
|
||||
const [message] = await Promise.all([
|
||||
page.waitForEvent('console'),
|
||||
output.waitFor('select'),
|
||||
@ -153,10 +173,9 @@ describe.skip(USES_HOOKS)('Recorder', function() {
|
||||
expect(message.text()).toBe("2");
|
||||
});
|
||||
|
||||
it('should await popup', async function({context, output, server}) {
|
||||
const page = await context.newPage();
|
||||
it('should await popup', async function({context, page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a target=_blank rel=noopener href="/popup/popup.html">link</a>');
|
||||
await setContent('<a target=_blank rel=noopener href="/popup/popup.html">link</a>');
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
output.waitFor('waitForEvent'),
|
||||
@ -171,10 +190,9 @@ describe.skip(USES_HOOKS)('Recorder', function() {
|
||||
expect(popup.url()).toBe(`${server.PREFIX}/popup/popup.html`);
|
||||
});
|
||||
|
||||
it('should await navigation', async function({context, output, server}) {
|
||||
const page = await context.newPage();
|
||||
it('should await navigation', async function({page, output, setContent, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a onclick="setTimeout(() => window.location.href='${server.PREFIX}/popup/popup.html', 1000)">link</a>`);
|
||||
await setContent(`<a onclick="setTimeout(() => window.location.href='${server.PREFIX}/popup/popup.html', 1000)">link</a>`);
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
output.waitFor('waitForNavigation'),
|
||||
|
Loading…
x
Reference in New Issue
Block a user