Skip to content

Commit 3ef8841

Browse files
Add base for GUI tests
1 parent 3f4f287 commit 3ef8841

File tree

6 files changed

+188
-2
lines changed

6 files changed

+188
-2
lines changed

.github/workflows/main.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ on:
33
pull_request:
44
merge_group:
55

6+
env:
7+
BROWSER_UI_TEST_VERSION: '0.18.2'
8+
69
jobs:
710
test:
811
runs-on: ${{ matrix.os }}
@@ -47,7 +50,19 @@ jobs:
4750
- uses: actions/checkout@v4
4851
- name: Install Rust
4952
run: bash ci/install-rust.sh ${{ matrix.rust }} ${{ matrix.target }}
53+
- name: Install npm
54+
if: matrix.os != 'windows-latest'
55+
uses: actions/setup-node@v3
56+
with:
57+
node-version: 20
58+
- name: Install browser-ui-test
59+
if: matrix.os != 'windows-latest'
60+
run: npm install browser-ui-test@"${BROWSER_UI_TEST_VERSION}"
61+
- name: Build and run tests (+ GUI)
62+
if: matrix.os != 'windows-latest'
63+
run: cargo test --locked --target ${{ matrix.target }} --test gui
5064
- name: Build and run tests
65+
if: matrix.os == 'windows-latest'
5166
run: cargo test --locked --target ${{ matrix.target }}
5267
- name: Test no default
5368
run: cargo test --no-default-features --target ${{ matrix.target }}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@ test_book/book/
1616
# Ignore Vim temporary and swap files.
1717
*.sw?
1818
*~
19+
20+
# GUI tests
21+
node_modules
22+
package-lock.json
23+
package.json

CONTRIBUTING.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,21 @@ We generally strive to keep mdBook compatible with a relatively recent browser o
138138
That is, supporting Chrome, Safari, Firefox, Edge on Windows, macOS, Linux, iOS, and Android.
139139
If possible, do your best to avoid breaking older browser releases.
140140
141-
Any change to the HTML or styling is encouraged to manually check on as many browsers and platforms that you can.
142-
Unfortunately at this time we don't have any automated UI or browser testing, so your assistance in testing is appreciated.
141+
GUI tests are checked with the GUI testsuite. To run it, you need to install `npm` first. Then run:
142+
143+
```
144+
cargo test --test gui
145+
```
146+
147+
The first time, it'll fail and ask you to install the `browser-ui-test` package. Install it then re-run the tests.
148+
149+
If you want to disable the headless mode, use the `DISABLE_HEADLESS_TEST=1` environment variable:
150+
151+
```
152+
DISABLE_HEADLESS_TEST=1 GUI_TESTS=1 cargo test gui
153+
```
154+
155+
You can find the `browser-ui-test` package documentation on [its repository](https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md).
143156
144157
## Updating highlight.js
145158

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,10 @@ name = "remove-emphasis"
8282
path = "examples/remove-emphasis/test.rs"
8383
crate-type = ["lib"]
8484
test = true
85+
86+
[[test]]
87+
harness = false
88+
test = false
89+
name = "gui"
90+
path = "tests/gui/runner.rs"
91+
crate-type = ["bin"]

