diff --git a/docs/tutorial/automated-testing.md b/docs/tutorial/automated-testing.md index e2386e262e387..56ad8282eed4b 100644 --- a/docs/tutorial/automated-testing.md +++ b/docs/tutorial/automated-testing.md @@ -58,8 +58,6 @@ To run your tests: $ npx wdio run wdio.conf.js ``` -[chrome-driver]: https://sites.google.com/chromium.org/driver/ - ### With Selenium [Selenium](https://www.selenium.dev/) is a web automation framework that @@ -116,6 +114,142 @@ driver.wait(() => { driver.quit() ``` +## Using Playwright + +[Microsoft Playwright](https://playwright.dev) is an end-to-end testing framework built +using browser-specific remote debugging protocols, similar to the [Puppeteer] headless +Node.js API but geared towards end-to-end testing. Playwright has experimental Electron +support via Electron's support for the [Chrome DevTools Protocol] (CDP). + +### Install dependencies + +You can install Playwright through your preferred Node.js package manager. The Playwright team +recommends using the `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` environment variable to avoid +unnecessary browser downloads when testing an Electron app. + +```sh npm2yarn +PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install --save-dev playwright +``` + +Playwright also comes with its own test runner, Playwright Test, which is built for end-to-end +testing. You can also install it as a dev dependency in your project: + +```sh npm2yarn +npm install --save-dev @playwright/test +``` + +:::caution Dependencies +This tutorial was written `playwright@1.16.3` and `@playwright/test@1.16.3`. Check out +[Playwright's releases][playwright-releases] page to learn about +changes that might affect the code below. +::: + +:::info Using third-party test runners +If you're interested in using an alternative test runner (e.g. Jest or Mocha), check out +Playwright's [Third-Party Test Runner][playwright-test-runners] guide. +::: + +### Write your tests + +Playwright launches your app in development mode through the `_electron.launch` API. +To point this API to your Electron app, you can pass the path to your main process +entry point (here, it is `main.js`). + +```js {5} +const { _electron: electron } = require('playwright') +const { test } = require('@playwright/test') + +test('launch app', async () => { + const electronApp = await electron.launch({ args: ['main.js'] }) + // close app + await electronApp.close() +}) +``` + +After that, you will access to an instance of Playwright's `ElectronApp` class. This +is a powerful class that has access to main process modules for example: + +```js {6-11} +const { _electron: electron } = require('playwright') +const { test } = require('@playwright/test') + +test('get isPackaged', async () => { + const electronApp = await electron.launch({ args: ['main.js'] }) + const isPackaged = await electronApp.evaluate(async ({ app }) => { + // This runs in Electron's main process, parameter here is always + // the result of the require('electron') in the main app script. + return app.isPackaged + }) + console.log(isPackaged) // false (because we're in development mode) + // close app + await electronApp.close() +}) +``` + +It can also create individual [Page][playwright-page] objects from Electron BrowserWindow instances. +For example, to grab the first BrowserWindow and save a screenshot: + +```js {6-7} +const { _electron: electron } = require('playwright') +const { test } = require('@playwright/test') + +test('save screenshot', async () => { + const electronApp = await electron.launch({ args: ['main.js'] }) + const window = await electronApp.firstWindow() + await window.screenshot({ path: 'intro.png' }) + // close app + await electronApp.close() +}) +``` + +Putting all this together using the PlayWright Test runner, let's create a `example.spec.js` +test file with a single test and assertion: + +```js title='example.spec.js' +const { _electron: electron } = require('playwright') +const { test, expect } = require('@playwright/test') + +test('example test', async () => { + const electronApp = await electron.launch({ args: ['.'] }) + const isPackaged = await electronApp.evaluate(async ({ app }) => { + // This runs in Electron's main process, parameter here is always + // the result of the require('electron') in the main app script. + return app.isPackaged; + }); + + expect(isPackaged).toBe(false); + + // Wait for the first BrowserWindow to open + // and return its Page object + const window = await electronApp.firstWindow() + await window.screenshot({ path: 'intro.png' }) + + // close app + await electronApp.close() +}); +``` + +Then, run Playwright Test using `npx playwright test`. You should see the test pass in your +console, and have an `intro.png` screenshot on your filesystem. + +```console +☁ $ npx playwright test + +Running 1 test using 1 worker + + ✓ example.spec.js:4:1 › example test (1s) +``` + +:::info +Playwright Test will automatically run any files matching the `.*(test|spec)\.(js|ts|mjs)` regex. +You can customize this match in the [Playwright Test configuration options][playwright-test-config]. +::: + +:::tip Further reading +Check out Playwright's documentation for the full [Electron][playwright-electron] +and [ElectronApplication][playwright-electronapplication] class APIs. +::: + ## Using a custom test driver It's also possible to write your own custom driver using Node.js' built-in IPC-over-STDIO. @@ -263,3 +397,13 @@ test.after.always('cleanup', async t => { await app.stop() }) ``` + +[chrome-driver]: https://sites.google.com/chromium.org/driver/ +[Puppeteer]: https://github.com/puppeteer/puppeteer +[playwright-electron]: https://playwright.dev/docs/api/class-electron/ +[playwright-electronapplication]: https://playwright.dev/docs/api/class-electronapplication +[playwright-page]: https://playwright.dev/docs/api/class-page +[playwright-releases]: https://github.com/microsoft/playwright/releases +[playwright-test-config]: https://playwright.dev/docs/api/class-testconfig#test-config-test-match +[playwright-test-runners]: https://playwright.dev/docs/test-runners/ +[Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/