feat: frame locators, roll driver (#695)
This commit is contained in:
@@ -11,7 +11,7 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->97.0.4688.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->98.0.4695.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: |
|
||||
|
||||
|
||||
+47
-33
@@ -23,7 +23,21 @@ import com.microsoft.playwright.Locator;
|
||||
/**
|
||||
* The {@code LocatorAssertions} class provides assertion methods that can be used to make assertions about the {@code Locator} state
|
||||
* in the tests. A new instance of {@code LocatorAssertions} is created by calling {@link PlaywrightAssertions#assertThat
|
||||
* PlaywrightAssertions.assertThat()}.
|
||||
* PlaywrightAssertions.assertThat()}:
|
||||
* <pre>{@code
|
||||
* ...
|
||||
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
*
|
||||
* public class TestLocator {
|
||||
* ...
|
||||
* @Test
|
||||
* void statusBecomesSubmitted() {
|
||||
* ...
|
||||
* page.click("#submit-button");
|
||||
* assertThat(page.locator(".status")).hasText("Submitted");
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public interface LocatorAssertions {
|
||||
class ContainsTextOptions {
|
||||
@@ -295,7 +309,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .list-item")).containsText(Arrays.asList("Text 1", "Text 4", "Text 5"));
|
||||
* assertThat(page.locator("list > .list-item")).containsText(new String[] {"Text 1", "Text 4", "Text 5"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -312,7 +326,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .list-item")).containsText(Arrays.asList("Text 1", "Text 4", "Text 5"));
|
||||
* assertThat(page.locator("list > .list-item")).containsText(new String[] {"Text 1", "Text 4", "Text 5"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -327,7 +341,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .list-item")).containsText(Arrays.asList("Text 1", "Text 4", "Text 5"));
|
||||
* assertThat(page.locator("list > .list-item")).containsText(new String[] {"Text 1", "Text 4", "Text 5"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -344,7 +358,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .list-item")).containsText(Arrays.asList("Text 1", "Text 4", "Text 5"));
|
||||
* assertThat(page.locator("list > .list-item")).containsText(new String[] {"Text 1", "Text 4", "Text 5"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -359,7 +373,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .list-item")).containsText(Arrays.asList("Text 1", "Text 4", "Text 5"));
|
||||
* assertThat(page.locator("list > .list-item")).containsText(new String[] {"Text 1", "Text 4", "Text 5"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -376,7 +390,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .list-item")).containsText(Arrays.asList("Text 1", "Text 4", "Text 5"));
|
||||
* assertThat(page.locator("list > .list-item")).containsText(new String[] {"Text 1", "Text 4", "Text 5"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -391,7 +405,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .list-item")).containsText(Arrays.asList("Text 1", "Text 4", "Text 5"));
|
||||
* assertThat(page.locator("list > .list-item")).containsText(new String[] {"Text 1", "Text 4", "Text 5"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -408,7 +422,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .list-item")).containsText(Arrays.asList("Text 1", "Text 4", "Text 5"));
|
||||
* assertThat(page.locator("list > .list-item")).containsText(new String[] {"Text 1", "Text 4", "Text 5"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -466,7 +480,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(Arrays.asList("component", "component selected", "component"));
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -482,7 +496,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(Arrays.asList("component", "component selected", "component"));
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -496,7 +510,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(Arrays.asList("component", "component selected", "component"));
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -512,7 +526,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(Arrays.asList("component", "component selected", "component"));
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -526,7 +540,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(Arrays.asList("component", "component selected", "component"));
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -542,7 +556,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(Arrays.asList("component", "component selected", "component"));
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -556,7 +570,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(Arrays.asList("component", "component selected", "component"));
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -572,7 +586,7 @@ public interface LocatorAssertions {
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasClass(Arrays.asList("component", "component selected", "component"));
|
||||
* assertThat(page.locator("list > .component")).hasClass(new String[] {"component", "component selected", "component"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected class or RegExp or a list of those.
|
||||
@@ -689,13 +703,13 @@ public interface LocatorAssertions {
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with the given text. You can use regular expressions for the value as well.
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, Test User"));
|
||||
* assertThat(page.locator(".title")).hasText("Welcome, Test User");
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, .*"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasText(Arrays.asList("Text 1", "Text 2", "Text 3"));
|
||||
* assertThat(page.locator("list > .component")).hasText(new String[] {"Text 1", "Text 2", "Text 3"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -706,13 +720,13 @@ public interface LocatorAssertions {
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with the given text. You can use regular expressions for the value as well.
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, Test User"));
|
||||
* assertThat(page.locator(".title")).hasText("Welcome, Test User");
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, .*"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasText(Arrays.asList("Text 1", "Text 2", "Text 3"));
|
||||
* assertThat(page.locator("list > .component")).hasText(new String[] {"Text 1", "Text 2", "Text 3"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -721,13 +735,13 @@ public interface LocatorAssertions {
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with the given text. You can use regular expressions for the value as well.
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, Test User"));
|
||||
* assertThat(page.locator(".title")).hasText("Welcome, Test User");
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, .*"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasText(Arrays.asList("Text 1", "Text 2", "Text 3"));
|
||||
* assertThat(page.locator("list > .component")).hasText(new String[] {"Text 1", "Text 2", "Text 3"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -738,13 +752,13 @@ public interface LocatorAssertions {
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with the given text. You can use regular expressions for the value as well.
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, Test User"));
|
||||
* assertThat(page.locator(".title")).hasText("Welcome, Test User");
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, .*"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasText(Arrays.asList("Text 1", "Text 2", "Text 3"));
|
||||
* assertThat(page.locator("list > .component")).hasText(new String[] {"Text 1", "Text 2", "Text 3"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -753,13 +767,13 @@ public interface LocatorAssertions {
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with the given text. You can use regular expressions for the value as well.
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, Test User"));
|
||||
* assertThat(page.locator(".title")).hasText("Welcome, Test User");
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, .*"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasText(Arrays.asList("Text 1", "Text 2", "Text 3"));
|
||||
* assertThat(page.locator("list > .component")).hasText(new String[] {"Text 1", "Text 2", "Text 3"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -770,13 +784,13 @@ public interface LocatorAssertions {
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with the given text. You can use regular expressions for the value as well.
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, Test User"));
|
||||
* assertThat(page.locator(".title")).hasText("Welcome, Test User");
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, .*"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasText(Arrays.asList("Text 1", "Text 2", "Text 3"));
|
||||
* assertThat(page.locator("list > .component")).hasText(new String[] {"Text 1", "Text 2", "Text 3"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -785,13 +799,13 @@ public interface LocatorAssertions {
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with the given text. You can use regular expressions for the value as well.
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, Test User"));
|
||||
* assertThat(page.locator(".title")).hasText("Welcome, Test User");
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, .*"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasText(Arrays.asList("Text 1", "Text 2", "Text 3"));
|
||||
* assertThat(page.locator("list > .component")).hasText(new String[] {"Text 1", "Text 2", "Text 3"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
@@ -802,13 +816,13 @@ public interface LocatorAssertions {
|
||||
/**
|
||||
* Ensures the {@code Locator} points to an element with the given text. You can use regular expressions for the value as well.
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, Test User"));
|
||||
* assertThat(page.locator(".title")).hasText("Welcome, Test User");
|
||||
* assertThat(page.locator(".title")).hasText(Pattern.compile("Welcome, .*"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Note that if array is passed as an expected value, entire lists can be asserted:
|
||||
* <pre>{@code
|
||||
* assertThat(page.locator("list > .component")).hasText(Arrays.asList("Text 1", "Text 2", "Text 3"));
|
||||
* assertThat(page.locator("list > .component")).hasText(new String[] {"Text 1", "Text 2", "Text 3"});
|
||||
* }</pre>
|
||||
*
|
||||
* @param expected Expected substring or RegExp or a list of those.
|
||||
|
||||
@@ -23,7 +23,21 @@ import com.microsoft.playwright.Page;
|
||||
/**
|
||||
* The {@code PageAssertions} class provides assertion methods that can be used to make assertions about the {@code Page} state in the
|
||||
* tests. A new instance of {@code LocatorAssertions} is created by calling {@link PlaywrightAssertions#assertThat
|
||||
* PlaywrightAssertions.assertThat()}.
|
||||
* PlaywrightAssertions.assertThat()}:
|
||||
* <pre>{@code
|
||||
* ...
|
||||
* import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
*
|
||||
* public class TestPage {
|
||||
* ...
|
||||
* @Test
|
||||
* void navigatesToLoginPage() {
|
||||
* ...
|
||||
* page.click("#login");
|
||||
* assertThat(page).hasURL(Pattern.compile(".*\/login"));
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public interface PageAssertions {
|
||||
class HasTitleOptions {
|
||||
@@ -97,7 +111,7 @@ public interface PageAssertions {
|
||||
/**
|
||||
* Ensures the page is navigated to the given URL.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasURL('.com');
|
||||
* assertThat(page).hasURL(".com");
|
||||
* }</pre>
|
||||
*
|
||||
* @param urlOrRegExp Expected substring or RegExp.
|
||||
@@ -108,7 +122,7 @@ public interface PageAssertions {
|
||||
/**
|
||||
* Ensures the page is navigated to the given URL.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasURL('.com');
|
||||
* assertThat(page).hasURL(".com");
|
||||
* }</pre>
|
||||
*
|
||||
* @param urlOrRegExp Expected substring or RegExp.
|
||||
@@ -117,7 +131,7 @@ public interface PageAssertions {
|
||||
/**
|
||||
* Ensures the page is navigated to the given URL.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasURL('.com');
|
||||
* assertThat(page).hasURL(".com");
|
||||
* }</pre>
|
||||
*
|
||||
* @param urlOrRegExp Expected substring or RegExp.
|
||||
@@ -128,7 +142,7 @@ public interface PageAssertions {
|
||||
/**
|
||||
* Ensures the page is navigated to the given URL.
|
||||
* <pre>{@code
|
||||
* assertThat(page).hasURL('.com');
|
||||
* assertThat(page).hasURL(".com");
|
||||
* }</pre>
|
||||
*
|
||||
* @param urlOrRegExp Expected substring or RegExp.
|
||||
@@ -138,7 +152,7 @@ public interface PageAssertions {
|
||||
* Makes the assertion check for the opposite condition. For example, this code tests that the page URL doesn't contain
|
||||
* {@code "error"}:
|
||||
* <pre>{@code
|
||||
* assertThat(page).not().hasURL('error');
|
||||
* assertThat(page).not().hasURL("error");
|
||||
* }</pre>
|
||||
*/
|
||||
PageAssertions not();
|
||||
|
||||
@@ -160,7 +160,7 @@ public class TestLocatorAssertions extends TestBase {
|
||||
fail("did not throw");
|
||||
} catch (AssertionFailedError e) {
|
||||
assertEquals("[]", e.getExpected().getStringRepresentation());
|
||||
assertEquals("[]", e.getActual().getStringRepresentation());
|
||||
assertEquals("null", e.getActual().getStringRepresentation());
|
||||
assertTrue(e.getMessage().contains("Locator expected not to have text"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestLocatorFrame extends TestBase {
|
||||
private static void routeIframe(Page page) {
|
||||
page.route("**/empty.html", route -> route.fulfill(new Route.FulfillOptions()
|
||||
.setBody("<iframe src='iframe.html'></iframe>").setContentType("text/html")));
|
||||
page.route("**/iframe.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setBody("<html>\n" +
|
||||
" <div>\n" +
|
||||
" <button>Hello iframe</button>\n" +
|
||||
" <iframe src='iframe-2.html'></iframe>\n" +
|
||||
" </div>\n" +
|
||||
" <span>1</span>\n" +
|
||||
" <span>2</span>\n" +
|
||||
" </html>").setContentType("text/html"));
|
||||
});
|
||||
page.route("**/iframe-2.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setBody("<html><button>Hello nested iframe</button></html>").setContentType("text/html"));
|
||||
});
|
||||
}
|
||||
|
||||
private static void routeAmbiguous(Page page) {
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setBody("<iframe src='iframe-1.html'></iframe>\n" +
|
||||
"<iframe src='iframe-2.html'></iframe>\n" +
|
||||
"<iframe src='iframe-3.html'></iframe>")
|
||||
.setContentType("text/html"));
|
||||
});
|
||||
page.route("**/iframe-*", route -> {
|
||||
try {
|
||||
String path = new URL(route.request().url()).getPath().substring(1);
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setBody("<html><button>Hello from " + path + "</button></html>")
|
||||
.setContentType("text/html"));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForIframe() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").locator("button");
|
||||
button.waitFor();
|
||||
assertEquals("Hello iframe", button.innerText());
|
||||
assertThat(button).hasText("Hello iframe");
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForNestedIframe() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").frameLocator("iframe").locator("button");
|
||||
button.waitFor();
|
||||
assertEquals("Hello nested iframe", button.innerText());
|
||||
assertThat(button).hasText("Hello nested iframe");
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForAnd() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator locator = page.frameLocator("iframe").locator("button");
|
||||
assertThat(locator).hasText("Hello iframe");
|
||||
assertEquals("Hello iframe", locator.innerText());
|
||||
Locator spans = page.frameLocator("iframe").locator("span");
|
||||
assertThat(spans).hasCount(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForFrame() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
try {
|
||||
page.frameLocator("iframe").locator("span").click(new Locator.ClickOptions().setTimeout(300));
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("waiting for frame \"iframe\""), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitForFrame2() {
|
||||
routeIframe(page);
|
||||
page.evaluate("url => setTimeout(() => location.href = url, 300)", server.EMPTY_PAGE);
|
||||
page.frameLocator("iframe").locator("button").click();
|
||||
}
|
||||
|
||||
void shouldWaitForFrameToGo() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotWaitForFrame() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page.frameLocator("iframe").locator("span")).isHidden();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotWaitForFrame2() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page.frameLocator("iframe").locator("span")).not().isVisible();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotWaitForFrame3() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page.frameLocator("iframe").locator("span")).hasCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldClickInLazyIframe() {
|
||||
page.route("**/iframe.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setBody("<html><button>Hello iframe</button></html>").setContentType("text/html"));
|
||||
});
|
||||
// empty pge
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
|
||||
// add blank iframe
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" const iframe = document.createElement('iframe');\n" +
|
||||
" document.body.appendChild(iframe);\n" +
|
||||
" // navigate iframe\n" +
|
||||
" setTimeout(() => iframe.src = 'iframe.html', 500);\n" +
|
||||
" }, 500);");
|
||||
// Click in iframe
|
||||
Locator button = page.frameLocator("iframe").locator("button");
|
||||
button.click();
|
||||
assertThat(button).hasText("Hello iframe");
|
||||
assertEquals("Hello iframe", button.innerText());
|
||||
}
|
||||
|
||||
@Test
|
||||
void waitForShouldSurviveFrameReattach() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").locator("button:has-text('Hello nested iframe')");
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" document.querySelector('iframe').remove();\n" +
|
||||
" setTimeout(() => {\n" +
|
||||
" const iframe = document.createElement('iframe');\n" +
|
||||
" iframe.src = 'iframe-2.html';\n" +
|
||||
" document.body.appendChild(iframe);\n" +
|
||||
" }, 500);\n" +
|
||||
" }, 500);");
|
||||
button.waitFor();
|
||||
}
|
||||
|
||||
@Test
|
||||
void clickShouldSurviveFrameReattach() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").locator("button:has-text('Hello nested iframe')");
|
||||
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" document.querySelector('iframe').remove();\n" +
|
||||
" setTimeout(() => {\n" +
|
||||
" const iframe = document.createElement('iframe');\n" +
|
||||
" iframe.src = 'iframe-2.html';\n" +
|
||||
" document.body.appendChild(iframe);\n" +
|
||||
" }, 500);\n" +
|
||||
" }, 500);");
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void clickShouldSurviveIframeNavigation() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.frameLocator("iframe").locator("button:has-text('Hello nested iframe')");
|
||||
page.evaluate("setTimeout(() => {\n" +
|
||||
" document.querySelector('iframe').src = 'iframe-2.html';\n" +
|
||||
" }, 500);");
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNonWorkForNonFrame() {
|
||||
routeIframe(page);
|
||||
page.setContent("<div></div>");
|
||||
Locator button = page.frameLocator("div").locator("button");
|
||||
try {
|
||||
button.waitFor();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("<div></div>"), e.getMessage());
|
||||
assertTrue(e.getMessage().contains("<iframe> was expected"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void locatorFrameLocatorShouldWorkForIframe() {
|
||||
routeIframe(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.locator("body").frameLocator("iframe").locator("button");
|
||||
button.waitFor();
|
||||
assertThat(button).hasText("Hello iframe");
|
||||
assertEquals("Hello iframe", button.innerText());
|
||||
button.click();
|
||||
}
|
||||
|
||||
@Test
|
||||
void locatorFrameLocatorShouldThrowOnAmbiguity() {
|
||||
routeAmbiguous(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button = page.locator("body").frameLocator("iframe").locator("button");
|
||||
try {
|
||||
button.waitFor();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Error: strict mode violation: \"body >> iframe\" resolved to 3 elements"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void locatorFrameLocatorShouldNotThrowOnFirstLastNth() {
|
||||
routeAmbiguous(page);
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Locator button1 = page.locator("body").frameLocator("iframe").first().locator("button");
|
||||
assertThat(button1).hasText("Hello from iframe-1.html");
|
||||
Locator button2 = page.locator("body").frameLocator("iframe").nth(1).locator("button");
|
||||
assertThat(button2).hasText("Hello from iframe-2.html");
|
||||
Locator button3 = page.locator("body").frameLocator("iframe").last().locator("button");
|
||||
assertThat(button3).hasText("Hello from iframe-3.html");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2653,6 +2653,19 @@ public interface Frame {
|
||||
* }</pre>
|
||||
*/
|
||||
ElementHandle frameElement();
|
||||
/**
|
||||
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
||||
* that iframe. Following snippet locates element with text "Submit" in the iframe with id {@code my-frame}, like {@code <iframe
|
||||
* id="my-frame">}:
|
||||
* <pre>{@code
|
||||
* Locator locator = frame.frameLocator("#my-iframe").locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
FrameLocator frameLocator(String selector);
|
||||
/**
|
||||
* Returns element attribute value.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* FrameLocator represents a view to the {@code iframe} on the page. It captures the logic sufficient to retrieve the {@code iframe}
|
||||
* and locate elements in that iframe. FrameLocator can be created with either {@link Page#frameLocator
|
||||
* Page.frameLocator()} or {@link Locator#frameLocator Locator.frameLocator()} method.
|
||||
* <pre>{@code
|
||||
* Locator locator = page.frameLocator("#my-frame").locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> **Strictness**
|
||||
*
|
||||
* <p> Frame locators are strict. This means that all operations on frame locators will throw if more than one element matches
|
||||
* given selector.
|
||||
* <pre>{@code
|
||||
* // Throws if there are several frames in DOM:
|
||||
* page.frame_locator(".result-frame").locator("button").click();
|
||||
*
|
||||
* // Works because we explicitly tell locator to pick the first frame:
|
||||
* page.frame_locator(".result-frame").first().locator("button").click();
|
||||
* }</pre>
|
||||
*/
|
||||
public interface FrameLocator {
|
||||
/**
|
||||
* Returns locator to the first matching frame.
|
||||
*/
|
||||
FrameLocator first();
|
||||
/**
|
||||
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
||||
* that iframe.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
FrameLocator frameLocator(String selector);
|
||||
/**
|
||||
* Returns locator to the last matching frame.
|
||||
*/
|
||||
FrameLocator last();
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* Returns locator to the n-th matching frame.
|
||||
*/
|
||||
FrameLocator nth(int index);
|
||||
}
|
||||
|
||||
@@ -1898,6 +1898,18 @@ public interface Locator {
|
||||
* Calls <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus">focus</a> on the element.
|
||||
*/
|
||||
void focus(FocusOptions options);
|
||||
/**
|
||||
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
||||
* that iframe:
|
||||
* <pre>{@code
|
||||
* Locator locator = page.frameLocator("iframe").locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
FrameLocator frameLocator(String selector);
|
||||
/**
|
||||
* Returns element attribute value.
|
||||
*
|
||||
@@ -2045,8 +2057,7 @@ public interface Locator {
|
||||
*/
|
||||
Locator last();
|
||||
/**
|
||||
* The method finds an element matching the specified selector in the {@code Locator}'s subtree. See <a
|
||||
* href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more details.
|
||||
* 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.
|
||||
|
||||
@@ -3993,6 +3993,19 @@ public interface Page extends AutoCloseable {
|
||||
* @param url A glob pattern, regex pattern or predicate receiving frame's {@code url} as a [URL] object.
|
||||
*/
|
||||
Frame frameByUrl(Predicate<String> url);
|
||||
/**
|
||||
* When working with iframes, you can create a frame locator that will enter the iframe and allow selecting elements in
|
||||
* that iframe. Following snippet locates element with text "Submit" in the iframe with id {@code my-frame}, like {@code <iframe
|
||||
* id="my-frame">}:
|
||||
* <pre>{@code
|
||||
* Locator locator = page.frameLocator("#my-iframe").locator("text=Submit");
|
||||
* locator.click();
|
||||
* }</pre>
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
FrameLocator frameLocator(String selector);
|
||||
/**
|
||||
* An array of all frames attached to the page.
|
||||
*/
|
||||
|
||||
@@ -358,6 +358,11 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator frameLocator(String selector) {
|
||||
return new FrameLocatorImpl(this, selector);
|
||||
}
|
||||
|
||||
ElementHandle frameElementImpl() {
|
||||
JsonObject json = sendMessage("frameElement").getAsJsonObject();
|
||||
return connection.getExistingObject(json.getAsJsonObject("element").get("guid").getAsString());
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.FrameLocator;
|
||||
|
||||
class FrameLocatorImpl implements FrameLocator {
|
||||
private final FrameImpl frame;
|
||||
private final String frameSelector;
|
||||
|
||||
FrameLocatorImpl(FrameImpl frame, String selector) {
|
||||
this.frame = frame;
|
||||
this.frameSelector = selector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator first() {
|
||||
return new FrameLocatorImpl(frame, frameSelector + " >> nth=0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocatorImpl frameLocator(String selector) {
|
||||
return new FrameLocatorImpl(frame, frameSelector + " >> control=enter-frame >> " + selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator last() {
|
||||
return new FrameLocatorImpl(frame, frameSelector + " >> nth=-1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocatorImpl locator(String selector) {
|
||||
return new LocatorImpl(frame, frameSelector + " >> control=enter-frame >> " + selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator nth(int index) {
|
||||
return new FrameLocatorImpl(frame, frameSelector + " >> nth=" + index);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,7 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
import com.microsoft.playwright.Locator;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.BoundingBox;
|
||||
import com.microsoft.playwright.options.FilePayload;
|
||||
import com.microsoft.playwright.options.SelectOption;
|
||||
@@ -151,6 +148,11 @@ class LocatorImpl implements Locator {
|
||||
frame.focus(selector, convertViaJson(options, Frame.FocusOptions.class).setStrict(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocatorImpl frameLocator(String selector) {
|
||||
return new FrameLocatorImpl(frame, this.selector + " >> " + selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String name, GetAttributeOptions options) {
|
||||
if (options == null) {
|
||||
|
||||
@@ -737,6 +737,11 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
return frameFor(new UrlMatcher(predicate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameLocator frameLocator(String selector) {
|
||||
return mainFrame.frameLocator(selector);
|
||||
}
|
||||
|
||||
private Frame frameFor(UrlMatcher matcher) {
|
||||
for (Frame frame : frames) {
|
||||
if (matcher.test(frame.url())) {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.17.0-next-1636157051000
|
||||
1.17.0-next-1636496060000
|
||||
|
||||
Reference in New Issue
Block a user