From 2b0b50358bbe9bf24d5d6897983d045100d1167b Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Tue, 13 Jul 2021 06:15:36 -0700 Subject: [PATCH] feat: remaining baseUrl implementation bits (#515) --- .../playwright/impl/BrowserContextImpl.java | 15 +++- .../playwright/impl/BrowserImpl.java | 3 + .../playwright/impl/BrowserTypeImpl.java | 3 + .../microsoft/playwright/impl/FrameImpl.java | 4 +- .../microsoft/playwright/impl/PageImpl.java | 12 +-- .../microsoft/playwright/impl/UrlMatcher.java | 23 ++++-- .../java/com/microsoft/playwright/Server.java | 3 + .../playwright/TestBrowserContextBaseUrl.java | 73 +++++++++++++++++-- 8 files changed, 113 insertions(+), 23 deletions(-) diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java index f6b045e1..e0d53e3f 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java @@ -26,6 +26,8 @@ import com.microsoft.playwright.options.FunctionCallback; import com.microsoft.playwright.options.Geolocation; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; @@ -53,6 +55,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { private final ListenerCollection listeners = new ListenerCollection<>(); final TimeoutSettings timeoutSettings = new TimeoutSettings(); Path videosDir; + URL baseUrl; enum EventType { CLOSE, @@ -73,6 +76,14 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { this.tracing = new TracingImpl(this); } + void setBaseUrl(String spec) { + try { + this.baseUrl = new URL(spec); + } catch (MalformedURLException e) { + this.baseUrl = null; + } + } + @Override public void onClose(Consumer handler) { listeners.add(EventType.CLOSE, handler); @@ -307,7 +318,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { @Override public void route(String url, Consumer handler) { - route(new UrlMatcher(url), handler); + route(new UrlMatcher(this.baseUrl, url), handler); } @Override @@ -406,7 +417,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { @Override public void unroute(String url, Consumer handler) { - unroute(new UrlMatcher(url), handler); + unroute(new UrlMatcher(this.baseUrl, url), handler); } @Override diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java index 2c559a71..2cd546b8 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java @@ -171,6 +171,9 @@ class BrowserImpl extends ChannelOwner implements Browser { if (options.recordVideoDir != null) { context.videosDir = options.recordVideoDir; } + if (options.baseURL != null) { + context.setBaseUrl(options.baseURL); + } contexts.add(context); return context; } diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java index 6e9890b7..9a78a4ad 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java @@ -189,6 +189,9 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType { if (options.recordVideoDir != null) { context.videosDir = options.recordVideoDir; } + if (options.baseURL != null) { + context.setBaseUrl(options.baseURL); + } return context; } diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java index 3c016929..551914cd 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/FrameImpl.java @@ -885,7 +885,7 @@ public class FrameImpl extends ChannelOwner implements Frame { List> waitables = new ArrayList<>(); if (matcher == null) { - matcher = UrlMatcher.forOneOf(options.url); + matcher = UrlMatcher.forOneOf(page.context().baseUrl, options.url); } waitables.add(new WaitForNavigationHelper(matcher, convertViaJson(options.waitUntil, LoadState.class))); waitables.add(page.createWaitForCloseHelper()); @@ -930,7 +930,7 @@ public class FrameImpl extends ChannelOwner implements Frame { @Override public void waitForURL(String url, WaitForURLOptions options) { - waitForURL(new UrlMatcher(url), options); + waitForURL(new UrlMatcher(page.context().baseUrl, url), options); } @Override diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java index 7e90b520..529a9bb7 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java @@ -716,7 +716,7 @@ public class PageImpl extends ChannelOwner implements Page { @Override public Frame frameByUrl(String glob) { - return frameFor(new UrlMatcher(glob)); + return frameFor(new UrlMatcher(browserContext.baseUrl, glob)); } @Override @@ -931,7 +931,7 @@ public class PageImpl extends ChannelOwner implements Page { @Override public void route(String url, Consumer handler) { - route(new UrlMatcher(url), handler); + route(new UrlMatcher(browserContext.baseUrl, url), handler); } @Override @@ -1139,7 +1139,7 @@ public class PageImpl extends ChannelOwner implements Page { @Override public void unroute(String url, Consumer handler) { - unroute(new UrlMatcher(url), handler); + unroute(new UrlMatcher(browserContext.baseUrl, url), handler); } @Override @@ -1276,7 +1276,7 @@ public class PageImpl extends ChannelOwner implements Page { @Override public Request waitForRequest(String urlGlob, WaitForRequestOptions options, Runnable code) { - return waitForRequest(toRequestPredicate(new UrlMatcher(urlGlob)), options, code); + return waitForRequest(toRequestPredicate(new UrlMatcher(browserContext.baseUrl, urlGlob)), options, code); } @Override @@ -1325,7 +1325,7 @@ public class PageImpl extends ChannelOwner implements Page { @Override public Response waitForResponse(String urlGlob, WaitForResponseOptions options, Runnable code) { - return waitForResponse(toResponsePredicate(new UrlMatcher(urlGlob)), options, code); + return waitForResponse(toResponsePredicate(new UrlMatcher(browserContext.baseUrl, urlGlob)), options, code); } @Override @@ -1367,7 +1367,7 @@ public class PageImpl extends ChannelOwner implements Page { @Override public void waitForURL(String url, WaitForURLOptions options) { - waitForURL(new UrlMatcher(url), options); + waitForURL(new UrlMatcher(browserContext.baseUrl, url), options); } @Override diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java b/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java index af6f7bfd..c0c599e9 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/UrlMatcher.java @@ -18,6 +18,8 @@ package com.microsoft.playwright.impl; import com.microsoft.playwright.PlaywrightException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Objects; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -33,15 +35,15 @@ class UrlMatcher { } static UrlMatcher any() { - return new UrlMatcher(null, null); + return new UrlMatcher((Object) null, null); } - static UrlMatcher forOneOf(Object object) { + static UrlMatcher forOneOf(URL baseUrl, Object object) { if (object == null) { return UrlMatcher.any(); } if (object instanceof String) { - return new UrlMatcher((String) object); + return new UrlMatcher(baseUrl, (String) object); } if (object instanceof Pattern) { return new UrlMatcher((Pattern) object); @@ -52,8 +54,19 @@ class UrlMatcher { throw new PlaywrightException("Url must be String, Pattern or Predicate, found: " + object.getClass().getTypeName()); } - UrlMatcher(String url) { - this(url, toPredicate(Pattern.compile(globToRegex(url))).or(s -> url == null || url.equals(s))); + private static String resolveUrl(URL baseUrl, String spec) { + if (baseUrl == null) { + return spec; + } + try { + return new URL(baseUrl, spec).toString(); + } catch (MalformedURLException e) { + return spec; + } + } + + UrlMatcher(URL base, String url) { + this(url, toPredicate(Pattern.compile(globToRegex(resolveUrl(base, url)))).or(s -> url == null || url.equals(s))); } UrlMatcher(Pattern pattern) { diff --git a/playwright/src/test/java/com/microsoft/playwright/Server.java b/playwright/src/test/java/com/microsoft/playwright/Server.java index bb4876a5..51686bdc 100644 --- a/playwright/src/test/java/com/microsoft/playwright/Server.java +++ b/playwright/src/test/java/com/microsoft/playwright/Server.java @@ -189,6 +189,9 @@ public class Server implements HttpHandler { if (csp.containsKey(path)) { exchange.getResponseHeaders().add("Content-Security-Policy", csp.get(path)); } + if ("/".equals(path)) { + path = "/index.html"; + } File file = new File(resourcesDir, path.substring(1)); if (!file.exists()) { exchange.sendResponseHeaders(404, 0); diff --git a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextBaseUrl.java b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextBaseUrl.java index 0a253ae2..a8892c23 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextBaseUrl.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextBaseUrl.java @@ -2,21 +2,78 @@ package com.microsoft.playwright; import org.junit.jupiter.api.Test; +import java.net.MalformedURLException; + import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class TestBrowserContextBaseUrl extends TestBase { @Test - void shouldConstructANewURLWhenABaseURLInBrowserNewContextIsPassedToPageGoto() { - BrowserContext context = browser.newContext(new Browser.NewContextOptions().setBaseURL(server.PREFIX)); - Page page = context.newPage(); - assertEquals(server.EMPTY_PAGE, (page.navigate("/empty.html")).url()); - context.close(); + void shouldConstructANewURLWhenABaseURLInBrowserNewContextIsPassedToPageGoto() throws MalformedURLException { + try (BrowserContext context = browser.newContext(new Browser.NewContextOptions().setBaseURL(server.PREFIX))) { + Page page = context.newPage(); + assertEquals(server.EMPTY_PAGE, page.navigate("/empty.html").url()); + } } @Test void shouldConstructANewURLWhenABaseURLInBrowserNewPageIsPassedToPageGoto() { - Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX)); - assertEquals(server.EMPTY_PAGE, (page.navigate("/empty.html")).url()); - page.close(); + try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX))) { + assertEquals(server.EMPTY_PAGE, page.navigate("/empty.html").url()); + } + } + @Test + void shouldConstructTheURLsCorrectlyWhenABaseURLWithoutATrailingSlashInBrowserNewPageIsPassedToPageGoto() { + try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX + "/url-construction"))) { + assertEquals(server.PREFIX + "/mypage.html", page.navigate("mypage.html").url()); + assertEquals(server.PREFIX + "/mypage.html", page.navigate("./mypage.html").url()); + assertEquals(server.PREFIX + "/mypage.html", page.navigate("/mypage.html").url()); + } + } + + @Test + void shouldConstructTheURLsCorrectlyWhenABaseURLWithATrailingSlashInBrowserNewPageIsPassedToPageGoto() { + try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX + "/url-construction/"))) { + assertEquals(server.PREFIX + "/url-construction/mypage.html", page.navigate("mypage.html").url()); + assertEquals(server.PREFIX + "/url-construction/mypage.html", page.navigate("./mypage.html").url()); + assertEquals(server.PREFIX + "/mypage.html", page.navigate("/mypage.html").url()); + assertEquals(server.PREFIX + "/url-construction/", page.navigate(".").url()); + assertEquals(server.PREFIX + "/", page.navigate("/").url()); + } + } + + @Test + void shouldNotConstructANewURLWhenValidURLsArePassed() { + try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL("http://microsoft.com"))) { + assertEquals(server.EMPTY_PAGE, page.navigate(server.EMPTY_PAGE).url()); + + page.navigate("data:text/html,Hello world"); + assertEquals("data:text/html,Hello world", page.evaluate("window.location.href")); + + page.navigate("about:blank"); + assertEquals("about:blank", page.evaluate("window.location.href")); + } + } + + @Test + void shouldBeAbleToMatchAURLRelativeToItsGivenURLWithUrlMatcher() { + try (Page page = browser.newPage(new Browser.NewPageOptions().setBaseURL(server.PREFIX + "/foobar/"))) { + page.navigate("/kek/index.html"); + page.waitForURL("/kek/index.html"); + assertEquals(server.PREFIX + "/kek/index.html", page.url()); + + page.route("./kek/index.html", route -> route.fulfill(new Route.FulfillOptions().setBody("base-url-matched-route"))); + Request[] request = {null}; + Response response = page.waitForResponse("./kek/index.html", () -> { + request[0] = page.waitForRequest("./kek/index.html", () -> { + page.navigate("./kek/index.html"); + }); + }); + assertNotNull(request[0]); + assertNotNull(response); + assertEquals(server.PREFIX + "/foobar/kek/index.html", request[0].url()); + assertEquals(server.PREFIX + "/foobar/kek/index.html", response.url()); + assertEquals("base-url-matched-route", response.text()); + } } }