Skip to content

Commit

Permalink
feat: roll driver, implement hasText locator option (microsoft#747)
Browse files Browse the repository at this point in the history
  • Loading branch information
yury-s authored Dec 16, 2021
1 parent a4348f2 commit c230bed
Show file tree
Hide file tree
Showing 22 changed files with 399 additions and 105 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom

| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->98.0.4730.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->99.0.4763.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->15.4<!-- GEN:stop --> ||||
| Firefox <!-- GEN:firefox-version -->94.0.1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@
*/
public interface LocatorAssertions {
class IsCheckedOptions {
public Boolean checked;
/**
* Time to retry the assertion for.
*/
public Double timeout;

public IsCheckedOptions setChecked(boolean checked) {
this.checked = checked;
return this;
}
/**
* Time to retry the assertion for.
*/
Expand Down Expand Up @@ -840,7 +845,7 @@ default void hasId(Pattern id) {
* Ensures the {@code Locator} points to an element with given JavaScript property. Note that this property can be of a primitive
* type as well as a plain serializable JavaScript object.
* <pre>{@code
* assertThat(page.locator("input")).hasJSProperty("type", "text");
* assertThat(page.locator("input")).hasJSProperty("loaded", true);
* }</pre>
*
* @param name Property name.
Expand All @@ -853,7 +858,7 @@ default void hasJSProperty(String name, Object value) {
* Ensures the {@code Locator} points to an element with given JavaScript property. Note that this property can be of a primitive
* type as well as a plain serializable JavaScript object.
* <pre>{@code
* assertThat(page.locator("input")).hasJSProperty("type", "text");
* assertThat(page.locator("input")).hasJSProperty("loaded", true);
* }</pre>
*
* @param name Property name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
import static java.util.Arrays.asList;

class AssertionsBase {
Expand Down Expand Up @@ -83,22 +84,7 @@ static ExpectedTextValue expectedRegex(Pattern pattern) {
ExpectedTextValue expected = new ExpectedTextValue();
expected.regexSource = pattern.pattern();
if (pattern.flags() != 0) {
expected.regexFlags = "";
if ((pattern.flags() & Pattern.CASE_INSENSITIVE) != 0) {
// Case-insensitive search.
expected.regexFlags += "i";
}
if ((pattern.flags() & Pattern.DOTALL) != 0) {
// Allows . to match newline characters.
expected.regexFlags += "s";
}
if ((pattern.flags() & Pattern.MULTILINE) != 0) {
// Multi-line search.
expected.regexFlags += "m";
}
if ((pattern.flags() & ~(Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.DOTALL)) != 0) {
throw new PlaywrightException("Unexpected RegEx flag, only CASE_INSENSITIVE, DOTALL and MULTILINE are supported.");
}
expected.regexFlags = toJsRegexFlags(pattern);
}
return expected;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public interface Browser extends AutoCloseable {

class NewContextOptions {
/**
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
* Whether to automatically download all the attachments. Defaults to {@code true} where all the downloads are accepted.
*/
public Boolean acceptDownloads;
/**
Expand Down Expand Up @@ -205,7 +205,7 @@ class NewContextOptions {
public Optional<ViewportSize> viewportSize;

/**
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
* Whether to automatically download all the attachments. Defaults to {@code true} where all the downloads are accepted.
*/
public NewContextOptions setAcceptDownloads(boolean acceptDownloads) {
this.acceptDownloads = acceptDownloads;
Expand Down Expand Up @@ -481,7 +481,7 @@ public NewContextOptions setViewportSize(ViewportSize viewportSize) {
}
class NewPageOptions {
/**
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
* Whether to automatically download all the attachments. Defaults to {@code true} where all the downloads are accepted.
*/
public Boolean acceptDownloads;
/**
Expand Down Expand Up @@ -629,7 +629,7 @@ class NewPageOptions {
public Optional<ViewportSize> viewportSize;

/**
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
* Whether to automatically download all the attachments. Defaults to {@code true} where all the downloads are accepted.
*/
public NewPageOptions setAcceptDownloads(boolean acceptDownloads) {
this.acceptDownloads = acceptDownloads;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ public LaunchOptions setTracesDir(Path tracesDir) {
}
class LaunchPersistentContextOptions {
/**
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
* Whether to automatically download all the attachments. Defaults to {@code true} where all the downloads are accepted.
*/
public Boolean acceptDownloads;
/**
Expand Down Expand Up @@ -581,7 +581,7 @@ class LaunchPersistentContextOptions {
public Optional<ViewportSize> viewportSize;

/**
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
* Whether to automatically download all the attachments. Defaults to {@code true} where all the downloads are accepted.
*/
public LaunchPersistentContextOptions setAcceptDownloads(boolean acceptDownloads) {
this.acceptDownloads = acceptDownloads;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@
* // wait for download to complete
* Path path = download.path();
* }</pre>
*
* <p> <strong>NOTE:</strong> Browser context **must** be created with the {@code acceptDownloads} set to {@code true} when user needs access to the downloaded
* content. If {@code acceptDownloads} is not set, download events are emitted, but the actual download is not performed and user
* has no access to the downloaded files.
*/
public interface Download {
/**
Expand Down
37 changes: 36 additions & 1 deletion playwright/src/main/java/com/microsoft/playwright/Frame.java
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,30 @@ public IsVisibleOptions setTimeout(double timeout) {
return this;
}
}
class LocatorOptions {
/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public Object hasText;

/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public LocatorOptions setHasText(String hasText) {
this.hasText = hasText;
return this;
}
/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public LocatorOptions setHasText(Pattern hasText) {
this.hasText = hasText;
return this;
}
}
class PressOptions {
/**
* Time to wait between {@code keydown} and {@code keyup} in milliseconds. Defaults to 0.
Expand Down Expand Up @@ -2955,7 +2979,18 @@ default boolean isVisible(String selector) {
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
* selectors</a> for more details.
*/
Locator locator(String selector);
default Locator locator(String selector) {
return locator(selector, null);
}
/**
* The method returns an element locator that can be used to perform actions in the frame. Locator is resolved to the
* element immediately before performing an action, so a series of actions on the same locator can in fact be performed on
* different DOM elements. That would happen if the DOM structure between those actions has changed.
*
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
* selectors</a> for more details.
*/
Locator locator(String selector, LocatorOptions options);
/**
* Returns frame's name attribute as specified in the tag.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.microsoft.playwright;

import java.util.*;
import java.util.regex.Pattern;

/**
* FrameLocator represents a view to the {@code iframe} on the page. It captures the logic sufficient to retrieve the {@code iframe}
Expand All @@ -38,8 +39,40 @@
* // Works because we explicitly tell locator to pick the first frame:
* page.frame_locator(".result-frame").first().locator("button").click();
* }</pre>
*
* <p> **Converting Locator to FrameLocator**
*
* <p> If you have a {@code Locator} object pointing to an {@code iframe} it can be converted to {@code FrameLocator} using <a
* href="https://developer.mozilla.org/en-US/docs/Web/CSS/:scope">{@code :scope}</a> CSS selector:
* <pre>{@code
* Locator frameLocator = locator.frameLocator(':scope');
* }</pre>
*/
public interface FrameLocator {
class LocatorOptions {
/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public Object hasText;

/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public LocatorOptions setHasText(String hasText) {
this.hasText = hasText;
return this;
}
/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public LocatorOptions setHasText(Pattern hasText) {
this.hasText = hasText;
return this;
}
}
/**
* Returns locator to the first matching frame.
*/
Expand All @@ -62,7 +95,16 @@ public interface FrameLocator {
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
* selectors</a> for more details.
*/
Locator locator(String selector);
default Locator locator(String selector) {
return locator(selector, null);
}
/**
* The method finds an element matching the specified selector in the FrameLocator's subtree.
*
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
* selectors</a> for more details.
*/
Locator locator(String selector, LocatorOptions options);
/**
* Returns locator to the n-th matching frame.
*/
Expand Down
80 changes: 39 additions & 41 deletions playwright/src/main/java/com/microsoft/playwright/Locator.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,49 +19,14 @@
import com.microsoft.playwright.options.*;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Pattern;

/**
* Locator represents a view to the element(s) on the page. It captures the logic sufficient to retrieve the element at any
* given moment. Locator can be created with the {@link Page#locator Page.locator()} method.
* <pre>{@code
* Locator locator = page.locator("text=Submit");
* locator.click();
* }</pre>
* Locators are the central piece of Playwright's auto-waiting and retry-ability. In a nutshell, locators represent a way
* to find element(s) on the page at any moment. Locator can be created with the {@link Page#locator Page.locator()}
* method.
*
* <p> The difference between the Locator and {@code ElementHandle} is that the latter points to a particular element, while Locator
* captures the logic of how to retrieve that element.
*
* <p> In the example below, handle points to a particular DOM element on page. If that element changes text or is used by
* React to render an entirely different component, handle is still pointing to that very DOM element. This can lead to
* unexpected behaviors.
* <pre>{@code
* ElementHandle handle = page.querySelector("text=Submit");
* handle.hover();
* handle.click();
* }</pre>
*
* <p> With the locator, every time the {@code element} is used, up-to-date DOM element is located in the page using the selector. So
* in the snippet below, underlying DOM element is going to be located twice.
* <pre>{@code
* Locator locator = page.locator("text=Submit");
* locator.hover();
* locator.click();
* }</pre>
*
* <p> **Strictness**
*
* <p> Locators are strict. This means that all operations on locators that imply some target DOM element will throw if more
* than one element matches given selector.
* <pre>{@code
* // Throws if there are several buttons in DOM:
* page.locator("button").click();
*
* // Works because we explicitly tell locator to pick the first element:
* page.locator("button").first().click();
*
* // Works because count knows what to do with multiple matches:
* page.locator("button").count();
* }</pre>
* <p> <a href="https://playwright.dev/java/docs/locators/">Learn more about locators</a>.
*/
public interface Locator {
class BoundingBoxOptions {
Expand Down Expand Up @@ -898,6 +863,30 @@ public IsVisibleOptions setTimeout(double timeout) {
return this;
}
}
class LocatorOptions {
/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public Object hasText;

/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public LocatorOptions setHasText(String hasText) {
this.hasText = hasText;
return this;
}
/**
* Matches elements containing specified text somewhere inside, possibly in a child or a descendant element. For example,
* {@code "Playwright"} matches {@code <article><div>Playwright</div></article>}.
*/
public LocatorOptions setHasText(Pattern hasText) {
this.hasText = hasText;
return this;
}
}
class PressOptions {
/**
* Time to wait between {@code keydown} and {@code keyup} in milliseconds. Defaults to 0.
Expand Down Expand Up @@ -2177,7 +2166,16 @@ default boolean isVisible() {
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
* selectors</a> for more details.
*/
Locator locator(String selector);
default Locator locator(String selector) {
return locator(selector, null);
}
/**
* The method finds an element matching the specified selector in the {@code Locator}'s subtree.
*
* @param selector A selector to use when resolving DOM element. See <a href="https://playwright.dev/java/docs/selectors/">working with
* selectors</a> for more details.
*/
Locator locator(String selector, LocatorOptions options);
/**
* Returns locator to the n-th matching element.
*/
Expand Down
Loading

0 comments on commit c230bed

Please sign in to comment.