feat(develop): update Webdriver tests section (#3450)

Co-authored-by: Fabian-Lars <github@fabianlars.de>
This commit is contained in:
Lucas Fernandes Nogueira
2025-08-11 14:12:45 -03:00
committed by GitHub
parent 268b54f440
commit 54f52669b5
6 changed files with 272 additions and 103 deletions

View File

@@ -5,8 +5,6 @@ sidebar:
i18nReady: true
---
{/* TODO: REVISE COPY TO V2 */}
import CommandTabs from '@components/CommandTabs.astro';
:::note
@@ -16,14 +14,14 @@ Make sure to go through the [prerequisites instructions] to be able to follow th
:::
This WebDriver testing example will use [Selenium] and a popular Node.js testing suite. You are expected to already have
Node.js installed, along with `npm` or `yarn` although the [finished example project] uses `yarn`.
Node.js installed, along with `npm` or `yarn` although the [finished example project] uses `pnpm`.
## Create a Directory for the Tests
Let's create a space to write these tests in our project. We will be using a nested directory for
this example project as we will later also go over other frameworks, but typically you will only need to use one. Create
the directory we will use with `mkdir -p webdriver/selenium`. The rest of this guide will assume you are inside the
`webdriver/selenium` directory.
the directory we will use with `mkdir -p e2e-tests`. The rest of this guide will assume you are inside the
`e2e-tests` directory.
## Initializing a Selenium Project
@@ -38,13 +36,14 @@ guide on how to set it up from scratch.
"name": "selenium",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"test": "mocha"
},
"dependencies": {
"chai": "^4.3.4",
"mocha": "^9.0.3",
"selenium-webdriver": "^4.0.0-beta.4"
"chai": "^5.2.1",
"mocha": "^11.7.1",
"selenium-webdriver": "^4.34.0"
}
}
```
@@ -80,21 +79,24 @@ testing file at `test/test.js` by default, so let's create that file now.
`test/test.js`:
```javascript
const os = require('os');
const path = require('path');
const { expect } = require('chai');
const { spawn, spawnSync } = require('child_process');
const { Builder, By, Capabilities } = require('selenium-webdriver');
import os from 'os';
import path from 'path';
import { expect } from 'chai';
import { spawn, spawnSync } from 'child_process';
import { Builder, By, Capabilities } from 'selenium-webdriver';
import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
// create the path to the expected application binary
const application = path.resolve(
__dirname,
'..',
'..',
'..',
'src-tauri',
'target',
'release',
'hello-tauri-webdriver'
'debug',
'tauri-app'
);
// keep track of the webdriver instance we create
@@ -102,13 +104,18 @@ let driver;
// keep track of the tauri-driver process we start
let tauriDriver;
let exit = false;
before(async function () {
// set timeout to 2 minutes to allow the program to build if it needs to
this.timeout(120000);
// ensure the program has been built
spawnSync('cargo', ['build', '--release']);
// ensure the app has been built
spawnSync('yarn', ['tauri', 'build', '--debug', '--no-bundle'], {
cwd: path.resolve(__dirname, '../..'),
stdio: 'inherit',
shell: true,
});
// start tauri-driver
tauriDriver = spawn(
@@ -116,6 +123,16 @@ before(async function () {
[],
{ stdio: [null, process.stdout, process.stderr] }
);
tauriDriver.on('error', (error) => {
console.error('tauri-driver error:', error);
process.exit(1);
});
tauriDriver.on('exit', (code) => {
if (!exit) {
console.error('tauri-driver exited with code:', code);
process.exit(1);
}
});
const capabilities = new Capabilities();
capabilities.set('tauri:options', { application });
@@ -130,10 +147,7 @@ before(async function () {
after(async function () {
// stop the webdriver session
await driver.quit();
// kill the tauri-driver process
tauriDriver.kill();
await closeTauriDriver();
});
describe('Hello Tauri', () => {
@@ -160,6 +174,34 @@ describe('Hello Tauri', () => {
expect(luma).to.be.lessThan(100);
});
});
async function closeTauriDriver() {
exit = true;
// kill the tauri-driver process
tauriDriver.kill();
// stop the webdriver session
await driver.quit();
}
function onShutdown(fn) {
const cleanup = () => {
try {
fn();
} finally {
process.exit();
}
};
process.on('exit', cleanup);
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
process.on('SIGHUP', cleanup);
process.on('SIGBREAK', cleanup);
}
onShutdown(() => {
closeTauriDriver();
});
```
If you are familiar with JS testing frameworks, `describe`, `it`, and `expect` should look familiar. We also have
@@ -201,7 +243,7 @@ application at all!
[prerequisites instructions]: /develop/tests/webdriver/
[selenium]: https://selenium.dev/
[finished example project]: https://github.com/chippers/hello_tauri
[finished example project]: https://github.com/tauri-apps/webdriver-example
[mocha]: https://mochajs.org/
[chai]: https://www.chaijs.com/
[`selenium-webdriver`]: https://www.npmjs.com/package/selenium-webdriver

View File

@@ -5,8 +5,6 @@ sidebar:
i18nReady: true
---
{/* TODO: REVISE COPY TO V2 */}
import CommandTabs from '@components/CommandTabs.astro';
:::note
@@ -16,14 +14,14 @@ Make sure to go through the [prerequisites instructions] to be able to follow th
:::
This WebDriver testing example will use [WebdriverIO], and its testing suite. It is expected to have Node.js already
installed, along with `npm` or `yarn` although the [finished example project] uses `yarn`.
installed, along with `npm` or `yarn` although the [finished example project] uses `pnpm`.
## Create a Directory for the Tests
Let's create a space to write these tests in our project. We will be using a nested directory for
this example project as we will later also go over other frameworks, but typically you only need to use one. Create
the directory we will use with `mkdir -p webdriver/webdriverio`. The rest of this guide assumes you are inside the
`webdriver/webdriverio` directory.
the directory we will use with `mkdir e2e-tests`. The rest of this guide assumes you are inside the
`e2e-tests` directory.
## Initializing a WebdriverIO Project
@@ -38,16 +36,17 @@ guide on setting it up from scratch.
"name": "webdriverio",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"test": "wdio run wdio.conf.js"
},
"dependencies": {
"@wdio/cli": "^7.9.1"
"@wdio/cli": "^9.19.0"
},
"devDependencies": {
"@wdio/local-runner": "^7.9.1",
"@wdio/mocha-framework": "^7.9.1",
"@wdio/spec-reporter": "^7.9.0"
"@wdio/local-runner": "^9.19.0,
"@wdio/mocha-framework": "^9.19.0",
"@wdio/spec-reporter": "^9.19.0"
}
}
```
@@ -80,21 +79,27 @@ config file which controls most aspects of our testing suite.
`wdio.conf.js`:
```javascript
const os = require('os');
const path = require('path');
const { spawn, spawnSync } = require('child_process');
import os from 'os';
import path from 'path';
import { spawn, spawnSync } from 'child_process';
import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
// keep track of the `tauri-driver` child process
let tauriDriver;
let exit = false;
exports.config = {
export const config = {
host: '127.0.0.1',
port: 4444,
specs: ['./develop/tests/specs/**/*.js'],
maxInstances: 1,
capabilities: [
{
maxInstances: 1,
'tauri:options': {
application: '../../target/release/hello-tauri-webdriver',
application: '../src-tauri/target/debug/tauri-app',
},
},
],
@@ -106,24 +111,71 @@ exports.config = {
},
// ensure the rust project is built since we expect this binary to exist for the webdriver sessions
onPrepare: () => spawnSync('cargo', ['build', '--release']),
onPrepare: () => {
spawnSync('yarn', ['tauri', 'build', '--debug', '--no-bundle'], {
cwd: path.resolve(__dirname, '..'),
stdio: 'inherit',
shell: true,
});
},
// ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests
beforeSession: () =>
(tauriDriver = spawn(
beforeSession: () => {
tauriDriver = spawn(
path.resolve(os.homedir(), '.cargo', 'bin', 'tauri-driver'),
[],
{ stdio: [null, process.stdout, process.stderr] }
)),
);
tauriDriver.on('error', (error) => {
console.error('tauri-driver error:', error);
process.exit(1);
});
tauriDriver.on('exit', (code) => {
if (!exit) {
console.error('tauri-driver exited with code:', code);
process.exit(1);
}
});
},
// clean up the `tauri-driver` process we spawned at the start of the session
afterSession: () => tauriDriver.kill(),
// note that afterSession might not run if the session fails to start, so we also run the cleanup on shutdown
afterSession: () => {
closeTauriDriver();
},
};
function closeTauriDriver() {
exit = true;
tauriDriver?.kill();
}
function onShutdown(fn) {
const cleanup = () => {
try {
fn();
} finally {
process.exit();
}
};
process.on('exit', cleanup);
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
process.on('SIGHUP', cleanup);
process.on('SIGBREAK', cleanup);
}
// ensure tauri-driver is closed when our test process exits
onShutdown(() => {
closeTauriDriver();
});
```
If you are interested in the properties on the `exports.config` object, I [suggest reading the documentation][webdriver documentation].
If you are interested in the properties on the `config` object, we [suggest reading the documentation][webdriver documentation].
For non-WDIO specific items, there are comments explaining why we are running commands in `onPrepare`, `beforeSession`,
and `afterSession`. We also have our specs set to `"./develop/tests/specs/**/*.js"`, so let's create a spec now.
and `afterSession`. We also have our specs set to `"./test/specs/**/*.js"`, so let's create a spec now.
## Spec
@@ -217,7 +269,7 @@ of configuration and a single command to run it! Even better, we didn't have to
[prerequisites instructions]: /develop/tests/webdriver/
[webdriverio]: https://webdriver.io/
[finished example project]: https://github.com/chippers/hello_tauri
[finished example project]: https://github.com/tauri-apps/webdriver-example
[mocha]: https://mochajs.org/
[webdriver documentation]: https://webdriver.io/docs/configurationfile
[webdriverio api docs]: https://webdriver.io/docs/api

View File

@@ -6,19 +6,20 @@ sidebar:
i18nReady: true
---
Utilizing Linux and some programs to create a fake display, it is possible to run [WebDriver] tests with
[`tauri-driver`] on your CI. The following example uses the [WebdriverIO] example we [previously built together] and
It is possible to run [WebDriver] tests with [`tauri-driver`] on your CI. The following example uses the [WebdriverIO] example we [previously built together] and
GitHub Actions.
This means the following assumptions:
The WebDriver tests are executed on Linux by creating a fake display.
Some CI systems such as GitHub Actions also support running WebDriver tests on Windows.
1. The Tauri application is in the repository root and the binary builds when running `cargo build --release`.
2. The [WebDriverIO] test runner is in the `webdriver/webdriverio` directory and runs when `yarn test` is used in that
directory.
## GitHub Actions
The following is a commented GitHub Actions workflow file at `.github/workflows/webdriver.yml`
The following GitHub Actions assumes:
```yaml
1. The Tauri application is in the `src-tauri` folder.
2. The [WebDriverIO] test runner is in the `e2e-tests` directory and runs when `yarn test` is used in that directory.
```yaml title=".github/workflows/webdriver.yml"
# run this action when the repository is pushed to
on: [push]
@@ -31,8 +32,14 @@ jobs:
# the display name of the test job
name: WebDriverIO Test Runner
# we want to run on the latest linux environment
runs-on: ubuntu-22.04
# run on the matrix platform
runs-on: ${{ matrix.platform }}
strategy:
# do not fail other matrix runs if one fails
fail-fast: false
# set all platforms our test should run on
matrix:
platform: [ubuntu-latest, windows-latest]
# the steps our job runs **in order**
steps:
@@ -42,55 +49,72 @@ jobs:
# install system dependencies that Tauri needs to compile on Linux.
# note the extra dependencies for `tauri-driver` to run which are: `webkit2gtk-driver` and `xvfb`
- name: Tauri dependencies
if: matrix.platform == 'ubuntu-latest'
run: |
sudo apt update && sudo apt install -y \
libwebkit2gtk-4.1-dev \
build-essential \
curl \
wget \
file \
libxdo-dev \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
webkit2gtk-driver \
xvfb
sudo apt-get update &&
sudo apt-get install -y
libwebkit2gtk-4.1-dev
libayatana-appindicator3-dev
webkit2gtk-driver
xvfb
# install a matching Microsoft Edge Driver version using msedgedriver-tool
- name: install msdgedriver (Windows)
if: matrix.platform == 'windows-latest'
run: |
cargo install --git https://github.com/chippers/msedgedriver-tool
& "$HOME/.cargo/bin/msedgedriver-tool.exe"
$PWD.Path >> $env:GITHUB_PATH
# install latest stable Rust release
- name: Setup rust-toolchain stable
id: rust-toolchain
uses: dtolnay/rust-toolchain@stable
# we run our rust tests before the webdriver tests to avoid testing a broken application
# setup caching for the Rust target folder
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
# we run our Rust tests before the webdriver tests to avoid testing a broken application
- name: Cargo test
run: cargo test
# build a release build of our application to be used during our WebdriverIO tests
- name: Cargo build
run: cargo build --release
# install the latest stable node version at the time of writing
- name: Node 20
- name: Node 24
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 24
cache: 'yarn'
# install our Node.js dependencies with Yarn
# install the application Node.js dependencies with Yarn
- name: Yarn install
run: yarn install --frozen-lockfile
working-directory: webdriver/webdriverio
# install the e2e-tests Node.js dependencies with Yarn
- name: Yarn install
run: yarn install --frozen-lockfile
working-directory: e2e-tests
# install the latest version of `tauri-driver`.
# note: the tauri-driver version is independent of any other Tauri versions
- name: Install tauri-driver
run: cargo install tauri-driver --locked
# run the WebdriverIO test suite.
# run the WebdriverIO test suite on Linux.
# we run it through `xvfb-run` (the dependency we installed earlier) to have a fake
# display server which allows our application to run headless without any changes to the code
- name: WebdriverIO
- name: WebdriverIO (Linux)
if: matrix.platform == 'ubuntu-latest'
run: xvfb-run yarn test
working-directory: webdriver/webdriverio
working-directory: e2e-tests
# run the WebdriverIO test suite on Windows.
# in this case we can run the tests directly.
- name: WebdriverIO (Windows)
if: matrix.platform == 'windows-latest'
run: yarn test
working-directory: e2e-tests
```
[previously built together]: /develop/tests/webdriver/example/webdriverio/

View File

@@ -25,7 +25,7 @@ Because we currently utilize the platform's native [WebDriver] server, there are
### Linux
We use `WebKitWebDriver` on Linux platforms. Check if this binary exists already (command `which WebKitWebDriver`) as
We use `WebKitWebDriver` on Linux platforms. Check if this binary exists already by running the `which WebKitWebDriver` command as
some distributions bundle it with the regular WebKit package. Other platforms may have a separate package for them, such
as `webkit2gtk-driver` on Debian-based distributions.
@@ -35,6 +35,13 @@ Make sure to grab the version of [Microsoft Edge Driver] that matches your Windo
being built and tested on. This should almost always be the latest stable version on up-to-date Windows installs. If the
two versions do not match, you may experience your WebDriver testing suite hanging while trying to connect.
You can use the [msedgedriver-tool](https://github.com/chippers/msedgedriver-tool) to download the appropriate Microsoft Edge Driver:
```powershell
cargo install --git https://github.com/chippers/msedgedriver-tool
& "$HOME/.cargo/bin/msedgedriver-tool.exe"
```
The download contains a binary called `msedgedriver.exe`. [`tauri-driver`] looks for that binary in the `$PATH` so make
sure it's either available on the path or use the `--native-driver` option on [`tauri-driver`]. You may want to download this automatically as part of the CI setup process to ensure the Edge, and Edge Driver versions
stay in sync on Windows CI machines. A guide on how to do this may be added at a later date.
@@ -45,7 +52,7 @@ Below are step-by-step guides to show how to create a minimal example applicatio
is tested with WebDriver.
If you prefer to see the result of the guide and look over a finished minimal codebase that utilizes it, you
can look at https://github.com/chippers/hello_tauri.
can look at https://github.com/tauri-apps/webdriver-example.
import { LinkCard, CardGrid } from '@astrojs/starlight/components';

View File

@@ -14,11 +14,11 @@ Make sure to go through the [prerequisites instructions] to be able to follow th
:::
此 WebDriver 测试示例将使用 [Selenium][] 和一个流行的 Node.js 测试套装。 您应该已经安装 node.js 并且一起安装了 `npm` 或 `yarn` [已完成的示例项目][] 使用了 `yarn`.
此 WebDriver 测试示例将使用 [Selenium][] 和一个流行的 Node.js 测试套装。 您应该已经安装 node.js 并且一起安装了 `npm` 或 `yarn` [已完成的示例项目][] 使用了 `pnpm`.
## 创建测试目录
让我们创建一个空目录用做测试项目。 在此示例项目中我们将使用嵌套目录,因为稍后还将使用其他框架,但通常您只需要使用一个框架。创建接下来我们需要使用的目录。 创建我们将要使用的目录 `mkdir -p webdriver/selenium` 。 指南接下来的部分我们假定您也在目录 `webdriver/selenium` 中完成。
让我们创建一个空目录用做测试项目。 在此示例项目中我们将使用嵌套目录,因为稍后还将使用其他框架,但通常您只需要使用一个框架。创建接下来我们需要使用的目录。 创建我们将要使用的目录 `mkdir -p e2e-tests` 。 指南接下来的部分我们假定您也在目录 `e2e-tests` 中完成。
## 初始化 Selenium 项目
@@ -31,13 +31,14 @@ Make sure to go through the [prerequisites instructions] to be able to follow th
"name": "selenium",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"test": "mocha"
},
"dependencies": {
"chai": "^4.3.4",
"mocha": "^9.0.3",
"selenium-webdriver": "^4.0.0-beta.4"
"chai": "^5.2.1",
"mocha": "^11.7.1",
"selenium-webdriver": "^4.34.0"
}
}
```
@@ -67,21 +68,24 @@ Make sure to go through the [prerequisites instructions] to be able to follow th
`test/test.js`:
```javascript
const os = require('os');
const path = require('path');
const { expect } = require('chai');
const { spawn, spawnSync } = require('child_process');
const { Builder, By, Capabilities } = require('selenium-webdriver');
import os from 'os';
import path from 'path';
import { expect } from 'chai';
import { spawn, spawnSync } from 'child_process';
import { Builder, By, Capabilities } from 'selenium-webdriver';
import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
// create the path to the expected application binary
const application = path.resolve(
__dirname,
'..',
'..',
'..',
'src-tauri',
'target',
'release',
'hello-tauri-webdriver'
'debug',
'tauri-app'
);
// keep track of the webdriver instance we create
@@ -89,13 +93,18 @@ let driver;
// keep track of the tauri-driver process we start
let tauriDriver;
let exit = false;
before(async function () {
// set timeout to 2 minutes to allow the program to build if it needs to
this.timeout(120000);
// ensure the program has been built
spawnSync('cargo', ['build', '--release']);
// ensure the app has been built
spawnSync('yarn', ['tauri', 'build', '--debug', '--no-bundle'], {
cwd: path.resolve(__dirname, '../..'),
stdio: 'inherit',
shell: true,
});
// start tauri-driver
tauriDriver = spawn(
@@ -103,6 +112,16 @@ before(async function () {
[],
{ stdio: [null, process.stdout, process.stderr] }
);
tauriDriver.on('error', (error) => {
console.error('tauri-driver error:', error);
process.exit(1);
});
tauriDriver.on('exit', (code) => {
if (!exit) {
console.error('tauri-driver exited with code:', code);
process.exit(1);
}
});
const capabilities = new Capabilities();
capabilities.set('tauri:options', { application });
@@ -117,10 +136,7 @@ before(async function () {
after(async function () {
// stop the webdriver session
await driver.quit();
// kill the tauri-driver process
tauriDriver.kill();
await closeTauriDriver();
});
describe('Hello Tauri', () => {
@@ -147,6 +163,34 @@ describe('Hello Tauri', () => {
expect(luma).to.be.lessThan(100);
});
});
async function closeTauriDriver() {
exit = true;
// kill the tauri-driver process
tauriDriver.kill();
// stop the webdriver session
await driver.quit();
}
function onShutdown(fn) {
const cleanup = () => {
try {
fn();
} finally {
process.exit();
}
};
process.on('exit', cleanup);
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
process.on('SIGHUP', cleanup);
process.on('SIGBREAK', cleanup);
}
onShutdown(() => {
closeTauriDriver();
});
```
If you are familiar with JS testing frameworks, `describe`, `it`, and `expect` should look familiar. We also have
@@ -188,7 +232,7 @@ application at all!
[prerequisites instructions]: /develop/tests/webdriver/
[Selenium]: https://selenium.dev/
[已完成的示例项目]: https://github.com/chippers/hello_tauri
[已完成的示例项目]: https://github.com/tauri-apps/webdriver-example
[Mocha]: https://mochajs.org/
[Chai]: https://www.chaijs.com/
[`selenium-webdriver`]: https://www.npmjs.com/package/selenium-webdriver

View File

@@ -35,7 +35,7 @@ Make sure to grab the version of [Microsoft Edge Driver][] that matches your Win
Below are step-by-step guides to show how to create a minimal example application that is tested with WebDriver.
如果您想要查看根据指南操作后完成的最小代码库,则可以 <a href="https://github.com/chippers/hello_tauri" target="_blank">查看这里</a>。 该示例还附带了一个 CI 脚本,用于使用 GitHub 操作进行测试,但您可能仍然对 [WebDriver CI](/zh-cn/develop/tests/webdriver/ci/) 指南感兴趣,因为它对概念进行了更多解释 。
如果您想要查看根据指南操作后完成的最小代码库,则可以 <a href="https://github.com/tauri-apps/webdriver-example" target="_blank">查看这里</a>。 该示例还附带了一个 CI 脚本,用于使用 GitHub 操作进行测试,但您可能仍然对 [WebDriver CI](/zh-cn/develop/tests/webdriver/ci/) 指南感兴趣,因为它对概念进行了更多解释 。
import { LinkCard, CardGrid } from '@astrojs/starlight/components';