Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 43 additions & 20 deletions packages/web-integration/src/puppeteer/base-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ type ScreencastCdpSession = {
): void;
};

type InputCdpSession = {
send(method: string, params?: Record<string, unknown>): Promise<unknown>;
detach(): Promise<void>;
};

function isClosedPageError(error: unknown) {
if (!(error instanceof Error)) {
return false;
Expand Down Expand Up @@ -798,36 +803,54 @@ export class Page<
};
}

private async createInputCdpSession(): Promise<InputCdpSession> {
if (this.interfaceType === 'puppeteer') {
const page = this.underlyingPage as PuppeteerPage;
return (await (typeof page.createCDPSession === 'function'
? page.createCDPSession()
: page.target().createCDPSession())) as unknown as InputCdpSession;
}

const page = this.underlyingPage as PlaywrightPage;
return (await page
.context()
.newCDPSession(page)) as unknown as InputCdpSession;
}

private async selectAllByCdp(): Promise<void> {
const client = await this.createInputCdpSession();
try {
await client.send('Input.dispatchKeyEvent', {
type: 'rawKeyDown',

commands: ['selectAll'],
});
await client.send('Input.dispatchKeyEvent', {
type: 'keyUp',
});
} finally {
await client.detach().catch(() => undefined);
}
}

async clearInput(element?: ElementInfo): Promise<void> {
const backspace = async () => {
await sleep(100);
await this.keyboard.press([{ key: 'Backspace' }]);
};

const isMac = process.platform === 'darwin';
debugPage('clearInput begin');
if (isMac) {
if (this.interfaceType === 'puppeteer') {
// https://github.com/segment-boneyard/nightmare/issues/810#issuecomment-452669866
element &&
(await this.mouse.click(element.center[0], element.center[1], {
count: 3,
}));
await backspace();
}

element && (await this.mouse.click(element.center[0], element.center[1]));
await this.underlyingPage.keyboard.down('Meta');
await this.underlyingPage.keyboard.press('a');
await this.underlyingPage.keyboard.up('Meta');
await backspace();
} else {
element && (await this.mouse.click(element.center[0], element.center[1]));
await this.underlyingPage.keyboard.down('Control');
await this.underlyingPage.keyboard.press('a');
await this.underlyingPage.keyboard.up('Control');
element && (await this.mouse.click(element.center[0], element.center[1]));
try {
await this.selectAllByCdp();
await backspace();
debugPage('clearInput end');
return;
} catch (error) {
debugPage('clearInput cdp selectAll failed, fallback to shortcut', error);
}

debugPage('clearInput end');
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { WebPage as PlaywrightWebPage } from '@/playwright/page';
import { PuppeteerWebPage } from '@/puppeteer/page';
import { type Browser as PlaywrightBrowser, chromium } from 'playwright';
import puppeteer, { type Browser as PuppeteerBrowser } from 'puppeteer';
import { afterAll, beforeAll, describe, expect, test } from 'vitest';

const TEST_TIMEOUT_MS = 120_000;

const PAGE_HTML = `
<!DOCTYPE html>
<html>
<body style="padding: 24px;">
<input id="target" value="value to clear" style="width: 240px; padding: 8px;" />
</body>
</html>
`;

async function puppeteerInputCenter(page: any): Promise<[number, number]> {
return page.$eval('#target', (el: HTMLInputElement) => {
const rect = el.getBoundingClientRect();
return [rect.left + rect.width / 2, rect.top + rect.height / 2];
});
}

async function playwrightInputCenter(page: any): Promise<[number, number]> {
return page.locator('#target').evaluate((el: HTMLInputElement) => {
const rect = el.getBoundingClientRect();
return [rect.left + rect.width / 2, rect.top + rect.height / 2];
});
}

describe('BasePage clearInput CDP selectAll', () => {
describe('Puppeteer', () => {
let browser: PuppeteerBrowser;

beforeAll(async () => {
browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
}, TEST_TIMEOUT_MS);

afterAll(async () => {
await browser?.close();
}, TEST_TIMEOUT_MS);

test(
'clears the focused input',
async () => {
const page = await browser.newPage();
await page.setContent(PAGE_HTML);

const webPage = new PuppeteerWebPage(page);
const center = await puppeteerInputCenter(page);

await webPage.clearInput({ center } as any);

const value = await page.$eval(
'#target',
(el) => (el as HTMLInputElement).value,
);
await page.close();

expect(value).toBe('');
},
TEST_TIMEOUT_MS,
);
});

describe('Playwright', () => {
let browser: PlaywrightBrowser;

beforeAll(async () => {
browser = await chromium.launch({
headless: true,
// CI installs Puppeteer's Chrome cache, but not Playwright's browser
// bundle because dependencies are installed with --ignore-scripts.
executablePath: puppeteer.executablePath(),
});
}, TEST_TIMEOUT_MS);

afterAll(async () => {
await browser?.close();
}, TEST_TIMEOUT_MS);

test(
'clears the focused input',
async () => {
const page = await browser.newPage();
await page.setContent(PAGE_HTML);

const webPage = new PlaywrightWebPage(page);
const center = await playwrightInputCenter(page);

await webPage.clearInput({ center } as any);

const value = await page.locator('#target').evaluate((el) => {
return (el as HTMLInputElement).value;
});
await page.close();

expect(value).toBe('');
},
TEST_TIMEOUT_MS,
);
});
});
Loading