From 373613e57d7cb2dfd78ec880b8856a33aa20e29a Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 29 Nov 2024 10:02:39 +0900 Subject: [PATCH 1/5] feat(browser): support `actionTimeout` as playwright provider options --- packages/browser/matchers.d.ts | 4 ++++ packages/browser/providers/playwright.d.ts | 7 ++++++- packages/browser/src/node/commands/clear.ts | 4 +--- packages/browser/src/node/commands/click.ts | 6 +----- packages/browser/src/node/commands/dragAndDrop.ts | 5 +---- packages/browser/src/node/commands/fill.ts | 2 +- packages/browser/src/node/commands/hover.ts | 5 +---- packages/browser/src/node/commands/screenshot.ts | 1 - packages/browser/src/node/commands/select.ts | 5 +---- packages/browser/src/node/providers/playwright.ts | 10 +++++++--- 10 files changed, 23 insertions(+), 26 deletions(-) diff --git a/packages/browser/matchers.d.ts b/packages/browser/matchers.d.ts index 8f4540f5129f..45ec41ec87b4 100644 --- a/packages/browser/matchers.d.ts +++ b/packages/browser/matchers.d.ts @@ -17,6 +17,10 @@ declare module 'vitest' { type PromisifyDomAssertion = Promisify> interface ExpectStatic { + /** + * `expect.element(locator)` is a shorthand for `expect.poll(() => locator.element())`. + * You can set default timeout via `expect.poll.timeout` config. + */ element: (element: T, options?: ExpectPollOptions) => PromisifyDomAssertion> } } diff --git a/packages/browser/providers/playwright.d.ts b/packages/browser/providers/playwright.d.ts index f98cee599724..b6e9d0744e3c 100644 --- a/packages/browser/providers/playwright.d.ts +++ b/packages/browser/providers/playwright.d.ts @@ -16,7 +16,12 @@ declare module 'vitest/node' { context?: Omit< BrowserContextOptions, 'ignoreHTTPSErrors' | 'serviceWorkers' - > + > & { + /** + * The maximum time in milliseconds to wait for `userEvent` action to complete. + * @default TODO: 1000? or 0 like playwright? + */ + actionTimeout?: number } } export interface BrowserCommandContext { diff --git a/packages/browser/src/node/commands/clear.ts b/packages/browser/src/node/commands/clear.ts index a55d7c4eea8c..9de4737eeae7 100644 --- a/packages/browser/src/node/commands/clear.ts +++ b/packages/browser/src/node/commands/clear.ts @@ -10,9 +10,7 @@ export const clear: UserEventCommand = async ( if (context.provider instanceof PlaywrightBrowserProvider) { const { iframe } = context const element = iframe.locator(selector) - await element.clear({ - timeout: 1000, - }) + await element.clear() } else if (context.provider instanceof WebdriverBrowserProvider) { const browser = context.browser diff --git a/packages/browser/src/node/commands/click.ts b/packages/browser/src/node/commands/click.ts index d3d24bd179b0..075f24adc059 100644 --- a/packages/browser/src/node/commands/click.ts +++ b/packages/browser/src/node/commands/click.ts @@ -11,10 +11,7 @@ export const click: UserEventCommand = async ( const provider = context.provider if (provider instanceof PlaywrightBrowserProvider) { const tester = context.iframe - await tester.locator(selector).click({ - timeout: 1000, - ...options, - }) + await tester.locator(selector).click(options) } else if (provider instanceof WebdriverBrowserProvider) { const browser = context.browser @@ -53,7 +50,6 @@ export const tripleClick: UserEventCommand = async ( if (provider instanceof PlaywrightBrowserProvider) { const tester = context.iframe await tester.locator(selector).click({ - timeout: 1000, ...options, clickCount: 3, }) diff --git a/packages/browser/src/node/commands/dragAndDrop.ts b/packages/browser/src/node/commands/dragAndDrop.ts index d961910adcd1..6fc66bbb54f3 100644 --- a/packages/browser/src/node/commands/dragAndDrop.ts +++ b/packages/browser/src/node/commands/dragAndDrop.ts @@ -14,10 +14,7 @@ export const dragAndDrop: UserEventCommand = async ( await frame.dragAndDrop( source, target, - { - timeout: 1000, - ...options_, - }, + options_, ) } else if (context.provider instanceof WebdriverBrowserProvider) { diff --git a/packages/browser/src/node/commands/fill.ts b/packages/browser/src/node/commands/fill.ts index db2b68b4b45d..3010a5ffbb47 100644 --- a/packages/browser/src/node/commands/fill.ts +++ b/packages/browser/src/node/commands/fill.ts @@ -12,7 +12,7 @@ export const fill: UserEventCommand = async ( if (context.provider instanceof PlaywrightBrowserProvider) { const { iframe } = context const element = iframe.locator(selector) - await element.fill(text, { timeout: 1000, ...options }) + await element.fill(text, options) } else if (context.provider instanceof WebdriverBrowserProvider) { const browser = context.browser diff --git a/packages/browser/src/node/commands/hover.ts b/packages/browser/src/node/commands/hover.ts index 392b7e3e6f85..0b01fdceb5fd 100644 --- a/packages/browser/src/node/commands/hover.ts +++ b/packages/browser/src/node/commands/hover.ts @@ -9,10 +9,7 @@ export const hover: UserEventCommand = async ( options = {}, ) => { if (context.provider instanceof PlaywrightBrowserProvider) { - await context.iframe.locator(selector).hover({ - timeout: 1000, - ...options, - }) + await context.iframe.locator(selector).hover(options) } else if (context.provider instanceof WebdriverBrowserProvider) { const browser = context.browser diff --git a/packages/browser/src/node/commands/screenshot.ts b/packages/browser/src/node/commands/screenshot.ts index f3685d0ab5bf..2f739e2efc27 100644 --- a/packages/browser/src/node/commands/screenshot.ts +++ b/packages/browser/src/node/commands/screenshot.ts @@ -30,7 +30,6 @@ export const screenshot: BrowserCommand<[string, ScreenshotOptions]> = async ( const { element: selector, ...config } = options const element = context.iframe.locator(`${selector}`) const buffer = await element.screenshot({ - timeout: 1000, ...config, path: savePath, }) diff --git a/packages/browser/src/node/commands/select.ts b/packages/browser/src/node/commands/select.ts index 92121f685872..22da023a1781 100644 --- a/packages/browser/src/node/commands/select.ts +++ b/packages/browser/src/node/commands/select.ts @@ -26,10 +26,7 @@ export const selectOptions: UserEventCommand = async return elementHandler })) as (readonly string[]) | (readonly ElementHandle[]) - await selectElement.selectOption(values, { - timeout: 1000, - ...options, - }) + await selectElement.selectOption(values, options) } else if (context.provider instanceof WebdriverBrowserProvider) { const values = userValues as any as [({ index: number })] diff --git a/packages/browser/src/node/providers/playwright.ts b/packages/browser/src/node/providers/playwright.ts index fa9bf02b678f..f01e3cf37f3e 100644 --- a/packages/browser/src/node/providers/playwright.ts +++ b/packages/browser/src/node/providers/playwright.ts @@ -31,7 +31,7 @@ export class PlaywrightBrowserProvider implements BrowserProvider { private options?: { launch?: LaunchOptions - context?: BrowserContextOptions + context?: BrowserContextOptions & { actionTimeout?: number } } public contexts = new Map() @@ -108,8 +108,9 @@ export class PlaywrightBrowserProvider implements BrowserProvider { } const browser = await this.openBrowser() + const { actionTimeout, ...contextOptions } = this.options?.context ?? {} const options = { - ...this.options?.context, + ...contextOptions, ignoreHTTPSErrors: true, serviceWorkers: 'allow', } satisfies BrowserContextOptions @@ -117,6 +118,9 @@ export class PlaywrightBrowserProvider implements BrowserProvider { options.viewport = null } const context = await browser.newContext(options) + if (actionTimeout) { + context.setDefaultTimeout(actionTimeout) + } this.contexts.set(contextId, context) return context } @@ -187,7 +191,7 @@ export class PlaywrightBrowserProvider implements BrowserProvider { async openPage(contextId: string, url: string, beforeNavigate?: () => Promise) { const browserPage = await this.openBrowserPage(contextId) await beforeNavigate?.() - await browserPage.goto(url) + await browserPage.goto(url, { timeout: 0 }) } async getCDPSession(contextId: string) { From 0ef968b92b3f24ba3e040d815f546fce7ce075cc Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 29 Nov 2024 10:46:26 +0900 Subject: [PATCH 2/5] test: add test --- test/browser/fixtures/timeout/timeout.test.ts | 16 +++++++++++ .../browser/fixtures/timeout/vitest.config.ts | 27 +++++++++++++++++++ test/browser/specs/runner.test.ts | 15 ++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 test/browser/fixtures/timeout/timeout.test.ts create mode 100644 test/browser/fixtures/timeout/vitest.config.ts diff --git a/test/browser/fixtures/timeout/timeout.test.ts b/test/browser/fixtures/timeout/timeout.test.ts new file mode 100644 index 000000000000..3870179be1af --- /dev/null +++ b/test/browser/fixtures/timeout/timeout.test.ts @@ -0,0 +1,16 @@ +import { page } from '@vitest/browser/context'; +import { afterEach, expect, test } from 'vitest'; + +afterEach(() => { + document.body.innerHTML = '' +}) + +test('click', async () => { + document.body.innerHTML = '
hello
' + await page.getByText('world').click() +}) + +test('element', async () => { + document.body.innerHTML = '
hello
' + await expect.element(page.getByText('world')).toBeVisible() +}) diff --git a/test/browser/fixtures/timeout/vitest.config.ts b/test/browser/fixtures/timeout/vitest.config.ts new file mode 100644 index 000000000000..997fa84abcc4 --- /dev/null +++ b/test/browser/fixtures/timeout/vitest.config.ts @@ -0,0 +1,27 @@ +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vitest/config' + +const provider = process.env.PROVIDER || 'playwright' +const name = + process.env.BROWSER || (provider === 'playwright' ? 'chromium' : 'chrome') + +export default defineConfig({ + cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), + test: { + browser: { + enabled: true, + provider, + name, + providerOptions: { + context: { + actionTimeout: 500 + } + } + }, + expect: { + poll: { + timeout: 500 + } + } + }, +}) diff --git a/test/browser/specs/runner.test.ts b/test/browser/specs/runner.test.ts index 545ad88116d9..beb449e3709f 100644 --- a/test/browser/specs/runner.test.ts +++ b/test/browser/specs/runner.test.ts @@ -1,6 +1,6 @@ import { readFile } from 'node:fs/promises' import { beforeAll, describe, expect, onTestFailed, test } from 'vitest' -import { browser, runBrowserTests } from './utils' +import { browser, provider, runBrowserTests } from './utils' describe('running browser tests', async () => { let stderr: string @@ -153,3 +153,16 @@ test('user-event', async () => { } `) }) + +test('timeout', async () => { + const { stderr } = await runBrowserTests({ + root: './fixtures/timeout', + }) + expect(stderr).toContain('Matcher did not succeed in 500ms') + if (provider === 'playwright') { + expect(stderr).toContain('locator.click: Timeout 500ms exceeded.') + } + if (provider === 'webdriverio') { + expect(stderr).toContain('Cannot find element with locator') + } +}) From e3fe38d3b72633f76dc8bc29935638feaed8fc97 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 29 Nov 2024 10:52:33 +0900 Subject: [PATCH 3/5] chore: new line --- packages/browser/providers/playwright.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/browser/providers/playwright.d.ts b/packages/browser/providers/playwright.d.ts index b6e9d0744e3c..c1c9739afcaf 100644 --- a/packages/browser/providers/playwright.d.ts +++ b/packages/browser/providers/playwright.d.ts @@ -21,7 +21,8 @@ declare module 'vitest/node' { * The maximum time in milliseconds to wait for `userEvent` action to complete. * @default TODO: 1000? or 0 like playwright? */ - actionTimeout?: number } + actionTimeout?: number + } } export interface BrowserCommandContext { From eaceb799323248dea574828a18b6e3cf349a3bcf Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 29 Nov 2024 10:54:29 +0900 Subject: [PATCH 4/5] chore: comment --- packages/browser/providers/playwright.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/browser/providers/playwright.d.ts b/packages/browser/providers/playwright.d.ts index c1c9739afcaf..695b259f2504 100644 --- a/packages/browser/providers/playwright.d.ts +++ b/packages/browser/providers/playwright.d.ts @@ -19,7 +19,7 @@ declare module 'vitest/node' { > & { /** * The maximum time in milliseconds to wait for `userEvent` action to complete. - * @default TODO: 1000? or 0 like playwright? + * @default 0 (no timeout) */ actionTimeout?: number } From c27b40882b1080a50ee332e7d5b3e32dda0e8ac6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 29 Nov 2024 11:22:47 +0900 Subject: [PATCH 5/5] test: more --- test/browser/fixtures/timeout/timeout.test.ts | 7 ++++++- test/browser/specs/runner.test.ts | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/browser/fixtures/timeout/timeout.test.ts b/test/browser/fixtures/timeout/timeout.test.ts index 3870179be1af..2ea9982805f6 100644 --- a/test/browser/fixtures/timeout/timeout.test.ts +++ b/test/browser/fixtures/timeout/timeout.test.ts @@ -5,11 +5,16 @@ afterEach(() => { document.body.innerHTML = '' }) -test('click', async () => { +test('click default', async () => { document.body.innerHTML = '
hello
' await page.getByText('world').click() }) +test('click override', async () => { + document.body.innerHTML = '
hello
' + await page.getByText('world').click({ timeout: 345 }) +}) + test('element', async () => { document.body.innerHTML = '
hello
' await expect.element(page.getByText('world')).toBeVisible() diff --git a/test/browser/specs/runner.test.ts b/test/browser/specs/runner.test.ts index beb449e3709f..4ccc02129dbe 100644 --- a/test/browser/specs/runner.test.ts +++ b/test/browser/specs/runner.test.ts @@ -161,6 +161,7 @@ test('timeout', async () => { expect(stderr).toContain('Matcher did not succeed in 500ms') if (provider === 'playwright') { expect(stderr).toContain('locator.click: Timeout 500ms exceeded.') + expect(stderr).toContain('locator.click: Timeout 345ms exceeded.') } if (provider === 'webdriverio') { expect(stderr).toContain('Cannot find element with locator')