feat: make waitForSelectro return Deferred (#13)
This commit is contained in:
@@ -173,6 +173,9 @@ class Types {
|
||||
add("Page.waitForResponse", "Promise<Response>", "Deferred<Response>");
|
||||
add("Page.waitForNavigation", "Promise<null|Response>", "Deferred<Response>");
|
||||
add("Frame.waitForNavigation", "Promise<null|Response>", "Deferred<Response>");
|
||||
add("Page.waitForSelector", "Promise<null|ElementHandle>", "Deferred<ElementHandle>", new Empty());
|
||||
add("Frame.waitForSelector", "Promise<null|ElementHandle>", "Deferred<ElementHandle>", new Empty());
|
||||
add("ElementHandle.waitForSelector", "Promise<null|ElementHandle>", "Deferred<ElementHandle>", new Empty());
|
||||
|
||||
// Custom options
|
||||
add("Page.pdf.options.margin.top", "string|number", "String");
|
||||
|
||||
@@ -438,9 +438,9 @@ public interface ElementHandle extends JSHandle {
|
||||
waitForElementState(state, null);
|
||||
}
|
||||
void waitForElementState(ElementState state, WaitForElementStateOptions options);
|
||||
default ElementHandle waitForSelector(String selector) {
|
||||
default Deferred<ElementHandle> waitForSelector(String selector) {
|
||||
return waitForSelector(selector, null);
|
||||
}
|
||||
ElementHandle waitForSelector(String selector, WaitForSelectorOptions options);
|
||||
Deferred<ElementHandle> waitForSelector(String selector, WaitForSelectorOptions options);
|
||||
}
|
||||
|
||||
|
||||
@@ -587,10 +587,10 @@ public interface Frame {
|
||||
return waitForNavigation(null);
|
||||
}
|
||||
Deferred<Response> waitForNavigation(WaitForNavigationOptions options);
|
||||
default ElementHandle waitForSelector(String selector) {
|
||||
default Deferred<ElementHandle> waitForSelector(String selector) {
|
||||
return waitForSelector(selector, null);
|
||||
}
|
||||
ElementHandle waitForSelector(String selector, WaitForSelectorOptions options);
|
||||
Deferred<ElementHandle> waitForSelector(String selector, WaitForSelectorOptions options);
|
||||
void waitForTimeout(int timeout);
|
||||
}
|
||||
|
||||
|
||||
@@ -954,10 +954,10 @@ public interface Page {
|
||||
return waitForResponse(urlOrPredicate, null);
|
||||
}
|
||||
Deferred<Response> waitForResponse(String urlOrPredicate, WaitForResponseOptions options);
|
||||
default ElementHandle waitForSelector(String selector) {
|
||||
default Deferred<ElementHandle> waitForSelector(String selector) {
|
||||
return waitForSelector(selector, null);
|
||||
}
|
||||
ElementHandle waitForSelector(String selector, WaitForSelectorOptions options);
|
||||
Deferred<ElementHandle> waitForSelector(String selector, WaitForSelectorOptions options);
|
||||
void waitForTimeout(int timeout);
|
||||
List<Worker> workers();
|
||||
Deferred<Void> waitForClose();
|
||||
|
||||
@@ -18,13 +18,13 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Deferred;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class ChannelOwner {
|
||||
final Connection connection;
|
||||
@@ -62,6 +62,10 @@ class ChannelOwner {
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
Deferred<JsonElement> sendMessageAsync(String method, JsonObject params) {
|
||||
return connection.sendMessageAsync(guid, method, params);
|
||||
}
|
||||
|
||||
JsonElement sendMessage(String method, JsonObject params) {
|
||||
return connection.sendMessage(guid, method, params);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Deferred;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@@ -67,17 +68,24 @@ public class Connection {
|
||||
}
|
||||
|
||||
public JsonElement sendMessage(String guid, String method, JsonObject params) {
|
||||
CompletableFuture<Message> result = internalSendMessage(guid, method, params);
|
||||
while (!result.isDone()) {
|
||||
processOneMessage();
|
||||
}
|
||||
try {
|
||||
return result.get().result;
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return sendMessageAsync(guid, method, params).get();
|
||||
}
|
||||
|
||||
public Deferred<JsonElement> sendMessageAsync(String guid, String method, JsonObject params) {
|
||||
CompletableFuture<Message> result = internalSendMessage(guid, method, params);
|
||||
return () -> {
|
||||
while (!result.isDone()) {
|
||||
processOneMessage();
|
||||
}
|
||||
try {
|
||||
return result.get().result;
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public void sendMessageNoWait(String guid, String method, JsonObject params) {
|
||||
internalSendMessage(guid, method, params);
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Deferred;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -229,7 +229,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
public Deferred<ElementHandle> waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,9 +513,29 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return new WaitForNavigationHelper(new UrlMatcher(options.url), options.waitUntil);
|
||||
}
|
||||
|
||||
private static String toProtocol(WaitForSelectorOptions.State state) {
|
||||
return state.toString().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return null;
|
||||
public Deferred<ElementHandle> waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForSelectorOptions();
|
||||
}
|
||||
JsonObject params = new Gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
if (options.state != null) {
|
||||
params.remove("state");
|
||||
params.addProperty("state", toProtocol(options.state));
|
||||
}
|
||||
Deferred<JsonElement> json = sendMessageAsync("waitForSelector", params);
|
||||
return () -> {
|
||||
JsonObject element = json.get().getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return connection.getExistingObject(element.get("guid").getAsString());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
@@ -569,8 +568,8 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return null;
|
||||
public Deferred<ElementHandle> waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return mainFrame.waitForSelector(selector, convertViaJson(options, Frame.WaitForSelectorOptions.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/**
|
||||
* 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.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestEvalOnSelector {
|
||||
private static Playwright playwright;
|
||||
private static Server server;
|
||||
private static Browser browser;
|
||||
private static boolean isChromium;
|
||||
private static boolean isWebKit;
|
||||
private static boolean headful;
|
||||
private BrowserContext context;
|
||||
private Page page;
|
||||
|
||||
@BeforeAll
|
||||
static void launchBrowser() {
|
||||
playwright = Playwright.create();
|
||||
BrowserType.LaunchOptions options = new BrowserType.LaunchOptions();
|
||||
browser = playwright.chromium().launch(options);
|
||||
isChromium = true;
|
||||
isWebKit = false;
|
||||
headful = false;
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
static void startServer() throws IOException {
|
||||
server = new Server(8907);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void stopServer() throws IOException {
|
||||
browser.close();
|
||||
server.stop();
|
||||
server = null;
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
server.reset();
|
||||
context = browser.newContext();
|
||||
page = context.newPage();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
context.close();
|
||||
context = null;
|
||||
page = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithCssSelector() {
|
||||
page.setContent("<section id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("css=section", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithIdSelector() {
|
||||
page.setContent("<section id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("id=testAttribute", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithDataTestSelector() {
|
||||
page.setContent("<section data-test=foo id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("data-test=foo", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithDataTestidSelector() {
|
||||
page.setContent("<section data-testid=foo id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("data-testid=foo", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithDataTestIdSelector() {
|
||||
page.setContent("<section data-test-id=foo id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("data-test-id=foo", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithTextSelector1() {
|
||||
page.setContent("<section id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("text='43543'", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithXpathSelector() {
|
||||
page.setContent("<section id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("xpath=/html/body/section", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithTextSelector2() {
|
||||
page.setContent("<section id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("text=43543", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAutoDetectCssSelector() {
|
||||
page.setContent("<section id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("section", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAutoDetectCssSelectorWithAttributes() {
|
||||
page.setContent("<section id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("section[id='testAttribute']", "e => e.id");
|
||||
assertEquals("testAttribute", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAutoDetectNestedSelectors() {
|
||||
page.setContent("<div foo=bar><section>43543<span>Hello<div id=target></div></span></section></div>");
|
||||
Object idAttribute = page.evalOnSelector("div[foo=bar] > section >> 'Hello' >> div", "e => e.id");
|
||||
assertEquals("target", idAttribute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAcceptArguments() {
|
||||
page.setContent("<section>hello</section>");
|
||||
Object text = page.evalOnSelector("section", "(e, suffix) => e.textContent + suffix", " world!");
|
||||
assertEquals("hello world!", text);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAcceptElementHandlesAsArguments() {
|
||||
page.setContent("<section>hello</section><div> world</div>");
|
||||
ElementHandle divHandle = page.querySelector("div");
|
||||
Object text = page.evalOnSelector("section", "(e, div) => e.textContent + div.textContent", divHandle);
|
||||
assertEquals("hello world", text);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowErrorIfNoElementIsFound() {
|
||||
try {
|
||||
page.evalOnSelector("section", "e => e.id");
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("failed to find element matching selector \"section\""));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportSyntax() {
|
||||
page.setContent("<section><div>hello</div></section>");
|
||||
Object text = page.evalOnSelector("css=section >> css=div", "(e, suffix) => e.textContent + suffix", " world!");
|
||||
assertEquals("hello world!", text);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportSyntaxWithDifferentEngines() {
|
||||
page.setContent("<section><div><span>hello</span></div></section>");
|
||||
Object text = page.evalOnSelector("xpath=/html/body/section >> css=div >> text='hello'", "(e, suffix) => e.textContent + suffix", " world!");
|
||||
assertEquals("hello world!", text);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportSpacesWithSyntax() {
|
||||
page.navigate(server.PREFIX + "/deep-shadow.html");
|
||||
Object text = page.evalOnSelector(" css = div >>css=div>>css = span ", "e => e.textContent");
|
||||
assertEquals("Hello from root2", text);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotStopAtFirstFailureWithSyntax() {
|
||||
page.setContent("<div><span>Next</span><button>Previous</button><button>Next</button></div>");
|
||||
Object html = page.evalOnSelector("button >> 'Next'", "e => e.outerHTML");
|
||||
assertEquals("<button>Next</button>", html);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportCapture() {
|
||||
page.setContent("<section><div><span>a</span></div></section><section><div><span>b</span></div></section>");
|
||||
assertEquals("<div><span>b</span></div>", page.evalOnSelector("*css=div >> 'b'", "e => e.outerHTML"));
|
||||
assertEquals("<div><span>b</span></div>", page.evalOnSelector("section >> *css=div >> 'b'", "e => e.outerHTML"));
|
||||
assertEquals("<span>b</span>", page.evalOnSelector("css=div >> *text='b'", "e => e.outerHTML"));
|
||||
assertNotNull(page.querySelector("*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowOnMultipleCaptures() {
|
||||
try {
|
||||
page.evalOnSelector("*css=div >> *css=span", "e => e.outerHTML");
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Only one of the selectors can capture using * modifier"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowOnMalformedCapture() {
|
||||
try {
|
||||
page.evalOnSelector("*=div", "e => e.outerHTML");
|
||||
fail("did not throw");
|
||||
} catch (RuntimeException e) {
|
||||
assertTrue(e.getMessage().contains("Unknown engine \"\" while parsing selector *=div"));
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void shouldWorkWithSpacesInCssAttributes() {
|
||||
page.setContent("<div><input placeholder='Select date'></div>");
|
||||
assertNotNull(page.waitForSelector("[placeholder=\"Select date\"]"));
|
||||
assertNotNull(page.waitForSelector("[placeholder='Select date']"));
|
||||
assertNotNull(page.waitForSelector("input[placeholder=\"Select date\"]"));
|
||||
assertNotNull(page.waitForSelector("input[placeholder='Select date']"));
|
||||
assertNotNull(page.querySelector("[placeholder=\"Select date\"]"));
|
||||
assertNotNull(page.querySelector("[placeholder='Select date']"));
|
||||
assertNotNull(page.querySelector("input[placeholder=\"Select date\"]"));
|
||||
assertNotNull(page.querySelector("input[placeholder='Select date']"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("[placeholder=\"Select date\"]", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("[placeholder='Select date']", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("input[placeholder=\"Select date\"]", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("input[placeholder='Select date']", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("css=[placeholder=\"Select date\"]", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("css=[placeholder='Select date']", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("css=input[placeholder=\"Select date\"]", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("css=input[placeholder='Select date']", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("div >> [placeholder=\"Select date\"]", "e => e.outerHTML"));
|
||||
assertEquals("<input placeholder=\"Select date\">", page.evalOnSelector("div >> [placeholder='Select date']", "e => e.outerHTML"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithQuotesInCssAttributes() {
|
||||
page.setContent("<div><input placeholder=\"Select"date\"></div>");
|
||||
assertNotNull(page.querySelector("[placeholder=\"Select\\\"date\"]"));
|
||||
assertNotNull(page.querySelector("[placeholder='Select\"date']"));
|
||||
page.setContent("<div><input placeholder=\"Select " date\"></div>");
|
||||
assertNotNull(page.querySelector("[placeholder=\"Select \\\" date\"]"));
|
||||
assertNotNull(page.querySelector("[placeholder='Select \" date']"));
|
||||
page.setContent("<div><input placeholder=\"Select'date\"></div>");
|
||||
assertNotNull(page.querySelector("[placeholder=\"Select'date\"]"));
|
||||
assertNotNull(page.querySelector("[placeholder='Select\\'date']"));
|
||||
page.setContent("<div><input placeholder=\"Select ' date\"></div>");
|
||||
assertNotNull(page.querySelector("[placeholder=\"Select ' date\"]"));
|
||||
assertNotNull(page.querySelector("[placeholder='Select \\' date']"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithSpacesInCssAttributesWhenMissing() {
|
||||
Deferred<ElementHandle> inputPromise = page.waitForSelector("[placeholder='Select date']");
|
||||
assertNull(page.querySelector("[placeholder='Select date']"));
|
||||
page.setContent("<div><input placeholder='Select date'></div>");
|
||||
inputPromise.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithQuotesInCssAttributesWhenMissing() {
|
||||
Deferred<ElementHandle> inputPromise = page.waitForSelector("[placeholder='Select\\\"date']");
|
||||
assertNull(page.querySelector("[placeholder='Select\\\"date']"));
|
||||
page.setContent("<div><input placeholder='Select"date'></div>");
|
||||
inputPromise.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnComplexValues() {
|
||||
page.setContent("<section id='testAttribute'>43543</section>");
|
||||
Object idAttribute = page.evalOnSelector("css=section", "e => [{ id: e.id }]");
|
||||
assertEquals(Arrays.asList(Collections.singletonMap("id", "testAttribute")), idAttribute);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user