Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions java/test/org/openqa/selenium/environment/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ java_test_suite(
deps = [
":environment",
":test-base",
artifact("org.assertj:assertj-core"),
artifact("org.junit.jupiter:junit-jupiter-api"),
artifact("org.jspecify:jspecify"),
] + firefox["deps"] + JUNIT5_DEPS,
Expand Down
43 changes: 39 additions & 4 deletions java/test/org/openqa/selenium/environment/webserver/Page.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class Page {
private String[] bodyParts = {};
private @Nullable String onLoad;
private @Nullable String onBeforeUnload;
private boolean doctype = false;

public Page withTitle(String title) {
this.title = title;
Expand Down Expand Up @@ -58,7 +59,44 @@ public Page withOnBeforeUnload(String onBeforeUnload) {
return this;
}

/**
* Opt-in to a standards-mode document shape: a leading {@code <!DOCTYPE html>} and the {@code
* <script>}/{@code <style>} blocks moved inside {@code <head>}, instead of after it closes.
* Layout-sensitive tests should use this to get deterministic parsing/quirks-mode behavior;
* default output is unchanged for backward compatibility with existing callers.
*/
public Page withDoctype() {
this.doctype = true;
return this;
}

public String toString() {
String body =
String.format(
"<body %s %s>",
onLoad == null ? "" : String.format("onload='%s'", onLoad),
onBeforeUnload == null ? "" : String.format("onbeforeunload='%s'", onBeforeUnload));

if (doctype) {
return String.join(
"\n",
"<!DOCTYPE html>",
"<html>",
"<head>",
String.format("<title>%s</title>", title),
"<script type='text/javascript'>",
String.join("\n", scripts),
"</script>",
"<style>",
String.join("\n", styles),
"</style>",
"</head>",
body,
String.join("\n", bodyParts),
"</body>",
"</html>");
}

return String.join(
"\n",
"<html>",
Expand All @@ -71,10 +109,7 @@ public String toString() {
"<style>",
String.join("\n", styles),
"</style>",
String.format(
"<body %s %s>",
onLoad == null ? "" : String.format("onload='%s'", onLoad),
onBeforeUnload == null ? "" : String.format("onbeforeunload='%s'", onBeforeUnload)),
body,
String.join("\n", bodyParts),
"</body>",
"</html>");
Expand Down
65 changes: 65 additions & 0 deletions java/test/org/openqa/selenium/environment/webserver/PageTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.environment.webserver;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;

class PageTest {

@Test
void defaultToStringHasNoDoctypeAndMatchesLegacyShape() {
String page =
new Page().withTitle("Legacy").withScripts("var a = 1;").withStyles("body { }").toString();

// No doctype: default output stays in quirks mode for backward compatibility.
assertThat(page).doesNotContain("<!DOCTYPE");

// Legacy shape/ordering contract: <script>/<style> sit after </head> closes, not inside it.
// containsSubsequence asserts presence AND order together, so a missing "</head>" fails here
// instead of silently passing the way raw indexOf().isLessThan() would (both return -1).
assertThat(page).containsSubsequence("</head>", "<script");
assertThat(page).containsSubsequence("</head>", "<style");

// Key content is still present, in the expected tag order, without pinning incidental
// whitespace (e.g. the exact "<body >" spacing or the blank line for an empty body).
assertThat(page)
.contains("<title>Legacy</title>")
.contains("var a = 1;")
.contains("body { }")
.contains("<body")
.contains("</body>")
.contains("</html>");
}

@Test
void withDoctypeEmitsStandardsModeShapeWithScriptAndStyleInsideHead() {
String page =
new Page()
.withDoctype()
.withTitle("Standards")
.withScripts("var a = 1;")
.withStyles("body { }")
.toString();

assertThat(page).startsWith("<!DOCTYPE html>");
assertThat(page.indexOf("</style>")).isLessThan(page.indexOf("</head>"));
assertThat(page.indexOf("</script>")).isLessThan(page.indexOf("</head>"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -367,4 +367,42 @@ void nearLocatorShouldNotFindFarElements() {
.isThrownBy(() -> driver.findElement(with(By.id("rect4")).near(rect)))
.withMessageContaining("Cannot locate an element using");
}

@Test
void shouldFindCellThatSharesACollapsedBorderWithTheAnchorCell() {
String url =
appServer.create(
new Page()
.withDoctype()
.withTitle("Collapsed Borders")
.withStyles(
" table {\n"
+ " border-collapse: collapse;\n"
+ " table-layout: fixed;\n"
+ " width: 450px;\n"
+ " }\n"
+ " td, th {\n"
+ " border: 1px solid black;\n"
+ " box-sizing: border-box;\n"
+ " width: 150px;\n"
+ " height: 40px;\n"
+ " padding: 4px;\n"
+ " }")
.withBody(
"<table>\n"
+ " <tr><th id=\"company\">Company</th><th"
+ " id=\"contact\">Contact</th><th id=\"country\">Country</th></tr>\n"
+ " <tr><td id=\"alfreds\">Alfreds Futterkiste</td><td"
+ " id=\"maria\">Maria Anders</td><td id=\"germany\">Germany</td></tr>\n"
+ " <tr><td id=\"centro\">Centro comercial Moctezuma</td><td"
+ " id=\"francisco\">Francisco Chang</td><td"
+ " id=\"mexico\">Mexico</td></tr>\n"
+ " </table>"));
driver.get(url);

WebElement cell =
driver.findElement(with(tagName("td")).toRightOf(By.id("alfreds")).below(By.id("contact")));

Comment thread
qodo-code-review[bot] marked this conversation as resolved.
assertThat(cell.getAttribute("id")).isEqualTo("maria");
}
}