feat: add page.onMouseAction callback + tests
Adds onMouseAction to playwright fork — an async callback on Page that fires before every mouse action (move/down/up/wheel). Covers both page.mouse.* and locator-initiated actions. Test validates: - Callback fires for page.mouse.click() with correct coordinates - Callback fires for locator.click() with resolved element center - Callback stops firing when set to null
This commit is contained in:
+1
-1
Submodule playwright updated: fc998ca899...a0239178da
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Test for the page.onMouseAction callback added to the playwright fork.
|
||||
* Verifies the callback fires for both explicit page.mouse.* calls
|
||||
* and locator-initiated actions like page.locator().click().
|
||||
*/
|
||||
import { chromium } from '@xmorse/playwright-core'
|
||||
import type { MouseActionEvent } from '@xmorse/playwright-core'
|
||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
||||
import { getCdpUrl } from './utils.js'
|
||||
import {
|
||||
setupTestContext,
|
||||
cleanupTestContext,
|
||||
getExtensionServiceWorker,
|
||||
type TestContext,
|
||||
safeCloseCDPBrowser,
|
||||
} from './test-utils.js'
|
||||
import './test-declarations.js'
|
||||
|
||||
const TEST_PORT = 19994
|
||||
|
||||
describe('onMouseAction callback', () => {
|
||||
let cleanup: (() => Promise<void>) | null = null
|
||||
let testCtx: TestContext | null = null
|
||||
|
||||
beforeAll(async () => {
|
||||
testCtx = await setupTestContext({
|
||||
port: TEST_PORT,
|
||||
tempDirPrefix: 'pw-mouse-action-test-',
|
||||
toggleExtension: true,
|
||||
})
|
||||
}, 600000)
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanupTestContext(testCtx, cleanup)
|
||||
cleanup = null
|
||||
testCtx = null
|
||||
})
|
||||
|
||||
it('should fire onMouseAction for page.mouse.click()', async () => {
|
||||
const browserContext = testCtx!.browserContext
|
||||
const serviceWorker = await getExtensionServiceWorker(browserContext)
|
||||
|
||||
const page = await browserContext.newPage()
|
||||
await page.goto('data:text/html,<html><body><button id="btn">Click me</button></body></html>')
|
||||
await page.bringToFront()
|
||||
|
||||
await serviceWorker.evaluate(async () => {
|
||||
await (globalThis as any).toggleExtensionForActiveTab()
|
||||
})
|
||||
await new Promise((r) => { setTimeout(r, 200) })
|
||||
|
||||
const directBrowser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
|
||||
const contexts = directBrowser.contexts()
|
||||
const pages = contexts[0].pages()
|
||||
const targetPage = pages.find((p) => p.url().startsWith('data:'))
|
||||
expect(targetPage).toBeDefined()
|
||||
|
||||
const events: MouseActionEvent[] = []
|
||||
targetPage!.onMouseAction = async (event) => {
|
||||
events.push({ ...event })
|
||||
}
|
||||
|
||||
await targetPage!.mouse.click(100, 100)
|
||||
|
||||
// click dispatches: move → down → up
|
||||
const types = events.map((e) => e.type)
|
||||
expect(types).toContain('move')
|
||||
expect(types).toContain('down')
|
||||
expect(types).toContain('up')
|
||||
|
||||
// All events should have coordinates
|
||||
for (const event of events) {
|
||||
expect(typeof event.x).toBe('number')
|
||||
expect(typeof event.y).toBe('number')
|
||||
expect(typeof event.button).toBe('string')
|
||||
}
|
||||
|
||||
// The move event should have the target coordinates
|
||||
const moveEvent = events.find((e) => e.type === 'move')!
|
||||
expect(moveEvent.x).toBe(100)
|
||||
expect(moveEvent.y).toBe(100)
|
||||
|
||||
await safeCloseCDPBrowser(directBrowser)
|
||||
}, 30000)
|
||||
|
||||
it('should fire onMouseAction for locator.click()', async () => {
|
||||
const browserContext = testCtx!.browserContext
|
||||
|
||||
const directBrowser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
|
||||
const contexts = directBrowser.contexts()
|
||||
const pages = contexts[0].pages()
|
||||
const targetPage = pages.find((p) => p.url().startsWith('data:'))
|
||||
expect(targetPage).toBeDefined()
|
||||
|
||||
const events: MouseActionEvent[] = []
|
||||
targetPage!.onMouseAction = async (event) => {
|
||||
events.push({ ...event })
|
||||
}
|
||||
|
||||
// locator.click() resolves coordinates server-side, then calls server Mouse
|
||||
await targetPage!.locator('#btn').click()
|
||||
|
||||
const types = events.map((e) => e.type)
|
||||
expect(types).toContain('move')
|
||||
expect(types).toContain('down')
|
||||
expect(types).toContain('up')
|
||||
|
||||
// The button center should be somewhere reasonable (not 0,0)
|
||||
const moveEvent = events.find((e) => e.type === 'move')!
|
||||
expect(moveEvent.x).toBeGreaterThan(0)
|
||||
expect(moveEvent.y).toBeGreaterThan(0)
|
||||
|
||||
await safeCloseCDPBrowser(directBrowser)
|
||||
}, 30000)
|
||||
|
||||
it('should not fire when onMouseAction is set to null', async () => {
|
||||
const browserContext = testCtx!.browserContext
|
||||
|
||||
const directBrowser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
|
||||
const contexts = directBrowser.contexts()
|
||||
const pages = contexts[0].pages()
|
||||
const targetPage = pages.find((p) => p.url().startsWith('data:'))
|
||||
expect(targetPage).toBeDefined()
|
||||
|
||||
const events: MouseActionEvent[] = []
|
||||
targetPage!.onMouseAction = async (event) => {
|
||||
events.push({ ...event })
|
||||
}
|
||||
|
||||
// Disable callback
|
||||
targetPage!.onMouseAction = null
|
||||
|
||||
await targetPage!.mouse.click(50, 50)
|
||||
|
||||
expect(events).toHaveLength(0)
|
||||
|
||||
await safeCloseCDPBrowser(directBrowser)
|
||||
}, 30000)
|
||||
})
|
||||
Reference in New Issue
Block a user