From 15668805a74400a14b507969bfa23ca17620a9da Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Mon, 5 May 2025 12:18:54 -0400 Subject: [PATCH 01/11] [Java] Updated Design Patterns pages to use codeblocks --- .../design_strategies/BotPattern.java | 26 + .../LoadableComponentBasic.java | 69 +++ .../LoadableComponentExtended.java | 96 ++++ .../design_strategies/PageObjectTests.java | 30 + .../design_strategies/ProjectPage.java | 28 + .../design_strategies/SecuredPage.java | 52 ++ .../test_practices/design_strategies.en.md | 516 ++++++------------ .../test_practices/design_strategies.ja.md | 515 ++++++----------- .../test_practices/design_strategies.pt-br.md | 514 ++++++----------- .../test_practices/design_strategies.zh-cn.md | 514 ++++++----------- 10 files changed, 1029 insertions(+), 1331 deletions(-) create mode 100644 examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java create mode 100644 examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java create mode 100644 examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java create mode 100644 examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java create mode 100644 examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java create mode 100644 examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java b/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java new file mode 100644 index 00000000000..61171e6f1a8 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java @@ -0,0 +1,26 @@ +public class ActionBot { + private final WebDriver driver; + + public ActionBot(WebDriver driver) { + this.driver = driver; + } + + public void click(By locator) { + driver.findElement(locator).click(); + } + + public void submit(By locator) { + driver.findElement(locator).submit(); + } + + /** + * Type something into an input field. WebDriver doesn't normally clear these + * before typing, so this method does that first. It also sends a return key + * to move the focus out of the element. + */ + public void type(By locator, String text) { + WebElement element = driver.findElement(locator); + element.clear(); + element.sendKeys(text + "\n"); + } +} \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java new file mode 100644 index 00000000000..cabe80bf9ae --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java @@ -0,0 +1,69 @@ +package com.example.webdriver; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +public class EditIssue { + + private final WebDriver driver; + + public EditIssue(WebDriver driver) { + this.driver = driver; + } + + public void setTitle(String title) { + WebElement field = driver.findElement(By.id("issue_title"))); + clearAndType(field, title); + } + + public void setBody(String body) { + WebElement field = driver.findElement(By.id("issue_body")); + clearAndType(field, body); + } + + public void setHowToReproduce(String howToReproduce) { + WebElement field = driver.findElement(By.id("issue_form_repro-command")); + clearAndType(field, howToReproduce); + } + + public void setLogOutput(String logOutput) { + WebElement field = driver.findElement(By.id("issue_form_logs")); + clearAndType(field, logOutput); + } + + public void setOperatingSystem(String operatingSystem) { + WebElement field = driver.findElement(By.id("issue_form_operating-system")); + clearAndType(field, operatingSystem); + } + + public void setSeleniumVersion(String seleniumVersion) { + WebElement field = driver.findElement(By.id("issue_form_selenium-version")); + clearAndType(field, logOutput); + } + + public void setBrowserVersion(String browserVersion) { + WebElement field = driver.findElement(By.id("issue_form_browser-versions")); + clearAndType(field, browserVersion); + } + + public void setDriverVersion(String driverVersion) { + WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); + clearAndType(field, driverVersion); + } + + public void setUsingGrid(String usingGrid) { + WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); + clearAndType(field, usingGrid); + } + + public IssueList submit() { + driver.findElement(By.cssSelector("button[type='submit']")).click(); + return new IssueList(driver); + } + + private void clearAndType(WebElement field, String text) { + field.clear(); + field.sendKeys(text); + } +} \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java new file mode 100644 index 00000000000..a6ee3a812c1 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java @@ -0,0 +1,96 @@ +package com.example.webdriver; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.PageFactory; + +import static junit.framework.Assert.assertTrue; + +public class EditIssue extends LoadableComponent { + + private final WebDriver driver; + + // By default the PageFactory will locate elements with the same name or id + // as the field. Since the issue_title element has an id attribute of "issue_title" + // we don't need any additional annotations. + private WebElement issue_title; + + // But we'd prefer a different name in our code than "issue_body", so we use the + // FindBy annotation to tell the PageFactory how to locate the element. + @FindBy(id = "issue_body") private WebElement body; + + public EditIssue(WebDriver driver) { + this.driver = driver; + + // This call sets the WebElement fields. + PageFactory.initElements(driver, this); + } + + @Override + protected void load() { + driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); + } + + @Override + protected void isLoaded() throws Error { + String url = driver.getCurrentUrl(); + assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); + } + + public void setHowToReproduce(String howToReproduce) { + WebElement field = driver.findElement(By.id("issue_form_repro-command")); + clearAndType(field, howToReproduce); + } + + public void setLogOutput(String logOutput) { + WebElement field = driver.findElement(By.id("issue_form_logs")); + clearAndType(field, logOutput); + } + + public void setOperatingSystem(String operatingSystem) { + WebElement field = driver.findElement(By.id("issue_form_operating-system")); + clearAndType(field, operatingSystem); + } + + public void setSeleniumVersion(String seleniumVersion) { + WebElement field = driver.findElement(By.id("issue_form_selenium-version")); + clearAndType(field, logOutput); + } + + public void setBrowserVersion(String browserVersion) { + WebElement field = driver.findElement(By.id("issue_form_browser-versions")); + clearAndType(field, browserVersion); + } + + public void setDriverVersion(String driverVersion) { + WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); + clearAndType(field, driverVersion); + } + + public void setUsingGrid(String usingGrid) { + WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); + clearAndType(field, usingGrid); + } + + public IssueList submit() { + driver.findElement(By.cssSelector("button[type='submit']")).click(); + return new IssueList(driver); + } + + private void clearAndType(WebElement field, String text) { + field.clear(); + field.sendKeys(text); + } +} + +EditIssue page = new EditIssue(driver).get(); + +// Further Updates + + @Override + protected void load() { + securedPage.get(); + + driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); + } \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java b/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java new file mode 100644 index 00000000000..724e2e995d5 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java @@ -0,0 +1,30 @@ +import org.junit.jupiter.api.Test; + +public class FooTest { + private EditIssue editIssue; + + // Removed @Before + public void prepareComponents() { + WebDriver driver = new FirefoxDriver(); + + ProjectPage project = new ProjectPage(driver, "selenium"); + SecuredPage securedPage = new SecuredPage(driver, project, "example", "top secret"); + editIssue = new EditIssue(driver, securedPage); + } + + // Removed @Test + public void demonstrateNestedLoadableComponents() { + editIssue.get(); + + editIssue.title.sendKeys('Title'); + editIssue.body.sendKeys('What Happened'); + editIssue.setHowToReproduce('How to Reproduce'); + editIssue.setLogOutput('Log Output'); + editIssue.setOperatingSystem('Operating System'); + editIssue.setSeleniumVersion('Selenium Version'); + editIssue.setBrowserVersion('Browser Version'); + editIssue.setDriverVersion('Driver Version'); + editIssue.setUsingGrid('I Am Using Grid'); + } + +} \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java b/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java new file mode 100644 index 00000000000..0f7b5b253f2 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java @@ -0,0 +1,28 @@ +package com.example.webdriver; + +import org.openqa.selenium.WebDriver; + +import static org.junit.Assert.assertTrue; + +public class ProjectPage extends LoadableComponent { + + private final WebDriver driver; + private final String projectName; + + public ProjectPage(WebDriver driver, String projectName) { + this.driver = driver; + this.projectName = projectName; + } + + @Override + protected void load() { + driver.get("http://" + projectName + ".googlecode.com/"); + } + + @Override + protected void isLoaded() throws Error { + String url = driver.getCurrentUrl(); + + assertTrue(url.contains(projectName)); + } +} \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java b/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java new file mode 100644 index 00000000000..e5040c1c462 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java @@ -0,0 +1,52 @@ +package com.example.webdriver; + +import org.openqa.selenium.By; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import static org.junit.Assert.fail; + +public class SecuredPage extends LoadableComponent { + + private final WebDriver driver; + private final LoadableComponent parent; + private final String username; + private final String password; + + public SecuredPage(WebDriver driver, LoadableComponent parent, String username, String password) { + this.driver = driver; + this.parent = parent; + this.username = username; + this.password = password; + } + + @Override + protected void load() { + parent.get(); + + String originalUrl = driver.getCurrentUrl(); + + // Sign in + driver.get("https://www.google.com/accounts/ServiceLogin?service=code"); + driver.findElement(By.name("Email")).sendKeys(username); + WebElement passwordField = driver.findElement(By.name("Passwd")); + passwordField.sendKeys(password); + passwordField.submit(); + + // Now return to the original URL + driver.get(originalUrl); + } + + @Override + protected void isLoaded() throws Error { + // If you're signed in, you have the option of picking a different login. + // Let's check for the presence of that. + + try { + WebElement div = driver.findElement(By.id("multilogin-dropdown")); + } catch (NoSuchElementException e) { + fail("Cannot locate user name link"); + } + } +} \ No newline at end of file diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.en.md b/website_and_docs/content/documentation/test_practices/design_strategies.en.md index 2ddf9965cae..8c392e827b7 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.en.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.en.md @@ -37,103 +37,75 @@ As an example of a UI that we'd like to model, take a look at the [new issue](https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+) page. From the point of view of a test author, this offers the service of being able to file a new issue. A basic Page Object would look like: -```java -package com.example.webdriver; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -public class EditIssue { - - private final WebDriver driver; - - public EditIssue(WebDriver driver) { - this.driver = driver; - } - - public void setTitle(String title) { - WebElement field = driver.findElement(By.id("issue_title"))); - clearAndType(field, title); - } - - public void setBody(String body) { - WebElement field = driver.findElement(By.id("issue_body")); - clearAndType(field, body); - } - - public void setHowToReproduce(String howToReproduce) { - WebElement field = driver.findElement(By.id("issue_form_repro-command")); - clearAndType(field, howToReproduce); - } - - public void setLogOutput(String logOutput) { - WebElement field = driver.findElement(By.id("issue_form_logs")); - clearAndType(field, logOutput); - } - - public void setOperatingSystem(String operatingSystem) { - WebElement field = driver.findElement(By.id("issue_form_operating-system")); - clearAndType(field, operatingSystem); - } - - public void setSeleniumVersion(String seleniumVersion) { - WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); - } - - public void setBrowserVersion(String browserVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-versions")); - clearAndType(field, browserVersion); - } - - public void setDriverVersion(String driverVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); - clearAndType(field, driverVersion); - } - - public void setUsingGrid(String usingGrid) { - WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); - clearAndType(field, usingGrid); - } - - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } - - private void clearAndType(WebElement field, String text) { - field.clear(); - field.sendKeys(text); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java#L1-L69" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} In order to turn this into a LoadableComponent, all we need to do is to set that as the base type: -```java -public class EditIssue extends LoadableComponent { - // rest of class ignored for now -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L10" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This signature looks a little unusual, but all it means is that this class represents a LoadableComponent that loads the EditIssue page. By extending this base class, we need to implement two new methods: -```java - @Override - protected void load() { - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } - - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); - } -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L30-L39" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} The `load` method is used to navigate to the page, whilst the `isLoaded` method is used to determine whether we are on the right page. Although the method looks like it should return @@ -143,102 +115,51 @@ it's possible to give users of the class clear information that can be used to d With a little rework, our PageObject looks like: -```java -package com.example.webdriver; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static junit.framework.Assert.assertTrue; - -public class EditIssue extends LoadableComponent { - - private final WebDriver driver; - - // By default the PageFactory will locate elements with the same name or id - // as the field. Since the issue_title element has an id attribute of "issue_title" - // we don't need any additional annotations. - private WebElement issue_title; - - // But we'd prefer a different name in our code than "issue_body", so we use the - // FindBy annotation to tell the PageFactory how to locate the element. - @FindBy(id = "issue_body") private WebElement body; - - public EditIssue(WebDriver driver) { - this.driver = driver; - - // This call sets the WebElement fields. - PageFactory.initElements(driver, this); - } - - @Override - protected void load() { - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } - - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); - } - - public void setHowToReproduce(String howToReproduce) { - WebElement field = driver.findElement(By.id("issue_form_repro-command")); - clearAndType(field, howToReproduce); - } - - public void setLogOutput(String logOutput) { - WebElement field = driver.findElement(By.id("issue_form_logs")); - clearAndType(field, logOutput); - } - - public void setOperatingSystem(String operatingSystem) { - WebElement field = driver.findElement(By.id("issue_form_operating-system")); - clearAndType(field, operatingSystem); - } - - public void setSeleniumVersion(String seleniumVersion) { - WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); - } - - public void setBrowserVersion(String browserVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-versions")); - clearAndType(field, browserVersion); - } - - public void setDriverVersion(String driverVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); - clearAndType(field, driverVersion); - } - - public void setUsingGrid(String usingGrid) { - WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); - clearAndType(field, usingGrid); - } - - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } - - private void clearAndType(WebElement field, String text) { - field.clear(); - field.sendKeys(text); - } -} - -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L1-L85" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} That doesn't seem to have bought us much, right? One thing it has done is encapsulate the information about how to navigate to the page into the page itself, meaning that this information's not scattered through the code base. It also means that we can do this in our tests: -```java -EditIssue page = new EditIssue(driver).get(); -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L87" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This call will cause the driver to navigate to the page if that's necessary. @@ -260,141 +181,81 @@ What would this look like in code? For a start, each logical component would have its own class. The "load" method in each of them would "get" the parent. The end result, in addition to the EditIssue class above is: -ProjectPage.java: - -```java -package com.example.webdriver; - -import org.openqa.selenium.WebDriver; - -import static org.junit.Assert.assertTrue; - -public class ProjectPage extends LoadableComponent { - - private final WebDriver driver; - private final String projectName; - - public ProjectPage(WebDriver driver, String projectName) { - this.driver = driver; - this.projectName = projectName; - } - - @Override - protected void load() { - driver.get("http://" + projectName + ".googlecode.com/"); - } +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); +ProjectPage.java: - assertTrue(url.contains(projectName)); - } -} -``` +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java#L1-L38" >}} and SecuredPage.java: -```java -package com.example.webdriver; - -import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -import static org.junit.Assert.fail; - -public class SecuredPage extends LoadableComponent { - - private final WebDriver driver; - private final LoadableComponent parent; - private final String username; - private final String password; - - public SecuredPage(WebDriver driver, LoadableComponent parent, String username, String password) { - this.driver = driver; - this.parent = parent; - this.username = username; - this.password = password; - } - - @Override - protected void load() { - parent.get(); - - String originalUrl = driver.getCurrentUrl(); - - // Sign in - driver.get("https://www.google.com/accounts/ServiceLogin?service=code"); - driver.findElement(By.name("Email")).sendKeys(username); - WebElement passwordField = driver.findElement(By.name("Passwd")); - passwordField.sendKeys(password); - passwordField.submit(); - - // Now return to the original URL - driver.get(originalUrl); - } - - @Override - protected void isLoaded() throws Error { - // If you're signed in, you have the option of picking a different login. - // Let's check for the presence of that. - - try { - WebElement div = driver.findElement(By.id("multilogin-dropdown")); - } catch (NoSuchElementException e) { - fail("Cannot locate user name link"); - } - } -} -``` +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java#L1-L52" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} The "load" method in EditIssue now looks like: -```java - @Override - protected void load() { - securedPage.get(); - - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L91-L96" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This shows that the components are all "nested" within each other. A call to `get()` in EditIssue will cause all its dependencies to load too. The example usage: -```java -public class FooTest { - private EditIssue editIssue; - - @Before - public void prepareComponents() { - WebDriver driver = new FirefoxDriver(); - - ProjectPage project = new ProjectPage(driver, "selenium"); - SecuredPage securedPage = new SecuredPage(driver, project, "example", "top secret"); - editIssue = new EditIssue(driver, securedPage); - } - - @Test - public void demonstrateNestedLoadableComponents() { - editIssue.get(); - - editIssue.title.sendKeys('Title'); - editIssue.body.sendKeys('What Happened'); - editIssue.setHowToReproduce('How to Reproduce'); - editIssue.setLogOutput('Log Output'); - editIssue.setOperatingSystem('Operating System'); - editIssue.setSeleniumVersion('Selenium Version'); - editIssue.setBrowserVersion('Browser Version'); - editIssue.setDriverVersion('Driver Version'); - editIssue.setUsingGrid('I Am Using Grid'); - } - -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java#L1-L30" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} If you're using a library such as [Guiceberry](https://github.com/zorzella/guiceberry) in your tests, the preamble of setting up the PageObjects can be omitted leading to nice, clear, readable tests. @@ -412,34 +273,26 @@ A "bot" is an action-oriented abstraction over the raw Selenium APIs. This means that if you find that commands aren't doing the Right Thing for your app, it's easy to change them. As an example: -```java -public class ActionBot { - private final WebDriver driver; - - public ActionBot(WebDriver driver) { - this.driver = driver; - } - - public void click(By locator) { - driver.findElement(locator).click(); - } - - public void submit(By locator) { - driver.findElement(locator).submit(); - } - - /** - * Type something into an input field. WebDriver doesn't normally clear these - * before typing, so this method does that first. It also sends a return key - * to move the focus out of the element. - */ - public void type(By locator, String text) { - WebElement element = driver.findElement(locator); - element.clear(); - element.sendKeys(text + "\n"); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java#L1-L26" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} Once these abstractions have been built and duplication in your tests identified, it's possible to layer PageObjects on top of bots. @@ -447,11 +300,6 @@ Once these abstractions have been built and duplication in your tests identified {{< tabpane text=true >}} {{< tab header="Python" >}} - -An example of `python + pytest + selenium` which implemented "**Action Bot**, **Loadable Component** and **Page Object**". - -A `pytest` fixture `chrome_driver`. - {{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L6-L26" >}} {{< /tab >}} {{< tab header="Java" >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.ja.md b/website_and_docs/content/documentation/test_practices/design_strategies.ja.md index 48d08bda107..15e5bf7056f 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.ja.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.ja.md @@ -34,102 +34,74 @@ LoadableComponentは、PageObjectsの作成の負担を軽減することを目 テスト作成者の観点から、これは新しい問題を提出できるサービスを提供します。 基本的なページオブジェクトは次のようになります。 -```java -package com.example.webdriver; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -public class EditIssue { - - private final WebDriver driver; - - public EditIssue(WebDriver driver) { - this.driver = driver; - } - - public void setTitle(String title) { - WebElement field = driver.findElement(By.id("issue_title"))); - clearAndType(field, title); - } - - public void setBody(String body) { - WebElement field = driver.findElement(By.id("issue_body")); - clearAndType(field, body); - } - - public void setHowToReproduce(String howToReproduce) { - WebElement field = driver.findElement(By.id("issue_form_repro-command")); - clearAndType(field, howToReproduce); - } - - public void setLogOutput(String logOutput) { - WebElement field = driver.findElement(By.id("issue_form_logs")); - clearAndType(field, logOutput); - } - - public void setOperatingSystem(String operatingSystem) { - WebElement field = driver.findElement(By.id("issue_form_operating-system")); - clearAndType(field, operatingSystem); - } - - public void setSeleniumVersion(String seleniumVersion) { - WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); - } - - public void setBrowserVersion(String browserVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-versions")); - clearAndType(field, browserVersion); - } - - public void setDriverVersion(String driverVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); - clearAndType(field, driverVersion); - } - - public void setUsingGrid(String usingGrid) { - WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); - clearAndType(field, usingGrid); - } - - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } - - private void clearAndType(WebElement field, String text) { - field.clear(); - field.sendKeys(text); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java#L1-L69" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} これをLoadableComponentに変換するには、これを基本型として設定するだけです。 -```java -public class EditIssue extends LoadableComponent { - // rest of class ignored for now -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L10" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} この署名は少し変わっているように見えますが、それは、このクラスがEditIssueページをロードするLoadableComponentを表すことを意味します。 このベースクラスを拡張することにより、2つの新しいメソッドを実装する必要があります。 -```java - @Override - protected void load() { - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } - - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); - } -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L30-L39" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} `load` メソッドはページに移動するために使用され、 `isLoaded` メソッドは正しいページにいるかどうかを判断するために使用されます。 このメソッドはブール値を返す必要があるように見えますが、代わりにJUnitのAssertクラスを使用して一連のアサーションを実行します。 @@ -138,103 +110,52 @@ public class EditIssue extends LoadableComponent { 少し手直しすると、PageObjectは次のようになります。 -```java -package com.example.webdriver; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static junit.framework.Assert.assertTrue; - -public class EditIssue extends LoadableComponent { - - private final WebDriver driver; - - // By default the PageFactory will locate elements with the same name or id - // as the field. Since the issue_title element has an id attribute of "issue_title" - // we don't need any additional annotations. - private WebElement issue_title; - - // But we'd prefer a different name in our code than "issue_body", so we use the - // FindBy annotation to tell the PageFactory how to locate the element. - @FindBy(id = "issue_body") private WebElement body; - - public EditIssue(WebDriver driver) { - this.driver = driver; - - // This call sets the WebElement fields. - PageFactory.initElements(driver, this); - } - - @Override - protected void load() { - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } - - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); - } - - public void setHowToReproduce(String howToReproduce) { - WebElement field = driver.findElement(By.id("issue_form_repro-command")); - clearAndType(field, howToReproduce); - } - - public void setLogOutput(String logOutput) { - WebElement field = driver.findElement(By.id("issue_form_logs")); - clearAndType(field, logOutput); - } - - public void setOperatingSystem(String operatingSystem) { - WebElement field = driver.findElement(By.id("issue_form_operating-system")); - clearAndType(field, operatingSystem); - } - - public void setSeleniumVersion(String seleniumVersion) { - WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); - } - - public void setBrowserVersion(String browserVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-versions")); - clearAndType(field, browserVersion); - } - - public void setDriverVersion(String driverVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); - clearAndType(field, driverVersion); - } - - public void setUsingGrid(String usingGrid) { - WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); - clearAndType(field, usingGrid); - } - - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } - - private void clearAndType(WebElement field, String text) { - field.clear(); - field.sendKeys(text); - } -} - -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L1-L85" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} それは私たちをあまり信じられなかったようですよね? これまでに行ったことの1つは、ページに移動する方法に関する情報をページ自体にカプセル化することです。 つまり、この情報はコードベース全体に散らばっていません。 これは、テストで下記を実行できることも意味します。 -```java -EditIssue page = new EditIssue(driver).get(); -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L87" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} この呼び出しにより、ドライバーは必要に応じてページに移動します。 @@ -256,140 +177,81 @@ LoadableComponentsは、他のLoadableComponentsと組み合わせて使用す それぞれの "load" メソッドは、親クラスを "get" します。 上記のEditIssueクラスに加えて、最終結果は次のようになります。 -ProjectPage.java: - -```java -package com.example.webdriver; - -import org.openqa.selenium.WebDriver; - -import static org.junit.Assert.assertTrue; - -public class ProjectPage extends LoadableComponent { - - private final WebDriver driver; - private final String projectName; - - public ProjectPage(WebDriver driver, String projectName) { - this.driver = driver; - this.projectName = projectName; - } - - @Override - protected void load() { - driver.get("http://" + projectName + ".googlecode.com/"); - } +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); +ProjectPage.java: - assertTrue(url.contains(projectName)); - } -} -``` +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java#L1-L38" >}} and SecuredPage.java: -```java -package com.example.webdriver; - -import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -import static org.junit.Assert.fail; - -public class SecuredPage extends LoadableComponent { - - private final WebDriver driver; - private final LoadableComponent parent; - private final String username; - private final String password; - - public SecuredPage(WebDriver driver, LoadableComponent parent, String username, String password) { - this.driver = driver; - this.parent = parent; - this.username = username; - this.password = password; - } - - @Override - protected void load() { - parent.get(); - - String originalUrl = driver.getCurrentUrl(); - - // Sign in - driver.get("https://www.google.com/accounts/ServiceLogin?service=code"); - driver.findElement(By.name("Email")).sendKeys(username); - WebElement passwordField = driver.findElement(By.name("Passwd")); - passwordField.sendKeys(password); - passwordField.submit(); - - // Now return to the original URL - driver.get(originalUrl); - } - - @Override - protected void isLoaded() throws Error { - // If you're signed in, you have the option of picking a different login. - // Let's check for the presence of that. - - try { - WebElement div = driver.findElement(By.id("multilogin-dropdown")); - } catch (NoSuchElementException e) { - fail("Cannot locate user name link"); - } - } -} -``` +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java#L1-L52" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} EditIssueの "load" メソッドは次のようになります。 -```java - @Override - protected void load() { - securedPage.get(); - - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L91-L96" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} これは、コンポーネントがすべて相互に "ネストされている" ことを示しています。 EditIssueで `get()` を呼び出すと、そのすべての依存関係も読み込まれます。 使用例: -```java -public class FooTest { - private EditIssue editIssue; - - @Before - public void prepareComponents() { - WebDriver driver = new FirefoxDriver(); - - ProjectPage project = new ProjectPage(driver, "selenium"); - SecuredPage securedPage = new SecuredPage(driver, project, "example", "top secret"); - editIssue = new EditIssue(driver, securedPage); - } - - @Test - public void demonstrateNestedLoadableComponents() { - editIssue.get(); - - editIssue.title.sendKeys('Title'); - editIssue.body.sendKeys('What Happened'); - editIssue.setHowToReproduce('How to Reproduce'); - editIssue.setLogOutput('Log Output'); - editIssue.setOperatingSystem('Operating System'); - editIssue.setSeleniumVersion('Selenium Version'); - editIssue.setBrowserVersion('Browser Version'); - editIssue.setDriverVersion('Driver Version'); - editIssue.setUsingGrid('I Am Using Grid'); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java#L1-L30" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} テストで [Guiceberry](https://github.com/zorzella/guiceberry) などのライブラリを使用している場合は、PageObjectsの設定の前文を省略して、わかりやすく読みやすいテストを作成できます。 @@ -404,34 +266,26 @@ PageObjectsは、テストでの重複を減らすための便利な方法です つまり、コマンドがアプリに対して正しいことをしていないことがわかった場合、コマンドを簡単に変更できます。 例として: -```java -public class ActionBot { - private final WebDriver driver; - - public ActionBot(WebDriver driver) { - this.driver = driver; - } - - public void click(By locator) { - driver.findElement(locator).click(); - } - - public void submit(By locator) { - driver.findElement(locator).submit(); - } - - /** - * Type something into an input field. WebDriver doesn't normally clear these - * before typing, so this method does that first. It also sends a return key - * to move the focus out of the element. - */ - public void type(By locator, String text) { - WebElement element = driver.findElement(locator); - element.clear(); - element.sendKeys(text + "\n"); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java#L1-L26" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} これらの抽象化が構築され、テストでの重複が特定されると、ボットの上にPageObjectsを階層化することができます。 @@ -439,11 +293,6 @@ public class ActionBot { {{< tabpane text=true >}} {{< tab header="Python" >}} - -**Action Bot**、**Loadable Component**、および **Page Object** を実装した `python + pytest + selenium` の例です。 - -A `pytest` fixture `chrome_driver`. - {{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L6-L26" >}} {{< /tab >}} {{< tab header="Java" >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md b/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md index d712eba42f2..603ace565bd 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md @@ -40,103 +40,75 @@ the [new issue](https://github.com/SeleniumHQ/selenium/issues/new?assignees=&lab the point of view of a test author, this offers the service of being able to file a new issue. A basic Page Object would look like: -```java -package com.example.webdriver; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -public class EditIssue { - - private final WebDriver driver; - - public EditIssue(WebDriver driver) { - this.driver = driver; - } - - public void setTitle(String title) { - WebElement field = driver.findElement(By.id("issue_title"))); - clearAndType(field, title); - } - - public void setBody(String body) { - WebElement field = driver.findElement(By.id("issue_body")); - clearAndType(field, body); - } - - public void setHowToReproduce(String howToReproduce) { - WebElement field = driver.findElement(By.id("issue_form_repro-command")); - clearAndType(field, howToReproduce); - } - - public void setLogOutput(String logOutput) { - WebElement field = driver.findElement(By.id("issue_form_logs")); - clearAndType(field, logOutput); - } - - public void setOperatingSystem(String operatingSystem) { - WebElement field = driver.findElement(By.id("issue_form_operating-system")); - clearAndType(field, operatingSystem); - } - - public void setSeleniumVersion(String seleniumVersion) { - WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); - } - - public void setBrowserVersion(String browserVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-versions")); - clearAndType(field, browserVersion); - } - - public void setDriverVersion(String driverVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); - clearAndType(field, driverVersion); - } - - public void setUsingGrid(String usingGrid) { - WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); - clearAndType(field, usingGrid); - } - - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } - - private void clearAndType(WebElement field, String text) { - field.clear(); - field.sendKeys(text); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java#L1-L69" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} In order to turn this into a LoadableComponent, all we need to do is to set that as the base type: -```java -public class EditIssue extends LoadableComponent { - // rest of class ignored for now -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L10" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This signature looks a little unusual, but it all means is that this class represents a LoadableComponent that loads the EditIssue page. By extending this base class, we need to implement two new methods: -```java - @Override - protected void load() { - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } - - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); - } -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L30-L39" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} The `load` method is used to navigate to the page, whilst the `isLoaded` method is used to determine whether we are on the right page. Although the @@ -148,103 +120,52 @@ used to debug tests. With a little rework, our PageObject looks like: -```java -package com.example.webdriver; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static junit.framework.Assert.assertTrue; - -public class EditIssue extends LoadableComponent { - - private final WebDriver driver; - - // By default the PageFactory will locate elements with the same name or id - // as the field. Since the issue_title element has an id attribute of "issue_title" - // we don't need any additional annotations. - private WebElement issue_title; - - // But we'd prefer a different name in our code than "issue_body", so we use the - // FindBy annotation to tell the PageFactory how to locate the element. - @FindBy(id = "issue_body") private WebElement body; - - public EditIssue(WebDriver driver) { - this.driver = driver; - - // This call sets the WebElement fields. - PageFactory.initElements(driver, this); - } - - @Override - protected void load() { - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } - - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); - } - - public void setHowToReproduce(String howToReproduce) { - WebElement field = driver.findElement(By.id("issue_form_repro-command")); - clearAndType(field, howToReproduce); - } - - public void setLogOutput(String logOutput) { - WebElement field = driver.findElement(By.id("issue_form_logs")); - clearAndType(field, logOutput); - } - - public void setOperatingSystem(String operatingSystem) { - WebElement field = driver.findElement(By.id("issue_form_operating-system")); - clearAndType(field, operatingSystem); - } - - public void setSeleniumVersion(String seleniumVersion) { - WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); - } - - public void setBrowserVersion(String browserVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-versions")); - clearAndType(field, browserVersion); - } - - public void setDriverVersion(String driverVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); - clearAndType(field, driverVersion); - } - - public void setUsingGrid(String usingGrid) { - WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); - clearAndType(field, usingGrid); - } - - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } - - private void clearAndType(WebElement field, String text) { - field.clear(); - field.sendKeys(text); - } -} - -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L1-L85" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} That doesn't seem to have bought us much, right? One thing it has done is encapsulate the information about how to navigate to the page into the page itself, meaning that this information's not scattered through the code base. It also means that we can do this in our tests: -```java -EditIssue page = new EditIssue(driver).get(); -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L87" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This call will cause the driver to navigate to the page if that's necessary. @@ -266,138 +187,79 @@ What would this look like in code? For a start, each logical component would have its own class. The "load" method in each of them would "get" the parent. The end result, in addition to the EditIssue class above is: -ProjectPage.java: - -```java -package com.example.webdriver; - -import org.openqa.selenium.WebDriver; - -import static org.junit.Assert.assertTrue; - -public class ProjectPage extends LoadableComponent { - - private final WebDriver driver; - private final String projectName; - - public ProjectPage(WebDriver driver, String projectName) { - this.driver = driver; - this.projectName = projectName; - } - - @Override - protected void load() { - driver.get("http://" + projectName + ".googlecode.com/"); - } +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); +ProjectPage.java: - assertTrue(url.contains(projectName)); - } -} -``` +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java#L1-L38" >}} and SecuredPage.java: -```java -package com.example.webdriver; - -import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -import static org.junit.Assert.fail; - -public class SecuredPage extends LoadableComponent { - - private final WebDriver driver; - private final LoadableComponent parent; - private final String username; - private final String password; - - public SecuredPage(WebDriver driver, LoadableComponent parent, String username, String password) { - this.driver = driver; - this.parent = parent; - this.username = username; - this.password = password; - } - - @Override - protected void load() { - parent.get(); - - String originalUrl = driver.getCurrentUrl(); - - // Sign in - driver.get("https://www.google.com/accounts/ServiceLogin?service=code"); - driver.findElement(By.name("Email")).sendKeys(username); - WebElement passwordField = driver.findElement(By.name("Passwd")); - passwordField.sendKeys(password); - passwordField.submit(); - - // Now return to the original URL - driver.get(originalUrl); - } - - @Override - protected void isLoaded() throws Error { - // If you're signed in, you have the option of picking a different login. - // Let's check for the presence of that. - - try { - WebElement div = driver.findElement(By.id("multilogin-dropdown")); - } catch (NoSuchElementException e) { - fail("Cannot locate user name link"); - } - } -} -``` +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java#L1-L52" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} The "load" method in EditIssue now looks like: -```java - @Override - protected void load() { - securedPage.get(); - - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L91-L96" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This shows that the components are all "nested" within each other. A call to `get()` in EditIssue will cause all its dependencies to load too. The example usage: -```java -public class FooTest { - private EditIssue editIssue; - - @Before - public void prepareComponents() { - WebDriver driver = new FirefoxDriver(); - - ProjectPage project = new ProjectPage(driver, "selenium"); - SecuredPage securedPage = new SecuredPage(driver, project, "example", "top secret"); - editIssue = new EditIssue(driver, securedPage); - } - - @Test - public void demonstrateNestedLoadableComponents() { - editIssue.get(); - - editIssue.title.sendKeys('Title'); - editIssue.body.sendKeys('What Happened'); - editIssue.setHowToReproduce('How to Reproduce'); - editIssue.setLogOutput('Log Output'); - editIssue.setOperatingSystem('Operating System'); - editIssue.setSeleniumVersion('Selenium Version'); - editIssue.setBrowserVersion('Browser Version'); - editIssue.setDriverVersion('Driver Version'); - editIssue.setUsingGrid('I Am Using Grid'); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java#L1-L30" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} If you're using a library such as [Guiceberry](https://github.com/zorzella/guiceberry) in your tests, the preamble of setting up the PageObjects can be omitted leading to nice, clear, readable tests. @@ -411,34 +273,26 @@ Although PageObjects are a useful way of reducing duplication in your tests, it' A "bot" is an action-oriented abstraction over the raw Selenium APIs. This means that if you find that commands aren't doing the Right Thing for your app, it's easy to change them. As an example: -```java -public class ActionBot { - private final WebDriver driver; - - public ActionBot(WebDriver driver) { - this.driver = driver; - } - - public void click(By locator) { - driver.findElement(locator).click(); - } - - public void submit(By locator) { - driver.findElement(locator).submit(); - } - - /** - * Type something into an input field. WebDriver doesn't normally clear these - * before typing, so this method does that first. It also sends a return key - * to move the focus out of the element. - */ - public void type(By locator, String text) { - WebElement element = driver.findElement(locator); - element.clear(); - element.sendKeys(text + "\n"); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java#L1-L26" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} Once these abstractions have been built and duplication in your tests identified, it's possible to layer PageObjects on top of bots. @@ -447,10 +301,6 @@ Once these abstractions have been built and duplication in your tests identified {{< tabpane text=true >}} {{< tab header="Python" >}} -An example of `python + pytest + selenium` which implemented "**Action Bot**, **Loadable Component** and **Page Object**". - -A `pytest` fixture `chrome_driver`. - {{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L6-L26" >}} {{< /tab >}} {{< tab header="Java" >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md b/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md index ef7460035ff..19af94dde3d 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md @@ -49,103 +49,75 @@ the [new issue](https://github.com/SeleniumHQ/selenium/issues/new?assignees=&lab From the point of view of a test author, this offers the service of being able to file a new issue. A basic Page Object would look like: -```java -package com.example.webdriver; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -public class EditIssue { - - private final WebDriver driver; - - public EditIssue(WebDriver driver) { - this.driver = driver; - } - - public void setTitle(String title) { - WebElement field = driver.findElement(By.id("issue_title"))); - clearAndType(field, title); - } - - public void setBody(String body) { - WebElement field = driver.findElement(By.id("issue_body")); - clearAndType(field, body); - } - - public void setHowToReproduce(String howToReproduce) { - WebElement field = driver.findElement(By.id("issue_form_repro-command")); - clearAndType(field, howToReproduce); - } - - public void setLogOutput(String logOutput) { - WebElement field = driver.findElement(By.id("issue_form_logs")); - clearAndType(field, logOutput); - } - - public void setOperatingSystem(String operatingSystem) { - WebElement field = driver.findElement(By.id("issue_form_operating-system")); - clearAndType(field, operatingSystem); - } - - public void setSeleniumVersion(String seleniumVersion) { - WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); - } - - public void setBrowserVersion(String browserVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-versions")); - clearAndType(field, browserVersion); - } - - public void setDriverVersion(String driverVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); - clearAndType(field, driverVersion); - } - - public void setUsingGrid(String usingGrid) { - WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); - clearAndType(field, usingGrid); - } - - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } - - private void clearAndType(WebElement field, String text) { - field.clear(); - field.sendKeys(text); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java#L1-L69" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} In order to turn this into a LoadableComponent, all we need to do is to set that as the base type: -```java -public class EditIssue extends LoadableComponent { - // rest of class ignored for now -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L10" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This signature looks a little unusual, but it all means is that this class represents a LoadableComponent that loads the EditIssue page. By extending this base class, we need to implement two new methods: -```java - @Override - protected void load() { - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } - - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); - } -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L30-L39" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} The `load` method is used to navigate to the page, whilst the `isLoaded` method is used to determine whether we are on the right page. Although the @@ -157,103 +129,52 @@ can be used to debug tests. With a little rework, our PageObject looks like: -```java -package com.example.webdriver; -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.PageFactory; - -import static junit.framework.Assert.assertTrue; - -public class EditIssue extends LoadableComponent { - - private final WebDriver driver; - - // By default the PageFactory will locate elements with the same name or id - // as the field. Since the issue_title element has an id attribute of "issue_title" - // we don't need any additional annotations. - private WebElement issue_title; - - // But we'd prefer a different name in our code than "issue_body", so we use the - // FindBy annotation to tell the PageFactory how to locate the element. - @FindBy(id = "issue_body") private WebElement body; - - public EditIssue(WebDriver driver) { - this.driver = driver; - - // This call sets the WebElement fields. - PageFactory.initElements(driver, this); - } - - @Override - protected void load() { - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } - - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); - } - - public void setHowToReproduce(String howToReproduce) { - WebElement field = driver.findElement(By.id("issue_form_repro-command")); - clearAndType(field, howToReproduce); - } - - public void setLogOutput(String logOutput) { - WebElement field = driver.findElement(By.id("issue_form_logs")); - clearAndType(field, logOutput); - } - - public void setOperatingSystem(String operatingSystem) { - WebElement field = driver.findElement(By.id("issue_form_operating-system")); - clearAndType(field, operatingSystem); - } - - public void setSeleniumVersion(String seleniumVersion) { - WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); - } - - public void setBrowserVersion(String browserVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-versions")); - clearAndType(field, browserVersion); - } - - public void setDriverVersion(String driverVersion) { - WebElement field = driver.findElement(By.id("issue_form_browser-driver-versions")); - clearAndType(field, driverVersion); - } - - public void setUsingGrid(String usingGrid) { - WebElement field = driver.findElement(By.id("issue_form_selenium-grid-version")); - clearAndType(field, usingGrid); - } - - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } - - private void clearAndType(WebElement field, String text) { - field.clear(); - field.sendKeys(text); - } -} - -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L1-L85" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} That doesn't seem to have bought us much, right? One thing it has done is encapsulate the information about how to navigate to the page into the page itself, meaning that this information's not scattered through the code base. It also means that we can do this in our tests: -```java -EditIssue page = new EditIssue(driver).get(); -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L87" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This call will cause the driver to navigate to the page if that's necessary. @@ -275,139 +196,80 @@ What would this look like in code? For a start, each logical component would have its own class. The "load" method in each of them would "get" the parent. The end result, in addition to the EditIssue class above is: -ProjectPage.java: - -```java -package com.example.webdriver; - -import org.openqa.selenium.WebDriver; - -import static org.junit.Assert.assertTrue; - -public class ProjectPage extends LoadableComponent { - - private final WebDriver driver; - private final String projectName; - - public ProjectPage(WebDriver driver, String projectName) { - this.driver = driver; - this.projectName = projectName; - } - - @Override - protected void load() { - driver.get("http://" + projectName + ".googlecode.com/"); - } +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} - @Override - protected void isLoaded() throws Error { - String url = driver.getCurrentUrl(); +ProjectPage.java: - assertTrue(url.contains(projectName)); - } -} -``` +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java#L1-L38" >}} and SecuredPage.java: -```java -package com.example.webdriver; - -import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; - -import static org.junit.Assert.fail; - -public class SecuredPage extends LoadableComponent { - - private final WebDriver driver; - private final LoadableComponent parent; - private final String username; - private final String password; - - public SecuredPage(WebDriver driver, LoadableComponent parent, String username, String password) { - this.driver = driver; - this.parent = parent; - this.username = username; - this.password = password; - } - - @Override - protected void load() { - parent.get(); - - String originalUrl = driver.getCurrentUrl(); - - // Sign in - driver.get("https://www.google.com/accounts/ServiceLogin?service=code"); - driver.findElement(By.name("Email")).sendKeys(username); - WebElement passwordField = driver.findElement(By.name("Passwd")); - passwordField.sendKeys(password); - passwordField.submit(); - - // Now return to the original URL - driver.get(originalUrl); - } - - @Override - protected void isLoaded() throws Error { - // If you're signed in, you have the option of picking a different login. - // Let's check for the presence of that. - - try { - WebElement div = driver.findElement(By.id("multilogin-dropdown")); - } catch (NoSuchElementException e) { - fail("Cannot locate user name link"); - } - } -} -``` +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java#L1-L52" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} The "load" method in EditIssue now looks like: -```java - @Override - protected void load() { - securedPage.get(); - - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L91-L96" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} This shows that the components are all "nested" within each other. A call to `get()` in EditIssue will cause all its dependencies to load too. The example usage: -```java -public class FooTest { - private EditIssue editIssue; - - @Before - public void prepareComponents() { - WebDriver driver = new FirefoxDriver(); - - ProjectPage project = new ProjectPage(driver, "selenium"); - SecuredPage securedPage = new SecuredPage(driver, project, "example", "top secret"); - editIssue = new EditIssue(driver, securedPage); - } - - @Test - public void demonstrateNestedLoadableComponents() { - editIssue.get(); - - editIssue.title.sendKeys('Title'); - editIssue.body.sendKeys('What Happened'); - editIssue.setHowToReproduce('How to Reproduce'); - editIssue.setLogOutput('Log Output'); - editIssue.setOperatingSystem('Operating System'); - editIssue.setSeleniumVersion('Selenium Version'); - editIssue.setBrowserVersion('Browser Version'); - editIssue.setDriverVersion('Driver Version'); - editIssue.setUsingGrid('I Am Using Grid'); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java#L1-L30" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} If you're using a library such as [Guiceberry](https://github.com/zorzella/guiceberry) in your tests, the preamble of setting up the PageObjects can be omitted leading to nice, clear, readable tests. @@ -425,34 +287,26 @@ A "bot" is an action-oriented abstraction over the raw Selenium APIs. This means that if you find that commands aren't doing the Right Thing for your app, it's easy to change them. As an example: -```java -public class ActionBot { - private final WebDriver driver; - - public ActionBot(WebDriver driver) { - this.driver = driver; - } - - public void click(By locator) { - driver.findElement(locator).click(); - } - - public void submit(By locator) { - driver.findElement(locator).submit(); - } - - /** - * Type something into an input field. WebDriver doesn't normally clear these - * before typing, so this method does that first. It also sends a return key - * to move the focus out of the element. - */ - public void type(By locator, String text) { - WebElement element = driver.findElement(locator); - element.clear(); - element.sendKeys(text + "\n"); - } -} -``` +{{< tabpane text=true >}} +{{< tab header="Python" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Java" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java#L1-L26" >}} +{{< /tab >}} +{{< tab header="CSharp" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Ruby" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="JavaScript" >}} +{{< badge-code >}} +{{< /tab >}} +{{< tab header="Kotlin" >}} +{{< badge-code >}} +{{< /tab >}} +{{< /tabpane >}} Once these abstractions have been built and duplication in your tests identified, it's possible to layer PageObjects on top of bots. @@ -462,10 +316,6 @@ identified, it's possible to layer PageObjects on top of bots. {{< tabpane text=true >}} {{< tab header="Python" >}} -一个用例 使用 `python + pytest + selenium` 实现了设计策略 "**Action Bot**, **Loadable Component** 和 **Page Object**". - -A `pytest` fixture `chrome_driver`. - {{< gh-codeblock path="/examples/python/tests/design_strategy/using_best_practice.py#L6-L26" >}} {{< /tab >}} {{< tab header="Java" >}} From f1aff332ffe3f33f9b4f548e414e75502e16fe31 Mon Sep 17 00:00:00 2001 From: Simon Benzer <69980130+shbenzer@users.noreply.github.com> Date: Mon, 5 May 2025 12:39:29 -0400 Subject: [PATCH 02/11] Update PageObjectTests.java --- .../design_strategies/PageObjectTests.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java b/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java index 724e2e995d5..73e082c744c 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/PageObjectTests.java @@ -1,30 +1,30 @@ -import org.junit.jupiter.api.Test; +// import org.junit.jupiter.api.Test; -public class FooTest { - private EditIssue editIssue; +// public class FooTest { +// private EditIssue editIssue; - // Removed @Before - public void prepareComponents() { - WebDriver driver = new FirefoxDriver(); +// @Before +// public void prepareComponents() { +// WebDriver driver = new FirefoxDriver(); - ProjectPage project = new ProjectPage(driver, "selenium"); - SecuredPage securedPage = new SecuredPage(driver, project, "example", "top secret"); - editIssue = new EditIssue(driver, securedPage); - } +// ProjectPage project = new ProjectPage(driver, "selenium"); +// SecuredPage securedPage = new SecuredPage(driver, project, "example", "top secret"); +// editIssue = new EditIssue(driver, securedPage); +// } - // Removed @Test - public void demonstrateNestedLoadableComponents() { - editIssue.get(); +// @Test +// public void demonstrateNestedLoadableComponents() { +// editIssue.get(); - editIssue.title.sendKeys('Title'); - editIssue.body.sendKeys('What Happened'); - editIssue.setHowToReproduce('How to Reproduce'); - editIssue.setLogOutput('Log Output'); - editIssue.setOperatingSystem('Operating System'); - editIssue.setSeleniumVersion('Selenium Version'); - editIssue.setBrowserVersion('Browser Version'); - editIssue.setDriverVersion('Driver Version'); - editIssue.setUsingGrid('I Am Using Grid'); - } +// editIssue.title.sendKeys('Title'); +// editIssue.body.sendKeys('What Happened'); +// editIssue.setHowToReproduce('How to Reproduce'); +// editIssue.setLogOutput('Log Output'); +// editIssue.setOperatingSystem('Operating System'); +// editIssue.setSeleniumVersion('Selenium Version'); +// editIssue.setBrowserVersion('Browser Version'); +// editIssue.setDriverVersion('Driver Version'); +// editIssue.setUsingGrid('I Am Using Grid'); +// } -} \ No newline at end of file +// } From 3e1ff487507563a8e0beffabfc8f5bae190a33ab Mon Sep 17 00:00:00 2001 From: Simon Benzer <69980130+shbenzer@users.noreply.github.com> Date: Mon, 5 May 2025 12:44:22 -0400 Subject: [PATCH 03/11] LoadableComponentBasic.java typo --- .../selenium/design_strategies/LoadableComponentBasic.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java index cabe80bf9ae..08058e2bbc5 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java @@ -13,7 +13,7 @@ public EditIssue(WebDriver driver) { } public void setTitle(String title) { - WebElement field = driver.findElement(By.id("issue_title"))); + WebElement field = driver.findElement(By.id("issue_title")); clearAndType(field, title); } @@ -66,4 +66,4 @@ private void clearAndType(WebElement field, String text) { field.clear(); field.sendKeys(text); } -} \ No newline at end of file +} From b9ee30f03a3b2d3a79365e5f4cb39a93cd976952 Mon Sep 17 00:00:00 2001 From: Simon Benzer <69980130+shbenzer@users.noreply.github.com> Date: Mon, 5 May 2025 12:45:00 -0400 Subject: [PATCH 04/11] Update LoadableComponentExtended.java --- .../design_strategies/LoadableComponentExtended.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java index a6ee3a812c1..ad65d4d0161 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java @@ -88,9 +88,9 @@ private void clearAndType(WebElement field, String text) { // Further Updates - @Override - protected void load() { - securedPage.get(); + // @Override + // protected void load() { + // securedPage.get(); - driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); - } \ No newline at end of file + // driver.get("https://github.com/SeleniumHQ/selenium/issues/new?assignees=&labels=I-defect%2Cneeds-triaging&projects=&template=bug-report.yml&title=%5B%F0%9F%90%9B+Bug%5D%3A+"); + // } From d2a84d25399758c8177f26f889846c6136aa45fb Mon Sep 17 00:00:00 2001 From: Simon Benzer <69980130+shbenzer@users.noreply.github.com> Date: Mon, 5 May 2025 12:48:11 -0400 Subject: [PATCH 05/11] Update LoadableComponentExtended.java --- .../selenium/design_strategies/LoadableComponentExtended.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java index ad65d4d0161..6e6891c4f5a 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java @@ -84,7 +84,7 @@ private void clearAndType(WebElement field, String text) { } } -EditIssue page = new EditIssue(driver).get(); +// EditIssue page = new EditIssue(driver).get(); // Further Updates From b0f55418a6526db8bcd7f2f48c7bf8b9ba0a2b81 Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Fri, 9 May 2025 10:08:41 -0400 Subject: [PATCH 06/11] renamed files --- .../{BotPattern.java => ActionBot.java} | 0 ...{LoadableComponentBasic.java => EditIssue.java} | 0 ...ComponentExtended.java => EditIssueBetter.java} | 2 +- .../test_practices/design_strategies.en.md | 14 +++++++------- .../test_practices/design_strategies.ja.md | 14 +++++++------- .../test_practices/design_strategies.pt-br.md | 14 +++++++------- .../test_practices/design_strategies.zh-cn.md | 14 +++++++------- 7 files changed, 29 insertions(+), 29 deletions(-) rename examples/java/src/test/java/dev/selenium/design_strategies/{BotPattern.java => ActionBot.java} (100%) rename examples/java/src/test/java/dev/selenium/design_strategies/{LoadableComponentBasic.java => EditIssue.java} (100%) rename examples/java/src/test/java/dev/selenium/design_strategies/{LoadableComponentExtended.java => EditIssueBetter.java} (97%) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java b/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java similarity index 100% rename from examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java rename to examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java similarity index 100% rename from examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java rename to examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java similarity index 97% rename from examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java rename to examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java index 6e6891c4f5a..e0cc430b7be 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java @@ -7,7 +7,7 @@ import static junit.framework.Assert.assertTrue; -public class EditIssue extends LoadableComponent { +public class EditIssueBetter extends LoadableComponent { private final WebDriver driver; diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.en.md b/website_and_docs/content/documentation/test_practices/design_strategies.en.md index 8c392e827b7..266dd13d15e 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.en.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.en.md @@ -42,7 +42,7 @@ this offers the service of being able to file a new issue. A basic Page Object w {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java#L1-L69" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java#L1-L69" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -65,7 +65,7 @@ In order to turn this into a LoadableComponent, all we need to do is to set that {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L10" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L10" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -91,7 +91,7 @@ By extending this base class, we need to implement two new methods: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L30-L39" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L30-L39" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -120,7 +120,7 @@ With a little rework, our PageObject looks like: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L1-L85" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L1-L85" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -145,7 +145,7 @@ this information's not scattered through the code base. It also means that we ca {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L87" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L87" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -216,7 +216,7 @@ The "load" method in EditIssue now looks like: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L91-L96" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L91-L96" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -278,7 +278,7 @@ for your app, it's easy to change them. As an example: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java#L1-L26" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L1-L26" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.ja.md b/website_and_docs/content/documentation/test_practices/design_strategies.ja.md index 15e5bf7056f..62b59d6f53c 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.ja.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.ja.md @@ -39,7 +39,7 @@ LoadableComponentは、PageObjectsの作成の負担を軽減することを目 {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java#L1-L69" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java#L1-L69" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -62,7 +62,7 @@ LoadableComponentは、PageObjectsの作成の負担を軽減することを目 {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L10" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L10" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -87,7 +87,7 @@ LoadableComponentは、PageObjectsの作成の負担を軽減することを目 {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L30-L39" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L30-L39" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -115,7 +115,7 @@ LoadableComponentは、PageObjectsの作成の負担を軽減することを目 {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L1-L85" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L1-L85" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -141,7 +141,7 @@ LoadableComponentは、PageObjectsの作成の負担を軽減することを目 {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L87" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L87" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -212,7 +212,7 @@ EditIssueの "load" メソッドは次のようになります。 {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L91-L96" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L91-L96" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -271,7 +271,7 @@ PageObjectsは、テストでの重複を減らすための便利な方法です {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java#L1-L26" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L1-L26" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md b/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md index 603ace565bd..2248914d4b9 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md @@ -45,7 +45,7 @@ file a new issue. A basic Page Object would look like: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java#L1-L69" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java#L1-L69" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -68,7 +68,7 @@ In order to turn this into a LoadableComponent, all we need to do is to set that {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L10" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L10" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -94,7 +94,7 @@ By extending this base class, we need to implement two new methods: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L30-L39" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L30-L39" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -125,7 +125,7 @@ With a little rework, our PageObject looks like: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L1-L85" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L1-L85" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -151,7 +151,7 @@ It also means that we can do this in our tests: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L87" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L87" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -222,7 +222,7 @@ The "load" method in EditIssue now looks like: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L91-L96" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L91-L96" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -278,7 +278,7 @@ A "bot" is an action-oriented abstraction over the raw Selenium APIs. This means {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java#L1-L26" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L1-L26" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md b/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md index 19af94dde3d..a5464f7238d 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md @@ -54,7 +54,7 @@ able to file a new issue. A basic Page Object would look like: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentBasic.java#L1-L69" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java#L1-L69" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -77,7 +77,7 @@ In order to turn this into a LoadableComponent, all we need to do is to set that {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L10" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L10" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -103,7 +103,7 @@ By extending this base class, we need to implement two new methods: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L30-L39" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L30-L39" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -134,7 +134,7 @@ With a little rework, our PageObject looks like: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L1-L85" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L1-L85" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -160,7 +160,7 @@ the code base. It also means that we can do this in our tests: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L87" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L87" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -231,7 +231,7 @@ The "load" method in EditIssue now looks like: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponentExtended.java#L91-L96" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java#L91-L96" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} @@ -292,7 +292,7 @@ for your app, it's easy to change them. As an example: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/BotPattern.java#L1-L26" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L1-L26" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} From 5312e2976e13f71d8baf854595f0da75089e94f3 Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Fri, 9 May 2025 10:12:53 -0400 Subject: [PATCH 07/11] fixes --- .../java/dev/selenium/design_strategies/EditIssueBetter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java index e0cc430b7be..87223ce40b6 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java @@ -20,7 +20,7 @@ public class EditIssueBetter extends LoadableComponent { // FindBy annotation to tell the PageFactory how to locate the element. @FindBy(id = "issue_body") private WebElement body; - public EditIssue(WebDriver driver) { + public EditIssueBetter(WebDriver driver) { this.driver = driver; // This call sets the WebElement fields. @@ -84,7 +84,7 @@ private void clearAndType(WebElement field, String text) { } } -// EditIssue page = new EditIssue(driver).get(); +// EditIssueBetter page = new EditIssueBetter(driver).get(); // Further Updates From e560ac59d35b047dc6fc1a9f6aa09b97ba0fd64a Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Fri, 9 May 2025 10:28:13 -0400 Subject: [PATCH 08/11] cleaning errors --- .../dev/selenium/design_strategies/ActionBot.java | 4 ++++ .../dev/selenium/design_strategies/EditIssue.java | 2 +- .../selenium/design_strategies/EditIssueBetter.java | 12 ++++++------ .../design_strategies/LoadableComponent.java | 0 .../test_practices/design_strategies.en.md | 2 +- .../test_practices/design_strategies.ja.md | 2 +- .../test_practices/design_strategies.pt-br.md | 2 +- .../test_practices/design_strategies.zh-cn.md | 2 +- 8 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponent.java diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java b/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java index 61171e6f1a8..30b0d8d2880 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java @@ -1,3 +1,7 @@ +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + public class ActionBot { private final WebDriver driver; diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java index 08058e2bbc5..825653ec5b3 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java @@ -39,7 +39,7 @@ public void setOperatingSystem(String operatingSystem) { public void setSeleniumVersion(String seleniumVersion) { WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); + clearAndType(field, seleniumVersion); } public void setBrowserVersion(String browserVersion) { diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java index 87223ce40b6..987b4d99728 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java @@ -7,7 +7,7 @@ import static junit.framework.Assert.assertTrue; -public class EditIssueBetter extends LoadableComponent { +public class EditIssueBetter extends EditIssue { private final WebDriver driver; @@ -55,7 +55,7 @@ public void setOperatingSystem(String operatingSystem) { public void setSeleniumVersion(String seleniumVersion) { WebElement field = driver.findElement(By.id("issue_form_selenium-version")); - clearAndType(field, logOutput); + clearAndType(field, seleniumVersion); } public void setBrowserVersion(String browserVersion) { @@ -73,10 +73,10 @@ public void setUsingGrid(String usingGrid) { clearAndType(field, usingGrid); } - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } + // public IssueList submit() { + // driver.findElement(By.cssSelector("button[type='submit']")).click(); + // return new IssueList(driver); + // } private void clearAndType(WebElement field, String text) { field.clear(); diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponent.java b/examples/java/src/test/java/dev/selenium/design_strategies/LoadableComponent.java new file mode 100644 index 00000000000..e69de29bb2d diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.en.md b/website_and_docs/content/documentation/test_practices/design_strategies.en.md index 54add2aa9ab..4a0947c85c6 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.en.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.en.md @@ -278,7 +278,7 @@ for your app, it's easy to change them. As an example: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L1-L26" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L5-L30" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.ja.md b/website_and_docs/content/documentation/test_practices/design_strategies.ja.md index 6de5ca25176..832bf6065df 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.ja.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.ja.md @@ -271,7 +271,7 @@ PageObjectsは、テストでの重複を減らすための便利な方法です {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L1-L26" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L5-L30" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md b/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md index f307d3658d9..12bb49e9c54 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.pt-br.md @@ -278,7 +278,7 @@ A "bot" is an action-oriented abstraction over the raw Selenium APIs. This means {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L1-L26" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L5-L30" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} diff --git a/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md b/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md index f886c5b551b..9399d0e9926 100644 --- a/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md +++ b/website_and_docs/content/documentation/test_practices/design_strategies.zh-cn.md @@ -292,7 +292,7 @@ for your app, it's easy to change them. As an example: {{< badge-code >}} {{< /tab >}} {{< tab header="Java" >}} -{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L1-L26" >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/design_strategies/ActionBot.java#L5-L30" >}} {{< /tab >}} {{< tab header="CSharp" >}} {{< badge-code >}} From 0472965ef30f3675933a19ba01e68a6308616fc7 Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Fri, 9 May 2025 10:40:57 -0400 Subject: [PATCH 09/11] whack-a-mole --- .../java/dev/selenium/design_strategies/EditIssue.java | 8 ++++---- .../dev/selenium/design_strategies/EditIssueBetter.java | 4 ++-- .../java/dev/selenium/design_strategies/ProjectPage.java | 8 ++++---- .../java/dev/selenium/design_strategies/SecuredPage.java | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java index 825653ec5b3..0e601a01756 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssue.java @@ -57,10 +57,10 @@ public void setUsingGrid(String usingGrid) { clearAndType(field, usingGrid); } - public IssueList submit() { - driver.findElement(By.cssSelector("button[type='submit']")).click(); - return new IssueList(driver); - } + // public IssueList submit() { + // driver.findElement(By.cssSelector("button[type='submit']")).click(); + // return new IssueList(driver); + // } private void clearAndType(WebElement field, String text) { field.clear(); diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java index 987b4d99728..f4749ec05bb 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java @@ -5,7 +5,7 @@ import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; -import static junit.framework.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; public class EditIssueBetter extends EditIssue { @@ -21,7 +21,7 @@ public class EditIssueBetter extends EditIssue { @FindBy(id = "issue_body") private WebElement body; public EditIssueBetter(WebDriver driver) { - this.driver = driver; + super(driver); // This call sets the WebElement fields. PageFactory.initElements(driver, this); diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java b/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java index 0f7b5b253f2..c83c136fb72 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java @@ -2,9 +2,9 @@ import org.openqa.selenium.WebDriver; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; -public class ProjectPage extends LoadableComponent { +public class ProjectPage { private final WebDriver driver; private final String projectName; @@ -14,12 +14,12 @@ public ProjectPage(WebDriver driver, String projectName) { this.projectName = projectName; } - @Override + protected void load() { driver.get("http://" + projectName + ".googlecode.com/"); } - @Override + protected void isLoaded() throws Error { String url = driver.getCurrentUrl(); diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java b/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java index e5040c1c462..d572d6ba5aa 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java @@ -7,21 +7,21 @@ import static org.junit.Assert.fail; -public class SecuredPage extends LoadableComponent { +public class SecuredPage{ private final WebDriver driver; private final LoadableComponent parent; private final String username; private final String password; - public SecuredPage(WebDriver driver, LoadableComponent parent, String username, String password) { + public SecuredPage(WebDriver driver, String username, String password) { this.driver = driver; this.parent = parent; this.username = username; this.password = password; } - @Override + protected void load() { parent.get(); @@ -38,7 +38,7 @@ protected void load() { driver.get(originalUrl); } - @Override + protected void isLoaded() throws Error { // If you're signed in, you have the option of picking a different login. // Let's check for the presence of that. From bb3631a1fe11a11f0355b9127b1a3e95087a330e Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Fri, 9 May 2025 10:45:53 -0400 Subject: [PATCH 10/11] continued moles --- .../test/java/dev/selenium/design_strategies/SecuredPage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java b/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java index d572d6ba5aa..c3f38d68762 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java @@ -5,12 +5,12 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SecuredPage{ private final WebDriver driver; - private final LoadableComponent parent; + // private final LoadableComponent parent; private final String username; private final String password; From a1bcaf8fab889582fdd6d9f0d8988b8a0053dcab Mon Sep 17 00:00:00 2001 From: Simon Benzer Date: Fri, 9 May 2025 10:52:20 -0400 Subject: [PATCH 11/11] ugh --- .../java/dev/selenium/design_strategies/EditIssueBetter.java | 4 ++-- .../test/java/dev/selenium/design_strategies/ProjectPage.java | 4 ++-- .../test/java/dev/selenium/design_strategies/SecuredPage.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java index f4749ec05bb..2b0600baeed 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/EditIssueBetter.java @@ -5,7 +5,7 @@ import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Assertions; public class EditIssueBetter extends EditIssue { @@ -35,7 +35,7 @@ protected void load() { @Override protected void isLoaded() throws Error { String url = driver.getCurrentUrl(); - assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); + Assertions.assertTrue("Not on the issue entry page: " + url, url.endsWith("/new")); } public void setHowToReproduce(String howToReproduce) { diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java b/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java index c83c136fb72..c56f9c68478 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/ProjectPage.java @@ -2,7 +2,7 @@ import org.openqa.selenium.WebDriver; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Assertions; public class ProjectPage { @@ -23,6 +23,6 @@ protected void load() { protected void isLoaded() throws Error { String url = driver.getCurrentUrl(); - assertTrue(url.contains(projectName)); + Assertions.assertTrue(url.contains(projectName)); } } \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java b/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java index c3f38d68762..f79386cb289 100644 --- a/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java +++ b/examples/java/src/test/java/dev/selenium/design_strategies/SecuredPage.java @@ -5,7 +5,7 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -import static org.junit.jupiter.api.Assertions.assertTrue; + public class SecuredPage{