diff --git a/.hgignore b/.hgignore
index aa8ee10d9fd8..c398e60c0033 100644
--- a/.hgignore
+++ b/.hgignore
@@ -126,6 +126,8 @@ _OPT\.OBJ/
^remote/test/puppeteer/test/build
^remote/test/puppeteer/test/output-firefox
^remote/test/puppeteer/test/output-chromium
+^remote/test/puppeteer/testserver/lib/
+^remote/test/puppeteer/utils/mochaRunner/lib/
^remote/test/puppeteer/website
# git checkout of libstagefright
diff --git a/remote/.gitignore b/remote/.gitignore
index 33fc56a0b3cd..516011818958 100644
--- a/remote/.gitignore
+++ b/remote/.gitignore
@@ -15,4 +15,6 @@ test/puppeteer/src/generated
test/puppeteer/test/build
test/puppeteer/test/output-firefox
test/puppeteer/test/output-chromium
+test/puppeteer/testserver/lib/
+test/puppeteer/utils/mochaRunner/lib/
test/puppeteer/website
diff --git a/remote/mach_commands.py b/remote/mach_commands.py
index 40ac0a1378ad..b51f4d64c91a 100644
--- a/remote/mach_commands.py
+++ b/remote/mach_commands.py
@@ -17,7 +17,6 @@ import mozprofile
from mach.decorators import Command, CommandArgument, SubCommand
from mozbuild import nodeutil
from mozbuild.base import BinaryNotFoundException, MozbuildObject
-from six import iteritems
EX_CONFIG = 78
EX_SOFTWARE = 70
@@ -261,8 +260,10 @@ class MochaOutputHandler(object):
if not status and not test_start:
return
test_info = event[1]
- test_name = test_info.get("fullTitle", "")
+ test_full_title = test_info.get("fullTitle", "")
+ test_name = test_full_title
test_path = test_info.get("file", "")
+ test_file_name = os.path.basename(test_path).replace(".js", "")
test_err = test_info.get("err")
if status == "FAIL" and test_err:
if "timeout" in test_err.lower():
@@ -276,7 +277,32 @@ class MochaOutputHandler(object):
if test_start:
self.logger.test_start(test_name)
return
- expected = self.expected.get(test_name, ["PASS"])
+ expected_name = "[{}] {}".format(test_file_name, test_full_title)
+ expected_item = next(
+ (
+ expectation
+ for expectation in list(self.expected)
+ if expectation["testIdPattern"] == expected_name
+ ),
+ None,
+ )
+ if expected_item is None:
+ # if there is no expectation data for a specific test case,
+ # try to find data for a whole file.
+ expected_item_for_file = next(
+ (
+ expectation
+ for expectation in list(self.expected)
+ if expectation["testIdPattern"] == f"[{test_file_name}]"
+ ),
+ None,
+ )
+ if expected_item_for_file is None:
+ expected = ["PASS"]
+ else:
+ expected = expected_item_for_file["expectations"]
+ else:
+ expected = expected_item["expectations"]
# mozlog doesn't really allow unexpected skip,
# so if a test is disabled just expect that and note the unexpected skip
# Also, mocha doesn't log test-start for skipped tests
@@ -308,34 +334,7 @@ class MochaOutputHandler(object):
known_intermittent=known_intermittent,
)
- def new_expected(self):
- new_expected = OrderedDict()
- for test_name, status in iteritems(self.test_results):
- if test_name not in self.expected:
- new_status = [status]
- else:
- if status in self.expected[test_name]:
- new_status = self.expected[test_name]
- else:
- new_status = [status]
- new_expected[test_name] = new_status
- return new_expected
-
- def after_end(self, subset=False):
- if not subset:
- missing = set(self.expected) - set(self.test_results)
- extra = set(self.test_results) - set(self.expected)
- if missing:
- self.has_unexpected = True
- for test_name in missing:
- self.logger.error("TEST-UNEXPECTED-MISSING %s" % (test_name,))
- if self.expected and extra:
- self.has_unexpected = True
- for test_name in extra:
- self.logger.error(
- "TEST-UNEXPECTED-MISSING Unknown new test %s" % (test_name,)
- )
-
+ def after_end(self):
if self.unexpected_skips:
self.has_unexpected = True
for test_name in self.unexpected_skips:
@@ -392,8 +391,6 @@ class PuppeteerRunner(MozbuildObject):
before invoking npm. Overrides default preferences.
`enable_webrender`:
Boolean to indicate whether to enable WebRender compositor in Gecko.
- `write_results`:
- Path to write the results json file
`subset`
Indicates only a subset of tests are being run, so we should
skip the check for missing results
@@ -425,6 +422,7 @@ class PuppeteerRunner(MozbuildObject):
"--timeout",
"20000",
"--no-parallel",
+ "--no-coverage",
]
env["HEADLESS"] = str(params.get("headless", False))
@@ -454,15 +452,31 @@ class PuppeteerRunner(MozbuildObject):
env["EXTRA_LAUNCH_OPTIONS"] = json.dumps(extra_options)
expected_path = os.path.join(
- os.path.dirname(__file__), "test", "puppeteer-expected.json"
+ os.path.dirname(__file__),
+ "test",
+ "puppeteer",
+ "test",
+ "TestExpectations.json",
)
- if product == "firefox" and os.path.exists(expected_path):
+ if os.path.exists(expected_path):
with open(expected_path) as f:
expected_data = json.load(f)
else:
- expected_data = {}
+ expected_data = []
+ # Filter expectation data for the selected browser,
+ # headless or headful mode, and the operating system.
+ platform = os.uname().sysname.lower() if os.uname() else "win32"
+ expectations = filter(
+ lambda el: product in el["parameters"]
+ and (
+ (env["HEADLESS"] == "False" and "headless" not in el["parameters"])
+ or "headful" not in el["parameters"]
+ )
+ and platform in el["platforms"],
+ expected_data,
+ )
- output_handler = MochaOutputHandler(logger, expected_data)
+ output_handler = MochaOutputHandler(logger, list(expectations))
proc = npm(
*command,
cwd=self.puppeteer_dir,
@@ -476,7 +490,7 @@ class PuppeteerRunner(MozbuildObject):
# failure, so use an output_timeout as a fallback
wait_proc(proc, "npm", output_timeout=60, exit_on_fail=False)
- output_handler.after_end(params.get("subset", False))
+ output_handler.after_end()
# Non-zero return codes are non-fatal for now since we have some
# issues with unresolved promises that shouldn't otherwise block
@@ -484,12 +498,6 @@ class PuppeteerRunner(MozbuildObject):
if proc.returncode != 0:
logger.warning("npm exited with code %s" % proc.returncode)
- if params["write_results"]:
- with open(params["write_results"], "w") as f:
- json.dump(
- output_handler.new_expected(), f, indent=2, separators=(",", ": ")
- )
-
if output_handler.has_unexpected:
exit(1, "Got unexpected results")
@@ -547,18 +555,6 @@ def create_parser_puppeteer():
"debug level messages with -v, trace messages with -vv,"
"and to not truncate long trace messages with -vvv",
)
- p.add_argument(
- "--write-results",
- action="store",
- nargs="?",
- default=None,
- const=os.path.join(
- os.path.dirname(__file__), "test", "puppeteer-expected.json"
- ),
- help="Path to write updated results to (defaults to the "
- "expectations file if the argument is provided but "
- "no path is passed)",
- )
p.add_argument(
"--subset",
action="store_true",
@@ -597,7 +593,6 @@ def puppeteer_test(
verbosity=0,
tests=None,
product="firefox",
- write_results=None,
subset=False,
**kwargs,
):
@@ -657,7 +652,6 @@ def puppeteer_test(
"extra_prefs": prefs,
"product": product,
"extra_launcher_options": options,
- "write_results": write_results,
"subset": subset,
}
puppeteer = command_context._spawn(PuppeteerRunner)
diff --git a/remote/test/puppeteer-expected.json b/remote/test/puppeteer-expected.json
deleted file mode 100644
index eb1a12a7fc83..000000000000
--- a/remote/test/puppeteer-expected.json
+++ /dev/null
@@ -1,2455 +0,0 @@
-{
- "Accessibility should work (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility should report uninteresting nodes (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility roledescription (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility orientation (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility autocomplete (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility multiselectable (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility keyshortcuts (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes should not report text nodes inside controls (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes rich text editable fields should have children (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes rich text editable fields with role should have children (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes non editable textbox with role and tabIndex and label should not have children (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes checkbox with and tabIndex and label should not have children (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes checkbox without label should not have children (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes plaintext contenteditable plain text field with role should not have children (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes root option should work a button (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes root option should work an input (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes root option should work a menu (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes root option should return null when the element is no longer in DOM (accessibility.spec.js)": [
- "SKIP"
- ],
- "Accessibility filtering children of leaf nodes root option should support the interestingOnly option (accessibility.spec.js)": [
- "SKIP"
- ],
- "Browser specs Browser.version should return whether we are in headless (browser.spec.js)": [
- "PASS"
- ],
- "Browser specs Browser.userAgent should include WebKit (browser.spec.js)": [
- "PASS"
- ],
- "Browser specs Browser.target should return browser target (browser.spec.js)": [
- "PASS"
- ],
- "Browser specs Browser.process should return child_process instance (browser.spec.js)": [
- "PASS"
- ],
- "Browser specs Browser.process should not return child_process for remote browser (browser.spec.js)": [
- "PASS"
- ],
- "Browser specs Browser.isConnected should set the browser connected state (browser.spec.js)": [
- "PASS"
- ],
- "BrowserContext should have default context (browsercontext.spec.js)": [
- "PASS"
- ],
- "BrowserContext should create new incognito context (browsercontext.spec.js)": [
- "PASS"
- ],
- "BrowserContext should close all belonging targets once closing context (browsercontext.spec.js)": [
- "PASS"
- ],
- "BrowserContext window.open should use parent tab context (browsercontext.spec.js)": [
- "FAIL"
- ],
- "BrowserContext should fire target events (browsercontext.spec.js)": [
- "FAIL"
- ],
- "BrowserContext should wait for a target (browsercontext.spec.js)": [
- "TIMEOUT"
- ],
- "BrowserContext should timeout waiting for a non-existent target (browsercontext.spec.js)": [
- "PASS"
- ],
- "BrowserContext should isolate localStorage and cookies (browsercontext.spec.js)": [
- "FAIL"
- ],
- "BrowserContext should work across sessions (browsercontext.spec.js)": [
- "FAIL"
- ],
- "Page.click should click the button (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click svg (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click the button if window.Node is removed (click.spec.js)": [
- "FAIL"
- ],
- "Page.click should click on a span with an inline element inside (click.spec.js)": [
- "PASS"
- ],
- "Page.click should not throw UnhandledPromiseRejection when page closes (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click the button after navigation (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click with disabled javascript (click.spec.js)": [
- "FAIL"
- ],
- "Page.click should click when one of inline box children is outside of viewport (click.spec.js)": [
- "PASS"
- ],
- "Page.click should select the text by triple clicking (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click offscreen buttons (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click wrapped links (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click on checkbox input and toggle (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click on checkbox label and toggle (click.spec.js)": [
- "FAIL", "PASS"
- ],
- "Page.click should fail to click a missing button (click.spec.js)": [
- "PASS"
- ],
- "Page.click should not hang with touch-enabled viewports (click.spec.js)": [
- "PASS"
- ],
- "Page.click should scroll and click the button (click.spec.js)": [
- "PASS"
- ],
- "Page.click should double click the button (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click a partially obscured button (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click a rotated button (click.spec.js)": [
- "PASS"
- ],
- "Page.click should fire contextmenu event on right click (click.spec.js)": [
- "PASS"
- ],
- "Page.click should fire aux event on middle click (click.spec.js)": [
- "PASS"
- ],
- "Page.click should fire back click (click.spec.js)": [
- "PASS"
- ],
- "Page.click should fire forward click (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click links which cause navigation (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click the button inside an iframe (click.spec.js)": [
- "PASS"
- ],
- "Page.click should click the button with fixed position inside an iframe (click.spec.js)": [
- "SKIP"
- ],
- "Page.click should click the button with deviceScaleFactor set (click.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.cookies should return no cookies in pristine browser context (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.cookies should get a cookie (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.cookies should properly report httpOnly cookie (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.cookies should properly report \"Strict\" sameSite cookie (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.cookies should properly report \"Lax\" sameSite cookie (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.cookies should get multiple cookies (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.cookies should get cookies from multiple urls (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.setCookie should work (cookies.spec.js)": [
- "FAIL"
- ],
- "Cookie specs Page.setCookie should isolate cookies in browser contexts (cookies.spec.js)": [
- "FAIL"
- ],
- "Cookie specs Page.setCookie should set multiple cookies (cookies.spec.js)": [
- "FAIL"
- ],
- "Cookie specs Page.setCookie should have |expires| set to |-1| for session cookies (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.setCookie should set cookie with reasonable defaults (cookies.spec.js)": [
- "FAIL"
- ],
- "Cookie specs Page.setCookie should set a cookie with a path (cookies.spec.js)": [
- "FAIL"
- ],
- "Cookie specs Page.setCookie should not set a cookie on a blank page (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.setCookie should not set a cookie with blank page URL (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.setCookie should not set a cookie on a data URL page (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.setCookie should default to setting secure cookie for HTTPS websites (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.setCookie should be able to set unsecure cookie for HTTP website (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.setCookie should set a cookie on a different domain (cookies.spec.js)": [
- "FAIL"
- ],
- "Cookie specs Page.setCookie should set cookies from a frame (cookies.spec.js)": [
- "FAIL"
- ],
- "Cookie specs Page.setCookie should set secure same-site cookies from a frame (cookies.spec.js)": [
- "PASS"
- ],
- "Cookie specs Page.deleteCookie should work (cookies.spec.js)": [
- "FAIL"
- ],
- "DefaultBrowserContext page.cookies() should work (defaultbrowsercontext.spec.js)": [
- "PASS"
- ],
- "DefaultBrowserContext page.setCookie() should work (defaultbrowsercontext.spec.js)": [
- "FAIL"
- ],
- "DefaultBrowserContext page.deleteCookie() should work (defaultbrowsercontext.spec.js)": [
- "FAIL"
- ],
- "Page.Events.Dialog should fire (dialog.spec.js)": [
- "PASS"
- ],
- "Page.Events.Dialog should allow accepting prompts (dialog.spec.js)": [
- "FAIL"
- ],
- "Page.Events.Dialog should dismiss the prompt (dialog.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.boundingBox should work (elementhandle.spec.js)": [
- "FAIL"
- ],
- "ElementHandle specs ElementHandle.boundingBox should handle nested frames (elementhandle.spec.js)": [
- "PASS", "FAIL"
- ],
- "ElementHandle specs ElementHandle.boundingBox should return null for invisible elements (elementhandle.spec.js)": [
- "FAIL"
- ],
- "ElementHandle specs ElementHandle.boundingBox should force a layout (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.boundingBox should work with SVG nodes (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.boxModel should work (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.boxModel should return null for invisible elements (elementhandle.spec.js)": [
- "FAIL"
- ],
- "ElementHandle specs ElementHandle.contentFrame should work (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.click should work (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.click should work for Shadow DOM v1 (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.click should not work for TextNodes (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.click should throw for detached nodes (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.click should throw for hidden nodes (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.click should throw for recursively hidden nodes (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.click should throw for elements (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Element.waitForSelector should wait correctly with waitForSelector on an element (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Element.waitForXPath should wait correctly with waitForXPath on an element (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.hover should work (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.isIntersectingViewport should work (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.isIntersectingViewport should work with threshold (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs ElementHandle.isIntersectingViewport should work with threshold of 1 (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should register and unregister (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should throw with invalid query names (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should work for multiple elements (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should eval correctly (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should wait correctly with waitForSelector (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should wait correctly with waitForSelector on an element (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should wait correctly with waitFor (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should work when both queryOne and queryAll are registered (elementhandle.spec.js)": [
- "PASS"
- ],
- "ElementHandle specs Custom queries should eval when both queryOne and queryAll are registered (elementhandle.spec.js)": [
- "PASS"
- ],
- "Emulation Page.viewport should get the proper viewport size (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.viewport should support mobile emulation (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.viewport should support touch emulation (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.viewport should be detectable by Modernizr (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.viewport should detect touch when applying viewport with touches (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.viewport should support landscape emulation (emulation.spec.js)": [
- "FAIL"
- ],
- "Emulation Page.emulate should work (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.emulate should support clicking (emulation.spec.js)": [
- "PASS", "FAIL"
- ],
- "Emulation Page.emulateMediaType should work (emulation.spec.js)": [
- "FAIL"
- ],
- "Emulation Page.emulateMediaType should throw in case of bad argument (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.emulateMediaFeatures should work (emulation.spec.js)": [
- "FAIL"
- ],
- "Emulation Page.emulateMediaFeatures should throw in case of bad argument (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.emulateTimezone should work (emulation.spec.js)": [
- "FAIL"
- ],
- "Emulation Page.emulateTimezone should throw for invalid timezone IDs (emulation.spec.js)": [
- "FAIL"
- ],
- "Emulation Page.emulateVisionDeficiency should work (emulation.spec.js)": [
- "FAIL"
- ],
- "Emulation Page.emulateVisionDeficiency should throw for invalid vision deficiencies (emulation.spec.js)": [
- "PASS"
- ],
- "Emulation Page.emulateNetworkConditions should change navigator.connection.effectiveType (emulation.spec.js)": [
- "FAIL"
- ],
- "Emulation Page.emulateCPUThrottling should change the CPU throttling rate successfully (emulation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Page.evaluate should work (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should transfer BigInt (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should transfer NaN (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should transfer -0 (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should transfer Infinity (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should transfer -Infinity (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should transfer arrays (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should transfer arrays as arrays, not objects (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should modify global environment (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should evaluate in the page context (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should return undefined for objects with symbols (evaluation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Page.evaluate should work with function shorthands (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should work with unicode chars (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should throw when evaluation triggers reload (evaluation.spec.js)": [
- "TIMEOUT"
- ],
- "Evaluation specs Page.evaluate should await promise (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should work right after framenavigated (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should work from-inside an exposed function (evaluation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Page.evaluate should reject promise with exception (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should support thrown strings as error messages (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should support thrown numbers as error messages (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should return complex objects (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should return BigInt (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should return NaN (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should return -0 (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should return Infinity (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should return -Infinity (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should accept \"null\" as one of multiple parameters (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should properly serialize null fields (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should return undefined for non-serializable objects (evaluation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Page.evaluate should fail for circular object (evaluation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Page.evaluate should be able to throw a tricky error (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should accept a string (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should accept a string with semi colons (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should accept a string with comments (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should accept element handle as an argument (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should throw if underlying element was disposed (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should throw if elementHandles are from other frames (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should simulate a user gesture (evaluation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Page.evaluate should throw a nice error after a navigation (evaluation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Page.evaluate should not throw an error when evaluation does a navigation (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should transfer 100Mb of data from page to node.js (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluate should throw error with detailed information on exception inside promise (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Page.evaluateOnNewDocument should evaluate before anything else on the page (evaluation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Page.evaluateOnNewDocument should work with CSP (evaluation.spec.js)": [
- "FAIL"
- ],
- "Evaluation specs Frame.evaluate should have different execution contexts (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Frame.evaluate should have correct execution contexts (evaluation.spec.js)": [
- "PASS"
- ],
- "Evaluation specs Frame.evaluate should execute after cross-site navigation (evaluation.spec.js)": [
- "PASS"
- ],
- "EventEmitter on on: adds an event listener that is fired when the event is emitted (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter on on sends the event data to the handler (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter on on: supports chaining (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter on addListener: adds an event listener that is fired when the event is emitted (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter on addListener sends the event data to the handler (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter on addListener: supports chaining (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter off off: removes the listener so it is no longer called (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter off off: supports chaining (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter off removeListener: removes the listener so it is no longer called (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter off removeListener: supports chaining (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter once only calls the listener once and then removes it (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter once supports chaining (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter emit calls all the listeners for an event (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter emit passes data through to the listener (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter emit returns true if the event has listeners (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter emit returns false if the event has listeners (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter listenerCount returns the number of listeners for the given event (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter removeAllListeners removes every listener from all events by default (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter removeAllListeners returns the emitter for chaining (EventEmitter.spec.js)": [
- "PASS"
- ],
- "EventEmitter removeAllListeners can filter to remove only listeners for a given event name (EventEmitter.spec.js)": [
- "PASS"
- ],
- "Fixtures dumpio option should work with pipe option (fixtures.spec.js)": [
- "SKIP"
- ],
- "Fixtures should dump browser process stderr (fixtures.spec.js)": [
- "PASS"
- ],
- "Fixtures should close the browser when the node process closes (fixtures.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame.executionContext should work (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame.evaluateHandle should work (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame.evaluate should throw for detached frames (frame.spec.js)": [
- "TIMEOUT"
- ],
- "Frame specs Frame.evaluate allows readonly array to be an argument (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame.page should retrieve the page from a frame (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should handle nested frames (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should send events when frames are manipulated dynamically (frame.spec.js)": [
- "FAIL"
- ],
- "Frame specs Frame Management should send \"framenavigated\" when navigating on anchor URLs (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should persist mainFrame on cross-process navigation (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should not send attach/detach events for main frame (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should detach child frames on navigation (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should support framesets (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should report frame from-inside shadow DOM (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should report frame.name() (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should report frame.parent() (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should report different frame instance when frame re-attaches (frame.spec.js)": [
- "FAIL"
- ],
- "Frame specs Frame Management should support url fragment (frame.spec.js)": [
- "PASS"
- ],
- "Frame specs Frame Management should support lazy frames (frame.spec.js)": [
- "FAIL"
- ],
- "Frame specs Frame.client should return the client instance (frame.spec.js)": [
- "PASS"
- ],
- "Emulate idle state changing idle state emulation causes change of the IdleDetector state (idle_override.spec.js)": [
- "FAIL"
- ],
- "ignoreHTTPSErrors should work (ignorehttpserrors.spec.js)": [
- "PASS"
- ],
- "ignoreHTTPSErrors should work with request interception (ignorehttpserrors.spec.js)": [
- "FAIL"
- ],
- "ignoreHTTPSErrors should work with mixed content (ignorehttpserrors.spec.js)": [
- "PASS"
- ],
- "ignoreHTTPSErrors Response.securityDetails should work (ignorehttpserrors.spec.js)": [
- "FAIL"
- ],
- "ignoreHTTPSErrors Response.securityDetails should be |null| for non-secure requests (ignorehttpserrors.spec.js)": [
- "PASS"
- ],
- "ignoreHTTPSErrors Response.securityDetails Network redirects should report SecurityDetails (ignorehttpserrors.spec.js)": [
- "PASS", "FAIL"
- ],
- "InjectedUtil tests should work (injected.spec.js)": [
- "PASS"
- ],
- "input tests input should upload the file (input.spec.js)": [
- "SKIP"
- ],
- "input tests Page.waitForFileChooser should work when file input is attached to DOM (input.spec.js)": [
- "SKIP"
- ],
- "input tests Page.waitForFileChooser should work when file input is not attached to DOM (input.spec.js)": [
- "SKIP"
- ],
- "input tests Page.waitForFileChooser should respect timeout (input.spec.js)": [
- "SKIP"
- ],
- "input tests Page.waitForFileChooser should respect default timeout when there is no custom timeout (input.spec.js)": [
- "SKIP"
- ],
- "input tests Page.waitForFileChooser should prioritize exact timeout over default timeout (input.spec.js)": [
- "SKIP"
- ],
- "input tests Page.waitForFileChooser should work with no timeout (input.spec.js)": [
- "SKIP"
- ],
- "input tests Page.waitForFileChooser should return the same file chooser when there are many watchdogs simultaneously (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.accept should accept single file (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.accept should be able to read selected file (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.accept should be able to reset selected files with empty file list (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.accept should not accept multiple files for single-file input (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.accept should succeed even for non-existent files (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.accept should error on read of non-existent files (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.accept should fail when accepting file chooser twice (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.cancel should cancel dialog (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.cancel should fail when canceling file chooser twice (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.isMultiple should work for single file pick (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.isMultiple should work for \"multiple\" (input.spec.js)": [
- "SKIP"
- ],
- "input tests FileChooser.isMultiple should work for \"webkitdirectory\" (input.spec.js)": [
- "SKIP"
- ],
- "JSHandle Page.evaluateHandle should work (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle Page.evaluateHandle should return the RemoteObject (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle Page.evaluateHandle should accept object handle as an argument (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle Page.evaluateHandle should accept object handle to primitive types (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle Page.evaluateHandle should warn about recursive objects (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle Page.evaluateHandle should accept object handle to unserializable value (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle Page.evaluateHandle should use the same JS wrappers (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.getProperty should work (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.jsonValue should work (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.jsonValue works with jsonValues that are not objects (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.jsonValue works with jsonValues that are primitives (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.jsonValue should not work with dates (jshandle.spec.js)": [
- "FAIL"
- ],
- "JSHandle JSHandle.jsonValue should throw for circular objects (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.getProperties should work (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.getProperties should return even non-own properties (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.asElement should work (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.asElement should return null for non-elements (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.asElement should return ElementHandle for TextNodes (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.toString should work for primitives (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.toString should work for complicated objects (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.toString should work with different subtypes (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.clickablePoint should work (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.clickablePoint should work for iframes (jshandle.spec.js)": [
- "PASS"
- ],
- "JSHandle JSHandle.click should work (jshandle.spec.js)": [
- "FAIL"
- ],
- "Keyboard should type into a textarea (keyboard.spec.js)": [
- "PASS"
- ],
- "Keyboard should press the metaKey (keyboard.spec.js)": [
- "FAIL"
- ],
- "Keyboard should move with the arrow keys (keyboard.spec.js)": [
- "PASS"
- ],
- "Keyboard should send a character with ElementHandle.press (keyboard.spec.js)": [
- "PASS"
- ],
- "Keyboard ElementHandle.press should support |text| option (keyboard.spec.js)": [
- "FAIL"
- ],
- "Keyboard should send a character with sendCharacter (keyboard.spec.js)": [
- "FAIL"
- ],
- "Keyboard should report shiftKey (keyboard.spec.js)": [
- "PASS", "FAIL"
- ],
- "Keyboard should report multiple modifiers (keyboard.spec.js)": [
- "PASS"
- ],
- "Keyboard should send proper codes while typing (keyboard.spec.js)": [
- "PASS"
- ],
- "Keyboard should send proper codes while typing with shift (keyboard.spec.js)": [
- "PASS"
- ],
- "Keyboard should not type canceled events (keyboard.spec.js)": [
- "PASS"
- ],
- "Keyboard should specify repeat property (keyboard.spec.js)": [
- "FAIL"
- ],
- "Keyboard should type all kinds of characters (keyboard.spec.js)": [
- "FAIL"
- ],
- "Keyboard should specify location (keyboard.spec.js)": [
- "FAIL"
- ],
- "Keyboard should throw on unknown keys (keyboard.spec.js)": [
- "PASS"
- ],
- "Keyboard should type emoji (keyboard.spec.js)": [
- "FAIL"
- ],
- "Keyboard should type emoji into an iframe (keyboard.spec.js)": [
- "FAIL"
- ],
- "Keyboard should press the meta key (keyboard.spec.js)": [
- "PASS", "FAIL"
- ],
- "Launcher specs Puppeteer BrowserFetcher should download and extract chrome linux binary (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer BrowserFetcher should download and extract firefox linux binary (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Browser.disconnect should reject navigation when browser closes (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Browser.disconnect should reject waitForSelector when browser closes (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Browser.close should terminate network waiters (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should reject all promises when browser is closed (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should reject if executable path is invalid (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch userDataDir option (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch tmp profile should be cleaned up (launcher.spec.js)": [
- "SKIP"
- ],
- "Launcher specs Puppeteer Puppeteer.launch userDataDir option restores preferences (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch userDataDir argument (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch userDataDir argument with non-existent dir (launcher.spec.js)": [
- "SKIP"
- ],
- "Launcher specs Puppeteer Puppeteer.launch userDataDir option should restore state (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch userDataDir option should restore cookies (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should return the default arguments (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should report the correct product (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should work with no default arguments (launcher.spec.js)": [
- "FAIL", "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should filter out ignored default arguments in Chrome (launcher.spec.js)": [
- "SKIP"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should filter out ignored default argument in Firefox (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should have default URL when launching browser (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should have custom URL when launching browser (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should pass the timeout parameter to browser.waitForTarget (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should work with timeout = 0 (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should set the default viewport (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should disable the default viewport (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should take fullPage screenshots when defaultViewport is null (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should set the debugging port (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should not allow setting debuggingPort and pipe (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should launch Chrome properly with --no-startup-window and waitForInitialPage=false (launcher.spec.js)": [
- "SKIP"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should be able to launch Chrome (launcher.spec.js)": [
- "SKIP"
- ],
- "Launcher specs Puppeteer Puppeteer.launch should be able to launch Firefox (launcher.spec.js)": [
- "FAIL"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should be able to connect multiple times to the same browser (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should be able to close remote browser (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should be able to connect to a browser with no page targets (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should support ignoreHTTPSErrors option (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should support targetFilter option in puppeteer.launch (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should support targetFilter option (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should be able to reconnect to a disconnected browser (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should be able to connect to the same page simultaneously (launcher.spec.js)": [
- "PASS", "TIMEOUT"
- ],
- "Launcher specs Puppeteer Puppeteer.connect should be able to reconnect (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.executablePath should work (launcher.spec.js)": [
- "SKIP"
- ],
- "Launcher specs Puppeteer Puppeteer.executablePath returns executablePath for channel (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.executablePath when PUPPETEER_EXECUTABLE_PATH is set its value is returned (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.executablePath when the product is chrome, platform is not darwin, and arch is arm64 and the executable exists returns /usr/bin/chromium-browser (launcher.spec.js)": [
- "SKIP"
- ],
- "Launcher specs Puppeteer Puppeteer.executablePath when the product is chrome, platform is not darwin, and arch is arm64 and the executable exists and PUPPETEER_EXECUTABLE_PATH is set its value is returned (launcher.spec.js)": [
- "PASS"
- ],
- "Launcher specs Puppeteer Puppeteer.executablePath when the product is chrome, platform is not darwin, and arch is arm64 and the executable does not exist does not return /usr/bin/chromium-browser (launcher.spec.js)": [
- "SKIP"
- ],
- "Launcher specs Browser target events should work (launcher.spec.js)": [
- "FAIL"
- ],
- "Launcher specs Browser.Events.disconnected should be emitted when: browser gets closed, disconnected or underlying websocket gets closed (launcher.spec.js)": [
- "PASS"
- ],
- "Mouse should click the document (mouse.spec.js)": [
- "PASS"
- ],
- "Mouse should resize the textarea (mouse.spec.js)": [
- "PASS"
- ],
- "Mouse should select the text with mouse (mouse.spec.js)": [
- "PASS"
- ],
- "Mouse should trigger hover state (mouse.spec.js)": [
- "PASS", "FAIL"
- ],
- "Mouse should trigger hover state with removed window.Node (mouse.spec.js)": [
- "FAIL"
- ],
- "Mouse should set modifier keys on click (mouse.spec.js)": [
- "PASS"
- ],
- "Mouse should send mouse wheel events (mouse.spec.js)": [
- "FAIL"
- ],
- "Mouse should tween mouse movement (mouse.spec.js)": [
- "PASS", "FAIL"
- ],
- "Mouse should work with mobile viewports and cross process navigations (mouse.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should work (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should work with anchor navigation (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should work with redirects (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should navigate to about:blank (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should return response when page changes its URL after load (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should work with subframes return 204 (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should fail when server returns 204 (navigation.spec.js)": [
- "TIMEOUT"
- ],
- "navigation Page.goto should navigate to empty page with domcontentloaded (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should work when page calls history API in beforeunload (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should navigate to empty page with networkidle0 (navigation.spec.js)": [
- "TIMEOUT"
- ],
- "navigation Page.goto should navigate to empty page with networkidle2 (navigation.spec.js)": [
- "TIMEOUT"
- ],
- "navigation Page.goto should fail when navigating to bad url (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Page.goto should fail when navigating to bad SSL (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Page.goto should fail when navigating to bad SSL after redirects (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should fail when main resources failed to load (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should fail when exceeding maximum navigation timeout (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should fail when exceeding default maximum navigation timeout (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should fail when exceeding default maximum timeout (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should prioritize default navigation timeout over default timeout (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should disable timeout when its set to 0 (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should work when navigating to valid url (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should work when navigating to data url (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Page.goto should work when navigating to 404 (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should return last response in redirect chain (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should wait for network idle to succeed navigation (navigation.spec.js)": [
- "TIMEOUT"
- ],
- "navigation Page.goto should not leak listeners during navigation (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should not leak listeners during bad navigation (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should not leak listeners during navigation of 11 pages (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should navigate to dataURL and fire dataURL requests (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Page.goto should navigate to URL with hash and fire requests without hash (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should work with self requesting page (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should fail when navigating and show the url at the error message (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goto should send referer (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Page.waitForNavigation should work (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.waitForNavigation should work with both domcontentloaded and load (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.waitForNavigation should work with clicking on anchor links (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.waitForNavigation should work with history.pushState() (navigation.spec.js)": [
- "FAIL", "TIMEOUT"
- ],
- "navigation Page.waitForNavigation should work with history.replaceState() (navigation.spec.js)": [
- "FAIL", "TIMEOUT"
- ],
- "navigation Page.waitForNavigation should work with DOM history.back()/history.forward() (navigation.spec.js)": [
- "FAIL", "TIMEOUT"
- ],
- "navigation Page.waitForNavigation should work when subframe issues window.stop() (navigation.spec.js)": [
- "TIMEOUT"
- ],
- "navigation Page.goBack should work (navigation.spec.js)": [
- "PASS"
- ],
- "navigation Page.goBack should work with HistoryAPI (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Frame.goto should navigate subframes (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Frame.goto should reject when frame detaches (navigation.spec.js)": [
- "TIMEOUT"
- ],
- "navigation Frame.goto should return matching responses (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Frame.waitForNavigation should work (navigation.spec.js)": [
- "FAIL"
- ],
- "navigation Frame.waitForNavigation should fail when frame detaches (navigation.spec.js)": [
- "TIMEOUT"
- ],
- "navigation Page.reload should work (navigation.spec.js)": [
- "PASS"
- ],
- "network Page.Events.Request should fire for navigation requests (network.spec.js)": [
- "PASS"
- ],
- "network Page.Events.Request should fire for iframes (network.spec.js)": [
- "PASS"
- ],
- "network Page.Events.Request should fire for fetches (network.spec.js)": [
- "PASS"
- ],
- "network Request.frame should work for main frame navigation request (network.spec.js)": [
- "PASS"
- ],
- "network Request.frame should work for subframe navigation request (network.spec.js)": [
- "PASS"
- ],
- "network Request.frame should work for fetch requests (network.spec.js)": [
- "PASS"
- ],
- "network Request.headers should define Chrome as user agent header (network.spec.js)": [
- "SKIP"
- ],
- "network Request.headers should define Firefox as user agent header (network.spec.js)": [
- "PASS"
- ],
- "network Response.headers should work (network.spec.js)": [
- "PASS"
- ],
- "network Request.initiator should return the initiator (network.spec.js)": [
- "FAIL"
- ],
- "network Response.fromCache should return |false| for non-cached content (network.spec.js)": [
- "PASS"
- ],
- "network Response.fromCache should work (network.spec.js)": [
- "FAIL"
- ],
- "network Response.fromServiceWorker should return |false| for non-service-worker content (network.spec.js)": [
- "PASS"
- ],
- "network Response.fromServiceWorker Response.fromServiceWorker (network.spec.js)": [
- "TIMEOUT"
- ],
- "network Request.postData should work (network.spec.js)": [
- "FAIL"
- ],
- "network Request.postData should be |undefined| when there is no post data (network.spec.js)": [
- "PASS"
- ],
- "network Response.text should work (network.spec.js)": [
- "TIMEOUT"
- ],
- "network Response.text should return uncompressed text (network.spec.js)": [
- "TIMEOUT"
- ],
- "network Response.text should throw when requesting body of redirected response (network.spec.js)": [
- "FAIL"
- ],
- "network Response.text should wait until response completes (network.spec.js)": [
- "TIMEOUT"
- ],
- "network Response.json should work (network.spec.js)": [
- "TIMEOUT"
- ],
- "network Response.buffer should work (network.spec.js)": [
- "FAIL", "TIMEOUT"
- ],
- "network Response.buffer should work with compression (network.spec.js)": [
- "FAIL", "TIMEOUT"
- ],
- "network Response.buffer should throw if the response does not have a body (network.spec.js)": [
- "TIMEOUT"
- ],
- "network Response.statusText should work (network.spec.js)": [
- "PASS"
- ],
- "network Response.statusText handles missing status text (network.spec.js)": [
- "PASS"
- ],
- "network Response.timing returns timing information (network.spec.js)": [
- "FAIL"
- ],
- "network Network Events Page.Events.Request (network.spec.js)": [
- "FAIL", "PASS"
- ],
- "network Network Events Page.Events.RequestServedFromCache (network.spec.js)": [
- "FAIL"
- ],
- "network Network Events Page.Events.Response (network.spec.js)": [
- "PASS", "FAIL"
- ],
- "network Network Events Page.Events.RequestFailed (network.spec.js)": [
- "FAIL"
- ],
- "network Network Events Page.Events.RequestFinished (network.spec.js)": [
- "FAIL"
- ],
- "network Network Events should fire events in proper order (network.spec.js)": [
- "FAIL"
- ],
- "network Network Events should support redirects (network.spec.js)": [
- "FAIL"
- ],
- "network Request.isNavigationRequest should work (network.spec.js)": [
- "FAIL"
- ],
- "network Request.isNavigationRequest should work with request interception (network.spec.js)": [
- "FAIL"
- ],
- "network Request.isNavigationRequest should work when navigating to image (network.spec.js)": [
- "SKIP"
- ],
- "network Page.setExtraHTTPHeaders should work (network.spec.js)": [
- "FAIL"
- ],
- "network Page.setExtraHTTPHeaders should throw for non-string header values (network.spec.js)": [
- "PASS"
- ],
- "network Page.authenticate should work (network.spec.js)": [
- "TIMEOUT"
- ],
- "network Page.authenticate should fail if wrong credentials (network.spec.js)": [
- "FAIL"
- ],
- "network Page.authenticate should allow disable authentication (network.spec.js)": [
- "FAIL"
- ],
- "network Page.authenticate should not disable caching (network.spec.js)": [
- "FAIL"
- ],
- "network raw network headers Same-origin set-cookie navigation (network.spec.js)": [
- "PASS"
- ],
- "network raw network headers Same-origin set-cookie subresource (network.spec.js)": [
- "PASS",
- "FAIL"
- ],
- "network raw network headers Cross-origin set-cookie (network.spec.js)": [
- "PASS",
- "FAIL"
- ],
- "Page Page.close should reject all promises when page is closed (page.spec.js)": [
- "PASS"
- ],
- "Page Page.close should not be visible in browser.pages (page.spec.js)": [
- "PASS"
- ],
- "Page Page.close should run beforeunload if asked for (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.close should *not* run beforeunload by default (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.close should set the page close state (page.spec.js)": [
- "PASS"
- ],
- "Page Page.close should terminate network waiters (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.Events.Load should fire when expected (page.spec.js)": [
- "PASS"
- ],
- "Page removing and adding event handlers should correctly fire event handlers as they are added and then removed (page.spec.js)": [
- "PASS"
- ],
- "Page removing and adding event handlers should correctly added and removed request events (page.spec.js)": [
- "PASS"
- ],
- "Page Page.Events.error should throw when page crashes (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.Events.Popup should work (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.Popup should work with noopener (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.Events.Popup should work with clicking target=_blank and without rel=opener (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.Events.Popup should work with clicking target=_blank and with rel=opener (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.Events.Popup should work with fake-clicking target=_blank and rel=noopener (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.Events.Popup should work with clicking target=_blank and rel=noopener (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page BrowserContext.overridePermissions should be prompt by default (page.spec.js)": [
- "PASS"
- ],
- "Page BrowserContext.overridePermissions should deny permission when not listed (page.spec.js)": [
- "FAIL"
- ],
- "Page BrowserContext.overridePermissions should fail when bad permission is given (page.spec.js)": [
- "PASS"
- ],
- "Page BrowserContext.overridePermissions should grant permission when listed (page.spec.js)": [
- "FAIL"
- ],
- "Page BrowserContext.overridePermissions should reset permissions (page.spec.js)": [
- "FAIL"
- ],
- "Page BrowserContext.overridePermissions should trigger permission onchange (page.spec.js)": [
- "FAIL"
- ],
- "Page BrowserContext.overridePermissions should isolate permissions between browser contexts (page.spec.js)": [
- "FAIL"
- ],
- "Page BrowserContext.overridePermissions should grant persistent-storage (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setGeolocation should work (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setGeolocation should throw when invalid longitude (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setOfflineMode should work (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setOfflineMode should emulate navigator.onLine (page.spec.js)": [
- "FAIL"
- ],
- "Page ExecutionContext.queryObjects should work (page.spec.js)": [
- "FAIL"
- ],
- "Page ExecutionContext.queryObjects should work for non-blank page (page.spec.js)": [
- "FAIL"
- ],
- "Page ExecutionContext.queryObjects should fail for disposed handles (page.spec.js)": [
- "PASS"
- ],
- "Page ExecutionContext.queryObjects should fail primitive values as prototypes (page.spec.js)": [
- "PASS"
- ],
- "Page Page.Events.Console should work (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.Console should work for different console API calls (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.Console should not fail for window object (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.Console should trigger correct Log (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.Console should have location when fetch fails (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.Console should have location and stack trace for console API calls (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.Console should not throw when there are console messages in detached iframes (page.spec.js)": [
- "PASS"
- ],
- "Page Page.Events.DOMContentLoaded should fire when expected (page.spec.js)": [
- "PASS"
- ],
- "Page Page.metrics should get metrics from a page (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.metrics metrics event fired on console.timeStamp (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.waitForRequest should work (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForRequest should work with predicate (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForRequest should respect timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForRequest should respect default timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForRequest should work with no timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForResponse should work (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForResponse should respect timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForResponse should respect default timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForResponse should work with predicate (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForResponse should work with async predicate (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForResponse should work with no timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForNetworkIdle should work (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForNetworkIdle should respect timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForNetworkIdle should respect idleTime (page.spec.js)": [
- "PASS"
- ],
- "Page Page.waitForNetworkIdle should work with no timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.exposeFunction should work (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should throw exception in page context (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should support throwing \"null\" (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should be callable from-inside evaluateOnNewDocument (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should survive navigation (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should await returned promise (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should work on frames (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should work on frames before navigation (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should not throw when frames detach (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should work with complex objects (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.exposeFunction should fallback to default export when passed a module object (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.PageError should fire (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setUserAgent should work (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setUserAgent should work for subframes (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setUserAgent should emulate device user-agent (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setUserAgent should work with additional userAgentMetdata (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setContent should work (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should work with doctype (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should work with HTML 4 doctype (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should respect timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should respect default navigation timeout (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should await resources to load (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should work fast enough (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should work with tricky content (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should work with accents (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should work with emojis (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setContent should work with newline (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setBypassCSP should bypass CSP meta tag (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setBypassCSP should bypass CSP header (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setBypassCSP should bypass after cross-process navigation (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setBypassCSP should bypass CSP in iframes as well (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.addScriptTag should throw an error if no options are provided (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should work with a url (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should work with a url and type=module (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should work with a path and type=module (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should work with a content and type=module (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should throw an error if loading from url fail (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should work with a path (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should include sourcemap when path is provided (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should work with content (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should add id when provided (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addScriptTag should throw when added with content to the CSP page (page.spec.js)": [
- "SKIP"
- ],
- "Page Page.addScriptTag should throw when added with URL to the CSP page (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addStyleTag should throw an error if no options are provided (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addStyleTag should work with a url (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addStyleTag should throw an error if loading from url fail (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addStyleTag should work with a path (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addStyleTag should include sourcemap when path is provided (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addStyleTag should work with content (page.spec.js)": [
- "PASS"
- ],
- "Page Page.addStyleTag should throw when added with content to the CSP page (page.spec.js)": [
- "TIMEOUT"
- ],
- "Page Page.addStyleTag should throw when added with URL to the CSP page (page.spec.js)": [
- "PASS"
- ],
- "Page Page.url should work (page.spec.js)": [
- "PASS"
- ],
- "Page Page.setJavaScriptEnabled should work (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setCacheEnabled should enable or disable the cache based on the state passed (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.setCacheEnabled should stay disabled when toggling request interception on/off (page.spec.js)": [
- "FAIL"
- ],
- "Page printing to PDF can print to PDF and save to file (page.spec.js)": [
- "PASS"
- ],
- "Page printing to PDF can print to PDF and stream the result (page.spec.js)": [
- "PASS"
- ],
- "Page printing to PDF should respect timeout (page.spec.js)": [
- "SKIP"
- ],
- "Page Page.title should return the page title (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should select single option (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should select only first option (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should not throw when select causes navigation (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should select multiple options (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should respect event bubbling (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should throw when element is not a (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should return [] on no matched values (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should return an array of matched values (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should return an array of one element when multiple is not set (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should return [] on no values (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should deselect all options when passed no values for a multiple select (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should deselect all options when passed no values for a select without multiple (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should throw if passed in non-strings (page.spec.js)": [
- "PASS"
- ],
- "Page Page.select should work when re-defining top-level Event class (page.spec.js)": [
- "FAIL"
- ],
- "Page Page.Events.Close should work with window.close (page.spec.js)": [
- "SKIP"
- ],
- "Page Page.Events.Close should work with page.close (page.spec.js)": [
- "PASS"
- ],
- "Page Page.browser should return the correct browser instance (page.spec.js)": [
- "PASS"
- ],
- "Page Page.browserContext should return the correct browser context instance (page.spec.js)": [
- "PASS"
- ],
- "Page Page.client should return the client instance (page.spec.js)": [
- "PASS"
- ],
- "request proxy should proxy requests when configured (proxy.spec.js)": [
- "SKIP"
- ],
- "request proxy should respect proxy bypass list (proxy.spec.js)": [
- "SKIP"
- ],
- "request proxy in incognito browser context should proxy requests when configured at browser level (proxy.spec.js)": [
- "SKIP"
- ],
- "request proxy in incognito browser context should respect proxy bypass list when configured at browser level (proxy.spec.js)": [
- "SKIP"
- ],
- "request proxy in incognito browser context should proxy requests when configured at context level (proxy.spec.js)": [
- "SKIP"
- ],
- "request proxy in incognito browser context should respect proxy bypass list when configured at context level (proxy.spec.js)": [
- "SKIP"
- ],
- "Query handler tests Pierce selectors should find first element in shadow (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests Pierce selectors should find all elements in shadow (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests Pierce selectors should find first child element (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests Pierce selectors should find all child elements (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests XPath selectors in Page should query existing element (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests XPath selectors in Page should return empty array for non-existing element (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests XPath selectors in Page should return first element (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests XPath selectors in Page should return multiple elements (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests XPath selectors in ElementHandles should query existing element (queryhandler.spec.js)": [
- "PASS"
- ],
- "Query handler tests XPath selectors in ElementHandles should return null for non-existing element (queryhandler.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$eval should work (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$eval should accept arguments (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$eval should accept ElementHandles as arguments (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$eval should throw error if no element is found (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$$eval should work (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$$eval should accept extra arguments (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$$eval should accept ElementHandles as arguments (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$$eval should handle many elements (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$ should query existing element (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$ should return null for non-existing element (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$$ should query existing elements (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$$ should return empty array if nothing is found (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$x should query existing element (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$x should return empty array for non-existing element (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector Page.$x should return multiple elements (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$ should query existing element (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$ should return null for non-existing element (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$eval should work (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$eval should retrieve content from subtree (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$eval should throw in case of missing selector (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$$eval should work (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$$eval should retrieve content from subtree (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$$eval should not throw in case of missing selector (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$$ should query existing elements (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$$ should return empty array for non-existing elements (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$x should query existing element (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector ElementHandle.$x should return null for non-existing element (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector QueryAll should have registered handler (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector QueryAll $$ should query existing elements (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector QueryAll $$ should return empty array for non-existing elements (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector QueryAll $$eval should work (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector QueryAll $$eval should accept extra arguments (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector QueryAll $$eval should accept ElementHandles as arguments (queryselector.spec.js)": [
- "PASS"
- ],
- "querySelector QueryAll $$eval should handle many elements (queryselector.spec.js)": [
- "PASS"
- ],
- "request interception Page.setRequestInterception should cooperatively respond by priority (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should cooperatively continue by priority (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should cooperatively abort by priority (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should intercept (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work when POST is redirected with 302 (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work when header manipulation headers with redirect (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be able to remove headers (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should contain referer header (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should properly return navigation response when URL has cookies (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should stop intercepting (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should show custom HTTP headers (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with redirect inside sync XHR (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with custom referer headers (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be abortable (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be able to access the error reason (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be abortable with custom error codes (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should send referer (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should fail navigation when aborting main resource (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with redirects (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with redirects for subresources (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be able to abort redirects (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with equal requests (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should navigate to dataURL and fire dataURL requests (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be able to fetch dataURL and fire dataURL requests (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should navigate to URL with hash and fire requests without hash (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with encoded server (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with badly encoded server (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with encoded server - 2 (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should not throw \"Invalid Interception Id\" if the request was cancelled (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should throw if interception is not enabled (requestinterception-experimental.spec.js)": [
- "PASS"
- ],
- "request interception Page.setRequestInterception should work with file URLs (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should not cache if cache disabled (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should cache if cache enabled (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should load fonts if cache enabled (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should work (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should amend HTTP headers (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should redirect in a way non-observable to page (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should amend method (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should amend post data (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should amend both post data and method on navigation (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should work (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should be able to access the response (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should work with status code 422 (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should redirect (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should allow mocking binary responses (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should stringify intercepted request response headers (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should indicate already-handled if an intercept has been handled (requestinterception-experimental.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should intercept (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work when POST is redirected with 302 (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work when header manipulation headers with redirect (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be able to remove headers (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should contain referer header (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should properly return navigation response when URL has cookies (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should stop intercepting (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should show custom HTTP headers (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with redirect inside sync XHR (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with custom referer headers (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be abortable (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be abortable with custom error codes (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should send referer (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should fail navigation when aborting main resource (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with redirects (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with redirects for subresources (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be able to abort redirects (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with equal requests (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should navigate to dataURL and fire dataURL requests (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should be able to fetch dataURL and fire dataURL requests (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should navigate to URL with hash and fire requests without hash (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with encoded server (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with badly encoded server (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should work with encoded server - 2 (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should not throw \"Invalid Interception Id\" if the request was cancelled (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should throw if interception is not enabled (requestinterception.spec.js)": [
- "PASS"
- ],
- "request interception Page.setRequestInterception should work with file URLs (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should not cache if cache disabled (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should cache if cache enabled (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Page.setRequestInterception should load fonts if cache enabled (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should work (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should amend HTTP headers (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should redirect in a way non-observable to page (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should amend method (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should amend post data (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should amend both post data and method on navigation (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.continue should fail if the header value is invalid (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should work (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should work with status code 422 (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should redirect (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should allow mocking multiple headers with same key (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should allow mocking binary responses (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should stringify intercepted request response headers (requestinterception.spec.js)": [
- "FAIL"
- ],
- "request interception Request.respond should fail if the header value is invalid (requestinterception.spec.js)": [
- "FAIL"
- ],
- "Screenshots Page.screenshot should work (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots Page.screenshot should clip rect (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots Page.screenshot should use scale for clip (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots Page.screenshot should get screenshot bigger than the viewport (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots Page.screenshot should run in parallel (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots Page.screenshot should take fullPage screenshots (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots Page.screenshot should run in parallel in multiple pages (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots Page.screenshot should allow transparency (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots Page.screenshot should render white background on jpeg file (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots Page.screenshot should work with webp (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots Page.screenshot should work with odd clip size on Retina displays (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots Page.screenshot should return base64 (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots Page.screenshot should work in \"fromSurface: false\" mode (screenshot.spec.js)": [
- "SKIP"
- ],
- "Screenshots ElementHandle.screenshot should work (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots ElementHandle.screenshot should work with a null viewport (screenshot.spec.js)": [
- "SKIP"
- ],
- "Screenshots ElementHandle.screenshot should take into account padding and border (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots ElementHandle.screenshot should capture full element when larger than viewport (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots ElementHandle.screenshot should scroll element into view (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots ElementHandle.screenshot should work with a rotated element (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots ElementHandle.screenshot should fail to screenshot a detached element (screenshot.spec.js)": [
- "FAIL"
- ],
- "Screenshots ElementHandle.screenshot should not hang with zero width/height element (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots ElementHandle.screenshot should work for an element with fractional dimensions (screenshot.spec.js)": [
- "PASS"
- ],
- "Screenshots ElementHandle.screenshot should work for an element with an offset (screenshot.spec.js)": [
- "FAIL"
- ],
- "Target Browser.targets should return all of the targets (target.spec.js)": [
- "PASS"
- ],
- "Target Browser.pages should return all of the pages (target.spec.js)": [
- "PASS"
- ],
- "Target should contain browser target (target.spec.js)": [
- "PASS"
- ],
- "Target should be able to use the default page in the browser (target.spec.js)": [
- "PASS"
- ],
- "Target should be able to use async waitForTarget (target.spec.js)": [
- "SKIP"
- ],
- "Target should report when a new page is created and closed (target.spec.js)": [
- "FAIL"
- ],
- "Target should report when a service worker is created and destroyed (target.spec.js)": [
- "TIMEOUT"
- ],
- "Target should create a worker from a service worker (target.spec.js)": [
- "TIMEOUT"
- ],
- "Target should create a worker from a shared worker (target.spec.js)": [
- "TIMEOUT"
- ],
- "Target should report when a target url changes (target.spec.js)": [
- "TIMEOUT"
- ],
- "Target should not report uninitialized pages (target.spec.js)": [
- "FAIL"
- ],
- "Target should not crash while redirecting if original request was missed (target.spec.js)": [
- "FAIL"
- ],
- "Target should have an opener (target.spec.js)": [
- "TIMEOUT", "FAIL"
- ],
- "Target Browser.waitForTarget should wait for a target (target.spec.js)": [
- "TIMEOUT"
- ],
- "Target Browser.waitForTarget should timeout waiting for a non-existent target (target.spec.js)": [
- "PASS"
- ],
- "Touchscreen should tap the button (touchscreen.spec.js)": [
- "FAIL"
- ],
- "Touchscreen should report touches (touchscreen.spec.js)": [
- "FAIL"
- ],
- "waittask specs Frame.waitForFunction should accept a string (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should work when resolved right before execution context disposal (waittask.spec.js)": [
- "PASS", "TIMEOUT"
- ],
- "waittask specs Frame.waitForFunction should poll on interval (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should poll on interval async (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should poll on mutation (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should poll on mutation async (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should poll on raf (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should poll on raf async (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should work with strict CSP policy (waittask.spec.js)": [
- "FAIL"
- ],
- "waittask specs Frame.waitForFunction should throw on bad polling value (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should throw negative polling interval (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should return the success value as a JSHandle (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should return the window as a success value (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should accept ElementHandle arguments (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should respect timeout (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should respect default timeout (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should disable timeout when its set to 0 (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should survive cross-process navigation (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForFunction should survive navigations (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Page.waitForTimeout waits for the given timeout before resolving (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForTimeout waits for the given timeout before resolving (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should immediately resolve promise if node exists (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should work with removed MutationObserver (waittask.spec.js)": [
- "FAIL"
- ],
- "waittask specs Frame.waitForSelector should resolve promise when node is added (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should work when node is added through innerHTML (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector Page.waitForSelector is shortcut for main frame (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should run in specified frame (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should throw when frame is detached (waittask.spec.js)": [
- "TIMEOUT"
- ],
- "waittask specs Frame.waitForSelector should survive cross-process navigation (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should wait for visible (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should wait for visible recursively (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector hidden should wait for visibility: hidden (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector hidden should wait for display: none (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector hidden should wait for removal (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should return null if waiting to hide non-existing element (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should respect timeout (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should have an error message specifically for awaiting an element to be hidden (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should respond to node attribute mutation (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should return the element handle (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForSelector should have correct stack trace for timeout (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForXPath should support some fancy xpath (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForXPath should respect timeout (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForXPath should run in specified frame (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForXPath should throw when frame is detached (waittask.spec.js)": [
- "TIMEOUT"
- ],
- "waittask specs Frame.waitForXPath hidden should wait for display: none (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForXPath should return the element handle (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForXPath should allow you to select a text node (waittask.spec.js)": [
- "PASS"
- ],
- "waittask specs Frame.waitForXPath should allow you to select an element with single slash (waittask.spec.js)": [
- "PASS"
- ],
- "Workers Page.workers (worker.spec.js)": [
- "SKIP"
- ],
- "Workers should emit created and destroyed events (worker.spec.js)": [
- "SKIP"
- ],
- "Workers should report console logs (worker.spec.js)": [
- "SKIP"
- ],
- "Workers should have JSHandles for console logs (worker.spec.js)": [
- "SKIP"
- ],
- "Workers should have an execution context (worker.spec.js)": [
- "SKIP"
- ],
- "Workers should report errors (worker.spec.js)": [
- "SKIP"
- ]
-}
diff --git a/remote/test/puppeteer/.mocharc.cjs b/remote/test/puppeteer/.mocharc.cjs
index e7168715d898..d1cd74f4eff5 100644
--- a/remote/test/puppeteer/.mocharc.cjs
+++ b/remote/test/puppeteer/.mocharc.cjs
@@ -18,10 +18,10 @@ module.exports = {
reporter: 'dot',
logLevel: 'debug',
require: ['./test/build/mocha-utils.js', 'source-map-support/register'],
- spec: 'test/build/*.spec.js',
+ spec: 'test/build/**/*.spec.js',
exit: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
parallel: !!process.env.PARALLEL,
- timeout: 25 * 1000,
+ timeout: 25_000,
reporter: process.env.CI ? 'spec' : 'dot',
};
diff --git a/remote/test/puppeteer/.release-please-manifest.json b/remote/test/puppeteer/.release-please-manifest.json
index eaffe70bedcf..b3ed9ce44ebb 100644
--- a/remote/test/puppeteer/.release-please-manifest.json
+++ b/remote/test/puppeteer/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "17.1.2"
+ ".": "18.0.0"
}
diff --git a/remote/test/puppeteer/CHANGELOG.md b/remote/test/puppeteer/CHANGELOG.md
index 47fc4e0ecf58..9f3bb88bd199 100644
--- a/remote/test/puppeteer/CHANGELOG.md
+++ b/remote/test/puppeteer/CHANGELOG.md
@@ -2,6 +2,32 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [18.0.0](https://github.com/puppeteer/puppeteer/compare/v17.1.3...v18.0.0) (2022-09-19)
+
+
+### âš BREAKING CHANGES
+
+* fix bounding box visibility conditions (#8954)
+
+### Features
+
+* add text query handler ([#8956](https://github.com/puppeteer/puppeteer/issues/8956)) ([633e7cf](https://github.com/puppeteer/puppeteer/commit/633e7cfdf99d42f420d0af381394bd1f6ac7bcd1))
+
+
+### Bug Fixes
+
+* fix bounding box visibility conditions ([#8954](https://github.com/puppeteer/puppeteer/issues/8954)) ([ac9929d](https://github.com/puppeteer/puppeteer/commit/ac9929d80f6f7d4905a39183ae235500e29b4f53))
+* suppress init errors if the target is closed ([#8947](https://github.com/puppeteer/puppeteer/issues/8947)) ([cfaaa5e](https://github.com/puppeteer/puppeteer/commit/cfaaa5e2c07e5f98baeb7de99e303aa840a351e8))
+* use win64 version of chromium when on arm64 windows ([#8927](https://github.com/puppeteer/puppeteer/issues/8927)) ([64843b8](https://github.com/puppeteer/puppeteer/commit/64843b88853210314677ab1b434729513ce615a7))
+
+## [17.1.3](https://github.com/puppeteer/puppeteer/compare/v17.1.2...v17.1.3) (2022-09-08)
+
+
+### Bug Fixes
+
+* FirefoxLauncher should not use BrowserFetcher in puppeteer-core ([#8920](https://github.com/puppeteer/puppeteer/issues/8920)) ([f2e8de7](https://github.com/puppeteer/puppeteer/commit/f2e8de777fc5d547778fdc6cac658add84ed4082)), closes [#8919](https://github.com/puppeteer/puppeteer/issues/8919)
+* linux arm64 check on windows arm ([#8917](https://github.com/puppeteer/puppeteer/issues/8917)) ([f02b926](https://github.com/puppeteer/puppeteer/commit/f02b926245e28b5671087c051dbdbb3165696f08)), closes [#8915](https://github.com/puppeteer/puppeteer/issues/8915)
+
## [17.1.2](https://github.com/puppeteer/puppeteer/compare/v17.1.1...v17.1.2) (2022-09-07)
diff --git a/remote/test/puppeteer/json-mocha-reporter.js b/remote/test/puppeteer/json-mocha-reporter.js
index 47fc74a9c5a4..ffe1d6067501 100644
--- a/remote/test/puppeteer/json-mocha-reporter.js
+++ b/remote/test/puppeteer/json-mocha-reporter.js
@@ -15,10 +15,11 @@ to the project.
function JSONExtra(runner, options) {
mocha.reporters.Base.call(this, runner, options);
+ mocha.reporters.JSON.call(this, runner, options);
const self = this;
runner.once(constants.EVENT_RUN_BEGIN, function () {
- writeEvent(['start', { total: runner.total }]);
+ writeEvent(['start', {total: runner.total}]);
});
runner.on(constants.EVENT_TEST_PASS, function (test) {
diff --git a/remote/test/puppeteer/moz.yaml b/remote/test/puppeteer/moz.yaml
index b52604b80640..5b74bb74ea78 100644
--- a/remote/test/puppeteer/moz.yaml
+++ b/remote/test/puppeteer/moz.yaml
@@ -1,4 +1,3 @@
-schema: 1
bugzilla:
component: Agent
product: Remote Protocol
@@ -6,5 +5,6 @@ origin:
description: Headless Chrome Node API
license: Apache-2.0
name: puppeteer
- release: 0d2d99efeca73fba255fb10b28b5d3f50c2e20e4
- url: https://github.com/puppeteer/puppeteer
+ release: 7d6927209e5d557891bd618ddb01d54bc3566307
+ url: /Users/alexandraborovova/Projects/puppeteer
+schema: 1
diff --git a/remote/test/puppeteer/package-lock.json b/remote/test/puppeteer/package-lock.json
index 70e3a1267fae..0e5d372ff40a 100644
--- a/remote/test/puppeteer/package-lock.json
+++ b/remote/test/puppeteer/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "puppeteer",
- "version": "17.1.2",
+ "version": "18.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "puppeteer",
- "version": "17.1.2",
+ "version": "18.0.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -80,7 +80,8 @@
"text-diff": "1.0.1",
"tsd": "0.22.0",
"tsx": "3.8.2",
- "typescript": "4.7.4"
+ "typescript": "4.7.4",
+ "zod": "3.18.0"
},
"engines": {
"node": ">=14.1.0"
@@ -7809,6 +7810,15 @@
"optionalDependencies": {
"commander": "^2.20.3"
}
+ },
+ "node_modules/zod": {
+ "version": "3.18.0",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.18.0.tgz",
+ "integrity": "sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
}
},
"dependencies": {
@@ -13501,6 +13511,12 @@
"lodash.isequal": "^4.5.0",
"validator": "^13.7.0"
}
+ },
+ "zod": {
+ "version": "3.18.0",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.18.0.tgz",
+ "integrity": "sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA==",
+ "dev": true
}
}
}
diff --git a/remote/test/puppeteer/package.json b/remote/test/puppeteer/package.json
index ef1bf03e1bde..899a6dc7d5d7 100644
--- a/remote/test/puppeteer/package.json
+++ b/remote/test/puppeteer/package.json
@@ -1,6 +1,6 @@
{
"name": "puppeteer",
- "version": "17.1.2",
+ "version": "18.0.0",
"description": "A high-level API to control headless Chrome over the DevTools Protocol",
"keywords": [
"puppeteer",
@@ -27,14 +27,14 @@
"node": ">=14.1.0"
},
"scripts": {
- "test": "c8 --check-coverage --lines 93 run-s test:chrome:* test:firefox",
+ "test": "cross-env MOZ_WEBRENDER=0 PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 node utils/mochaRunner/lib/main.js",
"test:types": "tsd",
"test:install": "scripts/test-install.sh",
- "test:firefox": "cross-env PUPPETEER_PRODUCT=firefox MOZ_WEBRENDER=0 PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 mocha",
+ "test:firefox": "npm run test -- --test-suite firefox-headless",
"test:chrome": "run-s test:chrome:*",
- "test:chrome:headless": "cross-env HEADLESS=true PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 mocha",
- "test:chrome:headless-chrome": "cross-env HEADLESS=chrome PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 mocha",
- "test:chrome:headful": "cross-env HEADLESS=false PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 mocha",
+ "test:chrome:headless": "npm run test -- --test-suite chrome-headless",
+ "test:chrome:headless-chrome": "npm run test -- --test-suite chrome-new-headless",
+ "test:chrome:headful": "npm run test -- --test-suite chrome-headful",
"prepublishOnly": "npm run build",
"prepare": "node typescript-if-required.js && husky install",
"lint": "run-s lint:prettier lint:eslint",
@@ -139,6 +139,7 @@
"text-diff": "1.0.1",
"tsd": "0.22.0",
"tsx": "3.8.2",
- "typescript": "4.7.4"
+ "typescript": "4.7.4",
+ "zod": "3.18.0"
}
}
diff --git a/remote/test/puppeteer/scripts/test-install.sh b/remote/test/puppeteer/scripts/test-install.sh
index 15d1a552dee6..c7d14e4f40a2 100755
--- a/remote/test/puppeteer/scripts/test-install.sh
+++ b/remote/test/puppeteer/scripts/test-install.sh
@@ -127,3 +127,31 @@ echo '{"type":"module"}' >>$TMPDIR/package.json
npm install --loglevel silent "${tarball}"
node --input-type="module" --eval="import puppeteer from 'puppeteer-core'"
node --input-type="module" --eval="import 'puppeteer-core/lib/esm/puppeteer/revisions.js';"
+
+echo "Testing... Puppeteer Core launch with executablePath"
+TMPDIR="$(mktemp -d)"
+cd "$TMPDIR"
+echo '{"type":"module"}' >> "$TMPDIR/package.json"
+npm install --loglevel silent "${tarball}"
+# The test tries to launch the node process because
+# real browsers are not downloaded by puppeteer-core.
+# The expected error is "Failed to launch the browser process"
+# so the test verifies that it does not fail for other reasons.
+node --input-type="module" --eval="
+import puppeteer from 'puppeteer-core';
+(async () => {
+ puppeteer.launch({
+ product: 'firefox',
+ executablePath: 'node'
+ }).catch(error => error.message.includes('Failed to launch the browser process') ? process.exit(0) : process.exit(1));
+})();
+"
+node --input-type="module" --eval="
+import puppeteer from 'puppeteer-core';
+(async () => {
+ puppeteer.launch({
+ product: 'chrome',
+ executablePath: 'node'
+ }).catch(error => error.message.includes('Failed to launch the browser process') ? process.exit(0) : process.exit(1));
+})();
+"
diff --git a/remote/test/puppeteer/src/api/Browser.ts b/remote/test/puppeteer/src/api/Browser.ts
new file mode 100644
index 000000000000..24ffcdffa29e
--- /dev/null
+++ b/remote/test/puppeteer/src/api/Browser.ts
@@ -0,0 +1,628 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+/* eslint-disable @typescript-eslint/no-unused-vars */
+
+import {ChildProcess} from 'child_process';
+import {Protocol} from 'devtools-protocol';
+import {EventEmitter} from '../common/EventEmitter.js';
+import type {Page} from '../common/Page.js'; // TODO: move to ./api
+import type {Target} from '../common/Target.js'; // TODO: move to ./api
+
+/**
+ * BrowserContext options.
+ *
+ * @public
+ */
+export interface BrowserContextOptions {
+ /**
+ * Proxy server with optional port to use for all requests.
+ * Username and password can be set in `Page.authenticate`.
+ */
+ proxyServer?: string;
+ /**
+ * Bypass the proxy for the given list of hosts.
+ */
+ proxyBypassList?: string[];
+}
+
+/**
+ * @internal
+ */
+export type BrowserCloseCallback = () => Promise | void;
+
+/**
+ * @public
+ */
+export type TargetFilterCallback = (
+ target: Protocol.Target.TargetInfo
+) => boolean;
+
+/**
+ * @internal
+ */
+export type IsPageTargetCallback = (
+ target: Protocol.Target.TargetInfo
+) => boolean;
+
+/**
+ * @internal
+ */
+export const WEB_PERMISSION_TO_PROTOCOL_PERMISSION = new Map<
+ Permission,
+ Protocol.Browser.PermissionType
+>([
+ ['geolocation', 'geolocation'],
+ ['midi', 'midi'],
+ ['notifications', 'notifications'],
+ // TODO: push isn't a valid type?
+ // ['push', 'push'],
+ ['camera', 'videoCapture'],
+ ['microphone', 'audioCapture'],
+ ['background-sync', 'backgroundSync'],
+ ['ambient-light-sensor', 'sensors'],
+ ['accelerometer', 'sensors'],
+ ['gyroscope', 'sensors'],
+ ['magnetometer', 'sensors'],
+ ['accessibility-events', 'accessibilityEvents'],
+ ['clipboard-read', 'clipboardReadWrite'],
+ ['clipboard-write', 'clipboardReadWrite'],
+ ['payment-handler', 'paymentHandler'],
+ ['persistent-storage', 'durableStorage'],
+ ['idle-detection', 'idleDetection'],
+ // chrome-specific permissions we have.
+ ['midi-sysex', 'midiSysex'],
+]);
+
+/**
+ * @public
+ */
+export type Permission =
+ | 'geolocation'
+ | 'midi'
+ | 'notifications'
+ | 'camera'
+ | 'microphone'
+ | 'background-sync'
+ | 'ambient-light-sensor'
+ | 'accelerometer'
+ | 'gyroscope'
+ | 'magnetometer'
+ | 'accessibility-events'
+ | 'clipboard-read'
+ | 'clipboard-write'
+ | 'payment-handler'
+ | 'persistent-storage'
+ | 'idle-detection'
+ | 'midi-sysex';
+
+/**
+ * @public
+ */
+export interface WaitForTargetOptions {
+ /**
+ * Maximum wait time in milliseconds. Pass `0` to disable the timeout.
+ * @defaultValue 30 seconds.
+ */
+ timeout?: number;
+}
+
+/**
+ * All the events a {@link Browser | browser instance} may emit.
+ *
+ * @public
+ */
+export const enum BrowserEmittedEvents {
+ /**
+ * Emitted when Puppeteer gets disconnected from the Chromium instance. This
+ * might happen because of one of the following:
+ *
+ * - Chromium is closed or crashed
+ *
+ * - The {@link Browser.disconnect | browser.disconnect } method was called.
+ */
+ Disconnected = 'disconnected',
+
+ /**
+ * Emitted when the url of a target changes. Contains a {@link Target} instance.
+ *
+ * @remarks
+ *
+ * Note that this includes target changes in incognito browser contexts.
+ */
+ TargetChanged = 'targetchanged',
+
+ /**
+ * Emitted when a target is created, for example when a new page is opened by
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
+ * or by {@link Browser.newPage | browser.newPage}
+ *
+ * Contains a {@link Target} instance.
+ *
+ * @remarks
+ *
+ * Note that this includes target creations in incognito browser contexts.
+ */
+ TargetCreated = 'targetcreated',
+ /**
+ * Emitted when a target is destroyed, for example when a page is closed.
+ * Contains a {@link Target} instance.
+ *
+ * @remarks
+ *
+ * Note that this includes target destructions in incognito browser contexts.
+ */
+ TargetDestroyed = 'targetdestroyed',
+}
+
+/**
+ * A Browser is created when Puppeteer connects to a Chromium instance, either through
+ * {@link PuppeteerNode.launch} or {@link Puppeteer.connect}.
+ *
+ * @remarks
+ *
+ * The Browser class extends from Puppeteer's {@link EventEmitter} class and will
+ * emit various events which are documented in the {@link BrowserEmittedEvents} enum.
+ *
+ * @example
+ * An example of using a {@link Browser} to create a {@link Page}:
+ *
+ * ```ts
+ * const puppeteer = require('puppeteer');
+ *
+ * (async () => {
+ * const browser = await puppeteer.launch();
+ * const page = await browser.newPage();
+ * await page.goto('https://example.com');
+ * await browser.close();
+ * })();
+ * ```
+ *
+ * @example
+ * An example of disconnecting from and reconnecting to a {@link Browser}:
+ *
+ * ```ts
+ * const puppeteer = require('puppeteer');
+ *
+ * (async () => {
+ * const browser = await puppeteer.launch();
+ * // Store the endpoint to be able to reconnect to Chromium
+ * const browserWSEndpoint = browser.wsEndpoint();
+ * // Disconnect puppeteer from Chromium
+ * browser.disconnect();
+ *
+ * // Use the endpoint to reestablish a connection
+ * const browser2 = await puppeteer.connect({browserWSEndpoint});
+ * // Close Chromium
+ * await browser2.close();
+ * })();
+ * ```
+ *
+ * @public
+ */
+export class Browser extends EventEmitter {
+ /**
+ * @internal
+ */
+ constructor() {
+ super();
+ }
+
+ /**
+ * @internal
+ */
+ _attach(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * @internal
+ */
+ _detach(): void {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * @internal
+ */
+ get _targets(): Map {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * The spawned browser process. Returns `null` if the browser instance was created with
+ * {@link Puppeteer.connect}.
+ */
+ process(): ChildProcess | null {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * @internal
+ */
+ _getIsPageTargetCallback(): IsPageTargetCallback | undefined {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Creates a new incognito browser context. This won't share cookies/cache with other
+ * browser contexts.
+ *
+ * @example
+ *
+ * ```ts
+ * (async () => {
+ * const browser = await puppeteer.launch();
+ * // Create a new incognito browser context.
+ * const context = await browser.createIncognitoBrowserContext();
+ * // Create a new page in a pristine context.
+ * const page = await context.newPage();
+ * // Do stuff
+ * await page.goto('https://example.com');
+ * })();
+ * ```
+ */
+ createIncognitoBrowserContext(
+ options?: BrowserContextOptions
+ ): Promise;
+ createIncognitoBrowserContext(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Returns an array of all open browser contexts. In a newly created browser, this will
+ * return a single instance of {@link BrowserContext}.
+ */
+ browserContexts(): BrowserContext[] {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Returns the default browser context. The default browser context cannot be closed.
+ */
+ defaultBrowserContext(): BrowserContext {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * @internal
+ */
+ _disposeContext(contextId?: string): Promise;
+ _disposeContext(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * The browser websocket endpoint which can be used as an argument to
+ * {@link Puppeteer.connect}.
+ *
+ * @returns The Browser websocket url.
+ *
+ * @remarks
+ *
+ * The format is `ws://${host}:${port}/devtools/browser/`.
+ *
+ * You can find the `webSocketDebuggerUrl` from `http://${host}:${port}/json/version`.
+ * Learn more about the
+ * {@link https://chromedevtools.github.io/devtools-protocol | devtools protocol} and
+ * the {@link
+ * https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target
+ * | browser endpoint}.
+ */
+ wsEndpoint(): string {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Promise which resolves to a new {@link Page} object. The Page is created in
+ * a default browser context.
+ */
+ newPage(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * @internal
+ */
+ _createPageInContext(contextId?: string): Promise;
+ _createPageInContext(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * All active targets inside the Browser. In case of multiple browser contexts, returns
+ * an array with all the targets in all browser contexts.
+ */
+ targets(): Target[] {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * The target associated with the browser.
+ */
+ target(): Target {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Searches for a target in all browser contexts.
+ *
+ * @param predicate - A function to be run for every target.
+ * @returns The first target found that matches the `predicate` function.
+ *
+ * @example
+ *
+ * An example of finding a target for a page opened via `window.open`:
+ *
+ * ```ts
+ * await page.evaluate(() => window.open('https://www.example.com/'));
+ * const newWindowTarget = await browser.waitForTarget(
+ * target => target.url() === 'https://www.example.com/'
+ * );
+ * ```
+ */
+ waitForTarget(
+ predicate: (x: Target) => boolean | Promise,
+ options?: WaitForTargetOptions
+ ): Promise;
+ waitForTarget(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * An array of all open pages inside the Browser.
+ *
+ * @remarks
+ *
+ * In case of multiple browser contexts, returns an array with all the pages in all
+ * browser contexts. Non-visible pages, such as `"background_page"`, will not be listed
+ * here. You can find them using {@link Target.page}.
+ */
+ pages(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * A string representing the browser name and version.
+ *
+ * @remarks
+ *
+ * For headless Chromium, this is similar to `HeadlessChrome/61.0.3153.0`. For
+ * non-headless, this is similar to `Chrome/61.0.3153.0`.
+ *
+ * The format of browser.version() might change with future releases of Chromium.
+ */
+ version(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * The browser's original user agent. Pages can override the browser user agent with
+ * {@link Page.setUserAgent}.
+ */
+ userAgent(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Closes Chromium and all of its pages (if any were opened). The {@link Browser} object
+ * itself is considered to be disposed and cannot be used anymore.
+ */
+ close(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Disconnects Puppeteer from the browser, but leaves the Chromium process running.
+ * After calling `disconnect`, the {@link Browser} object is considered disposed and
+ * cannot be used anymore.
+ */
+ disconnect(): void {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Indicates that the browser is connected.
+ */
+ isConnected(): boolean {
+ throw new Error('Not implemented');
+ }
+}
+/**
+ * @public
+ */
+export const enum BrowserContextEmittedEvents {
+ /**
+ * Emitted when the url of a target inside the browser context changes.
+ * Contains a {@link Target} instance.
+ */
+ TargetChanged = 'targetchanged',
+
+ /**
+ * Emitted when a target is created within the browser context, for example
+ * when a new page is opened by
+ * {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
+ * or by {@link BrowserContext.newPage | browserContext.newPage}
+ *
+ * Contains a {@link Target} instance.
+ */
+ TargetCreated = 'targetcreated',
+ /**
+ * Emitted when a target is destroyed within the browser context, for example
+ * when a page is closed. Contains a {@link Target} instance.
+ */
+ TargetDestroyed = 'targetdestroyed',
+}
+
+/**
+ * BrowserContexts provide a way to operate multiple independent browser
+ * sessions. When a browser is launched, it has a single BrowserContext used by
+ * default. The method {@link Browser.newPage | Browser.newPage} creates a page
+ * in the default browser context.
+ *
+ * @remarks
+ *
+ * The Browser class extends from Puppeteer's {@link EventEmitter} class and
+ * will emit various events which are documented in the
+ * {@link BrowserContextEmittedEvents} enum.
+ *
+ * If a page opens another page, e.g. with a `window.open` call, the popup will
+ * belong to the parent page's browser context.
+ *
+ * Puppeteer allows creation of "incognito" browser contexts with
+ * {@link Browser.createIncognitoBrowserContext | Browser.createIncognitoBrowserContext}
+ * method. "Incognito" browser contexts don't write any browsing data to disk.
+ *
+ * @example
+ *
+ * ```ts
+ * // Create a new incognito browser context
+ * const context = await browser.createIncognitoBrowserContext();
+ * // Create a new page inside context.
+ * const page = await context.newPage();
+ * // ... do stuff with page ...
+ * await page.goto('https://example.com');
+ * // Dispose context once it's no longer needed.
+ * await context.close();
+ * ```
+ *
+ * @public
+ */
+export class BrowserContext extends EventEmitter {
+ /**
+ * @internal
+ */
+ constructor() {
+ super();
+ }
+
+ /**
+ * An array of all active targets inside the browser context.
+ */
+ targets(): Target[] {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * This searches for a target in this specific browser context.
+ *
+ * @example
+ * An example of finding a target for a page opened via `window.open`:
+ *
+ * ```ts
+ * await page.evaluate(() => window.open('https://www.example.com/'));
+ * const newWindowTarget = await browserContext.waitForTarget(
+ * target => target.url() === 'https://www.example.com/'
+ * );
+ * ```
+ *
+ * @param predicate - A function to be run for every target
+ * @param options - An object of options. Accepts a timout,
+ * which is the maximum wait time in milliseconds.
+ * Pass `0` to disable the timeout. Defaults to 30 seconds.
+ * @returns Promise which resolves to the first target found
+ * that matches the `predicate` function.
+ */
+ waitForTarget(
+ predicate: (x: Target) => boolean | Promise,
+ options?: {timeout?: number}
+ ): Promise;
+ waitForTarget(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * An array of all pages inside the browser context.
+ *
+ * @returns Promise which resolves to an array of all open pages.
+ * Non visible pages, such as `"background_page"`, will not be listed here.
+ * You can find them using {@link Target.page | the target page}.
+ */
+ pages(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Returns whether BrowserContext is incognito.
+ * The default browser context is the only non-incognito browser context.
+ *
+ * @remarks
+ * The default browser context cannot be closed.
+ */
+ isIncognito(): boolean {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * @example
+ *
+ * ```ts
+ * const context = browser.defaultBrowserContext();
+ * await context.overridePermissions('https://html5demos.com', [
+ * 'geolocation',
+ * ]);
+ * ```
+ *
+ * @param origin - The origin to grant permissions to, e.g. "https://example.com".
+ * @param permissions - An array of permissions to grant.
+ * All permissions that are not listed here will be automatically denied.
+ */
+ overridePermissions(origin: string, permissions: Permission[]): Promise;
+ overridePermissions(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Clears all permission overrides for the browser context.
+ *
+ * @example
+ *
+ * ```ts
+ * const context = browser.defaultBrowserContext();
+ * context.overridePermissions('https://example.com', ['clipboard-read']);
+ * // do stuff ..
+ * context.clearPermissionOverrides();
+ * ```
+ */
+ clearPermissionOverrides(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Creates a new page in the browser context.
+ */
+ newPage(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * The browser this browser context belongs to.
+ */
+ browser(): Browser {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Closes the browser context. All the targets that belong to the browser context
+ * will be closed.
+ *
+ * @remarks
+ * Only incognito browser contexts can be closed.
+ */
+ close(): Promise {
+ throw new Error('Not implemented');
+ }
+}
diff --git a/remote/test/puppeteer/src/common/AriaQueryHandler.ts b/remote/test/puppeteer/src/common/AriaQueryHandler.ts
index 2ec86f5bf247..97bd9b885692 100644
--- a/remote/test/puppeteer/src/common/AriaQueryHandler.ts
+++ b/remote/test/puppeteer/src/common/AriaQueryHandler.ts
@@ -19,8 +19,8 @@ import {assert} from '../util/assert.js';
import {CDPSession} from './Connection.js';
import {ElementHandle} from './ElementHandle.js';
import {Frame} from './Frame.js';
-import {MAIN_WORLD, PageBinding, PUPPETEER_WORLD} from './IsolatedWorld.js';
-import {InternalQueryHandler} from './QueryHandler.js';
+import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorld.js';
+import {PuppeteerQueryHandler} from './QueryHandler.js';
async function queryAXTree(
client: CDPSession,
@@ -95,7 +95,7 @@ const queryOneId = async (element: ElementHandle, selector: string) => {
return res[0].backendDOMNodeId;
};
-const queryOne: InternalQueryHandler['queryOne'] = async (
+const queryOne: PuppeteerQueryHandler['queryOne'] = async (
element,
selector
) => {
@@ -108,7 +108,7 @@ const queryOne: InternalQueryHandler['queryOne'] = async (
)) as ElementHandle;
};
-const waitFor: InternalQueryHandler['waitFor'] = async (
+const waitFor: PuppeteerQueryHandler['waitFor'] = async (
elementOrFrame,
selector,
options
@@ -121,21 +121,20 @@ const waitFor: InternalQueryHandler['waitFor'] = async (
frame = elementOrFrame.frame;
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(elementOrFrame);
}
- const binding: PageBinding = {
- name: 'ariaQuerySelector',
- pptrFunction: async (selector: string) => {
- const id = await queryOneId(
- element || (await frame.worlds[PUPPETEER_WORLD].document()),
- selector
- );
- if (!id) {
- return null;
- }
- return (await frame.worlds[PUPPETEER_WORLD].adoptBackendNode(
- id
- )) as ElementHandle;
- },
+
+ const ariaQuerySelector = async (selector: string) => {
+ const id = await queryOneId(
+ element || (await frame.worlds[PUPPETEER_WORLD].document()),
+ selector
+ );
+ if (!id) {
+ return null;
+ }
+ return (await frame.worlds[PUPPETEER_WORLD].adoptBackendNode(
+ id
+ )) as ElementHandle;
};
+
const result = await frame.worlds[PUPPETEER_WORLD]._waitForSelectorInPage(
(_: Element, selector: string) => {
return (
@@ -147,22 +146,19 @@ const waitFor: InternalQueryHandler['waitFor'] = async (
element,
selector,
options,
- binding
+ new Set([ariaQuerySelector])
);
if (element) {
await element.dispose();
}
- if (!result) {
- return null;
- }
if (!(result instanceof ElementHandle)) {
- await result.dispose();
+ await result?.dispose();
return null;
}
return result.frame.worlds[MAIN_WORLD].transferHandle(result);
};
-const queryAll: InternalQueryHandler['queryAll'] = async (
+const queryAll: PuppeteerQueryHandler['queryAll'] = async (
element,
selector
) => {
@@ -182,7 +178,7 @@ const queryAll: InternalQueryHandler['queryAll'] = async (
/**
* @internal
*/
-export const ariaHandler: InternalQueryHandler = {
+export const ariaHandler: PuppeteerQueryHandler = {
queryOne,
waitFor,
queryAll,
diff --git a/remote/test/puppeteer/src/common/Browser.ts b/remote/test/puppeteer/src/common/Browser.ts
index 253cfff29142..0f4ab11b50f6 100644
--- a/remote/test/puppeteer/src/common/Browser.ts
+++ b/remote/test/puppeteer/src/common/Browser.ts
@@ -18,7 +18,6 @@ import {ChildProcess} from 'child_process';
import {Protocol} from 'devtools-protocol';
import {assert} from '../util/assert.js';
import {CDPSession, Connection, ConnectionEmittedEvents} from './Connection.js';
-import {EventEmitter} from './EventEmitter.js';
import {waitWithTimeout} from './util.js';
import {Page} from './Page.js';
import {Viewport} from './PuppeteerViewport.js';
@@ -27,196 +26,24 @@ import {TaskQueue} from './TaskQueue.js';
import {TargetManager, TargetManagerEmittedEvents} from './TargetManager.js';
import {ChromeTargetManager} from './ChromeTargetManager.js';
import {FirefoxTargetManager} from './FirefoxTargetManager.js';
-
-/**
- * BrowserContext options.
- *
- * @public
- */
-export interface BrowserContextOptions {
- /**
- * Proxy server with optional port to use for all requests.
- * Username and password can be set in `Page.authenticate`.
- */
- proxyServer?: string;
- /**
- * Bypass the proxy for the given semi-colon-separated list of hosts.
- */
- proxyBypassList?: string[];
-}
-
-/**
- * @internal
- */
-export type BrowserCloseCallback = () => Promise | void;
-
-/**
- * @public
- */
-export type TargetFilterCallback = (
- target: Protocol.Target.TargetInfo
-) => boolean;
-
-/**
- * @internal
- */
-export type IsPageTargetCallback = (
- target: Protocol.Target.TargetInfo
-) => boolean;
-
-const WEB_PERMISSION_TO_PROTOCOL_PERMISSION = new Map<
+import {
+ Browser as BrowserBase,
+ BrowserContext,
+ BrowserCloseCallback,
+ TargetFilterCallback,
+ IsPageTargetCallback,
+ BrowserEmittedEvents,
+ BrowserContextEmittedEvents,
+ BrowserContextOptions,
+ WEB_PERMISSION_TO_PROTOCOL_PERMISSION,
+ WaitForTargetOptions,
Permission,
- Protocol.Browser.PermissionType
->([
- ['geolocation', 'geolocation'],
- ['midi', 'midi'],
- ['notifications', 'notifications'],
- // TODO: push isn't a valid type?
- // ['push', 'push'],
- ['camera', 'videoCapture'],
- ['microphone', 'audioCapture'],
- ['background-sync', 'backgroundSync'],
- ['ambient-light-sensor', 'sensors'],
- ['accelerometer', 'sensors'],
- ['gyroscope', 'sensors'],
- ['magnetometer', 'sensors'],
- ['accessibility-events', 'accessibilityEvents'],
- ['clipboard-read', 'clipboardReadWrite'],
- ['clipboard-write', 'clipboardReadWrite'],
- ['payment-handler', 'paymentHandler'],
- ['persistent-storage', 'durableStorage'],
- ['idle-detection', 'idleDetection'],
- // chrome-specific permissions we have.
- ['midi-sysex', 'midiSysex'],
-]);
+} from '../api/Browser.js';
/**
- * @public
+ * @internal
*/
-export type Permission =
- | 'geolocation'
- | 'midi'
- | 'notifications'
- | 'camera'
- | 'microphone'
- | 'background-sync'
- | 'ambient-light-sensor'
- | 'accelerometer'
- | 'gyroscope'
- | 'magnetometer'
- | 'accessibility-events'
- | 'clipboard-read'
- | 'clipboard-write'
- | 'payment-handler'
- | 'persistent-storage'
- | 'idle-detection'
- | 'midi-sysex';
-
-/**
- * @public
- */
-export interface WaitForTargetOptions {
- /**
- * Maximum wait time in milliseconds. Pass `0` to disable the timeout.
- * @defaultValue 30 seconds.
- */
- timeout?: number;
-}
-
-/**
- * All the events a {@link Browser | browser instance} may emit.
- *
- * @public
- */
-export const enum BrowserEmittedEvents {
- /**
- * Emitted when Puppeteer gets disconnected from the Chromium instance. This
- * might happen because of one of the following:
- *
- * - Chromium is closed or crashed
- *
- * - The {@link Browser.disconnect | browser.disconnect } method was called.
- */
- Disconnected = 'disconnected',
-
- /**
- * Emitted when the url of a target changes. Contains a {@link Target} instance.
- *
- * @remarks
- *
- * Note that this includes target changes in incognito browser contexts.
- */
- TargetChanged = 'targetchanged',
-
- /**
- * Emitted when a target is created, for example when a new page is opened by
- * {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
- * or by {@link Browser.newPage | browser.newPage}
- *
- * Contains a {@link Target} instance.
- *
- * @remarks
- *
- * Note that this includes target creations in incognito browser contexts.
- */
- TargetCreated = 'targetcreated',
- /**
- * Emitted when a target is destroyed, for example when a page is closed.
- * Contains a {@link Target} instance.
- *
- * @remarks
- *
- * Note that this includes target destructions in incognito browser contexts.
- */
- TargetDestroyed = 'targetdestroyed',
-}
-
-/**
- * A Browser is created when Puppeteer connects to a Chromium instance, either through
- * {@link PuppeteerNode.launch} or {@link Puppeteer.connect}.
- *
- * @remarks
- *
- * The Browser class extends from Puppeteer's {@link EventEmitter} class and will
- * emit various events which are documented in the {@link BrowserEmittedEvents} enum.
- *
- * @example
- * An example of using a {@link Browser} to create a {@link Page}:
- *
- * ```ts
- * const puppeteer = require('puppeteer');
- *
- * (async () => {
- * const browser = await puppeteer.launch();
- * const page = await browser.newPage();
- * await page.goto('https://example.com');
- * await browser.close();
- * })();
- * ```
- *
- * @example
- * An example of disconnecting from and reconnecting to a {@link Browser}:
- *
- * ```ts
- * const puppeteer = require('puppeteer');
- *
- * (async () => {
- * const browser = await puppeteer.launch();
- * // Store the endpoint to be able to reconnect to Chromium
- * const browserWSEndpoint = browser.wsEndpoint();
- * // Disconnect puppeteer from Chromium
- * browser.disconnect();
- *
- * // Use the endpoint to reestablish a connection
- * const browser2 = await puppeteer.connect({browserWSEndpoint});
- * // Close Chromium
- * await browser2.close();
- * })();
- * ```
- *
- * @public
- */
-export class Browser extends EventEmitter {
+export class CDPBrowser extends BrowserBase {
/**
* @internal
*/
@@ -230,8 +57,8 @@ export class Browser extends EventEmitter {
closeCallback?: BrowserCloseCallback,
targetFilterCallback?: TargetFilterCallback,
isPageTargetCallback?: IsPageTargetCallback
- ): Promise {
- const browser = new Browser(
+ ): Promise {
+ const browser = new CDPBrowser(
product,
connection,
contextIds,
@@ -252,15 +79,15 @@ export class Browser extends EventEmitter {
#closeCallback: BrowserCloseCallback;
#targetFilterCallback: TargetFilterCallback;
#isPageTargetCallback!: IsPageTargetCallback;
- #defaultContext: BrowserContext;
- #contexts: Map;
+ #defaultContext: CDPBrowserContext;
+ #contexts: Map;
#screenshotTaskQueue: TaskQueue;
#targetManager: TargetManager;
/**
* @internal
*/
- get _targets(): Map {
+ override get _targets(): Map {
return this.#targetManager.getAvailableTargets();
}
@@ -305,12 +132,12 @@ export class Browser extends EventEmitter {
this.#targetFilterCallback
);
}
- this.#defaultContext = new BrowserContext(this.#connection, this);
+ this.#defaultContext = new CDPBrowserContext(this.#connection, this);
this.#contexts = new Map();
for (const contextId of contextIds) {
this.#contexts.set(
contextId,
- new BrowserContext(this.#connection, this, contextId)
+ new CDPBrowserContext(this.#connection, this, contextId)
);
}
}
@@ -322,7 +149,7 @@ export class Browser extends EventEmitter {
/**
* @internal
*/
- async _attach(): Promise {
+ override async _attach(): Promise {
this.#connection.on(
ConnectionEmittedEvents.Disconnected,
this.#emitDisconnected
@@ -349,7 +176,7 @@ export class Browser extends EventEmitter {
/**
* @internal
*/
- _detach(): void {
+ override _detach(): void {
this.#connection.off(
ConnectionEmittedEvents.Disconnected,
this.#emitDisconnected
@@ -376,7 +203,7 @@ export class Browser extends EventEmitter {
* The spawned browser process. Returns `null` if the browser instance was created with
* {@link Puppeteer.connect}.
*/
- process(): ChildProcess | null {
+ override process(): ChildProcess | null {
return this.#process ?? null;
}
@@ -402,7 +229,7 @@ export class Browser extends EventEmitter {
/**
* @internal
*/
- _getIsPageTargetCallback(): IsPageTargetCallback | undefined {
+ override _getIsPageTargetCallback(): IsPageTargetCallback | undefined {
return this.#isPageTargetCallback;
}
@@ -424,9 +251,9 @@ export class Browser extends EventEmitter {
* })();
* ```
*/
- async createIncognitoBrowserContext(
+ override async createIncognitoBrowserContext(
options: BrowserContextOptions = {}
- ): Promise {
+ ): Promise {
const {proxyServer, proxyBypassList} = options;
const {browserContextId} = await this.#connection.send(
@@ -436,7 +263,7 @@ export class Browser extends EventEmitter {
proxyBypassList: proxyBypassList && proxyBypassList.join(','),
}
);
- const context = new BrowserContext(
+ const context = new CDPBrowserContext(
this.#connection,
this,
browserContextId
@@ -449,21 +276,21 @@ export class Browser extends EventEmitter {
* Returns an array of all open browser contexts. In a newly created browser, this will
* return a single instance of {@link BrowserContext}.
*/
- browserContexts(): BrowserContext[] {
+ override browserContexts(): CDPBrowserContext[] {
return [this.#defaultContext, ...Array.from(this.#contexts.values())];
}
/**
* Returns the default browser context. The default browser context cannot be closed.
*/
- defaultBrowserContext(): BrowserContext {
+ override defaultBrowserContext(): CDPBrowserContext {
return this.#defaultContext;
}
/**
* @internal
*/
- async _disposeContext(contextId?: string): Promise {
+ override async _disposeContext(contextId?: string): Promise {
if (!contextId) {
return;
}
@@ -564,7 +391,7 @@ export class Browser extends EventEmitter {
* https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target
* | browser endpoint}.
*/
- wsEndpoint(): string {
+ override wsEndpoint(): string {
return this.#connection.url();
}
@@ -572,14 +399,14 @@ export class Browser extends EventEmitter {
* Promise which resolves to a new {@link Page} object. The Page is created in
* a default browser context.
*/
- async newPage(): Promise {
+ override async newPage(): Promise {
return this.#defaultContext.newPage();
}
/**
* @internal
*/
- async _createPageInContext(contextId?: string): Promise {
+ override async _createPageInContext(contextId?: string): Promise {
const {targetId} = await this.#connection.send('Target.createTarget', {
url: 'about:blank',
browserContextId: contextId || undefined,
@@ -605,7 +432,7 @@ export class Browser extends EventEmitter {
* All active targets inside the Browser. In case of multiple browser contexts, returns
* an array with all the targets in all browser contexts.
*/
- targets(): Target[] {
+ override targets(): Target[] {
return Array.from(
this.#targetManager.getAvailableTargets().values()
).filter(target => {
@@ -616,7 +443,7 @@ export class Browser extends EventEmitter {
/**
* The target associated with the browser.
*/
- target(): Target {
+ override target(): Target {
const browserTarget = this.targets().find(target => {
return target.type() === 'browser';
});
@@ -643,7 +470,7 @@ export class Browser extends EventEmitter {
* );
* ```
*/
- async waitForTarget(
+ override async waitForTarget(
predicate: (x: Target) => boolean | Promise,
options: WaitForTargetOptions = {}
): Promise {
@@ -683,7 +510,7 @@ export class Browser extends EventEmitter {
* browser contexts. Non-visible pages, such as `"background_page"`, will not be listed
* here. You can find them using {@link Target.page}.
*/
- async pages(): Promise {
+ override async pages(): Promise {
const contextPages = await Promise.all(
this.browserContexts().map(context => {
return context.pages();
@@ -705,7 +532,7 @@ export class Browser extends EventEmitter {
*
* The format of browser.version() might change with future releases of Chromium.
*/
- async version(): Promise {
+ override async version(): Promise {
const version = await this.#getVersion();
return version.product;
}
@@ -714,26 +541,27 @@ export class Browser extends EventEmitter {
* The browser's original user agent. Pages can override the browser user agent with
* {@link Page.setUserAgent}.
*/
- async userAgent(): Promise {
+ override async userAgent(): Promise {
const version = await this.#getVersion();
return version.userAgent;
}
/**
- * Closes Chromium and all of its pages (if any were opened). The {@link Browser} object
- * itself is considered to be disposed and cannot be used anymore.
+ * Closes Chromium and all of its pages (if any were opened). The
+ * {@link CDPBrowser} object itself is considered to be disposed and cannot be
+ * used anymore.
*/
- async close(): Promise {
+ override async close(): Promise {
await this.#closeCallback.call(null);
this.disconnect();
}
/**
* Disconnects Puppeteer from the browser, but leaves the Chromium process running.
- * After calling `disconnect`, the {@link Browser} object is considered disposed and
+ * After calling `disconnect`, the {@link CDPBrowser} object is considered disposed and
* cannot be used anymore.
*/
- disconnect(): void {
+ override disconnect(): void {
this.#targetManager.dispose();
this.#connection.dispose();
}
@@ -741,7 +569,7 @@ export class Browser extends EventEmitter {
/**
* Indicates that the browser is connected.
*/
- isConnected(): boolean {
+ override isConnected(): boolean {
return !this.#connection._closed;
}
@@ -749,75 +577,19 @@ export class Browser extends EventEmitter {
return this.#connection.send('Browser.getVersion');
}
}
-/**
- * @public
- */
-export const enum BrowserContextEmittedEvents {
- /**
- * Emitted when the url of a target inside the browser context changes.
- * Contains a {@link Target} instance.
- */
- TargetChanged = 'targetchanged',
-
- /**
- * Emitted when a target is created within the browser context, for example
- * when a new page is opened by
- * {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/open | window.open}
- * or by {@link BrowserContext.newPage | browserContext.newPage}
- *
- * Contains a {@link Target} instance.
- */
- TargetCreated = 'targetcreated',
- /**
- * Emitted when a target is destroyed within the browser context, for example
- * when a page is closed. Contains a {@link Target} instance.
- */
- TargetDestroyed = 'targetdestroyed',
-}
/**
- * BrowserContexts provide a way to operate multiple independent browser
- * sessions. When a browser is launched, it has a single BrowserContext used by
- * default. The method {@link Browser.newPage | Browser.newPage} creates a page
- * in the default browser context.
- *
- * @remarks
- *
- * The Browser class extends from Puppeteer's {@link EventEmitter} class and
- * will emit various events which are documented in the
- * {@link BrowserContextEmittedEvents} enum.
- *
- * If a page opens another page, e.g. with a `window.open` call, the popup will
- * belong to the parent page's browser context.
- *
- * Puppeteer allows creation of "incognito" browser contexts with
- * {@link Browser.createIncognitoBrowserContext | Browser.createIncognitoBrowserContext}
- * method. "Incognito" browser contexts don't write any browsing data to disk.
- *
- * @example
- *
- * ```ts
- * // Create a new incognito browser context
- * const context = await browser.createIncognitoBrowserContext();
- * // Create a new page inside context.
- * const page = await context.newPage();
- * // ... do stuff with page ...
- * await page.goto('https://example.com');
- * // Dispose context once it's no longer needed.
- * await context.close();
- * ```
- *
- * @public
+ * @internal
*/
-export class BrowserContext extends EventEmitter {
+export class CDPBrowserContext extends BrowserContext {
#connection: Connection;
- #browser: Browser;
+ #browser: CDPBrowser;
#id?: string;
/**
* @internal
*/
- constructor(connection: Connection, browser: Browser, contextId?: string) {
+ constructor(connection: Connection, browser: CDPBrowser, contextId?: string) {
super();
this.#connection = connection;
this.#browser = browser;
@@ -827,7 +599,7 @@ export class BrowserContext extends EventEmitter {
/**
* An array of all active targets inside the browser context.
*/
- targets(): Target[] {
+ override targets(): Target[] {
return this.#browser.targets().filter(target => {
return target.browserContext() === this;
});
@@ -853,7 +625,7 @@ export class BrowserContext extends EventEmitter {
* @returns Promise which resolves to the first target found
* that matches the `predicate` function.
*/
- waitForTarget(
+ override waitForTarget(
predicate: (x: Target) => boolean | Promise,
options: {timeout?: number} = {}
): Promise {
@@ -869,7 +641,7 @@ export class BrowserContext extends EventEmitter {
* Non visible pages, such as `"background_page"`, will not be listed here.
* You can find them using {@link Target.page | the target page}.
*/
- async pages(): Promise {
+ override async pages(): Promise {
const pages = await Promise.all(
this.targets()
.filter(target => {
@@ -897,7 +669,7 @@ export class BrowserContext extends EventEmitter {
* @remarks
* The default browser context cannot be closed.
*/
- isIncognito(): boolean {
+ override isIncognito(): boolean {
return !!this.#id;
}
@@ -915,7 +687,7 @@ export class BrowserContext extends EventEmitter {
* @param permissions - An array of permissions to grant.
* All permissions that are not listed here will be automatically denied.
*/
- async overridePermissions(
+ override async overridePermissions(
origin: string,
permissions: Permission[]
): Promise {
@@ -946,7 +718,7 @@ export class BrowserContext extends EventEmitter {
* context.clearPermissionOverrides();
* ```
*/
- async clearPermissionOverrides(): Promise {
+ override async clearPermissionOverrides(): Promise {
await this.#connection.send('Browser.resetPermissions', {
browserContextId: this.#id || undefined,
});
@@ -955,14 +727,14 @@ export class BrowserContext extends EventEmitter {
/**
* Creates a new page in the browser context.
*/
- newPage(): Promise {
+ override newPage(): Promise {
return this.#browser._createPageInContext(this.#id);
}
/**
* The browser this browser context belongs to.
*/
- browser(): Browser {
+ override browser(): CDPBrowser {
return this.#browser;
}
@@ -973,7 +745,7 @@ export class BrowserContext extends EventEmitter {
* @remarks
* Only incognito browser contexts can be closed.
*/
- async close(): Promise {
+ override async close(): Promise {
assert(this.#id, 'Non-incognito profiles cannot be closed!');
await this.#browser._disposeContext(this.#id);
}
diff --git a/remote/test/puppeteer/src/common/BrowserConnector.ts b/remote/test/puppeteer/src/common/BrowserConnector.ts
index 8d65873c3184..4eaba3bd2cdc 100644
--- a/remote/test/puppeteer/src/common/BrowserConnector.ts
+++ b/remote/test/puppeteer/src/common/BrowserConnector.ts
@@ -18,11 +18,8 @@ import {debugError} from './util.js';
import {isErrorLike} from '../util/ErrorLike.js';
import {isNode} from '../environment.js';
import {assert} from '../util/assert.js';
-import {
- Browser,
- IsPageTargetCallback,
- TargetFilterCallback,
-} from './Browser.js';
+import {IsPageTargetCallback, TargetFilterCallback} from '../api/Browser.js';
+import {CDPBrowser} from './Browser.js';
import {Connection} from './Connection.js';
import {ConnectionTransport} from './ConnectionTransport.js';
import {getFetch} from './fetch.js';
@@ -55,6 +52,11 @@ export interface BrowserConnectOptions {
* @internal
*/
_isPageTarget?: IsPageTargetCallback;
+ /**
+ * @defaultValue 'cdp'
+ * @internal
+ */
+ protocol?: 'cdp' | 'webDriverBiDi';
}
const getWebSocketTransportClass = async () => {
@@ -70,13 +72,13 @@ const getWebSocketTransportClass = async () => {
*
* @internal
*/
-export async function _connectToBrowser(
+export async function _connectToCDPBrowser(
options: BrowserConnectOptions & {
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;
}
-): Promise {
+): Promise {
const {
browserWSEndpoint,
browserURL,
@@ -118,7 +120,7 @@ export async function _connectToBrowser(
const {browserContextIds} = await connection.send(
'Target.getBrowserContexts'
);
- const browser = await Browser._create(
+ const browser = await CDPBrowser._create(
product || 'chrome',
connection,
browserContextIds,
@@ -131,7 +133,6 @@ export async function _connectToBrowser(
targetFilter,
isPageTarget
);
- await browser.pages();
return browser;
}
diff --git a/remote/test/puppeteer/src/common/ChromeTargetManager.ts b/remote/test/puppeteer/src/common/ChromeTargetManager.ts
index 445225edf352..b967adb08703 100644
--- a/remote/test/puppeteer/src/common/ChromeTargetManager.ts
+++ b/remote/test/puppeteer/src/common/ChromeTargetManager.ts
@@ -20,7 +20,7 @@ import {CDPSession, Connection} from './Connection.js';
import {EventEmitter} from './EventEmitter.js';
import {Target} from './Target.js';
import {debugError} from './util.js';
-import {TargetFilterCallback} from './Browser.js';
+import {TargetFilterCallback} from '../api/Browser.js';
import {
TargetInterceptor,
TargetFactory,
diff --git a/remote/test/puppeteer/src/common/Connection.ts b/remote/test/puppeteer/src/common/Connection.ts
index 541e225e4e07..84312fa2ef07 100644
--- a/remote/test/puppeteer/src/common/Connection.ts
+++ b/remote/test/puppeteer/src/common/Connection.ts
@@ -56,7 +56,7 @@ export class Connection extends EventEmitter {
#transport: ConnectionTransport;
#delay: number;
#lastId = 0;
- #sessions: Map = new Map();
+ #sessions: Map = new Map();
#closed = false;
#callbacks: Map = new Map();
#manuallyAttached = new Set();
@@ -147,7 +147,7 @@ export class Connection extends EventEmitter {
const object = JSON.parse(message);
if (object.method === 'Target.attachedToTarget') {
const sessionId = object.params.sessionId;
- const session = new CDPSession(
+ const session = new CDPSessionImpl(
this,
object.params.targetInfo.type,
sessionId
@@ -310,6 +310,47 @@ export const CDPSessionEmittedEvents = {
* @public
*/
export class CDPSession extends EventEmitter {
+ /**
+ * @internal
+ */
+ constructor() {
+ super();
+ }
+
+ connection(): Connection | undefined {
+ throw new Error('Not implemented');
+ }
+
+ send(
+ method: T,
+ ...paramArgs: ProtocolMapping.Commands[T]['paramsType']
+ ): Promise;
+ send(): Promise<
+ ProtocolMapping.Commands[T]['returnType']
+ > {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Detaches the cdpSession from the target. Once detached, the cdpSession object
+ * won't emit any events and can't be used to send messages.
+ */
+ async detach(): Promise {
+ throw new Error('Not implemented');
+ }
+
+ /**
+ * Returns the session's id.
+ */
+ id(): string {
+ throw new Error('Not implemented');
+ }
+}
+
+/**
+ * @internal
+ */
+export class CDPSessionImpl extends CDPSession {
#sessionId: string;
#targetType: string;
#callbacks: Map = new Map();
@@ -325,11 +366,11 @@ export class CDPSession extends EventEmitter {
this.#sessionId = sessionId;
}
- connection(): Connection | undefined {
+ override connection(): Connection | undefined {
return this.#connection;
}
- send(
+ override send(
method: T,
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
): Promise {
@@ -386,7 +427,7 @@ export class CDPSession extends EventEmitter {
* Detaches the cdpSession from the target. Once detached, the cdpSession object
* won't emit any events and can't be used to send messages.
*/
- async detach(): Promise {
+ override async detach(): Promise {
if (!this.#connection) {
throw new Error(
`Session already detached. Most likely the ${
@@ -419,7 +460,7 @@ export class CDPSession extends EventEmitter {
/**
* Returns the session's id.
*/
- id(): string {
+ override id(): string {
return this.#sessionId;
}
}
@@ -445,3 +486,13 @@ function rewriteError(
error.originalMessage = originalMessage ?? error.originalMessage;
return error;
}
+
+/**
+ * @internal
+ */
+export function isTargetClosedError(err: Error): boolean {
+ return (
+ err.message.includes('Target closed') ||
+ err.message.includes('Session closed')
+ );
+}
diff --git a/remote/test/puppeteer/src/common/Coverage.ts b/remote/test/puppeteer/src/common/Coverage.ts
index a75a772a6980..a41f7b4709e4 100644
--- a/remote/test/puppeteer/src/common/Coverage.ts
+++ b/remote/test/puppeteer/src/common/Coverage.ts
@@ -142,7 +142,8 @@ export class Coverage {
* Anonymous scripts are ones that don't have an associated url. These are
* scripts that are dynamically created on the page using `eval` or
* `new Function`. If `reportAnonymousScripts` is set to `true`, anonymous
- * scripts will have `pptr://__puppeteer_evaluation_script__` as their URL.
+ * scripts URL will start with `debugger://VM` (unless a magic //# sourceURL
+ * comment is present, in which case that will the be URL).
*/
async startJSCoverage(options: JSCoverageOptions = {}): Promise {
return await this.#jsCoverage.start(options);
diff --git a/remote/test/puppeteer/src/common/ElementHandle.ts b/remote/test/puppeteer/src/common/ElementHandle.ts
index 8cb19fa0428f..39db023d1ba8 100644
--- a/remote/test/puppeteer/src/common/ElementHandle.ts
+++ b/remote/test/puppeteer/src/common/ElementHandle.ts
@@ -1,3 +1,19 @@
+/**
+ * Copyright 2019 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
import {Protocol} from 'devtools-protocol';
import {assert} from '../util/assert.js';
import {ExecutionContext} from './ExecutionContext.js';
diff --git a/remote/test/puppeteer/src/common/EventEmitter.ts b/remote/test/puppeteer/src/common/EventEmitter.ts
index ce78874b436c..e7c8015550ef 100644
--- a/remote/test/puppeteer/src/common/EventEmitter.ts
+++ b/remote/test/puppeteer/src/common/EventEmitter.ts
@@ -1,3 +1,19 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
import mitt, {
Emitter,
EventType,
diff --git a/remote/test/puppeteer/src/common/ExecutionContext.ts b/remote/test/puppeteer/src/common/ExecutionContext.ts
index 13dfbc51223c..c5058fa11615 100644
--- a/remote/test/puppeteer/src/common/ExecutionContext.ts
+++ b/remote/test/puppeteer/src/common/ExecutionContext.ts
@@ -18,6 +18,7 @@ import {Protocol} from 'devtools-protocol';
import {CDPSession} from './Connection.js';
import {IsolatedWorld} from './IsolatedWorld.js';
import {JSHandle} from './JSHandle.js';
+import {LazyArg} from './LazyArg.js';
import {EvaluateFunc, HandleFor} from './types.js';
import {
createJSHandle,
@@ -273,7 +274,7 @@ export class ExecutionContext {
callFunctionOnPromise = this._client.send('Runtime.callFunctionOn', {
functionDeclaration: functionText + '\n' + suffix + '\n',
executionContextId: this._contextId,
- arguments: args.map(convertArgument.bind(this)),
+ arguments: await Promise.all(args.map(convertArgument.bind(this))),
returnByValue,
awaitPromise: true,
userGesture: true,
@@ -298,10 +299,13 @@ export class ExecutionContext {
? valueFromRemoteObject(remoteObject)
: createJSHandle(this, remoteObject);
- function convertArgument(
+ async function convertArgument(
this: ExecutionContext,
arg: unknown
- ): Protocol.Runtime.CallArgument {
+ ): Promise {
+ if (arg instanceof LazyArg) {
+ arg = await arg.get();
+ }
if (typeof arg === 'bigint') {
// eslint-disable-line valid-typeof
return {unserializableValue: `${arg.toString()}n`};
diff --git a/remote/test/puppeteer/src/common/FirefoxTargetManager.ts b/remote/test/puppeteer/src/common/FirefoxTargetManager.ts
index e31a205aa1dc..38bd042b1877 100644
--- a/remote/test/puppeteer/src/common/FirefoxTargetManager.ts
+++ b/remote/test/puppeteer/src/common/FirefoxTargetManager.ts
@@ -18,7 +18,7 @@ import Protocol from 'devtools-protocol';
import {assert} from '../util/assert.js';
import {CDPSession, Connection} from './Connection.js';
import {Target} from './Target.js';
-import {TargetFilterCallback} from './Browser.js';
+import {TargetFilterCallback} from '../api/Browser.js';
import {
TargetFactory,
TargetInterceptor,
diff --git a/remote/test/puppeteer/src/common/Frame.ts b/remote/test/puppeteer/src/common/Frame.ts
index 9d2d7759f0a1..3662d8413142 100644
--- a/remote/test/puppeteer/src/common/Frame.ts
+++ b/remote/test/puppeteer/src/common/Frame.ts
@@ -1,3 +1,19 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
import {Protocol} from 'devtools-protocol';
import {assert} from '../util/assert.js';
import {isErrorLike} from '../util/ErrorLike.js';
@@ -36,7 +52,7 @@ export interface FrameWaitForFunctionOptions {
*
* - `mutation` - to execute `pageFunction` on every DOM mutation.
*/
- polling?: string | number;
+ polling?: 'raf' | 'mutation' | number;
/**
* Maximum time to wait in milliseconds. Defaults to `30000` (30 seconds).
* Pass `0` to disable the timeout. Puppeteer's default timeout can be changed
@@ -150,7 +166,6 @@ export interface FrameAddStyleTagOptions {
* @public
*/
export class Frame {
- #parentFrame: Frame | null;
#url = '';
#detached = false;
#client!: CDPSession;
@@ -186,30 +201,25 @@ export class Frame {
/**
* @internal
*/
- _childFrames: Set ;
+ _parentId?: string;
/**
* @internal
*/
constructor(
frameManager: FrameManager,
- parentFrame: Frame | null,
frameId: string,
+ parentFrameId: string | undefined,
client: CDPSession
) {
this._frameManager = frameManager;
- this.#parentFrame = parentFrame ?? null;
this.#url = '';
this._id = frameId;
+ this._parentId = parentFrameId;
this.#detached = false;
this._loaderId = '';
- this._childFrames = new Set();
- if (this.#parentFrame) {
- this.#parentFrame._childFrames.add(this);
- }
-
this.updateClient(client);
}
@@ -220,7 +230,7 @@ export class Frame {
this.#client = client;
this.worlds = {
[MAIN_WORLD]: new IsolatedWorld(this),
- [PUPPETEER_WORLD]: new IsolatedWorld(this, true),
+ [PUPPETEER_WORLD]: new IsolatedWorld(this),
};
}
@@ -664,7 +674,6 @@ export class Frame {
options: FrameWaitForFunctionOptions = {},
...args: Params
): Promise>>> {
- // TODO: Fix when NodeHandle has been added.
return this.worlds[MAIN_WORLD].waitForFunction(
pageFunction,
options,
@@ -721,14 +730,14 @@ export class Frame {
* @returns The parent frame, if any. Detached and main frames return `null`.
*/
parentFrame(): Frame | null {
- return this.#parentFrame;
+ return this._frameManager._frameTree.parentFrame(this._id) || null;
}
/**
* @returns An array of child frames.
*/
childFrames(): Frame[] {
- return Array.from(this._childFrames);
+ return this._frameManager._frameTree.childFrames(this._id);
}
/**
@@ -776,8 +785,8 @@ export class Frame {
return this.worlds[MAIN_WORLD].transferHandle(
await this.worlds[PUPPETEER_WORLD].evaluateHandle(
- async ({url, id, type, content}) => {
- const promise = InjectedUtil.createDeferredPromise();
+ async ({createDeferredPromise}, {url, id, type, content}) => {
+ const promise = createDeferredPromise();
const script = document.createElement('script');
script.type = type;
script.text = content;
@@ -809,6 +818,7 @@ export class Frame {
await promise;
return script;
},
+ await this.worlds[PUPPETEER_WORLD].puppeteerUtil,
{...options, type, content}
)
);
@@ -858,8 +868,8 @@ export class Frame {
return this.worlds[MAIN_WORLD].transferHandle(
await this.worlds[PUPPETEER_WORLD].evaluateHandle(
- async ({url, content}) => {
- const promise = InjectedUtil.createDeferredPromise();
+ async ({createDeferredPromise}, {url, content}) => {
+ const promise = createDeferredPromise();
let element: HTMLStyleElement | HTMLLinkElement;
if (!url) {
element = document.createElement('style');
@@ -892,6 +902,7 @@ export class Frame {
await promise;
return element;
},
+ await this.worlds[PUPPETEER_WORLD].puppeteerUtil,
options
)
);
@@ -1089,9 +1100,5 @@ export class Frame {
this.#detached = true;
this.worlds[MAIN_WORLD]._detach();
this.worlds[PUPPETEER_WORLD]._detach();
- if (this.#parentFrame) {
- this.#parentFrame._childFrames.delete(this);
- }
- this.#parentFrame = null;
}
}
diff --git a/remote/test/puppeteer/src/common/FrameManager.ts b/remote/test/puppeteer/src/common/FrameManager.ts
index 8ab336bc2667..752b5d288f31 100644
--- a/remote/test/puppeteer/src/common/FrameManager.ts
+++ b/remote/test/puppeteer/src/common/FrameManager.ts
@@ -16,13 +16,12 @@
import {Protocol} from 'devtools-protocol';
import {assert} from '../util/assert.js';
-import {createDebuggableDeferredPromise} from '../util/DebuggableDeferredPromise.js';
-import {DeferredPromise} from '../util/DeferredPromise.js';
import {isErrorLike} from '../util/ErrorLike.js';
-import {CDPSession} from './Connection.js';
+import {CDPSession, isTargetClosedError} from './Connection.js';
import {EventEmitter} from './EventEmitter.js';
import {EVALUATION_SCRIPT_URL, ExecutionContext} from './ExecutionContext.js';
import {Frame} from './Frame.js';
+import {FrameTree} from './FrameTree.js';
import {IsolatedWorld, MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorld.js';
import {NetworkManager} from './NetworkManager.js';
import {Page} from './Page.js';
@@ -60,20 +59,13 @@ export class FrameManager extends EventEmitter {
#page: Page;
#networkManager: NetworkManager;
#timeoutSettings: TimeoutSettings;
- #frames = new Map();
#contextIdToContext = new Map();
#isolatedWorlds = new Set();
- #mainFrame?: Frame;
#client: CDPSession;
/**
- * Keeps track of OOPIF targets/frames (target ID == frame ID for OOPIFs)
- * that are being initialized.
+ * @internal
*/
- #framesPendingTargetInit = new Map>();
- /**
- * Keeps track of frames that are in the process of being attached in #onFrameAttached.
- */
- #framesPendingAttachment = new Map>();
+ _frameTree = new FrameTree();
get timeoutSettings(): TimeoutSettings {
return this.#timeoutSettings;
@@ -140,19 +132,8 @@ export class FrameManager extends EventEmitter {
});
}
- async initialize(
- targetId: string,
- client: CDPSession = this.#client
- ): Promise {
+ async initialize(client: CDPSession = this.#client): Promise {
try {
- if (!this.#framesPendingTargetInit.has(targetId)) {
- this.#framesPendingTargetInit.set(
- targetId,
- createDebuggableDeferredPromise(
- `Waiting for target frame ${targetId} failed`
- )
- );
- }
const result = await Promise.all([
client.send('Page.enable'),
client.send('Page.getFrameTree'),
@@ -172,18 +153,11 @@ export class FrameManager extends EventEmitter {
]);
} catch (error) {
// The target might have been closed before the initialization finished.
- if (
- isErrorLike(error) &&
- (error.message.includes('Target closed') ||
- error.message.includes('Session closed'))
- ) {
+ if (isErrorLike(error) && isTargetClosedError(error)) {
return;
}
throw error;
- } finally {
- this.#framesPendingTargetInit.get(targetId)?.resolve();
- this.#framesPendingTargetInit.delete(targetId);
}
}
@@ -202,16 +176,17 @@ export class FrameManager extends EventEmitter {
}
mainFrame(): Frame {
- assert(this.#mainFrame, 'Requesting main frame too early!');
- return this.#mainFrame;
+ const mainFrame = this._frameTree.getMainFrame();
+ assert(mainFrame, 'Requesting main frame too early!');
+ return mainFrame;
}
frames(): Frame[] {
- return Array.from(this.#frames.values());
+ return Array.from(this._frameTree.frames());
}
frame(frameId: string): Frame | null {
- return this.#frames.get(frameId) || null;
+ return this._frameTree.getById(frameId) || null;
}
onAttachedToTarget(target: Target): void {
@@ -219,16 +194,16 @@ export class FrameManager extends EventEmitter {
return;
}
- const frame = this.#frames.get(target._getTargetInfo().targetId);
+ const frame = this.frame(target._getTargetInfo().targetId);
if (frame) {
frame.updateClient(target._session()!);
}
this.setupEventListeners(target._session()!);
- this.initialize(target._getTargetInfo().targetId, target._session());
+ this.initialize(target._session());
}
onDetachedFromTarget(target: Target): void {
- const frame = this.#frames.get(target._targetId);
+ const frame = this.frame(target._targetId);
if (frame && frame.isOOPFrame()) {
// When an OOP iframe is removed from the page, it
// will only get a Target.detachedFromTarget event.
@@ -237,7 +212,7 @@ export class FrameManager extends EventEmitter {
}
#onLifecycleEvent(event: Protocol.Page.LifecycleEventEvent): void {
- const frame = this.#frames.get(event.frameId);
+ const frame = this.frame(event.frameId);
if (!frame) {
return;
}
@@ -246,7 +221,7 @@ export class FrameManager extends EventEmitter {
}
#onFrameStartedLoading(frameId: string): void {
- const frame = this.#frames.get(frameId);
+ const frame = this.frame(frameId);
if (!frame) {
return;
}
@@ -254,7 +229,7 @@ export class FrameManager extends EventEmitter {
}
#onFrameStoppedLoading(frameId: string): void {
- const frame = this.#frames.get(frameId);
+ const frame = this.frame(frameId);
if (!frame) {
return;
}
@@ -288,8 +263,8 @@ export class FrameManager extends EventEmitter {
frameId: string,
parentFrameId: string
): void {
- if (this.#frames.has(frameId)) {
- const frame = this.#frames.get(frameId)!;
+ let frame = this.frame(frameId);
+ if (frame) {
if (session && frame.isOOPFrame()) {
// If an OOP iframes becomes a normal iframe again
// it is first attached to the parent page before
@@ -298,86 +273,41 @@ export class FrameManager extends EventEmitter {
}
return;
}
- const parentFrame = this.#frames.get(parentFrameId);
- const complete = (parentFrame: Frame) => {
- assert(parentFrame, `Parent frame ${parentFrameId} not found`);
- const frame = new Frame(this, parentFrame, frameId, session);
- this.#frames.set(frame._id, frame);
- this.emit(FrameManagerEmittedEvents.FrameAttached, frame);
- };
-
- if (parentFrame) {
- return complete(parentFrame);
- }
-
- const frame = this.#framesPendingTargetInit.get(parentFrameId);
- if (frame) {
- if (!this.#framesPendingAttachment.has(frameId)) {
- this.#framesPendingAttachment.set(
- frameId,
- createDebuggableDeferredPromise(
- `Waiting for frame ${frameId} to attach failed`
- )
- );
- }
- frame.then(() => {
- complete(this.#frames.get(parentFrameId)!);
- this.#framesPendingAttachment.get(frameId)?.resolve();
- this.#framesPendingAttachment.delete(frameId);
- });
- return;
- }
-
- throw new Error(`Parent frame ${parentFrameId} not found`);
+ frame = new Frame(this, frameId, parentFrameId, session);
+ this._frameTree.addFrame(frame);
+ this.emit(FrameManagerEmittedEvents.FrameAttached, frame);
}
- #onFrameNavigated(framePayload: Protocol.Page.Frame): void {
+ async #onFrameNavigated(framePayload: Protocol.Page.Frame): Promise {
const frameId = framePayload.id;
const isMainFrame = !framePayload.parentId;
- const frame = isMainFrame ? this.#mainFrame : this.#frames.get(frameId);
- const complete = (frame?: Frame) => {
- assert(
- isMainFrame || frame,
- `Missing frame isMainFrame=${isMainFrame}, frameId=${frameId}`
- );
+ let frame = this._frameTree.getById(frameId);
- // Detach all child frames first.
- if (frame) {
- for (const child of frame.childFrames()) {
- this.#removeFramesRecursively(child);
- }
+ // Detach all child frames first.
+ if (frame) {
+ for (const child of frame.childFrames()) {
+ this.#removeFramesRecursively(child);
}
-
- // Update or create main frame.
- if (isMainFrame) {
- if (frame) {
- // Update frame id to retain frame identity on cross-process navigation.
- this.#frames.delete(frame._id);
- frame._id = frameId;
- } else {
- // Initial main frame navigation.
- frame = new Frame(this, null, frameId, this.#client);
- }
- this.#frames.set(frameId, frame);
- this.#mainFrame = frame;
- }
-
- // Update frame payload.
- assert(frame);
- frame._navigated(framePayload);
-
- this.emit(FrameManagerEmittedEvents.FrameNavigated, frame);
- };
- const pendingFrame = this.#framesPendingAttachment.get(frameId);
- if (pendingFrame) {
- pendingFrame.then(() => {
- complete(isMainFrame ? this.#mainFrame : this.#frames.get(frameId));
- });
- } else {
- complete(frame);
}
+
+ // Update or create main frame.
+ if (isMainFrame) {
+ if (frame) {
+ // Update frame id to retain frame identity on cross-process navigation.
+ this._frameTree.removeFrame(frame);
+ frame._id = frameId;
+ } else {
+ // Initial main frame navigation.
+ frame = new Frame(this, frameId, undefined, this.#client);
+ }
+ this._frameTree.addFrame(frame);
+ }
+
+ frame = await this._frameTree.waitForFrame(frameId);
+ frame._navigated(framePayload);
+ this.emit(FrameManagerEmittedEvents.FrameNavigated, frame);
}
async #createIsolatedWorld(session: CDPSession, name: string): Promise {
@@ -414,7 +344,7 @@ export class FrameManager extends EventEmitter {
}
#onFrameNavigatedWithinDocument(frameId: string, url: string): void {
- const frame = this.#frames.get(frameId);
+ const frame = this.frame(frameId);
if (!frame) {
return;
}
@@ -427,7 +357,7 @@ export class FrameManager extends EventEmitter {
frameId: string,
reason: Protocol.Page.FrameDetachedEventReason
): void {
- const frame = this.#frames.get(frameId);
+ const frame = this.frame(frameId);
if (reason === 'remove') {
// Only remove the frame if the reason for the detached event is
// an actual removement of the frame.
@@ -446,8 +376,7 @@ export class FrameManager extends EventEmitter {
): void {
const auxData = contextPayload.auxData as {frameId?: string} | undefined;
const frameId = auxData && auxData.frameId;
- const frame =
- typeof frameId === 'string' ? this.#frames.get(frameId) : undefined;
+ const frame = typeof frameId === 'string' ? this.frame(frameId) : undefined;
let world: IsolatedWorld | undefined;
if (frame) {
// Only care about execution contexts created for the current session.
@@ -513,7 +442,7 @@ export class FrameManager extends EventEmitter {
this.#removeFramesRecursively(child);
}
frame._detach();
- this.#frames.delete(frame._id);
+ this._frameTree.removeFrame(frame);
this.emit(FrameManagerEmittedEvents.FrameDetached, frame);
}
}
diff --git a/remote/test/puppeteer/src/common/FrameTree.ts b/remote/test/puppeteer/src/common/FrameTree.ts
new file mode 100644
index 000000000000..2cce9c33e84c
--- /dev/null
+++ b/remote/test/puppeteer/src/common/FrameTree.ts
@@ -0,0 +1,111 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+import {
+ createDeferredPromise,
+ DeferredPromise,
+} from '../util/DeferredPromise.js';
+import type {Frame} from './Frame.js';
+
+/**
+ * Keeps track of the page frame tree and it's is managed by
+ * {@link FrameManager}. FrameTree uses frame IDs to reference frame and it
+ * means that referenced frames might not be in the tree anymore. Thus, the tree
+ * structure is eventually consistent.
+ * @internal
+ */
+export class FrameTree {
+ #frames = new Map();
+ // frameID -> parentFrameID
+ #parentIds = new Map();
+ // frameID -> childFrameIDs
+ #childIds = new Map>();
+ #mainFrame?: Frame;
+ #waitRequests = new Map>>();
+
+ getMainFrame(): Frame | undefined {
+ return this.#mainFrame;
+ }
+
+ getById(frameId: string): Frame | undefined {
+ return this.#frames.get(frameId);
+ }
+
+ /**
+ * Returns a promise that is resolved once the frame with
+ * the given ID is added to the tree.
+ */
+ waitForFrame(frameId: string): Promise {
+ const frame = this.getById(frameId);
+ if (frame) {
+ return Promise.resolve(frame);
+ }
+ const deferred = createDeferredPromise ();
+ const callbacks =
+ this.#waitRequests.get(frameId) || new Set>();
+ callbacks.add(deferred);
+ return deferred;
+ }
+
+ frames(): Frame[] {
+ return Array.from(this.#frames.values());
+ }
+
+ addFrame(frame: Frame): void {
+ this.#frames.set(frame._id, frame);
+ if (frame._parentId) {
+ this.#parentIds.set(frame._id, frame._parentId);
+ if (!this.#childIds.has(frame._parentId)) {
+ this.#childIds.set(frame._parentId, new Set());
+ }
+ this.#childIds.get(frame._parentId)!.add(frame._id);
+ } else {
+ this.#mainFrame = frame;
+ }
+ this.#waitRequests.get(frame._id)?.forEach(request => {
+ return request.resolve(frame);
+ });
+ }
+
+ removeFrame(frame: Frame): void {
+ this.#frames.delete(frame._id);
+ this.#parentIds.delete(frame._id);
+ if (frame._parentId) {
+ this.#childIds.get(frame._parentId)?.delete(frame._id);
+ } else {
+ this.#mainFrame = undefined;
+ }
+ }
+
+ childFrames(frameId: string): Frame[] {
+ const childIds = this.#childIds.get(frameId);
+ if (!childIds) {
+ return [];
+ }
+ return Array.from(childIds)
+ .map(id => {
+ return this.getById(id);
+ })
+ .filter((frame): frame is Frame => {
+ return frame !== undefined;
+ });
+ }
+
+ parentFrame(frameId: string): Frame | undefined {
+ const parentId = this.#parentIds.get(frameId);
+ return parentId ? this.getById(parentId) : undefined;
+ }
+}
diff --git a/remote/test/puppeteer/src/common/IsolatedWorld.ts b/remote/test/puppeteer/src/common/IsolatedWorld.ts
index 9fe587e7c48b..04dba2e82ee7 100644
--- a/remote/test/puppeteer/src/common/IsolatedWorld.ts
+++ b/remote/test/puppeteer/src/common/IsolatedWorld.ts
@@ -16,39 +16,23 @@
import {Protocol} from 'devtools-protocol';
import {source as injectedSource} from '../generated/injected.js';
+import type PuppeteerUtil from '../injected/injected.js';
import {assert} from '../util/assert.js';
import {createDeferredPromise} from '../util/DeferredPromise.js';
+import {isErrorLike} from '../util/ErrorLike.js';
import {CDPSession} from './Connection.js';
import {ElementHandle} from './ElementHandle.js';
-import {TimeoutError} from './Errors.js';
import {ExecutionContext} from './ExecutionContext.js';
import {Frame} from './Frame.js';
import {FrameManager} from './FrameManager.js';
import {MouseButton} from './Input.js';
import {JSHandle} from './JSHandle.js';
+import {LazyArg} from './LazyArg.js';
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
import {TimeoutSettings} from './TimeoutSettings.js';
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
-import {
- createJSHandle,
- debugError,
- isNumber,
- isString,
- makePredicateString,
- pageBindingInitString,
-} from './util.js';
-
-// predicateQueryHandler and checkWaitForOptions are declared here so that
-// TypeScript knows about them when used in the predicate function below.
-declare const predicateQueryHandler: (
- element: Element | Document,
- selector: string
-) => Promise>;
-declare const checkWaitForOptions: (
- node: Node | null,
- waitForVisible: boolean,
- waitForHidden: boolean
-) => Element | null | boolean;
+import {createJSHandle, debugError, pageBindingInitString} from './util.js';
+import {TaskManager, WaitTask} from './WaitTask.js';
/**
* @public
@@ -114,7 +98,6 @@ export interface IsolatedWorldChart {
*/
export class IsolatedWorld {
#frame: Frame;
- #injected: boolean;
#document?: ElementHandle;
#context = createDeferredPromise();
#detached = false;
@@ -124,10 +107,15 @@ export class IsolatedWorld {
// Contains mapping from functions that should be bound to Puppeteer functions.
#boundFunctions = new Map();
- #waitTasks = new Set();
+ #taskManager = new TaskManager();
+ #puppeteerUtil = createDeferredPromise>();
- get _waitTasks(): Set {
- return this.#waitTasks;
+ get puppeteerUtil(): Promise> {
+ return this.#puppeteerUtil;
+ }
+
+ get taskManager(): TaskManager {
+ return this.#taskManager;
}
get _boundFunctions(): Map {
@@ -138,11 +126,10 @@ export class IsolatedWorld {
return `${name}_${contextId}`;
};
- constructor(frame: Frame, injected = false) {
+ constructor(frame: Frame) {
// Keep own reference to client because it might differ from the FrameManager's
// client for OOP iframes.
this.#frame = frame;
- this.#injected = injected;
this.#client.on('Runtime.bindingCalled', this.#onBindingCalled);
}
@@ -164,17 +151,30 @@ export class IsolatedWorld {
clearContext(): void {
this.#document = undefined;
+ this.#puppeteerUtil = createDeferredPromise();
this.#context = createDeferredPromise();
}
setContext(context: ExecutionContext): void {
- if (this.#injected) {
- context.evaluate(injectedSource).catch(debugError);
- }
+ this.#injectPuppeteerUtil(context);
this.#ctxBindings.clear();
this.#context.resolve(context);
- for (const waitTask of this._waitTasks) {
- waitTask.rerun();
+ }
+
+ async #injectPuppeteerUtil(context: ExecutionContext): Promise {
+ try {
+ this.#puppeteerUtil.resolve(
+ (await context.evaluateHandle(
+ `(() => {
+ const module = {};
+ ${injectedSource}
+ return module.exports.default;
+ })()`
+ )) as JSHandle
+ );
+ this.#taskManager.rerunAll();
+ } catch (error: unknown) {
+ debugError(error);
}
}
@@ -185,11 +185,9 @@ export class IsolatedWorld {
_detach(): void {
this.#detached = true;
this.#client.off('Runtime.bindingCalled', this.#onBindingCalled);
- for (const waitTask of this._waitTasks) {
- waitTask.terminate(
- new Error('waitForFunction failed: frame got detached.')
- );
- }
+ this.#taskManager.terminateAll(
+ new Error('waitForFunction failed: frame got detached.')
+ );
}
executionContext(): Promise {
@@ -411,8 +409,6 @@ export class IsolatedWorld {
// TODO: In theory, it would be enough to call this just once
await context._client.send('Runtime.addBinding', {
name,
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore The protocol definition is not up to date.
executionContextName: context._contextName,
});
await context.evaluate(expression);
@@ -420,18 +416,19 @@ export class IsolatedWorld {
// We could have tried to evaluate in a context which was already
// destroyed. This happens, for example, if the page is navigated while
// we are trying to add the binding
- const ctxDestroyed = (error as Error).message.includes(
- 'Execution context was destroyed'
- );
- const ctxNotFound = (error as Error).message.includes(
- 'Cannot find context with specified id'
- );
- if (ctxDestroyed || ctxNotFound) {
- return;
- } else {
- debugError(error);
- return;
+ if (error instanceof Error) {
+ // Destroyed context.
+ if (error.message.includes('Execution context was destroyed')) {
+ return;
+ }
+ // Missing context.
+ if (error.message.includes('Cannot find context with specified id')) {
+ return;
+ }
}
+
+ debugError(error);
+ return;
}
this.#ctxBindings.add(
IsolatedWorld.#bindingIdentifier(name, context._contextId)
@@ -476,7 +473,17 @@ export class IsolatedWorld {
throw new Error(`Bound function $name is not found`);
}
const result = await fn(...args);
- await context.evaluate(deliverResult, name, seq, result);
+ await context.evaluate(
+ (name: string, seq: number, result: unknown) => {
+ // @ts-expect-error Code is evaluated in a different context.
+ const callbacks = self[name].callbacks;
+ callbacks.get(seq).resolve(result);
+ callbacks.delete(seq);
+ },
+ name,
+ seq,
+ result
+ );
} catch (error) {
// The WaitTask may already have been resolved by timing out, or the
// exection context may have been destroyed.
@@ -488,14 +495,6 @@ export class IsolatedWorld {
}
debugError(error);
}
- function deliverResult(name: string, seq: number, result: unknown): void {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore Code is evaluated in a different context.
- (globalThis as any)[name].callbacks.get(seq).resolve(result);
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore Code is evaluated in a different context.
- (globalThis as any)[name].callbacks.delete(seq);
- }
};
async _waitForSelectorInPage(
@@ -503,59 +502,97 @@ export class IsolatedWorld {
root: ElementHandle | undefined,
selector: string,
options: WaitForSelectorOptions,
- binding?: PageBinding
+ bindings = new Set<(...args: never[]) => unknown>()
): Promise | null> {
const {
visible: waitForVisible = false,
hidden: waitForHidden = false,
timeout = this.#timeoutSettings.timeout(),
} = options;
- const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation';
- const title = `selector \`${selector}\`${
- waitForHidden ? ' to be hidden' : ''
- }`;
- async function predicate(
- root: Element | Document,
- selector: string,
- waitForVisible: boolean,
- waitForHidden: boolean
- ): Promise {
- const node = (await predicateQueryHandler(root, selector)) as Element;
- return checkWaitForOptions(node, waitForVisible, waitForHidden);
+
+ try {
+ const handle = await this.waitForFunction(
+ async (PuppeteerUtil, query, selector, root, visible) => {
+ if (!PuppeteerUtil) {
+ return;
+ }
+ const node = (await PuppeteerUtil.createFunction(query)(
+ root || document,
+ selector,
+ PuppeteerUtil
+ )) as Node | null;
+ return PuppeteerUtil.checkVisibility(node, visible);
+ },
+ {
+ bindings,
+ polling: waitForVisible || waitForHidden ? 'raf' : 'mutation',
+ root,
+ timeout,
+ },
+ new LazyArg(async () => {
+ try {
+ // In case CDP fails.
+ return await this.puppeteerUtil;
+ } catch {
+ return undefined;
+ }
+ }),
+ queryOne.toString(),
+ selector,
+ root,
+ waitForVisible ? true : waitForHidden ? false : undefined
+ );
+ const elementHandle = handle.asElement();
+ if (!elementHandle) {
+ await handle.dispose();
+ return null;
+ }
+ return elementHandle;
+ } catch (error) {
+ if (!isErrorLike(error)) {
+ throw error;
+ }
+ error.message = `Waiting for selector \`${selector}\` failed: ${error.message}`;
+ throw error;
}
- const waitTaskOptions: WaitTaskOptions = {
- isolatedWorld: this,
- predicateBody: makePredicateString(predicate, queryOne),
- predicateAcceptsContextElement: true,
- title,
- polling,
- timeout,
- args: [selector, waitForVisible, waitForHidden],
- binding,
- root,
- };
- const waitTask = new WaitTask(waitTaskOptions);
- return waitTask.promise;
}
- waitForFunction(
- pageFunction: Function | string,
- options: {polling?: string | number; timeout?: number} = {},
- ...args: unknown[]
- ): Promise {
- const {polling = 'raf', timeout = this.#timeoutSettings.timeout()} =
- options;
- const waitTaskOptions: WaitTaskOptions = {
- isolatedWorld: this,
- predicateBody: pageFunction,
- predicateAcceptsContextElement: false,
- title: 'function',
- polling,
- timeout,
- args,
- };
- const waitTask = new WaitTask(waitTaskOptions);
- return waitTask.promise;
+ waitForFunction<
+ Params extends unknown[],
+ Func extends EvaluateFunc = EvaluateFunc
+ >(
+ pageFunction: Func | string,
+ options: {
+ polling?: 'raf' | 'mutation' | number;
+ timeout?: number;
+ root?: ElementHandle;
+ bindings?: Set<(...args: never[]) => unknown>;
+ } = {},
+ ...args: Params
+ ): Promise>>> {
+ const {
+ polling = 'raf',
+ timeout = this.#timeoutSettings.timeout(),
+ bindings,
+ root,
+ } = options;
+ if (typeof polling === 'number' && polling < 0) {
+ throw new Error('Cannot poll with non-positive interval');
+ }
+ const waitTask = new WaitTask(
+ this,
+ {
+ bindings,
+ polling,
+ root,
+ timeout,
+ },
+ pageFunction as unknown as
+ | ((...args: unknown[]) => Promise>>)
+ | string,
+ ...args
+ );
+ return waitTask.result;
}
async title(): Promise {
@@ -593,315 +630,3 @@ export class IsolatedWorld {
return result;
}
}
-
-/**
- * @internal
- */
-export interface WaitTaskOptions {
- isolatedWorld: IsolatedWorld;
- predicateBody: Function | string;
- predicateAcceptsContextElement: boolean;
- title: string;
- polling: string | number;
- timeout: number;
- binding?: PageBinding;
- args: unknown[];
- root?: ElementHandle;
-}
-
-const noop = (): void => {};
-
-/**
- * @internal
- */
-export class WaitTask {
- #isolatedWorld: IsolatedWorld;
- #polling: 'raf' | 'mutation' | number;
- #timeout: number;
- #predicateBody: string;
- #predicateAcceptsContextElement: boolean;
- #args: unknown[];
- #binding?: PageBinding;
- #runCount = 0;
- #resolve: (x: JSHandle) => void = noop;
- #reject: (x: Error) => void = noop;
- #timeoutTimer?: NodeJS.Timeout;
- #terminated = false;
- #root: ElementHandle | null = null;
-
- promise: Promise;
-
- constructor(options: WaitTaskOptions) {
- if (isString(options.polling)) {
- assert(
- options.polling === 'raf' || options.polling === 'mutation',
- 'Unknown polling option: ' + options.polling
- );
- } else if (isNumber(options.polling)) {
- assert(
- options.polling > 0,
- 'Cannot poll with non-positive interval: ' + options.polling
- );
- } else {
- throw new Error('Unknown polling options: ' + options.polling);
- }
-
- function getPredicateBody(predicateBody: Function | string) {
- if (isString(predicateBody)) {
- return `return (${predicateBody});`;
- }
- return `return (${predicateBody})(...args);`;
- }
-
- this.#isolatedWorld = options.isolatedWorld;
- this.#polling = options.polling;
- this.#timeout = options.timeout;
- this.#root = options.root || null;
- this.#predicateBody = getPredicateBody(options.predicateBody);
- this.#predicateAcceptsContextElement =
- options.predicateAcceptsContextElement;
- this.#args = options.args;
- this.#binding = options.binding;
- this.#runCount = 0;
- this.#isolatedWorld._waitTasks.add(this);
- if (this.#binding) {
- this.#isolatedWorld._boundFunctions.set(
- this.#binding.name,
- this.#binding.pptrFunction
- );
- }
- this.promise = new Promise((resolve, reject) => {
- this.#resolve = resolve;
- this.#reject = reject;
- });
- // Since page navigation requires us to re-install the pageScript, we should track
- // timeout on our end.
- if (options.timeout) {
- const timeoutError = new TimeoutError(
- `waiting for ${options.title} failed: timeout ${options.timeout}ms exceeded`
- );
- this.#timeoutTimer = setTimeout(() => {
- return this.terminate(timeoutError);
- }, options.timeout);
- }
- this.rerun();
- }
-
- terminate(error: Error): void {
- this.#terminated = true;
- this.#reject(error);
- this.#cleanup();
- }
-
- async rerun(): Promise {
- const runCount = ++this.#runCount;
- let success: JSHandle | null = null;
- let error: Error | null = null;
- const context = await this.#isolatedWorld.executionContext();
- if (this.#terminated || runCount !== this.#runCount) {
- return;
- }
- if (this.#binding) {
- await this.#isolatedWorld._addBindingToContext(
- context,
- this.#binding.name
- );
- }
- if (this.#terminated || runCount !== this.#runCount) {
- return;
- }
- try {
- success = await context.evaluateHandle(
- waitForPredicatePageFunction,
- this.#root || null,
- this.#predicateBody,
- this.#predicateAcceptsContextElement,
- this.#polling,
- this.#timeout,
- ...this.#args
- );
- } catch (error_) {
- error = error_ as Error;
- }
-
- if (this.#terminated || runCount !== this.#runCount) {
- if (success) {
- await success.dispose();
- }
- return;
- }
-
- // Ignore timeouts in pageScript - we track timeouts ourselves.
- // If the frame's execution context has already changed, `frame.evaluate` will
- // throw an error - ignore this predicate run altogether.
- if (
- !error &&
- (await this.#isolatedWorld
- .evaluate(s => {
- return !s;
- }, success)
- .catch(() => {
- return true;
- }))
- ) {
- if (!success) {
- throw new Error('Assertion: result handle is not available');
- }
- await success.dispose();
- return;
- }
- if (error) {
- if (error.message.includes('TypeError: binding is not a function')) {
- return this.rerun();
- }
- // When frame is detached the task should have been terminated by the IsolatedWorld.
- // This can fail if we were adding this task while the frame was detached,
- // so we terminate here instead.
- if (
- error.message.includes(
- 'Execution context is not available in detached frame'
- )
- ) {
- this.terminate(
- new Error('waitForFunction failed: frame got detached.')
- );
- return;
- }
-
- // When the page is navigated, the promise is rejected.
- // We will try again in the new execution context.
- if (error.message.includes('Execution context was destroyed')) {
- return;
- }
-
- // We could have tried to evaluate in a context which was already
- // destroyed.
- if (error.message.includes('Cannot find context with specified id')) {
- return;
- }
-
- this.#reject(error);
- } else {
- if (!success) {
- throw new Error('Assertion: result handle is not available');
- }
- this.#resolve(success);
- }
- this.#cleanup();
- }
-
- #cleanup(): void {
- this.#timeoutTimer !== undefined && clearTimeout(this.#timeoutTimer);
- this.#isolatedWorld._waitTasks.delete(this);
- }
-}
-
-async function waitForPredicatePageFunction(
- root: Node | null,
- predicateBody: string,
- predicateAcceptsContextElement: boolean,
- polling: 'raf' | 'mutation' | number,
- timeout: number,
- ...args: unknown[]
-): Promise {
- root = root || document;
- const predicate = new Function('...args', predicateBody);
- let timedOut = false;
- if (timeout) {
- setTimeout(() => {
- return (timedOut = true);
- }, timeout);
- }
- switch (polling) {
- case 'raf':
- return await pollRaf();
- case 'mutation':
- return await pollMutation();
- default:
- return await pollInterval(polling);
- }
-
- async function pollMutation(): Promise {
- const success = predicateAcceptsContextElement
- ? await predicate(root, ...args)
- : await predicate(...args);
- if (success) {
- return Promise.resolve(success);
- }
-
- let fulfill = (_?: unknown) => {};
- const result = new Promise(x => {
- return (fulfill = x);
- });
- const observer = new MutationObserver(async () => {
- if (timedOut) {
- observer.disconnect();
- fulfill();
- }
- const success = predicateAcceptsContextElement
- ? await predicate(root, ...args)
- : await predicate(...args);
- if (success) {
- observer.disconnect();
- fulfill(success);
- }
- });
- if (!root) {
- throw new Error('Root element is not found.');
- }
- observer.observe(root, {
- childList: true,
- subtree: true,
- attributes: true,
- });
- return result;
- }
-
- async function pollRaf(): Promise {
- let fulfill = (_?: unknown): void => {};
- const result = new Promise(x => {
- return (fulfill = x);
- });
- await onRaf();
- return result;
-
- async function onRaf(): Promise {
- if (timedOut) {
- fulfill();
- return;
- }
- const success = predicateAcceptsContextElement
- ? await predicate(root, ...args)
- : await predicate(...args);
- if (success) {
- fulfill(success);
- } else {
- requestAnimationFrame(onRaf);
- }
- }
- }
-
- async function pollInterval(pollInterval: number): Promise {
- let fulfill = (_?: unknown): void => {};
- const result = new Promise(x => {
- return (fulfill = x);
- });
- await onTimeout();
- return result;
-
- async function onTimeout(): Promise {
- if (timedOut) {
- fulfill();
- return;
- }
- const success = predicateAcceptsContextElement
- ? await predicate(root, ...args)
- : await predicate(...args);
- if (success) {
- fulfill(success);
- } else {
- setTimeout(onTimeout, pollInterval);
- }
- }
- }
-}
diff --git a/remote/test/puppeteer/src/common/LazyArg.ts b/remote/test/puppeteer/src/common/LazyArg.ts
new file mode 100644
index 000000000000..c182663a2958
--- /dev/null
+++ b/remote/test/puppeteer/src/common/LazyArg.ts
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * @internal
+ */
+export class LazyArg {
+ #get: () => Promise;
+ constructor(get: () => Promise) {
+ this.#get = get;
+ }
+
+ get(): Promise {
+ return this.#get();
+ }
+}
diff --git a/remote/test/puppeteer/src/common/NetworkEventManager.ts b/remote/test/puppeteer/src/common/NetworkEventManager.ts
index 0b0e373fc121..ecea2c2ebd76 100644
--- a/remote/test/puppeteer/src/common/NetworkEventManager.ts
+++ b/remote/test/puppeteer/src/common/NetworkEventManager.ts
@@ -1,3 +1,19 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
import {Protocol} from 'devtools-protocol';
import {HTTPRequest} from './HTTPRequest.js';
diff --git a/remote/test/puppeteer/src/common/NetworkManager.ts b/remote/test/puppeteer/src/common/NetworkManager.ts
index da1a0d19f18b..92deee72ccc5 100644
--- a/remote/test/puppeteer/src/common/NetworkManager.ts
+++ b/remote/test/puppeteer/src/common/NetworkManager.ts
@@ -15,7 +15,6 @@
*/
import {Protocol} from 'devtools-protocol';
-import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
import {assert} from '../util/assert.js';
import {EventEmitter} from './EventEmitter.js';
import {Frame} from './Frame.js';
@@ -25,6 +24,7 @@ import {FetchRequestId, NetworkEventManager} from './NetworkEventManager.js';
import {debugError, isString} from './util.js';
import {DeferredPromise} from '../util/DeferredPromise.js';
import {createDebuggableDeferredPromise} from '../util/DebuggableDeferredPromise.js';
+import {CDPSession} from './Connection.js';
/**
* @public
@@ -66,13 +66,6 @@ export const NetworkManagerEmittedEvents = {
RequestFinished: Symbol('NetworkManager.RequestFinished'),
} as const;
-interface CDPSession extends EventEmitter {
- send(
- method: T,
- ...paramArgs: ProtocolMapping.Commands[T]['paramsType']
- ): Promise;
-}
-
interface FrameManager {
frame(frameId: string): Frame | null;
}
diff --git a/remote/test/puppeteer/src/common/Page.ts b/remote/test/puppeteer/src/common/Page.ts
index ab3a2a2c824a..de3ea001c678 100644
--- a/remote/test/puppeteer/src/common/Page.ts
+++ b/remote/test/puppeteer/src/common/Page.ts
@@ -23,8 +23,12 @@ import {
} from '../util/DeferredPromise.js';
import {isErrorLike} from '../util/ErrorLike.js';
import {Accessibility} from './Accessibility.js';
-import {Browser, BrowserContext} from './Browser.js';
-import {CDPSession, CDPSessionEmittedEvents} from './Connection.js';
+import type {Browser, BrowserContext} from '../api/Browser.js';
+import {
+ CDPSession,
+ CDPSessionEmittedEvents,
+ isTargetClosedError,
+} from './Connection.js';
import {ConsoleMessage, ConsoleMessageType} from './ConsoleMessage.js';
import {Coverage} from './Coverage.js';
import {Dialog} from './Dialog.js';
@@ -36,6 +40,7 @@ import {
Frame,
FrameAddScriptTagOptions,
FrameAddStyleTagOptions,
+ FrameWaitForFunctionOptions,
} from './Frame.js';
import {FrameManager, FrameManagerEmittedEvents} from './FrameManager.js';
import {HTTPRequest} from './HTTPRequest.js';
@@ -470,7 +475,15 @@ export class Page extends EventEmitter {
);
await page.#initialize();
if (defaultViewport) {
- await page.setViewport(defaultViewport);
+ try {
+ await page.setViewport(defaultViewport);
+ } catch (err) {
+ if (isErrorLike(err) && isTargetClosedError(err)) {
+ debugError(err);
+ } else {
+ throw err;
+ }
+ }
}
return page;
}
@@ -645,11 +658,19 @@ export class Page extends EventEmitter {
};
async #initialize(): Promise {
- await Promise.all([
- this.#frameManager.initialize(this.#target._targetId),
- this.#client.send('Performance.enable'),
- this.#client.send('Log.enable'),
- ]);
+ try {
+ await Promise.all([
+ this.#frameManager.initialize(),
+ this.#client.send('Performance.enable'),
+ this.#client.send('Log.enable'),
+ ]);
+ } catch (err) {
+ if (isErrorLike(err) && isTargetClosedError(err)) {
+ debugError(err);
+ } else {
+ throw err;
+ }
+ }
}
async #onFileChooser(
@@ -3544,32 +3565,14 @@ export class Page extends EventEmitter {
* ```
*
* @param pageFunction - Function to be evaluated in browser context
- * @param options - Optional waiting parameters
- *
- * - `polling` - An interval at which the `pageFunction` is executed, defaults
- * to `raf`. If `polling` is a number, then it is treated as an interval in
- * milliseconds at which the function would be executed. If polling is a
- * string, then it can be one of the following values:
- * - `raf` - to constantly execute `pageFunction` in
- * `requestAnimationFrame` callback. This is the tightest polling mode
- * which is suitable to observe styling changes.
- * - `mutation`- to execute pageFunction on every DOM mutation.
- * - `timeout` - maximum time to wait for in milliseconds. Defaults to `30000`
- * (30 seconds). Pass `0` to disable timeout. The default value can be
- * changed by using the {@link Page.setDefaultTimeout} method.
- * @param args - Arguments to pass to `pageFunction`
- * @returns A `Promise` which resolves to a JSHandle/ElementHandle of the the
- * `pageFunction`'s return value.
+ * @param options - Options for configuring waiting behavior.
*/
waitForFunction<
Params extends unknown[],
Func extends EvaluateFunc = EvaluateFunc
>(
pageFunction: Func | string,
- options: {
- timeout?: number;
- polling?: string | number;
- } = {},
+ options: FrameWaitForFunctionOptions = {},
...args: Params
): Promise>>> {
return this.mainFrame().waitForFunction(pageFunction, options, ...args);
diff --git a/remote/test/puppeteer/src/common/Puppeteer.ts b/remote/test/puppeteer/src/common/Puppeteer.ts
index 579f68a937a0..8fab36e1c5b0 100644
--- a/remote/test/puppeteer/src/common/Puppeteer.ts
+++ b/remote/test/puppeteer/src/common/Puppeteer.ts
@@ -13,8 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {Browser} from './Browser.js';
-import {BrowserConnectOptions, _connectToBrowser} from './BrowserConnector.js';
+import {Browser} from '../api/Browser.js';
+import {
+ BrowserConnectOptions,
+ _connectToCDPBrowser,
+} from './BrowserConnector.js';
import {ConnectionTransport} from './ConnectionTransport.js';
import {devices} from './DeviceDescriptors.js';
import {errors} from './Errors.js';
@@ -54,7 +57,13 @@ export interface ConnectOptions extends BrowserConnectOptions {
* @public
*/
export class Puppeteer {
+ /**
+ * @internal
+ */
protected _isPuppeteerCore: boolean;
+ /**
+ * @internal
+ */
protected _changedProduct = false;
/**
@@ -75,7 +84,7 @@ export class Puppeteer {
* @returns Promise which resolves to browser instance.
*/
connect(options: ConnectOptions): Promise {
- return _connectToBrowser(options);
+ return _connectToCDPBrowser(options);
}
/**
diff --git a/remote/test/puppeteer/src/common/QueryHandler.ts b/remote/test/puppeteer/src/common/QueryHandler.ts
index 5fd360e06d5d..1aa48ff6ac40 100644
--- a/remote/test/puppeteer/src/common/QueryHandler.ts
+++ b/remote/test/puppeteer/src/common/QueryHandler.ts
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import PuppeteerUtil from '../injected/injected.js';
import {ariaHandler} from './AriaQueryHandler.js';
import {ElementHandle} from './ElementHandle.js';
import {Frame} from './Frame.js';
@@ -41,6 +42,28 @@ export interface CustomQueryHandler {
* @internal
*/
export interface InternalQueryHandler {
+ /**
+ * @returns A {@link Node} matching the given `selector` from {@link node}.
+ */
+ queryOne?: (
+ node: Node,
+ selector: string,
+ PuppeteerUtil: PuppeteerUtil
+ ) => Node | null;
+ /**
+ * @returns Some {@link Node}s matching the given `selector` from {@link node}.
+ */
+ queryAll?: (
+ node: Node,
+ selector: string,
+ PuppeteerUtil: PuppeteerUtil
+ ) => Node[];
+}
+
+/**
+ * @internal
+ */
+export interface PuppeteerQueryHandler {
/**
* Queries for a single node given a selector and {@link ElementHandle}.
*
@@ -71,15 +94,19 @@ export interface InternalQueryHandler {
) => Promise | null>;
}
-function internalizeCustomQueryHandler(
- handler: CustomQueryHandler
-): InternalQueryHandler {
- const internalHandler: InternalQueryHandler = {};
+function createPuppeteerQueryHandler(
+ handler: InternalQueryHandler
+): PuppeteerQueryHandler {
+ const internalHandler: PuppeteerQueryHandler = {};
if (handler.queryOne) {
const queryOne = handler.queryOne;
internalHandler.queryOne = async (element, selector) => {
- const jsHandle = await element.evaluateHandle(queryOne, selector);
+ const jsHandle = await element.evaluateHandle(
+ queryOne,
+ selector,
+ await element.executionContext()._world!.puppeteerUtil
+ );
const elementHandle = jsHandle.asElement();
if (elementHandle) {
return elementHandle;
@@ -121,7 +148,11 @@ function internalizeCustomQueryHandler(
if (handler.queryAll) {
const queryAll = handler.queryAll;
internalHandler.queryAll = async (element, selector) => {
- const jsHandle = await element.evaluateHandle(queryAll, selector);
+ const jsHandle = await element.evaluateHandle(
+ queryAll,
+ selector,
+ await element.executionContext()._world!.puppeteerUtil
+ );
const properties = await jsHandle.getProperties();
await jsHandle.dispose();
const result = [];
@@ -138,7 +169,7 @@ function internalizeCustomQueryHandler(
return internalHandler;
}
-const defaultHandler = internalizeCustomQueryHandler({
+const defaultHandler = createPuppeteerQueryHandler({
queryOne: (element, selector) => {
if (!('querySelector' in element)) {
throw new Error(
@@ -165,87 +196,35 @@ const defaultHandler = internalizeCustomQueryHandler({
},
});
-const pierceHandler = internalizeCustomQueryHandler({
- queryOne: (element, selector) => {
- let found: Node | null = null;
- const search = (root: Node) => {
- const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
- do {
- const currentNode = iter.currentNode as HTMLElement;
- if (currentNode.shadowRoot) {
- search(currentNode.shadowRoot);
- }
- if (currentNode instanceof ShadowRoot) {
- continue;
- }
- if (currentNode !== root && !found && currentNode.matches(selector)) {
- found = currentNode;
- }
- } while (!found && iter.nextNode());
- };
- if (element instanceof Document) {
- element = element.documentElement;
- }
- search(element);
- return found;
+const pierceHandler = createPuppeteerQueryHandler({
+ queryOne: (element, selector, {pierceQuerySelector}) => {
+ return pierceQuerySelector(element, selector);
},
-
- queryAll: (element, selector) => {
- const result: Node[] = [];
- const collect = (root: Node) => {
- const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
- do {
- const currentNode = iter.currentNode as HTMLElement;
- if (currentNode.shadowRoot) {
- collect(currentNode.shadowRoot);
- }
- if (currentNode instanceof ShadowRoot) {
- continue;
- }
- if (currentNode !== root && currentNode.matches(selector)) {
- result.push(currentNode);
- }
- } while (iter.nextNode());
- };
- if (element instanceof Document) {
- element = element.documentElement;
- }
- collect(element);
- return result;
+ queryAll: (element, selector, {pierceQuerySelectorAll}) => {
+ return pierceQuerySelectorAll(element, selector);
},
});
-const xpathHandler = internalizeCustomQueryHandler({
- queryOne: (element, selector) => {
- const doc = element.ownerDocument || document;
- const result = doc.evaluate(
- selector,
- element,
- null,
- XPathResult.FIRST_ORDERED_NODE_TYPE
- );
- return result.singleNodeValue;
+const xpathHandler = createPuppeteerQueryHandler({
+ queryOne: (element, selector, {xpathQuerySelector}) => {
+ return xpathQuerySelector(element, selector);
},
+ queryAll: (element, selector, {xpathQuerySelectorAll}) => {
+ return xpathQuerySelectorAll(element, selector);
+ },
+});
- queryAll: (element, selector) => {
- const doc = element.ownerDocument || document;
- const iterator = doc.evaluate(
- selector,
- element,
- null,
- XPathResult.ORDERED_NODE_ITERATOR_TYPE
- );
- const array: Node[] = [];
- let item;
- while ((item = iterator.iterateNext())) {
- array.push(item);
- }
- return array;
+const textQueryHandler = createPuppeteerQueryHandler({
+ queryOne: (element, selector, {textQuerySelector}) => {
+ return textQuerySelector(element, selector);
+ },
+ queryAll: (element, selector, {textQuerySelectorAll}) => {
+ return textQuerySelectorAll(element, selector);
},
});
interface RegisteredQueryHandler {
- handler: InternalQueryHandler;
+ handler: PuppeteerQueryHandler;
transformSelector?: (selector: string) => string;
}
@@ -253,6 +232,7 @@ const INTERNAL_QUERY_HANDLERS = new Map([
['aria', {handler: ariaHandler}],
['pierce', {handler: pierceHandler}],
['xpath', {handler: xpathHandler}],
+ ['text', {handler: textQueryHandler}],
]);
const QUERY_HANDLERS = new Map();
@@ -294,7 +274,7 @@ export function registerCustomQueryHandler(
throw new Error(`Custom query handler names may only contain [a-zA-Z]`);
}
- QUERY_HANDLERS.set(name, {handler: internalizeCustomQueryHandler(handler)});
+ QUERY_HANDLERS.set(name, {handler: createPuppeteerQueryHandler(handler)});
}
/**
@@ -331,7 +311,7 @@ const CUSTOM_QUERY_SEPARATORS = ['=', '/'];
*/
export function getQueryHandlerAndSelector(selector: string): {
updatedSelector: string;
- queryHandler: InternalQueryHandler;
+ queryHandler: PuppeteerQueryHandler;
} {
for (const handlerMap of [QUERY_HANDLERS, INTERNAL_QUERY_HANDLERS]) {
for (const [
diff --git a/remote/test/puppeteer/src/common/Target.ts b/remote/test/puppeteer/src/common/Target.ts
index 5744834f1b35..b1611d24badc 100644
--- a/remote/test/puppeteer/src/common/Target.ts
+++ b/remote/test/puppeteer/src/common/Target.ts
@@ -17,7 +17,11 @@
import {Page, PageEmittedEvents} from './Page.js';
import {WebWorker} from './WebWorker.js';
import {CDPSession} from './Connection.js';
-import {Browser, BrowserContext, IsPageTargetCallback} from './Browser.js';
+import type {
+ Browser,
+ BrowserContext,
+ IsPageTargetCallback,
+} from '../api/Browser.js';
import {Viewport} from './PuppeteerViewport.js';
import {Protocol} from 'devtools-protocol';
import {TaskQueue} from './TaskQueue.js';
diff --git a/remote/test/puppeteer/src/common/WaitTask.ts b/remote/test/puppeteer/src/common/WaitTask.ts
new file mode 100644
index 000000000000..4a5370e515ba
--- /dev/null
+++ b/remote/test/puppeteer/src/common/WaitTask.ts
@@ -0,0 +1,257 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+import type {Poller} from '../injected/Poller.js';
+import {createDeferredPromise} from '../util/DeferredPromise.js';
+import {ElementHandle} from './ElementHandle.js';
+import {TimeoutError} from './Errors.js';
+import {IsolatedWorld} from './IsolatedWorld.js';
+import {JSHandle} from './JSHandle.js';
+import {HandleFor} from './types.js';
+
+/**
+ * @internal
+ */
+export interface WaitTaskOptions {
+ bindings?: Set<(...args: never[]) => unknown>;
+ polling: 'raf' | 'mutation' | number;
+ root?: ElementHandle;
+ timeout: number;
+}
+
+/**
+ * @internal
+ */
+export class WaitTask {
+ #world: IsolatedWorld;
+ #bindings: Set<(...args: never[]) => unknown>;
+ #polling: 'raf' | 'mutation' | number;
+ #root?: ElementHandle;
+
+ #fn: string;
+ #args: unknown[];
+
+ #timeout?: NodeJS.Timeout;
+
+ #result = createDeferredPromise>();
+
+ #poller?: JSHandle>;
+
+ constructor(
+ world: IsolatedWorld,
+ options: WaitTaskOptions,
+ fn: ((...args: unknown[]) => Promise) | string,
+ ...args: unknown[]
+ ) {
+ this.#world = world;
+ this.#bindings = options.bindings ?? new Set();
+ this.#polling = options.polling;
+ this.#root = options.root;
+
+ switch (typeof fn) {
+ case 'string':
+ this.#fn = `() => {return (${fn});}`;
+ break;
+ default:
+ this.#fn = fn.toString();
+ break;
+ }
+ this.#args = args;
+
+ this.#world.taskManager.add(this);
+
+ if (options.timeout) {
+ this.#timeout = setTimeout(() => {
+ this.terminate(
+ new TimeoutError(`Waiting failed: ${options.timeout}ms exceeded`)
+ );
+ }, options.timeout);
+ }
+
+ if (this.#bindings.size !== 0) {
+ for (const fn of this.#bindings) {
+ this.#world._boundFunctions.set(fn.name, fn);
+ }
+ }
+
+ this.rerun();
+ }
+
+ get result(): Promise> {
+ return this.#result;
+ }
+
+ async rerun(): Promise {
+ try {
+ if (this.#bindings.size !== 0) {
+ const context = await this.#world.executionContext();
+ await Promise.all(
+ [...this.#bindings].map(async ({name}) => {
+ return await this.#world._addBindingToContext(context, name);
+ })
+ );
+ }
+
+ switch (this.#polling) {
+ case 'raf':
+ this.#poller = await this.#world.evaluateHandle(
+ ({RAFPoller, createFunction}, fn, ...args) => {
+ const fun = createFunction(fn);
+ return new RAFPoller(() => {
+ return fun(...args) as Promise;
+ });
+ },
+ await this.#world.puppeteerUtil,
+ this.#fn,
+ ...this.#args
+ );
+ break;
+ case 'mutation':
+ this.#poller = await this.#world.evaluateHandle(
+ ({MutationPoller, createFunction}, root, fn, ...args) => {
+ const fun = createFunction(fn);
+ return new MutationPoller(() => {
+ return fun(...args) as Promise;
+ }, root || document);
+ },
+ await this.#world.puppeteerUtil,
+ this.#root,
+ this.#fn,
+ ...this.#args
+ );
+ break;
+ default:
+ this.#poller = await this.#world.evaluateHandle(
+ ({IntervalPoller, createFunction}, ms, fn, ...args) => {
+ const fun = createFunction(fn);
+ return new IntervalPoller(() => {
+ return fun(...args) as Promise;
+ }, ms);
+ },
+ await this.#world.puppeteerUtil,
+ this.#polling,
+ this.#fn,
+ ...this.#args
+ );
+ break;
+ }
+
+ await this.#poller.evaluate(poller => {
+ poller.start();
+ });
+
+ const result = await this.#poller.evaluateHandle(poller => {
+ return poller.result();
+ });
+ this.#result.resolve(result);
+
+ await this.terminate();
+ } catch (error) {
+ const badError = this.getBadError(error);
+ if (badError) {
+ await this.terminate(badError);
+ }
+ }
+ }
+
+ async terminate(error?: unknown): Promise {
+ this.#world.taskManager.delete(this);
+
+ if (this.#timeout) {
+ clearTimeout(this.#timeout);
+ }
+
+ if (error && !this.#result.finished()) {
+ this.#result.reject(error);
+ }
+
+ if (this.#poller) {
+ try {
+ await this.#poller.evaluateHandle(async poller => {
+ await poller.stop();
+ });
+ if (this.#poller) {
+ await this.#poller.dispose();
+ this.#poller = undefined;
+ }
+ } catch {
+ // Ignore errors since they most likely come from low-level cleanup.
+ }
+ }
+ }
+
+ /**
+ * Not all errors lead to termination. They usually imply we need to rerun the task.
+ */
+ getBadError(error: unknown): unknown {
+ if (error instanceof Error) {
+ // When frame is detached the task should have been terminated by the IsolatedWorld.
+ // This can fail if we were adding this task while the frame was detached,
+ // so we terminate here instead.
+ if (
+ error.message.includes(
+ 'Execution context is not available in detached frame'
+ )
+ ) {
+ return new Error('Waiting failed: Frame detached');
+ }
+
+ // When the page is navigated, the promise is rejected.
+ // We will try again in the new execution context.
+ if (error.message.includes('Execution context was destroyed')) {
+ return;
+ }
+
+ // We could have tried to evaluate in a context which was already
+ // destroyed.
+ if (error.message.includes('Cannot find context with specified id')) {
+ return;
+ }
+ }
+
+ return error;
+ }
+}
+
+/**
+ * @internal
+ */
+export class TaskManager {
+ #tasks: Set = new Set();
+
+ add(task: WaitTask): void {
+ this.#tasks.add(task);
+ }
+
+ delete(task: WaitTask): void {
+ this.#tasks.delete(task);
+ }
+
+ terminateAll(error?: Error): void {
+ for (const task of this.#tasks) {
+ task.terminate(error);
+ }
+ this.#tasks.clear();
+ }
+
+ async rerunAll(): Promise {
+ await Promise.all(
+ [...this.#tasks].map(task => {
+ return task.rerun();
+ })
+ );
+ }
+}
diff --git a/remote/test/puppeteer/src/common/bidi/Browser.ts b/remote/test/puppeteer/src/common/bidi/Browser.ts
new file mode 100644
index 000000000000..c94319f2316a
--- /dev/null
+++ b/remote/test/puppeteer/src/common/bidi/Browser.ts
@@ -0,0 +1,52 @@
+import {
+ Browser as BrowserBase,
+ BrowserCloseCallback,
+} from '../../api/Browser.js';
+import {Connection} from './Connection.js';
+import {ChildProcess} from 'child_process';
+
+/**
+ * @internal
+ */
+export class Browser extends BrowserBase {
+ /**
+ * @internal
+ */
+ static async create(opts: Options): Promise {
+ // TODO: await until the connection is established.
+ return new Browser(opts);
+ }
+
+ #process?: ChildProcess;
+ #closeCallback?: BrowserCloseCallback;
+ #connection: Connection;
+
+ /**
+ * @internal
+ */
+ constructor(opts: Options) {
+ super();
+ this.#process = opts.process;
+ this.#closeCallback = opts.closeCallback;
+ this.#connection = opts.connection;
+ }
+
+ override async close(): Promise {
+ await this.#closeCallback?.call(null);
+ this.#connection.dispose();
+ }
+
+ override isConnected(): boolean {
+ return !this.#connection.closed;
+ }
+
+ override process(): ChildProcess | null {
+ return this.#process ?? null;
+ }
+}
+
+interface Options {
+ process?: ChildProcess;
+ closeCallback?: BrowserCloseCallback;
+ connection: Connection;
+}
diff --git a/remote/test/puppeteer/src/common/bidi/Connection.ts b/remote/test/puppeteer/src/common/bidi/Connection.ts
new file mode 100644
index 000000000000..0f9de01a7b45
--- /dev/null
+++ b/remote/test/puppeteer/src/common/bidi/Connection.ts
@@ -0,0 +1,167 @@
+/**
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+import {debug} from '../Debug.js';
+const debugProtocolSend = debug('puppeteer:webDriverBiDi:SEND â–º');
+const debugProtocolReceive = debug('puppeteer:webDriverBiDi:RECV â—€');
+
+import {ConnectionTransport} from '../ConnectionTransport.js';
+import {EventEmitter} from '../EventEmitter.js';
+import {ProtocolError} from '../Errors.js';
+import {ConnectionCallback} from '../Connection.js';
+
+interface Command {
+ id: number;
+ method: string;
+ params: object;
+}
+
+interface CommandResponse {
+ id: number;
+ result: object;
+}
+
+interface ErrorResponse {
+ id: number;
+ error: string;
+ message: string;
+ stacktrace?: string;
+}
+
+interface Event {
+ method: string;
+ params: object;
+}
+
+/**
+ * @internal
+ */
+export class Connection extends EventEmitter {
+ #transport: ConnectionTransport;
+ #delay: number;
+ #lastId = 0;
+ #closed = false;
+ #callbacks: Map = new Map();
+
+ constructor(transport: ConnectionTransport, delay = 0) {
+ super();
+ this.#delay = delay;
+
+ this.#transport = transport;
+ this.#transport.onmessage = this.onMessage.bind(this);
+ this.#transport.onclose = this.#onClose.bind(this);
+ }
+
+ get closed(): boolean {
+ return this.#closed;
+ }
+
+ send(method: string, params: object): Promise {
+ const id = ++this.#lastId;
+ const stringifiedMessage = JSON.stringify({
+ id,
+ method,
+ params,
+ } as Command);
+ debugProtocolSend(stringifiedMessage);
+ this.#transport.send(stringifiedMessage);
+ return new Promise((resolve, reject) => {
+ this.#callbacks.set(id, {
+ resolve,
+ reject,
+ error: new ProtocolError(),
+ method,
+ });
+ });
+ }
+
+ /**
+ * @internal
+ */
+ protected async onMessage(message: string): Promise {
+ if (this.#delay) {
+ await new Promise(f => {
+ return setTimeout(f, this.#delay);
+ });
+ }
+ debugProtocolReceive(message);
+ const object = JSON.parse(message) as
+ | Event
+ | ErrorResponse
+ | CommandResponse;
+ if ('id' in object) {
+ const callback = this.#callbacks.get(object.id);
+ // Callbacks could be all rejected if someone has called `.dispose()`.
+ if (callback) {
+ this.#callbacks.delete(object.id);
+ if ('error' in object) {
+ callback.reject(
+ createProtocolError(callback.error, callback.method, object)
+ );
+ } else {
+ callback.resolve(object.result);
+ }
+ }
+ } else {
+ this.emit(object.method, object.params);
+ }
+ }
+
+ #onClose(): void {
+ if (this.#closed) {
+ return;
+ }
+ this.#closed = true;
+ this.#transport.onmessage = undefined;
+ this.#transport.onclose = undefined;
+ for (const callback of this.#callbacks.values()) {
+ callback.reject(
+ rewriteError(
+ callback.error,
+ `Protocol error (${callback.method}): Connection closed.`
+ )
+ );
+ }
+ this.#callbacks.clear();
+ }
+
+ dispose(): void {
+ this.#onClose();
+ this.#transport.close();
+ }
+}
+
+function rewriteError(
+ error: ProtocolError,
+ message: string,
+ originalMessage?: string
+): Error {
+ error.message = message;
+ error.originalMessage = originalMessage ?? error.originalMessage;
+ return error;
+}
+
+function createProtocolError(
+ error: ProtocolError,
+ method: string,
+ object: ErrorResponse
+): Error {
+ let message = `Protocol error (${method}): ${object.error} ${object.message}`;
+ if (object.stacktrace) {
+ message += ` ${object.stacktrace}`;
+ }
+ return rewriteError(error, message, object.message);
+}
diff --git a/remote/test/puppeteer/src/common/types.ts b/remote/test/puppeteer/src/common/types.ts
index 8af6f23f2910..42f984e18f03 100644
--- a/remote/test/puppeteer/src/common/types.ts
+++ b/remote/test/puppeteer/src/common/types.ts
@@ -16,6 +16,7 @@
import {JSHandle} from './JSHandle.js';
import {ElementHandle} from './ElementHandle.js';
+import {LazyArg} from './LazyArg.js';
/**
* @public
@@ -36,11 +37,17 @@ export type HandleOr = HandleFor | JSHandle | T;
* @public
*/
export type FlattenHandle = T extends HandleOr ? U : never;
+
+/**
+ * @internal
+ */
+export type FlattenLazyArg = T extends LazyArg ? U : T;
+
/**
* @public
*/
export type InnerParams = {
- [K in keyof T]: FlattenHandle;
+ [K in keyof T]: FlattenHandle>>;
};
/**
diff --git a/remote/test/puppeteer/src/common/util.ts b/remote/test/puppeteer/src/common/util.ts
index 0b280292ee98..a39e914f90a4 100644
--- a/remote/test/puppeteer/src/common/util.ts
+++ b/remote/test/puppeteer/src/common/util.ts
@@ -249,28 +249,26 @@ export function evaluationString(
* @internal
*/
export function pageBindingInitString(type: string, name: string): string {
- function addPageBinding(type: string, bindingName: string): void {
- /* Cast window to any here as we're about to add properties to it
- * via win[bindingName] which TypeScript doesn't like.
- */
- const win = window as any;
- const binding = win[bindingName];
+ function addPageBinding(type: string, name: string): void {
+ // This is the CDP binding.
+ // @ts-expect-error: In a different context.
+ const callCDP = self[name];
- win[bindingName] = (...args: unknown[]): Promise => {
- const me = (window as any)[bindingName];
- let callbacks = me.callbacks;
- if (!callbacks) {
- callbacks = new Map();
- me.callbacks = callbacks;
- }
- const seq = (me.lastSeq || 0) + 1;
- me.lastSeq = seq;
- const promise = new Promise((resolve, reject) => {
- return callbacks.set(seq, {resolve, reject});
- });
- binding(JSON.stringify({type, name: bindingName, seq, args}));
- return promise;
- };
+ // We replace the CDP binding with a Puppeteer binding.
+ Object.assign(self, {
+ [name](...args: unknown[]): Promise {
+ // This is the Puppeteer binding.
+ // @ts-expect-error: In a different context.
+ const callPuppeteer = self[name];
+ callPuppeteer.callbacks ??= new Map();
+ const seq = (callPuppeteer.lastSeq ?? 0) + 1;
+ callPuppeteer.lastSeq = seq;
+ callCDP(JSON.stringify({type, name, seq, args}));
+ return new Promise((resolve, reject) => {
+ callPuppeteer.callbacks.set(seq, {resolve, reject});
+ });
+ },
+ });
}
return evaluationString(addPageBinding, type, name);
}
@@ -328,50 +326,6 @@ export function pageBindingDeliverErrorValueString(
return evaluationString(deliverErrorValue, name, seq, value);
}
-/**
- * @internal
- */
-export function makePredicateString(
- predicate: Function,
- predicateQueryHandler: Function
-): string {
- function checkWaitForOptions(
- node: Node | null,
- waitForVisible: boolean,
- waitForHidden: boolean
- ): Node | null | boolean {
- if (!node) {
- return waitForHidden;
- }
- if (!waitForVisible && !waitForHidden) {
- return node;
- }
- const element =
- node.nodeType === Node.TEXT_NODE
- ? (node.parentElement as Element)
- : (node as Element);
-
- const style = window.getComputedStyle(element);
- const isVisible =
- style && style.visibility !== 'hidden' && hasVisibleBoundingBox();
- const success =
- waitForVisible === isVisible || waitForHidden === !isVisible;
- return success ? node : null;
-
- function hasVisibleBoundingBox(): boolean {
- const rect = element.getBoundingClientRect();
- return !!(rect.top || rect.bottom || rect.width || rect.height);
- }
- }
-
- return `
- (() => {
- const predicateQueryHandler = ${predicateQueryHandler};
- const checkWaitForOptions = ${checkWaitForOptions};
- return (${predicate})(...args)
- })() `;
-}
-
/**
* @internal
*/
diff --git a/remote/test/puppeteer/src/compat.d.ts b/remote/test/puppeteer/src/compat.d.ts
index 6c330dc078f4..9d41b661e96e 100644
--- a/remote/test/puppeteer/src/compat.d.ts
+++ b/remote/test/puppeteer/src/compat.d.ts
@@ -1,3 +1,19 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
declare const puppeteerDirname: string;
export {puppeteerDirname};
diff --git a/remote/test/puppeteer/src/constants.ts b/remote/test/puppeteer/src/constants.ts
index d8e562ccec5f..36e64652f998 100644
--- a/remote/test/puppeteer/src/constants.ts
+++ b/remote/test/puppeteer/src/constants.ts
@@ -1,3 +1,19 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
import {dirname} from 'path';
import {puppeteerDirname} from './compat.js';
diff --git a/remote/test/puppeteer/src/generated/version.ts b/remote/test/puppeteer/src/generated/version.ts
index 6b5116dfd5a9..a36f55772035 100644
--- a/remote/test/puppeteer/src/generated/version.ts
+++ b/remote/test/puppeteer/src/generated/version.ts
@@ -1,4 +1,4 @@
/**
* @internal
*/
-export const packageVersion = '17.1.2';
+export const packageVersion = '18.0.0';
diff --git a/remote/test/puppeteer/src/injected/PierceQuerySelector.ts b/remote/test/puppeteer/src/injected/PierceQuerySelector.ts
new file mode 100644
index 000000000000..7909b1f1ba30
--- /dev/null
+++ b/remote/test/puppeteer/src/injected/PierceQuerySelector.ts
@@ -0,0 +1,67 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// 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.
+
+export const pierceQuerySelector = (
+ root: Node,
+ selector: string
+): Element | null => {
+ let found: Node | null = null;
+ const search = (root: Node) => {
+ const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
+ do {
+ const currentNode = iter.currentNode as Element;
+ if (currentNode.shadowRoot) {
+ search(currentNode.shadowRoot);
+ }
+ if (currentNode instanceof ShadowRoot) {
+ continue;
+ }
+ if (currentNode !== root && !found && currentNode.matches(selector)) {
+ found = currentNode;
+ }
+ } while (!found && iter.nextNode());
+ };
+ if (root instanceof Document) {
+ root = root.documentElement;
+ }
+ search(root);
+ return found;
+};
+
+export const pierceQuerySelectorAll = (
+ element: Node,
+ selector: string
+): Element[] => {
+ const result: Element[] = [];
+ const collect = (root: Node) => {
+ const iter = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
+ do {
+ const currentNode = iter.currentNode as Element;
+ if (currentNode.shadowRoot) {
+ collect(currentNode.shadowRoot);
+ }
+ if (currentNode instanceof ShadowRoot) {
+ continue;
+ }
+ if (currentNode !== root && currentNode.matches(selector)) {
+ result.push(currentNode);
+ }
+ } while (iter.nextNode());
+ };
+ if (element instanceof Document) {
+ element = element.documentElement;
+ }
+ collect(element);
+ return result;
+};
diff --git a/remote/test/puppeteer/src/injected/Poller.ts b/remote/test/puppeteer/src/injected/Poller.ts
index c6748ebbc49a..73d0f1a02bb2 100644
--- a/remote/test/puppeteer/src/injected/Poller.ts
+++ b/remote/test/puppeteer/src/injected/Poller.ts
@@ -1,15 +1,37 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+import {assert} from '../util/assert.js';
import {
createDeferredPromise,
DeferredPromise,
} from '../util/DeferredPromise.js';
-import {assert} from '../util/assert.js';
-interface Poller {
- start(): Promise;
+/**
+ * @internal
+ */
+export interface Poller {
+ start(): Promise;
stop(): Promise;
result(): Promise;
}
+/**
+ * @internal
+ */
export class MutationPoller implements Poller {
#fn: () => Promise;
@@ -22,12 +44,12 @@ export class MutationPoller implements Poller {
this.#root = root;
}
- async start(): Promise {
+ async start(): Promise {
const promise = (this.#promise = createDeferredPromise());
const result = await this.#fn();
if (result) {
promise.resolve(result);
- return result;
+ return;
}
this.#observer = new MutationObserver(async () => {
@@ -43,8 +65,6 @@ export class MutationPoller implements Poller {
subtree: true,
attributes: true,
});
-
- return this.#promise;
}
async stop(): Promise {
@@ -54,6 +74,7 @@ export class MutationPoller implements Poller {
}
if (this.#observer) {
this.#observer.disconnect();
+ this.#observer = undefined;
}
}
@@ -70,12 +91,12 @@ export class RAFPoller implements Poller {
this.#fn = fn;
}
- async start(): Promise {
+ async start(): Promise {
const promise = (this.#promise = createDeferredPromise());
const result = await this.#fn();
if (result) {
promise.resolve(result);
- return result;
+ return;
}
const poll = async () => {
@@ -91,8 +112,6 @@ export class RAFPoller implements Poller {
await this.stop();
};
window.requestAnimationFrame(poll);
-
- return this.#promise;
}
async stop(): Promise {
@@ -119,12 +138,12 @@ export class IntervalPoller implements Poller {
this.#ms = ms;
}
- async start(): Promise {
+ async start(): Promise {
const promise = (this.#promise = createDeferredPromise());
const result = await this.#fn();
if (result) {
promise.resolve(result);
- return result;
+ return;
}
this.#interval = setInterval(async () => {
@@ -135,8 +154,6 @@ export class IntervalPoller implements Poller {
promise.resolve(result);
await this.stop();
}, this.#ms);
-
- return this.#promise;
}
async stop(): Promise {
@@ -146,6 +163,7 @@ export class IntervalPoller implements Poller {
}
if (this.#interval) {
clearInterval(this.#interval);
+ this.#interval = undefined;
}
}
diff --git a/remote/test/puppeteer/src/injected/TextContent.ts b/remote/test/puppeteer/src/injected/TextContent.ts
new file mode 100644
index 000000000000..b01fcd4c0ca1
--- /dev/null
+++ b/remote/test/puppeteer/src/injected/TextContent.ts
@@ -0,0 +1,153 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+interface NonTrivialValueNode extends Node {
+ value: string;
+}
+
+const TRIVIAL_VALUE_INPUT_TYPES = new Set(['checkbox', 'image', 'radio']);
+
+/**
+ * Determines if the node has a non-trivial value property.
+ */
+const isNonTrivialValueNode = (node: Node): node is NonTrivialValueNode => {
+ if (node instanceof HTMLSelectElement) {
+ return true;
+ }
+ if (node instanceof HTMLTextAreaElement) {
+ return true;
+ }
+ if (
+ node instanceof HTMLInputElement &&
+ !TRIVIAL_VALUE_INPUT_TYPES.has(node.type)
+ ) {
+ return true;
+ }
+ return false;
+};
+
+const UNSUITABLE_NODE_NAMES = new Set(['SCRIPT', 'STYLE']);
+
+/**
+ * Determines whether a given node is suitable for text matching.
+ *
+ * @internal
+ */
+export const isSuitableNodeForTextMatching = (node: Node): boolean => {
+ return (
+ !UNSUITABLE_NODE_NAMES.has(node.nodeName) && !document.head?.contains(node)
+ );
+};
+
+/**
+ * @internal
+ */
+export type TextContent = {
+ // Contains the full text of the node.
+ full: string;
+ // Contains the text immediately beneath the node.
+ immediate: string[];
+};
+
+/**
+ * Maps {@link Node}s to their computed {@link TextContent}.
+ */
+const textContentCache = new WeakMap();
+const eraseFromCache = (node: Node | null) => {
+ while (node) {
+ textContentCache.delete(node);
+ if (node instanceof ShadowRoot) {
+ node = node.host;
+ } else {
+ node = node.parentNode;
+ }
+ }
+};
+
+/**
+ * Erases the cache when the tree has mutated text.
+ */
+const observedNodes = new WeakSet();
+const textChangeObserver = new MutationObserver(mutations => {
+ for (const mutation of mutations) {
+ eraseFromCache(mutation.target);
+ }
+});
+
+/**
+ * Builds the text content of a node using some custom logic.
+ *
+ * @remarks
+ * The primary reason this function exists is due to {@link ShadowRoot}s not having
+ * text content.
+ *
+ * @internal
+ */
+export const createTextContent = (root: Node): TextContent => {
+ let value = textContentCache.get(root);
+ if (value) {
+ return value;
+ }
+ value = {full: '', immediate: []};
+ if (!isSuitableNodeForTextMatching(root)) {
+ return value;
+ }
+
+ let currentImmediate = '';
+ if (isNonTrivialValueNode(root)) {
+ value.full = root.value;
+ value.immediate.push(root.value);
+
+ root.addEventListener(
+ 'input',
+ event => {
+ eraseFromCache(event.target as HTMLInputElement);
+ },
+ {once: true, capture: true}
+ );
+ } else {
+ for (let child = root.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType === Node.TEXT_NODE) {
+ value.full += child.nodeValue ?? '';
+ currentImmediate += child.nodeValue ?? '';
+ continue;
+ }
+ if (currentImmediate) {
+ value.immediate.push(currentImmediate);
+ }
+ currentImmediate = '';
+ if (child.nodeType === Node.ELEMENT_NODE) {
+ value.full += createTextContent(child).full;
+ }
+ }
+ if (currentImmediate) {
+ value.immediate.push(currentImmediate);
+ }
+ if (root instanceof Element && root.shadowRoot) {
+ value.full += createTextContent(root.shadowRoot).full;
+ }
+
+ if (!observedNodes.has(root)) {
+ textChangeObserver.observe(root, {
+ childList: true,
+ characterData: true,
+ });
+ observedNodes.add(root);
+ }
+ }
+ textContentCache.set(root, value);
+ return value;
+};
diff --git a/remote/test/puppeteer/src/injected/TextQuerySelector.ts b/remote/test/puppeteer/src/injected/TextQuerySelector.ts
new file mode 100644
index 000000000000..77c17a7334ec
--- /dev/null
+++ b/remote/test/puppeteer/src/injected/TextQuerySelector.ts
@@ -0,0 +1,86 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+import {
+ createTextContent,
+ isSuitableNodeForTextMatching,
+} from './TextContent.js';
+
+/**
+ * Queries the given node for a node matching the given text selector.
+ *
+ * @internal
+ */
+export const textQuerySelector = (
+ root: Node,
+ selector: string
+): Element | null => {
+ for (const node of root.childNodes) {
+ if (node instanceof Element && isSuitableNodeForTextMatching(node)) {
+ let matchedNode: Element | null;
+ if (node.shadowRoot) {
+ matchedNode = textQuerySelector(node.shadowRoot, selector);
+ } else {
+ matchedNode = textQuerySelector(node, selector);
+ }
+ if (matchedNode) {
+ return matchedNode;
+ }
+ }
+ }
+
+ if (root instanceof Element) {
+ const textContent = createTextContent(root);
+ if (textContent.full.includes(selector)) {
+ return root;
+ }
+ }
+ return null;
+};
+
+/**
+ * Queries the given node for all nodes matching the given text selector.
+ *
+ * @internal
+ */
+export const textQuerySelectorAll = (
+ root: Node,
+ selector: string
+): Element[] => {
+ let results: Element[] = [];
+ for (const node of root.childNodes) {
+ if (node instanceof Element) {
+ let matchedNodes: Element[];
+ if (node.shadowRoot) {
+ matchedNodes = textQuerySelectorAll(node.shadowRoot, selector);
+ } else {
+ matchedNodes = textQuerySelectorAll(node, selector);
+ }
+ results = results.concat(matchedNodes);
+ }
+ }
+ if (results.length > 0) {
+ return results;
+ }
+
+ if (root instanceof Element) {
+ const textContent = createTextContent(root);
+ if (textContent.full.includes(selector)) {
+ return [root];
+ }
+ }
+ return [];
+};
diff --git a/remote/test/puppeteer/src/injected/XPathQuerySelector.ts b/remote/test/puppeteer/src/injected/XPathQuerySelector.ts
new file mode 100644
index 000000000000..30b64889b1cb
--- /dev/null
+++ b/remote/test/puppeteer/src/injected/XPathQuerySelector.ts
@@ -0,0 +1,45 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+export const xpathQuerySelector = (
+ root: Node,
+ selector: string
+): Node | null => {
+ const doc = root.ownerDocument || document;
+ const result = doc.evaluate(
+ selector,
+ root,
+ null,
+ XPathResult.FIRST_ORDERED_NODE_TYPE
+ );
+ return result.singleNodeValue;
+};
+
+export const xpathQuerySelectorAll = (root: Node, selector: string): Node[] => {
+ const doc = root.ownerDocument || document;
+ const iterator = doc.evaluate(
+ selector,
+ root,
+ null,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE
+ );
+ const array: Node[] = [];
+ let item;
+ while ((item = iterator.iterateNext())) {
+ array.push(item);
+ }
+ return array;
+};
diff --git a/remote/test/puppeteer/src/injected/injected.ts b/remote/test/puppeteer/src/injected/injected.ts
index 7650a28fd5c1..b184c8bbba78 100644
--- a/remote/test/puppeteer/src/injected/injected.ts
+++ b/remote/test/puppeteer/src/injected/injected.ts
@@ -1,14 +1,37 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
import {createDeferredPromise} from '../util/DeferredPromise.js';
import * as Poller from './Poller.js';
+import * as TextContent from './TextContent.js';
+import * as TextQuerySelector from './TextQuerySelector.js';
+import * as XPathQuerySelector from './XPathQuerySelector.js';
+import * as PierceQuerySelector from './PierceQuerySelector.js';
import * as util from './util.js';
-Object.assign(
- self,
- Object.freeze({
- InjectedUtil: {
- ...Poller,
- ...util,
- createDeferredPromise,
- },
- })
-);
+const PuppeteerUtil = Object.freeze({
+ ...util,
+ ...Poller,
+ ...TextContent,
+ ...TextQuerySelector,
+ ...XPathQuerySelector,
+ ...PierceQuerySelector,
+ createDeferredPromise,
+});
+
+type PuppeteerUtil = typeof PuppeteerUtil;
+
+export default PuppeteerUtil;
diff --git a/remote/test/puppeteer/src/injected/util.ts b/remote/test/puppeteer/src/injected/util.ts
index 79e68e5e0f4d..94f993f75ed3 100644
--- a/remote/test/puppeteer/src/injected/util.ts
+++ b/remote/test/puppeteer/src/injected/util.ts
@@ -1,7 +1,25 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
const createdFunctions = new Map unknown>();
/**
* Creates a function from a string.
+ *
+ * @internal
*/
export const createFunction = (
functionValue: string
@@ -16,3 +34,42 @@ export const createFunction = (
createdFunctions.set(functionValue, fn);
return fn;
};
+
+const HIDDEN_VISIBILITY_VALUES = ['hidden', 'collapse'];
+
+/**
+ * @internal
+ */
+export const checkVisibility = (
+ node: Node | null,
+ visible?: boolean
+): Node | boolean => {
+ if (!node) {
+ return visible === false;
+ }
+ if (visible === undefined) {
+ return node;
+ }
+ const element = (
+ node.nodeType === Node.TEXT_NODE ? node.parentElement : node
+ ) as Element;
+
+ const style = window.getComputedStyle(element);
+ const isVisible =
+ style &&
+ !HIDDEN_VISIBILITY_VALUES.includes(style.visibility) &&
+ isBoundingBoxVisible(element);
+ return visible === isVisible ? node : false;
+};
+
+function isBoundingBoxVisible(element: Element): boolean {
+ const rect = element.getBoundingClientRect();
+ return (
+ rect.width > 0 &&
+ rect.height > 0 &&
+ rect.right > 0 &&
+ rect.bottom > 0 &&
+ rect.left < self.innerWidth &&
+ rect.top < self.innerHeight
+ );
+}
diff --git a/remote/test/puppeteer/src/node/BrowserFetcher.ts b/remote/test/puppeteer/src/node/BrowserFetcher.ts
index 7c425c2dbba5..7eeae6ee7d1c 100644
--- a/remote/test/puppeteer/src/node/BrowserFetcher.ts
+++ b/remote/test/puppeteer/src/node/BrowserFetcher.ts
@@ -237,7 +237,12 @@ export class BrowserFetcher {
this.#platform = 'linux';
break;
case 'win32':
- this.#platform = os.arch() === 'x64' ? 'win64' : 'win32';
+ this.#platform =
+ os.arch() === 'x64' ||
+ // Windows 11 for ARM supports x64 emulation
+ (os.arch() === 'arm64' && _isWindows11(os.release()))
+ ? 'win64'
+ : 'win32';
return;
default:
assert(false, 'Unsupported platform: ' + platform);
@@ -336,7 +341,7 @@ export class BrowserFetcher {
}
// Use system Chromium builds on Linux ARM devices
- if (os.platform() !== 'darwin' && os.arch() === 'arm64') {
+ if (os.platform() === 'linux' && os.arch() === 'arm64') {
handleArm64();
return;
}
@@ -497,6 +502,25 @@ function parseFolderPath(
return {product, platform, revision};
}
+/**
+ * Windows 11 is identified by 10.0.22000 or greater
+ * @internal
+ */
+function _isWindows11(version: string): boolean {
+ const parts = version.split('.');
+ if (parts.length > 2) {
+ const major = parseInt(parts[0] as string, 10);
+ const minor = parseInt(parts[1] as string, 10);
+ const patch = parseInt(parts[2] as string, 10);
+ return (
+ major > 10 ||
+ (major === 10 && minor > 0) ||
+ (major === 10 && minor === 0 && patch >= 22000)
+ );
+ }
+ return false;
+}
+
/**
* @internal
*/
diff --git a/remote/test/puppeteer/src/node/BrowserRunner.ts b/remote/test/puppeteer/src/node/BrowserRunner.ts
index c18350a6da13..4567ba73df52 100644
--- a/remote/test/puppeteer/src/node/BrowserRunner.ts
+++ b/remote/test/puppeteer/src/node/BrowserRunner.ts
@@ -22,6 +22,7 @@ import removeFolder from 'rimraf';
import {promisify} from 'util';
import {assert} from '../util/assert.js';
import {Connection} from '../common/Connection.js';
+import {Connection as BiDiConnection} from '../common/bidi/Connection.js';
import {debug} from '../common/Debug.js';
import {TimeoutError} from '../common/Errors.js';
import {
@@ -245,6 +246,25 @@ export class BrowserRunner {
removeEventListeners(this.#listeners);
}
+ async setupWebDriverBiDiConnection(options: {
+ timeout: number;
+ slowMo: number;
+ preferredRevision: string;
+ }): Promise {
+ assert(this.proc, 'BrowserRunner not started.');
+
+ const {timeout, slowMo, preferredRevision} = options;
+ let browserWSEndpoint = await waitForWSEndpoint(
+ this.proc,
+ timeout,
+ preferredRevision,
+ /^WebDriver BiDi listening on (ws:\/\/.*)$/
+ );
+ browserWSEndpoint += '/session';
+ const transport = await WebSocketTransport.create(browserWSEndpoint);
+ return new BiDiConnection(transport, slowMo);
+ }
+
async setupConnection(options: {
usePipe?: boolean;
timeout: number;
@@ -279,7 +299,8 @@ export class BrowserRunner {
function waitForWSEndpoint(
browserProcess: childProcess.ChildProcess,
timeout: number,
- preferredRevision: string
+ preferredRevision: string,
+ regex = /^DevTools listening on (ws:\/\/.*)$/
): Promise {
assert(browserProcess.stderr, '`browserProcess` does not have stderr.');
const rl = readline.createInterface(browserProcess.stderr);
@@ -327,7 +348,7 @@ function waitForWSEndpoint(
function onLine(line: string): void {
stderr += line + '\n';
- const match = line.match(/^DevTools listening on (ws:\/\/.*)$/);
+ const match = line.match(regex);
if (!match) {
return;
}
diff --git a/remote/test/puppeteer/src/node/ChromeLauncher.ts b/remote/test/puppeteer/src/node/ChromeLauncher.ts
index ab07b56f6e61..c9196617680f 100644
--- a/remote/test/puppeteer/src/node/ChromeLauncher.ts
+++ b/remote/test/puppeteer/src/node/ChromeLauncher.ts
@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';
import {assert} from '../util/assert.js';
-import {Browser} from '../common/Browser.js';
+import {CDPBrowser} from '../common/Browser.js';
import {Product} from '../common/Product.js';
import {BrowserRunner} from './BrowserRunner.js';
import {
@@ -43,7 +43,7 @@ export class ChromeLauncher implements ProductLauncher {
this._isPuppeteerCore = isPuppeteerCore;
}
- async launch(options: PuppeteerNodeLaunchOptions = {}): Promise {
+ async launch(options: PuppeteerNodeLaunchOptions = {}): Promise {
const {
ignoreDefaultArgs = false,
args = [],
@@ -154,7 +154,7 @@ export class ChromeLauncher implements ProductLauncher {
slowMo,
preferredRevision: this._preferredRevision,
});
- browser = await Browser._create(
+ browser = await CDPBrowser._create(
this.product,
connection,
[],
diff --git a/remote/test/puppeteer/src/node/FirefoxLauncher.ts b/remote/test/puppeteer/src/node/FirefoxLauncher.ts
index c27f1b8d8b37..6295057c3817 100644
--- a/remote/test/puppeteer/src/node/FirefoxLauncher.ts
+++ b/remote/test/puppeteer/src/node/FirefoxLauncher.ts
@@ -2,7 +2,9 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
import {assert} from '../util/assert.js';
-import {Browser} from '../common/Browser.js';
+import {Browser} from '../api/Browser.js';
+import {CDPBrowser as CDPBrowser} from '../common/Browser.js';
+import {Browser as BiDiBrowser} from '../common/bidi/Browser.js';
import {Product} from '../common/Product.js';
import {BrowserFetcher} from './BrowserFetcher.js';
import {BrowserRunner} from './BrowserRunner.js';
@@ -58,6 +60,7 @@ export class FirefoxLauncher implements ProductLauncher {
extraPrefsFirefox = {},
waitForInitialPage = true,
debuggingPort = null,
+ protocol = 'cdp',
} = options;
const firefoxArguments = [];
@@ -113,7 +116,9 @@ export class FirefoxLauncher implements ProductLauncher {
firefoxArguments.push(userDataDir);
}
- await this._updateRevision();
+ if (!this._isPuppeteerCore) {
+ await this._updateRevision();
+ }
let firefoxExecutable = executablePath;
if (!executablePath) {
const {missingText, executablePath} = resolveExecutablePath(this);
@@ -143,6 +148,27 @@ export class FirefoxLauncher implements ProductLauncher {
pipe,
});
+ if (protocol === 'webDriverBiDi') {
+ let browser;
+ try {
+ const connection = await runner.setupWebDriverBiDiConnection({
+ timeout,
+ slowMo,
+ preferredRevision: this._preferredRevision,
+ });
+ browser = await BiDiBrowser.create({
+ connection,
+ closeCallback: runner.close.bind(runner),
+ process: runner.proc,
+ });
+ } catch (error) {
+ runner.kill();
+ throw error;
+ }
+
+ return browser;
+ }
+
let browser;
try {
const connection = await runner.setupConnection({
@@ -151,7 +177,7 @@ export class FirefoxLauncher implements ProductLauncher {
slowMo,
preferredRevision: this._preferredRevision,
});
- browser = await Browser._create(
+ browser = await CDPBrowser._create(
this.product,
connection,
[],
diff --git a/remote/test/puppeteer/src/node/ProductLauncher.ts b/remote/test/puppeteer/src/node/ProductLauncher.ts
index a1d7c84f4600..e3dc3aabcecb 100644
--- a/remote/test/puppeteer/src/node/ProductLauncher.ts
+++ b/remote/test/puppeteer/src/node/ProductLauncher.ts
@@ -15,7 +15,7 @@
*/
import os from 'os';
-import {Browser} from '../common/Browser.js';
+import {Browser} from '../api/Browser.js';
import {BrowserFetcher} from './BrowserFetcher.js';
import {
diff --git a/remote/test/puppeteer/src/node/Puppeteer.ts b/remote/test/puppeteer/src/node/Puppeteer.ts
index 374b86164826..b9968f469bf3 100644
--- a/remote/test/puppeteer/src/node/Puppeteer.ts
+++ b/remote/test/puppeteer/src/node/Puppeteer.ts
@@ -22,7 +22,7 @@ import {
import {BrowserFetcher, BrowserFetcherOptions} from './BrowserFetcher.js';
import {LaunchOptions, BrowserLaunchArgumentOptions} from './LaunchOptions.js';
import {BrowserConnectOptions} from '../common/BrowserConnector.js';
-import {Browser} from '../common/Browser.js';
+import {Browser} from '../api/Browser.js';
import {createLauncher, ProductLauncher} from './ProductLauncher.js';
import {PUPPETEER_REVISIONS} from '../revisions.js';
import {Product} from '../common/Product.js';
@@ -78,6 +78,9 @@ export class PuppeteerNode extends Puppeteer {
#projectRoot?: string;
#productName?: Product;
+ /**
+ * @internal
+ */
_preferredRevision: string;
/**
diff --git a/remote/test/puppeteer/src/templates/injected.ts.tmpl b/remote/test/puppeteer/src/templates/injected.ts.tmpl
index 74146a41158a..4cd5305d37df 100644
--- a/remote/test/puppeteer/src/templates/injected.ts.tmpl
+++ b/remote/test/puppeteer/src/templates/injected.ts.tmpl
@@ -1,10 +1,8 @@
-import {createDeferredPromise} from '../util/DeferredPromise.js';
-
-declare global {
- const InjectedUtil: {
- createDeferredPromise: typeof createDeferredPromise;
- };
-}
-
-/** @internal */
+/**
+ * CommonJS JavaScript code that provides the puppeteer utilities. See the
+ * [README](https://github.com/puppeteer/puppeteer/blob/main/src/injected/README.md)
+ * for injection for more information.
+ *
+ * @internal
+ */
export const source = SOURCE_CODE;
diff --git a/remote/test/puppeteer/src/tsconfig.cjs.json b/remote/test/puppeteer/src/tsconfig.cjs.json
index c92ca3f87cc8..e7c4de56698d 100644
--- a/remote/test/puppeteer/src/tsconfig.cjs.json
+++ b/remote/test/puppeteer/src/tsconfig.cjs.json
@@ -8,6 +8,5 @@
"references": [
{"path": "../vendor/tsconfig.cjs.json"},
{"path": "../compat/cjs/tsconfig.json"}
- ],
- "exclude": ["injected/injected.ts"]
+ ]
}
diff --git a/remote/test/puppeteer/src/tsconfig.esm.json b/remote/test/puppeteer/src/tsconfig.esm.json
index aba2f2403588..f5444a1860de 100644
--- a/remote/test/puppeteer/src/tsconfig.esm.json
+++ b/remote/test/puppeteer/src/tsconfig.esm.json
@@ -8,6 +8,5 @@
"references": [
{"path": "../vendor/tsconfig.esm.json"},
{"path": "../compat/esm/tsconfig.json"}
- ],
- "exclude": ["injected/injected.ts"]
+ ]
}
diff --git a/remote/test/puppeteer/src/types.ts b/remote/test/puppeteer/src/types.ts
index 00bed28c5a29..246e658420f0 100644
--- a/remote/test/puppeteer/src/types.ts
+++ b/remote/test/puppeteer/src/types.ts
@@ -1,5 +1,6 @@
// AUTOGENERATED - Use `npm run generate:sources` to regenerate.
+export * from './api/Browser.js';
export * from './common/Accessibility.js';
export * from './common/AriaQueryHandler.js';
export * from './common/Browser.js';
@@ -23,11 +24,13 @@ export * from './common/FileChooser.js';
export * from './common/FirefoxTargetManager.js';
export * from './common/Frame.js';
export * from './common/FrameManager.js';
+export * from './common/FrameTree.js';
export * from './common/HTTPRequest.js';
export * from './common/HTTPResponse.js';
export * from './common/Input.js';
export * from './common/IsolatedWorld.js';
export * from './common/JSHandle.js';
+export * from './common/LazyArg.js';
export * from './common/LifecycleWatcher.js';
export * from './common/NetworkConditions.js';
export * from './common/NetworkEventManager.js';
@@ -47,12 +50,12 @@ export * from './common/Tracing.js';
export * from './common/types.js';
export * from './common/USKeyboardLayout.js';
export * from './common/util.js';
+export * from './common/WaitTask.js';
export * from './common/WebWorker.js';
export * from './compat.d.js';
export * from './constants.js';
export * from './environment.js';
export * from './generated/injected.js';
-export * from './generated/version.js';
export * from './initializePuppeteer.js';
export * from './node/BrowserFetcher.js';
export * from './node/BrowserRunner.js';
diff --git a/remote/test/puppeteer/src/util/DeferredPromise.ts b/remote/test/puppeteer/src/util/DeferredPromise.ts
index 2f21087ba858..8c7945e3591e 100644
--- a/remote/test/puppeteer/src/util/DeferredPromise.ts
+++ b/remote/test/puppeteer/src/util/DeferredPromise.ts
@@ -6,8 +6,8 @@ import {TimeoutError} from '../common/Errors.js';
export interface DeferredPromise extends Promise {
finished: () => boolean;
resolved: () => boolean;
- resolve: (_: T) => void;
- reject: (_: Error) => void;
+ resolve: (value: T) => void;
+ reject: (reason?: unknown) => void;
}
/**
@@ -32,8 +32,8 @@ export function createDeferredPromise(
): DeferredPromise {
let isResolved = false;
let isRejected = false;
- let resolver = (_: T): void => {};
- let rejector = (_: Error) => {};
+ let resolver: (value: T) => void;
+ let rejector: (reason?: unknown) => void;
const taskPromise = new Promise((resolve, reject) => {
resolver = resolve;
rejector = reject;
@@ -59,7 +59,7 @@ export function createDeferredPromise(
isResolved = true;
resolver(value);
},
- reject: (err: Error) => {
+ reject: (err?: unknown) => {
clearTimeout(timeoutId);
isRejected = true;
rejector(err);
diff --git a/remote/test/puppeteer/test/TestExpectations.json b/remote/test/puppeteer/test/TestExpectations.json
new file mode 100644
index 000000000000..77b62c6419c2
--- /dev/null
+++ b/remote/test/puppeteer/test/TestExpectations.json
@@ -0,0 +1,3128 @@
+[
+ {
+ "testIdPattern": "[accessibility.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[ariaqueryhandler.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[browsercontext.spec] BrowserContext should fire target events",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[browsercontext.spec] BrowserContext should isolate localStorage and cookies",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[browsercontext.spec] BrowserContext should wait for a target",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[browsercontext.spec] BrowserContext should work across sessions",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[browsercontext.spec] BrowserContext window.open should use parent tab context",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[CDPSession.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[chromiumonly.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[click.spec] Page.click should click on checkbox label and toggle",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL", "PASS"]
+ },
+ {
+ "testIdPattern": "[click.spec] Page.click should click the button with fixed position inside an iframe",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[click.spec] Page.click should click the button if window.Node is removed",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[click.spec] Page.click should click with disabled javascript",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.cookies should get cookies from multiple urls",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.deleteCookie should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should default to setting secure cookie for HTTPS websites",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should isolate cookies in browser contexts",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should set a cookie on a different domain",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should set a cookie with a path",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should set cookie with reasonable defaults",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should set cookies from a frame",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should set multiple cookies",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should set secure same-site cookies from a frame",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[cookies.spec] Cookie specs Page.setCookie should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[coverage.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[defaultbrowsercontext.spec] DefaultBrowserContext page.deleteCookie() should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[defaultbrowsercontext.spec] DefaultBrowserContext page.setCookie() should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[dialog.spec] Page.Events.Dialog should allow accepting prompts",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[drag-and-drop.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should handle nested frames",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should handle nested frames",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should return null for invisible elements",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should return null for invisible elements",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boxModel should return null for invisible elements",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boxModel should return null for invisible elements",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.click should work",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.contentFrame should work",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulate should support clicking",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateCPUThrottling should change the CPU throttling rate successfully",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateCPUThrottling should change the CPU throttling rate successfully",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateMediaFeatures should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateMediaType should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateNetworkConditions should change navigator.connection.effectiveType",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateNetworkConditions should change navigator.connection.effectiveType",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateTimezone should throw for invalid timezone IDs",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateTimezone should throw for invalid timezone IDs",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateTimezone should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateTimezone should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateVisionDeficiency should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.emulateVisionDeficiency should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[emulation.spec] Emulation Page.viewport should support landscape emulation",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should be able to throw a tricky error",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should fail for circular object",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should fail for circular object",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should not throw an error when evaluation does a navigation",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should return undefined for non-serializable objects",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should return undefined for non-serializable objects",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should return undefined for objects with symbols",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should return undefined for objects with symbols",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should simulate a user gesture",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should simulate a user gesture",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw a nice error after a navigation",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw a nice error after a navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw if elementHandles are from other frames",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should throw when evaluation triggers reload",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work from-inside an exposed function",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluate should work from-inside an exposed function",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should evaluate before anything else on the page",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should evaluate before anything else on the page",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should work with CSP",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[evaluation.spec] Evaluation specs Page.evaluateOnNewDocument should work with CSP",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[fixtures.spec] Fixtures dumpio option should work with pipe option",
+ "platforms": ["linux"],
+ "parameters": ["chrome", "headful"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[fixtures.spec] Fixtures dumpio option should work with pipe option",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[fixtures.spec] Fixtures dumpio option should work with pipe option",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should handle nested frames",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should report different frame instance when frame re-attaches",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should report different frame instance when frame re-attaches",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should report frame from-inside shadow DOM",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should report frame.name()",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should report frame.parent()",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should send events when frames are manipulated dynamically",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should send events when frames are manipulated dynamically",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should support lazy frames",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame Management should support lazy frames",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame.evaluate should throw for detached frames",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[frame.spec] Frame specs Frame.executionContext should work",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[headful.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[idle_override.spec] Emulate idle state changing idle state emulation causes change of the IdleDetector state",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[idle_override.spec] Emulate idle state changing idle state emulation causes change of the IdleDetector state",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[idle_overrides.spec]",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[ignorehttperrors.spec]",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors Response.securityDetails should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors Response.securityDetails should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with mixed content",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with request interception",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors should work with request interception",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors Response.securityDetails Network redirects should report SecurityDetails",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[input.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[jshandle.spec] JSHandle JSHandle.click should work",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[jshandle.spec] JSHandle JSHandle.click should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[jshandle.spec] JSHandle JSHandle.jsonValue should not work with dates",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[jshandle.spec] JSHandle JSHandle.jsonValue should not work with dates",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[jshandle.spec] JSHandle JSHandleonValue should not work with dates",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard ElementHandle.press should support |text| option",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard ElementHandle.press should support |text| option",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should press the meta key",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should press the meta key",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should press the metaKey",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should press the metaKey",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should report shiftKey",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should report shiftKey",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should send a character with sendCharacter",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should send a character with sendCharacter",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should specify location",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should specify location",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should specify repeat property",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should specify repeat property",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should type all kinds of characters",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should type all kinds of characters",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should type emoji",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should type emoji",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should type emoji into an iframe",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[keyboard.spec] Keyboard should type emoji into an iframe",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Browser target events should work",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Browser target events should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Browser.Events.disconnected should be emitted when: browser gets closed, disconnected or underlying websocket gets closed",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.connect should be able to connect to the same page simultaneously",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.executablePath should work",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.connect should be able to reconnect to a disconnected browser",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.connect should support targetFilter option",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.executablePath when the product is chrome, platform is not darwin, and arch is arm64 and the executable does not exist does not return /usr/bin/chromium-browser",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.executablePath when the product is chrome, platform is not darwin, and arch is arm64 and the executable exists returns /usr/bin/chromium-browser",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should work with no default arguments",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL", "PASS"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should filter out ignored default arguments in Chrome",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should have custom URL when launching browser",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should launch Chrome properly with --no-startup-window and waitForInitialPage=false",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should be able to launch Chrome",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch should be able to launch Firefox",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch tmp profile should be cleaned up",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch userDataDir argument with non-existent dir",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should send mouse wheel events",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should send mouse wheel events",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should trigger hover state",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should trigger hover state",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should trigger hover state with removed window.Node",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should trigger hover state with removed window.Node",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should tween mouse movement",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[mouse.spec] Mouse should tween mouse movement",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.goto should navigate subframes",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.goto should navigate subframes",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.goto should reject when frame detaches",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.goto should reject when frame detaches",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.goto should return matching responses",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.goto should return matching responses",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.waitForNavigation should fail when frame detaches",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.waitForNavigation should fail when frame detaches",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.waitForNavigation should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Frame.waitForNavigation should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goBack should work with HistoryAPI",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goBack should work with HistoryAPI",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad SSL",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad SSL",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad url",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should fail when navigating to bad url",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should fail when server returns 204",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to dataURL and fire dataURL requests",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to dataURL and fire dataURL requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to empty page with networkidle0",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to empty page with networkidle2",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should navigate to URL with hash and fire requests without hash",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should send referer",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should send referer",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should wait for network idle to succeed navigation",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should work when navigating to data url",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should work when navigating to data url",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.goto should work with subframes return 204",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work when subframe issues window.stop()",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with DOM history.back()/history.forward()",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with history.pushState()",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[navigation.spec] navigation Page.waitForNavigation should work with history.replaceState()",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network \"after each\" hook for \"should wait until response completes\"",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Network Events Page.Events.Request",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Network Events Page.Events.Response",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Network Events Page.Events.RequestFailed",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Network Events Page.Events.RequestFinished",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Network Events Page.Events.RequestServedFromCache",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Network Events should fire events in proper order",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Network Events should support redirects",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Page.authenticate should allow disable authentication",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Page.authenticate should fail if wrong credentials",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Page.authenticate should not disable caching",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Page.authenticate should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Page.setExtraHTTPHeaders should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network raw network headers Cross-origin set-cookie",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL", "PASS"]
+ },
+ {
+ "testIdPattern": "[network.spec] network raw network headers Same-origin set-cookie subresource",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL", "PASS"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.frame should work for subframe navigation request",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.headers should define Chrome as user agent header",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.headers should define Firefox as user agent header",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["chrome"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.initiator should return the initiator",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.initiator should return the initiator",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.isNavigationRequest should work",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.isNavigationRequest should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.isNavigationRequest should work when navigating to image",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.isNavigationRequest should work with request interception",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.isNavigationRequest should work with request interception",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.postData should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Request.postData should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.buffer should throw if the response does not have a body",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.buffer should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.buffer should work with compression",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.fromCache should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.fromCache should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.fromServiceWorker Response.fromServiceWorker",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.fromServiceWorker Response.fromServiceWorker",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.json should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.text should return uncompressed text",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.text should return uncompressed text",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.text should throw when requesting body of redirected response",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.text should throw when requesting body of redirected response",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.text should wait until response completes",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.text should wait until response completes",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.text should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.text should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[network.spec] network Response.timing returns timing information",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[oopif.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[oopif.spec] OOPIF \"after all\" hook for \"should keep track of a frames OOP state\"",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[oopif.spec] OOPIF \"before all\" hook for \"should keep track of a frames OOP state\"",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[oopif.spec] OOPIF should support OOP iframes becoming normal iframes again",
+ "platforms": ["darwin"],
+ "parameters": ["chrome", "headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[oopif.spec] OOPIF should keep track of a frames OOP state",
+ "platforms": ["darwin"],
+ "parameters": ["chrome", "headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[oopif.spec] OOPIF should wait for inner OOPIFs",
+ "platforms": ["linux"],
+ "parameters": ["chrome", "headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.connect should be able to connect to a browser with no page targets",
+ "platforms": ["linux", "darwin", "win32"],
+ "parameters": ["chrome", "chrome-headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page \"before each\" hook for \"should return the page title\"",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["PASS", "FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should deny permission when not listed",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should deny permission when not listed",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should grant permission when listed",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should grant permission when listed",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should grant persistent-storage",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should grant persistent-storage",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should isolate permissions between browser contexts",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should isolate permissions between browser contexts",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should reset permissions",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should reset permissions",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should trigger permission onchange",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page BrowserContext.overridePermissions should trigger permission onchange",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page ExecutionContext.queryObjects should work",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page ExecutionContext.queryObjects should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page ExecutionContext.queryObjects should work for non-trivial page",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.addStyleTag should throw when added with content to the CSP page",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.close should *not* run beforeunload by default",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.close should run beforeunload if asked for",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.close should run beforeunload if asked for",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.close should terminate network waiters",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Close should work with window.close",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should have location and stack trace for console API calls",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should have location and stack trace for console API calls",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should have location when fetch fails",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should have location when fetch fails",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should not fail for window object",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should not fail for window object",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should trigger correct Log",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should trigger correct Log",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should work for different console API calls",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Console should work for different console API calls",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.error should throw when page crashes",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.error should throw when page crashes",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with clicking target=_blank and rel=noopener",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with clicking target=_blank and rel=noopener",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with clicking target=_blank and with rel=opener",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with clicking target=_blank and with rel=opener",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with clicking target=_blank and without rel=opener",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with clicking target=_blank and without rel=opener",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with fake-clicking target=_blank and rel=noopener",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with fake-clicking target=_blank and rel=noopener",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with noopener",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.Events.Popup should work with noopener",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should await returned promise",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should await returned promise",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should be callable from-inside evaluateOnNewDocument",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should be callable from-inside evaluateOnNewDocument",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should fallback to default export when passed a module object",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should fallback to default export when passed a module object",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should not throw when frames detach",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should not throw when frames detach",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should support throwing \"null\"",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should support throwing \"null\"",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should survive navigation",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should survive navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should throw exception in page context",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should throw exception in page context",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should work on frames",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should work on frames",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should work on frames before navigation",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should work on frames before navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should work with complex objects",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.exposeFunction should work with complex objects",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.metrics metrics event fired on console.timeStamp",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.metrics metrics event fired on console.timeStamp",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.metrics should get metrics from a page",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.metrics should get metrics from a page",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.select should work when re-defining top-level Event class",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.select should work when re-defining top-level Event class",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setBypassCSP should bypass after cross-process navigation",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setBypassCSP should bypass after cross-process navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setBypassCSP should bypass CSP header",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setBypassCSP should bypass CSP header",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setBypassCSP should bypass CSP in iframes as well",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setBypassCSP should bypass CSP in iframes as well",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.addScriptTag should throw when added with content to the CSP page",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setBypassCSP should bypass CSP meta tag",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setBypassCSP should bypass CSP meta tag",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setCacheEnabled should enable or disable the cache based on the state passed",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setCacheEnabled should enable or disable the cache based on the state passed",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setCacheEnabled should stay disabled when toggling request interception on/off",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setCacheEnabled should stay disabled when toggling request interception on/off",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page printing to PDF should respect timeout",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setGeolocation should work",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setGeolocation should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setJavaScriptEnabled should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setJavaScriptEnabled should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setOfflineMode should emulate navigator.onLine",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setOfflineMode should emulate navigator.onLine",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setOfflineMode should work",
+ "platforms": ["darwin"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setOfflineMode should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setUserAgent should work with additional userAgentMetdata",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[page.spec] Page Page.setUserAgent should work with additional userAgentMetdata",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[proxy.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[proxy.spec] request proxy in incognito browser context should proxy requests when configured at context level",
+ "platforms": ["win32"],
+ "parameters": ["chrome", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec]",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should cooperatively respond by priority",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should cooperatively continue by priority",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should cooperatively abort by priority",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should intercept",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work when POST is redirected with 302",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work when header manipulation headers with redirect",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be able to remove headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should contain referer header",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should properly return navigation response when URL has cookies",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should stop intercepting",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should show custom HTTP headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with redirect inside sync XHR",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with custom referer headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be abortable",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be able to access the error reason",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be abortable with custom error codes",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should send referer",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should fail navigation when aborting main resource",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with redirects",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with redirects for subresources",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be able to abort redirects",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with equal requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should navigate to dataURL and fire dataURL requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be able to fetch dataURL and fire dataURL requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should navigate to URL with hash and fire requests without hash",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with encoded server",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with badly encoded server",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with encoded server - 2",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should not throw \"Invalid Interception Id\" if the request was cancelled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with file URLs",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should not cache if cache disabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should cache if cache enabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should load fonts if cache enabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.continue should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.continue should amend HTTP headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.continue should redirect in a way non-observable to page",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.continue should amend method",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.continue should amend post data",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.continue should amend both post data and method on navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.respond should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.respond should be able to access the response",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.respond should work with status code 422",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.respond should redirect",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.respond should allow mocking binary responses",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.respond should stringify intercepted request response headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Request.respond should indicate already-handled if an intercept has been handled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should intercept",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work when POST is redirected with 302",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work when header manipulation headers with redirect",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be able to remove headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should contain referer header",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should properly return navigation response when URL has cookies",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should stop intercepting",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should show custom HTTP headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with redirect inside sync XHR",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with custom referer headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be abortable",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be abortable with custom error codes",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should send referer",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should fail navigation when aborting main resource",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with redirects",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with redirects for subresources",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should be able to abort redirects",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec] request interception Page.setRequestInterception should work with equal requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception-experimental.spec.js] request interception Page.setRequestInterception should navigate to dataURL and fire dataURL requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should be able to fetch dataURL and fire dataURL requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should navigate to URL with hash and fire requests without hash",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with encoded server",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with badly encoded server",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with encoded server - 2",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] equest interception Page.setRequestInterception should not throw \"Invalid Interception Id\" if the request was cancelled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] equest interception Page.setRequestInterception should not throw \"Invalid Interception Id\" if the request was cancelled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with file URLs",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should not cache if cache disabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should cache if cache enabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should load fonts if cache enabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should amend HTTP headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should redirect in a way non-observable to page",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should amend method",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should amend post data",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should amend both post data and method on navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should fail if the header value is invalid",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.respond should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.respond should work with status code 422",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.respond should redirect",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.respond should allow mocking multiple headers with same key",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.respond should allow mocking binary responses",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.respond should stringify intercepted request response headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.respond should fail if the header value is invalid",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should intercept",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work when POST is redirected with 302",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work when header manipulation headers with redirect",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should be able to remove headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should contain referer header",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should properly return navigation response when URL has cookies",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should stop intercepting",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should show custom HTTP headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with redirect inside sync XHR",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with custom referer headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should be abortable",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should be abortable with custom error codes",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should send referer",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should fail navigation when aborting main resource",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with redirects",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with redirects for subresources",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should be able to abort redirects",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with equal requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should navigate to dataURL and fire dataURL requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should be able to fetch dataURL and fire dataURL requests",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should navigate to URL with hash and fire requests without hash",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with encoded server",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with badly encoded server",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with encoded server - 2",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should not throw \"Invalid Interception Id\" if the request was cancelled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with file URLs",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should not cache if cache disabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should cache if cache enabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should load fonts if cache enabled",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should amend HTTP headers",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should redirect in a way non-observable to page",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should amend method",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should amend post data",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should amend both post data and method on navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.continue should fail if the header value is invalid",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec] request interception Request.respond should work",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[requestinterception.spec]",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should capture full element when larger than viewport",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should use scale for clip",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should use scale for clip",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should fail to screenshot a detached element",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should fail to screenshot a detached element",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work for an element with an offset",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work for an element with an offset",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work with a null viewport",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work with a rotated element",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots ElementHandle.screenshot should work with a rotated element",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should allow transparency",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should allow transparency",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should clip rect",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should clip rect",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should get screenshot bigger than the viewport",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should render white background on jpeg file",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should render white background on jpeg file",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should return base64",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should return base64",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should take fullPage screenshots",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should work",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should work in \"fromSurface: false\" mode",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["chrome", "headless"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should work in \"fromSurface: false\" mode",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[screenshot.spec] Screenshots Page.screenshot should work with webp",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target Browser.waitForTarget should wait for a target",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should be able to use async waitForTarget",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should create a worker from a service worker",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should create a worker from a shared worker",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should have an opener",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should not crash while redirecting if original request was missed",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should not crash while redirecting if original request was missed",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should not report uninitialized pages",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should not report uninitialized pages",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should report when a new page is created and closed",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should report when a new page is created and closed",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should report when a service worker is created and destroyed",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[target.spec] Target should report when a target url changes",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[TargetManager.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[touchscreen.spec]",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[touchscreen.spec] Touchscreen should tap the button",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[touchscreen.spec] Touchscreen should report touches",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[tracing.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work when resolved right before execution context disposal",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work with strict CSP policy",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work with strict CSP policy",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should survive cross-process navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL", "PASS"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector Page.waitForSelector is shortcut for main frame",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should run in specified frame",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should throw when frame is detached",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should survive cross-process navigation",
+ "platforms": ["linux"],
+ "parameters": ["firefox"],
+ "expectations": ["FAIL", "PASS"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should work with removed MutationObserver",
+ "platforms": ["win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should run in specified frame",
+ "platforms": ["darwin", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should throw when frame is detached",
+ "platforms": ["darwin", "linux", "win32"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[worker.spec]",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["SKIP"]
+ },
+ {
+ "testIdPattern": "[launcher.spec] Launcher specs Puppeteer Puppeteer.launch can launch and close the browser",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ },
+ {
+ "testIdPattern": "[Connection.spec] WebDriver BiDi",
+ "platforms": ["darwin", "win32", "linux"],
+ "parameters": ["firefox", "webDriverBiDi"],
+ "expectations": ["PASS"]
+ }
+]
diff --git a/remote/test/puppeteer/test/TestSuites.json b/remote/test/puppeteer/test/TestSuites.json
new file mode 100644
index 000000000000..d0c208a661d7
--- /dev/null
+++ b/remote/test/puppeteer/test/TestSuites.json
@@ -0,0 +1,54 @@
+{
+ "testSuites": [
+ {
+ "id": "chrome-headless",
+ "platforms": ["linux", "win32", "darwin"],
+ "parameters": ["chrome", "headless"],
+ "expectedLineCoverage": 93
+ },
+ {
+ "id": "chrome-headful",
+ "platforms": ["linux"],
+ "parameters": ["chrome", "headful"],
+ "expectedLineCoverage": 93
+ },
+ {
+ "id": "chrome-new-headless",
+ "platforms": ["linux"],
+ "parameters": ["chrome", "chrome-headless"],
+ "expectedLineCoverage": 93
+ },
+ {
+ "id": "firefox-headless",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless"],
+ "expectedLineCoverage": 80
+ },
+ {
+ "id": "firefox-bidi",
+ "platforms": ["linux"],
+ "parameters": ["firefox", "headless", "webDriverBiDi"],
+ "expectedLineCoverage": 56
+ }
+ ],
+ "parameterDefinitons": {
+ "chrome": {
+ "PUPPETEER_PRODUCT": "chrome"
+ },
+ "firefox": {
+ "PUPPETEER_PRODUCT": "firefox"
+ },
+ "headless": {
+ "HEADLESS": "true"
+ },
+ "headful": {
+ "HEADLESS": "false"
+ },
+ "chrome-headless": {
+ "HEADLESS": "chrome"
+ },
+ "webDriverBiDi": {
+ "PUPPETEER_PROTOCOL": "webDriverBiDi"
+ }
+ }
+}
diff --git a/remote/test/puppeteer/test/golden-firefox/screenshot-clip-rect-scale2.png b/remote/test/puppeteer/test/golden-firefox/screenshot-clip-rect-scale2.png
deleted file mode 100644
index d713d2794392..000000000000
Binary files a/remote/test/puppeteer/test/golden-firefox/screenshot-clip-rect-scale2.png and /dev/null differ
diff --git a/remote/test/puppeteer/test/src/CDPSession.spec.ts b/remote/test/puppeteer/test/src/CDPSession.spec.ts
index 920c31051cb3..9f82f8623438 100644
--- a/remote/test/puppeteer/test/src/CDPSession.spec.ts
+++ b/remote/test/puppeteer/test/src/CDPSession.spec.ts
@@ -20,11 +20,10 @@ import {
getTestState,
setupTestBrowserHooks,
setupTestPageAndContextHooks,
- describeChromeOnly,
} from './mocha-utils.js';
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
-describeChromeOnly('Target.createCDPSession', function () {
+describe('Target.createCDPSession', function () {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
diff --git a/remote/test/puppeteer/test/src/NetworkManager.spec.ts b/remote/test/puppeteer/test/src/NetworkManager.spec.ts
index cd8d647746ff..96e3a5f3e9ac 100644
--- a/remote/test/puppeteer/test/src/NetworkManager.spec.ts
+++ b/remote/test/puppeteer/test/src/NetworkManager.spec.ts
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-import {describeChromeOnly} from './mocha-utils.js';
-
import expect from 'expect';
import {
NetworkManager,
@@ -28,9 +26,16 @@ import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
class MockCDPSession extends EventEmitter {
async send(): Promise {}
+ connection() {
+ return undefined;
+ }
+ async detach() {}
+ id() {
+ return '1';
+ }
}
-describeChromeOnly('NetworkManager', () => {
+describe('NetworkManager', () => {
it('should process extra info on multiple redirects', async () => {
const mockCDPSession = new MockCDPSession();
new NetworkManager(mockCDPSession, true, {
diff --git a/remote/test/puppeteer/test/src/TargetManager.spec.ts b/remote/test/puppeteer/test/src/TargetManager.spec.ts
index 175cde72f327..76aedefaa891 100644
--- a/remote/test/puppeteer/test/src/TargetManager.spec.ts
+++ b/remote/test/puppeteer/test/src/TargetManager.spec.ts
@@ -14,24 +14,24 @@
* limitations under the License.
*/
-import {describeChromeOnly, getTestState} from './mocha-utils'; // eslint-disable-line import/extensions
+import {getTestState} from './mocha-utils'; // eslint-disable-line import/extensions
import utils from './utils.js';
import expect from 'expect';
import {
- Browser,
- BrowserContext,
+ CDPBrowser,
+ CDPBrowserContext,
} from '../../lib/cjs/puppeteer/common/Browser.js';
-describeChromeOnly('TargetManager', () => {
+describe('TargetManager', () => {
/* We use a special browser for this test as we need the --site-per-process flag */
- let browser: Browser;
- let context: BrowserContext;
+ let browser: CDPBrowser;
+ let context: CDPBrowserContext;
before(async () => {
const {puppeteer, defaultBrowserOptions} = getTestState();
- browser = await puppeteer.launch(
+ browser = (await puppeteer.launch(
Object.assign({}, defaultBrowserOptions, {
args: (defaultBrowserOptions.args || []).concat([
'--site-per-process',
@@ -39,7 +39,7 @@ describeChromeOnly('TargetManager', () => {
'--host-rules=MAP * 127.0.0.1',
]),
})
- );
+ )) as CDPBrowser;
});
beforeEach(async () => {
diff --git a/remote/test/puppeteer/test/src/accessibility.spec.ts b/remote/test/puppeteer/test/src/accessibility.spec.ts
index fe066e9d01be..c20b137b98ee 100644
--- a/remote/test/puppeteer/test/src/accessibility.spec.ts
+++ b/remote/test/puppeteer/test/src/accessibility.spec.ts
@@ -21,10 +21,9 @@ import {
getTestState,
setupTestBrowserHooks,
setupTestPageAndContextHooks,
- describeFailsFirefox,
} from './mocha-utils.js';
-describeFailsFirefox('Accessibility', function () {
+describe('Accessibility', function () {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
@@ -346,7 +345,7 @@ describeFailsFirefox('Accessibility', function () {
});
// Firefox does not support contenteditable="plaintext-only".
- describeFailsFirefox('plaintext contenteditable', function () {
+ describe('plaintext contenteditable', function () {
it('plain text field with role should not have children', async () => {
const {page} = getTestState();
diff --git a/remote/test/puppeteer/test/src/ariaqueryhandler.spec.ts b/remote/test/puppeteer/test/src/ariaqueryhandler.spec.ts
index 3c0534908407..fabdf92175e6 100644
--- a/remote/test/puppeteer/test/src/ariaqueryhandler.spec.ts
+++ b/remote/test/puppeteer/test/src/ariaqueryhandler.spec.ts
@@ -19,14 +19,13 @@ import {
getTestState,
setupTestBrowserHooks,
setupTestPageAndContextHooks,
- describeChromeOnly,
} from './mocha-utils.js';
import {ElementHandle} from '../../lib/cjs/puppeteer/common/ElementHandle.js';
import utils from './utils.js';
import assert from 'assert';
-describeChromeOnly('AriaQueryHandler', () => {
+describe('AriaQueryHandler', () => {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
@@ -447,7 +446,7 @@ describeChromeOnly('AriaQueryHandler', () => {
let divHidden = false;
await page.setContent(
- `
`
+ `text
`
);
const waitForSelector = page
.waitForSelector('aria/[role="button"]', {hidden: true})
@@ -469,7 +468,9 @@ describeChromeOnly('AriaQueryHandler', () => {
const {page} = getTestState();
let divHidden = false;
- await page.setContent(`
`);
+ await page.setContent(
+ `text
`
+ );
const waitForSelector = page
.waitForSelector('aria/[role="main"]', {hidden: true})
.then(() => {
@@ -489,7 +490,7 @@ describeChromeOnly('AriaQueryHandler', () => {
it('hidden should wait for removal', async () => {
const {page} = getTestState();
- await page.setContent(`
`);
+ await page.setContent(`text
`);
let divRemoved = false;
const waitForSelector = page
.waitForSelector('aria/[role="main"]', {hidden: true})
@@ -517,15 +518,15 @@ describeChromeOnly('AriaQueryHandler', () => {
it('should respect timeout', async () => {
const {page, puppeteer} = getTestState();
- let error!: Error;
- await page
- .waitForSelector('aria/[role="button"]', {timeout: 10})
- .catch(error_ => {
- return (error = error_);
+ const error = await page
+ .waitForSelector('aria/[role="button"]', {
+ timeout: 10,
+ })
+ .catch(error => {
+ return error;
});
- expect(error).toBeTruthy();
expect(error.message).toContain(
- 'waiting for selector `[role="button"]` failed: timeout'
+ 'Waiting for selector `[role="button"]` failed: Waiting failed: 10ms exceeded'
);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
@@ -533,17 +534,15 @@ describeChromeOnly('AriaQueryHandler', () => {
it('should have an error message specifically for awaiting an element to be hidden', async () => {
const {page} = getTestState();
- await page.setContent(`
`);
- let error!: Error;
- await page
- .waitForSelector('aria/[role="main"]', {hidden: true, timeout: 10})
- .catch(error_ => {
- return (error = error_);
- });
- expect(error).toBeTruthy();
- expect(error.message).toContain(
- 'waiting for selector `[role="main"]` to be hidden failed: timeout'
- );
+ await page.setContent(`text
`);
+ const promise = page.waitForSelector('aria/[role="main"]', {
+ hidden: true,
+ timeout: 10,
+ });
+ await expect(promise).rejects.toMatchObject({
+ message:
+ 'Waiting for selector `[role="main"]` failed: Waiting failed: 10ms exceeded',
+ });
});
it('should respond to node attribute mutation', async () => {
@@ -582,7 +581,9 @@ describeChromeOnly('AriaQueryHandler', () => {
await page.waitForSelector('aria/zombo', {timeout: 10}).catch(error_ => {
return (error = error_);
});
- expect(error!.stack).toContain('waiting for selector `zombo` failed');
+ expect(error!.stack).toContain(
+ 'Waiting for selector `zombo` failed: Waiting failed: 10ms exceeded'
+ );
});
});
diff --git a/remote/test/puppeteer/test/src/bidi/Connection.spec.ts b/remote/test/puppeteer/test/src/bidi/Connection.spec.ts
new file mode 100644
index 000000000000..0bfa68fd411b
--- /dev/null
+++ b/remote/test/puppeteer/test/src/bidi/Connection.spec.ts
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2022 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+import expect from 'expect';
+import {Connection} from '../../../lib/cjs/puppeteer/common/bidi/Connection.js';
+import {ConnectionTransport} from '../../../lib/cjs/puppeteer/common/ConnectionTransport.js';
+
+describe('WebDriver BiDi', () => {
+ describe('Connection', () => {
+ class TestConnectionTransport implements ConnectionTransport {
+ sent: string[] = [];
+ closed = false;
+
+ send(message: string) {
+ this.sent.push(message);
+ }
+
+ close(): void {
+ this.closed = true;
+ }
+ }
+
+ it('should work', async () => {
+ const transport = new TestConnectionTransport();
+ const connection = new Connection(transport);
+ const responsePromise = connection.send('session.status', {
+ context: 'context',
+ });
+ expect(transport.sent).toEqual([
+ `{"id":1,"method":"session.status","params":{"context":"context"}}`,
+ ]);
+ const id = JSON.parse(transport.sent[0]!).id;
+ const rawResponse = {
+ id,
+ result: {ready: false, message: 'already connected'},
+ };
+ (transport as ConnectionTransport).onmessage?.(
+ JSON.stringify(rawResponse)
+ );
+ const response = await responsePromise;
+ expect(response).toEqual(rawResponse.result);
+ connection.dispose();
+ expect(transport.closed).toBeTruthy();
+ });
+ });
+});
diff --git a/remote/test/puppeteer/test/src/browsercontext.spec.ts b/remote/test/puppeteer/test/src/browsercontext.spec.ts
index 5c1321529e08..09eb0e4e7095 100644
--- a/remote/test/puppeteer/test/src/browsercontext.spec.ts
+++ b/remote/test/puppeteer/test/src/browsercontext.spec.ts
@@ -15,10 +15,7 @@
*/
import expect from 'expect';
-import {
- getTestState,
- setupTestBrowserHooks,
-} from './mocha-utils.js';
+import {getTestState, setupTestBrowserHooks} from './mocha-utils.js';
import {waitEvent} from './utils.js';
describe('BrowserContext', function () {
diff --git a/remote/test/puppeteer/test/src/chromiumonly.spec.ts b/remote/test/puppeteer/test/src/chromiumonly.spec.ts
index 5cb9cd3f2e44..93a670f1f2ed 100644
--- a/remote/test/puppeteer/test/src/chromiumonly.spec.ts
+++ b/remote/test/puppeteer/test/src/chromiumonly.spec.ts
@@ -19,10 +19,9 @@ import {
getTestState,
setupTestBrowserHooks,
setupTestPageAndContextHooks,
- describeChromeOnly,
} from './mocha-utils.js';
-describeChromeOnly('Chromium-Specific Launcher tests', function () {
+describe('Chromium-Specific Launcher tests', function () {
describe('Puppeteer.launch |browserURL| option', function () {
it('should be able to connect using browserUrl, with and without trailing slash', async () => {
const {defaultBrowserOptions, puppeteer} = getTestState();
@@ -138,7 +137,7 @@ describeChromeOnly('Chromium-Specific Launcher tests', function () {
});
});
-describeChromeOnly('Chromium-Specific Page Tests', function () {
+describe('Chromium-Specific Page Tests', function () {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('Page.setRequestInterception should work with intervention headers', async () => {
diff --git a/remote/test/puppeteer/test/src/click.spec.ts b/remote/test/puppeteer/test/src/click.spec.ts
index 9574643bce89..b9e7f47a00b6 100644
--- a/remote/test/puppeteer/test/src/click.spec.ts
+++ b/remote/test/puppeteer/test/src/click.spec.ts
@@ -51,24 +51,21 @@ describe('Page.click', function () {
})
).toBe(42);
});
- it(
- 'should click the button if window.Node is removed',
- async () => {
- const {page, server} = getTestState();
+ it('should click the button if window.Node is removed', async () => {
+ const {page, server} = getTestState();
- await page.goto(server.PREFIX + '/input/button.html');
+ await page.goto(server.PREFIX + '/input/button.html');
+ await page.evaluate(() => {
+ // @ts-expect-error Expected.
+ return delete window.Node;
+ });
+ await page.click('button');
+ expect(
await page.evaluate(() => {
- // @ts-expect-error Expected.
- return delete window.Node;
- });
- await page.click('button');
- expect(
- await page.evaluate(() => {
- return (globalThis as any).result;
- })
- ).toBe('Clicked');
- }
- );
+ return (globalThis as any).result;
+ })
+ ).toBe('Clicked');
+ });
// @see https://github.com/puppeteer/puppeteer/issues/4281
it('should click on a span with an inline element inside', async () => {
const {page} = getTestState();
@@ -421,7 +418,7 @@ describe('Page.click', function () {
).toBe('Clicked');
});
// @see https://github.com/puppeteer/puppeteer/issues/4110
- xit('should click the button with fixed position inside an iframe', async () => {
+ it.skip('should click the button with fixed position inside an iframe', async () => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);
diff --git a/remote/test/puppeteer/test/src/cookies.spec.ts b/remote/test/puppeteer/test/src/cookies.spec.ts
index 186535317c8b..6cf1ba1686b3 100644
--- a/remote/test/puppeteer/test/src/cookies.spec.ts
+++ b/remote/test/puppeteer/test/src/cookies.spec.ts
@@ -364,22 +364,19 @@ describe('Cookie specs', () => {
'At least one of the url and domain needs to be specified'
);
});
- it(
- 'should default to setting secure cookie for HTTPS websites',
- async () => {
- const {page, server} = getTestState();
+ it('should default to setting secure cookie for HTTPS websites', async () => {
+ const {page, server} = getTestState();
- await page.goto(server.EMPTY_PAGE);
- const SECURE_URL = 'https://example.com';
- await page.setCookie({
- url: SECURE_URL,
- name: 'foo',
- value: 'bar',
- });
- const [cookie] = await page.cookies(SECURE_URL);
- expect(cookie!.secure).toBe(true);
- }
- );
+ await page.goto(server.EMPTY_PAGE);
+ const SECURE_URL = 'https://example.com';
+ await page.setCookie({
+ url: SECURE_URL,
+ name: 'foo',
+ value: 'bar',
+ });
+ const [cookie] = await page.cookies(SECURE_URL);
+ expect(cookie!.secure).toBe(true);
+ });
it('should be able to set unsecure cookie for HTTP website', async () => {
const {page, server} = getTestState();
@@ -481,67 +478,64 @@ describe('Cookie specs', () => {
},
]);
});
- it(
- 'should set secure same-site cookies from a frame',
- async () => {
- const {httpsServer, puppeteer, defaultBrowserOptions} = getTestState();
+ it('should set secure same-site cookies from a frame', async () => {
+ const {httpsServer, puppeteer, defaultBrowserOptions} = getTestState();
- const browser = await puppeteer.launch({
- ...defaultBrowserOptions,
- ignoreHTTPSErrors: true,
+ const browser = await puppeteer.launch({
+ ...defaultBrowserOptions,
+ ignoreHTTPSErrors: true,
+ });
+
+ const page = await browser.newPage();
+
+ try {
+ await page.goto(httpsServer.PREFIX + '/grid.html');
+ await page.evaluate(src => {
+ let fulfill!: () => void;
+ const promise = new Promise(x => {
+ return (fulfill = x);
+ });
+ const iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ iframe.onload = fulfill;
+ iframe.src = src;
+ return promise;
+ }, httpsServer.CROSS_PROCESS_PREFIX);
+ await page.setCookie({
+ name: '127-same-site-cookie',
+ value: 'best',
+ url: httpsServer.CROSS_PROCESS_PREFIX,
+ sameSite: 'None',
});
- const page = await browser.newPage();
-
- try {
- await page.goto(httpsServer.PREFIX + '/grid.html');
- await page.evaluate(src => {
- let fulfill!: () => void;
- const promise = new Promise(x => {
- return (fulfill = x);
- });
- const iframe = document.createElement('iframe');
- document.body.appendChild(iframe);
- iframe.onload = fulfill;
- iframe.src = src;
- return promise;
- }, httpsServer.CROSS_PROCESS_PREFIX);
- await page.setCookie({
- name: '127-same-site-cookie',
- value: 'best',
- url: httpsServer.CROSS_PROCESS_PREFIX,
- sameSite: 'None',
- });
-
- expect(await page.frames()[1]!.evaluate('document.cookie')).toBe(
- '127-same-site-cookie=best'
- );
- expectCookieEquals(
- await page.cookies(httpsServer.CROSS_PROCESS_PREFIX),
- [
- {
- name: '127-same-site-cookie',
- value: 'best',
- domain: '127.0.0.1',
- path: '/',
- sameParty: false,
- expires: -1,
- size: 24,
- httpOnly: false,
- sameSite: 'None',
- secure: true,
- session: true,
- sourcePort: 443,
- sourceScheme: 'Secure',
- },
- ]
- );
- } finally {
- await page.close();
- await browser.close();
- }
+ expect(await page.frames()[1]!.evaluate('document.cookie')).toBe(
+ '127-same-site-cookie=best'
+ );
+ expectCookieEquals(
+ await page.cookies(httpsServer.CROSS_PROCESS_PREFIX),
+ [
+ {
+ name: '127-same-site-cookie',
+ value: 'best',
+ domain: '127.0.0.1',
+ path: '/',
+ sameParty: false,
+ expires: -1,
+ size: 24,
+ httpOnly: false,
+ sameSite: 'None',
+ secure: true,
+ session: true,
+ sourcePort: 443,
+ sourceScheme: 'Secure',
+ },
+ ]
+ );
+ } finally {
+ await page.close();
+ await browser.close();
}
- );
+ });
});
describe('Page.deleteCookie', function () {
diff --git a/remote/test/puppeteer/test/src/coverage.spec.ts b/remote/test/puppeteer/test/src/coverage.spec.ts
index 6e0fc5fe55f9..2f8f2e7a1de6 100644
--- a/remote/test/puppeteer/test/src/coverage.spec.ts
+++ b/remote/test/puppeteer/test/src/coverage.spec.ts
@@ -19,11 +19,10 @@ import {
getTestState,
setupTestPageAndContextHooks,
setupTestBrowserHooks,
- describeChromeOnly,
} from './mocha-utils.js';
describe('Coverage specs', function () {
- describeChromeOnly('JSCoverage', function () {
+ describe('JSCoverage', function () {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
@@ -134,7 +133,7 @@ describe('Coverage specs', function () {
).toBeGolden('jscoverage-involved.txt');
});
// @see https://crbug.com/990945
- xit('should not hang when there is a debugger statement', async () => {
+ it.skip('should not hang when there is a debugger statement', async () => {
const {page, server} = getTestState();
await page.coverage.startJSCoverage();
@@ -190,7 +189,7 @@ describe('Coverage specs', function () {
});
});
// @see https://crbug.com/990945
- xit('should not hang when there is a debugger statement', async () => {
+ it.skip('should not hang when there is a debugger statement', async () => {
const {page, server} = getTestState();
await page.coverage.startJSCoverage();
@@ -202,7 +201,7 @@ describe('Coverage specs', function () {
});
});
- describeChromeOnly('CSSCoverage', function () {
+ describe('CSSCoverage', function () {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
diff --git a/remote/test/puppeteer/test/src/drag-and-drop.spec.ts b/remote/test/puppeteer/test/src/drag-and-drop.spec.ts
index 0b0f93b5c91e..1ce367bb8969 100644
--- a/remote/test/puppeteer/test/src/drag-and-drop.spec.ts
+++ b/remote/test/puppeteer/test/src/drag-and-drop.spec.ts
@@ -19,10 +19,9 @@ import {
getTestState,
setupTestPageAndContextHooks,
setupTestBrowserHooks,
- describeChromeOnly,
} from './mocha-utils.js';
-describeChromeOnly('Input.drag', function () {
+describe('Input.drag', function () {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('should throw an exception if not enabled before usage', async () => {
diff --git a/remote/test/puppeteer/test/src/evaluation.spec.ts b/remote/test/puppeteer/test/src/evaluation.spec.ts
index 45fe4abbcdc7..4f34e2d8079b 100644
--- a/remote/test/puppeteer/test/src/evaluation.spec.ts
+++ b/remote/test/puppeteer/test/src/evaluation.spec.ts
@@ -37,7 +37,7 @@ describe('Evaluation specs', function () {
});
expect(result).toBe(21);
});
- (bigint ? it : xit)('should transfer BigInt', async () => {
+ (bigint ? it : it.skip)('should transfer BigInt', async () => {
const {page} = getTestState();
const result = await page.evaluate((a: bigint) => {
@@ -113,18 +113,15 @@ describe('Evaluation specs', function () {
await page.goto(server.PREFIX + '/global-var.html');
expect(await page.evaluate('globalVar')).toBe(123);
});
- it(
- 'should return undefined for objects with symbols',
- async () => {
- const {page} = getTestState();
+ it('should return undefined for objects with symbols', async () => {
+ const {page} = getTestState();
- expect(
- await page.evaluate(() => {
- return [Symbol('foo4')];
- })
- ).toBe(undefined);
- }
- );
+ expect(
+ await page.evaluate(() => {
+ return [Symbol('foo4')];
+ })
+ ).toBe(undefined);
+ });
it('should work with function shorthands', async () => {
const {page} = getTestState();
@@ -261,7 +258,7 @@ describe('Evaluation specs', function () {
expect(result).not.toBe(object);
expect(result).toEqual(object);
});
- (bigint ? it : xit)('should return BigInt', async () => {
+ (bigint ? it : it.skip)('should return BigInt', async () => {
const {page} = getTestState();
const result = await page.evaluate(() => {
@@ -322,18 +319,15 @@ describe('Evaluation specs', function () {
})
).toEqual({});
});
- it(
- 'should return undefined for non-serializable objects',
- async () => {
- const {page} = getTestState();
+ it('should return undefined for non-serializable objects', async () => {
+ const {page} = getTestState();
- expect(
- await page.evaluate(() => {
- return window;
- })
- ).toBe(undefined);
- }
- );
+ expect(
+ await page.evaluate(() => {
+ return window;
+ })
+ ).toBe(undefined);
+ });
it('should fail for circular object', async () => {
const {page} = getTestState();
@@ -408,27 +402,24 @@ describe('Evaluation specs', function () {
});
expect(error.message).toContain('JSHandle is disposed');
});
- it(
- 'should throw if elementHandles are from other frames',
- async () => {
- const {page, server} = getTestState();
+ it('should throw if elementHandles are from other frames', async () => {
+ const {page, server} = getTestState();
- await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
- const bodyHandle = await page.frames()[1]!.$('body');
- let error!: Error;
- await page
- .evaluate(body => {
- return body?.innerHTML;
- }, bodyHandle)
- .catch(error_ => {
- return (error = error_);
- });
- expect(error).toBeTruthy();
- expect(error.message).toContain(
- 'JSHandles can be evaluated only in the context they were created'
- );
- }
- );
+ await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ const bodyHandle = await page.frames()[1]!.$('body');
+ let error!: Error;
+ await page
+ .evaluate(body => {
+ return body?.innerHTML;
+ }, bodyHandle)
+ .catch(error_ => {
+ return (error = error_);
+ });
+ expect(error).toBeTruthy();
+ expect(error.message).toContain(
+ 'JSHandles can be evaluated only in the context they were created'
+ );
+ });
it('should simulate a user gesture', async () => {
const {page} = getTestState();
@@ -459,19 +450,16 @@ describe('Evaluation specs', function () {
});
expect((error as Error).message).toContain('navigation');
});
- it(
- 'should not throw an error when evaluation does a navigation',
- async () => {
- const {page, server} = getTestState();
+ it('should not throw an error when evaluation does a navigation', async () => {
+ const {page, server} = getTestState();
- await page.goto(server.PREFIX + '/one-style.html');
- const result = await page.evaluate(() => {
- (window as any).location = '/empty.html';
- return [42];
- });
- expect(result).toEqual([42]);
- }
- );
+ await page.goto(server.PREFIX + '/one-style.html');
+ const result = await page.evaluate(() => {
+ (window as any).location = '/empty.html';
+ return [42];
+ });
+ expect(result).toEqual([42]);
+ });
it('should transfer 100Mb of data from page to node.js', async function () {
const {page} = getTestState();
diff --git a/remote/test/puppeteer/test/src/fixtures.spec.ts b/remote/test/puppeteer/test/src/fixtures.spec.ts
index e20054dc6dcd..ee87e7139af2 100644
--- a/remote/test/puppeteer/test/src/fixtures.spec.ts
+++ b/remote/test/puppeteer/test/src/fixtures.spec.ts
@@ -17,12 +17,12 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import expect from 'expect';
-import {getTestState, itHeadlessOnly} from './mocha-utils.js';
+import {getTestState} from './mocha-utils.js';
import path from 'path';
describe('Fixtures', function () {
- itHeadlessOnly('dumpio option should work with pipe option', async () => {
+ it('dumpio option should work with pipe option', async () => {
const {defaultBrowserOptions, puppeteerPath, headless} = getTestState();
if (headless === 'chrome') {
// This test only works in the old headless mode.
diff --git a/remote/test/puppeteer/test/src/frame.spec.ts b/remote/test/puppeteer/test/src/frame.spec.ts
index 5f10b84f711a..d8769a74a8db 100644
--- a/remote/test/puppeteer/test/src/frame.spec.ts
+++ b/remote/test/puppeteer/test/src/frame.spec.ts
@@ -137,40 +137,37 @@ describe('Frame specs', function () {
' http://localhost:/frames/frame.html (aframe)',
]);
});
- it(
- 'should send events when frames are manipulated dynamically',
- async () => {
- const {page, server} = getTestState();
+ it('should send events when frames are manipulated dynamically', async () => {
+ const {page, server} = getTestState();
- await page.goto(server.EMPTY_PAGE);
- // validate frameattached events
- const attachedFrames: Frame[] = [];
- page.on('frameattached', frame => {
- return attachedFrames.push(frame);
- });
- await utils.attachFrame(page, 'frame1', './assets/frame.html');
- expect(attachedFrames.length).toBe(1);
- expect(attachedFrames[0]!.url()).toContain('/assets/frame.html');
+ await page.goto(server.EMPTY_PAGE);
+ // validate frameattached events
+ const attachedFrames: Frame[] = [];
+ page.on('frameattached', frame => {
+ return attachedFrames.push(frame);
+ });
+ await utils.attachFrame(page, 'frame1', './assets/frame.html');
+ expect(attachedFrames.length).toBe(1);
+ expect(attachedFrames[0]!.url()).toContain('/assets/frame.html');
- // validate framenavigated events
- const navigatedFrames: Frame[] = [];
- page.on('framenavigated', frame => {
- return navigatedFrames.push(frame);
- });
- await utils.navigateFrame(page, 'frame1', './empty.html');
- expect(navigatedFrames.length).toBe(1);
- expect(navigatedFrames[0]!.url()).toBe(server.EMPTY_PAGE);
+ // validate framenavigated events
+ const navigatedFrames: Frame[] = [];
+ page.on('framenavigated', frame => {
+ return navigatedFrames.push(frame);
+ });
+ await utils.navigateFrame(page, 'frame1', './empty.html');
+ expect(navigatedFrames.length).toBe(1);
+ expect(navigatedFrames[0]!.url()).toBe(server.EMPTY_PAGE);
- // validate framedetached events
- const detachedFrames: Frame[] = [];
- page.on('framedetached', frame => {
- return detachedFrames.push(frame);
- });
- await utils.detachFrame(page, 'frame1');
- expect(detachedFrames.length).toBe(1);
- expect(detachedFrames[0]!.isDetached()).toBe(true);
- }
- );
+ // validate framedetached events
+ const detachedFrames: Frame[] = [];
+ page.on('framedetached', frame => {
+ return detachedFrames.push(frame);
+ });
+ await utils.detachFrame(page, 'frame1');
+ expect(detachedFrames.length).toBe(1);
+ expect(detachedFrames[0]!.isDetached()).toBe(true);
+ });
it('should send "framenavigated" when navigating on anchor URLs', async () => {
const {page, server} = getTestState();
@@ -299,31 +296,24 @@ describe('Frame specs', function () {
expect(page.frames()[1]!.parentFrame()).toBe(page.mainFrame());
expect(page.frames()[2]!.parentFrame()).toBe(page.mainFrame());
});
- it(
- 'should report different frame instance when frame re-attaches',
- async () => {
- const {page, server} = getTestState();
+ it('should report different frame instance when frame re-attaches', async () => {
+ const {page, server} = getTestState();
- const frame1 = await utils.attachFrame(
- page,
- 'frame1',
- server.EMPTY_PAGE
- );
- await page.evaluate(() => {
- (globalThis as any).frame = document.querySelector('#frame1');
- (globalThis as any).frame.remove();
- });
- expect(frame1!.isDetached()).toBe(true);
- const [frame2] = await Promise.all([
- utils.waitEvent(page, 'frameattached'),
- page.evaluate(() => {
- return document.body.appendChild((globalThis as any).frame);
- }),
- ]);
- expect(frame2.isDetached()).toBe(false);
- expect(frame1).not.toBe(frame2);
- }
- );
+ const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
+ await page.evaluate(() => {
+ (globalThis as any).frame = document.querySelector('#frame1');
+ (globalThis as any).frame.remove();
+ });
+ expect(frame1!.isDetached()).toBe(true);
+ const [frame2] = await Promise.all([
+ utils.waitEvent(page, 'frameattached'),
+ page.evaluate(() => {
+ return document.body.appendChild((globalThis as any).frame);
+ }),
+ ]);
+ expect(frame2.isDetached()).toBe(false);
+ expect(frame1).not.toBe(frame2);
+ });
it('should support url fragment', async () => {
const {page, server} = getTestState();
diff --git a/remote/test/puppeteer/test/src/headful.spec.ts b/remote/test/puppeteer/test/src/headful.spec.ts
index 95abfaa2c6e4..9cd9c4fb026a 100644
--- a/remote/test/puppeteer/test/src/headful.spec.ts
+++ b/remote/test/puppeteer/test/src/headful.spec.ts
@@ -24,11 +24,7 @@ import {
PuppeteerLaunchOptions,
PuppeteerNode,
} from '../../lib/cjs/puppeteer/node/Puppeteer.js';
-import {
- describeChromeOnly,
- getTestState,
- itFailsWindows,
-} from './mocha-utils.js';
+import {getTestState} from './mocha-utils.js';
const rmAsync = promisify(rimraf);
const mkdtempAsync = promisify(fs.mkdtemp);
@@ -44,7 +40,7 @@ const serviceWorkerExtensionPath = path.join(
'extension'
);
-describeChromeOnly('headful tests', function () {
+describe('headful tests', function () {
/* These tests fire up an actual browser so let's
* allow a higher timeout
*/
@@ -214,43 +210,40 @@ describeChromeOnly('headful tests', function () {
expect(pages).toEqual(['about:blank']);
await browser.close();
});
- itFailsWindows(
- 'headless should be able to read cookies written by headful',
- async () => {
- /* Needs investigation into why but this fails consistently on Windows CI. */
- const {server, puppeteer} = getTestState();
+ it('headless should be able to read cookies written by headful', async () => {
+ /* Needs investigation into why but this fails consistently on Windows CI. */
+ const {server, puppeteer} = getTestState();
- const userDataDir = await mkdtempAsync(TMP_FOLDER);
- // Write a cookie in headful chrome
- const headfulBrowser = await launchBrowser(
- puppeteer,
- Object.assign({userDataDir}, headfulOptions)
- );
- const headfulPage = await headfulBrowser.newPage();
- await headfulPage.goto(server.EMPTY_PAGE);
- await headfulPage.evaluate(() => {
- return (document.cookie =
- 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
- });
- await headfulBrowser.close();
- // Read the cookie from headless chrome
- const headlessBrowser = await launchBrowser(
- puppeteer,
- Object.assign({userDataDir}, headlessOptions)
- );
- const headlessPage = await headlessBrowser.newPage();
- await headlessPage.goto(server.EMPTY_PAGE);
- const cookie = await headlessPage.evaluate(() => {
- return document.cookie;
- });
- await headlessBrowser.close();
- // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
- await rmAsync(userDataDir).catch(() => {});
- expect(cookie).toBe('foo=true');
- }
- );
+ const userDataDir = await mkdtempAsync(TMP_FOLDER);
+ // Write a cookie in headful chrome
+ const headfulBrowser = await launchBrowser(
+ puppeteer,
+ Object.assign({userDataDir}, headfulOptions)
+ );
+ const headfulPage = await headfulBrowser.newPage();
+ await headfulPage.goto(server.EMPTY_PAGE);
+ await headfulPage.evaluate(() => {
+ return (document.cookie =
+ 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
+ });
+ await headfulBrowser.close();
+ // Read the cookie from headless chrome
+ const headlessBrowser = await launchBrowser(
+ puppeteer,
+ Object.assign({userDataDir}, headlessOptions)
+ );
+ const headlessPage = await headlessBrowser.newPage();
+ await headlessPage.goto(server.EMPTY_PAGE);
+ const cookie = await headlessPage.evaluate(() => {
+ return document.cookie;
+ });
+ await headlessBrowser.close();
+ // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
+ await rmAsync(userDataDir).catch(() => {});
+ expect(cookie).toBe('foo=true');
+ });
// TODO: Support OOOPIF. @see https://github.com/puppeteer/puppeteer/issues/2548
- xit('OOPIF: should report google.com frame', async () => {
+ it.skip('OOPIF: should report google.com frame', async () => {
const {server, puppeteer} = getTestState();
// https://google.com is isolated by default in Chromium embedder.
diff --git a/remote/test/puppeteer/test/src/ignorehttpserrors.spec.ts b/remote/test/puppeteer/test/src/ignorehttpserrors.spec.ts
index e9dd56e6cd69..fd435f2405b8 100644
--- a/remote/test/puppeteer/test/src/ignorehttpserrors.spec.ts
+++ b/remote/test/puppeteer/test/src/ignorehttpserrors.spec.ts
@@ -16,15 +16,10 @@
import expect from 'expect';
import {TLSSocket} from 'tls';
-import {
- Browser,
- BrowserContext,
-} from '../../lib/cjs/puppeteer/common/Browser.js';
+import {Browser, BrowserContext} from '../../lib/cjs/puppeteer/api/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {HTTPResponse} from '../../lib/cjs/puppeteer/common/HTTPResponse.js';
-import {
- getTestState
-} from './mocha-utils.js';
+import {getTestState} from './mocha-utils.js';
describe('ignoreHTTPSErrors', function () {
/* Note that this test creates its own browser rather than use
diff --git a/remote/test/puppeteer/test/src/injected.spec.ts b/remote/test/puppeteer/test/src/injected.spec.ts
index b2fa5ed8b5c9..a970fd38777f 100644
--- a/remote/test/puppeteer/test/src/injected.spec.ts
+++ b/remote/test/puppeteer/test/src/injected.spec.ts
@@ -23,18 +23,35 @@ import {
setupTestPageAndContextHooks,
} from './mocha-utils.js';
-describe('InjectedUtil tests', function () {
+describe('PuppeteerUtil tests', function () {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('should work', async () => {
const {page} = getTestState();
- const handle = await page
- .mainFrame()
- .worlds[PUPPETEER_WORLD].evaluate(() => {
- return typeof InjectedUtil === 'object';
- });
- expect(handle).toBeTruthy();
+ const world = page.mainFrame().worlds[PUPPETEER_WORLD];
+ const value = await world.evaluate(PuppeteerUtil => {
+ return typeof PuppeteerUtil === 'object';
+ }, world.puppeteerUtil);
+ expect(value).toBeTruthy();
+ });
+
+ describe('createFunction tests', function () {
+ it('should work', async () => {
+ const {page} = getTestState();
+
+ const world = page.mainFrame().worlds[PUPPETEER_WORLD];
+ const value = await world.evaluate(
+ ({createFunction}, fnString) => {
+ return createFunction(fnString)(4);
+ },
+ await world.puppeteerUtil,
+ (() => {
+ return 4;
+ }).toString()
+ );
+ expect(value).toBe(4);
+ });
});
});
diff --git a/remote/test/puppeteer/test/src/input.spec.ts b/remote/test/puppeteer/test/src/input.spec.ts
index 831c362e40fd..925125a2c74f 100644
--- a/remote/test/puppeteer/test/src/input.spec.ts
+++ b/remote/test/puppeteer/test/src/input.spec.ts
@@ -20,7 +20,6 @@ import {
getTestState,
setupTestBrowserHooks,
setupTestPageAndContextHooks,
- describeFailsFirefox,
} from './mocha-utils.js';
const FILE_TO_UPLOAD = path.join(__dirname, '/../assets/file-to-upload.txt');
@@ -29,7 +28,7 @@ describe('input tests', function () {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
- describeFailsFirefox('input', function () {
+ describe('input', function () {
it('should upload the file', async () => {
const {page, server} = getTestState();
@@ -76,7 +75,7 @@ describe('input tests', function () {
});
});
- describeFailsFirefox('Page.waitForFileChooser', function () {
+ describe('Page.waitForFileChooser', function () {
it('should work when file input is attached to DOM', async () => {
const {page} = getTestState();
@@ -159,7 +158,7 @@ describe('input tests', function () {
});
});
- describeFailsFirefox('FileChooser.accept', function () {
+ describe('FileChooser.accept', function () {
it('should accept single file', async () => {
const {page} = getTestState();
@@ -325,7 +324,7 @@ describe('input tests', function () {
});
});
- describeFailsFirefox('FileChooser.cancel', function () {
+ describe('FileChooser.cancel', function () {
it('should cancel dialog', async () => {
const {page} = getTestState();
@@ -373,7 +372,7 @@ describe('input tests', function () {
});
});
- describeFailsFirefox('FileChooser.isMultiple', () => {
+ describe('FileChooser.isMultiple', () => {
it('should work for single file pick', async () => {
const {page} = getTestState();
diff --git a/remote/test/puppeteer/test/src/keyboard.spec.ts b/remote/test/puppeteer/test/src/keyboard.spec.ts
index 0bcb2a57f448..f6d0ce0a83b6 100644
--- a/remote/test/puppeteer/test/src/keyboard.spec.ts
+++ b/remote/test/puppeteer/test/src/keyboard.spec.ts
@@ -119,21 +119,18 @@ describe('Keyboard', function () {
})
).toBe('a');
});
- it(
- 'ElementHandle.press should support |text| option',
- async () => {
- const {page, server} = getTestState();
+ it('ElementHandle.press should support |text| option', async () => {
+ const {page, server} = getTestState();
- await page.goto(server.PREFIX + '/input/textarea.html');
- const textarea = (await page.$('textarea'))!;
- await textarea.press('a', {text: 'Ñ‘'});
- expect(
- await page.evaluate(() => {
- return document.querySelector('textarea')!.value;
- })
- ).toBe('Ñ‘');
- }
- );
+ await page.goto(server.PREFIX + '/input/textarea.html');
+ const textarea = (await page.$('textarea'))!;
+ await textarea.press('a', {text: 'Ñ‘'});
+ expect(
+ await page.evaluate(() => {
+ return document.querySelector('textarea')!.value;
+ })
+ ).toBe('Ñ‘');
+ });
it('should send a character with sendCharacter', async () => {
const {page, server} = getTestState();
diff --git a/remote/test/puppeteer/test/src/launcher.spec.ts b/remote/test/puppeteer/test/src/launcher.spec.ts
index 8ecaa5f61921..3f6a5a6897b3 100644
--- a/remote/test/puppeteer/test/src/launcher.spec.ts
+++ b/remote/test/puppeteer/test/src/launcher.spec.ts
@@ -24,12 +24,7 @@ import {TLSSocket} from 'tls';
import {promisify} from 'util';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {Product} from '../../lib/cjs/puppeteer/common/Product.js';
-import {
- getTestState,
- itChromeOnly,
- itFirefoxOnly,
- itOnlyRegularInstall,
-} from './mocha-utils.js';
+import {getTestState, itOnlyRegularInstall} from './mocha-utils.js';
import utils from './utils.js';
const mkdtempAsync = promisify(fs.mkdtemp);
@@ -208,6 +203,11 @@ describe('Launcher specs', function () {
});
});
describe('Puppeteer.launch', function () {
+ it('can launch and close the browser', async () => {
+ const {defaultBrowserOptions, puppeteer} = getTestState();
+ const browser = await puppeteer.launch(defaultBrowserOptions);
+ await browser.close();
+ });
it('should reject all promises when browser is closed', async () => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const browser = await puppeteer.launch(defaultBrowserOptions);
@@ -250,7 +250,7 @@ describe('Launcher specs', function () {
// This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
await rmAsync(userDataDir).catch(() => {});
});
- itChromeOnly('tmp profile should be cleaned up', async () => {
+ it('tmp profile should be cleaned up', async () => {
const {defaultBrowserOptions, puppeteer} = getTestState();
// Set a custom test tmp dir so that we can validate that
@@ -279,7 +279,7 @@ describe('Launcher specs', function () {
// Restore env var
process.env['PUPPETEER_TMP_DIR'] = '';
});
- itFirefoxOnly('userDataDir option restores preferences', async () => {
+ it('userDataDir option restores preferences', async () => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const userDataDir = await mkdtempAsync(TMP_FOLDER);
@@ -325,7 +325,7 @@ describe('Launcher specs', function () {
// This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
await rmAsync(userDataDir).catch(() => {});
});
- itChromeOnly('userDataDir argument with non-existent dir', async () => {
+ it('userDataDir argument with non-existent dir', async () => {
const {isChrome, puppeteer, defaultBrowserOptions} = getTestState();
const userDataDir = await mkdtempAsync(TMP_FOLDER);
@@ -459,49 +459,43 @@ describe('Launcher specs', function () {
await page.close();
await browser.close();
});
- itChromeOnly(
- 'should filter out ignored default arguments in Chrome',
- async () => {
- const {defaultBrowserOptions, puppeteer} = getTestState();
- // Make sure we launch with `--enable-automation` by default.
- const defaultArgs = puppeteer.defaultArgs();
- const browser = await puppeteer.launch(
- Object.assign({}, defaultBrowserOptions, {
- // Ignore first and third default argument.
- ignoreDefaultArgs: [defaultArgs[0]!, defaultArgs[2]],
- })
- );
- const spawnargs = browser.process()!.spawnargs;
- if (!spawnargs) {
- throw new Error('spawnargs not present');
- }
- expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1);
- expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1);
- expect(spawnargs.indexOf(defaultArgs[2]!)).toBe(-1);
- await browser.close();
+ it('should filter out ignored default arguments in Chrome', async () => {
+ const {defaultBrowserOptions, puppeteer} = getTestState();
+ // Make sure we launch with `--enable-automation` by default.
+ const defaultArgs = puppeteer.defaultArgs();
+ const browser = await puppeteer.launch(
+ Object.assign({}, defaultBrowserOptions, {
+ // Ignore first and third default argument.
+ ignoreDefaultArgs: [defaultArgs[0]!, defaultArgs[2]],
+ })
+ );
+ const spawnargs = browser.process()!.spawnargs;
+ if (!spawnargs) {
+ throw new Error('spawnargs not present');
}
- );
- itFirefoxOnly(
- 'should filter out ignored default argument in Firefox',
- async () => {
- const {defaultBrowserOptions, puppeteer} = getTestState();
+ expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1);
+ expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1);
+ expect(spawnargs.indexOf(defaultArgs[2]!)).toBe(-1);
+ await browser.close();
+ });
+ it('should filter out ignored default argument in Firefox', async () => {
+ const {defaultBrowserOptions, puppeteer} = getTestState();
- const defaultArgs = puppeteer.defaultArgs();
- const browser = await puppeteer.launch(
- Object.assign({}, defaultBrowserOptions, {
- // Only the first argument is fixed, others are optional.
- ignoreDefaultArgs: [defaultArgs[0]!],
- })
- );
- const spawnargs = browser.process()!.spawnargs;
- if (!spawnargs) {
- throw new Error('spawnargs not present');
- }
- expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1);
- expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1);
- await browser.close();
+ const defaultArgs = puppeteer.defaultArgs();
+ const browser = await puppeteer.launch(
+ Object.assign({}, defaultBrowserOptions, {
+ // Only the first argument is fixed, others are optional.
+ ignoreDefaultArgs: [defaultArgs[0]!],
+ })
+ );
+ const spawnargs = browser.process()!.spawnargs;
+ if (!spawnargs) {
+ throw new Error('spawnargs not present');
}
- );
+ expect(spawnargs.indexOf(defaultArgs[0]!)).toBe(-1);
+ expect(spawnargs.indexOf(defaultArgs[1]!)).not.toBe(-1);
+ await browser.close();
+ });
it('should have default URL when launching browser', async function () {
const {defaultBrowserOptions, puppeteer} = getTestState();
const browser = await puppeteer.launch(defaultBrowserOptions);
@@ -511,24 +505,21 @@ describe('Launcher specs', function () {
expect(pages).toEqual(['about:blank']);
await browser.close();
});
- it(
- 'should have custom URL when launching browser',
- async () => {
- const {server, puppeteer, defaultBrowserOptions} = getTestState();
+ it('should have custom URL when launching browser', async () => {
+ const {server, puppeteer, defaultBrowserOptions} = getTestState();
- const options = Object.assign({}, defaultBrowserOptions);
- options.args = [server.EMPTY_PAGE].concat(options.args || []);
- const browser = await puppeteer.launch(options);
- const pages = await browser.pages();
- expect(pages.length).toBe(1);
- const page = pages[0]!;
- if (page.url() !== server.EMPTY_PAGE) {
- await page.waitForNavigation();
- }
- expect(page.url()).toBe(server.EMPTY_PAGE);
- await browser.close();
+ const options = Object.assign({}, defaultBrowserOptions);
+ options.args = [server.EMPTY_PAGE].concat(options.args || []);
+ const browser = await puppeteer.launch(options);
+ const pages = await browser.pages();
+ expect(pages.length).toBe(1);
+ const page = pages[0]!;
+ if (page.url() !== server.EMPTY_PAGE) {
+ await page.waitForNavigation();
}
- );
+ expect(page.url()).toBe(server.EMPTY_PAGE);
+ await browser.close();
+ });
it('should pass the timeout parameter to browser.waitForTarget', async () => {
const {puppeteer, defaultBrowserOptions} = getTestState();
const options = Object.assign({}, defaultBrowserOptions, {
@@ -614,24 +605,21 @@ describe('Launcher specs', function () {
});
expect(error.message).toContain('either pipe or debugging port');
});
- itChromeOnly(
- 'should launch Chrome properly with --no-startup-window and waitForInitialPage=false',
- async () => {
- const {defaultBrowserOptions, puppeteer} = getTestState();
- const options = {
- waitForInitialPage: false,
- // This is needed to prevent Puppeteer from adding an initial blank page.
- // See also https://github.com/puppeteer/puppeteer/blob/ad6b736039436fcc5c0a262e5b575aa041427be3/src/node/Launcher.ts#L200
- ignoreDefaultArgs: true,
- ...defaultBrowserOptions,
- args: ['--no-startup-window'],
- };
- const browser = await puppeteer.launch(options);
- const pages = await browser.pages();
- expect(pages.length).toBe(0);
- await browser.close();
- }
- );
+ it('should launch Chrome properly with --no-startup-window and waitForInitialPage=false', async () => {
+ const {defaultBrowserOptions, puppeteer} = getTestState();
+ const options = {
+ waitForInitialPage: false,
+ // This is needed to prevent Puppeteer from adding an initial blank page.
+ // See also https://github.com/puppeteer/puppeteer/blob/ad6b736039436fcc5c0a262e5b575aa041427be3/src/node/Launcher.ts#L200
+ ignoreDefaultArgs: true,
+ ...defaultBrowserOptions,
+ args: ['--no-startup-window'],
+ };
+ const browser = await puppeteer.launch(options);
+ const pages = await browser.pages();
+ expect(pages.length).toBe(0);
+ await browser.close();
+ });
});
describe('Puppeteer.launch', function () {
@@ -808,68 +796,62 @@ describe('Launcher specs', function () {
.sort()
).toEqual(['about:blank', server.EMPTY_PAGE]);
});
- it(
- 'should be able to reconnect to a disconnected browser',
- async () => {
- const {server, puppeteer, defaultBrowserOptions} = getTestState();
+ it('should be able to reconnect to a disconnected browser', async () => {
+ const {server, puppeteer, defaultBrowserOptions} = getTestState();
- const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
- const browserWSEndpoint = originalBrowser.wsEndpoint();
- const page = await originalBrowser.newPage();
- await page.goto(server.PREFIX + '/frames/nested-frames.html');
- originalBrowser.disconnect();
+ const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
+ const browserWSEndpoint = originalBrowser.wsEndpoint();
+ const page = await originalBrowser.newPage();
+ await page.goto(server.PREFIX + '/frames/nested-frames.html');
+ originalBrowser.disconnect();
- const browser = await puppeteer.connect({browserWSEndpoint});
- const pages = await browser.pages();
- const restoredPage = pages.find(page => {
- return page.url() === server.PREFIX + '/frames/nested-frames.html';
- })!;
- expect(utils.dumpFrames(restoredPage.mainFrame())).toEqual([
- 'http://localhost:/frames/nested-frames.html',
- ' http://localhost:/frames/two-frames.html (2frames)',
- ' http://localhost:/frames/frame.html (uno)',
- ' http://localhost:/frames/frame.html (dos)',
- ' http://localhost:/frames/frame.html (aframe)',
- ]);
- expect(
- await restoredPage.evaluate(() => {
- return 7 * 8;
- })
- ).toBe(56);
- await browser.close();
- }
- );
+ const browser = await puppeteer.connect({browserWSEndpoint});
+ const pages = await browser.pages();
+ const restoredPage = pages.find(page => {
+ return page.url() === server.PREFIX + '/frames/nested-frames.html';
+ })!;
+ expect(utils.dumpFrames(restoredPage.mainFrame())).toEqual([
+ 'http://localhost:/frames/nested-frames.html',
+ ' http://localhost:/frames/two-frames.html (2frames)',
+ ' http://localhost:/frames/frame.html (uno)',
+ ' http://localhost:/frames/frame.html (dos)',
+ ' http://localhost:/frames/frame.html (aframe)',
+ ]);
+ expect(
+ await restoredPage.evaluate(() => {
+ return 7 * 8;
+ })
+ ).toBe(56);
+ await browser.close();
+ });
// @see https://github.com/puppeteer/puppeteer/issues/4197#issuecomment-481793410
- it(
- 'should be able to connect to the same page simultaneously',
- async () => {
- const {puppeteer, defaultBrowserOptions} = getTestState();
+ it('should be able to connect to the same page simultaneously', async () => {
+ const {puppeteer, defaultBrowserOptions} = getTestState();
- const browserOne = await puppeteer.launch(defaultBrowserOptions);
- const browserTwo = await puppeteer.connect({
- browserWSEndpoint: browserOne.wsEndpoint(),
- });
- const [page1, page2] = await Promise.all([
- new Promise(x => {
- return browserOne.once('targetcreated', target => {
- return x(target.page());
- });
- }),
- browserTwo.newPage(),
- ]);
- expect(
- await page1.evaluate(() => {
- return 7 * 8;
- })
- ).toBe(56);
- expect(
- await page2.evaluate(() => {
- return 7 * 6;
- })
- ).toBe(42);
- await browserOne.close();
- }
- );
+ const browserOne = await puppeteer.launch(defaultBrowserOptions);
+ const browserTwo = await puppeteer.connect({
+ browserWSEndpoint: browserOne.wsEndpoint(),
+ });
+ const [page1, page2] = await Promise.all([
+ new Promise(x => {
+ return browserOne.once('targetcreated', target => {
+ return x(target.page());
+ });
+ }),
+ browserTwo.newPage(),
+ ]);
+ expect(
+ await page1.evaluate(() => {
+ return 7 * 8;
+ })
+ ).toBe(56);
+ expect(
+ await page2.evaluate(() => {
+ return 7 * 6;
+ })
+ ).toBe(42);
+ await browserOne.close();
+ });
it('should be able to reconnect', async () => {
const {puppeteer, server, defaultBrowserOptions} = getTestState();
const browserOne = await puppeteer.launch(defaultBrowserOptions);
@@ -932,7 +914,7 @@ describe('Launcher specs', function () {
describe('when the product is chrome, platform is not darwin, and arch is arm64', () => {
describe('and the executable exists', () => {
- itChromeOnly('returns /usr/bin/chromium-browser', async () => {
+ it('returns /usr/bin/chromium-browser', async () => {
const {puppeteer} = getTestState();
const osPlatformStub = sinon.stub(os, 'platform').returns('linux');
const osArchStub = sinon.stub(os, 'arch').returns('arm64');
@@ -971,26 +953,21 @@ describe('Launcher specs', function () {
});
});
describe('and the executable does not exist', () => {
- itChromeOnly(
- 'does not return /usr/bin/chromium-browser',
- async () => {
- const {puppeteer} = getTestState();
- const osPlatformStub = sinon
- .stub(os, 'platform')
- .returns('linux');
- const osArchStub = sinon.stub(os, 'arch').returns('arm64');
- const fsExistsStub = sinon.stub(fs, 'existsSync');
- fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(false);
+ it('does not return /usr/bin/chromium-browser', async () => {
+ const {puppeteer} = getTestState();
+ const osPlatformStub = sinon.stub(os, 'platform').returns('linux');
+ const osArchStub = sinon.stub(os, 'arch').returns('arm64');
+ const fsExistsStub = sinon.stub(fs, 'existsSync');
+ fsExistsStub.withArgs('/usr/bin/chromium-browser').returns(false);
- const executablePath = puppeteer.executablePath();
+ const executablePath = puppeteer.executablePath();
- expect(executablePath).not.toEqual('/usr/bin/chromium-browser');
+ expect(executablePath).not.toEqual('/usr/bin/chromium-browser');
- osPlatformStub.restore();
- osArchStub.restore();
- fsExistsStub.restore();
- }
- );
+ osPlatformStub.restore();
+ osArchStub.restore();
+ fsExistsStub.restore();
+ });
});
});
});
@@ -1020,51 +997,48 @@ describe('Launcher specs', function () {
});
describe('Browser.Events.disconnected', function () {
- it(
- 'should be emitted when: browser gets closed, disconnected or underlying websocket gets closed',
- async () => {
- const {puppeteer, defaultBrowserOptions} = getTestState();
- const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
- const browserWSEndpoint = originalBrowser.wsEndpoint();
- const remoteBrowser1 = await puppeteer.connect({
- browserWSEndpoint,
- });
- const remoteBrowser2 = await puppeteer.connect({
- browserWSEndpoint,
- });
+ it('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async () => {
+ const {puppeteer, defaultBrowserOptions} = getTestState();
+ const originalBrowser = await puppeteer.launch(defaultBrowserOptions);
+ const browserWSEndpoint = originalBrowser.wsEndpoint();
+ const remoteBrowser1 = await puppeteer.connect({
+ browserWSEndpoint,
+ });
+ const remoteBrowser2 = await puppeteer.connect({
+ browserWSEndpoint,
+ });
- let disconnectedOriginal = 0;
- let disconnectedRemote1 = 0;
- let disconnectedRemote2 = 0;
- originalBrowser.on('disconnected', () => {
- return ++disconnectedOriginal;
- });
- remoteBrowser1.on('disconnected', () => {
- return ++disconnectedRemote1;
- });
- remoteBrowser2.on('disconnected', () => {
- return ++disconnectedRemote2;
- });
+ let disconnectedOriginal = 0;
+ let disconnectedRemote1 = 0;
+ let disconnectedRemote2 = 0;
+ originalBrowser.on('disconnected', () => {
+ return ++disconnectedOriginal;
+ });
+ remoteBrowser1.on('disconnected', () => {
+ return ++disconnectedRemote1;
+ });
+ remoteBrowser2.on('disconnected', () => {
+ return ++disconnectedRemote2;
+ });
- await Promise.all([
- utils.waitEvent(remoteBrowser2, 'disconnected'),
- remoteBrowser2.disconnect(),
- ]);
+ await Promise.all([
+ utils.waitEvent(remoteBrowser2, 'disconnected'),
+ remoteBrowser2.disconnect(),
+ ]);
- expect(disconnectedOriginal).toBe(0);
- expect(disconnectedRemote1).toBe(0);
- expect(disconnectedRemote2).toBe(1);
+ expect(disconnectedOriginal).toBe(0);
+ expect(disconnectedRemote1).toBe(0);
+ expect(disconnectedRemote2).toBe(1);
- await Promise.all([
- utils.waitEvent(remoteBrowser1, 'disconnected'),
- utils.waitEvent(originalBrowser, 'disconnected'),
- originalBrowser.close(),
- ]);
+ await Promise.all([
+ utils.waitEvent(remoteBrowser1, 'disconnected'),
+ utils.waitEvent(originalBrowser, 'disconnected'),
+ originalBrowser.close(),
+ ]);
- expect(disconnectedOriginal).toBe(1);
- expect(disconnectedRemote1).toBe(1);
- expect(disconnectedRemote2).toBe(1);
- }
- );
+ expect(disconnectedOriginal).toBe(1);
+ expect(disconnectedRemote1).toBe(1);
+ expect(disconnectedRemote2).toBe(1);
+ });
});
});
diff --git a/remote/test/puppeteer/test/src/mocha-utils.ts b/remote/test/puppeteer/test/src/mocha-utils.ts
index 709a67158f3b..7d819921d0a1 100644
--- a/remote/test/puppeteer/test/src/mocha-utils.ts
+++ b/remote/test/puppeteer/test/src/mocha-utils.ts
@@ -17,14 +17,10 @@
import Protocol from 'devtools-protocol';
import expect from 'expect';
import * as fs from 'fs';
-import * as os from 'os';
import * as path from 'path';
import rimraf from 'rimraf';
import sinon from 'sinon';
-import {
- Browser,
- BrowserContext,
-} from '../../lib/cjs/puppeteer/common/Browser.js';
+import {Browser, BrowserContext} from '../../lib/cjs/puppeteer/api/Browser.js';
import {Page} from '../../lib/cjs/puppeteer/common/Page.js';
import {isErrorLike} from '../../lib/cjs/puppeteer/util/ErrorLike.js';
import {
@@ -34,6 +30,7 @@ import {
import puppeteer from '../../lib/cjs/puppeteer/puppeteer.js';
import {TestServer} from '../../utils/testserver/lib/index.js';
import {extendExpectWithToBeGolden} from './utils.js';
+import * as Mocha from 'mocha';
const setupServer = async () => {
const assetsPath = path.join(__dirname, '../assets');
@@ -63,14 +60,15 @@ export const getTestState = (): PuppeteerTestState => {
};
const product =
- process.env['PRODUCT'] || process.env['PUPPETEER_PRODUCT'] || 'Chromium';
+ process.env['PRODUCT'] || process.env['PUPPETEER_PRODUCT'] || 'chrome';
const alternativeInstall = process.env['PUPPETEER_ALT_INSTALL'] || false;
const headless = (process.env['HEADLESS'] || 'true').trim().toLowerCase();
const isHeadless = headless === 'true' || headless === 'chrome';
const isFirefox = product === 'firefox';
-const isChrome = product === 'Chromium';
+const isChrome = product === 'chrome';
+const protocol = process.env['PUPPETEER_PROTOCOL'] || 'cdp';
let extraLaunchOptions = {};
try {
@@ -91,6 +89,7 @@ const defaultBrowserOptions = Object.assign(
executablePath: process.env['BINARY'],
headless: headless === 'chrome' ? ('chrome' as const) : isHeadless,
dumpio: !!process.env['DUMPIO'],
+ protocol: protocol as 'cdp' | 'webDriverBiDi',
},
extraLaunchOptions
);
@@ -125,7 +124,11 @@ declare module 'expect/build/types' {
}
const setupGoldenAssertions = (): void => {
- const suffix = product.toLowerCase();
+ let suffix = product.toLowerCase();
+ if (suffix === 'chrome') {
+ // TODO: to avoid moving golden folders.
+ suffix = 'chromium';
+ }
const GOLDEN_DIR = path.join(__dirname, `../golden-${suffix}`);
const OUTPUT_DIR = path.join(__dirname, `../output-${suffix}`);
if (fs.existsSync(OUTPUT_DIR)) {
@@ -152,116 +155,21 @@ interface PuppeteerTestState {
}
const state: Partial