Skip to content

Commit 4bde887

Browse files
Add start of GUI tests for clippy lints page
1 parent 73bad36 commit 4bde887

File tree

8 files changed

+211
-0
lines changed

8 files changed

+211
-0
lines changed

.cargo/config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
bless = "test --config env.RUSTC_BLESS='1'"
33
uitest = "test --test compile-test"
44
uibless = "bless --test compile-test"
5+
guitest = "test --test gui"
56
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
67
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
78
collect-metadata = "test --test compile-test --config env.COLLECT_METADATA='1'"

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,8 @@ helper.txt
4646

4747
# mdbook generated output
4848
/book/book
49+
50+
# GUI tests
51+
node_modules
52+
package-lock.json
53+
package.json

book/src/development/basics.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ cargo uitest
6060
TESTNAME="test_" cargo uitest
6161
# only run dogfood tests
6262
cargo dev dogfood
63+
# only run GUI tests (clippy lints page)
64+
cargo guitest
6365
```
6466

6567
If the output of a [UI test] differs from the expected output, you can update

tests/gui.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// This test ensures that the clippy lints page is working as expected.
2+
3+
use std::ffi::OsStr;
4+
use std::path::Path;
5+
use std::process::Command;
6+
use std::time::SystemTime;
7+
8+
const BROWSER_UI_TEST_VERSION: &str = "0.18.2";
9+
10+
fn get_browser_ui_test_version_inner(global: bool) -> Option<String> {
11+
let mut command = Command::new("npm");
12+
command.arg("list").arg("--parseable").arg("--long").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(|v| v.to_owned())
22+
}
23+
24+
fn get_browser_ui_test_version() -> Option<String> {
25+
get_browser_ui_test_version_inner(false).or_else(|| get_browser_ui_test_version_inner(true))
26+
}
27+
28+
fn mtime(path: impl AsRef<Path>) -> SystemTime {
29+
let path = path.as_ref();
30+
if path.is_dir() {
31+
path.read_dir()
32+
.into_iter()
33+
.flatten()
34+
.flatten()
35+
.map(|entry| mtime(entry.path()))
36+
.max()
37+
.unwrap_or(SystemTime::UNIX_EPOCH)
38+
} else {
39+
path.metadata()
40+
.and_then(|metadata| metadata.modified())
41+
.unwrap_or(SystemTime::UNIX_EPOCH)
42+
}
43+
}
44+
45+
#[test]
46+
fn check_clippy_lints_page() {
47+
// do not run this test inside the upstream rustc repo.
48+
if option_env!("RUSTC_TEST_SUITE").is_some() {
49+
return;
50+
}
51+
match get_browser_ui_test_version() {
52+
Some(version) => {
53+
if version != BROWSER_UI_TEST_VERSION {
54+
eprintln!(
55+
"⚠️ Installed version of browser-ui-test (`{version}`) is different than the \
56+
one used in the CI (`{BROWSER_UI_TEST_VERSION}`) You can install this version \
57+
using `npm update browser-ui-test` or by using `npm install browser-ui-test\
58+
@{BROWSER_UI_TEST_VERSION}`",
59+
);
60+
}
61+
},
62+
None => {
63+
panic!(
64+
"`browser-ui-test` is not installed. You can install this package using `npm \
65+
update browser-ui-test` or by using `npm install browser-ui-test@{}`",
66+
BROWSER_UI_TEST_VERSION,
67+
);
68+
},
69+
}
70+
71+
// We build the lints page only if needed.
72+
let index_time = mtime("util/gh-pages/index.html");
73+
74+
if index_time < mtime("clippy_lints/src") || index_time < mtime("util/gh-pages/index_template.html") {
75+
if !Command::new("cargo")
76+
.arg("collect-metadata")
77+
.status()
78+
.is_ok_and(|status| status.success())
79+
{
80+
panic!("failed to run `cargo collect-metadata`");
81+
}
82+
}
83+
84+
let current_dir = std::env::current_dir()
85+
.expect("failed to retrieve current directory")
86+
.join("util/gh-pages/index.html");
87+
let current_dir = format!("file://{}", current_dir.display());
88+
let mut command = Command::new("npx");
89+
command
90+
.arg("browser-ui-test")
91+
.args(&["--variable", "DOC_PATH", current_dir.as_str()])
92+
.args(&["--test-folder", "tests/gui"]);
93+
if std::env::var_os("DISABLE_HEADLESS_TEST").is_some_and(|value| value == OsStr::new("1")) {
94+
command.arg("--no-headless");
95+
}
96+
97+
// Then we run the GUI tests on it.
98+
assert!(command.status().is_ok_and(|status| status.success()));
99+
}

tests/gui/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
The tests present here are used to test the clippy lints page. The
2+
goal is to prevent unsound/unexpected GUI (breaking) changes.
3+
4+
This is using the [browser-ui-test] framework to do so. It works as follows:
5+
6+
It wraps [puppeteer] to send commands to a web browser in order to navigate and
7+
test what's being currently displayed in the web page.
8+
9+
You can find more information and its documentation in its [repository][browser-ui-test].
10+
11+
If you don't want to run in headless mode (helpful to debug sometimes), you can use
12+
`DISABLE_HEADLESS_TEST=1`:
13+
14+
```bash
15+
$ DISABLE_HEADLESS_TEST=1 cargo guitest
16+
```
17+
18+
[browser-ui-test]: https://github.com/GuillaumeGomez/browser-UI-test/
19+
[puppeteer]: https://pptr.dev/

tests/gui/hash.goml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// This GUI test ensures that when the URL has a hash, it will open the target lint.
2+
3+
go-to: |DOC_PATH|
4+
// First we ensure that by default, the lint is not displayed.
5+
assert-css: ("#alloc_instead_of_core .lint-docs", {"display": "none"})
6+
// First we move the mouse cursor to the lint to make the anchor appear.
7+
move-cursor-to: "#alloc_instead_of_core"
8+
// We wait for the anchor to be visible.
9+
wait-for-css-false: ("#alloc_instead_of_core .anchor", {"display": "none"})
10+
click: "#alloc_instead_of_core .anchor"
11+
// Clicking on the anchor should have two effects:
12+
// 1. Change the URL hash.
13+
// 2. Open the lint.
14+
wait-for-css: ("#alloc_instead_of_core .lint-docs", {"display": "block"})
15+
wait-for-document-property: {"location"."hash": "#alloc_instead_of_core"}
16+
17+
// Now we reload the page. The lint should still be open since the hash is
18+
// targetting it.
19+
go-to: |DOC_PATH| + "#alloc_instead_of_core"
20+
wait-for-css: ("#alloc_instead_of_core .lint-docs", {"display": "block"})
21+
// Other lints should not be expanded.
22+
wait-for-css: ("#absolute_paths .lint-docs", {"display": "none"})

tests/gui/no-js.goml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// This GUI test checks the lints page works as expected when JS is disabled.
2+
javascript: false // disabling javascript
3+
go-to: |DOC_PATH|
4+
5+
define-function: (
6+
"check-expanded-collapsed",
7+
[display, content],
8+
block {
9+
wait-for-css: ("#absolute_paths > .lint-docs", {"display": |display|})
10+
assert-css: ("#absolute_paths .label-doc-folding::before", {"content": |content|})
11+
},
12+
)
13+
14+
define-function: (
15+
"check-expand-collapse-action",
16+
[selector],
17+
block {
18+
// We confirm it's collapsed.
19+
call-function: ("check-expanded-collapsed", {
20+
"display": "none",
21+
"content": '"+"',
22+
})
23+
// We click on the item to expand it.
24+
click: |selector|
25+
// We confirm it's expanded.
26+
call-function: ("check-expanded-collapsed", {
27+
"display": "block",
28+
"content": '"−"',
29+
})
30+
// We collapse it again.
31+
click: |selector|
32+
// We confirm it's collapsed again.
33+
call-function: ("check-expanded-collapsed", {
34+
"display": "none",
35+
"content": '"+"',
36+
})
37+
},
38+
)
39+
40+
// First we check that we can expand/collapse a lint by clicking on the lint.
41+
call-function: ("check-expand-collapse-action", {"selector": "#lint-absolute_paths"})
42+
// Then we check the expand/collapse works when clicking on the +/- button.
43+
call-function: ("check-expand-collapse-action", {"selector": "#absolute_paths .label-doc-folding"})
44+
45+
// Checking click on the anchor changes the location hash.
46+
assert-document-property: {"location"."hash": ""}
47+
click: "#absolute_paths .panel-title .anchor"
48+
assert-document-property: {"location"."hash": "#absolute_paths"}

tests/gui/search.goml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// This test ensures that the search is filtering lints correctly.
2+
go-to: |DOC_PATH|
3+
4+
assert-css: ("#absurd_extreme_comparisons", {"display": "block"})
5+
assert-css: ("#absolute_paths", {"display": "block"})
6+
assert-css: ("#join_absolute_paths", {"display": "block"})
7+
8+
// We update the search.
9+
write-into: ("#search-input", "absolute_paths")
10+
11+
// `absolute_paths` and `join_absolute_path` should still be visible, but
12+
// not `absurde_extreme_comparisons`.
13+
wait-for-css: ("#absurd_extreme_comparisons", {"display": "none"})
14+
assert-css: ("#absolute_paths", {"display": "block"})
15+
assert-css: ("#join_absolute_paths", {"display": "block"})

0 commit comments

Comments
 (0)