Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[browser] Provide access to Playwright's Keyboard classes in browser mode #4918

Closed
4 tasks done
motss opened this issue Jan 10, 2024 · 6 comments · Fixed by #5097
Closed
4 tasks done

[browser] Provide access to Playwright's Keyboard classes in browser mode #4918

motss opened this issue Jan 10, 2024 · 6 comments · Fixed by #5097
Labels
enhancement New feature or request feat: browser Issues and PRs related to the browser runner

Comments

@motss
Copy link

motss commented Jan 10, 2024

Clear and concise description of the problem

The experimental browser mode currently allows users to choose a list of providers (Webdriver.io, Playwright) to run their tests on various browsers supported by their chosen provider.

However, there isn't a way to reference the current page context in a test suite to call some of the API classes offered by Playwright, e.g. the Playwright's Keyboard class allows users to simulate keyboard inputs as if they were generated from a real keyboard.

Suggested solution

  1. To provide access to the current page context for API classes provided by Playwright
  2. To provide an API to send commands to Playwright, e.g. sendKeys command from web-test-runner

Alternative

No response

Additional context

No response

Validations

@hi-ogawa
Copy link
Contributor

Thanks for the feature request.

To make sure I understand this better, can you elaborate on what kind of tests you are intending to write on Vitest browser mode if such API is available? Providing some examples with your hypothetical API might help.

there isn't a way to reference the current page context in a test suite to call some of the API classes offered by Playwright

One important difference between Vitest's browser mode and Playwright is that "test suite" itself is executed on the browser on Vitest browser mode. It might be obvious, just in case, to give a concrete example:

// vitest browser mode
test("test", () => {
  expect(typeof window).toBe("object")
})

// playwright
test("test", async ({ page }) => {
  expect(typeof window).toBe("undefined")
  expect(await page.evaluate(() => typeof window)).toBe("object")  // page API is the interface to the browser
})

I think the better analogy would be that it's jsdom-like test environment but it's actually the real browser. So, it might make more sense to bring something like testing-library ecosystem https://testing-library.com/docs/ for selector, event utility etc... (actually I've never tried, but I feel it makes sense).

@sheremet-va
Copy link
Member

The idea is that people use solutions that are already provided by third-party packages. Eventually, Vitest should provide a set of utilities that are better addressed natively (like clicking - it's not possible to emulate the order of all events on the micro-task level specified by the spec). The API for this is not decided yet - I feel like we should make the runner more stable before starting with this.

@hi-ogawa
Copy link
Contributor

Thanks for sharing the plan. I also wanted to mention that if this PR #3584 eventually runs multiple test files (in parallel?) under separate iframes but are managed by single "page" instance, then exposing something in terms of provider's page interface level wouldn't make sense probably.

@motss
Copy link
Author

motss commented Jan 29, 2024

To make sure I understand this better, can you elaborate on what kind of tests you are intending to write on Vitest browser mode if such API is available? Providing some examples with your hypothetical API might help.

@hi-ogawa I guess I already provided a use case by mentioning web-test-runner's sendKeys() command. If that is not obvious to you and other folks, I've created a simple test case to demonstrate the use case which is to test if pressing Tab key focuses the elements in the correct order as part of the a11y test when building UI components.

it('focuses the element correctly', async () => {
    const el = await fixture(html`<my-element></my-element>`);

    expect(
      el.shadowRoot?.querySelector('a[href="https://vitejs.dev"]')
    ).toBeInTheDocument();

    (
      el.shadowRoot?.querySelector(
        'a[href="https://vitejs.dev"]'
      ) as HTMLAnchorElement
    )?.focus();

    expect(
      el.shadowRoot?.activeElement?.isEqualNode(
        el.shadowRoot?.querySelector('a[href="https://vitejs.dev"]')
      )
    ).true;

    /**
     * fixme: None of these can simulate a keypress tab to focus an element on the screen.
     */

    // document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
    // document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Tab' }));
    document.dispatchEvent(new KeyboardEvent('keypress', { key: 'Tab' }));
    // el.shadowRoot?.activeElement?.dispatchEvent(
    //   new KeyboardEvent('keypress', { key: 'Tab' })
    // );

    expect(
      el.shadowRoot?.activeElement?.isEqualNode(
        el.shadowRoot?.querySelector('a[href="https://lit.dev"]')
      )
    ).true;
  });

I could be wrong but simulating a keypress Tab is not as easy as I thought using JavaScript.

// None of these works to simulate keypress tab
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Tab' }));
document.dispatchEvent(new KeyboardEvent('keypress', { key: 'Tab' }));
document.shadowRoot?.activeElement?.dispatchEvent(new KeyboardEvent('keypress', { key: 'Tab' }));

So, the proposed solution is to make use of the Playwright's Keyboard class allows users to simulate keyboard inputs as if they were generated from a real keyboard, e.g. await page.keyboard.press('ArrowLeft');.

There's already a prior art from the web-test-runner where it provides the sendKeys() command to simulate any keyboard event directly from your test. It'd be great if vitest could find some inspirations from the web-test-runner and create something similar.

it('focuses next element', async () => {
  // ...
  await sendKeys({ press: 'Tab' });
  // ...
});

Hope this helps.

@hi-ogawa
Copy link
Contributor

@motss Thanks for taking a time to put together a sample!

The concerns I have with exposing playwright API (or any browser driver level features) is that, for one, I don't think it's so simple to implement it on Vitest browser mode since the code is executed in a different layer as I mentioned in my first comment #4918 (comment)

For 2nd, related to our plan of running multiple test files isolated in different iframes (but still in a single page managed by browser driver), it would probably makes it complicated to expose some browser driver features directly.

Nonetheless, I appreciate you sharing the idea. We'll probably get some inspirations from existing API and implementations, so it would be helpful in the future.

@sheremet-va
Copy link
Member

sheremet-va commented Jan 30, 2024

I do like the web-test-runner implementation. I have this sort of API in my mind to have a similar feature:

import { sendKeys } from '$page'

await sendKeys({ type: 'hello world' })

On the server side, we can implement some kind of API to get this virtual module from testing providers:

interface VirtualBrowserPage {
  sendKeys(options): void
}

interface BrowserProvider {
  getVirtualPageMethods() {
    return { sendKeys: this.sendKeys }
  }
}

And the virtual module is generated based on the returned keys:

const plugin = {
  resolveId(id) {
    if(id === '$page') {
      return '\0$page'
    }
  },
  load(id) {
    if(id === '\0$page') {
      const page = provider.getVirtualPageMethods()
      const methods = Object.keys(page)
      return `
		const rpc = () => __vitest_worker__.rpc
		${methods.map(method => `export const ${method} = (...args) => rpc().page(${method}, ...args)`}
      `
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feat: browser Issues and PRs related to the browser runner
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants