summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/test/src/mouse.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/test/src/mouse.spec.ts')
-rw-r--r--remote/test/puppeteer/test/src/mouse.spec.ts472
1 files changed, 472 insertions, 0 deletions
diff --git a/remote/test/puppeteer/test/src/mouse.spec.ts b/remote/test/puppeteer/test/src/mouse.spec.ts
new file mode 100644
index 0000000000..69229eb147
--- /dev/null
+++ b/remote/test/puppeteer/test/src/mouse.spec.ts
@@ -0,0 +1,472 @@
+/**
+ * @license
+ * Copyright 2018 Google Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import os from 'os';
+
+import expect from 'expect';
+import {MouseButton} from 'puppeteer-core/internal/api/Input.js';
+import type {Page} from 'puppeteer-core/internal/api/Page.js';
+import type {KeyInput} from 'puppeteer-core/internal/common/USKeyboardLayout.js';
+
+import {getTestState, setupTestBrowserHooks} from './mocha-utils.js';
+
+interface ClickData {
+ type: string;
+ detail: number;
+ clientX: number;
+ clientY: number;
+ isTrusted: boolean;
+ button: number;
+ buttons: number;
+}
+
+interface Dimensions {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+}
+
+function dimensions(): Dimensions {
+ const rect = document.querySelector('textarea')!.getBoundingClientRect();
+ return {
+ x: rect.left,
+ y: rect.top,
+ width: rect.width,
+ height: rect.height,
+ };
+}
+
+describe('Mouse', function () {
+ setupTestBrowserHooks();
+
+ it('should click the document', async () => {
+ const {page} = await getTestState();
+
+ await page.evaluate(() => {
+ (globalThis as any).clickPromise = new Promise(resolve => {
+ document.addEventListener('click', event => {
+ resolve({
+ type: event.type,
+ detail: event.detail,
+ clientX: event.clientX,
+ clientY: event.clientY,
+ isTrusted: event.isTrusted,
+ button: event.button,
+ });
+ });
+ });
+ });
+ await page.mouse.click(50, 60);
+ const event = await page.evaluate(() => {
+ return (globalThis as any).clickPromise;
+ });
+ expect(event.type).toBe('click');
+ expect(event.detail).toBe(1);
+ expect(event.clientX).toBe(50);
+ expect(event.clientY).toBe(60);
+ expect(event.isTrusted).toBe(true);
+ expect(event.button).toBe(0);
+ });
+ it('should resize the textarea', async () => {
+ const {page, server} = await getTestState();
+
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ const {x, y, width, height} = await page.evaluate(dimensions);
+ const mouse = page.mouse;
+ await mouse.move(x + width - 4, y + height - 4);
+ await mouse.down();
+ await mouse.move(x + width + 100, y + height + 100);
+ await mouse.up();
+ const newDimensions = await page.evaluate(dimensions);
+ expect(newDimensions.width).toBe(Math.round(width + 104));
+ expect(newDimensions.height).toBe(Math.round(height + 104));
+ });
+ it('should select the text with mouse', async () => {
+ const {page, server} = await getTestState();
+
+ const text =
+ "This is the text that we are going to try to select. Let's see how it goes.";
+
+ await page.goto(`${server.PREFIX}/input/textarea.html`);
+ await page.focus('textarea');
+ await page.keyboard.type(text);
+ using handle = await page
+ .locator('textarea')
+ .filterHandle(async element => {
+ return await element.evaluate((element, text) => {
+ return element.value === text;
+ }, text);
+ })
+ .waitHandle();
+ const {x, y} = await page.evaluate(dimensions);
+ await page.mouse.move(x + 2, y + 2);
+ await page.mouse.down();
+ await page.mouse.move(100, 100);
+ await page.mouse.up();
+ expect(
+ await handle.evaluate(element => {
+ return element.value.substring(
+ element.selectionStart,
+ element.selectionEnd
+ );
+ })
+ ).toBe(text);
+ });
+ it('should trigger hover state', async () => {
+ const {page, server} = await getTestState();
+
+ await page.goto(server.PREFIX + '/input/scrollable.html');
+ await page.hover('#button-6');
+ expect(
+ await page.evaluate(() => {
+ return document.querySelector('button:hover')!.id;
+ })
+ ).toBe('button-6');
+ await page.hover('#button-2');
+ expect(
+ await page.evaluate(() => {
+ return document.querySelector('button:hover')!.id;
+ })
+ ).toBe('button-2');
+ await page.hover('#button-91');
+ expect(
+ await page.evaluate(() => {
+ return document.querySelector('button:hover')!.id;
+ })
+ ).toBe('button-91');
+ });
+ it('should trigger hover state with removed window.Node', async () => {
+ const {page, server} = await getTestState();
+
+ await page.goto(server.PREFIX + '/input/scrollable.html');
+ await page.evaluate(() => {
+ // @ts-expect-error Expected.
+ return delete window.Node;
+ });
+ await page.hover('#button-6');
+ expect(
+ await page.evaluate(() => {
+ return document.querySelector('button:hover')!.id;
+ })
+ ).toBe('button-6');
+ });
+ it('should set modifier keys on click', async () => {
+ const {page, server, isFirefox} = await getTestState();
+
+ await page.goto(server.PREFIX + '/input/scrollable.html');
+ await page.evaluate(() => {
+ return document.querySelector('#button-3')!.addEventListener(
+ 'mousedown',
+ e => {
+ return ((globalThis as any).lastEvent = e);
+ },
+ true
+ );
+ });
+ const modifiers = new Map<KeyInput, string>([
+ ['Shift', 'shiftKey'],
+ ['Control', 'ctrlKey'],
+ ['Alt', 'altKey'],
+ ['Meta', 'metaKey'],
+ ]);
+ // In Firefox, the Meta modifier only exists on Mac
+ if (isFirefox && os.platform() !== 'darwin') {
+ modifiers.delete('Meta');
+ }
+ for (const [modifier, key] of modifiers) {
+ await page.keyboard.down(modifier);
+ await page.click('#button-3');
+ if (
+ !(await page.evaluate((mod: string) => {
+ return (globalThis as any).lastEvent[mod];
+ }, key))
+ ) {
+ throw new Error(key + ' should be true');
+ }
+ await page.keyboard.up(modifier);
+ }
+ await page.click('#button-3');
+ for (const [modifier, key] of modifiers) {
+ if (
+ await page.evaluate((mod: string) => {
+ return (globalThis as any).lastEvent[mod];
+ }, key)
+ ) {
+ throw new Error(modifiers.get(modifier) + ' should be false');
+ }
+ }
+ });
+ it('should send mouse wheel events', async () => {
+ const {page, server} = await getTestState();
+
+ await page.goto(server.PREFIX + '/input/wheel.html');
+ using elem = (await page.$('div'))!;
+ const boundingBoxBefore = (await elem.boundingBox())!;
+ expect(boundingBoxBefore).toMatchObject({
+ width: 115,
+ height: 115,
+ });
+
+ await page.mouse.move(
+ boundingBoxBefore.x + boundingBoxBefore.width / 2,
+ boundingBoxBefore.y + boundingBoxBefore.height / 2
+ );
+
+ await page.mouse.wheel({deltaY: -100});
+ const boundingBoxAfter = await elem.boundingBox();
+ expect(boundingBoxAfter).toMatchObject({
+ width: 230,
+ height: 230,
+ });
+ });
+ it('should tween mouse movement', async () => {
+ const {page} = await getTestState();
+
+ await page.mouse.move(100, 100);
+ await page.evaluate(() => {
+ (globalThis as any).result = [];
+ document.addEventListener('mousemove', event => {
+ (globalThis as any).result.push([event.clientX, event.clientY]);
+ });
+ });
+ await page.mouse.move(200, 300, {steps: 5});
+ expect(await page.evaluate('result')).toEqual([
+ [120, 140],
+ [140, 180],
+ [160, 220],
+ [180, 260],
+ [200, 300],
+ ]);
+ });
+ // @see https://crbug.com/929806
+ it('should work with mobile viewports and cross process navigations', async () => {
+ const {page, server} = await getTestState();
+
+ await page.goto(server.EMPTY_PAGE);
+ await page.setViewport({width: 360, height: 640, isMobile: true});
+ await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html');
+ await page.evaluate(() => {
+ document.addEventListener('click', event => {
+ (globalThis as any).result = {x: event.clientX, y: event.clientY};
+ });
+ });
+
+ await page.mouse.click(30, 40);
+
+ expect(await page.evaluate('result')).toEqual({x: 30, y: 40});
+ });
+ it('should not throw if buttons are pressed twice', async () => {
+ const {page, server} = await getTestState();
+
+ await page.goto(server.EMPTY_PAGE);
+
+ await page.mouse.down();
+ await page.mouse.down();
+ });
+
+ interface AddMouseDataListenersOptions {
+ includeMove?: boolean;
+ }
+
+ const addMouseDataListeners = (
+ page: Page,
+ options: AddMouseDataListenersOptions = {}
+ ) => {
+ return page.evaluate(({includeMove}) => {
+ const clicks: ClickData[] = [];
+ const mouseEventListener = (event: MouseEvent) => {
+ clicks.push({
+ type: event.type,
+ detail: event.detail,
+ clientX: event.clientX,
+ clientY: event.clientY,
+ isTrusted: event.isTrusted,
+ button: event.button,
+ buttons: event.buttons,
+ });
+ };
+ document.addEventListener('mousedown', mouseEventListener);
+ if (includeMove) {
+ document.addEventListener('mousemove', mouseEventListener);
+ }
+ document.addEventListener('mouseup', mouseEventListener);
+ document.addEventListener('click', mouseEventListener);
+ document.addEventListener('auxclick', mouseEventListener);
+ (window as unknown as {clicks: ClickData[]}).clicks = clicks;
+ }, options);
+ };
+
+ it('should not throw if clicking in parallel', async () => {
+ const {page, server} = await getTestState();
+
+ await page.goto(server.EMPTY_PAGE);
+ await addMouseDataListeners(page);
+
+ await Promise.all([page.mouse.click(0, 5), page.mouse.click(6, 10)]);
+
+ const data = await page.evaluate(() => {
+ return (window as unknown as {clicks: ClickData[]}).clicks;
+ });
+ const commonAttrs = {
+ isTrusted: true,
+ detail: 1,
+ clientY: 5,
+ clientX: 0,
+ button: 0,
+ };
+ expect(data.splice(0, 3)).toMatchObject({
+ 0: {
+ type: 'mousedown',
+ buttons: 1,
+ ...commonAttrs,
+ },
+ 1: {
+ type: 'mouseup',
+ buttons: 0,
+ ...commonAttrs,
+ },
+ 2: {
+ type: 'click',
+ buttons: 0,
+ ...commonAttrs,
+ },
+ });
+ Object.assign(commonAttrs, {
+ clientX: 6,
+ clientY: 10,
+ });
+ expect(data).toMatchObject({
+ 0: {
+ type: 'mousedown',
+ buttons: 1,
+ ...commonAttrs,
+ },
+ 1: {
+ type: 'mouseup',
+ buttons: 0,
+ ...commonAttrs,
+ },
+ 2: {
+ type: 'click',
+ buttons: 0,
+ ...commonAttrs,
+ },
+ });
+ });
+
+ it('should reset properly', async () => {
+ const {page, server, isChrome} = await getTestState();
+
+ await page.goto(server.EMPTY_PAGE);
+
+ await page.mouse.move(5, 5);
+ await Promise.all([
+ page.mouse.down({button: MouseButton.Left}),
+ page.mouse.down({button: MouseButton.Middle}),
+ page.mouse.down({button: MouseButton.Right}),
+ ]);
+
+ await addMouseDataListeners(page, {includeMove: true});
+ await page.mouse.reset();
+
+ const data = await page.evaluate(() => {
+ return (window as unknown as {clicks: ClickData[]}).clicks;
+ });
+ const commonAttrs = {
+ isTrusted: true,
+ clientY: 5,
+ clientX: 5,
+ };
+
+ expect(data.slice(0, 2)).toMatchObject([
+ {
+ ...commonAttrs,
+ button: 2,
+ buttons: 5,
+ detail: 1,
+ type: 'mouseup',
+ },
+ {
+ ...commonAttrs,
+ button: 2,
+ buttons: 5,
+ detail: 1,
+ type: 'auxclick',
+ },
+ ]);
+ // TODO(crbug/1485040): This should align with the firefox implementation.
+ if (isChrome) {
+ expect(data.slice(2)).toMatchObject([
+ {
+ ...commonAttrs,
+ button: 1,
+ buttons: 1,
+ detail: 0,
+ type: 'mouseup',
+ },
+ {
+ ...commonAttrs,
+ button: 0,
+ buttons: 0,
+ detail: 0,
+ type: 'mouseup',
+ },
+ ]);
+ return;
+ }
+ expect(data.slice(2)).toMatchObject([
+ {
+ ...commonAttrs,
+ button: 1,
+ buttons: 1,
+ detail: 1,
+ type: 'mouseup',
+ },
+ {
+ ...commonAttrs,
+ button: 1,
+ buttons: 1,
+ detail: 1,
+ type: 'auxclick',
+ },
+ {
+ ...commonAttrs,
+ button: 0,
+ buttons: 0,
+ detail: 1,
+ type: 'mouseup',
+ },
+ {
+ ...commonAttrs,
+ button: 0,
+ buttons: 0,
+ detail: 1,
+ type: 'click',
+ },
+ ]);
+ });
+
+ it('should evaluate before mouse event', async () => {
+ const {page, server} = await getTestState();
+
+ await page.goto(server.EMPTY_PAGE);
+ await page.goto(server.CROSS_PROCESS_PREFIX + '/input/button.html');
+
+ using button = await page.waitForSelector('button');
+
+ const point = await button!.clickablePoint();
+
+ const result = page.evaluate(() => {
+ return new Promise(resolve => {
+ document
+ .querySelector('button')
+ ?.addEventListener('click', resolve, {once: true});
+ });
+ });
+ await page.mouse.click(point?.x, point?.y);
+ await result;
+ });
+});