summaryrefslogtreecommitdiffstats
path: root/tests/rustdoc-gui
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
commit218caa410aa38c29984be31a5229b9fa717560ee (patch)
treec54bd55eeb6e4c508940a30e94c0032fbd45d677 /tests/rustdoc-gui
parentReleasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz
rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/rustdoc-gui')
-rw-r--r--tests/rustdoc-gui/README.md34
-rw-r--r--tests/rustdoc-gui/anchor-navigable.goml11
-rw-r--r--tests/rustdoc-gui/anchors.goml109
-rw-r--r--tests/rustdoc-gui/auto-hide-trait-implementations.goml13
-rw-r--r--tests/rustdoc-gui/basic-code.goml6
-rw-r--r--tests/rustdoc-gui/check-code-blocks-margin.goml6
-rw-r--r--tests/rustdoc-gui/check-stab-in-docblock.goml27
-rw-r--r--tests/rustdoc-gui/check_info_sign_position.goml11
-rw-r--r--tests/rustdoc-gui/code-blocks-overflow.goml8
-rw-r--r--tests/rustdoc-gui/code-color.goml24
-rw-r--r--tests/rustdoc-gui/code-sidebar-toggle.goml7
-rw-r--r--tests/rustdoc-gui/code-tags.goml24
-rw-r--r--tests/rustdoc-gui/codeblock-sub.goml5
-rw-r--r--tests/rustdoc-gui/codeblock-tooltip.goml149
-rw-r--r--tests/rustdoc-gui/cursor.goml24
-rw-r--r--tests/rustdoc-gui/default-settings.goml8
-rw-r--r--tests/rustdoc-gui/docblock-big-code-mobile.goml13
-rw-r--r--tests/rustdoc-gui/docblock-code-block-line-number.goml71
-rw-r--r--tests/rustdoc-gui/docblock-details.goml34
-rw-r--r--tests/rustdoc-gui/docblock-table-overflow.goml21
-rw-r--r--tests/rustdoc-gui/docblock-table.goml52
-rw-r--r--tests/rustdoc-gui/duplicate-macro-reexport.goml14
-rw-r--r--tests/rustdoc-gui/enum-variants.goml10
-rw-r--r--tests/rustdoc-gui/escape-key.goml35
-rw-r--r--tests/rustdoc-gui/font-weight.goml44
-rw-r--r--tests/rustdoc-gui/hash-item-expansion.goml11
-rw-r--r--tests/rustdoc-gui/headers-color.goml70
-rw-r--r--tests/rustdoc-gui/headings.goml234
-rw-r--r--tests/rustdoc-gui/help-page.goml72
-rw-r--r--tests/rustdoc-gui/highlight-colors.goml94
-rw-r--r--tests/rustdoc-gui/huge-collection-of-constants.goml9
-rw-r--r--tests/rustdoc-gui/huge-logo.goml21
-rw-r--r--tests/rustdoc-gui/impl-default-expansion.goml3
-rw-r--r--tests/rustdoc-gui/impl-doc.goml9
-rw-r--r--tests/rustdoc-gui/implementors.goml41
-rw-r--r--tests/rustdoc-gui/item-decl-colors.goml80
-rw-r--r--tests/rustdoc-gui/item-info-alignment.goml10
-rw-r--r--tests/rustdoc-gui/item-info-overflow.goml31
-rw-r--r--tests/rustdoc-gui/item-info.goml32
-rw-r--r--tests/rustdoc-gui/item-summary-table.goml6
-rw-r--r--tests/rustdoc-gui/javascript-disabled.goml6
-rw-r--r--tests/rustdoc-gui/jump-to-def-background.goml22
-rw-r--r--tests/rustdoc-gui/label-next-to-symbol.goml66
-rw-r--r--tests/rustdoc-gui/links-color.goml98
-rw-r--r--tests/rustdoc-gui/list_code_block.goml4
-rw-r--r--tests/rustdoc-gui/method-margins.goml18
-rw-r--r--tests/rustdoc-gui/mobile.goml34
-rw-r--r--tests/rustdoc-gui/module-items-font.goml67
-rw-r--r--tests/rustdoc-gui/no-docblock.goml13
-rw-r--r--tests/rustdoc-gui/notable-trait.goml276
-rw-r--r--tests/rustdoc-gui/overflow-tooltip-information.goml8
-rw-r--r--tests/rustdoc-gui/pocket-menu.goml98
-rw-r--r--tests/rustdoc-gui/run-on-hover.goml54
-rw-r--r--tests/rustdoc-gui/rust-logo.goml58
-rw-r--r--tests/rustdoc-gui/scrape-examples-button-focus.goml31
-rw-r--r--tests/rustdoc-gui/scrape-examples-color.goml60
-rw-r--r--tests/rustdoc-gui/scrape-examples-fonts.goml9
-rw-r--r--tests/rustdoc-gui/scrape-examples-layout.goml49
-rw-r--r--tests/rustdoc-gui/scrape-examples-toggle.goml51
-rw-r--r--tests/rustdoc-gui/search-filter.goml87
-rw-r--r--tests/rustdoc-gui/search-form-elements.goml263
-rw-r--r--tests/rustdoc-gui/search-input-mobile.goml11
-rw-r--r--tests/rustdoc-gui/search-keyboard.goml28
-rw-r--r--tests/rustdoc-gui/search-no-result.goml36
-rw-r--r--tests/rustdoc-gui/search-reexport.goml33
-rw-r--r--tests/rustdoc-gui/search-result-color.goml401
-rw-r--r--tests/rustdoc-gui/search-result-description.goml5
-rw-r--r--tests/rustdoc-gui/search-result-display.goml78
-rw-r--r--tests/rustdoc-gui/search-result-go-to-first.goml19
-rw-r--r--tests/rustdoc-gui/search-result-keyword.goml13
-rw-r--r--tests/rustdoc-gui/search-tab-change-title-fn-sig.goml74
-rw-r--r--tests/rustdoc-gui/search-tab.goml76
-rw-r--r--tests/rustdoc-gui/settings.goml253
-rw-r--r--tests/rustdoc-gui/shortcuts.goml31
-rw-r--r--tests/rustdoc-gui/sidebar-links-color.goml171
-rw-r--r--tests/rustdoc-gui/sidebar-macro-reexport.goml5
-rw-r--r--tests/rustdoc-gui/sidebar-mobile-scroll.goml31
-rw-r--r--tests/rustdoc-gui/sidebar-mobile.goml88
-rw-r--r--tests/rustdoc-gui/sidebar-source-code-display.goml215
-rw-r--r--tests/rustdoc-gui/sidebar-source-code.goml90
-rw-r--r--tests/rustdoc-gui/sidebar.goml151
-rw-r--r--tests/rustdoc-gui/source-anchor-scroll.goml20
-rw-r--r--tests/rustdoc-gui/source-code-page.goml218
-rw-r--r--tests/rustdoc-gui/src-font-size.goml16
-rw-r--r--tests/rustdoc-gui/src/huge_logo/Cargo.lock7
-rw-r--r--tests/rustdoc-gui/src/huge_logo/Cargo.toml8
-rw-r--r--tests/rustdoc-gui/src/huge_logo/src/lib.rs17
-rw-r--r--tests/rustdoc-gui/src/lib2/Cargo.lock22
-rw-r--r--tests/rustdoc-gui/src/lib2/Cargo.toml11
-rw-r--r--tests/rustdoc-gui/src/lib2/another_folder/mod.rs3
-rw-r--r--tests/rustdoc-gui/src/lib2/another_folder/sub_mod/mod.rs1
-rw-r--r--tests/rustdoc-gui/src/lib2/another_mod/mod.rs1
-rw-r--r--tests/rustdoc-gui/src/lib2/http/Cargo.toml7
-rw-r--r--tests/rustdoc-gui/src/lib2/http/lib.rs1
-rw-r--r--tests/rustdoc-gui/src/lib2/implementors/Cargo.lock7
-rw-r--r--tests/rustdoc-gui/src/lib2/implementors/Cargo.toml10
-rw-r--r--tests/rustdoc-gui/src/lib2/implementors/lib.rs22
-rw-r--r--tests/rustdoc-gui/src/lib2/lib.rs343
-rw-r--r--tests/rustdoc-gui/src/link_to_definition/Cargo.lock7
-rw-r--r--tests/rustdoc-gui/src/link_to_definition/Cargo.toml7
-rw-r--r--tests/rustdoc-gui/src/link_to_definition/lib.rs35
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/Cargo.lock7
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/Cargo.toml8
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs13
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs13
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs13
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs13
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs13
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs13
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs13
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/examples/check.rs26
-rw-r--r--tests/rustdoc-gui/src/scrape_examples/src/lib.rs9
-rw-r--r--tests/rustdoc-gui/src/settings/.cargo/config.toml2
-rw-r--r--tests/rustdoc-gui/src/settings/Cargo.lock7
-rw-r--r--tests/rustdoc-gui/src/settings/Cargo.toml7
-rw-r--r--tests/rustdoc-gui/src/settings/lib.rs1
-rw-r--r--tests/rustdoc-gui/src/staged_api/Cargo.lock7
-rw-r--r--tests/rustdoc-gui/src/staged_api/Cargo.toml12
-rw-r--r--tests/rustdoc-gui/src/staged_api/lib.rs12
-rw-r--r--tests/rustdoc-gui/src/test_docs/Cargo.lock7
-rw-r--r--tests/rustdoc-gui/src/test_docs/Cargo.toml13
-rw-r--r--tests/rustdoc-gui/src/test_docs/build.rs15
-rw-r--r--tests/rustdoc-gui/src/test_docs/lib.rs476
-rw-r--r--tests/rustdoc-gui/src/test_docs/macros.rs4
-rw-r--r--tests/rustdoc-gui/stab-badge.goml41
-rw-r--r--tests/rustdoc-gui/struct-fields.goml5
-rw-r--r--tests/rustdoc-gui/target.goml35
-rw-r--r--tests/rustdoc-gui/theme-change.goml67
-rw-r--r--tests/rustdoc-gui/theme-in-history.goml27
-rw-r--r--tests/rustdoc-gui/toggle-click-deadspace.goml15
-rw-r--r--tests/rustdoc-gui/toggle-docs-mobile.goml33
-rw-r--r--tests/rustdoc-gui/toggle-docs.goml75
-rw-r--r--tests/rustdoc-gui/toggle-implementors.goml4
-rw-r--r--tests/rustdoc-gui/toggled-open-implementations.goml5
-rw-r--r--tests/rustdoc-gui/trait-sidebar-item-order.goml13
-rw-r--r--tests/rustdoc-gui/type-declation-overflow.goml76
-rw-r--r--tests/rustdoc-gui/unsafe-fn.goml28
-rw-r--r--tests/rustdoc-gui/where-whitespace.goml27
138 files changed, 6582 insertions, 0 deletions
diff --git a/tests/rustdoc-gui/README.md b/tests/rustdoc-gui/README.md
new file mode 100644
index 000000000..1126a72ab
--- /dev/null
+++ b/tests/rustdoc-gui/README.md
@@ -0,0 +1,34 @@
+The tests present here are used to test the generated HTML from rustdoc. The
+goal is to prevent unsound/unexpected GUI changes.
+
+This is using the [browser-ui-test] framework to do so. It works as follows:
+
+It wraps [puppeteer] to send commands to a web browser in order to navigate and
+test what's being currently displayed in the web page.
+
+You can find more information and its documentation in its [repository][browser-ui-test].
+
+If you need to have more information on the tests run, you can use `--test-args`:
+
+```bash
+$ ./x.py test tests/rustdoc-gui --stage 1 --test-args --debug
+```
+
+If you don't want to run in headless mode (helpful to debug sometimes), you can use
+`--no-headless`:
+
+```bash
+$ ./x.py test tests/rustdoc-gui --stage 1 --test-args --no-headless
+```
+
+To see the supported options, use `--help`.
+
+Important to be noted: if the chromium instance crashes when you run it, you might need to
+use `--no-sandbox` to make it work:
+
+```bash
+$ ./x.py test tests/rustdoc-gui --stage 1 --test-args --no-sandbox
+```
+
+[browser-ui-test]: https://github.com/GuillaumeGomez/browser-UI-test/
+[puppeteer]: https://pptr.dev/
diff --git a/tests/rustdoc-gui/anchor-navigable.goml b/tests/rustdoc-gui/anchor-navigable.goml
new file mode 100644
index 000000000..9d5c55a1e
--- /dev/null
+++ b/tests/rustdoc-gui/anchor-navigable.goml
@@ -0,0 +1,11 @@
+// The `impl Foo` heading underneath `Implementations` has a §
+// anchor to its left (used for linking to that heading). The anchor only shows
+// up when hovering the `impl Foo`. This test ensures there's no gap between the
+// anchor and the `impl Foo`. If there were a gap, this would cause an annoying
+// problem: you hover `impl Foo` to see the anchor, then when you move your
+// mouse to the left, the anchor disappears before you reach it.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+// We check that ".item-info" is bigger than its content.
+move-cursor-to: ".impl"
+assert-property: (".impl > a.anchor", {"offsetWidth": "8"})
+assert-css: (".impl > a.anchor", {"left": "-8px"})
diff --git a/tests/rustdoc-gui/anchors.goml b/tests/rustdoc-gui/anchors.goml
new file mode 100644
index 000000000..85cb72274
--- /dev/null
+++ b/tests/rustdoc-gui/anchors.goml
@@ -0,0 +1,109 @@
+// This test is to ensure that the anchors (`§`) have the expected color and position.
+
+define-function: (
+ "check-colors",
+ (theme, main_color, title_color, main_heading_color, main_heading_type_color, src_link_color, sidebar_link_color),
+ block {
+ goto: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
+ // This is needed to ensure that the text color is computed.
+ show-text: true
+
+ // Setting the theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ // We reload the page so the local storage settings are being used.
+ reload:
+
+ assert-css: ("#toggle-all-docs", {"color": |main_color|})
+ assert-css: (".main-heading h1 a:nth-of-type(1)", {"color": |main_heading_color|})
+ assert-css: (".main-heading a:nth-of-type(2)", {"color": |main_heading_type_color|})
+ assert-css: (
+ ".rightside .srclink",
+ {"color": |src_link_color|, "text-decoration": "none solid " + |src_link_color|},
+ ALL,
+ )
+ compare-elements-css: (
+ ".rightside .srclink",
+ ".rightside.srclink",
+ ["color", "text-decoration"],
+ )
+ compare-elements-css: (
+ ".main-heading .srclink",
+ ".rightside.srclink",
+ ["color", "text-decoration"],
+ )
+
+ move-cursor-to: ".main-heading .srclink"
+ assert-css: (
+ ".main-heading .srclink",
+ {"color": |src_link_color|, "text-decoration": "underline solid " + |src_link_color|},
+ )
+ move-cursor-to: ".impl-items .rightside .srclink"
+ assert-css: (
+ ".impl-items .rightside .srclink",
+ {"color": |src_link_color|, "text-decoration": "none solid " + |src_link_color|},
+ )
+ move-cursor-to: ".impl-items .rightside.srclink"
+ assert-css: (
+ ".impl-items .rightside.srclink",
+ {"color": |src_link_color|, "text-decoration": "none solid " + |src_link_color|},
+ )
+
+ goto: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
+ // Since we changed page, we need to set the theme again.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ // We reload the page so the local storage settings are being used.
+ reload:
+
+ assert-css: ("#top-doc-prose-title", {"color": |title_color|})
+
+ assert-css: (".sidebar a", {"color": |sidebar_link_color|})
+ assert-css: (".main-heading h1 a", {"color": |title_color|})
+
+ // We move the cursor over the "Implementations" title so the anchor is displayed.
+ move-cursor-to: "h2#implementations"
+ assert-css: ("h2#implementations a.anchor", {"color": |main_color|})
+
+ // Same thing with the impl block title.
+ move-cursor-to: "#impl-HeavilyDocumentedStruct"
+ assert-css: ("#impl-HeavilyDocumentedStruct a.anchor", {"color": |main_color|})
+
+ assert-css: ("#title-for-struct-impl-item-doc", {"margin-left": "0px"})
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "main_color": "rgb(197, 197, 197)",
+ "title_color": "rgb(255, 255, 255)",
+ "main_heading_color": "rgb(255, 255, 255)",
+ "main_heading_type_color": "rgb(255, 160, 165)",
+ "src_link_color": "rgb(57, 175, 215)",
+ "sidebar_link_color": "rgb(83, 177, 219)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "main_color": "rgb(221, 221, 221)",
+ "title_color": "rgb(221, 221, 221)",
+ "main_heading_color": "rgb(221, 221, 221)",
+ "main_heading_type_color": "rgb(45, 191, 184)",
+ "src_link_color": "rgb(210, 153, 29)",
+ "sidebar_link_color": "rgb(253, 191, 53)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "main_color": "rgb(0, 0, 0)",
+ "title_color": "rgb(0, 0, 0)",
+ "main_heading_color": "rgb(0, 0, 0)",
+ "main_heading_type_color": "rgb(173, 55, 138)",
+ "src_link_color": "rgb(56, 115, 173)",
+ "sidebar_link_color": "rgb(53, 109, 164)",
+ },
+)
diff --git a/tests/rustdoc-gui/auto-hide-trait-implementations.goml b/tests/rustdoc-gui/auto-hide-trait-implementations.goml
new file mode 100644
index 000000000..0a619c352
--- /dev/null
+++ b/tests/rustdoc-gui/auto-hide-trait-implementations.goml
@@ -0,0 +1,13 @@
+// Checks that the setting "auto hide trait implementations" is working as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+
+// By default, the trait implementations are not collapsed.
+assert-attribute: ("#trait-implementations-list > details", {"open": ""}, ALL)
+
+// We now set the setting to auto hide all trait implementations.
+local-storage: {"rustdoc-auto-hide-trait-implementations": "true" }
+// We reload to ensure the trait implementations are collapsed as expected.
+reload:
+
+// We now check that all matching elements don't have the open attributes.
+assert-attribute-false: ("#trait-implementations-list > details", {"open": ""}, ALL)
diff --git a/tests/rustdoc-gui/basic-code.goml b/tests/rustdoc-gui/basic-code.goml
new file mode 100644
index 000000000..971c2f948
--- /dev/null
+++ b/tests/rustdoc-gui/basic-code.goml
@@ -0,0 +1,6 @@
+// Small test to ensure the "src-line-numbers" element is only present once on
+// the page.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+click: ".srclink"
+wait-for: ".src-line-numbers"
+assert-count: (".src-line-numbers", 1)
diff --git a/tests/rustdoc-gui/check-code-blocks-margin.goml b/tests/rustdoc-gui/check-code-blocks-margin.goml
new file mode 100644
index 000000000..c2cec0020
--- /dev/null
+++ b/tests/rustdoc-gui/check-code-blocks-margin.goml
@@ -0,0 +1,6 @@
+// This test ensures that the docblock elements have the appropriate left margin.
+goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+// The top docblock elements shouldn't have left margin...
+assert-css: ("#main-content .item-decl", {"margin-left": "0px"})
+// ... but all the others should!
+assert-css: ("#main-content .docblock", {"margin-left": "24px"})
diff --git a/tests/rustdoc-gui/check-stab-in-docblock.goml b/tests/rustdoc-gui/check-stab-in-docblock.goml
new file mode 100644
index 000000000..266fa9997
--- /dev/null
+++ b/tests/rustdoc-gui/check-stab-in-docblock.goml
@@ -0,0 +1,27 @@
+// This test checks that using `.stab` attributes in `.docblock` elements doesn't
+// create scrollable paragraphs.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Needs the text to be display to check for scrollable content.
+show-text: true
+size: (786, 600)
+// Confirms that there 3 paragraphs.
+assert-count: (".top-doc .docblock p", 3)
+// Checking that there is no scrollable content.
+store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(1)", "clientHeight")
+store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(1)", "clientWidth")
+assert-property: (
+ ".top-doc .docblock p:nth-of-type(1)",
+ {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|},
+)
+store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(2)", "clientHeight")
+store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(2)", "clientWidth")
+assert-property: (
+ ".top-doc .docblock p:nth-of-type(2)",
+ {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|},
+)
+store-property: (clientHeight, ".top-doc .docblock p:nth-of-type(3)", "clientHeight")
+store-property: (clientWidth, ".top-doc .docblock p:nth-of-type(3)", "clientWidth")
+assert-property: (
+ ".top-doc .docblock p:nth-of-type(3)",
+ {"scrollHeight": |clientHeight|, "scrollWidth": |clientWidth|},
+)
diff --git a/tests/rustdoc-gui/check_info_sign_position.goml b/tests/rustdoc-gui/check_info_sign_position.goml
new file mode 100644
index 000000000..f36e73fc5
--- /dev/null
+++ b/tests/rustdoc-gui/check_info_sign_position.goml
@@ -0,0 +1,11 @@
+// This test checks the position of the information on the code blocks (like
+// `compile_fail` or `ignore`).
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+goto: "./fn.check_list_code_block.html"
+// If the codeblock is the first element of the docblock, the information tooltip must have
+// have some top margin to avoid going over the toggle (the "[+]").
+assert-css: (".docblock > .example-wrap.compile_fail .tooltip", { "margin-top": "16px" })
+// Checks that the other codeblocks don't have this top margin.
+assert-css: ("ol > li > .example-wrap.compile_fail .tooltip", { "margin-top": "0px" })
+assert-css: ("ol > li > .example-wrap.ignore .tooltip", { "margin-top": "0px" })
+assert-css: (".docblock > .example-wrap.ignore .tooltip", { "margin-top": "0px" })
diff --git a/tests/rustdoc-gui/code-blocks-overflow.goml b/tests/rustdoc-gui/code-blocks-overflow.goml
new file mode 100644
index 000000000..fbf0e890b
--- /dev/null
+++ b/tests/rustdoc-gui/code-blocks-overflow.goml
@@ -0,0 +1,8 @@
+// This test ensures that codeblocks content don't overflow.
+goto: "file://" + |DOC_PATH| + "/lib2/sub_mod/struct.Foo.html"
+size: (1080, 600)
+// There should be two codeblocks: a rust one and a non-rust one.
+assert-count: (".docblock > .example-wrap", 2)
+assert: ".docblock > .example-wrap > .language-txt"
+assert: ".docblock > .example-wrap > .rust-example-rendered"
+assert-css: (".docblock > .example-wrap > pre", {"width": "796px", "overflow-x": "auto"}, ALL)
diff --git a/tests/rustdoc-gui/code-color.goml b/tests/rustdoc-gui/code-color.goml
new file mode 100644
index 000000000..cb550a457
--- /dev/null
+++ b/tests/rustdoc-gui/code-color.goml
@@ -0,0 +1,24 @@
+// The ayu theme has a different color for the "<code>" tags in the doc blocks. We need to
+// check that the rule isn't applied on other "<code>" elements.
+//
+// While we're at it, we also check it for the other themes.
+goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+// If the text isn't displayed, the browser doesn't compute color style correctly...
+show-text: true
+
+define-function: (
+ "check-colors",
+ (theme, doc_code_color, doc_inline_code_color),
+ block {
+ // Set the theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ // We reload the page so the local storage settings are being used.
+ reload:
+ assert-css: (".docblock pre > code", {"color": |doc_code_color|}, ALL)
+ assert-css: (".docblock > p > code", {"color": |doc_inline_code_color|}, ALL)
+ },
+)
+
+call-function: ("check-colors", ("ayu", "rgb(230, 225, 207)", "rgb(255, 180, 84)"))
+call-function: ("check-colors", ("dark", "rgb(221, 221, 221)", "rgb(221, 221, 221)"))
+call-function: ("check-colors", ("light", "rgb(0, 0, 0)", "rgb(0, 0, 0)"))
diff --git a/tests/rustdoc-gui/code-sidebar-toggle.goml b/tests/rustdoc-gui/code-sidebar-toggle.goml
new file mode 100644
index 000000000..df665bd46
--- /dev/null
+++ b/tests/rustdoc-gui/code-sidebar-toggle.goml
@@ -0,0 +1,7 @@
+// This test checks that the source code pages sidebar toggle is working as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+click: ".srclink"
+wait-for: "#src-sidebar-toggle"
+click: "#src-sidebar-toggle"
+fail: true
+assert-css: ("#source-sidebar", { "left": "-300px" })
diff --git a/tests/rustdoc-gui/code-tags.goml b/tests/rustdoc-gui/code-tags.goml
new file mode 100644
index 000000000..8561f537f
--- /dev/null
+++ b/tests/rustdoc-gui/code-tags.goml
@@ -0,0 +1,24 @@
+// This test ensures that items and documentation code blocks are wrapped in <pre><code>
+
+// We need to disable this check because `implementors/test_docs/trait.AnotherOne.js`
+// doesn't exist.
+fail-on-request-error: false
+goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+size: (1080, 600)
+// There should be four doc codeblocks.
+// Check that their content is inside <pre><code>
+assert-count: (".example-wrap pre > code", 4)
+// Check that function signature is inside <pre><code>
+assert: ".item-decl pre.rust > code"
+
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+assert: ".item-decl pre.rust > code"
+
+goto: "file://" + |DOC_PATH| + "/test_docs/enum.AnEnum.html"
+assert: ".item-decl pre.rust > code"
+
+goto: "file://" + |DOC_PATH| + "/test_docs/trait.AnotherOne.html"
+assert: ".item-decl pre.rust > code"
+
+goto: "file://" + |DOC_PATH| + "/test_docs/type.SomeType.html"
+assert: ".item-decl pre.rust > code"
diff --git a/tests/rustdoc-gui/codeblock-sub.goml b/tests/rustdoc-gui/codeblock-sub.goml
new file mode 100644
index 000000000..cbd314d27
--- /dev/null
+++ b/tests/rustdoc-gui/codeblock-sub.goml
@@ -0,0 +1,5 @@
+// Test that code blocks nested within <sub> do not have a line height of 0.
+goto: "file://" + |DOC_PATH| + "/test_docs/codeblock_sub/index.html"
+
+store-property: (codeblock_sub_1, "#codeblock-sub-1", "offsetHeight")
+assert-property-false: ("#codeblock-sub-3", { "offsetHeight": |codeblock_sub_1| })
diff --git a/tests/rustdoc-gui/codeblock-tooltip.goml b/tests/rustdoc-gui/codeblock-tooltip.goml
new file mode 100644
index 000000000..a3ef4e77b
--- /dev/null
+++ b/tests/rustdoc-gui/codeblock-tooltip.goml
@@ -0,0 +1,149 @@
+// Checking the colors of the codeblocks tooltips.
+goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+show-text: true
+
+define-function: (
+ "check-colors",
+ (theme, background, color, border),
+ block {
+ // Setting the theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+
+ // compile_fail block
+ assert-css: (
+ ".docblock .example-wrap.compile_fail .tooltip",
+ {"color": "rgba(255, 0, 0, 0.5)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.compile_fail",
+ {"border-left": "2px solid rgba(255, 0, 0, 0.5)"},
+ )
+
+ move-cursor-to: ".docblock .example-wrap.compile_fail .tooltip"
+
+ assert-css: (
+ ".docblock .example-wrap.compile_fail .tooltip",
+ {"color": "rgb(255, 0, 0)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.compile_fail",
+ {"border-left": "2px solid rgb(255, 0, 0)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.compile_fail .tooltip::after",
+ {
+ "content": '"This example deliberately fails to compile"',
+ "padding": "5px 3px 3px",
+ "background-color": |background|,
+ "color": |color|,
+ "border": "1px solid " + |border|,
+ },
+ )
+ assert-css: (
+ ".docblock .example-wrap.compile_fail .tooltip::before",
+ {
+ "border-width": "5px",
+ "border-style": "solid",
+ "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)",
+ },
+ )
+
+ // should_panic block
+ assert-css: (
+ ".docblock .example-wrap.should_panic .tooltip",
+ {"color": "rgba(255, 0, 0, 0.5)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.should_panic",
+ {"border-left": "2px solid rgba(255, 0, 0, 0.5)"},
+ )
+
+ move-cursor-to: ".docblock .example-wrap.should_panic .tooltip"
+
+ assert-css: (
+ ".docblock .example-wrap.should_panic .tooltip",
+ {"color": "rgb(255, 0, 0)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.should_panic",
+ {"border-left": "2px solid rgb(255, 0, 0)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.should_panic .tooltip::after",
+ {
+ "content": '"This example panics"',
+ "padding": "5px 3px 3px",
+ "background-color": |background|,
+ "color": |color|,
+ "border": "1px solid " + |border|,
+ },
+ )
+ assert-css: (
+ ".docblock .example-wrap.should_panic .tooltip::before",
+ {
+ "border-width": "5px",
+ "border-style": "solid",
+ "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)",
+ },
+ )
+
+ // ignore block
+ assert-css: (
+ ".docblock .example-wrap.ignore .tooltip",
+ {"color": "rgba(255, 142, 0, 0.6)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.ignore",
+ {"border-left": "2px solid rgba(255, 142, 0, 0.6)"},
+ )
+
+ move-cursor-to: ".docblock .example-wrap.ignore .tooltip"
+
+ assert-css: (
+ ".docblock .example-wrap.ignore .tooltip",
+ {"color": "rgb(255, 142, 0)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.ignore",
+ {"border-left": "2px solid rgb(255, 142, 0)"},
+ )
+ assert-css: (
+ ".docblock .example-wrap.ignore .tooltip::after",
+ {
+ "content": '"This example is not tested"',
+ "padding": "5px 3px 3px",
+ "background-color": |background|,
+ "color": |color|,
+ "border": "1px solid " + |border|,
+ },
+ )
+ assert-css: (
+ ".docblock .example-wrap.ignore .tooltip::before",
+ {
+ "border-width": "5px",
+ "border-style": "solid",
+ "border-color": "rgba(0, 0, 0, 0) " + |background| + " rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)",
+ },
+ )
+ },
+)
+
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "background": "rgb(49, 69, 89)",
+ "color": "rgb(197, 197, 197)",
+ "border": "rgb(92, 103, 115)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "background": "rgb(0, 0, 0)",
+ "color": "rgb(255, 255, 255)",
+ "border": "rgb(224, 224, 224)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "background": "rgb(0, 0, 0)",
+ "color": "rgb(255, 255, 255)",
+ "border": "rgb(224, 224, 224)",
+})
diff --git a/tests/rustdoc-gui/cursor.goml b/tests/rustdoc-gui/cursor.goml
new file mode 100644
index 000000000..59b139797
--- /dev/null
+++ b/tests/rustdoc-gui/cursor.goml
@@ -0,0 +1,24 @@
+// This test ensures that several clickable items actually have the pointer cursor.
+goto: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html"
+
+// the `[+]/[-]` button
+assert-css: ("#toggle-all-docs", {"cursor": "pointer"})
+
+// the button next to the path header
+assert-css: ("#copy-path", {"cursor": "pointer"})
+
+// the search tabs
+write: (".search-input", "Foo")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-css: ("#search-tabs > button", {"cursor": "pointer"})
+
+// mobile sidebar toggle button
+size: (500, 700)
+assert-css: (".sidebar-menu-toggle", {"cursor": "pointer"})
+
+// the sidebar toggle button on the source code pages
+goto: "file://" + |DOC_PATH| + "/src/lib2/lib.rs.html"
+assert-css: ("#src-sidebar-toggle > button", {"cursor": "pointer"})
diff --git a/tests/rustdoc-gui/default-settings.goml b/tests/rustdoc-gui/default-settings.goml
new file mode 100644
index 000000000..ab27b001e
--- /dev/null
+++ b/tests/rustdoc-gui/default-settings.goml
@@ -0,0 +1,8 @@
+// This test ensures that the default settings are correctly applied.
+//
+// The "settings" crate uses "ayu" as default setting, which is what we will
+// check.
+goto: "file://" + |DOC_PATH| + "/settings/index.html"
+// Wait a bit to be sure the default theme is applied.
+// If the theme isn't applied, the command will time out.
+wait-for-css: ("body", {"background-color": "rgb(15, 20, 25)"})
diff --git a/tests/rustdoc-gui/docblock-big-code-mobile.goml b/tests/rustdoc-gui/docblock-big-code-mobile.goml
new file mode 100644
index 000000000..3ce921c2c
--- /dev/null
+++ b/tests/rustdoc-gui/docblock-big-code-mobile.goml
@@ -0,0 +1,13 @@
+// If we have a long `<code>`, we need to ensure that it'll be fully displayed on mobile, meaning
+// that it'll be on two lines.
+emulate: "iPhone 8" // it has the following size: (375, 667)
+goto: "file://" + |DOC_PATH| + "/test_docs/long_code_block/index.html"
+// We now check that the block is on two lines:
+show-text: true // We need to enable text draw to be able to have the "real" size
+// Little explanations for this test: if the text wasn't displayed on two lines, it would take
+// around 20px (which is the font size).
+assert-property: (".docblock p > code", {"offsetHeight": "44"})
+
+// Same check, but where the long code block is also a link
+goto: "file://" + |DOC_PATH| + "/test_docs/long_code_block_link/index.html"
+assert-property: (".docblock p > a > code", {"offsetHeight": "44"})
diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml
new file mode 100644
index 000000000..69bcf5339
--- /dev/null
+++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml
@@ -0,0 +1,71 @@
+// Checks that the setting "line numbers" is working as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+
+// Otherwise, we can't check text color
+show-text: true
+
+// We check that without this setting, there is no line number displayed.
+assert-false: "pre.example-line-numbers"
+
+// Let's now check some CSS properties...
+define-function: (
+ "check-colors",
+ (theme, color),
+ block {
+ // We now set the setting to show the line numbers on code examples.
+ local-storage: {
+ "rustdoc-theme": |theme|,
+ "rustdoc-use-system-theme": "false",
+ "rustdoc-line-numbers": "true"
+ }
+ // We reload to make the line numbers appear and change theme.
+ reload:
+ // We wait for them to be added into the DOM by the JS...
+ wait-for: "pre.example-line-numbers"
+ // If the test didn't fail, it means that it was found!
+ assert-css: (
+ "pre.example-line-numbers",
+ {
+ "color": |color|,
+ "margin": "0px",
+ "padding": "14px 8px",
+ "text-align": "right",
+ },
+ ALL,
+ )
+ },
+)
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "color": "rgb(92, 103, 115)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "color": "rgb(59, 145, 226)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "color": "rgb(198, 126, 45)",
+})
+
+// The first code block has two lines so let's check its `<pre>` elements lists both of them.
+assert-text: ("pre.example-line-numbers", "1\n2")
+
+// Now, try changing the setting dynamically. We'll turn it off, using the settings menu,
+// and make sure it goes away.
+
+// First, open the settings menu.
+click: "#settings-menu"
+wait-for: "#settings"
+assert-css: ("#settings", {"display": "block"})
+
+// Then, click the toggle button.
+click: "input#line-numbers"
+wait-for: 100 // wait-for-false does not exist
+assert-false: "pre.example-line-numbers"
+assert-local-storage: {"rustdoc-line-numbers": "false" }
+
+// Finally, turn it on again.
+click: "input#line-numbers"
+wait-for: "pre.example-line-numbers"
+assert-local-storage: {"rustdoc-line-numbers": "true" }
diff --git a/tests/rustdoc-gui/docblock-details.goml b/tests/rustdoc-gui/docblock-details.goml
new file mode 100644
index 000000000..9ae571efb
--- /dev/null
+++ b/tests/rustdoc-gui/docblock-details.goml
@@ -0,0 +1,34 @@
+// This ensures that the `<details>`/`<summary>` elements are displayed as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/details/struct.Details.html"
+show-text: true
+local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"}
+reload:
+
+// We first check that the headers in the `.top-doc` doc block still have their
+// bottom border.
+assert-text: (".top-doc .docblock > h3", "Hello")
+assert-css: (
+ ".top-doc .docblock > h3",
+ {"border-bottom": "1px solid rgb(210, 210, 210)"},
+)
+// We now check that the `<summary>` doesn't have a bottom border and has the correct display.
+assert-css: (
+ ".top-doc .docblock summary h4",
+ {"border-bottom-width": "0px"},
+)
+// This allows to ensure that summary is on one line only!
+assert-property: (".top-doc .docblock summary h4", {"offsetHeight": "33"})
+assert-css: (".top-doc .docblock summary h4", {"margin-top": "15px", "margin-bottom": "5px"})
+// So `33 + 15 + 5` == `53`
+assert-property: (".top-doc .docblock summary", {"offsetHeight": "53"})
+
+// We now check the `<summary>` on a method.
+assert-css: (
+ ".method-toggle .docblock summary h4",
+ {"border-bottom-width": "0px"},
+)
+// This allows to ensure that summary is on one line only!
+assert-property: (".method-toggle .docblock summary h4", {"offsetHeight": "30"})
+assert-css: (".method-toggle .docblock summary h4", {"margin-top": "15px", "margin-bottom": "5px"})
+// So `30 + 15 + 5` == `50`
+assert-property: (".method-toggle .docblock summary", {"offsetHeight": "50"})
diff --git a/tests/rustdoc-gui/docblock-table-overflow.goml b/tests/rustdoc-gui/docblock-table-overflow.goml
new file mode 100644
index 000000000..d8670089a
--- /dev/null
+++ b/tests/rustdoc-gui/docblock-table-overflow.goml
@@ -0,0 +1,21 @@
+// This test ensures that the type declaration content overflow is handled inside the <pre> directly.
+goto: "file://" + |DOC_PATH| + "/lib2/long_table/struct.Foo.html"
+// We set a fixed size so there is no chance of "random" resize.
+size: (1100, 800)
+// Logically, the ".docblock" and the "<p>" should have the same scroll width.
+compare-elements-property: (".top-doc .docblock", ".top-doc .docblock > p", ["scrollWidth"])
+assert-property: (".top-doc .docblock", {"scrollWidth": "816"})
+// However, since there is overflow in the <table>, its scroll width is bigger.
+assert-property: (".top-doc .docblock table", {"scrollWidth": "1572"})
+
+// Checking it works on other doc blocks as well...
+
+// Logically, the ".docblock" and the "<p>" should have the same scroll width.
+compare-elements-property: (
+ "#implementations-list > details .docblock",
+ "#implementations-list > details .docblock > p",
+ ["scrollWidth"],
+)
+assert-property: ("#implementations-list > details .docblock", {"scrollWidth": "816"})
+// However, since there is overflow in the <table>, its scroll width is bigger.
+assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1572"})
diff --git a/tests/rustdoc-gui/docblock-table.goml b/tests/rustdoc-gui/docblock-table.goml
new file mode 100644
index 000000000..3dcb8abd4
--- /dev/null
+++ b/tests/rustdoc-gui/docblock-table.goml
@@ -0,0 +1,52 @@
+// This test checks the appearance of the tables in the doc comments.
+goto: "file://" + |DOC_PATH| + "/test_docs/doc_block_table/struct.DocBlockTable.html#method.func"
+
+compare-elements-css: (".impl-items .docblock table th", ".top-doc .docblock table th", ["border"])
+compare-elements-css: (".impl-items .docblock table td", ".top-doc .docblock table td", ["border"])
+
+define-function: (
+ "check-colors",
+ (theme, border_color, zebra_stripe_color),
+ block {
+ local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}
+ reload:
+ assert-css: (".top-doc .docblock table tbody tr:nth-child(1)", {
+ "background-color": "rgba(0, 0, 0, 0)",
+ })
+ assert-css: (".top-doc .docblock table tbody tr:nth-child(2)", {
+ "background-color": |zebra_stripe_color|,
+ })
+ assert-css: (".top-doc .docblock table tbody tr:nth-child(3)", {
+ "background-color": "rgba(0, 0, 0, 0)",
+ })
+ assert-css: (".top-doc .docblock table tbody tr:nth-child(4)", {
+ "background-color": |zebra_stripe_color|,
+ })
+ assert-css: (".top-doc .docblock table td", {
+ "border-style": "solid",
+ "border-width": "1px",
+ "border-color": |border_color|,
+ })
+ assert-css: (".top-doc .docblock table th", {
+ "border-style": "solid",
+ "border-width": "1px",
+ "border-color": |border_color|,
+ })
+ }
+)
+
+call-function: ("check-colors", {
+ "theme": "dark",
+ "border_color": "rgb(224, 224, 224)",
+ "zebra_stripe_color": "rgb(42, 42, 42)",
+})
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "border_color": "rgb(92, 103, 115)",
+ "zebra_stripe_color": "rgb(25, 31, 38)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "border_color": "rgb(224, 224, 224)",
+ "zebra_stripe_color": "rgb(245, 245, 245)",
+})
diff --git a/tests/rustdoc-gui/duplicate-macro-reexport.goml b/tests/rustdoc-gui/duplicate-macro-reexport.goml
new file mode 100644
index 000000000..496203c12
--- /dev/null
+++ b/tests/rustdoc-gui/duplicate-macro-reexport.goml
@@ -0,0 +1,14 @@
+// This test ensures that there is no macro duplicates in the sidebar.
+goto: "file://" + |DOC_PATH| + "/test_docs/macro.a.html"
+// Waiting for the elements in the sidebar to be rendered.
+wait-for: ".sidebar-elems .macro"
+// Check there is only one macro named "a" listed in the sidebar.
+assert-count: (
+ "//*[@class='sidebar-elems']//*[@class='block macro']//li/a[text()='a']",
+ 1,
+)
+// Check there is only one macro named "b" listed in the sidebar.
+assert-count: (
+ "//*[@class='sidebar-elems']//*[@class='block macro']//li/a[text()='b']",
+ 1,
+)
diff --git a/tests/rustdoc-gui/enum-variants.goml b/tests/rustdoc-gui/enum-variants.goml
new file mode 100644
index 000000000..8dfc49285
--- /dev/null
+++ b/tests/rustdoc-gui/enum-variants.goml
@@ -0,0 +1,10 @@
+// Verifies that there is non-zero margin on variants and their docblocks.
+goto: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html"
+
+assert-css: (".variants > .variant", {"margin": "0px 0px 12px"})
+assert-css: (".variants > .docblock", {"margin": "0px 0px 32px 24px"})
+
+assert-css: (
+ "details.non-exhaustive > summary",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
diff --git a/tests/rustdoc-gui/escape-key.goml b/tests/rustdoc-gui/escape-key.goml
new file mode 100644
index 000000000..5d80d2496
--- /dev/null
+++ b/tests/rustdoc-gui/escape-key.goml
@@ -0,0 +1,35 @@
+// This test ensures that the "Escape" shortcut is handled correctly based on the
+// current content displayed.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// First, we check that the search results are hidden when the Escape key is pressed.
+write: (".search-input", "test")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+wait-for: "#search h1" // The search element is empty before the first search
+// Check that the currently displayed element is search.
+wait-for: "#alternative-display #search"
+assert-attribute: ("#main-content", {"class": "content hidden"})
+assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
+press-key: "Escape"
+// Checks that search is no longer in the displayed content.
+wait-for: "#not-displayed #search"
+assert-false: "#alternative-display #search"
+assert-attribute: ("#main-content", {"class": "content"})
+assert-document-property: ({"URL": "index.html"}, [ENDS_WITH])
+
+// Check that focusing the search input brings back the search results
+focus: ".search-input"
+wait-for: "#alternative-display #search"
+assert-attribute: ("#main-content", {"class": "content hidden"})
+assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
+
+// Check that Escape hides the search results when a search result is focused.
+focus: ".search-input"
+assert: ".search-input:focus"
+press-key: "ArrowDown"
+assert-false: ".search-input:focus"
+assert: "#results a:focus"
+press-key: "Escape"
+wait-for: "#not-displayed #search"
+assert-false: "#alternative-display #search"
+assert-attribute: ("#main-content", {"class": "content"})
diff --git a/tests/rustdoc-gui/font-weight.goml b/tests/rustdoc-gui/font-weight.goml
new file mode 100644
index 000000000..fafb15631
--- /dev/null
+++ b/tests/rustdoc-gui/font-weight.goml
@@ -0,0 +1,44 @@
+// This test checks that the font weight is correctly applied.
+goto: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html"
+assert-css: ("//*[@class='item-decl']//a[text()='Alias']", {"font-weight": "400"})
+assert-css: (
+ "//*[@class='structfield small-section-header']//a[text()='Alias']",
+ {"font-weight": "400"},
+)
+assert-css: ("#method\.a_method > .code-header", {"font-weight": "600"})
+assert-css: ("#associatedtype\.X > .code-header", {"font-weight": "600"})
+assert-css: ("#associatedconstant\.Y > .code-header", {"font-weight": "600"})
+
+goto: "file://" + |DOC_PATH| + "/test_docs/type.SomeType.html"
+assert-css: (".top-doc .docblock p", {"font-weight": "400"}, ALL)
+
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+assert-css: (".impl-items .method > .code-header", {"font-weight": "600"}, ALL)
+
+goto: "file://" + |DOC_PATH| + "/lib2/trait.Trait.html"
+
+// This is a complex selector, so here's how it works:
+//
+// * //*[@class='item-decl'] — selects element of any tag with classes docblock and item-decl
+// * /pre[@class='rust'] — selects immediate child with tag pre and class rust
+// * /code — selects immediate child with tag code
+// * /a[@class='constant'] — selects immediate child with tag a and class constant
+// * //text() — selects child that is text node
+// * /parent::* — selects immediate parent of the text node (the * means it can be any tag)
+//
+// This uses '/parent::*' as a proxy for the style of the text node.
+// We can't just select the '<a>' because intermediate tags could be added.
+assert-count: (
+ "//*[@class='item-decl']/pre[@class='rust']/code/a[@class='constant']//text()/parent::*",
+ 1,
+)
+assert-css: (
+ "//*[@class='item-decl']/pre[@class='rust']/code/a[@class='constant']//text()/parent::*",
+ {"font-weight": "400"},
+)
+
+assert-count: (".methods .associatedtype", 1)
+assert-css: (".methods .associatedtype", {"font-weight": "600"})
+assert-count: (".methods .constant", 1)
+assert-css: (".methods .constant", {"font-weight": "600"})
+assert-css: (".methods .method > .code-header", {"font-weight": "600"})
diff --git a/tests/rustdoc-gui/hash-item-expansion.goml b/tests/rustdoc-gui/hash-item-expansion.goml
new file mode 100644
index 000000000..3cf94f624
--- /dev/null
+++ b/tests/rustdoc-gui/hash-item-expansion.goml
@@ -0,0 +1,11 @@
+// This test ensures that the element corresponding to the hash is displayed.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html#method.borrow"
+// In the blanket implementations list, "Borrow" is the second one, hence the ":nth(2)".
+assert-attribute: ("#blanket-implementations-list > details:nth-child(2)", {"open": ""})
+// We first check that the impl block is open by default.
+assert-attribute: ("#implementations-list details", {"open": ""})
+// To ensure that we will click on the currently hidden method.
+assert-text: (".sidebar-elems section .block li > a", "must_use")
+click: ".sidebar-elems section .block li > a"
+// We check that the impl block was opened as expected so that we can see the method.
+assert-attribute: ("#implementations-list > details", {"open": ""})
diff --git a/tests/rustdoc-gui/headers-color.goml b/tests/rustdoc-gui/headers-color.goml
new file mode 100644
index 000000000..92cf050a5
--- /dev/null
+++ b/tests/rustdoc-gui/headers-color.goml
@@ -0,0 +1,70 @@
+// This test check for headers text and background colors for the different themes.
+
+define-function: (
+ "check-colors",
+ (theme, color, code_header_color, focus_background_color, headings_color),
+ block {
+ goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+ // This is needed so that the text color is computed.
+ show-text: true
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: (
+ ".impl",
+ {"color": |color|, "background-color": "rgba(0, 0, 0, 0)"},
+ ALL,
+ )
+ assert-css: (
+ ".impl .code-header",
+ {"color": |code_header_color|, "background-color": "rgba(0, 0, 0, 0)"},
+ ALL,
+ )
+ goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html#impl-Foo"
+ assert-css: (
+ "#impl-Foo",
+ {"color": |color|, "background-color": |focus_background_color|},
+ )
+ goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html#method.must_use"
+ assert-css: (
+ "#method\.must_use",
+ {"color": |color|, "background-color": |focus_background_color|},
+ ALL,
+ )
+ goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+ assert-css: (".small-section-header a", {"color": |color|}, ALL)
+ goto: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
+ // We select headings (h2, h3, h...).
+ assert-css: (".docblock > :not(p) > a", {"color": |headings_color|}, ALL)
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "color": "rgb(197, 197, 197)",
+ "code_header_color": "rgb(230, 225, 207)",
+ "focus_background_color": "rgba(255, 236, 164, 0.06)",
+ "headings_color": "rgb(57, 175, 215)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "color": "rgb(221, 221, 221)",
+ "code_header_color": "rgb(221, 221, 221)",
+ "focus_background_color": "rgb(73, 74, 61)",
+ "headings_color": "rgb(210, 153, 29)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "color": "rgb(0, 0, 0)",
+ "code_header_color": "rgb(0, 0, 0)",
+ "focus_background_color": "rgb(253, 255, 211)",
+ "headings_color": "rgb(56, 115, 173)",
+ },
+)
diff --git a/tests/rustdoc-gui/headings.goml b/tests/rustdoc-gui/headings.goml
new file mode 100644
index 000000000..e4ba5f124
--- /dev/null
+++ b/tests/rustdoc-gui/headings.goml
@@ -0,0 +1,234 @@
+// This test checks that headers (a) have the correct heading level, (b) are the right size,
+// and (c) have the correct underlining (or absence of underlining).
+// The sizes may change as design changes, but try to make sure a lower header is never bigger than
+// its parent headers. Also make sure lower headers don't have underlines when their parents lack
+// an underline.
+// Most of these sizes are set in CSS in `em` units, so here's a conversion chart based on our
+// default 16px font size:
+// 24px 1.5em
+// 22px 1.375rem
+// 20px 1.25rem
+// 18px 1.125em
+// 16px 1rem
+// 14px 0.875rem
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
+
+assert-css: (".main-heading h1", {"font-size": "24px"})
+
+assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
+assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
+assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "18px"})
+assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"})
+
+assert-css: ("h2#fields", {"font-size": "22px"})
+assert-css: ("h2#fields", {"border-bottom-width": "1px"})
+assert-css: ("h3#title-for-field", {"font-size": "20px"})
+assert-css: ("h3#title-for-field", {"border-bottom-width": "0px"})
+assert-css: ("h4#sub-heading-for-field", {"font-size": "16px"})
+assert-css: ("h4#sub-heading-for-field", {"border-bottom-width": "0px"})
+
+assert-css: ("h2#implementations", {"font-size": "22px"})
+assert-css: ("h2#implementations", {"border-bottom-width": "1px"})
+
+assert-css: ("#impl-HeavilyDocumentedStruct > h3.code-header", {"font-size": "18px"})
+assert-css: ("#impl-HeavilyDocumentedStruct > h3.code-header", {"border-bottom-width": "0px"})
+assert-css: ("#method\.do_nothing > h4.code-header", {"font-size": "16px"})
+assert-css: ("#method\.do_nothing > h4.code-header", {"border-bottom-width": "0px"})
+
+assert-css: ("h4#title-for-struct-impl-doc", {"font-size": "16px"})
+assert-css: ("h4#title-for-struct-impl-doc", {"border-bottom-width": "0px"})
+assert-css: ("h5#sub-heading-for-struct-impl-doc", {"font-size": "16px"})
+assert-css: ("h5#sub-heading-for-struct-impl-doc", {"border-bottom-width": "0px"})
+assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"font-size": "14px"})
+assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"border-bottom-width": "0px"})
+
+assert-css: ("h5#title-for-struct-impl-item-doc", {"font-size": "16px"})
+assert-css: ("h5#title-for-struct-impl-item-doc", {"border-bottom-width": "0px"})
+assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"font-size": "14px"})
+assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"border-bottom-width": "0px"})
+assert-css: ("h6#sub-sub-heading-for-struct-impl-item-doc", {"font-size": "14px"})
+
+goto: "file://" + |DOC_PATH| + "/test_docs/enum.HeavilyDocumentedEnum.html"
+
+assert-css: (".main-heading h1", {"font-size": "24px"})
+
+assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
+assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
+assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "18px"})
+assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"})
+
+assert-css: ("h2#variants", {"font-size": "22px"})
+assert-css: ("h2#variants", {"border-bottom-width": "1px"})
+
+assert-css: ("h4#none-prose-title", {"font-size": "16px"})
+assert-css: ("h4#none-prose-title", {"border-bottom-width": "0px"})
+assert-css: ("h5#none-prose-sub-heading", {"font-size": "16px"})
+assert-css: ("h5#none-prose-sub-heading", {"border-bottom-width": "0px"})
+
+assert-css: ("h4#wrapped-prose-title", {"font-size": "16px"})
+assert-css: ("h4#wrapped-prose-title", {"border-bottom-width": "0px"})
+assert-css: ("h5#wrapped-prose-sub-heading", {"font-size": "16px"})
+assert-css: ("h5#wrapped-prose-sub-heading", {"border-bottom-width": "0px"})
+
+assert-css: ("h5#wrapped0-prose-title", {"font-size": "16px"})
+assert-css: ("h5#wrapped0-prose-title", {"border-bottom-width": "0px"})
+assert-css: ("h6#wrapped0-prose-sub-heading", {"font-size": "14px"})
+assert-css: ("h6#wrapped0-prose-sub-heading", {"border-bottom-width": "0px"})
+
+assert-css: ("h5#structy-prose-title", {"font-size": "16px"})
+assert-css: ("h5#structy-prose-title", {"border-bottom-width": "0px"})
+assert-css: ("h6#structy-prose-sub-heading", {"font-size": "14px"})
+assert-css: ("h6#structy-prose-sub-heading", {"border-bottom-width": "0px"})
+
+assert-css: ("h2#implementations", {"font-size": "22px"})
+assert-css: ("h2#implementations", {"border-bottom-width": "1px"})
+
+assert-css: ("#impl-HeavilyDocumentedEnum > h3.code-header", {"font-size": "18px"})
+assert-css: ("#impl-HeavilyDocumentedEnum > h3.code-header", {"border-bottom-width": "0px"})
+assert-css: ("#method\.do_nothing > h4.code-header", {"font-size": "16px"})
+assert-css: ("#method\.do_nothing > h4.code-header", {"border-bottom-width": "0px"})
+
+assert-css: ("h4#title-for-enum-impl-doc", {"font-size": "16px"})
+assert-css: ("h4#title-for-enum-impl-doc", {"border-bottom-width": "0px"})
+assert-css: ("h5#sub-heading-for-enum-impl-doc", {"font-size": "16px"})
+assert-css: ("h5#sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"})
+assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"font-size": "14px"})
+assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"})
+
+assert-css: ("h5#title-for-enum-impl-item-doc", {"font-size": "16px"})
+assert-css: ("h5#title-for-enum-impl-item-doc", {"border-bottom-width": "0px"})
+assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"font-size": "14px"})
+assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"})
+assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"font-size": "14px"})
+assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"})
+
+assert-text: ("//ul[@class='block mod']/preceding-sibling::h3", "Modules")
+assert-css: ("//ul[@class='block mod']/preceding-sibling::h3", {"border-bottom-width": "0px"}, ALL)
+
+goto: "file://" + |DOC_PATH| + "/test_docs/union.HeavilyDocumentedUnion.html"
+
+assert-css: (".main-heading h1", {"font-size": "24px"})
+
+assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
+assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
+
+assert-css: ("h2#fields", {"font-size": "22px"})
+assert-css: ("h2#fields", {"border-bottom-width": "1px"})
+
+assert-css: ("h3#title-for-union-variant", {"font-size": "20px"})
+assert-css: ("h3#title-for-union-variant", {"border-bottom-width": "0px"})
+assert-css: ("h4#sub-heading-for-union-variant", {"font-size": "16px"})
+assert-css: ("h4#sub-heading-for-union-variant", {"border-bottom-width": "0px"})
+
+assert-css: ("h2#implementations", {"font-size": "22px"})
+assert-css: ("h2#implementations", {"border-bottom-width": "1px"})
+
+assert-css: ("#impl-HeavilyDocumentedUnion > h3.code-header", {"font-size": "18px"})
+assert-css: ("#impl-HeavilyDocumentedUnion > h3.code-header", {"border-bottom-width": "0px"})
+assert-css: ("h4#title-for-union-impl-doc", {"font-size": "16px"})
+assert-css: ("h4#title-for-union-impl-doc", {"border-bottom-width": "0px"})
+assert-css: ("h5#sub-heading-for-union-impl-doc", {"font-size": "16px"})
+assert-css: ("h5#sub-heading-for-union-impl-doc", {"border-bottom-width": "0px"})
+
+assert-css: ("h5#title-for-union-impl-item-doc", {"font-size": "16px"})
+assert-css: ("h5#title-for-union-impl-item-doc", {"border-bottom-width": "0px"})
+assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"font-size": "14px"})
+assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"border-bottom-width": "0px"})
+
+goto: "file://" + |DOC_PATH| + "/test_docs/macro.heavily_documented_macro.html"
+
+assert-css: (".main-heading h1", {"font-size": "24px"})
+
+assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
+assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
+
+// Needed to check colors
+show-text: true
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
+
+define-function: (
+ "check-colors",
+ (theme, heading_color, small_heading_color, heading_border_color),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: (
+ ".top-doc .docblock h2",
+ {"color": |heading_color|, "border-bottom": "1px solid " + |heading_border_color|},
+ )
+ assert-css: (
+ ".top-doc .docblock h3",
+ {"color": |heading_color|, "border-bottom": "1px solid " + |heading_border_color|},
+ )
+ assert-css: (
+ ".top-doc .docblock h4",
+ {"color": |heading_color|, "border-bottom": "1px solid " + |heading_border_color|},
+ )
+ assert-css: (
+ ".top-doc .docblock h5",
+ {"color": |small_heading_color|, "border-bottom-width": "0px"},
+ )
+ assert-css: (
+ "#implementations-list .docblock h4",
+ {"color": |heading_color|, "border-bottom-width": "0px"},
+ )
+ assert-css: (
+ "#implementations-list .docblock h5",
+ {"color": |small_heading_color|, "border-bottom-width": "0px"},
+ )
+ assert-css: (
+ "#implementations-list .docblock h6",
+ {"color": |small_heading_color|, "border-bottom-width": "0px"},
+ )
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "heading_color": "rgb(255, 255, 255)",
+ "small_heading_color": "rgb(197, 197, 197)",
+ "heading_border_color": "rgb(92, 103, 115)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "heading_color": "rgb(221, 221, 221)",
+ "small_heading_color": "rgb(221, 221, 221)",
+ "heading_border_color": "rgb(210, 210, 210)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "heading_color": "rgb(0, 0, 0)",
+ "small_heading_color": "rgb(0, 0, 0)",
+ "heading_border_color": "rgb(221, 221, 221)",
+ },
+)
+
+define-function: (
+ "check-since-color",
+ (theme),
+ block {
+ local-storage: {"rustdoc-theme": |theme|}
+ reload:
+ assert-css: (".since", {"color": "rgb(128, 128, 128)"}, ALL)
+ },
+)
+
+goto: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
+call-function: ("check-since-color", ("ayu"))
+call-function: ("check-since-color", ("dark"))
+call-function: ("check-since-color", ("light"))
diff --git a/tests/rustdoc-gui/help-page.goml b/tests/rustdoc-gui/help-page.goml
new file mode 100644
index 000000000..5f4c1ba2f
--- /dev/null
+++ b/tests/rustdoc-gui/help-page.goml
@@ -0,0 +1,72 @@
+// This test ensures that opening the help page in its own tab works.
+goto: "file://" + |DOC_PATH| + "/help.html"
+size: (1000, 1000) // Try desktop size first.
+wait-for: "#help"
+assert-css: ("#help", {"display": "block"})
+assert-css: ("#help dd", {"font-size": "16px"})
+click: "#help-button > a"
+assert-css: ("#help", {"display": "block"})
+compare-elements-property: (".sub", "#help", ["offsetWidth"])
+compare-elements-position: (".sub", "#help", ("x"))
+size: (500, 1000) // Try mobile next.
+assert-css: ("#help", {"display": "block"})
+compare-elements-property: (".sub", "#help", ["offsetWidth"])
+compare-elements-position: (".sub", "#help", ("x"))
+
+// Checking the color of the elements of the help menu.
+show-text: true
+define-function: (
+ "check-colors",
+ (theme, color, background, box_shadow),
+ block {
+ // Setting the theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ // We reload the page so the local storage settings are being used.
+ reload:
+ assert-css: ("#help kbd", {
+ "color": |color|,
+ "background-color": |background|,
+ "box-shadow": |box_shadow| + " 0px -1px 0px 0px inset",
+ }, ALL)
+ },
+)
+
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "color": "rgb(197, 197, 197)",
+ "background": "rgb(49, 69, 89)",
+ "box_shadow": "rgb(92, 103, 115)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "color": "rgb(0, 0, 0)",
+ "background": "rgb(250, 251, 252)",
+ "box_shadow": "rgb(198, 203, 209)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "color": "rgb(0, 0, 0)",
+ "background": "rgb(250, 251, 252)",
+ "box_shadow": "rgb(198, 203, 209)",
+})
+
+// This test ensures that opening the help popover without switching pages works.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+size: (1000, 1000) // Only supported on desktop.
+assert-false: "#help"
+click: "#help-button > a"
+assert-css: ("#help", {"display": "block"})
+assert-css: ("#help dd", {"font-size": "16px"})
+click: "#help-button > a"
+assert-css: ("#help", {"display": "none"})
+compare-elements-property-false: (".sub", "#help", ["offsetWidth"])
+compare-elements-position-false: (".sub", "#help", ("x"))
+
+// This test ensures that the "the rustdoc book" anchor link within the help popover works.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+size: (1000, 1000) // Popover only appears when the screen width is >700px.
+assert-false: "#help"
+click: "#help-button > a"
+click: ".popover a[href='https://doc.rust-lang.org/rustdoc/']"
+wait-for: 2000
+assert-document-property: {"URL": "https://doc.rust-lang.org/rustdoc/"}
diff --git a/tests/rustdoc-gui/highlight-colors.goml b/tests/rustdoc-gui/highlight-colors.goml
new file mode 100644
index 000000000..b182150a5
--- /dev/null
+++ b/tests/rustdoc-gui/highlight-colors.goml
@@ -0,0 +1,94 @@
+// This test checks the highlight colors in the source code pages.
+goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+show-text: true
+
+define-function: (
+ "check-colors",
+ (
+ theme,
+ kw,
+ kw2,
+ prelude_ty,
+ prelude_val,
+ lifetime,
+ number,
+ string,
+ bool_val,
+ self,
+ attr,
+ macro,
+ question_mark,
+ comment,
+ doc_comment,
+ ),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: ("pre.rust .kw", {"color": |kw|}, ALL)
+ assert-css: ("pre.rust .kw-2", {"color": |kw2|}, ALL)
+ assert-css: ("pre.rust .prelude-ty", {"color": |prelude_ty|}, ALL)
+ assert-css: ("pre.rust .prelude-val", {"color": |prelude_val|}, ALL)
+ assert-css: ("pre.rust .lifetime", {"color": |lifetime|}, ALL)
+ assert-css: ("pre.rust .number", {"color": |number|}, ALL)
+ assert-css: ("pre.rust .string", {"color": |string|}, ALL)
+ assert-css: ("pre.rust .bool-val", {"color": |bool_val|}, ALL)
+ assert-css: ("pre.rust .self", {"color": |self|}, ALL)
+ assert-css: ("pre.rust .attr", {"color": |attr|}, ALL)
+ assert-css: ("pre.rust .macro", {"color": |macro|}, ALL)
+ assert-css: ("pre.rust .question-mark", {"color": |question_mark|}, ALL)
+ assert-css: ("pre.rust .comment", {"color": |comment|}, ALL)
+ assert-css: ("pre.rust .doccomment", {"color": |doc_comment|}, ALL)
+ },
+)
+
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "kw": "rgb(255, 119, 51)",
+ "kw2": "rgb(255, 119, 51)",
+ "prelude_ty": "rgb(105, 242, 223)",
+ "prelude_val": "rgb(255, 119, 51)",
+ "lifetime": "rgb(255, 119, 51)",
+ "number": "rgb(184, 204, 82)",
+ "string": "rgb(184, 204, 82)",
+ "bool_val": "rgb(255, 119, 51)",
+ "self": "rgb(54, 163, 217)",
+ "attr": "rgb(230, 225, 207)",
+ "macro": "rgb(163, 122, 204)",
+ "question_mark": "rgb(255, 144, 17)",
+ "comment": "rgb(120, 135, 151)",
+ "doc_comment": "rgb(161, 172, 136)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "kw": "rgb(171, 138, 193)",
+ "kw2": "rgb(118, 154, 203)",
+ "prelude_ty": "rgb(118, 154, 203)",
+ "prelude_val": "rgb(238, 104, 104)",
+ "lifetime": "rgb(217, 127, 38)",
+ "number": "rgb(131, 163, 0)",
+ "string": "rgb(131, 163, 0)",
+ "bool_val": "rgb(238, 104, 104)",
+ "self": "rgb(238, 104, 104)",
+ "attr": "rgb(238, 104, 104)",
+ "macro": "rgb(62, 153, 159)",
+ "question_mark": "rgb(255, 144, 17)",
+ "comment": "rgb(141, 141, 139)",
+ "doc_comment": "rgb(140, 163, 117)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "kw": "rgb(137, 89, 168)",
+ "kw2": "rgb(66, 113, 174)",
+ "prelude_ty": "rgb(66, 113, 174)",
+ "prelude_val": "rgb(200, 40, 41)",
+ "lifetime": "rgb(183, 101, 20)",
+ "number": "rgb(113, 140, 0)",
+ "string": "rgb(113, 140, 0)",
+ "bool_val": "rgb(200, 40, 41)",
+ "self": "rgb(200, 40, 41)",
+ "attr": "rgb(200, 40, 41)",
+ "macro": "rgb(62, 153, 159)",
+ "question_mark": "rgb(255, 144, 17)",
+ "comment": "rgb(142, 144, 140)",
+ "doc_comment": "rgb(77, 77, 76)",
+})
diff --git a/tests/rustdoc-gui/huge-collection-of-constants.goml b/tests/rustdoc-gui/huge-collection-of-constants.goml
new file mode 100644
index 000000000..3ccd33f1c
--- /dev/null
+++ b/tests/rustdoc-gui/huge-collection-of-constants.goml
@@ -0,0 +1,9 @@
+// Make sure that the last two entries are more than 12 pixels apart and not stacked on each other.
+
+goto: "file://" + |DOC_PATH| + "/test_docs/huge_amount_of_consts/index.html"
+
+compare-elements-position-near-false: (
+ "//*[@class='item-table']//div[last()-1]",
+ "//*[@class='item-table']//div[last()-3]",
+ {"y": 12},
+)
diff --git a/tests/rustdoc-gui/huge-logo.goml b/tests/rustdoc-gui/huge-logo.goml
new file mode 100644
index 000000000..01f06771c
--- /dev/null
+++ b/tests/rustdoc-gui/huge-logo.goml
@@ -0,0 +1,21 @@
+// huge_logo crate has a custom 712x860 logo
+// test to ensure the maximum size in the layout works correctly
+goto: "file://" + |DOC_PATH| + "/huge_logo/index.html"
+
+size: (1280, 1024)
+// offsetWidth = width of sidebar
+assert-property: (".sidebar .logo-container", {"offsetWidth": "200", "offsetHeight": 100})
+assert-property: (".sidebar .logo-container img", {"offsetWidth": "100", "offsetHeight": 100})
+
+size: (400, 600)
+// offset = size + margin
+assert-property: (".mobile-topbar .logo-container", {"offsetWidth": "55", "offsetHeight": 45})
+assert-property: (".mobile-topbar .logo-container img", {"offsetWidth": "35", "offsetHeight": 35})
+
+goto: "file://" + |DOC_PATH| + "/src/huge_logo/lib.rs.html"
+
+size: (1280, 1024)
+assert-property: (".sub-logo-container", {"offsetWidth": "60", "offsetHeight": 60})
+
+size: (400, 600)
+assert-property: (".sub-logo-container", {"offsetWidth": "35", "offsetHeight": 35})
diff --git a/tests/rustdoc-gui/impl-default-expansion.goml b/tests/rustdoc-gui/impl-default-expansion.goml
new file mode 100644
index 000000000..c3f9240cc
--- /dev/null
+++ b/tests/rustdoc-gui/impl-default-expansion.goml
@@ -0,0 +1,3 @@
+// This test ensures that the impl blocks are open by default.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+assert-attribute: ("#implementations-list details.implementors-toggle", {"open": ""})
diff --git a/tests/rustdoc-gui/impl-doc.goml b/tests/rustdoc-gui/impl-doc.goml
new file mode 100644
index 000000000..6caffb9c3
--- /dev/null
+++ b/tests/rustdoc-gui/impl-doc.goml
@@ -0,0 +1,9 @@
+// A docblock on an impl must have a margin to separate it from the contents.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.TypeWithImplDoc.html"
+
+// The text is about 24px tall, so if there's a margin, then their position will be >24px apart
+compare-elements-position-near-false: (
+ "#implementations-list > .implementors-toggle > .docblock > p",
+ "#implementations-list > .implementors-toggle > .impl-items",
+ {"y": 24}
+)
diff --git a/tests/rustdoc-gui/implementors.goml b/tests/rustdoc-gui/implementors.goml
new file mode 100644
index 000000000..997c0ed8f
--- /dev/null
+++ b/tests/rustdoc-gui/implementors.goml
@@ -0,0 +1,41 @@
+// The goal of this test is to check that the external trait implementors, generated with JS,
+// have the same display than the "local" ones.
+goto: "file://" + |DOC_PATH| + "/implementors/trait.Whatever.html"
+assert: "#implementors-list"
+// There are supposed to be two implementors listed.
+assert-count: ("#implementors-list .impl", 2)
+// Now we check that both implementors have an anchor, an ID and a similar DOM.
+assert: ("#implementors-list .impl:nth-child(1) > a.anchor")
+assert-attribute: ("#implementors-list .impl:nth-child(1)", {"id": "impl-Whatever-for-Struct"})
+assert-attribute: ("#implementors-list .impl:nth-child(1) > a.anchor", {"href": "#impl-Whatever-for-Struct"})
+assert: "#implementors-list .impl:nth-child(1) > .code-header"
+
+assert: ("#implementors-list .impl:nth-child(2) > a.anchor")
+assert-attribute: ("#implementors-list .impl:nth-child(2)", {"id": "impl-Whatever-1"})
+assert-attribute: ("#implementors-list .impl:nth-child(2) > a.anchor", {"href": "#impl-Whatever-1"})
+assert: "#implementors-list .impl:nth-child(2) > .code-header"
+
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.HasEmptyTraits.html"
+compare-elements-position-near-false: (
+ "#impl-EmptyTrait1-for-HasEmptyTraits",
+ "#impl-EmptyTrait2-for-HasEmptyTraits",
+ {"y": 34},
+)
+compare-elements-position-near: (
+ "#impl-EmptyTrait3-for-HasEmptyTraits h3",
+ "#impl-EmptyTrait3-for-HasEmptyTraits .item-info",
+ {"y": 34},
+)
+
+// Now check that re-exports work correctly.
+// There should be exactly one impl shown on both of these pages.
+goto: "file://" + |DOC_PATH| + "/lib2/trait.TraitToReexport.html"
+assert-count: ("#implementors-list .impl", 1)
+goto: "file://" + |DOC_PATH| + "/implementors/trait.TraitToReexport.html"
+assert-count: ("#implementors-list .impl", 1)
+
+// Now check that the link is properly rewritten for a crate called `http`.
+// An older version of rustdoc had a buggy check for absolute links.
+goto: "file://" + |DOC_PATH| + "/http/trait.HttpTrait.html"
+assert-count: ("#implementors-list .impl", 1)
+assert-attribute: ("#implementors-list .impl a.trait", {"href": "../http/trait.HttpTrait.html"})
diff --git a/tests/rustdoc-gui/item-decl-colors.goml b/tests/rustdoc-gui/item-decl-colors.goml
new file mode 100644
index 000000000..c58e3eb7c
--- /dev/null
+++ b/tests/rustdoc-gui/item-decl-colors.goml
@@ -0,0 +1,80 @@
+// This test ensures that the color of the items in the type decl are working as expected.
+
+// We need to disable this check because `implementors/test_docs/trait.TraitWithoutGenerics.js`
+// doesn't exist.
+fail-on-request-error: false
+
+define-function: (
+ "check-colors",
+ (
+ theme,
+ attr_color,
+ trait_color,
+ struct_color,
+ enum_color,
+ primitive_color,
+ constant_color,
+ fn_color,
+ assoc_type_color,
+ ),
+ block {
+ goto: "file://" + |DOC_PATH| + "/test_docs/struct.WithGenerics.html"
+ show-text: true
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: (".item-decl .code-attribute", {"color": |attr_color|}, ALL)
+ assert-css: (".item-decl .trait", {"color": |trait_color|}, ALL)
+ // We need to add `code` here because otherwise it would select the parent too.
+ assert-css: (".item-decl code .struct", {"color": |struct_color|}, ALL)
+ assert-css: (".item-decl .enum", {"color": |enum_color|}, ALL)
+ assert-css: (".item-decl .primitive", {"color": |primitive_color|}, ALL)
+
+ goto: "file://" + |DOC_PATH| + "/test_docs/trait.TraitWithoutGenerics.html"
+ assert-css: (".item-decl .constant", {"color": |constant_color|}, ALL)
+ assert-css: (".item-decl .fn", {"color": |fn_color|}, ALL)
+ assert-css: (".item-decl .associatedtype", {"color": |assoc_type_color|}, ALL)
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "attr_color": "rgb(153, 153, 153)",
+ "trait_color": "rgb(57, 175, 215)",
+ "struct_color": "rgb(255, 160, 165)",
+ "enum_color": "rgb(255, 160, 165)",
+ "primitive_color": "rgb(255, 160, 165)",
+ "constant_color": "rgb(57, 175, 215)",
+ "fn_color": "rgb(253, 214, 135)",
+ "assoc_type_color": "rgb(57, 175, 215)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "attr_color": "rgb(153, 153, 153)",
+ "trait_color": "rgb(183, 140, 242)",
+ "struct_color": "rgb(45, 191, 184)",
+ "enum_color": "rgb(45, 191, 184)",
+ "primitive_color": "rgb(45, 191, 184)",
+ "constant_color": "rgb(210, 153, 29)",
+ "fn_color": "rgb(43, 171, 99)",
+ "assoc_type_color": "rgb(210, 153, 29)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "attr_color": "rgb(153, 153, 153)",
+ "trait_color": "rgb(110, 79, 201)",
+ "struct_color": "rgb(173, 55, 138)",
+ "enum_color": "rgb(173, 55, 138)",
+ "primitive_color": "rgb(173, 55, 138)",
+ "constant_color": "rgb(56, 115, 173)",
+ "fn_color": "rgb(173, 124, 55)",
+ "assoc_type_color": "rgb(56, 115, 173)",
+ },
+)
diff --git a/tests/rustdoc-gui/item-info-alignment.goml b/tests/rustdoc-gui/item-info-alignment.goml
new file mode 100644
index 000000000..94b52f005
--- /dev/null
+++ b/tests/rustdoc-gui/item-info-alignment.goml
@@ -0,0 +1,10 @@
+// This test ensures that the "item-info" looks about the same
+// whether or not it's inside a toggle.
+goto: "file://" + |DOC_PATH| + "/lib2/struct.ItemInfoAlignmentTest.html"
+
+// First, we try it in "desktop" mode.
+size: (1200, 870)
+compare-elements-position: (".impl-items > .item-info", "summary > .item-info", ("x"))
+// Next, we try it in "mobile" mode (max-width: 700px).
+size: (650, 650)
+compare-elements-position: (".impl-items > .item-info", "summary > .item-info", ("x"))
diff --git a/tests/rustdoc-gui/item-info-overflow.goml b/tests/rustdoc-gui/item-info-overflow.goml
new file mode 100644
index 000000000..8ea14621c
--- /dev/null
+++ b/tests/rustdoc-gui/item-info-overflow.goml
@@ -0,0 +1,31 @@
+// This test ensures that the "item-info" elements don't overflow.
+goto: "file://" + |DOC_PATH| + "/lib2/struct.LongItemInfo.html"
+// We set a fixed size so there is no chance of "random" resize.
+size: (1200, 870)
+// Logically, the "item-decl" and the "item-info" should have the same scroll width.
+compare-elements-property: (".item-decl", ".item-info", ["scrollWidth"])
+assert-property: (".item-info", {"scrollWidth": "940"})
+// Just to be sure we're comparing the correct "item-info":
+assert-text: (
+ ".item-info",
+ "Available on Android or Linux or Emscripten or DragonFly BSD",
+ STARTS_WITH,
+)
+
+// Checking the "item-info" on an impl block as well:
+goto: "file://" + |DOC_PATH| + "/lib2/struct.LongItemInfo2.html"
+compare-elements-property: (
+ "#impl-SimpleTrait-for-LongItemInfo2 .item-info",
+ "#impl-SimpleTrait-for-LongItemInfo2 + .docblock",
+ ["scrollWidth"],
+)
+assert-property: (
+ "#impl-SimpleTrait-for-LongItemInfo2 .item-info",
+ {"scrollWidth": "916"},
+)
+// Just to be sure we're comparing the correct "item-info":
+assert-text: (
+ "#impl-SimpleTrait-for-LongItemInfo2 .item-info",
+ "Available on Android or Linux or Emscripten or DragonFly BSD",
+ STARTS_WITH,
+)
diff --git a/tests/rustdoc-gui/item-info.goml b/tests/rustdoc-gui/item-info.goml
new file mode 100644
index 000000000..6780dfca6
--- /dev/null
+++ b/tests/rustdoc-gui/item-info.goml
@@ -0,0 +1,32 @@
+// This test ensures a few things for item info elements.
+goto: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html"
+// Ensuring that the item information don't take 100% of the width if unnecessary.
+// We set a fixed size so there is no chance of "random" resize.
+size: (1100, 800)
+// We check that ".item-info" is bigger than its content.
+assert-css: (".item-info", {"width": "840px"})
+assert-css: (".item-info .stab", {"width": "289px"})
+assert-position: (".item-info .stab", {"x": 245})
+
+// Now we ensure that they're not rendered on the same line.
+goto: "file://" + |DOC_PATH| + "/lib2/trait.Trait.html"
+// We first ensure that there are two item info on the trait.
+assert-count: ("#main-content > .item-info .stab", 2)
+// They should not have the same `y` position!
+compare-elements-position-false: (
+ "#main-content > .item-info .stab:nth-of-type(1)",
+ "#main-content > .item-info .stab:nth-of-type(2)",
+ ("y"),
+)
+// But they should have the same `x` position.
+compare-elements-position: (
+ "#main-content > .item-info .stab:nth-of-type(1)",
+ "#main-content > .item-info .stab:nth-of-type(2)",
+ ("x"),
+)
+// They are supposed to have the same height too.
+compare-elements-css: (
+ "#main-content > .item-info .stab:nth-of-type(1)",
+ "#main-content > .item-info .stab:nth-of-type(2)",
+ ["height"],
+)
diff --git a/tests/rustdoc-gui/item-summary-table.goml b/tests/rustdoc-gui/item-summary-table.goml
new file mode 100644
index 000000000..2a92e9da5
--- /dev/null
+++ b/tests/rustdoc-gui/item-summary-table.goml
@@ -0,0 +1,6 @@
+// This test ensures that <table> elements aren't display in items summary.
+goto: "file://" + |DOC_PATH| + "/lib2/summary_table/index.html"
+// We check that we picked the right item first.
+assert-text: (".item-table .item-left", "Foo")
+// Then we check that its summary is empty.
+assert-false: ".item-table .item-right"
diff --git a/tests/rustdoc-gui/javascript-disabled.goml b/tests/rustdoc-gui/javascript-disabled.goml
new file mode 100644
index 000000000..edf179d0d
--- /dev/null
+++ b/tests/rustdoc-gui/javascript-disabled.goml
@@ -0,0 +1,6 @@
+// When JavaScript is disabled, we hide the search bar, because it
+// can't be used without JS.
+javascript: false
+
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+assert-css: (".sub", {"display": "none"})
diff --git a/tests/rustdoc-gui/jump-to-def-background.goml b/tests/rustdoc-gui/jump-to-def-background.goml
new file mode 100644
index 000000000..8ee3ccf4a
--- /dev/null
+++ b/tests/rustdoc-gui/jump-to-def-background.goml
@@ -0,0 +1,22 @@
+// We check the background color on the jump to definition links in the source code page.
+goto: "file://" + |DOC_PATH| + "/src/link_to_definition/lib.rs.html"
+
+define-function: (
+ "check-background-color",
+ (theme, background_color),
+ block {
+ // Set the theme.
+ local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false" }
+ // We reload the page so the local storage settings are being used.
+ reload:
+ assert-css: (
+ "body.source .example-wrap pre.rust a",
+ {"background-color": |background_color|},
+ ALL,
+ )
+ },
+)
+
+call-function: ("check-background-color", ("ayu", "rgb(51, 51, 51)"))
+call-function: ("check-background-color", ("dark", "rgb(51, 51, 51)"))
+call-function: ("check-background-color", ("light", "rgb(238, 238, 238)"))
diff --git a/tests/rustdoc-gui/label-next-to-symbol.goml b/tests/rustdoc-gui/label-next-to-symbol.goml
new file mode 100644
index 000000000..05f8ddc71
--- /dev/null
+++ b/tests/rustdoc-gui/label-next-to-symbol.goml
@@ -0,0 +1,66 @@
+// These tests verify that labels like "UNIX" and "Deprecated" stay on the same line as their symbol.
+// It also verifies the staggered layout on mobile.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+
+// Desktop view
+size: (1080, 600)
+assert: (".stab.deprecated")
+assert: (".stab.portability")
+
+// make sure that deprecated and portability have the right colors
+assert-css: (
+ ".item-table .item-left .stab.deprecated",
+ { "background-color": "rgb(255, 245, 214)" },
+)
+assert-css: (
+ ".item-table .item-left .stab.portability",
+ { "background-color": "rgb(255, 245, 214)" },
+)
+
+// table like view
+assert-css: (".item-right.docblock-short", { "padding-left": "0px" })
+compare-elements-position-near: (
+ "//*[@class='item-left module-item']//a[text()='replaced_function']",
+ ".item-left .stab.deprecated",
+ {"y": 2},
+)
+compare-elements-position: (
+ ".item-left .stab.deprecated",
+ ".item-left .stab.portability",
+ ("y"),
+)
+
+// Ensure no wrap
+compare-elements-position: (
+ "//*[@class='item-left module-item']//a[text()='replaced_function']/..",
+ "//*[@class='item-right docblock-short'][text()='a thing with a label']",
+ ("y"),
+)
+
+
+// Mobile view
+size: (600, 600)
+// staggered layout with 2em spacing
+assert-css: (".item-right.docblock-short", { "padding-left": "32px" })
+compare-elements-position-near: (
+ "//*[@class='item-left module-item']//a[text()='replaced_function']",
+ ".item-left .stab.deprecated",
+ {"y": 2},
+)
+compare-elements-position: (
+ ".item-left .stab.deprecated",
+ ".item-left .stab.portability",
+ ("y"),
+)
+
+// Ensure wrap
+compare-elements-position-false: (
+ "//*[@class='item-left module-item']//a[text()='replaced_function']/..",
+ "//*[@class='item-right docblock-short'][text()='a thing with a label']",
+ ("y"),
+)
+compare-elements-position-false: (
+ ".item-left .stab.deprecated",
+ "//*[@class='item-right docblock-short'][text()='a thing with a label']",
+ ("y"),
+)
diff --git a/tests/rustdoc-gui/links-color.goml b/tests/rustdoc-gui/links-color.goml
new file mode 100644
index 000000000..14f7d9935
--- /dev/null
+++ b/tests/rustdoc-gui/links-color.goml
@@ -0,0 +1,98 @@
+// This test checks links colors.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+
+// This is needed so that the text color is computed.
+show-text: true
+
+define-function: (
+ "check-colors",
+ (theme, mod, macro, struct, enum, trait, fn, type, union, keyword,
+ sidebar, sidebar_current, sidebar_current_background),
+ block {
+ local-storage: {
+ "rustdoc-theme": |theme|,
+ "rustdoc-use-system-theme": "false",
+ }
+ reload:
+ // Checking results colors.
+ assert-css: (".item-table .mod", {"color": |mod|}, ALL)
+ assert-css: (".item-table .macro", {"color": |macro|}, ALL)
+ assert-css: (".item-table .struct", {"color": |struct|}, ALL)
+ assert-css: (".item-table .enum", {"color": |enum|}, ALL)
+ assert-css: (".item-table .trait", {"color": |trait|}, ALL)
+ assert-css: (".item-table .fn", {"color": |fn|}, ALL)
+ assert-css: (".item-table .type", {"color": |type|}, ALL)
+ assert-css: (".item-table .union", {"color": |union|}, ALL)
+ assert-css: (".item-table .keyword", {"color": |keyword|}, ALL)
+ // Checking sidebar elements.
+ assert-css: (
+ ".sidebar-elems a:not(.current)",
+ {"color": |sidebar|, "background-color": "rgba(0, 0, 0, 0)", "font-weight": "400"},
+ ALL,
+ )
+ assert-css: (
+ ".sidebar-elems a.current",
+ {
+ "color": |sidebar_current|,
+ "background-color": |sidebar_current_background|,
+ "font-weight": "500",
+ },
+ ALL,
+ )
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "mod": "rgb(57, 175, 215)",
+ "macro": "rgb(163, 122, 204)",
+ "struct": "rgb(255, 160, 165)",
+ "enum": "rgb(255, 160, 165)",
+ "trait": "rgb(57, 175, 215)",
+ "fn": "rgb(253, 214, 135)",
+ "type": "rgb(255, 160, 165)",
+ "union": "rgb(255, 160, 165)",
+ "keyword": "rgb(57, 175, 215)",
+ "sidebar": "rgb(83, 177, 219)",
+ "sidebar_current": "rgb(255, 180, 76)",
+ "sidebar_current_background": "rgba(0, 0, 0, 0)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "mod": "rgb(210, 153, 29)",
+ "macro": "rgb(9, 189, 0)",
+ "struct": "rgb(45, 191, 184)",
+ "enum": "rgb(45, 191, 184)",
+ "trait": "rgb(183, 140, 242)",
+ "fn": "rgb(43, 171, 99)",
+ "type": "rgb(45, 191, 184)",
+ "union": "rgb(45, 191, 184)",
+ "keyword": "rgb(210, 153, 29)",
+ "sidebar": "rgb(253, 191, 53)",
+ "sidebar_current": "rgb(253, 191, 53)",
+ "sidebar_current_background": "rgb(68, 68, 68)",
+ },
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "mod": "rgb(56, 115, 173)",
+ "macro": "rgb(6, 128, 0)",
+ "struct": "rgb(173, 55, 138)",
+ "enum": "rgb(173, 55, 138)",
+ "trait": "rgb(110, 79, 201)",
+ "fn": "rgb(173, 124, 55)",
+ "type": "rgb(173, 55, 138)",
+ "union": "rgb(173, 55, 138)",
+ "keyword": "rgb(56, 115, 173)",
+ "sidebar": "rgb(53, 109, 164)",
+ "sidebar_current": "rgb(53, 109, 164)",
+ "sidebar_current_background": "rgb(255, 255, 255)",
+ },
+)
diff --git a/tests/rustdoc-gui/list_code_block.goml b/tests/rustdoc-gui/list_code_block.goml
new file mode 100644
index 000000000..c527cfbfc
--- /dev/null
+++ b/tests/rustdoc-gui/list_code_block.goml
@@ -0,0 +1,4 @@
+// This test checks that code blocks in list are supported.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+goto: "./fn.check_list_code_block.html"
+assert: (".item-decl pre.rust")
diff --git a/tests/rustdoc-gui/method-margins.goml b/tests/rustdoc-gui/method-margins.goml
new file mode 100644
index 000000000..720268a9e
--- /dev/null
+++ b/tests/rustdoc-gui/method-margins.goml
@@ -0,0 +1,18 @@
+// This test ensures that the margins on methods are coherent inside an impl block.
+goto: "file://" + |DOC_PATH| + "/test_docs/trait_members/struct.HasTrait.html#impl-TraitMembers-for-HasTrait"
+
+assert-count: ("#trait-implementations-list > .toggle", 1)
+
+compare-elements-css: (
+ // compare margin on type with margin on method
+ "#trait-implementations-list .impl-items > .toggle:nth-child(1) > summary",
+ "#trait-implementations-list .impl-items > .toggle:nth-child(2) > summary",
+ ["margin"]
+)
+
+compare-elements-css: (
+ // compare margin on type with margin on method
+ "#trait-implementations-list .impl-items > .toggle:nth-child(1)",
+ "#trait-implementations-list .impl-items > .toggle:nth-child(2)",
+ ["margin"]
+)
diff --git a/tests/rustdoc-gui/mobile.goml b/tests/rustdoc-gui/mobile.goml
new file mode 100644
index 000000000..895864d89
--- /dev/null
+++ b/tests/rustdoc-gui/mobile.goml
@@ -0,0 +1,34 @@
+// Test various properties of the mobile UI
+goto: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
+size: (400, 600)
+
+font-size: 18
+wait-for: 100 // wait a bit for the resize and the font-size change to be fully taken into account.
+
+// The out-of-band info (source, stable version, collapse) should be below the
+// h1 when the screen gets narrow enough.
+assert-css: (".main-heading", {
+ "display": "flex",
+ "flex-direction": "column"
+})
+
+assert-property: (".mobile-topbar h2", {"offsetHeight": 36})
+
+// Note: We can't use assert-text here because the 'Since' is set by CSS and
+// is therefore not part of the DOM.
+assert-css: (".content .out-of-band .since::before", { "content": "\"Since \"" })
+
+size: (1000, 1000)
+wait-for: 100 // wait a bit for the resize to be fully taken into account.
+assert-css-false: (".content .out-of-band .since::before", { "content": "\"Since \"" })
+
+// On the settings page, the theme buttons should not line-wrap. Instead, they should
+// all be placed as a group on a line below the setting name "Theme."
+goto: "file://" + |DOC_PATH| + "/settings.html"
+size: (400, 600)
+// Ignored for now https://github.com/rust-lang/rust/issues/93784.
+// compare-elements-position-near-false: (
+// "#preferred-light-theme .setting-name",
+// "#preferred-light-theme .choice",
+// {"y": 16},
+// )
diff --git a/tests/rustdoc-gui/module-items-font.goml b/tests/rustdoc-gui/module-items-font.goml
new file mode 100644
index 000000000..cd3676a98
--- /dev/null
+++ b/tests/rustdoc-gui/module-items-font.goml
@@ -0,0 +1,67 @@
+// This test checks that the correct font is used on module items (in index.html pages).
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+assert-css: (
+ ".item-table .module-item a",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+ ALL,
+)
+assert-css: (
+ ".item-table .docblock-short",
+ {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
+ ALL,
+)
+
+// modules
+assert-css: (
+ "#modules + .item-table .item-left a",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
+assert-css: (
+ "#modules + .item-table .item-right.docblock-short",
+ {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
+)
+// structs
+assert-css: (
+ "#structs + .item-table .item-left a",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
+assert-css: (
+ "#structs + .item-table .item-right.docblock-short",
+ {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
+)
+// enums
+assert-css: (
+ "#enums + .item-table .item-left a",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
+assert-css: (
+ "#enums + .item-table .item-right.docblock-short",
+ {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
+)
+// traits
+assert-css: (
+ "#traits + .item-table .item-left a",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
+assert-css: (
+ "#traits + .item-table .item-right.docblock-short",
+ {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
+)
+// functions
+assert-css: (
+ "#functions + .item-table .item-left a",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
+assert-css: (
+ "#functions + .item-table .item-right.docblock-short",
+ {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
+)
+// keywords
+assert-css: (
+ "#keywords + .item-table .item-left a",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
+assert-css: (
+ "#keywords + .item-table .item-right.docblock-short",
+ {"font-family": '"Source Serif 4", NanumBarunGothic, serif'},
+)
diff --git a/tests/rustdoc-gui/no-docblock.goml b/tests/rustdoc-gui/no-docblock.goml
new file mode 100644
index 000000000..17a955064
--- /dev/null
+++ b/tests/rustdoc-gui/no-docblock.goml
@@ -0,0 +1,13 @@
+// This test checks that there are margins applied to methods with no docblocks.
+
+// We need to disable this check because `implementors/test_docs/trait.TraitWithNoDocblock.js`
+// doesn't exist.
+fail-on-request-error: false
+
+goto: "file://" + |DOC_PATH| + "/test_docs/trait.TraitWithNoDocblocks.html"
+// Check that the two methods are more than 24px apart.
+compare-elements-position-near-false: ("//*[@id='tymethod.first_fn']", "//*[@id='tymethod.second_fn']", {"y": 24})
+
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.TypeWithNoDocblocks.html"
+// Check that the two methods are more than 24px apart.
+compare-elements-position-near-false: ("//*[@id='method.first_fn']", "//*[@id='method.second_fn']", {"y": 24})
diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml
new file mode 100644
index 000000000..b4fa7d0db
--- /dev/null
+++ b/tests/rustdoc-gui/notable-trait.goml
@@ -0,0 +1,276 @@
+// This test checks the position of the `i` for the notable traits.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.NotableStructWithLongName.html"
+show-text: true
+// We start with a wide screen.
+size: (1100, 600)
+// Checking they have the same y position.
+compare-elements-position: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ ("y"),
+)
+// Checking they don't have the same x position.
+compare-elements-position-false: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ ("x"),
+)
+// The `i` should be *after* the type.
+assert-position: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ {"x": 677},
+)
+assert-position: (
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ {"x": 955},
+)
+// The tooltip should be below the `i`
+// Also, clicking the tooltip should bring its text into the DOM
+assert-count: ("//*[@class='notable popover']", 0)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+compare-elements-position-near: (
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ "//*[@class='notable popover']",
+ {"y": 30}
+)
+compare-elements-position-false: (
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ "//*[@class='notable popover']",
+ ("x")
+)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+move-cursor-to: "//h1"
+assert-count: ("//*[@class='notable popover']", 0)
+
+// Now only the `i` should be on the next line.
+size: (1055, 600)
+compare-elements-position-false: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ ("y", "x"),
+)
+
+// Now both the `i` and the struct name should be on the next line.
+size: (980, 600)
+// Checking they have the same y position.
+compare-elements-position: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ ("y"),
+)
+// Checking they don't have the same x position.
+compare-elements-position-false: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ ("x"),
+)
+// The `i` should be *after* the type.
+assert-position: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ {"x": 245},
+)
+assert-position: (
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ {"x": 523},
+)
+
+// Checking on mobile now.
+size: (650, 600)
+// Checking they have the same y position.
+compare-elements-position: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ ("y"),
+)
+// Checking they don't have the same x position.
+compare-elements-position-false: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ ("x"),
+)
+// The `i` should be *after* the type.
+assert-position: (
+ "//*[@id='method.create_an_iterator_from_read']//a[text()='NotableStructWithLongName']",
+ {"x": 15},
+)
+assert-position: (
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ {"x": 293},
+)
+// The tooltip should STILL be below `i`
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+compare-elements-position-near: (
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ "//*[@class='notable popover']",
+ {"y": 30}
+)
+compare-elements-position-false: (
+ "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']",
+ "//*[@class='notable popover']",
+ ("x")
+)
+assert-position: (
+ "//*[@class='notable popover']",
+ {"x": 0}
+)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+move-cursor-to: "//h1"
+assert-count: ("//*[@class='notable popover']", 0)
+
+// Now check the colors.
+define-function: (
+ "check-colors",
+ (theme, header_color, content_color, type_color, trait_color),
+ block {
+ goto: "file://" + |DOC_PATH| + "/test_docs/struct.NotableStructWithLongName.html"
+ // This is needed to ensure that the text color is computed.
+ show-text: true
+
+ // Setting the theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ // We reload the page so the local storage settings are being used.
+ reload:
+
+ move-cursor-to: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+ assert-count: (".notable.popover", 1)
+
+ assert-css: (
+ ".notable.popover h3",
+ {"color": |header_color|},
+ ALL,
+ )
+ assert-css: (
+ ".notable.popover pre",
+ {"color": |content_color|},
+ ALL,
+ )
+ assert-css: (
+ ".notable.popover pre a.struct",
+ {"color": |type_color|},
+ ALL,
+ )
+ assert-css: (
+ ".notable.popover pre a.trait",
+ {"color": |trait_color|},
+ ALL,
+ )
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "content_color": "rgb(230, 225, 207)",
+ "header_color": "rgb(255, 255, 255)",
+ "type_color": "rgb(255, 160, 165)",
+ "trait_color": "rgb(57, 175, 215)",
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "content_color": "rgb(221, 221, 221)",
+ "header_color": "rgb(221, 221, 221)",
+ "type_color": "rgb(45, 191, 184)",
+ "trait_color": "rgb(183, 140, 242)",
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "content_color": "rgb(0, 0, 0)",
+ "header_color": "rgb(0, 0, 0)",
+ "type_color": "rgb(173, 55, 138)",
+ "trait_color": "rgb(110, 79, 201)",
+ },
+)
+
+reload:
+
+// Check that pressing escape works
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+move-cursor-to: "//*[@class='notable popover']"
+assert-count: ("//*[@class='notable popover']", 1)
+press-key: "Escape"
+assert-count: ("//*[@class='notable popover']", 0)
+assert: "#method\.create_an_iterator_from_read .notable-traits:focus"
+
+// Check that clicking outside works.
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+click: ".search-input"
+assert-count: ("//*[@class='notable popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
+
+// Check that pressing tab over and over works.
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+move-cursor-to: "//*[@class='notable popover']"
+assert-count: ("//*[@class='notable popover']", 1)
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+press-key: "Tab"
+assert-count: ("//*[@class='notable popover']", 0)
+assert: "#method\.create_an_iterator_from_read .notable-traits:focus"
+
+// Now we check that the focus isn't given back to the wrong item when opening
+// another popover.
+store-window-property: (scroll, "scrollY")
+click: "#method\.create_an_iterator_from_read .fn"
+// We ensure that the scroll position changed.
+assert-window-property-false: {"scrollY": |scroll|}
+// Store the new position.
+store-window-property: (scroll, "scrollY")
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+wait-for: "//*[@class='notable popover']"
+click: "#settings-menu a"
+click: ".search-input"
+// We ensure we didn't come back to the previous focused item.
+assert-window-property-false: {"scrollY": |scroll|}
+
+// Same but with Escape handling.
+store-window-property: (scroll, "scrollY")
+click: "#method\.create_an_iterator_from_read .fn"
+// We ensure that the scroll position changed.
+assert-window-property-false: {"scrollY": |scroll|}
+// Store the new position.
+store-window-property: (scroll, "scrollY")
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+wait-for: "//*[@class='notable popover']"
+click: "#settings-menu a"
+press-key: "Escape"
+// We ensure we didn't come back to the previous focused item.
+assert-window-property-false: {"scrollY": |scroll|}
+
+// Opening the mobile sidebar should close the popover.
+size: (650, 600)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+click: ".sidebar-menu-toggle"
+assert: "//*[@class='sidebar shown']"
+assert-count: ("//*[@class='notable popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
+// Clicking a notable popover should close the sidebar.
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+assert-false: "//*[@class='sidebar shown']"
+
+// Also check the focus handling for the help button.
+size: (1100, 600)
+reload:
+assert-count: ("//*[@class='notable popover']", 0)
+click: "//*[@id='method.create_an_iterator_from_read']//*[@class='notable-traits']"
+assert-count: ("//*[@class='notable popover']", 1)
+click: "#help-button a"
+assert-count: ("//*[@class='notable popover']", 0)
+assert-false: "#method\.create_an_iterator_from_read .notable-traits:focus"
diff --git a/tests/rustdoc-gui/overflow-tooltip-information.goml b/tests/rustdoc-gui/overflow-tooltip-information.goml
new file mode 100644
index 000000000..09ad6cdd7
--- /dev/null
+++ b/tests/rustdoc-gui/overflow-tooltip-information.goml
@@ -0,0 +1,8 @@
+// The goal of this test is to ensure that the tooltip `.information` class doesn't
+// have overflow and max-width CSS rules set because they create a bug in firefox on
+// mac. For more information: https://github.com/rust-lang/rust/issues/89185
+goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+assert-css: (".docblock > .example-wrap .tooltip", {
+ "overflow-x": "visible",
+ "max-width": "none"
+}, ALL)
diff --git a/tests/rustdoc-gui/pocket-menu.goml b/tests/rustdoc-gui/pocket-menu.goml
new file mode 100644
index 000000000..c3649dc7b
--- /dev/null
+++ b/tests/rustdoc-gui/pocket-menu.goml
@@ -0,0 +1,98 @@
+// This test ensures that the "pocket menus" are working as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// First we check that the help menu doesn't exist yet.
+assert-false: "#help-button .popover"
+// Then we display the help menu.
+click: "#help-button"
+assert: "#help-button .popover"
+assert-css: ("#help-button .popover", {"display": "block"})
+
+// Now we click somewhere else on the page to ensure it is handling the blur event
+// correctly.
+click: ".sidebar"
+assert-css: ("#help-button .popover", {"display": "none"})
+
+// Now we will check that we cannot have two "pocket menus" displayed at the same time.
+click: "#help-button"
+assert-css: ("#help-button .popover", {"display": "block"})
+click: "#settings-menu"
+assert-css: ("#help-button .popover", {"display": "none"})
+assert-css: ("#settings-menu .popover", {"display": "block"})
+
+// Now the other way.
+click: "#help-button"
+assert-css: ("#help-button .popover", {"display": "block"})
+assert-css: ("#settings-menu .popover", {"display": "none"})
+
+// Now verify that clicking the help menu again closes it.
+click: "#help-button"
+assert-css: ("#help-button .popover", {"display": "none"})
+assert-css: ("#settings-menu .popover", {"display": "none"})
+
+// We check the borders color now:
+
+// Ayu theme
+local-storage: {
+ "rustdoc-theme": "ayu",
+ "rustdoc-use-system-theme": "false",
+}
+reload:
+
+click: "#help-button"
+assert-css: (
+ "#help-button .popover",
+ {"display": "block", "border-color": "rgb(92, 103, 115)"},
+)
+compare-elements-css: ("#help-button .popover", "#help-button .top", ["border-color"])
+compare-elements-css: ("#help-button .popover", "#help-button .bottom", ["border-color"])
+
+// Dark theme
+local-storage: {
+ "rustdoc-theme": "dark",
+ "rustdoc-use-system-theme": "false",
+}
+reload:
+
+click: "#help-button"
+assert-css: (
+ "#help-button .popover",
+ {"display": "block", "border-color": "rgb(224, 224, 224)"},
+)
+compare-elements-css: ("#help-button .popover", "#help-button .top", ["border-color"])
+compare-elements-css: ("#help-button .popover", "#help-button .bottom", ["border-color"])
+
+// Light theme
+local-storage: {
+ "rustdoc-theme": "light",
+ "rustdoc-use-system-theme": "false",
+}
+reload:
+
+click: "#help-button"
+assert-css: (
+ "#help-button .popover",
+ {"display": "block", "border-color": "rgb(224, 224, 224)"},
+)
+compare-elements-css: ("#help-button .popover", "#help-button .top", ["border-color"])
+compare-elements-css: ("#help-button .popover", "#help-button .bottom", ["border-color"])
+
+// Opening the mobile sidebar should close the settings popover.
+size: (650, 600)
+click: "#settings-menu a"
+assert-css: ("#settings-menu .popover", {"display": "block"})
+click: ".sidebar-menu-toggle"
+assert: "//*[@class='sidebar shown']"
+assert-css: ("#settings-menu .popover", {"display": "none"})
+// Opening the settings popover should close the sidebar.
+click: "#settings-menu a"
+assert-css: ("#settings-menu .popover", {"display": "block"})
+assert-false: "//*[@class='sidebar shown']"
+
+// Opening the settings popover at start (which async loads stuff) should also close.
+reload:
+click: ".sidebar-menu-toggle"
+assert: "//*[@class='sidebar shown']"
+assert-false: "#settings-menu .popover"
+click: "#settings-menu a"
+assert-false: "//*[@class='sidebar shown']"
+wait-for: "#settings-menu .popover"
diff --git a/tests/rustdoc-gui/run-on-hover.goml b/tests/rustdoc-gui/run-on-hover.goml
new file mode 100644
index 000000000..8dcb62c10
--- /dev/null
+++ b/tests/rustdoc-gui/run-on-hover.goml
@@ -0,0 +1,54 @@
+// Example code blocks sometimes have a "Run" button to run them on the
+// Playground. That button is hidden until the user hovers over the code block.
+// This test checks that it is hidden, and that it shows on hover. It also
+// checks for its color.
+goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
+show-text: true
+
+define-function: (
+ "check-run-button",
+ (theme, color, background, hover_color, hover_background),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: (".test-arrow", {"visibility": "hidden"})
+ move-cursor-to: ".example-wrap"
+ assert-css: (".test-arrow", {
+ "visibility": "visible",
+ "color": |color|,
+ "background-color": |background|,
+ "font-size": "22px",
+ "border-radius": "5px",
+ })
+ move-cursor-to: ".test-arrow"
+ assert-css: (".test-arrow:hover", {
+ "visibility": "visible",
+ "color": |hover_color|,
+ "background-color": |hover_background|,
+ "font-size": "22px",
+ "border-radius": "5px",
+ })
+ },
+)
+
+call-function: ("check-run-button", {
+ "theme": "ayu",
+ "color": "rgb(120, 135, 151)",
+ "background": "rgba(57, 175, 215, 0.09)",
+ "hover_color": "rgb(197, 197, 197)",
+ "hover_background": "rgba(57, 175, 215, 0.37)",
+})
+call-function: ("check-run-button", {
+ "theme": "dark",
+ "color": "rgb(222, 222, 222)",
+ "background": "rgba(78, 139, 202, 0.2)",
+ "hover_color": "rgb(222, 222, 222)",
+ "hover_background": "rgb(78, 139, 202)",
+})
+call-function: ("check-run-button", {
+ "theme": "light",
+ "color": "rgb(245, 245, 245)",
+ "background": "rgba(78, 139, 202, 0.2)",
+ "hover_color": "rgb(245, 245, 245)",
+ "hover_background": "rgb(78, 139, 202)",
+})
diff --git a/tests/rustdoc-gui/rust-logo.goml b/tests/rustdoc-gui/rust-logo.goml
new file mode 100644
index 000000000..2d15e8b96
--- /dev/null
+++ b/tests/rustdoc-gui/rust-logo.goml
@@ -0,0 +1,58 @@
+// This test ensures that the correct style is applied to the rust logo in the sidebar.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+
+define-function: (
+ "check-logo",
+ (theme, filter),
+ block {
+ // Going to the doc page.
+ goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+ // Changing theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: (".rust-logo", {"filter": |filter|})
+ // Going to the source code page.
+ goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+ // Changing theme (since it's local files, the local storage works by folder).
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: (".rust-logo", {"filter": |filter|})
+ // Now we check that the non-rust logos don't have a CSS filter set.
+ goto: "file://" + |DOC_PATH| + "/huge_logo/index.html"
+ // Changing theme on the new page (again...).
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ // Check there is no rust logo
+ assert-false: ".rust-logo"
+ // Check there is no filter.
+ assert-css: (".sidebar .logo-container img", {"filter": "none"})
+ },
+)
+
+call-function: (
+ "check-logo",
+ {
+ "theme": "ayu",
+ "filter": "drop-shadow(rgb(255, 255, 255) 1px 0px 0px) " +
+ "drop-shadow(rgb(255, 255, 255) 0px 1px 0px) " +
+ "drop-shadow(rgb(255, 255, 255) -1px 0px 0px) " +
+ "drop-shadow(rgb(255, 255, 255) 0px -1px 0px)",
+ },
+)
+call-function: (
+ "check-logo",
+ {
+ "theme": "dark",
+ "filter": "drop-shadow(rgb(255, 255, 255) 1px 0px 0px) " +
+ "drop-shadow(rgb(255, 255, 255) 0px 1px 0px) " +
+ "drop-shadow(rgb(255, 255, 255) -1px 0px 0px) " +
+ "drop-shadow(rgb(255, 255, 255) 0px -1px 0px)",
+ },
+)
+call-function: (
+ "check-logo",
+ {
+ "theme": "light",
+ "filter": "none",
+ },
+)
diff --git a/tests/rustdoc-gui/scrape-examples-button-focus.goml b/tests/rustdoc-gui/scrape-examples-button-focus.goml
new file mode 100644
index 000000000..1b5c3a0d2
--- /dev/null
+++ b/tests/rustdoc-gui/scrape-examples-button-focus.goml
@@ -0,0 +1,31 @@
+// This test ensures that the scraped examples buttons are working as expecting
+// when 'Enter' key is pressed when they're focused.
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
+
+// The next/prev buttons vertically scroll the code viewport between examples
+store-property: (initialScrollTop, ".scraped-example-list > .scraped-example pre", "scrollTop")
+focus: ".scraped-example-list > .scraped-example .next"
+press-key: "Enter"
+assert-property-false: (".scraped-example-list > .scraped-example pre", {
+ "scrollTop": |initialScrollTop|
+})
+focus: ".scraped-example-list > .scraped-example .prev"
+press-key: "Enter"
+assert-property: (".scraped-example-list > .scraped-example pre", {
+ "scrollTop": |initialScrollTop|
+})
+
+// The expand button increases the scrollHeight of the minimized code viewport
+store-property: (smallOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
+assert-property-false: (".scraped-example-list > .scraped-example pre", {
+ "scrollHeight": |smallOffsetHeight|
+})
+focus: ".scraped-example-list > .scraped-example .expand"
+press-key: "Enter"
+assert-property-false: (".scraped-example-list > .scraped-example pre", {
+ "offsetHeight": |smallOffsetHeight|
+})
+store-property: (fullOffsetHeight, ".scraped-example-list > .scraped-example pre", "offsetHeight")
+assert-property: (".scraped-example-list > .scraped-example pre", {
+ "scrollHeight": |fullOffsetHeight|
+})
diff --git a/tests/rustdoc-gui/scrape-examples-color.goml b/tests/rustdoc-gui/scrape-examples-color.goml
new file mode 100644
index 000000000..40f31b277
--- /dev/null
+++ b/tests/rustdoc-gui/scrape-examples-color.goml
@@ -0,0 +1,60 @@
+// Check that scrape example code blocks have the expected colors.
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
+show-text: true
+
+define-function: (
+ "check-colors",
+ (theme, highlight, highlight_focus, help_border, help_color, help_hover_border,
+ help_hover_color),
+ block {
+ local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false", }
+ reload:
+ wait-for: ".more-examples-toggle"
+ assert-css: (".scraped-example .example-wrap .rust span.highlight:not(.focus)", {
+ "background-color": |highlight|,
+ }, ALL)
+ assert-css: (".scraped-example .example-wrap .rust span.highlight.focus", {
+ "background-color": |highlight_focus|,
+ }, ALL)
+
+ assert-css: (".scraped-example-list .scrape-help", {
+ "border-color": |help_border|,
+ "color": |help_color|,
+ })
+ move-cursor-to: ".scraped-example-list .scrape-help"
+ assert-css: (".scraped-example-list .scrape-help:hover", {
+ "border-color": |help_hover_border|,
+ "color": |help_hover_color|,
+ })
+ // Moving the cursor to another item to not break next runs.
+ move-cursor-to: ".search-input"
+ }
+)
+
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "highlight": "rgb(91, 59, 1)",
+ "highlight_focus": "rgb(124, 75, 15)",
+ "help_border": "rgb(170, 170, 170)",
+ "help_color": "rgb(238, 238, 238)",
+ "help_hover_border": "rgb(255, 255, 255)",
+ "help_hover_color": "rgb(255, 255, 255)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "highlight": "rgb(91, 59, 1)",
+ "highlight_focus": "rgb(124, 75, 15)",
+ "help_border": "rgb(170, 170, 170)",
+ "help_color": "rgb(238, 238, 238)",
+ "help_hover_border": "rgb(255, 255, 255)",
+ "help_hover_color": "rgb(255, 255, 255)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "highlight": "rgb(252, 255, 214)",
+ "highlight_focus": "rgb(246, 253, 176)",
+ "help_border": "rgb(85, 85, 85)",
+ "help_color": "rgb(51, 51, 51)",
+ "help_hover_border": "rgb(0, 0, 0)",
+ "help_hover_color": "rgb(0, 0, 0)",
+})
diff --git a/tests/rustdoc-gui/scrape-examples-fonts.goml b/tests/rustdoc-gui/scrape-examples-fonts.goml
new file mode 100644
index 000000000..142f337cb
--- /dev/null
+++ b/tests/rustdoc-gui/scrape-examples-fonts.goml
@@ -0,0 +1,9 @@
+// This test ensures that the correct font is used in scraped examples.
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
+
+store-value: (font, '"Fira Sans", Arial, NanumBarunGothic, sans-serif')
+
+wait-for-css: (".scraped-example-title", {"font-family": |font|})
+wait-for-css: (".more-examples-toggle summary", {"font-family": |font|})
+wait-for-css: (".more-examples-toggle .hide-more", {"font-family": |font|})
+wait-for-css: (".example-links a", {"font-family": |font|})
diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml
new file mode 100644
index 000000000..95102528e
--- /dev/null
+++ b/tests/rustdoc-gui/scrape-examples-layout.goml
@@ -0,0 +1,49 @@
+// Check that the line number column has the correct layout.
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
+
+// Check that it's not zero.
+assert-property-false: (
+ ".more-scraped-examples .scraped-example .code-wrapper .src-line-numbers",
+ {"clientWidth": "0"}
+)
+
+// Check that examples with very long lines have the same width as ones that don't.
+store-property: (
+ clientWidth,
+ ".more-scraped-examples .scraped-example:nth-child(2) .code-wrapper .src-line-numbers",
+ "clientWidth"
+)
+
+assert-property: (
+ ".more-scraped-examples .scraped-example:nth-child(3) .code-wrapper .src-line-numbers",
+ {"clientWidth": |clientWidth|}
+)
+
+assert-property: (
+ ".more-scraped-examples .scraped-example:nth-child(4) .code-wrapper .src-line-numbers",
+ {"clientWidth": |clientWidth|}
+)
+
+assert-property: (
+ ".more-scraped-examples .scraped-example:nth-child(5) .code-wrapper .src-line-numbers",
+ {"clientWidth": |clientWidth|}
+)
+
+assert-property: (
+ ".more-scraped-examples .scraped-example:nth-child(6) .code-wrapper .src-line-numbers",
+ {"clientWidth": |clientWidth|}
+)
+
+// Check that for both mobile and desktop sizes, the buttons in scraped examples are displayed
+// correctly.
+
+store-value: (offset_y, 4)
+
+// First with desktop
+assert-position: (".scraped-example .code-wrapper", {"y": 255})
+assert-position: (".scraped-example .code-wrapper .prev", {"y": 255 + |offset_y|})
+
+// Then with mobile
+size: (600, 600)
+assert-position: (".scraped-example .code-wrapper", {"y": 314})
+assert-position: (".scraped-example .code-wrapper .prev", {"y": 314 + |offset_y|})
diff --git a/tests/rustdoc-gui/scrape-examples-toggle.goml b/tests/rustdoc-gui/scrape-examples-toggle.goml
new file mode 100644
index 000000000..2d5df6a5d
--- /dev/null
+++ b/tests/rustdoc-gui/scrape-examples-toggle.goml
@@ -0,0 +1,51 @@
+// This tests checks that the "scraped examples" toggle is working as expected.
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
+
+// Checking the color of the toggle line.
+show-text: true
+define-function: (
+ "check-color",
+ (theme, toggle_line_color, toggle_line_hover_color),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+
+ // Clicking "More examples..." will open additional examples
+ assert-attribute-false: (".more-examples-toggle", {"open": ""})
+ click: ".more-examples-toggle"
+ assert-attribute: (".more-examples-toggle", {"open": ""})
+
+ assert-css: (".toggle-line-inner", {"background-color": |toggle_line_color|}, ALL)
+ move-cursor-to: ".toggle-line"
+ assert-css: (
+ ".toggle-line:hover .toggle-line-inner",
+ {"background-color": |toggle_line_hover_color|},
+ )
+ // Moving cursor away from the toggle line to prevent disrupting next test.
+ move-cursor-to: ".search-input"
+ },
+)
+
+call-function: ("check-color", {
+ "theme": "ayu",
+ "toggle_line_color": "rgb(153, 153, 153)",
+ "toggle_line_hover_color": "rgb(197, 197, 197)",
+})
+call-function: ("check-color", {
+ "theme": "dark",
+ "toggle_line_color": "rgb(153, 153, 153)",
+ "toggle_line_hover_color": "rgb(197, 197, 197)",
+})
+call-function: ("check-color", {
+ "theme": "light",
+ "toggle_line_color": "rgb(204, 204, 204)",
+ "toggle_line_hover_color": "rgb(153, 153, 153)",
+})
+
+// Toggling all docs will close additional examples
+click: "#toggle-all-docs"
+assert-attribute-false: (".more-examples-toggle", {"open": ""})
+
+// After re-opening the docs, the additional examples should stay closed
+click: "#toggle-all-docs"
+assert-attribute-false: (".more-examples-toggle", {"open": ""})
diff --git a/tests/rustdoc-gui/search-filter.goml b/tests/rustdoc-gui/search-filter.goml
new file mode 100644
index 000000000..5bc6e87d6
--- /dev/null
+++ b/tests/rustdoc-gui/search-filter.goml
@@ -0,0 +1,87 @@
+// Checks that the crate search filtering is handled correctly and changes the results.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+show-text: true
+write: (".search-input", "test")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-text: ("#results .externcrate", "test_docs")
+
+wait-for: "#crate-search"
+// We now want to change the crate filter.
+click: "#crate-search"
+// We select "lib2" option then press enter to change the filter.
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+press-key: "Enter"
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-document-property: ({"URL": "&filter-crate="}, CONTAINS)
+// We check that there is no more "test_docs" appearing.
+assert-false: "#results .externcrate"
+// We also check that "lib2" is the filter crate.
+assert-property: ("#crate-search", {"value": "lib2"})
+
+// Now we check that leaving the search results and putting them back keeps the
+// crate filtering.
+press-key: "Escape"
+wait-for-css: ("#main-content", {"display": "block"})
+focus: ".search-input"
+wait-for-css: ("#main-content", {"display": "none"})
+// We check that there is no more "test_docs" appearing.
+assert-false: "#results .externcrate"
+assert-property: ("#crate-search", {"value": "lib2"})
+
+// Selecting back "All crates"
+click: "#crate-search"
+press-key: "ArrowUp"
+press-key: "ArrowUp"
+press-key: "ArrowUp"
+press-key: "ArrowUp"
+press-key: "Enter"
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-property: ("#crate-search", {"value": "all crates"})
+
+// Checking that the URL parameter is taken into account for crate filtering.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=test&filter-crate=lib2"
+wait-for: "#crate-search"
+assert-property: ("#crate-search", {"value": "lib2"})
+assert-false: "#results .externcrate"
+
+// Checking that the text for the "title" is correct (the "all crates" comes from the "<select>").
+assert-text: (".search-results-title", "Results in all crates", STARTS_WITH)
+
+// Checking the display of the crate filter.
+// We start with the light theme.
+local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"}
+reload:
+
+timeout: 2000
+wait-for: "#crate-search"
+assert-css: ("#crate-search", {
+ "border": "1px solid rgb(224, 224, 224)",
+ "color": "rgb(0, 0, 0)",
+ "background-color": "rgb(255, 255, 255)",
+})
+
+// We now check the dark theme.
+click: "#settings-menu"
+wait-for: "#settings"
+click: "#theme-dark"
+wait-for-css: ("#crate-search", {
+ "border": "1px solid rgb(224, 224, 224)",
+ "color": "rgb(221, 221, 221)",
+ "background-color": "rgb(53, 53, 53)",
+})
+
+// And finally we check the ayu theme.
+click: "#theme-ayu"
+wait-for-css: ("#crate-search", {
+ "border": "1px solid rgb(92, 103, 115)",
+ "color": "rgb(255, 255, 255)",
+ "background-color": "rgb(15, 20, 25)",
+})
diff --git a/tests/rustdoc-gui/search-form-elements.goml b/tests/rustdoc-gui/search-form-elements.goml
new file mode 100644
index 000000000..542db348c
--- /dev/null
+++ b/tests/rustdoc-gui/search-form-elements.goml
@@ -0,0 +1,263 @@
+// This test ensures that the elements in ".search-form" have the expected display.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+show-text: true
+
+// Ayu theme
+local-storage: {
+ "rustdoc-theme": "ayu",
+ "rustdoc-use-system-theme": "false",
+}
+reload:
+
+assert-css: (
+ ".search-input",
+ {
+ "border-color": "rgb(92, 103, 115)",
+ "background-color": "rgb(20, 25, 32)",
+ "color": "rgb(255, 255, 255)",
+ },
+)
+focus: ".search-input"
+// Nothing should change.
+assert-css: (
+ ".search-input",
+ {
+ "border-color": "rgb(92, 103, 115)",
+ "background-color": "rgb(20, 25, 32)",
+ "color": "rgb(255, 255, 255)",
+ },
+)
+
+assert-css: (
+ "#help-button",
+ {"border-color": "rgb(197, 197, 197)"},
+)
+assert-css: (
+ "#help-button > a",
+ {
+ "color": "rgb(255, 255, 255)",
+ "border-color": "rgb(92, 103, 115)",
+ "background-color": "rgb(20, 25, 32)",
+ },
+)
+move-cursor-to: "#help-button"
+assert-css: (
+ "#help-button:hover",
+ {"border-color": "rgb(197, 197, 197)"},
+)
+// Only "border-color" should change.
+assert-css: (
+ "#help-button:hover > a",
+ {
+ "color": "rgb(255, 255, 255)",
+ "border-color": "rgb(224, 224, 224)",
+ "background-color": "rgb(20, 25, 32)",
+ },
+)
+// Link color inside
+click: "#help-button"
+assert-css: (
+ "#help a",
+ {
+ "color": "rgb(57, 175, 215)",
+ },
+)
+
+assert-css: (
+ "#settings-menu",
+ {"border-color": "rgb(197, 197, 197)"},
+)
+assert-css: (
+ "#settings-menu > a",
+ {
+ "border-color": "rgb(92, 103, 115)",
+ "background-color": "rgb(20, 25, 32)",
+ },
+)
+move-cursor-to: "#settings-menu"
+assert-css: (
+ "#settings-menu:hover",
+ {"border-color": "rgb(197, 197, 197)"},
+)
+// Only "border-color" should change.
+assert-css: (
+ "#settings-menu:hover > a",
+ {
+ "border-color": "rgb(224, 224, 224)",
+ "background-color": "rgb(20, 25, 32)",
+ },
+)
+
+// Dark theme
+local-storage: {
+ "rustdoc-theme": "dark",
+ "rustdoc-use-system-theme": "false",
+}
+reload:
+
+assert-css: (
+ ".search-input",
+ {
+ "border-color": "rgb(224, 224, 224)",
+ "background-color": "rgb(240, 240, 240)",
+ "color": "rgb(17, 17, 17)",
+ },
+)
+focus: ".search-input"
+// Only "border-color" should change.
+assert-css: (
+ ".search-input",
+ {
+ "border-color": "rgb(0, 141, 253)",
+ "background-color": "rgb(240, 240, 240)",
+ "color": "rgb(17, 17, 17)",
+ },
+)
+
+assert-css: (
+ "#help-button",
+ {"border-color": "rgb(221, 221, 221)"},
+)
+assert-css: (
+ "#help-button > a",
+ {
+ "color": "rgb(0, 0, 0)",
+ "border-color": "rgb(224, 224, 224)",
+ "background-color": "rgb(240, 240, 240)",
+ },
+)
+move-cursor-to: "#help-button"
+assert-css: (
+ "#help-button:hover",
+ {"border-color": "rgb(221, 221, 221)"},
+)
+// Only "border-color" should change.
+assert-css: (
+ "#help-button:hover > a",
+ {
+ "color": "rgb(0, 0, 0)",
+ "border-color": "rgb(255, 185, 0)",
+ "background-color": "rgb(240, 240, 240)",
+ },
+)
+// Link color inside
+click: "#help-button"
+assert-css: (
+ "#help a",
+ {
+ "color": "rgb(210, 153, 29)",
+ },
+)
+
+assert-css: (
+ "#settings-menu",
+ {"border-color": "rgb(221, 221, 221)"},
+)
+assert-css: (
+ "#settings-menu > a",
+ {
+ "border-color": "rgb(224, 224, 224)",
+ "background-color": "rgb(240, 240, 240)",
+ },
+)
+move-cursor-to: "#settings-menu"
+assert-css: (
+ "#settings-menu:hover",
+ {"border-color": "rgb(221, 221, 221)"},
+)
+// Only "border-color" should change.
+assert-css: (
+ "#settings-menu:hover > a",
+ {
+ "color": "rgb(0, 0, 0)",
+ "border-color": "rgb(255, 185, 0)",
+ "background-color": "rgb(240, 240, 240)",
+ },
+)
+
+// Light theme
+local-storage: {
+ "rustdoc-theme": "light",
+ "rustdoc-use-system-theme": "false",
+}
+reload:
+
+assert-css: (
+ ".search-input",
+ {
+ "border-color": "rgb(224, 224, 224)",
+ "background-color": "rgb(255, 255, 255)",
+ "color": "rgb(0, 0, 0)",
+ },
+)
+focus: ".search-input"
+// Nothing should change.
+assert-css: (
+ ".search-input",
+ {
+ "border-color": "rgb(102, 175, 233)",
+ "background-color": "rgb(255, 255, 255)",
+ "color": "rgb(0, 0, 0)",
+ },
+)
+
+assert-css: (
+ "#help-button",
+ {"border-color": "rgb(0, 0, 0)"},
+)
+assert-css: (
+ "#help-button > a",
+ {
+ "color": "rgb(0, 0, 0)",
+ "border-color": "rgb(224, 224, 224)",
+ "background-color": "rgb(255, 255, 255)",
+ },
+)
+move-cursor-to: "#help-button"
+assert-css: (
+ "#help-button:hover",
+ {"border-color": "rgb(0, 0, 0)"},
+)
+// Only "border-color" should change.
+assert-css: (
+ "#help-button:hover > a",
+ {
+ "color": "rgb(0, 0, 0)",
+ "border-color": "rgb(113, 113, 113)",
+ "background-color": "rgb(255, 255, 255)",
+ },
+)
+// Link color inside
+click: "#help-button"
+assert-css: (
+ "#help a",
+ {
+ "color": "rgb(56, 115, 173)",
+ },
+)
+
+assert-css: (
+ "#settings-menu",
+ {"border-color": "rgb(0, 0, 0)"},
+)
+assert-css: (
+ "#settings-menu > a",
+ {
+ "border-color": "rgb(224, 224, 224)",
+ "background-color": "rgb(255, 255, 255)",
+ },
+)
+move-cursor-to: "#settings-menu"
+assert-css: (
+ "#settings-menu:hover",
+ {"border-color": "rgb(0, 0, 0)"},
+)
+// Only "border-color" should change.
+assert-css: (
+ "#settings-menu:hover > a",
+ {
+ "color": "rgb(0, 0, 0)",
+ "border-color": "rgb(113, 113, 113)",
+ "background-color": "rgb(255, 255, 255)",
+ },
+)
diff --git a/tests/rustdoc-gui/search-input-mobile.goml b/tests/rustdoc-gui/search-input-mobile.goml
new file mode 100644
index 000000000..ce0cef77c
--- /dev/null
+++ b/tests/rustdoc-gui/search-input-mobile.goml
@@ -0,0 +1,11 @@
+// Test to ensure that you can click on the search input, whatever the width.
+// The PR which fixed it is: https://github.com/rust-lang/rust/pull/81592
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+size: (463, 700)
+// We first check that the search input isn't already focused.
+assert-false: ("input.search-input:focus")
+click: "input.search-input"
+reload:
+size: (750, 700)
+click: "input.search-input"
+assert: ("input.search-input:focus")
diff --git a/tests/rustdoc-gui/search-keyboard.goml b/tests/rustdoc-gui/search-keyboard.goml
new file mode 100644
index 000000000..ed975664c
--- /dev/null
+++ b/tests/rustdoc-gui/search-keyboard.goml
@@ -0,0 +1,28 @@
+// Checks that the search tab results work correctly with function signature syntax
+// First, try a search-by-name
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "Foo")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+
+// Now use the keyboard commands to switch to the third result.
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+press-key: "ArrowDown"
+assert: ".search-results.active > a:focus:nth-of-type(3)"
+
+// Now switch to the second tab, then back to the first one, then arrow back up.
+press-key: "ArrowRight"
+assert: ".search-results.active:nth-of-type(2) > a:focus:nth-of-type(1)"
+press-key: "ArrowLeft"
+assert: ".search-results.active:nth-of-type(1) > a:focus:nth-of-type(3)"
+press-key: "ArrowUp"
+assert: ".search-results.active > a:focus:nth-of-type(2)"
+press-key: "ArrowUp"
+assert: ".search-results.active > a:focus:nth-of-type(1)"
+press-key: "ArrowUp"
+assert: ".search-input:focus"
+press-key: "ArrowDown"
+assert: ".search-results.active > a:focus:nth-of-type(1)"
diff --git a/tests/rustdoc-gui/search-no-result.goml b/tests/rustdoc-gui/search-no-result.goml
new file mode 100644
index 000000000..b76a44fa9
--- /dev/null
+++ b/tests/rustdoc-gui/search-no-result.goml
@@ -0,0 +1,36 @@
+// The goal of this test is to check the color of the "no result" links.
+goto: "file://" + |DOC_PATH| + "/lib2/index.html?search=sdkfskjfsdks"
+show-text: true
+
+define-function: (
+ "check-no-result",
+ (theme, link, link_hover),
+ block {
+ // Changing theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ wait-for: "#results"
+ assert: ".search-failed.active"
+ assert-css: ("#results a", {"color": |link|}, ALL)
+ move-cursor-to: "#results a"
+ assert-css: ("#results a:hover", {"color": |link_hover|})
+ // Moving the cursor to some other place to not create issues with next function run.
+ move-cursor-to: ".search-input"
+ },
+)
+
+call-function: ("check-no-result", {
+ "theme": "ayu",
+ "link": "rgb(57, 175, 215)",
+ "link_hover": "rgb(57, 175, 215)",
+})
+call-function: ("check-no-result", {
+ "theme": "dark",
+ "link": "rgb(210, 153, 29)",
+ "link_hover": "rgb(210, 153, 29)",
+})
+call-function: ("check-no-result", {
+ "theme": "light",
+ "link": "rgb(56, 115, 173)",
+ "link_hover": "rgb(56, 115, 173)",
+})
diff --git a/tests/rustdoc-gui/search-reexport.goml b/tests/rustdoc-gui/search-reexport.goml
new file mode 100644
index 000000000..dd19f03bd
--- /dev/null
+++ b/tests/rustdoc-gui/search-reexport.goml
@@ -0,0 +1,33 @@
+// Checks that the reexports are present in the search index, can have
+// doc aliases and are highligted when their ID is the hash of the page.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"}
+reload:
+// First we check that the reexport has the correct ID and no background color.
+assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;")
+assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
+write: (".search-input", "TheStdReexport")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+wait-for: "//a[@class='result-import']"
+assert-attribute: (
+ "//a[@class='result-import']",
+ {"href": "../test_docs/index.html#reexport.TheStdReexport"},
+)
+assert-text: ("//a[@class='result-import']", "test_docs::TheStdReexport")
+click: "//a[@class='result-import']"
+// We check that it has the background modified thanks to the focus.
+wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"})
+
+// We now check that the alias is working as well on the reexport.
+// To be SURE that the search will be run.
+press-key: 'Enter'
+write: (".search-input", "AliasForTheStdReexport")
+wait-for: "//a[@class='result-import']"
+assert-text: (
+ "//a[@class='result-import']",
+ "AliasForTheStdReexport - see test_docs::TheStdReexport",
+)
+// Same thing again, we click on it to ensure the background is once again set as expected.
+click: "//a[@class='result-import']"
+wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"})
diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml
new file mode 100644
index 000000000..d6d54ec4b
--- /dev/null
+++ b/tests/rustdoc-gui/search-result-color.goml
@@ -0,0 +1,401 @@
+// The goal of this test is to ensure the color of the text is the one expected.
+
+define-function: (
+ "check-result-color",
+ (result_kind, color, hover_color),
+ block {
+ assert-css: (".result-" + |result_kind| + " ." + |result_kind|, {"color": |color|}, ALL)
+ assert-css: (
+ ".result-" + |result_kind|,
+ {"color": |entry_color|, "background-color": |background_color|},
+ )
+ move-cursor-to: ".result-" + |result_kind|
+ assert-css: (
+ ".result-" + |result_kind| + ":hover",
+ {"color": |hover_entry_color|, "background-color": |hover_background_color|},
+ )
+ assert-css: (
+ ".result-" + |result_kind| + ":hover ." + |result_kind|,
+ {"color": |hover_color|},
+ )
+ move-cursor-to: ".search-input"
+ focus: ".result-" + |result_kind|
+ assert-css: (
+ ".result-" + |result_kind| + ":focus",
+ {"color": |hover_entry_color|, "background-color": |hover_background_color|},
+ )
+ assert-css: (
+ ".result-" + |result_kind| + ":focus ." + |result_kind|,
+ {"color": |hover_color|},
+ )
+ },
+)
+
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=coo"
+
+// This is needed so that the text color is computed.
+show-text: true
+
+// Ayu theme
+local-storage: {
+ "rustdoc-theme": "ayu",
+ "rustdoc-use-system-theme": "false",
+}
+reload:
+
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-css: (
+ "#search-tabs > button > .count",
+ {"color": "rgb(136, 136, 136)"},
+ ALL,
+)
+assert-css: (
+ "//*[@class='desc'][text()='Just a normal struct.']",
+ {"color": "rgb(197, 197, 197)"},
+)
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']",
+ {"color": "rgb(0, 150, 207)"},
+)
+
+// Checking the color of the bottom border.
+assert-css: (
+ ".search-results > a",
+ {"border-bottom-color": "rgba(170, 170, 170, 0.2)"}
+)
+
+// Checking the color of "keyword" text.
+assert-css: (
+ "//*[@class='result-name']//*[text()='(keyword)']",
+ {"color": "rgb(120, 135, 151)"},
+)
+
+store-value: (entry_color, "rgb(0, 150, 207)") // color of the search entry
+store-value: (hover_entry_color, "rgb(255, 255, 255)") // color of the hovered/focused search entry
+store-value: (background_color, "rgba(0, 0, 0, 0)") // background color
+store-value: (hover_background_color, "rgb(60, 60, 60)") // hover background color
+
+call-function: (
+ "check-result-color", (
+ "keyword", // item kind
+ "rgb(57, 175, 215)", // color of item kind
+ "rgb(57, 175, 215)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "struct", // item kind
+ "rgb(255, 160, 165)", // color of item kind
+ "rgb(255, 160, 165)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "associatedtype", // item kind
+ "rgb(57, 175, 215)", // color of item kind
+ "rgb(57, 175, 215)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "tymethod", // item kind
+ "rgb(253, 214, 135)", // color of item kind
+ "rgb(253, 214, 135)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "method", // item kind
+ "rgb(253, 214, 135)", // color of item kind
+ "rgb(253, 214, 135)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "structfield", // item kind
+ "rgb(0, 150, 207)", // color of item kind
+ "rgb(255, 255, 255)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "macro", // item kind
+ "rgb(163, 122, 204)", // color of item kind
+ "rgb(163, 122, 204)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "fn", // item kind
+ "rgb(253, 214, 135)", // color of item kind
+ "rgb(253, 214, 135)", // color of hovered/focused item kind
+ ),
+)
+
+// Checking the `<a>` container.
+move-cursor-to: ".search-input"
+focus: ".search-input" // To ensure the `<a>` container isnt focus or hover.
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(0, 150, 207)", "background-color": "rgba(0, 0, 0, 0)"},
+ ALL,
+)
+
+// Checking color and background on hover.
+move-cursor-to: "//*[@class='desc'][text()='Just a normal struct.']"
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']",
+ {"color": "rgb(255, 255, 255)"},
+)
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(255, 255, 255)", "background-color": "rgb(60, 60, 60)"},
+)
+
+// Dark theme
+local-storage: {
+ "rustdoc-theme": "dark",
+ "rustdoc-use-system-theme": "false",
+}
+reload:
+
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-css: (
+ "#search-tabs > button > .count",
+ {"color": "rgb(136, 136, 136)"},
+ ALL,
+)
+assert-css: (
+ "//*[@class='desc'][text()='Just a normal struct.']",
+ {"color": "rgb(221, 221, 221)"},
+)
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']",
+ {"color": "rgb(221, 221, 221)"},
+)
+
+// Checking the color of the bottom border.
+assert-css: (
+ ".search-results > a",
+ {"border-bottom-color": "rgba(170, 170, 170, 0.2)"}
+)
+
+// Checking the color for "keyword" text.
+assert-css: (
+ "//*[@class='result-name']//*[text()='(keyword)']",
+ {"color": "rgb(221, 221, 221)"},
+)
+
+store-value: (entry_color, "rgb(221, 221, 221)") // color of the search entry
+store-value: (hover_entry_color, "rgb(221, 221, 221)") // color of the hovered/focused search entry
+store-value: (background_color, "rgba(0, 0, 0, 0)") // background color
+store-value: (hover_background_color, "rgb(97, 97, 97)") // hover background color
+
+call-function: (
+ "check-result-color", (
+ "keyword", // item kind
+ "rgb(210, 153, 29)", // color of item kind
+ "rgb(210, 153, 29)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "struct", // item kind
+ "rgb(45, 191, 184)", // color of item kind
+ "rgb(45, 191, 184)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "associatedtype", // item kind
+ "rgb(210, 153, 29)", // color of item kind
+ "rgb(210, 153, 29)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "tymethod", // item kind
+ "rgb(43, 171, 99)", // color of item kind
+ "rgb(43, 171, 99)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "method", // item kind
+ "rgb(43, 171, 99)", // color of item kind
+ "rgb(43, 171, 99)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "structfield", // item kind
+ "rgb(221, 221, 221)", // color of item kind
+ "rgb(221, 221, 221)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "macro", // item kind
+ "rgb(9, 189, 0)", // color of item kind
+ "rgb(9, 189, 0)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "fn", // item kind
+ "rgb(43, 171, 99)", // color of item kind
+ "rgb(43, 171, 99)", // color of hovered/focused item kind
+ ),
+)
+
+// Checking the `<a>` container.
+move-cursor-to: ".search-input"
+focus: ".search-input" // To ensure the `<a>` container isnt focus or hover.
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(221, 221, 221)", "background-color": "rgba(0, 0, 0, 0)"},
+)
+
+// Light theme
+local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"}
+reload:
+
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-css: (
+ "#search-tabs > button > .count",
+ {"color": "rgb(136, 136, 136)"},
+ ALL,
+)
+assert-css: (
+ "//*[@class='desc'][text()='Just a normal struct.']",
+ {"color": "rgb(0, 0, 0)"},
+)
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']",
+ {"color": "rgb(0, 0, 0)"},
+)
+
+// Checking the color of the bottom border.
+assert-css: (
+ ".search-results > a",
+ {"border-bottom-color": "rgba(170, 170, 170, 0.2)"}
+)
+
+// Checking the color for "keyword" text.
+assert-css: (
+ "//*[@class='result-name']//*[text()='(keyword)']",
+ {"color": "rgb(0, 0, 0)"},
+)
+
+store-value: (entry_color, "rgb(0, 0, 0)") // color of the search entry
+store-value: (hover_entry_color, "rgb(0, 0, 0)") // color of the hovered/focused search entry
+store-value: (background_color, "rgba(0, 0, 0, 0)") // background color
+store-value: (hover_background_color, "rgb(204, 204, 204)") // hover background color
+
+call-function: (
+ "check-result-color", (
+ "keyword", // item kind
+ "rgb(56, 115, 173)", // color of item kind
+ "rgb(56, 115, 173)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "struct", // item kind
+ "rgb(173, 55, 138)", // color of item kind
+ "rgb(173, 55, 138)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "associatedtype", // item kind
+ "rgb(56, 115, 173)", // color of item kind
+ "rgb(56, 115, 173)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "tymethod", // item kind
+ "rgb(173, 124, 55)", // color of item kind
+ "rgb(173, 124, 55)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "method", // item kind
+ "rgb(173, 124, 55)", // color of item kind
+ "rgb(173, 124, 55)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "structfield", // item kind
+ "rgb(0, 0, 0)", // color of item kind
+ "rgb(0, 0, 0)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "macro", // item kind
+ "rgb(6, 128, 0)", // color of item kind
+ "rgb(6, 128, 0)", // color of hovered/focused item kind
+ ),
+)
+call-function: (
+ "check-result-color", (
+ "fn", // item kind
+ "rgb(173, 124, 55)", // color of item kind
+ "rgb(173, 124, 55)", // color of hovered/focused item kind
+ ),
+)
+
+// Checking the `<a>` container.
+move-cursor-to: ".search-input"
+focus: ".search-input" // To ensure the `<a>` container isnt focus or hover.
+assert-css: (
+ "//*[@class='result-name']/*[text()='test_docs::']/ancestor::a",
+ {"color": "rgb(0, 0, 0)", "background-color": "rgba(0, 0, 0, 0)"},
+)
+
+// Check the alias.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// If the text isn't displayed, the browser doesn't compute color style correctly...
+show-text: true
+
+define-function: (
+ "check-alias",
+ (theme, alias, grey),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ write: (".search-input", "thisisanalias")
+ // To be SURE that the search will be run.
+ press-key: 'Enter'
+ // Waiting for the search results to appear...
+ wait-for: "#search-tabs"
+ // Checking that the colors for the alias element are the ones expected.
+ assert-css: (".result-name > .alias", {"color": |alias|})
+ assert-css: (".result-name > .alias > .grey", {"color": |grey|})
+ // Leave the search results to prevent reloading with an already filled search input.
+ press-key: "Escape"
+ },
+)
+
+call-function: ("check-alias", {
+ "theme": "ayu",
+ "alias": "rgb(197, 197, 197)",
+ "grey": "rgb(153, 153, 153)",
+})
+call-function: ("check-alias", {
+ "theme": "dark",
+ "alias": "rgb(255, 255, 255)",
+ "grey": "rgb(204, 204, 204)",
+})
+call-function: ("check-alias", {
+ "theme": "light",
+ "alias": "rgb(0, 0, 0)",
+ "grey": "rgb(153, 153, 153)",
+})
diff --git a/tests/rustdoc-gui/search-result-description.goml b/tests/rustdoc-gui/search-result-description.goml
new file mode 100644
index 000000000..9fa210804
--- /dev/null
+++ b/tests/rustdoc-gui/search-result-description.goml
@@ -0,0 +1,5 @@
+// This test is to ensure that the codeblocks are correctly rendered in the search results.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=some_more_function"
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-text: (".search-results .desc code", "format!")
diff --git a/tests/rustdoc-gui/search-result-display.goml b/tests/rustdoc-gui/search-result-display.goml
new file mode 100644
index 000000000..43e608228
--- /dev/null
+++ b/tests/rustdoc-gui/search-result-display.goml
@@ -0,0 +1,78 @@
+// Checks that the search results have the expected width.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+size: (900, 1000)
+write: (".search-input", "test")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+wait-for: "#crate-search"
+// The width is returned by "getComputedStyle" which returns the exact number instead of the
+// CSS rule which is "50%"...
+assert-css: (".search-results div.desc", {"width": "310px"})
+size: (600, 100)
+// As counter-intuitive as it may seem, in this width, the width is "100%", which is why
+// when computed it's larger.
+assert-css: (".search-results div.desc", {"width": "566px"})
+
+// The result set is all on one line.
+assert-css: (".search-results .result-name > span", {"display": "inline"})
+
+// Check that the crate filter `<select>` is correctly handled when it goes to next line.
+// To do so we need to update the length of one of its `<option>`.
+size: (900, 900)
+
+// First we check the current width, height and position.
+assert-css: ("#crate-search", {"width": "223px"})
+assert-css: (".search-results-title", {"height": "44px", "width": "640px"})
+assert-css: ("#search", {"width": "640px"})
+
+// Then we update the text of one of the `<option>`.
+text: (
+ "#crate-search option",
+ "sdjfaksdjfaksjdbfkadsbfkjsadbfkdsbkfbsadkjfbkdsabfkadsfkjdsafa",
+)
+
+// Then we compare again to confirm the height didn't change.
+assert-css: ("#crate-search", {"width": "527px"})
+assert-css: (".search-results-title", {"height": "44px", "width": "640px"})
+// And we check that the `<select>` isn't bigger than its container (".search-results-title").
+assert-css: ("#search", {"width": "640px"})
+
+// Now checking that the crate filter is working as expected too.
+show-text: true
+define-function: (
+ "check-filter",
+ (theme, border, filter, hover_border, hover_filter),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ wait-for: "#crate-search"
+ assert-css: ("#crate-search", {"border": "1px solid " + |border|})
+ assert-css: ("#crate-search-div::after", {"filter": |filter|})
+ move-cursor-to: "#crate-search"
+ assert-css: ("#crate-search", {"border": "1px solid " + |hover_border|})
+ assert-css: ("#crate-search-div::after", {"filter": |hover_filter|})
+ move-cursor-to: ".search-input"
+ },
+)
+
+call-function: ("check-filter", {
+ "theme": "ayu",
+ "border": "rgb(92, 103, 115)",
+ "filter": "invert(0.41) sepia(0.12) saturate(4.87) hue-rotate(171deg) brightness(0.94) contrast(0.94)",
+ "hover_border": "rgb(224, 224, 224)",
+ "hover_filter": "invert(0.98) sepia(0.12) saturate(0.81) hue-rotate(343deg) brightness(1.13) contrast(0.76)",
+})
+call-function: ("check-filter", {
+ "theme": "dark",
+ "border": "rgb(224, 224, 224)",
+ "filter": "invert(0.94) sepia(0) saturate(7.21) hue-rotate(255deg) brightness(0.9) contrast(0.9)",
+ "hover_border": "rgb(33, 150, 243)",
+ "hover_filter": "invert(0.69) sepia(0.6) saturate(66.13) hue-rotate(184deg) brightness(1) contrast(0.91)",
+})
+call-function: ("check-filter", {
+ "theme": "light",
+ "border": "rgb(224, 224, 224)",
+ "filter": "invert(1) sepia(0) saturate(42.23) hue-rotate(289deg) brightness(1.14) contrast(0.76)",
+ "hover_border": "rgb(113, 113, 113)",
+ "hover_filter": "invert(0.44) sepia(0.18) saturate(0.23) hue-rotate(317deg) brightness(0.96) contrast(0.93)",
+})
diff --git a/tests/rustdoc-gui/search-result-go-to-first.goml b/tests/rustdoc-gui/search-result-go-to-first.goml
new file mode 100644
index 000000000..f444baa6c
--- /dev/null
+++ b/tests/rustdoc-gui/search-result-go-to-first.goml
@@ -0,0 +1,19 @@
+// This test ensures that the "go_to_first" feature is working as expected.
+
+// First, we check that the first page doesn't have the string we're looking for to ensure
+// that the feature is changing page as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+assert-text-false: (".main-heading h1", "Struct test_docs::Foo")
+
+// We now check that we land on the search result page if "go_to_first" isn't set.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo"
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-text-false: (".main-heading h1", "Struct test_docs::Foo")
+// Ensure that the search results are displayed, not the "normal" content.
+assert-css: ("#main-content", {"display": "none"})
+
+// Now we can check that the feature is working as expected!
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo&go_to_first=true"
+// Waiting for the page to load...
+wait-for-text: (".main-heading h1", "Struct test_docs::Foo")
diff --git a/tests/rustdoc-gui/search-result-keyword.goml b/tests/rustdoc-gui/search-result-keyword.goml
new file mode 100644
index 000000000..8c3577d9f
--- /dev/null
+++ b/tests/rustdoc-gui/search-result-keyword.goml
@@ -0,0 +1,13 @@
+// Checks that the "keyword" results have the expected text alongside them.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "CookieMonster")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+// Note: The two next assert commands could be merged as one but readability would be
+// less good.
+//
+// Checking that the CSS is displaying " (keyword)" in italic.
+assert-text: (".result-name span.keyword > i", "(keyword)")
+assert-text: (".result-name span.keyword", "CookieMonster (keyword)")
diff --git a/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml
new file mode 100644
index 000000000..1433dc4d7
--- /dev/null
+++ b/tests/rustdoc-gui/search-tab-change-title-fn-sig.goml
@@ -0,0 +1,74 @@
+// Checks that the search tab results work correctly with function signature syntax
+// First, try a search-by-name
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "Foo")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Names", STARTS_WITH)
+assert: "input.search-input:focus"
+// Use left-right keys
+press-key: "ArrowDown"
+assert: "#results > .search-results.active > a:nth-of-type(1):focus"
+press-key: "ArrowRight"
+wait-for-attribute: ("#search-tabs > button:nth-of-type(2)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowLeft"
+wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected"})
+
+// Now try search-by-return
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "-> String")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
+assert: "input.search-input:focus"
+// Use left-right keys
+press-key: "ArrowDown"
+assert: "#results > .search-results.active > a:nth-of-type(1):focus"
+press-key: "ArrowRight"
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowLeft"
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+
+// Try with a search-by-return with no results
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "-> Something")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
+
+// Try with a search-by-parameter
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "usize pattern")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH)
+
+// Try with a search-by-parameter-and-return
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+write: (".search-input", "pattern -> str")
+// To be SURE that the search will be run.
+press-key: 'Enter'
+// Waiting for the search results to appear...
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH)
diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml
new file mode 100644
index 000000000..36958f700
--- /dev/null
+++ b/tests/rustdoc-gui/search-tab.goml
@@ -0,0 +1,76 @@
+// Checking the colors of the search tab headers.
+goto: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html?search=something"
+show-text: true
+
+define-function: (
+ "check-colors",
+ (theme, background, background_selected, background_hover, border_bottom,
+ border_bottom_selected, border_bottom_hover, border_top, border_top_selected,
+ border_top_hover),
+ block {
+ // Setting the theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+
+ // These two commands are used to be sure the search will be run.
+ focus: ".search-input"
+ press-key: "Enter"
+
+ wait-for: "#search-tabs"
+ assert-css: ("#search-tabs > button:not(.selected)", {
+ "background-color": |background|,
+ "border-bottom": |border_bottom|,
+ "border-top": |border_top|,
+ })
+ assert-css: ("#search-tabs > button.selected", {
+ "background-color": |background_selected|,
+ "border-bottom": |border_bottom_selected|,
+ "border-top": |border_top_selected|,
+ })
+ move-cursor-to: "#search-tabs > button:not(.selected)"
+ assert-css: ("#search-tabs > button:not(.selected):hover", {
+ "background-color": |background_hover|,
+ "border-bottom": |border_bottom_hover|,
+ "border-top": |border_top_hover|,
+ })
+ // To prevent disrupting next run of this function.
+ move-cursor-to: ".search-input"
+ },
+)
+
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "background": "rgba(0, 0, 0, 0)",
+ "background_selected": "rgb(20, 25, 32)",
+ "background_hover": "rgba(0, 0, 0, 0)",
+ "border_bottom": "0px none rgb(197, 197, 197)",
+ "border_bottom_selected": "1px solid rgb(255, 180, 76)",
+ "border_bottom_hover": "1px solid rgba(242, 151, 24, 0.3)",
+ "border_top": "0px none rgb(197, 197, 197)",
+ "border_top_selected": "0px none rgb(197, 197, 197)",
+ "border_top_hover": "0px none rgb(197, 197, 197)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "background": "rgb(37, 37, 37)",
+ "background_selected": "rgb(53, 53, 53)",
+ "background_hover": "rgb(53, 53, 53)",
+ "border_bottom": "0px none rgb(221, 221, 221)",
+ "border_bottom_selected": "0px none rgb(221, 221, 221)",
+ "border_bottom_hover": "0px none rgb(221, 221, 221)",
+ "border_top": "2px solid rgb(37, 37, 37)",
+ "border_top_selected": "2px solid rgb(0, 137, 255)",
+ "border_top_hover": "2px solid rgb(0, 137, 255)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "background": "rgb(230, 230, 230)",
+ "background_selected": "rgb(255, 255, 255)",
+ "background_hover": "rgb(255, 255, 255)",
+ "border_bottom": "0px none rgb(0, 0, 0)",
+ "border_bottom_selected": "0px none rgb(0, 0, 0)",
+ "border_bottom_hover": "0px none rgb(0, 0, 0)",
+ "border_top": "2px solid rgb(230, 230, 230)",
+ "border_top_selected": "2px solid rgb(0, 137, 255)",
+ "border_top_hover": "2px solid rgb(0, 137, 255)",
+})
diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml
new file mode 100644
index 000000000..419cc5eba
--- /dev/null
+++ b/tests/rustdoc-gui/settings.goml
@@ -0,0 +1,253 @@
+// This test ensures that the settings menu display is working as expected and that
+// the settings page is also rendered as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+show-text: true // needed when we check for colors below.
+// First, we check that the settings page doesn't exist.
+assert-false: "#settings"
+// We now click on the settings button.
+click: "#settings-menu"
+wait-for: "#settings"
+assert-css: ("#settings", {"display": "block"})
+
+// Store the line margin to compare with the settings.html later.
+store-css: (setting_line_margin, ".setting-line", "margin")
+
+// Let's close it by clicking on the same button.
+click: "#settings-menu"
+wait-for-css: ("#settings", {"display": "none"})
+
+// Let's check that pressing "ESCAPE" is closing it.
+click: "#settings-menu"
+wait-for-css: ("#settings", {"display": "block"})
+press-key: "Escape"
+wait-for-css: ("#settings", {"display": "none"})
+
+// Let's click on it when the search results are displayed.
+focus: ".search-input"
+write: "test"
+// To be SURE that the search will be run.
+press-key: 'Enter'
+wait-for: "#alternative-display #search"
+click: "#settings-menu"
+wait-for-css: ("#settings", {"display": "block"})
+// Ensure that the search is still displayed.
+wait-for: "#alternative-display #search"
+assert: "#main-content.hidden"
+
+// Now let's check the content of the settings menu.
+local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"}
+reload:
+click: "#settings-menu"
+wait-for: "#settings"
+
+// We check that the "Use system theme" is disabled.
+assert-property: ("#theme-system-preference", {"checked": "false"})
+// Meaning that only the "theme" menu is showing up.
+assert: ".setting-line:not(.hidden) #theme"
+assert: ".setting-line.hidden #preferred-dark-theme"
+assert: ".setting-line.hidden #preferred-light-theme"
+
+// We check that the correct theme is selected.
+assert-property: ("#theme .choices #theme-dark", {"checked": "true"})
+
+// Some style checks...
+move-cursor-to: "#settings-menu > a"
+// First we check the "default" display for radio buttons.
+assert-css: (
+ "#theme-dark",
+ {
+ "border-color": "rgb(221, 221, 221)",
+ "box-shadow": "rgb(53, 53, 53) 0px 0px 0px 3px inset",
+ },
+)
+assert-css: ("#theme-light", {"border-color": "rgb(221, 221, 221)", "box-shadow": "none"})
+// Let's start with the hover for radio buttons.
+move-cursor-to: "#theme-dark"
+assert-css: (
+ "#theme-dark",
+ {
+ "border-color": "rgb(33, 150, 243)",
+ "box-shadow": "rgb(53, 53, 53) 0px 0px 0px 3px inset",
+ },
+)
+move-cursor-to: "#theme-light"
+assert-css: ("#theme-light", {"border-color": "rgb(33, 150, 243)", "box-shadow": "none"})
+move-cursor-to: "#theme-ayu"
+// Let's now check with the focus for radio buttons.
+focus: "#theme-dark"
+assert-css: (
+ "#theme-dark",
+ {
+ "border-color": "rgb(221, 221, 221)",
+ "box-shadow": "rgb(53, 53, 53) 0px 0px 0px 3px inset, rgb(33, 150, 243) 0px 0px 2px 2px",
+ },
+)
+focus: "#theme-light"
+assert-css: (
+ "#theme-light",
+ {
+ "border-color": "rgb(221, 221, 221)",
+ "box-shadow": "rgb(33, 150, 243) 0px 0px 1px 1px",
+ },
+)
+// Now we check we both focus and hover for radio buttons.
+move-cursor-to: "#theme-dark"
+focus: "#theme-dark"
+assert-css: (
+ "#theme-dark",
+ {
+ "border-color": "rgb(33, 150, 243)",
+ "box-shadow": "rgb(53, 53, 53) 0px 0px 0px 3px inset, rgb(33, 150, 243) 0px 0px 2px 2px",
+ },
+)
+move-cursor-to: "#theme-light"
+focus: "#theme-light"
+assert-css: (
+ "#theme-light",
+ {
+ "border-color": "rgb(33, 150, 243)",
+ "box-shadow": "rgb(33, 150, 243) 0px 0px 1px 1px",
+ },
+)
+// Now we check the setting-name for radio buttons is on a different line than the label.
+compare-elements-position-near: (
+ "#theme .setting-name",
+ "#theme .choices",
+ {"x": 1}
+)
+compare-elements-position-near-false: (
+ "#theme .setting-name",
+ "#theme .choices",
+ {"y": 1}
+)
+// Now we check that the label positions are all on the same line.
+compare-elements-position-near: (
+ "#theme .choices #theme-light",
+ "#theme .choices #theme-dark",
+ {"y": 1}
+)
+compare-elements-position-near: (
+ "#theme .choices #theme-dark",
+ "#theme .choices #theme-ayu",
+ {"y": 1}
+)
+compare-elements-position-near: (
+ "#theme .choices #theme-ayu",
+ "#theme .choices #theme-system-preference",
+ {"y": 1}
+)
+
+// First we check the "default" display for toggles.
+assert-css: (
+ "#auto-hide-large-items",
+ {
+ "background-color": "rgb(33, 150, 243)",
+ "border-color": "rgb(221, 221, 221)",
+ },
+)
+// Let's start with the hover for toggles.
+move-cursor-to: "#auto-hide-large-items"
+assert-css: (
+ "#auto-hide-large-items",
+ {
+ "background-color": "rgb(33, 150, 243)",
+ "border-color": "rgb(33, 150, 243)",
+ },
+)
+move-cursor-to: "#settings-menu > a"
+// Let's now check with the focus for toggles.
+focus: "#auto-hide-large-items"
+assert-css: (
+ "#auto-hide-large-items",
+ {
+ "background-color": "rgb(33, 150, 243)",
+ "border-color": "rgb(221, 221, 221)",
+ "box-shadow": "rgb(33, 150, 243) 0px 0px 1px 1px",
+ },
+)
+// Now we check we both focus and hover for toggles.
+move-cursor-to: "#auto-hide-large-items"
+focus: "#auto-hide-large-items"
+assert-css: (
+ "#auto-hide-large-items",
+ {
+ "background-color": "rgb(33, 150, 243)",
+ "border-color": "rgb(33, 150, 243)",
+ "box-shadow": "rgb(33, 150, 243) 0px 0px 1px 1px",
+ },
+)
+
+// We now switch the display.
+click: "#theme-system-preference"
+// Wait for the hidden element to show up.
+wait-for: ".setting-line:not(.hidden) #preferred-dark-theme"
+assert: ".setting-line:not(.hidden) #preferred-light-theme"
+
+// We check their text as well.
+assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme")
+assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme")
+
+// We now check that clicking on the toggles' text is like clicking on the checkbox.
+// To test it, we use the "Disable keyboard shortcuts".
+local-storage: {"rustdoc-disable-shortcuts": "false"}
+click: ".setting-line:last-child .settings-toggle .label"
+assert-local-storage: {"rustdoc-disable-shortcuts": "true"}
+
+// Make sure that "Disable keyboard shortcuts" actually took effect.
+press-key: "Escape"
+press-key: "?"
+assert-false: "#help-button .popover"
+wait-for-css: ("#settings-menu .popover", {"display": "block"})
+
+// Now turn keyboard shortcuts back on, and see if they work.
+click: ".setting-line:last-child .settings-toggle .label"
+assert-local-storage: {"rustdoc-disable-shortcuts": "false"}
+press-key: "Escape"
+press-key: "?"
+wait-for-css: ("#help-button .popover", {"display": "block"})
+assert-css: ("#settings-menu .popover", {"display": "none"})
+
+// Now switch back to the settings popover, and make sure the keyboard
+// shortcut works when a check box is selected.
+click: "#settings-menu > a"
+wait-for-css: ("#settings-menu .popover", {"display": "block"})
+focus: "#auto-hide-large-items"
+press-key: "?"
+wait-for-css: ("#settings-menu .popover", {"display": "none"})
+wait-for-css: ("#help-button .popover", {"display": "block"})
+
+// Now switch back to the settings popover, and make sure the keyboard
+// shortcut works when a check box is selected.
+click: "#settings-menu > a"
+wait-for-css: ("#settings-menu .popover", {"display": "block"})
+wait-for-css: ("#help-button .popover", {"display": "none"})
+focus: "#theme-system-preference"
+press-key: "?"
+wait-for-css: ("#settings-menu .popover", {"display": "none"})
+wait-for-css: ("#help-button .popover", {"display": "block"})
+
+// Now we go to the settings page to check that the CSS is loaded as expected.
+goto: "file://" + |DOC_PATH| + "/settings.html"
+wait-for: "#settings"
+assert-css: (".setting-line", {"position": "relative"})
+
+assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS)
+compare-elements-position: (".sub form", "#settings", ("x"))
+
+// Check that setting-line has the same margin in this mode as in the popover.
+assert-css: (".setting-line", {"margin": |setting_line_margin|})
+
+// We now check the display with JS disabled.
+assert-false: "noscript section"
+javascript: false
+reload:
+assert-css: ("noscript section", {"display": "block"})
+javascript: true
+
+// Check for the display on small screen
+show-text: true
+reload:
+size: (300, 1000)
+click: "#settings-menu"
+wait-for: "#settings"
+assert-css: (".setting-line", {"position": "relative"})
diff --git a/tests/rustdoc-gui/shortcuts.goml b/tests/rustdoc-gui/shortcuts.goml
new file mode 100644
index 000000000..9068680d6
--- /dev/null
+++ b/tests/rustdoc-gui/shortcuts.goml
@@ -0,0 +1,31 @@
+// Check that the various shortcuts are working.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// We first check that the search input isn't already focused.
+assert-false: "input.search-input:focus"
+press-key: "s"
+assert: "input.search-input:focus"
+press-key: "Escape"
+assert-false: "input.search-input:focus"
+// We now check for the help popup.
+press-key: "?"
+assert-css: ("#help-button .popover", {"display": "block"})
+press-key: "Escape"
+assert-css: ("#help-button .popover", {"display": "none"})
+// Checking doc collapse and expand.
+// It should be displaying a "-":
+assert-text: ("#toggle-all-docs", "[\u2212]")
+press-key: "-"
+wait-for-text: ("#toggle-all-docs", "[+]")
+assert-attribute: ("#toggle-all-docs", {"class": "will-expand"})
+// Pressing it again shouldn't do anything.
+press-key: "-"
+assert-text: ("#toggle-all-docs", "[+]")
+assert-attribute: ("#toggle-all-docs", {"class": "will-expand"})
+// Expanding now.
+press-key: "+"
+wait-for-text: ("#toggle-all-docs", "[\u2212]")
+assert-attribute: ("#toggle-all-docs", {"class": ""})
+// Pressing it again shouldn't do anything.
+press-key: "+"
+assert-text: ("#toggle-all-docs", "[\u2212]")
+assert-attribute: ("#toggle-all-docs", {"class": ""})
diff --git a/tests/rustdoc-gui/sidebar-links-color.goml b/tests/rustdoc-gui/sidebar-links-color.goml
new file mode 100644
index 000000000..1d5fdb7a4
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-links-color.goml
@@ -0,0 +1,171 @@
+// This test checks links colors in sidebar before and after hover.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+
+// This is needed so that the text color is computed.
+show-text: true
+
+define-function: (
+ "check-colors",
+ (
+ theme, struct, struct_hover, struct_hover_background, enum, enum_hover,
+ enum_hover_background, union, union_hover, union_hover_background, trait, trait_hover,
+ trait_hover_background, fn, fn_hover, fn_hover_background, type, type_hover,
+ type_hover_background, keyword, keyword_hover, keyword_hover_background,
+ ),
+ block {
+ local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false" }
+ reload:
+ // Struct
+ assert-css: (
+ ".sidebar .block.struct a:not(.current)",
+ {"color": |struct|, "background-color": "rgba(0, 0, 0, 0)"},
+ )
+ move-cursor-to: ".sidebar .block.struct a:not(.current)"
+ assert-css: (
+ ".sidebar .block.struct a:hover",
+ {"color": |struct_hover|, "background-color": |struct_hover_background|},
+ )
+ // Enum
+ assert-css: (
+ ".sidebar .block.enum a",
+ {"color": |enum|, "background-color": "rgba(0, 0, 0, 0)"},
+ )
+ move-cursor-to: ".sidebar .block.enum a"
+ assert-css: (
+ ".sidebar .block.enum a:hover",
+ {"color": |enum_hover|, "background-color": |enum_hover_background|},
+ )
+ // Union
+ assert-css: (
+ ".sidebar .block.union a",
+ {"color": |union|, "background-color": "rgba(0, 0, 0, 0)"},
+ )
+ move-cursor-to: ".sidebar .block.union a"
+ assert-css: (
+ ".sidebar .block.union a:hover",
+ {"color": |union_hover|, "background-color": |union_hover_background|},
+ )
+ // Trait
+ assert-css: (
+ ".sidebar .block.trait a",
+ {"color": |trait|, "background-color": "rgba(0, 0, 0, 0)"},
+ )
+ move-cursor-to: ".sidebar .block.trait a"
+ assert-css: (
+ ".sidebar .block.trait a:hover",
+ {"color": |trait_hover|, "background-color": |trait_hover_background|},
+ )
+ // Function
+ assert-css: (
+ ".sidebar .block.fn a",
+ {"color": |fn|, "background-color": "rgba(0, 0, 0, 0)"},
+ )
+ move-cursor-to: ".sidebar .block.fn a"
+ assert-css: (
+ ".sidebar .block.fn a:hover",
+ {"color": |fn_hover|, "background-color": |fn_hover_background|},
+ )
+ // Type definition
+ assert-css: (
+ ".sidebar .block.type a",
+ {"color": |type|, "background-color": "rgba(0, 0, 0, 0)"},
+ )
+ move-cursor-to: ".sidebar .block.type a"
+ assert-css: (
+ ".sidebar .block.type a:hover",
+ {"color": |type_hover|, "background-color": |type_hover_background|},
+ )
+ // Keyword
+ assert-css: (
+ ".sidebar .block.keyword a",
+ {"color": |keyword|, "background-color": "rgba(0, 0, 0, 0)"},
+ )
+ move-cursor-to: ".sidebar .block.keyword a"
+ assert-css: (
+ ".sidebar .block.keyword a:hover",
+ {"color": |keyword_hover|, "background-color": |keyword_hover_background|},
+ )
+ }
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "struct": "rgb(83, 177, 219)",
+ "struct_hover": "rgb(255, 180, 76)",
+ "struct_hover_background": "rgba(0, 0, 0, 0)",
+ "enum": "rgb(83, 177, 219)",
+ "enum_hover": "rgb(255, 180, 76)",
+ "enum_hover_background": "rgba(0, 0, 0, 0)",
+ "union": "rgb(83, 177, 219)",
+ "union_hover": "rgb(255, 180, 76)",
+ "union_hover_background": "rgba(0, 0, 0, 0)",
+ "trait": "rgb(83, 177, 219)",
+ "trait_hover": "rgb(255, 180, 76)",
+ "trait_hover_background": "rgba(0, 0, 0, 0)",
+ "fn": "rgb(83, 177, 219)",
+ "fn_hover": "rgb(255, 180, 76)",
+ "fn_hover_background": "rgba(0, 0, 0, 0)",
+ "type": "rgb(83, 177, 219)",
+ "type_hover": "rgb(255, 180, 76)",
+ "type_hover_background": "rgba(0, 0, 0, 0)",
+ "keyword": "rgb(83, 177, 219)",
+ "keyword_hover": "rgb(255, 180, 76)",
+ "keyword_hover_background": "rgba(0, 0, 0, 0)",
+ }
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "struct": "rgb(253, 191, 53)",
+ "struct_hover": "rgb(253, 191, 53)",
+ "struct_hover_background": "rgb(68, 68, 68)",
+ "enum": "rgb(253, 191, 53)",
+ "enum_hover": "rgb(253, 191, 53)",
+ "enum_hover_background": "rgb(68, 68, 68)",
+ "union": "rgb(253, 191, 53)",
+ "union_hover": "rgb(253, 191, 53)",
+ "union_hover_background": "rgb(68, 68, 68)",
+ "trait": "rgb(253, 191, 53)",
+ "trait_hover": "rgb(253, 191, 53)",
+ "trait_hover_background": "rgb(68, 68, 68)",
+ "fn": "rgb(253, 191, 53)",
+ "fn_hover": "rgb(253, 191, 53)",
+ "fn_hover_background": "rgb(68, 68, 68)",
+ "type": "rgb(253, 191, 53)",
+ "type_hover": "rgb(253, 191, 53)",
+ "type_hover_background": "rgb(68, 68, 68)",
+ "keyword": "rgb(253, 191, 53)",
+ "keyword_hover": "rgb(253, 191, 53)",
+ "keyword_hover_background": "rgb(68, 68, 68)",
+ }
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "struct": "rgb(53, 109, 164)",
+ "struct_hover": "rgb(53, 109, 164)",
+ "struct_hover_background": "rgb(255, 255, 255)",
+ "enum": "rgb(53, 109, 164)",
+ "enum_hover": "rgb(53, 109, 164)",
+ "enum_hover_background": "rgb(255, 255, 255)",
+ "union": "rgb(53, 109, 164)",
+ "union_hover": "rgb(53, 109, 164)",
+ "union_hover_background": "rgb(255, 255, 255)",
+ "trait": "rgb(53, 109, 164)",
+ "trait_hover": "rgb(53, 109, 164)",
+ "trait_hover_background": "rgb(255, 255, 255)",
+ "fn": "rgb(53, 109, 164)",
+ "fn_hover": "rgb(53, 109, 164)",
+ "fn_hover_background": "rgb(255, 255, 255)",
+ "type": "rgb(53, 109, 164)",
+ "type_hover": "rgb(53, 109, 164)",
+ "type_hover_background": "rgb(255, 255, 255)",
+ "keyword": "rgb(53, 109, 164)",
+ "keyword_hover": "rgb(53, 109, 164)",
+ "keyword_hover_background": "rgb(255, 255, 255)",
+ }
+)
diff --git a/tests/rustdoc-gui/sidebar-macro-reexport.goml b/tests/rustdoc-gui/sidebar-macro-reexport.goml
new file mode 100644
index 000000000..b5c1b6a43
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-macro-reexport.goml
@@ -0,0 +1,5 @@
+// This test ensures that the reexport of a macro doesn't make the original macro
+// displayed twice in the sidebar.
+goto: "file://" + |DOC_PATH| + "/test_docs/macro.repro.html"
+wait-for: ".sidebar-elems .block.macro a"
+assert-count: ("//*[@class='sidebar-elems']//*[@class='block macro']//a[text()='repro']", 1)
diff --git a/tests/rustdoc-gui/sidebar-mobile-scroll.goml b/tests/rustdoc-gui/sidebar-mobile-scroll.goml
new file mode 100644
index 000000000..2449269b1
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-mobile-scroll.goml
@@ -0,0 +1,31 @@
+// This test ensures that the mobile sidebar preserves scroll position.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+// Switching to "mobile view" by reducing the width to 600px.
+size: (700, 600)
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+
+// Scroll down.
+scroll-to: "//h2[@id='blanket-implementations']"
+assert-window-property: {"pageYOffset": "627"}
+
+// Open the sidebar menu.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "0px"})
+
+// We are no longer "scrolled". It's important that the user can't
+// scroll the body at all, but these test scripts are run only in Chrome,
+// and we need to use a more complicated solution to this problem because
+// of Mobile Safari...
+assert-window-property: {"pageYOffset": "0"}
+
+// Close the sidebar menu. Make sure the scroll position gets restored.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "-1000px"})
+assert-window-property: {"pageYOffset": "627"}
+
+// Now test that scrollability returns when the browser window is just resized.
+click: ".sidebar-menu-toggle"
+wait-for-css: (".sidebar", {"left": "0px"})
+assert-window-property: {"pageYOffset": "0"}
+size: (900, 600)
+assert-window-property: {"pageYOffset": "627"}
diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml
new file mode 100644
index 000000000..d5f4b6196
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-mobile.goml
@@ -0,0 +1,88 @@
+// This test ensure that the sidebar isn't "hidden" on mobile but instead moved out of the viewport.
+// This is especially important for devices for "text-first" content (like for users with
+// sight issues).
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+// Switching to "mobile view" by reducing the width to 600px.
+size: (600, 600)
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+// Opening the sidebar menu.
+click: ".sidebar-menu-toggle"
+assert-css: (".sidebar", {"display": "block", "left": "0px"})
+// Closing the sidebar menu.
+click: ".sidebar-menu-toggle"
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+// Force the sidebar open by focusing a link inside it.
+// This makes it easier for keyboard users to get to it.
+focus: ".sidebar-elems h3 a"
+assert-css: (".sidebar", {"display": "block", "left": "0px"})
+// When we tab out of the sidebar, close it.
+focus: ".search-input"
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+
+// Open the sidebar menu.
+click: ".sidebar-menu-toggle"
+assert-css: (".sidebar", {"left": "0px"})
+
+// Make sure the "struct Foo" header is hidden, since the mobile topbar already does it.
+assert-css: ("//nav[contains(@class, 'sidebar')]//h2/a[text()='Foo']/parent::h2", {"display": "none"})
+// Make sure the global navigation is still here.
+assert-css: ("//nav[contains(@class, 'sidebar')]//h2/a[text()='In test_docs']/parent::h2", {"display": "block"})
+
+// Click elsewhere.
+click: "body"
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+
+// Open the sidebar menu, and make sure pressing Escape closes it.
+click: ".sidebar-menu-toggle"
+assert-css: (".sidebar", {"left": "0px"})
+press-key: "Escape"
+assert-css: (".sidebar", {"display": "block", "left": "-1000px"})
+
+// Check that the topbar is visible
+assert-property: (".mobile-topbar", {"clientHeight": "45"})
+
+// Check that clicking an element from the sidebar scrolls to the right place
+// so the target is not obscured by the topbar.
+click: ".sidebar-menu-toggle"
+click: ".sidebar-elems section .block li > a"
+assert-position: ("#method\.must_use", {"y": 45})
+
+// Check that the bottom-most item on the sidebar menu can be scrolled fully into view.
+click: ".sidebar-menu-toggle"
+scroll-to: ".block.keyword li:nth-child(1)"
+compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 543.19})
+
+// Now checking the background color of the sidebar.
+show-text: true
+
+define-function: (
+ "check-colors",
+ (theme, color, background),
+ block {
+ local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}
+ reload:
+
+ // Open the sidebar menu.
+ click: ".sidebar-menu-toggle"
+ assert-css: (".sidebar", {
+ "background-color": |background|,
+ "color": |color|,
+ })
+ },
+)
+
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "color": "rgb(197, 197, 197)",
+ "background": "rgb(20, 25, 31)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "color": "rgb(221, 221, 221)",
+ "background": "rgb(80, 80, 80)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "color": "rgb(0, 0, 0)",
+ "background": "rgb(245, 245, 245)",
+})
diff --git a/tests/rustdoc-gui/sidebar-source-code-display.goml b/tests/rustdoc-gui/sidebar-source-code-display.goml
new file mode 100644
index 000000000..f3eb8ff76
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-source-code-display.goml
@@ -0,0 +1,215 @@
+// This test ensures that the elements in the sidebar are displayed correctly.
+javascript: false
+goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+// Since the javascript is disabled, there shouldn't be a toggle.
+assert-false: "#src-sidebar-toggle"
+wait-for-css: (".sidebar", {"display": "none"})
+
+// Let's retry with javascript enabled.
+javascript: true
+reload:
+wait-for: "#src-sidebar-toggle"
+assert-css: ("#src-sidebar-toggle", {"visibility": "visible"})
+assert-css: (".sidebar > *:not(#src-sidebar-toggle)", {"visibility": "hidden"})
+// Let's expand the sidebar now.
+click: "#src-sidebar-toggle"
+wait-for-css: ("#src-sidebar-toggle", {"visibility": "visible"})
+
+// We now check that opening the sidebar and clicking a link will leave it open.
+// The behavior here on desktop is different than the behavior on mobile,
+// but since the sidebar doesn't fill the entire screen here, it makes sense to have the
+// sidebar stay resident.
+wait-for-css: (".sidebar", {"width": "300px"})
+assert-local-storage: {"rustdoc-source-sidebar-show": "true"}
+click: ".sidebar a.selected"
+goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+wait-for-css: (".sidebar", {"width": "300px"})
+assert-local-storage: {"rustdoc-source-sidebar-show": "true"}
+
+// Now we check the display of the sidebar items.
+show-text: true
+
+define-function: (
+ "check-colors",
+ (
+ theme, color, color_hover, background, background_hover, background_toggle,
+ background_toggle_hover,
+ ),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ wait-for-css: ("#src-sidebar-toggle", {"visibility": "visible"})
+ assert-css: (
+ "#source-sidebar details[open] > .files a.selected",
+ {"color": |color_hover|, "background-color": |background|},
+ )
+
+ // Without hover or focus.
+ assert-css: ("#src-sidebar-toggle > button", {"background-color": |background_toggle|})
+ // With focus.
+ focus: "#src-sidebar-toggle > button"
+ assert-css: (
+ "#src-sidebar-toggle > button:focus",
+ {"background-color": |background_toggle_hover|},
+ )
+ focus: ".search-input"
+ // With hover.
+ move-cursor-to: "#src-sidebar-toggle > button"
+ assert-css: (
+ "#src-sidebar-toggle > button:hover",
+ {"background-color": |background_toggle_hover|},
+ )
+
+ // Without hover or focus.
+ assert-css: (
+ "#source-sidebar details[open] > .files a:not(.selected)",
+ {"color": |color|, "background-color": |background_toggle|},
+ )
+ // With focus.
+ focus: "#source-sidebar details[open] > .files a:not(.selected)"
+ wait-for-css: (
+ "#source-sidebar details[open] > .files a:not(.selected):focus",
+ {"color": |color_hover|, "background-color": |background_hover|},
+ )
+ focus: ".search-input"
+ // With hover.
+ move-cursor-to: "#source-sidebar details[open] > .files a:not(.selected)"
+ assert-css: (
+ "#source-sidebar details[open] > .files a:not(.selected):hover",
+ {"color": |color_hover|, "background-color": |background_hover|},
+ )
+
+ // Without hover or focus.
+ assert-css: (
+ "#source-sidebar .dir-entry summary",
+ {"color": |color|, "background-color": |background_toggle|},
+ )
+ // With focus.
+ focus: "#source-sidebar .dir-entry summary"
+ wait-for-css: (
+ "#source-sidebar .dir-entry summary:focus",
+ {"color": |color_hover|, "background-color": |background_hover|},
+ )
+ focus: ".search-input"
+ // With hover.
+ move-cursor-to: "#source-sidebar .dir-entry summary"
+ assert-css: (
+ "#source-sidebar .dir-entry summary:hover",
+ {"color": |color_hover|, "background-color": |background_hover|},
+ )
+
+ // Without hover or focus.
+ assert-css: (
+ "#source-sidebar details[open] > .folders > details > summary",
+ {"color": |color|, "background-color": |background_toggle|},
+ )
+ // With focus.
+ focus: "#source-sidebar details[open] > .folders > details > summary"
+ wait-for-css: (
+ "#source-sidebar details[open] > .folders > details > summary:focus",
+ {"color": |color_hover|, "background-color": |background_hover|},
+ )
+ focus: ".search-input"
+ // With hover.
+ move-cursor-to: "#source-sidebar details[open] > .folders > details > summary"
+ assert-css: (
+ "#source-sidebar details[open] > .folders > details > summary:hover",
+ {"color": |color_hover|, "background-color": |background_hover|},
+ )
+ },
+)
+
+call-function: ("check-colors", {
+ "theme": "light",
+ "color": "rgb(0, 0, 0)",
+ "color_hover": "rgb(0, 0, 0)",
+ "background": "rgb(255, 255, 255)",
+ "background_hover": "rgb(224, 224, 224)",
+ "background_toggle": "rgba(0, 0, 0, 0)",
+ "background_toggle_hover": "rgb(224, 224, 224)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "color": "rgb(221, 221, 221)",
+ "color_hover": "rgb(221, 221, 221)",
+ "background": "rgb(51, 51, 51)",
+ "background_hover": "rgb(68, 68, 68)",
+ "background_toggle": "rgba(0, 0, 0, 0)",
+ "background_toggle_hover": "rgb(103, 103, 103)",
+})
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "color": "rgb(197, 197, 197)",
+ "color_hover": "rgb(255, 180, 76)",
+ "background": "rgb(20, 25, 31)",
+ "background_hover": "rgb(20, 25, 31)",
+ "background_toggle": "rgba(0, 0, 0, 0)",
+ "background_toggle_hover": "rgba(70, 70, 70, 0.33)",
+})
+
+// Now checking on mobile devices.
+size: (500, 700)
+reload:
+// Waiting for the sidebar to be displayed...
+wait-for-css: ("#src-sidebar-toggle", {"visibility": "visible"})
+
+// We now check it takes the full size of the display.
+assert-property: ("body", {"clientWidth": "500", "clientHeight": "700"})
+assert-property: (".sidebar", {"clientWidth": "500", "clientHeight": "700"})
+
+// We now check the display of the toggle once the sidebar is expanded.
+assert-property: ("#src-sidebar-toggle", {"clientWidth": "500", "clientHeight": "39"})
+assert-css: (
+ "#src-sidebar-toggle",
+ {
+ "border-top-width": "0px",
+ "border-right-width": "0px",
+ "border-left-width": "0px",
+ "border-bottom-width": "1px",
+ },
+)
+
+// We now check that the scroll position is kept when opening the sidebar.
+click: "#src-sidebar-toggle"
+wait-for-css: (".sidebar", {"left": "-1000px"})
+// We scroll to line 117 to change the scroll position.
+scroll-to: '//*[@id="117"]'
+assert-window-property: {"pageYOffset": "2542"}
+// Expanding the sidebar...
+click: "#src-sidebar-toggle"
+wait-for-css: (".sidebar", {"left": "0px"})
+click: "#src-sidebar-toggle"
+wait-for-css: (".sidebar", {"left": "-1000px"})
+// The "scrollTop" property should be the same.
+assert-window-property: {"pageYOffset": "2542"}
+
+// We now check that the scroll position is restored if the window is resized.
+size: (500, 700)
+click: "#src-sidebar-toggle"
+wait-for-css: ("#source-sidebar", {"visibility": "visible"})
+assert-window-property: {"pageYOffset": "0"}
+size: (900, 900)
+assert-window-property: {"pageYOffset": "2542"}
+size: (500, 700)
+click: "#src-sidebar-toggle"
+wait-for-css: ("#source-sidebar", {"visibility": "hidden"})
+
+// We now check that opening the sidebar and clicking a link will close it.
+// The behavior here on mobile is different than the behavior on desktop,
+// but common sense dictates that if you have a list of files that fills the entire screen, and
+// you click one of them, you probably want to actually see the file's contents, and not just
+// make it the current selection.
+click: "#src-sidebar-toggle"
+wait-for-css: ("#source-sidebar", {"visibility": "visible"})
+assert-local-storage: {"rustdoc-source-sidebar-show": "true"}
+click: ".sidebar a.selected"
+goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+wait-for-css: ("#source-sidebar", {"visibility": "hidden"})
+assert-local-storage: {"rustdoc-source-sidebar-show": "false"}
+// Resize back to desktop size, to check that the sidebar doesn't spontaneously open.
+size: (1000, 1000)
+wait-for-css: ("#source-sidebar", {"visibility": "hidden"})
+assert-local-storage: {"rustdoc-source-sidebar-show": "false"}
+click: "#src-sidebar-toggle"
+wait-for-css: ("#source-sidebar", {"visibility": "visible"})
+assert-local-storage: {"rustdoc-source-sidebar-show": "true"}
diff --git a/tests/rustdoc-gui/sidebar-source-code.goml b/tests/rustdoc-gui/sidebar-source-code.goml
new file mode 100644
index 000000000..c8a29b58d
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-source-code.goml
@@ -0,0 +1,90 @@
+// The goal of this test is to ensure that the sidebar is working as expected in the source
+// code pages.
+goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+show-text: true
+
+// First, check the sidebar colors.
+define-function: (
+ "check-colors",
+ (theme, color, background_color),
+ block {
+ local-storage: {
+ "rustdoc-theme": |theme|,
+ "rustdoc-use-system-theme": "false",
+ }
+ reload:
+ // Checking results colors.
+ assert-css: (".source .sidebar", {
+ "color": |color|,
+ "background-color": |background_color|
+ }, ALL)
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "color": "rgb(197, 197, 197)",
+ "background_color": "rgb(20, 25, 31)",
+ }
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "color": "rgb(221, 221, 221)",
+ "background_color": "rgb(80, 80, 80)",
+ }
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "color": "rgb(0, 0, 0)",
+ "background_color": "rgb(245, 245, 245)",
+ }
+)
+
+// Next, desktop mode layout.
+size: (1100, 800)
+// We check that the sidebar isn't expanded and has the expected width.
+assert-css: ("nav.sidebar", {"width": "50px"})
+// We now click on the button to expand the sidebar.
+click: (10, 10)
+// We wait for the sidebar to be expanded.
+wait-for-css: (".source-sidebar-expanded nav.sidebar", {"width": "300px"})
+assert-css: (".source-sidebar-expanded nav.sidebar a", {"font-size": "14px"})
+// We collapse the sidebar.
+click: (10, 10)
+// We ensure that the class has been removed.
+wait-for: "html:not(.expanded)"
+assert: "nav.sidebar"
+
+// Checking that only the path to the current file is "open".
+goto: "file://" + |DOC_PATH| + "/src/lib2/another_folder/sub_mod/mod.rs.html"
+// First we expand the sidebar again.
+click: (10, 10)
+// We wait for the sidebar to be expanded.
+wait-for-css: (".source-sidebar-expanded nav.sidebar", {"width": "300px"})
+assert: "//*[@class='dir-entry' and @open]/*[text()='lib2']"
+assert: "//*[@class='dir-entry' and @open]/*[text()='another_folder']"
+assert: "//*[@class='dir-entry' and @open]/*[text()='sub_mod']"
+// Only "another_folder" should be "open" in "lib2".
+assert: "//*[@class='dir-entry' and not(@open)]/*[text()='another_mod']"
+// All other trees should be collapsed.
+assert-count: ("//*[@id='source-sidebar']/details[not(text()='lib2') and not(@open)]", 8)
+
+// We now switch to mobile mode.
+size: (600, 600)
+wait-for-css: (".source-sidebar-expanded nav.sidebar", {"left": "0px"})
+// We collapse the sidebar.
+click: (10, 10)
+// We check that the sidebar has been moved off-screen.
+assert-css: ("nav.sidebar", {"left": "-1000px"})
+// We ensure that the class has been removed.
+assert-false: ".source-sidebar-expanded"
+assert: "nav.sidebar"
+
+// Check that the topbar is not visible
+assert-false: ".mobile-topbar"
diff --git a/tests/rustdoc-gui/sidebar.goml b/tests/rustdoc-gui/sidebar.goml
new file mode 100644
index 000000000..9c742be05
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar.goml
@@ -0,0 +1,151 @@
+// Checks multiple things on the sidebar display (width of its elements, colors, etc).
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+show-text: true
+
+// First, check the sidebar colors.
+define-function: (
+ "check-colors",
+ (theme, color, background_color),
+ block {
+ local-storage: {
+ "rustdoc-theme": |theme|,
+ "rustdoc-use-system-theme": "false",
+ }
+ reload:
+ // Checking results colors.
+ assert-css: (".sidebar", {
+ "color": |color|,
+ "background-color": |background_color|
+ }, ALL)
+ },
+)
+
+call-function: (
+ "check-colors",
+ {
+ "theme": "ayu",
+ "color": "rgb(197, 197, 197)",
+ "background_color": "rgb(20, 25, 31)",
+ }
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "dark",
+ "color": "rgb(221, 221, 221)",
+ "background_color": "rgb(80, 80, 80)",
+ }
+)
+call-function: (
+ "check-colors",
+ {
+ "theme": "light",
+ "color": "rgb(0, 0, 0)",
+ "background_color": "rgb(245, 245, 245)",
+ }
+)
+
+local-storage: {"rustdoc-theme": "light"}
+// We reload the page so the local storage settings are being used.
+reload:
+
+assert-text: (".sidebar > .location", "Crate test_docs")
+// In modules, we only have one "location" element.
+assert-count: (".sidebar .location", 1)
+assert-count: (".sidebar h2", 1)
+assert-text: ("#all-types", "All Items")
+assert-css: ("#all-types", {"color": "rgb(53, 109, 164)"})
+// We check that we have the crates list and that the "current" on is "test_docs".
+assert-text: (".sidebar-elems ul.crate > li > a.current", "test_docs")
+// And we're also supposed to have the list of items in the current module.
+assert-text: (".sidebar-elems section ul > li:nth-child(1)", "Re-exports")
+assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Modules")
+assert-text: (".sidebar-elems section ul > li:nth-child(3)", "Macros")
+assert-text: (".sidebar-elems section ul > li:nth-child(4)", "Structs")
+assert-text: (".sidebar-elems section ul > li:nth-child(5)", "Enums")
+assert-text: (".sidebar-elems section ul > li:nth-child(6)", "Constants")
+assert-text: (".sidebar-elems section ul > li:nth-child(7)", "Traits")
+assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Functions")
+assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Type Definitions")
+assert-text: (".sidebar-elems section ul > li:nth-child(10)", "Unions")
+assert-text: (".sidebar-elems section ul > li:nth-child(11)", "Keywords")
+assert-text: ("#structs + .item-table .item-left > a", "Foo")
+click: "#structs + .item-table .item-left > a"
+
+// PAGE: struct.Foo.html
+assert-count: (".sidebar .location", 1)
+assert-count: (".sidebar h2", 2)
+// We check that there is no crate listed outside of the top level.
+assert-false: ".sidebar-elems > .crate"
+
+click: ".sidebar-elems section .block li > a"
+assert-property-false: ("html", {"scrollTop": "0"})
+
+click: ".sidebar h2.location a"
+assert-property: ("html", {"scrollTop": "0"})
+
+// We now go back to the crate page to click on the "lib2" crate link.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+assert-css: (".sidebar-elems ul.crate > li:first-child > a", {"color": "rgb(53, 109, 164)"})
+click: ".sidebar-elems ul.crate > li:first-child > a"
+
+// PAGE: lib2/index.html
+goto: "file://" + |DOC_PATH| + "/lib2/index.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+assert-text: (".sidebar > .location", "Crate lib2")
+// We check that we have the crates list and that the "current" on is now "lib2".
+assert-text: (".sidebar-elems ul.crate > li > a.current", "lib2")
+// We now go to the "foobar" function page.
+assert-text: (".sidebar-elems > section ul.block > li:nth-child(1)", "Modules")
+assert-text: (".sidebar-elems > section ul.block > li:nth-child(2)", "Structs")
+assert-text: (".sidebar-elems > section ul.block > li:nth-child(3)", "Traits")
+assert-text: (".sidebar-elems > section ul.block > li:nth-child(4)", "Functions")
+assert-text: (".sidebar-elems > section ul.block > li:nth-child(5)", "Type Definitions")
+assert-text: ("#functions + .item-table .item-left > a", "foobar")
+click: "#functions + .item-table .item-left > a"
+
+// PAGE: fn.foobar.html
+// In items containing no items (like functions or constants) and in modules, we have no
+// "location" elements. Only the parent module h2.
+assert-count: (".sidebar .location", 0)
+assert-count: (".sidebar h2", 1)
+assert-text: (".sidebar .sidebar-elems h2", "In lib2")
+// We check that we don't have the crate list.
+assert-false: ".sidebar-elems > .crate"
+
+goto: "./module/index.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+assert-text: (".sidebar > .location", "Module module")
+// We check that we don't have the crate list.
+assert-false: ".sidebar-elems > .crate"
+
+goto: "./sub_module/sub_sub_module/index.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+assert-text: (".sidebar > .location", "Module sub_sub_module")
+// We check that we don't have the crate list.
+assert-false: ".sidebar-elems .crate"
+assert-text: (".sidebar-elems > section ul > li:nth-child(1)", "Functions")
+assert-text: ("#functions + .item-table .item-left > a", "foo")
+
+// Links to trait implementations in the sidebar should not wrap even if they are long.
+goto: "file://" + |DOC_PATH| + "/lib2/struct.HasALongTraitWithParams.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+assert-property: (".sidebar-elems section .block li > a", {"offsetHeight": 29})
+
+// Test that clicking on of the "In <module>" headings in the sidebar links to the
+// appropriate anchor in index.html.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+assert-property: (".sidebar", {"clientWidth": "200"})
+click: "//ul[@class='block mod']/preceding-sibling::h3/a"
+// PAGE: index.html
+assert-css: ("#modules", {"background-color": "rgb(253, 255, 211)"})
+
+// Finally, assert that the `[+]/[−]` toggle doesn't affect sidebar width.
+click: "#toggle-all-docs"
+assert-text: ("#toggle-all-docs", "[+]")
+assert-property: (".sidebar", {"clientWidth": "200"})
+click: "#toggle-all-docs"
+assert-text: ("#toggle-all-docs", "[−]")
+assert-property: (".sidebar", {"clientWidth": "200"})
diff --git a/tests/rustdoc-gui/source-anchor-scroll.goml b/tests/rustdoc-gui/source-anchor-scroll.goml
new file mode 100644
index 000000000..ddfe0c3d1
--- /dev/null
+++ b/tests/rustdoc-gui/source-anchor-scroll.goml
@@ -0,0 +1,20 @@
+// We check that when the anchor changes and is output of the displayed content,
+// the page is scrolled to it.
+goto: "file://" + |DOC_PATH| + "/src/link_to_definition/lib.rs.html"
+
+// We reduce the window size to make it easier to make an element "out of the page".
+size: (600, 800)
+// We check that the scroll is at the top first.
+assert-property: ("html", {"scrollTop": "0"})
+
+click: '//a[text() = "barbar"]'
+assert-property: ("html", {"scrollTop": "125"})
+click: '//a[text() = "bar"]'
+assert-property: ("html", {"scrollTop": "156"})
+click: '//a[text() = "sub_fn"]'
+assert-property: ("html", {"scrollTop": "53"})
+
+// We now check that clicking on lines doesn't change the scroll
+// Extra information: the "sub_fn" function header is on line 1.
+click: '//*[@id="6"]'
+assert-property: ("html", {"scrollTop": "53"})
diff --git a/tests/rustdoc-gui/source-code-page.goml b/tests/rustdoc-gui/source-code-page.goml
new file mode 100644
index 000000000..7c35119e6
--- /dev/null
+++ b/tests/rustdoc-gui/source-code-page.goml
@@ -0,0 +1,218 @@
+// Checks that the interactions with the source code pages are working as expected.
+goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+show-text: true
+// Check that we can click on the line number.
+click: ".src-line-numbers > a:nth-child(4)" // This is the anchor for line 4.
+// Ensure that the page URL was updated.
+assert-document-property: ({"URL": "lib.rs.html#4"}, ENDS_WITH)
+assert-attribute: ("//*[@id='4']", {"class": "line-highlighted"})
+// Ensure that the default style, with the right border, isn't used.
+assert-css: ("//*[@id='4']", {"border-right-width": "0px"})
+reload:
+assert-attribute: ("//*[@id='4']", {"class": "line-highlighted"})
+assert-css: ("//*[@id='4']", {"border-right-width": "0px"})
+// We now check that the good anchors are highlighted
+goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html#4-6"
+assert-attribute-false: (".src-line-numbers > a:nth-child(3)", {"class": "line-highlighted"})
+assert-attribute: (".src-line-numbers > a:nth-child(4)", {"class": "line-highlighted"})
+assert-attribute: (".src-line-numbers > a:nth-child(5)", {"class": "line-highlighted"})
+assert-attribute: (".src-line-numbers > a:nth-child(6)", {"class": "line-highlighted"})
+assert-attribute-false: (".src-line-numbers > a:nth-child(7)", {"class": "line-highlighted"})
+
+define-function: (
+ "check-colors",
+ (theme, color, background_color, highlight_color, highlight_background_color),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: (
+ ".src-line-numbers > a:not(.line-highlighted)",
+ {"color": |color|, "background-color": |background_color|},
+ ALL,
+ )
+ assert-css: (
+ ".src-line-numbers > a.line-highlighted",
+ {"color": |highlight_color|, "background-color": |highlight_background_color|},
+ ALL,
+ )
+ },
+)
+
+call-function: ("check-colors", {
+ "theme": "ayu",
+ "color": "rgb(92, 103, 115)",
+ "background_color": "rgba(0, 0, 0, 0)",
+ "highlight_color": "rgb(112, 128, 144)",
+ "highlight_background_color": "rgba(255, 236, 164, 0.06)",
+})
+call-function: ("check-colors", {
+ "theme": "dark",
+ "color": "rgb(59, 145, 226)",
+ "background_color": "rgba(0, 0, 0, 0)",
+ "highlight_color": "rgb(59, 145, 226)",
+ "highlight_background_color": "rgb(10, 4, 47)",
+})
+call-function: ("check-colors", {
+ "theme": "light",
+ "color": "rgb(198, 126, 45)",
+ "background_color": "rgba(0, 0, 0, 0)",
+ "highlight_color": "rgb(198, 126, 45)",
+ "highlight_background_color": "rgb(253, 255, 211)",
+})
+
+// This is to ensure that the content is correctly align with the line numbers.
+compare-elements-position: ("//*[@id='1']", ".rust > code > span", ("y"))
+// Check the `href` property so that users can treat anchors as links.
+assert-property: (".src-line-numbers > a:nth-child(1)", {
+ "href": "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html#1"
+})
+assert-property: (".src-line-numbers > a:nth-child(2)", {
+ "href": "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html#2"
+})
+assert-property: (".src-line-numbers > a:nth-child(3)", {
+ "href": "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html#3"
+})
+assert-property: (".src-line-numbers > a:nth-child(4)", {
+ "href": "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html#4"
+})
+assert-property: (".src-line-numbers > a:nth-child(5)", {
+ "href": "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html#5"
+})
+assert-property: (".src-line-numbers > a:nth-child(6)", {
+ "href": "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html#6"
+})
+
+// Assert that the line numbers text is aligned to the right.
+assert-css: (".src-line-numbers", {"text-align": "right"})
+
+// Now let's check that clicking on something else than the line number doesn't
+// do anything (and certainly not add a `#NaN` to the URL!).
+goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+// We use this assert-position to know where we will click.
+assert-position: ("//*[@id='1']", {"x": 88, "y": 112})
+// We click on the left of the "1" anchor but still in the "src-line-number" `<pre>`.
+click: (87, 103)
+assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH)
+
+// Checking the source code sidebar.
+
+// First we "open" it.
+click: "#src-sidebar-toggle"
+assert: ".source-sidebar-expanded"
+
+// We check that the first entry of the sidebar is collapsed
+assert-property: ("#source-sidebar details:first-of-type", {"open": "false"})
+assert-text: ("#source-sidebar details:first-of-type > summary", "http")
+// We now click on it.
+click: "#source-sidebar details:first-of-type > summary"
+assert-property: ("#source-sidebar details:first-of-type", {"open": "true"})
+
+// And now we collapse it again.
+click: "#source-sidebar details:first-of-type > summary"
+assert-property: ("#source-sidebar details:first-of-type", {"open": "false"})
+
+// And open it again, since it'll be the reference we use to check positions.
+click: "#source-sidebar details:first-of-type > summary"
+assert-property: ("#source-sidebar details:first-of-type", {"open": "true"})
+
+// Check the sidebar directory entries have a marker and spacing (desktop).
+store-property: (
+ link_height,
+ "#source-sidebar > details:first-of-type.dir-entry[open] > .files > a",
+ "offsetHeight"
+)
+define-function: (
+ "check-sidebar-dir-entry",
+ (x, y),
+ block {
+ assert: "details:first-of-type.dir-entry[open] > summary::marker"
+ assert-css: ("#source-sidebar > details:first-of-type.dir-entry", {"padding-left": "4px"})
+ // This check ensures that the summary is only one line.
+ assert-property: (
+ "#source-sidebar > details:first-of-type.dir-entry[open] > summary",
+ {"offsetHeight": |link_height|}
+ )
+ assert-position: (
+ "#source-sidebar > details:first-of-type.dir-entry[open] > summary",
+ {"x": |x|, "y": |y|}
+ )
+ assert-property: (
+ "#source-sidebar > details:first-of-type.dir-entry[open] > .files > a",
+ {"offsetHeight": |link_height|}
+ )
+ assert-position: (
+ "#source-sidebar > details:first-of-type.dir-entry[open] > .files > a",
+ // left margin
+ {"x": |x| + 27, "y": |y| + |link_height|}
+ )
+ }
+)
+store-property: (
+ source_sidebar_title_height,
+ "#source-sidebar > .title",
+ "offsetHeight"
+)
+store-property: (
+ source_sidebar_title_y,
+ "#source-sidebar > .title",
+ "offsetTop"
+)
+call-function: ("check-sidebar-dir-entry", {
+ "x": 0,
+ // border + margin = 6
+ "y": |source_sidebar_title_y| + |source_sidebar_title_height| + 6,
+})
+
+// Check the search form
+assert-css: ("nav.sub", {"flex-direction": "row"})
+// The goal of this test is to ensure the search input is perfectly centered
+// between the top of the page and the top of the gray code block.
+// To check this, we maintain the invariant:
+//
+// offsetTop[nav.sub form] = offsetTop[#main-content] - offsetHeight[nav.sub form] - offsetTop[nav.sub form]
+assert-property: ("nav.sub form", {"offsetTop": 28, "offsetHeight": 34})
+assert-property: ("#main-content", {"offsetTop": 90})
+// 28 = 90 - 34 - 28
+
+// Now do the same check on moderately-sized, tablet mobile.
+size: (700, 700)
+assert-css: ("nav.sub", {"flex-direction": "row"})
+assert-property: ("nav.sub form", {"offsetTop": 21, "offsetHeight": 34})
+assert-property: ("#main-content", {"offsetTop": 76})
+// 21 = 76 - 34 - 21
+
+// Check the sidebar directory entries have a marker and spacing (tablet).
+store-property: (
+ source_sidebar_title_height,
+ "#source-sidebar > .title",
+ "offsetHeight"
+)
+store-property: (
+ source_sidebar_title_y,
+ "#source-sidebar > .title",
+ "offsetTop"
+)
+call-function: ("check-sidebar-dir-entry", {
+ "x": 0,
+ "y": |source_sidebar_title_y| + |source_sidebar_title_height| + 6,
+})
+
+// Tiny, phone mobile gets a different display where the logo is stacked on top.
+size: (450, 700)
+assert-css: ("nav.sub", {"flex-direction": "column"})
+
+// Check the sidebar directory entries have a marker and spacing (phone).
+store-property: (
+ source_sidebar_title_height,
+ "#source-sidebar > .title",
+ "offsetHeight"
+)
+store-property: (
+ source_sidebar_title_y,
+ "#source-sidebar > .title",
+ "offsetTop"
+)
+call-function: ("check-sidebar-dir-entry", {
+ "x": 0,
+ "y": |source_sidebar_title_y| + |source_sidebar_title_height| + 6,
+})
diff --git a/tests/rustdoc-gui/src-font-size.goml b/tests/rustdoc-gui/src-font-size.goml
new file mode 100644
index 000000000..9233f3744
--- /dev/null
+++ b/tests/rustdoc-gui/src-font-size.goml
@@ -0,0 +1,16 @@
+// This test ensures that the "[src]" have the same font size as their headers
+// to avoid having some weird height difference in the background when the element
+// is selected.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+show-text: true
+// Check the impl headers.
+assert-css: (".impl.has-srclink .srclink", {"font-size": "16px", "font-weight": 400}, ALL)
+assert-css: (".impl.has-srclink .code-header", {"font-size": "18px", "font-weight": 600}, ALL)
+// Check the impl items.
+assert-css: (".impl-items .has-srclink .srclink", {"font-size": "16px", "font-weight": 400}, ALL)
+assert-css: (".impl-items .has-srclink .code-header", {"font-size": "16px", "font-weight": 600}, ALL)
+
+// Check that we can click on source link
+store-document-property: (url, "URL")
+click: ".impl-items .has-srclink .srclink"
+assert-document-property-false: {"URL": |url|}
diff --git a/tests/rustdoc-gui/src/huge_logo/Cargo.lock b/tests/rustdoc-gui/src/huge_logo/Cargo.lock
new file mode 100644
index 000000000..142805750
--- /dev/null
+++ b/tests/rustdoc-gui/src/huge_logo/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "huge_logo"
+version = "0.1.0"
diff --git a/tests/rustdoc-gui/src/huge_logo/Cargo.toml b/tests/rustdoc-gui/src/huge_logo/Cargo.toml
new file mode 100644
index 000000000..3f10d09c8
--- /dev/null
+++ b/tests/rustdoc-gui/src/huge_logo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "huge_logo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/tests/rustdoc-gui/src/huge_logo/src/lib.rs b/tests/rustdoc-gui/src/huge_logo/src/lib.rs
new file mode 100644
index 000000000..ec137fb9a
--- /dev/null
+++ b/tests/rustdoc-gui/src/huge_logo/src/lib.rs
@@ -0,0 +1,17 @@
+// ignore-tidy-linelength
+#![doc(html_logo_url = "data:image/png;base64,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")]
+
+pub fn add(left: usize, right: usize) -> usize {
+ left + right
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn it_works() {
+ let result = add(2, 2);
+ assert_eq!(result, 4);
+ }
+}
diff --git a/tests/rustdoc-gui/src/lib2/Cargo.lock b/tests/rustdoc-gui/src/lib2/Cargo.lock
new file mode 100644
index 000000000..425a3ae7e
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/Cargo.lock
@@ -0,0 +1,22 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "http"
+version = "0.1.0"
+
+[[package]]
+name = "implementors"
+version = "0.1.0"
+dependencies = [
+ "http",
+]
+
+[[package]]
+name = "lib2"
+version = "0.1.0"
+dependencies = [
+ "http",
+ "implementors",
+]
diff --git a/tests/rustdoc-gui/src/lib2/Cargo.toml b/tests/rustdoc-gui/src/lib2/Cargo.toml
new file mode 100644
index 000000000..8bca77ff8
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "lib2"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+implementors = { path = "./implementors" }
+http = { path = "./http" }
diff --git a/tests/rustdoc-gui/src/lib2/another_folder/mod.rs b/tests/rustdoc-gui/src/lib2/another_folder/mod.rs
new file mode 100644
index 000000000..ec9a20859
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/another_folder/mod.rs
@@ -0,0 +1,3 @@
+pub fn another_fn() {}
+
+pub mod sub_mod;
diff --git a/tests/rustdoc-gui/src/lib2/another_folder/sub_mod/mod.rs b/tests/rustdoc-gui/src/lib2/another_folder/sub_mod/mod.rs
new file mode 100644
index 000000000..f16722cf3
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/another_folder/sub_mod/mod.rs
@@ -0,0 +1 @@
+pub fn subsubsub() {}
diff --git a/tests/rustdoc-gui/src/lib2/another_mod/mod.rs b/tests/rustdoc-gui/src/lib2/another_mod/mod.rs
new file mode 100644
index 000000000..9a4f007a2
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/another_mod/mod.rs
@@ -0,0 +1 @@
+pub fn tadam() {}
diff --git a/tests/rustdoc-gui/src/lib2/http/Cargo.toml b/tests/rustdoc-gui/src/lib2/http/Cargo.toml
new file mode 100644
index 000000000..fa719efa5
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/http/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "http"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
diff --git a/tests/rustdoc-gui/src/lib2/http/lib.rs b/tests/rustdoc-gui/src/lib2/http/lib.rs
new file mode 100644
index 000000000..204e07494
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/http/lib.rs
@@ -0,0 +1 @@
+pub trait HttpTrait {}
diff --git a/tests/rustdoc-gui/src/lib2/implementors/Cargo.lock b/tests/rustdoc-gui/src/lib2/implementors/Cargo.lock
new file mode 100644
index 000000000..cad99a991
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/implementors/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "implementors"
+version = "0.1.0"
diff --git a/tests/rustdoc-gui/src/lib2/implementors/Cargo.toml b/tests/rustdoc-gui/src/lib2/implementors/Cargo.toml
new file mode 100644
index 000000000..9dafc43df
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/implementors/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "implementors"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+http = { path = "../http/" }
diff --git a/tests/rustdoc-gui/src/lib2/implementors/lib.rs b/tests/rustdoc-gui/src/lib2/implementors/lib.rs
new file mode 100644
index 000000000..2842ac50d
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/implementors/lib.rs
@@ -0,0 +1,22 @@
+pub trait Whatever {
+ type Foo;
+
+ fn method() {}
+}
+
+pub struct Struct;
+
+impl Whatever for Struct {
+ type Foo = u8;
+}
+
+impl http::HttpTrait for Struct {}
+
+mod traits {
+ pub trait TraitToReexport {
+ fn method() {}
+ }
+}
+
+#[doc(inline)]
+pub use traits::TraitToReexport;
diff --git a/tests/rustdoc-gui/src/lib2/lib.rs b/tests/rustdoc-gui/src/lib2/lib.rs
new file mode 100644
index 000000000..34e67d9d2
--- /dev/null
+++ b/tests/rustdoc-gui/src/lib2/lib.rs
@@ -0,0 +1,343 @@
+// ignore-tidy-linelength
+
+#![feature(doc_cfg)]
+
+pub mod another_folder;
+pub mod another_mod;
+
+pub mod module {
+ pub mod sub_module {
+ pub mod sub_sub_module {
+ pub fn foo() {}
+ }
+ pub fn bar() {}
+ }
+ pub fn whatever() {}
+}
+
+pub fn foobar() {}
+
+pub type Alias = u32;
+
+#[doc(cfg(feature = "foo-method"))]
+pub struct Foo {
+ pub x: Alias,
+}
+
+impl Foo {
+ /// Some documentation
+ /// # A Heading
+ pub fn a_method(&self) {}
+}
+
+#[doc(cfg(feature = "foo-method"))]
+#[deprecated = "Whatever [`Foo::a_method`](#method.a_method)"]
+pub trait Trait {
+ type X;
+ const Y: u32;
+
+ #[deprecated = "Whatever [`Foo`](#tadam)"]
+ fn foo() {}
+ fn fooo();
+}
+
+impl Trait for Foo {
+ type X = u32;
+ const Y: u32 = 0;
+
+ fn fooo() {}
+}
+
+impl implementors::Whatever for Foo {
+ type Foo = u32;
+}
+
+#[doc(inline)]
+pub use implementors::TraitToReexport;
+
+pub struct StructToImplOnReexport;
+
+impl TraitToReexport for StructToImplOnReexport {}
+
+pub mod sub_mod {
+ /// ```txt
+ /// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ /// ```
+ ///
+ /// ```
+ /// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+ /// ```
+ pub struct Foo;
+}
+
+pub mod long_trait {
+ use std::ops::DerefMut;
+
+ pub trait ALongNameBecauseItHelpsTestingTheCurrentProblem:
+ DerefMut<Target = u32> + From<u128> + Send + Sync + AsRef<str> + 'static
+ {
+ }
+}
+
+pub mod long_table {
+ /// | This::is::a::kinda::very::long::header::number::one | This::is::a::kinda::very::long::header::number::two | This::is::a::kinda::very::long::header::number::one | This::is::a::kinda::very::long::header::number::two |
+ /// | ----------- | ----------- | ----------- | ----------- |
+ /// | This::is::a::kinda::long::content::number::one | This::is::a::kinda::very::long::content::number::two | This::is::a::kinda::long::content::number::one | This::is::a::kinda::very::long::content::number::two |
+ ///
+ /// I wanna sqdkfnqds f dsqf qds f dsqf dsq f dsq f qds f qds f qds f dsqq f dsf sqdf dsq fds f dsq f dq f ds fq sd fqds f dsq f sqd fsq df sd fdsqfqsd fdsq f dsq f dsqfd s dfq
+ pub struct Foo;
+
+ /// | This::is::a::kinda::very::long::header::number::one | This::is::a::kinda::very::long::header::number::two | This::is::a::kinda::very::long::header::number::one | This::is::a::kinda::very::long::header::number::two |
+ /// | ----------- | ----------- | ----------- | ----------- |
+ /// | This::is::a::kinda::long::content::number::one | This::is::a::kinda::very::long::content::number::two | This::is::a::kinda::long::content::number::one | This::is::a::kinda::very::long::content::number::two |
+ ///
+ /// I wanna sqdkfnqds f dsqf qds f dsqf dsq f dsq f qds f qds f qds f dsqq f dsf sqdf dsq fds f dsq f dq f ds fq sd fqds f dsq f sqd fsq df sd fdsqfqsd fdsq f dsq f dsqfd s dfq
+ impl Foo {
+ pub fn foo(&self) {}
+ }
+}
+
+pub mod summary_table {
+ /// | header 1 | header 2 |
+ /// | -------- | -------- |
+ /// | content | content |
+ pub struct Foo;
+}
+
+pub mod too_long {
+ pub type ReallyLongTypeNameLongLongLong =
+ Option<unsafe extern "C" fn(a: *const u8, b: *const u8) -> *const u8>;
+
+ pub const ReallyLongTypeNameLongLongLongConstBecauseWhyNotAConstRightGigaGigaSupraLong: u32 = 0;
+
+ /// This also has a really long doccomment. Lorem ipsum dolor sit amet,
+ /// consectetur adipiscing elit. Suspendisse id nibh malesuada, hendrerit
+ /// massa vel, tincidunt est. Nulla interdum, sem ac efficitur ornare, arcu
+ /// nunc dignissim nibh, at rutrum diam augue ac mauris. Fusce tincidunt et
+ /// ligula sed viverra. Aenean sed facilisis dui, non volutpat felis. In
+ /// vitae est dui. Donec felis nibh, blandit at nibh eu, tempor suscipit
+ /// nisl. Vestibulum ornare porta libero, eu faucibus purus iaculis ut. Ut
+ /// quis tincidunt nunc, in mollis purus. Nulla sed interdum quam. Nunc
+ /// vitae cursus ex.
+ pub struct SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName {
+ pub a: u32,
+ }
+
+ impl SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName {
+ /// ```
+ /// let x = SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName { a: 0 };
+ /// ```
+ pub fn foo(&self) {}
+ }
+}
+
+pub struct HasALongTraitWithParams {}
+
+pub trait LongTraitWithParamsBananaBananaBanana<T> {}
+
+impl LongTraitWithParamsBananaBananaBanana<usize> for HasALongTraitWithParams {}
+
+#[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "emscripten", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
+pub struct LongItemInfo;
+
+pub trait SimpleTrait {}
+pub struct LongItemInfo2;
+
+/// Some docs.
+#[doc(cfg(any(target_os = "android", target_os = "linux", target_os = "emscripten", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
+impl SimpleTrait for LongItemInfo2 {}
+
+pub struct WhereWhitespace<T>;
+
+impl<T> WhereWhitespace<T> {
+ pub fn new<F>(f: F) -> Self
+ where
+ F: FnMut() -> i32,
+ {}
+}
+
+impl<K, T> Whitespace<&K> for WhereWhitespace<T>
+where
+ K: std::fmt::Debug,
+{
+ type Output = WhereWhitespace<T>;
+ fn index(&self, _key: &K) -> &Self::Output {
+ self
+ }
+}
+
+pub trait Whitespace<Idx>
+where
+ Idx: ?Sized,
+{
+ type Output;
+ fn index(&self, index: Idx) -> &Self::Output;
+}
+
+pub struct ItemInfoAlignmentTest;
+
+impl ItemInfoAlignmentTest {
+ /// This method has docs
+ #[deprecated]
+ pub fn foo() {}
+ #[deprecated]
+ pub fn bar() {}
+}
+
+pub mod scroll_traits {
+ use std::iter::*;
+
+ /// Shamelessly (partially) copied from `std::iter::Iterator`.
+ /// It allows us to check that the scroll is working as expected on "hidden" items.
+ pub trait Iterator {
+ type Item;
+
+ fn next(&mut self) -> Option<Self::Item>;
+ fn size_hint(&self) -> (usize, Option<usize>);
+ fn count(self) -> usize
+ where
+ Self: Sized;
+ fn last(self) -> Option<Self::Item>
+ where
+ Self: Sized;
+ fn advance_by(&mut self, n: usize) -> Result<(), usize>;
+ fn nth(&mut self, n: usize) -> Option<Self::Item>;
+ fn step_by(self, step: usize) -> StepBy<Self>
+ where
+ Self: Sized;
+ fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter>
+ where
+ Self: Sized,
+ U: IntoIterator<Item = Self::Item>;
+ fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter>
+ where
+ Self: Sized,
+ U: IntoIterator;
+ fn intersperse(self, separator: Self::Item) -> Intersperse<Self>
+ where
+ Self: Sized,
+ Self::Item: Clone;
+ fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>
+ where
+ Self: Sized,
+ G: FnMut() -> Self::Item;
+ fn map<B, F>(self, f: F) -> Map<Self, F>
+ where
+ Self: Sized,
+ F: FnMut(Self::Item) -> B;
+ fn for_each<F>(self, f: F)
+ where
+ Self: Sized,
+ F: FnMut(Self::Item);
+ fn filter<P>(self, predicate: P) -> Filter<Self, P>
+ where
+ Self: Sized,
+ P: FnMut(&Self::Item) -> bool;
+ fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
+ where
+ Self: Sized,
+ F: FnMut(Self::Item) -> Option<B>;
+ fn enumerate(self) -> Enumerate<Self>
+ where
+ Self: Sized;
+ fn peekable(self) -> Peekable<Self>
+ where
+ Self: Sized;
+ fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
+ where
+ Self: Sized,
+ P: FnMut(&Self::Item) -> bool;
+ fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
+ where
+ Self: Sized,
+ P: FnMut(&Self::Item) -> bool;
+ fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
+ where
+ Self: Sized,
+ P: FnMut(Self::Item) -> Option<B>;
+ fn skip(self, n: usize) -> Skip<Self>
+ where
+ Self: Sized;
+ fn take(self, n: usize) -> Take<Self>
+ where
+ Self: Sized;
+ fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
+ where
+ Self: Sized,
+ F: FnMut(&mut St, Self::Item) -> Option<B>;
+ fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
+ where
+ Self: Sized,
+ U: IntoIterator,
+ F: FnMut(Self::Item) -> U;
+ fn flatten(self) -> Flatten<Self>
+ where
+ Self: Sized,
+ Self::Item: IntoIterator;
+ fn fuse(self) -> Fuse<Self>
+ where
+ Self: Sized;
+ fn inspect<F>(self, f: F) -> Inspect<Self, F>
+ where
+ Self: Sized,
+ F: FnMut(&Self::Item);
+ fn by_ref(&mut self) -> &mut Self
+ where
+ Self: Sized;
+ fn collect<B: FromIterator<Self::Item>>(self) -> B
+ where
+ Self: Sized;
+ fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
+ where
+ Self: Sized;
+ fn partition<B, F>(self, f: F) -> (B, B)
+ where
+ Self: Sized,
+ B: Default + Extend<Self::Item>,
+ F: FnMut(&Self::Item) -> bool;
+ fn partition_in_place<'a, T: 'a, P>(mut self, predicate: P) -> usize
+ where
+ Self: Sized + DoubleEndedIterator<Item = &'a mut T>,
+ P: FnMut(&T) -> bool;
+ fn is_partitioned<P>(mut self, mut predicate: P) -> bool
+ where
+ Self: Sized,
+ P: FnMut(Self::Item) -> bool;
+ fn fold<B, F>(mut self, init: B, mut f: F) -> B
+ where
+ Self: Sized,
+ F: FnMut(B, Self::Item) -> B;
+ fn reduce<F>(mut self, f: F) -> Option<Self::Item>
+ where
+ Self: Sized,
+ F: FnMut(Self::Item, Self::Item) -> Self::Item;
+ fn all<F>(&mut self, f: F) -> bool
+ where
+ Self: Sized,
+ F: FnMut(Self::Item) -> bool;
+ fn any<F>(&mut self, f: F) -> bool
+ where
+ Self: Sized,
+ F: FnMut(Self::Item) -> bool;
+ fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
+ where
+ Self: Sized,
+ P: FnMut(&Self::Item) -> bool;
+ fn find_map<B, F>(&mut self, f: F) -> Option<B>
+ where
+ Self: Sized,
+ F: FnMut(Self::Item) -> Option<B>;
+ fn position<P>(&mut self, predicate: P) -> Option<usize>
+ where
+ Self: Sized,
+ P: FnMut(Self::Item) -> bool;
+ /// We will scroll to "string" to ensure it scrolls as expected.
+ fn this_is_a_method_with_a_long_name_returning_something() -> String;
+ }
+
+ /// This one doesn't have hidden items (because there are too many) so we can also confirm that it
+ /// scrolls as expected.
+ pub trait TraitWithLongItemsName {
+ fn this_is_a_method_with_a_long_name_returning_something() -> String;
+ }
+}
diff --git a/tests/rustdoc-gui/src/link_to_definition/Cargo.lock b/tests/rustdoc-gui/src/link_to_definition/Cargo.lock
new file mode 100644
index 000000000..e4b4e52d0
--- /dev/null
+++ b/tests/rustdoc-gui/src/link_to_definition/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "link_to_definition"
+version = "0.1.0"
diff --git a/tests/rustdoc-gui/src/link_to_definition/Cargo.toml b/tests/rustdoc-gui/src/link_to_definition/Cargo.toml
new file mode 100644
index 000000000..cdd294d74
--- /dev/null
+++ b/tests/rustdoc-gui/src/link_to_definition/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "link_to_definition"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
diff --git a/tests/rustdoc-gui/src/link_to_definition/lib.rs b/tests/rustdoc-gui/src/link_to_definition/lib.rs
new file mode 100644
index 000000000..419a9ccee
--- /dev/null
+++ b/tests/rustdoc-gui/src/link_to_definition/lib.rs
@@ -0,0 +1,35 @@
+pub fn sub_fn() {
+ barbar();
+}
+fn barbar() {
+ bar(vec![], vec![], vec![], vec![], Bar { a: "a".into(), b: 0 });
+}
+
+pub struct Bar {
+ pub a: String,
+ pub b: u32,
+}
+
+pub fn foo(_b: &Bar) {}
+
+// The goal now is to add
+// a lot of lines so
+// that the next content
+// will be out of the screen
+// to allow us to test that
+// if the anchor changes to
+// something outside of the
+// current view, it'll
+// scroll to it as expected.
+
+// More filling content.
+
+pub fn bar(
+ _a: Vec<String>,
+ _b: Vec<String>,
+ _c: Vec<String>,
+ _d: Vec<String>,
+ _e: Bar,
+) {
+ sub_fn();
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/Cargo.lock b/tests/rustdoc-gui/src/scrape_examples/Cargo.lock
new file mode 100644
index 000000000..7cd6d0844
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "scrape_examples"
+version = "0.1.0"
diff --git a/tests/rustdoc-gui/src/scrape_examples/Cargo.toml b/tests/rustdoc-gui/src/scrape_examples/Cargo.toml
new file mode 100644
index 000000000..aea9b657d
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "scrape_examples"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/tests/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs
new file mode 100644
index 000000000..81a48ac50
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-1.rs
@@ -0,0 +1,13 @@
+fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs
new file mode 100644
index 000000000..c9fdf68d3
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-2.rs
@@ -0,0 +1,13 @@
+fn main() {
+ // ignore-tidy-linelength
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs
new file mode 100644
index 000000000..c9fdf68d3
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-3.rs
@@ -0,0 +1,13 @@
+fn main() {
+ // ignore-tidy-linelength
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs
new file mode 100644
index 000000000..81a48ac50
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-4.rs
@@ -0,0 +1,13 @@
+fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs
new file mode 100644
index 000000000..81a48ac50
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-5.rs
@@ -0,0 +1,13 @@
+fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs
new file mode 100644
index 000000000..81a48ac50
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-6.rs
@@ -0,0 +1,13 @@
+fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs
new file mode 100644
index 000000000..81a48ac50
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/examples/check-many-7.rs
@@ -0,0 +1,13 @@
+fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/examples/check.rs b/tests/rustdoc-gui/src/scrape_examples/examples/check.rs
new file mode 100644
index 000000000..b3f682fe4
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/examples/check.rs
@@ -0,0 +1,26 @@
+fn main() {
+ for i in 0..9 {
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ }
+ scrape_examples::test();
+ for i in 0..9 {
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ println!("hello world!");
+ }
+ scrape_examples::test();
+}
diff --git a/tests/rustdoc-gui/src/scrape_examples/src/lib.rs b/tests/rustdoc-gui/src/scrape_examples/src/lib.rs
new file mode 100644
index 000000000..88b03cf26
--- /dev/null
+++ b/tests/rustdoc-gui/src/scrape_examples/src/lib.rs
@@ -0,0 +1,9 @@
+/// # Examples
+///
+/// ```
+/// test();
+/// test();
+/// ```
+pub fn test() {}
+
+pub fn test_many() {}
diff --git a/tests/rustdoc-gui/src/settings/.cargo/config.toml b/tests/rustdoc-gui/src/settings/.cargo/config.toml
new file mode 100644
index 000000000..bbb8d11a2
--- /dev/null
+++ b/tests/rustdoc-gui/src/settings/.cargo/config.toml
@@ -0,0 +1,2 @@
+[build]
+rustdocflags = ["--default-theme", "ayu"]
diff --git a/tests/rustdoc-gui/src/settings/Cargo.lock b/tests/rustdoc-gui/src/settings/Cargo.lock
new file mode 100644
index 000000000..6f0de1ac1
--- /dev/null
+++ b/tests/rustdoc-gui/src/settings/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "settings"
+version = "0.1.0"
diff --git a/tests/rustdoc-gui/src/settings/Cargo.toml b/tests/rustdoc-gui/src/settings/Cargo.toml
new file mode 100644
index 000000000..c8a211a47
--- /dev/null
+++ b/tests/rustdoc-gui/src/settings/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "settings"
+version = "0.1.0"
+edition = "2018"
+
+[lib]
+path = "lib.rs"
diff --git a/tests/rustdoc-gui/src/settings/lib.rs b/tests/rustdoc-gui/src/settings/lib.rs
new file mode 100644
index 000000000..b76b4321d
--- /dev/null
+++ b/tests/rustdoc-gui/src/settings/lib.rs
@@ -0,0 +1 @@
+pub fn foo() {}
diff --git a/tests/rustdoc-gui/src/staged_api/Cargo.lock b/tests/rustdoc-gui/src/staged_api/Cargo.lock
new file mode 100644
index 000000000..6e8eba56c
--- /dev/null
+++ b/tests/rustdoc-gui/src/staged_api/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "staged_api"
+version = "0.1.0"
diff --git a/tests/rustdoc-gui/src/staged_api/Cargo.toml b/tests/rustdoc-gui/src/staged_api/Cargo.toml
new file mode 100644
index 000000000..b231be6ee
--- /dev/null
+++ b/tests/rustdoc-gui/src/staged_api/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "staged_api"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+path = "lib.rs"
+
+[features]
+default = ["some_feature", "some_other_feature"]
+some_feature = []
+some_other_feature = []
diff --git a/tests/rustdoc-gui/src/staged_api/lib.rs b/tests/rustdoc-gui/src/staged_api/lib.rs
new file mode 100644
index 000000000..5934593a8
--- /dev/null
+++ b/tests/rustdoc-gui/src/staged_api/lib.rs
@@ -0,0 +1,12 @@
+#![feature(staged_api)]
+#![stable(feature = "some_feature", since = "1.3.5")]
+
+#[stable(feature = "some_feature", since = "1.3.5")]
+pub struct Foo {}
+
+impl Foo {
+ #[stable(feature = "some_feature", since = "1.3.5")]
+ pub fn bar() {}
+ #[stable(feature = "some_other_feature", since = "1.3.6")]
+ pub fn yo() {}
+}
diff --git a/tests/rustdoc-gui/src/test_docs/Cargo.lock b/tests/rustdoc-gui/src/test_docs/Cargo.lock
new file mode 100644
index 000000000..6b80f6e88
--- /dev/null
+++ b/tests/rustdoc-gui/src/test_docs/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "test_docs"
+version = "0.1.0"
diff --git a/tests/rustdoc-gui/src/test_docs/Cargo.toml b/tests/rustdoc-gui/src/test_docs/Cargo.toml
new file mode 100644
index 000000000..8be819b76
--- /dev/null
+++ b/tests/rustdoc-gui/src/test_docs/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "test_docs"
+version = "0.1.0"
+edition = "2018"
+
+build = "build.rs"
+
+[lib]
+path = "lib.rs"
+
+[features]
+default = ["some-feature"]
+some-feature = []
diff --git a/tests/rustdoc-gui/src/test_docs/build.rs b/tests/rustdoc-gui/src/test_docs/build.rs
new file mode 100644
index 000000000..16c96ded9
--- /dev/null
+++ b/tests/rustdoc-gui/src/test_docs/build.rs
@@ -0,0 +1,15 @@
+//! generate 2000 constants for testing
+
+use std::{fs::write, path::PathBuf};
+
+fn main() -> std::io::Result<()> {
+ let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR is not defined");
+
+ let mut output = String::new();
+ for i in 0..2000 {
+ let line = format!("/// Some const A{0}\npub const A{0}: isize = 0;\n", i);
+ output.push_str(&*line);
+ };
+
+ write(&[&*out_dir, "huge_amount_of_consts.rs"].iter().collect::<PathBuf>(), output)
+}
diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs
new file mode 100644
index 000000000..512504396
--- /dev/null
+++ b/tests/rustdoc-gui/src/test_docs/lib.rs
@@ -0,0 +1,476 @@
+//! The point of this crate is to be able to have enough different "kinds" of
+//! documentation generated so we can test each different features.
+#![doc(html_playground_url="https://play.rust-lang.org/")]
+
+#![crate_name = "test_docs"]
+#![feature(rustdoc_internals)]
+#![feature(doc_cfg)]
+#![feature(associated_type_defaults)]
+
+/*!
+Enable the feature <span class="stab portability"><code>some-feature</code></span> to enjoy
+this crate even more!
+Enable the feature <span class="stab portability"><code>some-feature</code></span> to enjoy
+this crate even more!
+Enable the feature <span class="stab portability"><code>some-feature</code></span> to enjoy
+this crate even more!
+
+Also, stop using `bar` as it's <span class="stab deprecated" title="">deprecated</span>.
+Also, stop using `bar` as it's <span class="stab deprecated" title="">deprecated</span>.
+Also, stop using `bar` as it's <span class="stab deprecated" title="">deprecated</span>.
+
+Finally, you can use `quz` only on <span class="stab portability"><code>Unix or x86-64</code>
+</span>.
+Finally, you can use `quz` only on <span class="stab portability"><code>Unix or x86-64</code>
+</span>.
+*/
+
+use std::convert::AsRef;
+use std::fmt;
+
+/// Basic function with some code examples:
+///
+/// ```
+/// println!("nothing fancy");
+/// println!("but with two lines!");
+/// ```
+///
+/// A failing to compile one:
+///
+/// ```compile_fail
+/// println!("where did my argument {} go? :'(");
+/// ```
+///
+/// An ignored one:
+///
+/// ```ignore (it's a test)
+/// Let's say I'm just some text will ya?
+/// ```
+///
+/// A failing to run one:
+///
+/// ```should_panic
+/// panic!("tadam");
+/// ```
+///
+/// An inlined `code`!
+pub fn foo() {}
+
+/// Just a normal struct.
+pub struct Foo;
+
+impl Foo {
+ #[must_use]
+ pub fn must_use(&self) -> bool {
+ true
+ }
+}
+
+impl AsRef<str> for Foo {
+ fn as_ref(&self) -> &str {
+ "hello"
+ }
+}
+
+/// Just a normal enum.
+///
+/// # title!
+#[doc(alias = "ThisIsAnAlias")]
+#[non_exhaustive]
+pub enum WhoLetTheDogOut {
+ /// Woof!
+ Woof,
+ /// Meoooooooow...
+ Meow,
+}
+
+/// Who doesn't love to wrap a `format!` call?
+pub fn some_more_function<T: fmt::Debug>(t: &T) -> String {
+ format!("{:?}", t)
+}
+
+/// Woohoo! A trait!
+pub trait AnotherOne {
+ /// Some func 3.
+ fn func3();
+
+ /// Some func 1.
+ fn func1();
+
+ fn another();
+ fn why_not();
+
+ /// Some func 2.
+ fn func2();
+
+ fn hello();
+}
+
+/// ```compile_fail
+/// whatever
+/// ```
+///
+/// Check for "i" signs in lists!
+///
+/// 1. elem 1
+/// 2. test 1
+/// ```compile_fail
+/// fn foo() {}
+/// ```
+/// 3. elem 3
+/// 4. ```ignore (it's a test)
+/// fn foo() {}
+/// ```
+/// 5. elem 5
+///
+/// Final one:
+///
+/// ```ignore (still a test)
+/// let x = 12;
+/// ```
+pub fn check_list_code_block() {}
+
+/// a thing with a label
+#[deprecated(since = "1.0.0", note = "text why this deprecated")]
+#[doc(cfg(unix))]
+pub fn replaced_function() {}
+
+/// Some doc with `code`!
+pub enum AnEnum {
+ WithVariants { and: usize, sub: usize, variants: usize },
+}
+
+#[doc(keyword = "CookieMonster")]
+/// Some keyword.
+pub mod keyword {}
+
+/// Just some type alias.
+pub type SomeType = u32;
+
+pub mod huge_amount_of_consts {
+ include!(concat!(env!("OUT_DIR"), "/huge_amount_of_consts.rs"));
+}
+
+/// Very long code text `hereIgoWithLongTextBecauseWhyNotAndWhyWouldntI`.
+pub mod long_code_block {}
+
+/// Very long code text [`hereIgoWithLongTextBecauseWhyNotAndWhyWouldntI`][lnk].
+///
+/// [lnk]: crate::long_code_block_link
+pub mod long_code_block_link {}
+
+#[macro_export]
+macro_rules! repro {
+ () => {};
+}
+
+pub use crate::repro as repro2;
+
+/// # Top-doc Prose title
+///
+/// Text below title.
+///
+/// ## Top-doc Prose sub-heading
+///
+/// Text below sub-heading.
+///
+/// ### Top-doc Prose sub-sub-heading
+///
+/// Text below sub-sub-heading
+///
+/// #### You know the drill.
+///
+/// More text.
+pub struct HeavilyDocumentedStruct {
+ /// # Title for field
+ /// ## Sub-heading for field
+ pub nothing: (),
+}
+
+/// # Title for struct impl doc
+///
+/// Text below heading.
+///
+/// ## Sub-heading for struct impl doc
+///
+/// Text below sub-heading.
+///
+/// ### Sub-sub-heading for struct impl doc
+///
+/// Text below sub-sub-heading.
+///
+impl HeavilyDocumentedStruct {
+ /// # Title for struct impl-item doc
+ /// Text below title.
+ /// ## Sub-heading for struct impl-item doc
+ /// Text below sub-heading.
+ /// ### Sub-sub-heading for struct impl-item doc
+ /// Text below sub-sub-heading.
+ pub fn do_nothing() {}
+}
+
+/// # Top-doc Prose title
+///
+/// Text below title.
+///
+/// ## Top-doc Prose sub-heading
+///
+/// Text below sub-heading.
+///
+/// ### Top-doc Prose sub-sub-heading
+///
+/// Text below sub-sub-heading
+pub enum HeavilyDocumentedEnum {
+ /// # None prose title
+ /// ## None prose sub-heading
+ None,
+ /// # Wrapped prose title
+ /// ## Wrapped prose sub-heading
+ Wrapped(
+ /// # Wrapped.0 prose title
+ /// ## Wrapped.0 prose sub-heading
+ String,
+ String,
+ ),
+ Structy {
+ /// # Structy prose title
+ /// ## Structy prose sub-heading
+ alpha: String,
+ beta: String,
+ },
+}
+
+/// # Title for enum impl doc
+///
+/// Text below heading.
+///
+/// ## Sub-heading for enum impl doc
+///
+/// Text below sub-heading.
+///
+/// ### Sub-sub-heading for enum impl doc
+///
+/// Text below sub-sub-heading.
+///
+impl HeavilyDocumentedEnum {
+ /// # Title for enum impl-item doc
+ /// Text below title.
+ /// ## Sub-heading for enum impl-item doc
+ /// Text below sub-heading.
+ /// ### Sub-sub-heading for enum impl-item doc
+ /// Text below sub-sub-heading.
+ pub fn do_nothing() {}
+}
+
+/// # Top-doc prose title
+///
+/// Text below heading.
+///
+/// ## Top-doc prose sub-heading
+///
+/// Text below heading.
+pub union HeavilyDocumentedUnion {
+ /// # Title for union variant
+ /// ## Sub-heading for union variant
+ pub nothing: (),
+ pub something: f32,
+}
+
+/// # Title for union impl doc
+/// ## Sub-heading for union impl doc
+impl HeavilyDocumentedUnion {
+ /// # Title for union impl-item doc
+ /// ## Sub-heading for union impl-item doc
+ pub fn do_nothing() {}
+}
+
+/// # Top-doc prose title
+///
+/// Text below heading.
+///
+/// ## Top-doc prose sub-heading
+///
+/// Text below heading.
+#[macro_export]
+macro_rules! heavily_documented_macro {
+ () => {};
+}
+
+pub trait EmptyTrait1 {}
+pub trait EmptyTrait2 {}
+pub trait EmptyTrait3 {}
+
+pub struct HasEmptyTraits{}
+
+impl EmptyTrait1 for HasEmptyTraits {}
+impl EmptyTrait2 for HasEmptyTraits {}
+#[doc(cfg(feature = "some-feature"))]
+impl EmptyTrait3 for HasEmptyTraits {}
+
+mod macros;
+pub use macros::*;
+
+#[doc(alias = "AliasForTheStdReexport")]
+pub use ::std as TheStdReexport;
+
+pub mod details {
+ /// We check the appearance of the `<details>`/`<summary>` in here.
+ ///
+ /// ## Hello
+ ///
+ /// <details>
+ /// <summary><h4>I'm a summary</h4></summary>
+ /// <div>I'm the content of the details!</div>
+ /// </details>
+ pub struct Details;
+
+ impl Details {
+ /// We check the appearance of the `<details>`/`<summary>` in here.
+ ///
+ /// ## Hello
+ ///
+ /// <details>
+ /// <summary><h4>I'm a summary</h4></summary>
+ /// <div>I'm the content of the details!</div>
+ /// </details>
+ pub fn method() {}
+ }
+}
+
+pub mod doc_block_table {
+
+ pub trait DocBlockTableTrait {
+ fn func();
+ }
+
+ /// Struct doc.
+ ///
+ /// | header1 | header2 |
+ /// |--------------------------|--------------------------|
+ /// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
+ /// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
+ /// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
+ /// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
+ pub struct DocBlockTable {}
+
+ impl DocBlockTableTrait for DocBlockTable {
+ /// Trait impl func doc for struct.
+ ///
+ /// | header1 | header2 |
+ /// |--------------------------|--------------------------|
+ /// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
+ fn func() {
+ println!();
+ }
+ }
+
+}
+
+pub struct NotableStructWithLongName<R>(R);
+
+impl<R: std::io::Read> NotableStructWithLongName<R> {
+ pub fn create_an_iterator_from_read(r: R) -> NotableStructWithLongName<R> { Self(r) }
+}
+
+impl<R: std::io::Read> std::iter::Iterator for NotableStructWithLongName<R> {
+ type Item = ();
+
+ fn next(&mut self) -> Option<Self::Item> { () }
+}
+
+pub trait TraitWithNoDocblocks {
+ fn first_fn(&self);
+ fn second_fn(&self);
+}
+
+pub struct TypeWithNoDocblocks;
+
+impl TypeWithNoDocblocks {
+ fn x() -> Option<Self> {
+ Some(Self)
+ }
+ fn y() -> Option<u32> {
+ // code comment
+ let t = Self::x()?;
+ Some(0)
+ }
+}
+
+impl TypeWithNoDocblocks {
+ pub fn first_fn(&self) {}
+ pub fn second_fn<'a>(&'a self) {
+ let x = 12;
+ let y = "a";
+ let z = false;
+ }
+}
+
+pub unsafe fn unsafe_fn() {}
+
+pub fn safe_fn() {}
+
+#[repr(C)]
+pub struct WithGenerics<T: TraitWithNoDocblocks, S = String, E = WhoLetTheDogOut, P = i8> {
+ s: S,
+ t: T,
+ e: E,
+ p: P,
+}
+
+pub struct StructWithPublicUndocumentedFields {
+ pub first: u32,
+ pub second: u32,
+}
+
+pub const CONST: u8 = 0;
+
+pub trait TraitWithoutGenerics {
+ const C: u8 = CONST;
+ type T = SomeType;
+
+ fn foo();
+}
+
+pub mod trait_members {
+ pub trait TraitMembers {
+ /// Some type
+ type Type;
+ /// Some function
+ fn function();
+ /// Some other function
+ fn function2();
+ }
+ pub struct HasTrait;
+ impl TraitMembers for HasTrait {
+ type Type = u8;
+ fn function() {}
+ fn function2() {}
+ }
+}
+
+pub struct TypeWithImplDoc;
+
+/// impl doc
+impl TypeWithImplDoc {
+ /// fn doc
+ pub fn test_fn() {}
+}
+
+/// <sub id="codeblock-sub-1">
+///
+/// ```
+/// one
+/// ```
+///
+/// </sub>
+///
+/// <sub id="codeblock-sub-3">
+///
+/// ```
+/// one
+/// two
+/// three
+/// ```
+///
+/// </sub>
+pub mod codeblock_sub {}
diff --git a/tests/rustdoc-gui/src/test_docs/macros.rs b/tests/rustdoc-gui/src/test_docs/macros.rs
new file mode 100644
index 000000000..07b2b9792
--- /dev/null
+++ b/tests/rustdoc-gui/src/test_docs/macros.rs
@@ -0,0 +1,4 @@
+#[macro_export]
+macro_rules! a{ () => {}}
+#[macro_export]
+macro_rules! b{ () => {}}
diff --git a/tests/rustdoc-gui/stab-badge.goml b/tests/rustdoc-gui/stab-badge.goml
new file mode 100644
index 000000000..50ba1ba62
--- /dev/null
+++ b/tests/rustdoc-gui/stab-badge.goml
@@ -0,0 +1,41 @@
+// All stability badges should have rounded corners and colored backgrounds.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+show-text: true
+define-function: (
+ "check-badge",
+ (theme, background, color),
+ block {
+ local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": |theme|}
+ goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+ assert: ".docblock .stab"
+ assert: ".item-table .stab"
+ assert-css: (".stab", {
+ "border-radius": "3px",
+ "color": |color|,
+ "background-color": |background|,
+ })
+ goto: "file://" + |DOC_PATH| + "/test_docs/fn.replaced_function.html"
+ assert: (".item-info .stab")
+ assert-css: (".stab", {
+ "border-radius": "3px",
+ "color": |color|,
+ "background-color": |background|,
+ })
+ },
+)
+
+call-function: ("check-badge", {
+ "theme": "ayu",
+ "color": "rgb(197, 197, 197)",
+ "background": "rgb(49, 69, 89)",
+})
+call-function: ("check-badge", {
+ "theme": "dark",
+ "color": "rgb(221, 221, 221)",
+ "background": "rgb(49, 69, 89)",
+})
+call-function: ("check-badge", {
+ "theme": "light",
+ "color": "rgb(0, 0, 0)",
+ "background": "rgb(255, 245, 214)",
+})
diff --git a/tests/rustdoc-gui/struct-fields.goml b/tests/rustdoc-gui/struct-fields.goml
new file mode 100644
index 000000000..fa3e16cb8
--- /dev/null
+++ b/tests/rustdoc-gui/struct-fields.goml
@@ -0,0 +1,5 @@
+// This test ensures that each field is on its own line (In other words, they have display: block).
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.StructWithPublicUndocumentedFields.html"
+
+store-property: (first_top, "//*[@id='structfield.first']", "offsetTop")
+assert-property-false: ("//*[@id='structfield.second']", { "offsetTop": |first_top| })
diff --git a/tests/rustdoc-gui/target.goml b/tests/rustdoc-gui/target.goml
new file mode 100644
index 000000000..ca393ee58
--- /dev/null
+++ b/tests/rustdoc-gui/target.goml
@@ -0,0 +1,35 @@
+// Check that the targetted element has the expected styles.
+goto: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html#method.a_method"
+show-text: true
+
+// Confirming that the method is the target.
+assert: "#method\.a_method:target"
+
+define-function: (
+ "check-style",
+ (theme, background, border),
+ block {
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ reload:
+ assert-css: ("#method\.a_method:target", {
+ "background-color": |background|,
+ "border-right": "3px solid " + |border|,
+ })
+ },
+)
+
+call-function: ("check-style", {
+ "theme": "ayu",
+ "background": "rgba(255, 236, 164, 0.06)",
+ "border": "rgba(255, 180, 76, 0.85)",
+})
+call-function: ("check-style", {
+ "theme": "dark",
+ "background": "rgb(73, 74, 61)",
+ "border": "rgb(187, 116, 16)",
+})
+call-function: ("check-style", {
+ "theme": "light",
+ "background": "rgb(253, 255, 211)",
+ "border": "rgb(173, 124, 55)",
+})
diff --git a/tests/rustdoc-gui/theme-change.goml b/tests/rustdoc-gui/theme-change.goml
new file mode 100644
index 000000000..cc47f1f45
--- /dev/null
+++ b/tests/rustdoc-gui/theme-change.goml
@@ -0,0 +1,67 @@
+// Ensures that the theme change is working as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"}
+reload:
+
+store-value: (background_light, "rgb(255, 255, 255)")
+store-value: (background_dark, "rgb(53, 53, 53)")
+store-value: (background_ayu, "rgb(15, 20, 25)")
+
+click: "#settings-menu"
+wait-for: "#theme-ayu"
+click: "#theme-ayu"
+// should be the ayu theme so let's check the color.
+wait-for-css: ("body", { "background-color": |background_ayu| })
+assert-local-storage: { "rustdoc-theme": "ayu" }
+click: "#theme-light"
+// should be the light theme so let's check the color.
+wait-for-css: ("body", { "background-color": |background_light| })
+assert-local-storage: { "rustdoc-theme": "light" }
+click: "#theme-dark"
+// Should be the dark theme so let's check the color.
+wait-for-css: ("body", { "background-color": |background_dark| })
+assert-local-storage: { "rustdoc-theme": "dark" }
+
+local-storage: {
+ "rustdoc-preferred-light-theme": "light",
+ "rustdoc-preferred-dark-theme": "light",
+}
+goto: "file://" + |DOC_PATH| + "/settings.html"
+
+wait-for: "#settings"
+click: "#theme-light"
+wait-for-css: ("body", { "background-color": |background_light| })
+assert-local-storage: { "rustdoc-theme": "light" }
+
+click: "#theme-dark"
+wait-for-css: ("body", { "background-color": |background_dark| })
+assert-local-storage: { "rustdoc-theme": "dark" }
+
+click: "#theme-ayu"
+wait-for-css: ("body", { "background-color": |background_ayu| })
+assert-local-storage: { "rustdoc-theme": "ayu" }
+
+assert-local-storage-false: { "rustdoc-use-system-theme": "true" }
+click: "#theme-system-preference"
+wait-for: ".setting-line:not(.hidden) #preferred-light-theme"
+assert-local-storage: { "rustdoc-use-system-theme": "true" }
+// We click on both preferred light and dark themes to be sure that there is a change.
+click: "#preferred-light-theme-dark"
+click: "#preferred-dark-theme-dark"
+wait-for-css: ("body", { "background-color": |background_dark| })
+
+reload:
+// Ensure that the "preferred themes" are still displayed.
+wait-for: ".setting-line:not(.hidden) #preferred-light-theme"
+click: "#theme-light"
+wait-for-css: ("body", { "background-color": |background_light| })
+assert-local-storage: { "rustdoc-theme": "light" }
+// Ensure it's now hidden again
+wait-for: ".setting-line.hidden #preferred-light-theme"
+// And ensure the theme was rightly set.
+wait-for-css: ("body", { "background-color": |background_light| })
+assert-local-storage: { "rustdoc-theme": "light" }
+
+reload:
+wait-for: "#settings"
+assert: ".setting-line.hidden #preferred-light-theme"
diff --git a/tests/rustdoc-gui/theme-in-history.goml b/tests/rustdoc-gui/theme-in-history.goml
new file mode 100644
index 000000000..10508e86a
--- /dev/null
+++ b/tests/rustdoc-gui/theme-in-history.goml
@@ -0,0 +1,27 @@
+// Ensures that the theme is working when going back in history.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// Set the theme to dark.
+local-storage: {
+ "rustdoc-theme": "dark",
+ "rustdoc-use-system-theme": "false",
+}
+// We reload the page so the local storage settings are being used.
+reload:
+assert-css: ("body", { "background-color": "rgb(53, 53, 53)" })
+assert-local-storage: { "rustdoc-theme": "dark" }
+
+// Now we go to the settings page.
+goto: "file://" + |DOC_PATH| + "/settings.html"
+wait-for: "#settings"
+// We change the theme to "light".
+click: "#theme-light"
+wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" })
+assert-local-storage: { "rustdoc-theme": "light" }
+
+// We go back in history.
+history-go-back:
+// Confirm that we're not on the settings page.
+assert-false: "#settings"
+// Check that the current theme is still "light".
+assert-css: ("body", { "background-color": "rgb(255, 255, 255)" })
+assert-local-storage: { "rustdoc-theme": "light" }
diff --git a/tests/rustdoc-gui/toggle-click-deadspace.goml b/tests/rustdoc-gui/toggle-click-deadspace.goml
new file mode 100644
index 000000000..ac346f25b
--- /dev/null
+++ b/tests/rustdoc-gui/toggle-click-deadspace.goml
@@ -0,0 +1,15 @@
+// This test ensures that clicking on a method summary, but not on the "[-]",
+// doesn't toggle the <details>.
+goto: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html"
+assert-attribute: (".impl-items .toggle", {"open": ""})
+click: "h4.code-header" // This is the position of "pub" in "pub fn a_method"
+assert-attribute: (".impl-items .toggle", {"open": ""})
+click-with-offset: (
+ ".impl-items .toggle summary",
+ {"x": -24, "y": 8}, // This is the position of "[-]" next to that pub fn.
+)
+assert-attribute-false: (".impl-items .toggle", {"open": ""})
+
+// Click the "Trait" part of "impl Trait" and verify it navigates.
+click: "#impl-Trait-for-Foo h3 a:first-of-type"
+assert-text: (".main-heading h1", "Trait lib2::Trait")
diff --git a/tests/rustdoc-gui/toggle-docs-mobile.goml b/tests/rustdoc-gui/toggle-docs-mobile.goml
new file mode 100644
index 000000000..6ce24a81b
--- /dev/null
+++ b/tests/rustdoc-gui/toggle-docs-mobile.goml
@@ -0,0 +1,33 @@
+// Checks that the documentation toggles on mobile have the correct position, style and work
+// as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+size: (433, 600)
+assert-attribute: (".top-doc", {"open": ""})
+click: (4, 270) // This is the position of the top doc comment toggle
+assert-attribute-false: (".top-doc", {"open": ""})
+click: (4, 270)
+assert-attribute: (".top-doc", {"open": ""})
+// To ensure that the toggle isn't over the text, we check that the toggle isn't clicked.
+click: (3, 270)
+assert-attribute: (".top-doc", {"open": ""})
+
+// Assert the position of the toggle on the top doc block.
+assert-position: (".top-doc summary::before", {"x": 4})
+// Assert the position of the toggle on the impl block.
+assert-position: ("#implementations-list > details > summary::before", {"x": 4})
+// Assert the position of the toggle on a method.
+assert-position: (
+ "#trait-implementations-list .impl-items .method-toggle > summary::before",
+ {"x": 4},
+)
+
+// Now we do the same but with a little bigger width
+size: (600, 600)
+assert-attribute: (".top-doc", {"open": ""})
+click: (4, 270) // New Y position since all search elements are back on one line.
+assert-attribute-false: (".top-doc", {"open": ""})
+click: (4, 270)
+assert-attribute: (".top-doc", {"open": ""})
+// To ensure that the toggle isn't over the text, we check that the toggle isn't clicked.
+click: (3, 270)
+assert-attribute: (".top-doc", {"open": ""})
diff --git a/tests/rustdoc-gui/toggle-docs.goml b/tests/rustdoc-gui/toggle-docs.goml
new file mode 100644
index 000000000..c9d236e9b
--- /dev/null
+++ b/tests/rustdoc-gui/toggle-docs.goml
@@ -0,0 +1,75 @@
+// Checks that the documentation toggles have the correct position, style and work as expected.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+assert-attribute: ("#main-content > details.top-doc", {"open": ""})
+assert-text: ("#toggle-all-docs", "[−]")
+click: "#toggle-all-docs"
+wait-for: 50
+// This is now collapsed so there shouldn't be the "open" attribute on details.
+assert-attribute-false: ("#main-content > details.top-doc", {"open": ""})
+assert-text: ("#toggle-all-docs", "[+]")
+assert-css: (
+ "#main-content > details.top-doc > summary",
+ {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
+)
+click: "#toggle-all-docs"
+// Not collapsed anymore so the "open" attribute should be back.
+wait-for-attribute: ("#main-content > details.top-doc", {"open": ""})
+assert-text: ("#toggle-all-docs", "[−]")
+
+// Check that it works on non-module pages as well.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+// We first check that everything is visible.
+assert-text: ("#toggle-all-docs", "[−]")
+assert-attribute: ("#implementations-list details.toggle", {"open": ""}, ALL)
+assert-attribute: ("#trait-implementations-list details.toggle", {"open": ""}, ALL)
+assert-attribute-false: (
+ "#blanket-implementations-list > details.toggle",
+ {"open": ""},
+ ALL,
+)
+
+// We collapse them all.
+click: "#toggle-all-docs"
+wait-for-text: ("#toggle-all-docs", "[+]")
+// We check that all <details> are collapsed (except for the impl block ones).
+assert-attribute-false: ("details.toggle:not(.implementors-toggle)", {"open": ""}, ALL)
+assert-attribute: ("#implementations-list > details.implementors-toggle", {"open": ""})
+// We now check that the other impl blocks are collapsed.
+assert-attribute-false: (
+ "#blanket-implementations-list > details.toggle.implementors-toggle",
+ {"open": ""},
+ ALL,
+)
+// We open them all again.
+click: "#toggle-all-docs"
+wait-for-text: ("#toggle-all-docs", "[−]")
+assert-attribute: ("details.toggle", {"open": ""}, ALL)
+
+// Checking the toggles style.
+show-text: true
+define-function: (
+ "check-color",
+ (theme, filter),
+ block {
+ // Setting the theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ // We reload the page so the local storage settings are being used.
+ reload:
+
+ assert-css: ("details.toggle > summary::before", {
+ "opacity": "0.5",
+ "filter": |filter|,
+ }, ALL)
+ move-cursor-to: "details.toggle summary"
+ assert-css: ("details.toggle > summary:hover::before", {
+ "opacity": "1",
+ "filter": |filter|,
+ })
+ // moving the cursor somewhere else to not mess with next function calls.
+ move-cursor-to: ".search-input"
+ },
+)
+
+call-function: ("check-color", {"theme": "ayu", "filter": "invert(1)"})
+call-function: ("check-color", {"theme": "dark", "filter": "invert(1)"})
+call-function: ("check-color", {"theme": "light", "filter": "none"})
diff --git a/tests/rustdoc-gui/toggle-implementors.goml b/tests/rustdoc-gui/toggle-implementors.goml
new file mode 100644
index 000000000..8ff5b91af
--- /dev/null
+++ b/tests/rustdoc-gui/toggle-implementors.goml
@@ -0,0 +1,4 @@
+// This test ensures that the implementors toggle are not open by default.
+goto: "file://" + |DOC_PATH| + "/implementors/trait.Whatever.html"
+
+assert-attribute-false: ("#implementors-list > details", {"open": ""}, ALL)
diff --git a/tests/rustdoc-gui/toggled-open-implementations.goml b/tests/rustdoc-gui/toggled-open-implementations.goml
new file mode 100644
index 000000000..000293b55
--- /dev/null
+++ b/tests/rustdoc-gui/toggled-open-implementations.goml
@@ -0,0 +1,5 @@
+// This tests that the "implementations" section on struct/enum pages
+// has all the implementations toggled open by default, so users can
+// find method names in those implementations with Ctrl-F.
+goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
+assert-attribute: (".toggle.implementors-toggle", {"open": ""})
diff --git a/tests/rustdoc-gui/trait-sidebar-item-order.goml b/tests/rustdoc-gui/trait-sidebar-item-order.goml
new file mode 100644
index 000000000..e5d023544
--- /dev/null
+++ b/tests/rustdoc-gui/trait-sidebar-item-order.goml
@@ -0,0 +1,13 @@
+// Checks that the elements in the sidebar are alphabetically sorted.
+
+// We need to disable this check because `implementors/test_docs/trait.AnotherOne.js`
+// doesn't exist.
+fail-on-request-error: false
+
+goto: "file://" + |DOC_PATH| + "/test_docs/trait.AnotherOne.html"
+assert-text: (".sidebar-elems section .block li:nth-of-type(1) > a", "another")
+assert-text: (".sidebar-elems section .block li:nth-of-type(2) > a", "func1")
+assert-text: (".sidebar-elems section .block li:nth-of-type(3) > a", "func2")
+assert-text: (".sidebar-elems section .block li:nth-of-type(4) > a", "func3")
+assert-text: (".sidebar-elems section .block li:nth-of-type(5) > a", "hello")
+assert-text: (".sidebar-elems section .block li:nth-of-type(6) > a", "why_not")
diff --git a/tests/rustdoc-gui/type-declation-overflow.goml b/tests/rustdoc-gui/type-declation-overflow.goml
new file mode 100644
index 000000000..644429c01
--- /dev/null
+++ b/tests/rustdoc-gui/type-declation-overflow.goml
@@ -0,0 +1,76 @@
+// ignore-tidy-linelength
+// This test ensures that the items declaration content overflow is handled inside the <pre> directly.
+
+// We need to disable this check because
+// `implementors/test_docs/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.js`
+// doesn't exist.
+fail-on-request-error: false
+
+goto: "file://" + |DOC_PATH| + "/lib2/long_trait/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.html"
+// We set a fixed size so there is no chance of "random" resize.
+size: (1100, 800)
+// Logically, the <body> scroll width should be the width of the window.
+assert-property: ("body", {"scrollWidth": "1100"})
+// However, since there is overflow in the type declaration, its scroll width is bigger.
+assert-property: (".item-decl pre", {"scrollWidth": "1324"})
+
+// In the table-ish view on the module index, the name should not be wrapped more than necessary.
+goto: "file://" + |DOC_PATH| + "/lib2/too_long/index.html"
+assert-property: (".item-table .struct", {"offsetWidth": "684"})
+
+// We now make the same check on type declaration...
+goto: "file://" + |DOC_PATH| + "/lib2/too_long/type.ReallyLongTypeNameLongLongLong.html"
+assert-property: ("body", {"scrollWidth": "1100"})
+// We now check that the section width hasn't grown because of it.
+assert-property: ("#main-content", {"scrollWidth": "840"})
+// And now checking that it has scrollable content.
+assert-property: (".item-decl pre", {"scrollWidth": "1103"})
+
+// ... and constant.
+// On a sidenote, it also checks that the (very) long title isn't changing the docblock width.
+goto: "file://" + |DOC_PATH| + "/lib2/too_long/constant.ReallyLongTypeNameLongLongLongConstBecauseWhyNotAConstRightGigaGigaSupraLong.html"
+assert-property: ("body", {"scrollWidth": "1100"})
+// We now check that the section width hasn't grown because of it.
+assert-property: ("#main-content", {"scrollWidth": "840"})
+// And now checking that it has scrollable content.
+assert-property: (".item-decl pre", {"scrollWidth": "950"})
+
+// On mobile:
+size: (600, 600)
+goto: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
+// It shouldn't have an overflow in the topbar either.
+store-property: (scrollWidth, ".mobile-topbar h2", "scrollWidth")
+assert-property: (".mobile-topbar h2", {"clientWidth": |scrollWidth|})
+assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"})
+
+// Check wrapping for top main-heading h1 and out-of-band.
+// On desktop, they wrap when too big.
+size: (1100, 800)
+goto: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+goto: "file://" + |DOC_PATH| + "/lib2/index.html"
+compare-elements-position: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+// make sure there is a gap between them
+compare-elements-position-near-false: (".main-heading h1", ".main-heading .out-of-band", {"x": 550})
+
+// On mobile, they always wrap.
+size: (600, 600)
+goto: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+goto: "file://" + |DOC_PATH| + "/lib2/index.html"
+compare-elements-position-false: (".main-heading h1", ".main-heading .out-of-band", ("y"))
+
+// Now we will check that the scrolling is working.
+// First on an item with "hidden methods".
+goto: "file://" + |DOC_PATH| + "/lib2/scroll_traits/trait.Iterator.html"
+
+click: ".item-decl .type-contents-toggle"
+assert-property: (".item-decl > pre", {"scrollLeft": 0})
+scroll-to: "//*[@class='item-decl']//details/a[text()='String']"
+assert-property-false: (".item-decl > pre", {"scrollLeft": 0})
+
+// Then on an item without "hidden methods".
+goto: "file://" + |DOC_PATH| + "/lib2/scroll_traits/trait.TraitWithLongItemsName.html"
+assert-property: (".item-decl > pre", {"scrollLeft": 0})
+scroll-to: "//*[@class='item-decl']//code/a[text()='String']"
+assert-property-false: (".item-decl > pre", {"scrollLeft": 0})
diff --git a/tests/rustdoc-gui/unsafe-fn.goml b/tests/rustdoc-gui/unsafe-fn.goml
new file mode 100644
index 000000000..d3a672ddd
--- /dev/null
+++ b/tests/rustdoc-gui/unsafe-fn.goml
@@ -0,0 +1,28 @@
+// Check position and color of the `<sup>` for unsafe elements.
+goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
+// If the text isn't displayed, the browser doesn't compute color style correctly...
+show-text: true
+
+compare-elements-property: (
+ "//a[@title='test_docs::safe_fn fn']/..",
+ "//a[@title='test_docs::unsafe_fn fn']/..",
+ ["clientHeight"]
+)
+
+define-function: (
+ "sup-check",
+ // `theme` is the theme being tested.
+ // `color` is the expected color of the `<sup>` element.
+ (theme, color),
+ block {
+ // Set the theme.
+ local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
+ // We reload the page so the local storage settings are being used.
+ reload:
+ assert-css: (".item-left sup", {"color": |color|})
+ },
+)
+
+call-function: ("sup-check", ("dark", "rgb(221, 221, 221)"))
+call-function: ("sup-check", ("ayu", "rgb(197, 197, 197)"))
+call-function: ("sup-check", ("light", "rgb(0, 0, 0)"))
diff --git a/tests/rustdoc-gui/where-whitespace.goml b/tests/rustdoc-gui/where-whitespace.goml
new file mode 100644
index 000000000..41596a9bc
--- /dev/null
+++ b/tests/rustdoc-gui/where-whitespace.goml
@@ -0,0 +1,27 @@
+// This test ensures that the where conditions are correctly displayed.
+goto: "file://" + |DOC_PATH| + "/lib2/trait.Whitespace.html"
+show-text: true
+// First, we check in the trait definition if the where clause is "on its own" (not on the same
+// line than "pub trait Whitespace<Idx>").
+compare-elements-position-false: (".item-decl code", ".where.fmt-newline", ("y"))
+// And that the code following it isn't on the same line either.
+compare-elements-position-false: (".item-decl .fn", ".where.fmt-newline", ("y"))
+
+goto: "file://" + |DOC_PATH| + "/lib2/struct.WhereWhitespace.html"
+// We make the screen a bit wider to ensure that the trait impl is on one line.
+size: (915, 915)
+
+compare-elements-position-false: ("#method\.new .fn", "#method\.new .where.fmt-newline", ("y"))
+// We ensure that both the trait name and the struct name are on the same line in
+// "impl<K, T> Whitespace<&K> for WhereWhitespace<T>".
+compare-elements-position: (
+ "#trait-implementations-list .impl h3 .trait",
+ "#trait-implementations-list .impl h3 .struct",
+ ("y"),
+)
+// And we now check that the where condition isn't on the same line.
+compare-elements-position-false: (
+ "#trait-implementations-list .impl h3 .trait",
+ "#trait-implementations-list .impl h3 .where.fmt-newline",
+ ("y"),
+)