tests/gui/runner.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use std::env::current_dir;
2+
use std::ffi::OsStr;
3+
use std::fs::{read_to_string, remove_dir_all};
4+
use std::process::Command;
5+
6+
fn get_available_browser_ui_test_version_inner(global: bool) -> Option<String> {
7+
let mut command = Command::new("npm");
8+
command
9+
.arg("list")
10+
.arg("--parseable")
11+
.arg("--long")
12+
.arg("--depth=0");
13+
if global {
14+
command.arg("--global");
15+
}
16+
let stdout = command.output().expect("`npm` command not found").stdout;
17+
let lines = String::from_utf8_lossy(&stdout);
18+
lines
19+
.lines()
20+
.find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@"))
21+
.map(std::borrow::ToOwned::to_owned)
22+
}
23+
24+
fn get_available_browser_ui_test_version() -> Option<String> {
25+
get_available_browser_ui_test_version_inner(false)
26+
.or_else(|| get_available_browser_ui_test_version_inner(true))
27+
}
28+
29+
fn expected_browser_ui_test_version() -> String {
30+
let content = read_to_string(".github/workflows/main.yml")
31+
.expect("failed to read `.github/workflows/main.yml`");
32+
for line in content.lines() {
33+
let line = line.trim();
34+
if let Some(version) = line.strip_prefix("BROWSER_UI_TEST_VERSION:") {
35+
return version.trim().replace('\'', "");
36+
}
37+
}
38+
panic!("failed to retrieved `browser-ui-test` version");
39+
}
40+
41+
fn main() {
42+
let browser_ui_test_version = expected_browser_ui_test_version();
43+
match get_available_browser_ui_test_version() {
44+
Some(version) => {
45+
if version != browser_ui_test_version {
46+
eprintln!(
47+
"⚠️ Installed version of browser-ui-test (`{version}`) is different than the \
48+
one used in the CI (`{browser_ui_test_version}`) You can install this version \
49+
using `npm update browser-ui-test` or by using `npm install browser-ui-test\
50+
@{browser_ui_test_version}`",
51+
);
52+
}
53+
}
54+
None => {
55+
panic!(
56+
"`browser-ui-test` is not installed. You can install this package using `npm \
57+
update browser-ui-test` or by using `npm install browser-ui-test\
58+
@{browser_ui_test_version}`",
59+
);
60+
}
61+
}
62+
63+
let current_dir = current_dir().expect("failed to retrieve current directory");
64+
let test_book = current_dir.join("test_book");
65+
66+
// Result doesn't matter.
67+
let _ = remove_dir_all(test_book.join("book"));
68+
69+
let mut cmd = Command::new("cargo");
70+
cmd.arg("run").arg("build").arg(&test_book);
71+
// Then we run the GUI tests on it.
72+
assert!(cmd.status().is_ok_and(|status| status.success()));
73+
74+
let book_dir = format!("file://{}", current_dir.join("test_book/book/").display());
75+
76+
let mut command = Command::new("npx");
77+
command
78+
.arg("browser-ui-test")
79+
.args(["--variable", "DOC_PATH", book_dir.as_str()])
80+
.args(["--test-folder", "tests/gui"]);
81+
if std::env::var_os("DISABLE_HEADLESS_TEST").is_some_and(|value| value == OsStr::new("1")) {
82+
command.arg("--no-headless");
83+
}
84+
85+
// Then we run the GUI tests on it.
86+
assert!(command.status().is_ok_and(|status| status.success()));
87+
}

tests/gui/sidebar.goml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// This GUI test checks sidebar hide/show and also its behaviour on smaller
2+
// width.
3+
4+
// We disable the requests checks because `searchindex.json` will always fail
5+
// locally.
6+
fail-on-request-error: false
7+
go-to: |DOC_PATH| + "index.html"
8+
set-window-size: (1100, 600)
9+
// Need to reload for the new size to be taken account by the JS.
10+
reload:
11+
12+
store-value: (content_indent, 308)
13+
14+
define-function: (
15+
"hide-sidebar",
16+
[],
17+
block {
18+
// The content should be "moved" to the right because of the sidebar.
19+
assert-css: ("#sidebar", {"transform": "none"})
20+
assert-position: ("#page-wrapper", {"x": |content_indent|})
21+
22+
// We now hide the sidebar.
23+
click: "#sidebar-toggle"
24+
wait-for: "body.sidebar-hidden"
25+
// `transform` is 0.3s so we need to wait a bit (0.5s) to ensure the animation is done.
26+
wait-for: 5000
27+
assert-css-false: ("#sidebar", {"transform": "none"})
28+
// The page content should now be on the left.
29+
assert-position: ("#page-wrapper", {"x": 0})
30+
},
31+
)
32+
33+
define-function: (
34+
"show-sidebar",
35+
[],
36+
block {
37+
// The page content should be on the left and the sidebar "moved out".
38+
assert-css: ("#sidebar", {"transform": "matrix(1, 0, 0, 1, -308, 0)"})
39+
assert-position: ("#page-wrapper", {"x": 0})
40+
41+
// We expand the sidebar.
42+
click: "#sidebar-toggle"
43+
wait-for: "body.sidebar-visible"
44+
// `transform` is 0.3s so we need to wait a bit (0.5s) to ensure the animation is done.
45+
wait-for: 5000
46+
assert-css-false: ("#sidebar", {"transform": "matrix(1, 0, 0, 1, -308, 0)"})
47+
// The page content should be moved to the right.
48+
assert-position: ("#page-wrapper", {"x": |content_indent|})
49+
},
50+
)
51+
52+
call-function: ("hide-sidebar", {})
53+
call-function: ("show-sidebar", {})
54+
55+
// We now test on smaller width to ensure that the sidebar is collapsed by default.
56+
set-window-size: (900, 600)
57+
reload:
58+
call-function: ("show-sidebar", {})
59+
call-function: ("hide-sidebar", {})

0 commit comments

Comments
 (0)