1
0
mirror of synced 2026-05-22 18:53:15 +00:00

feat: make waitForSelectro return Deferred (#13)

This commit is contained in:
Yury Semikhatsky
2020-10-13 15:06:03 -07:00
committed by GitHub
parent 4ca82f8761
commit 230ca30e41
10 changed files with 350 additions and 23 deletions
@@ -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&quot;date\"></div>");
assertNotNull(page.querySelector("[placeholder=\"Select\\\"date\"]"));
assertNotNull(page.querySelector("[placeholder='Select\"date']"));
page.setContent("<div><input placeholder=\"Select &quot; date\"></div>");
assertNotNull(page.querySelector("[placeholder=\"Select \\\" date\"]"));
assertNotNull(page.querySelector("[placeholder='Select \" date']"));
page.setContent("<div><input placeholder=\"Select&apos;date\"></div>");
assertNotNull(page.querySelector("[placeholder=\"Select'date\"]"));
assertNotNull(page.querySelector("[placeholder='Select\\'date']"));
page.setContent("<div><input placeholder=\"Select &apos; 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&quot;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);
}
}