This change turns quoted match to be case-sensitive (as before),
but not strictly full-string for the whole element's text.
This is a fix for a case where element contains text nodes and child elements:
```html
<div>text1<span>child node</span>text2</div>
```
We now match this div by `text="text1"` and `text="text2"`.
Cookies have a "Secure" attribute which tells the browsers
that a given cookie should only be sent via HTTPS. In it's
absense "Secure" is falsy and these cookies should be sent
with both HTTP and HTTPS requests. Playwright now returns
only the "Non-Secure" cookies for HTTP URLs, and both
"Secure" and "Non-Secure" cookies for HTTPS URLs.
Fixes#5504
We might not ever get the "download finished" event when closing the context:
- in Chromium, for any ongoing download;
- in all browsers, for failed downloads.
This should not prevent closing the context. Instead of waiting for the
download and then deleting it, we force delete it immediately and reject
any promises waiting for the download completion.
We get relative registry path when PLAYWRIGHT_BROWSERS_PATH or HOME is relative.
In this case, it would be good to resolve to the same absolute path
during installation and execution, and we can usually do that using INIT_CWD.
- Remove label retargeting, as it does not play nicely with recorder.
- nth-match() is now correctly chained.
- Performance improvements around parent selectors and regex text matches.
- Do not check children when parent does not contain the text we look for.
- Minor caching improvements in evaluator.
This gives up to 5X performance boost on text-heavy pages.
- Snap to buttons, inputs, selects, etc.
- Try `<label>` selector in addition to the element.
- Use parent selectors when needed.
- Remove xpath fallback as it should be covered with css.
Consider the following situation (one among many possible).
- FrameA has an oopif child FrameB;
- FrameA navigates to same-process origin (e.g. about:blank);
- at the same time, FrameC is attached to the FrameB in the
FrameB's process.
In this case, we get `frameNavigated` event for FrameA, immediately
followed by `frameAttached` event for FrameC. Since we detach all
FrameA's child frames on navigation, including the oopif FrameB,
there is no parent frame for FrameC to attach to.
In general, multiple processes coming from oopif may send their
events in wildly different order, and their view about the frame
tree may not always correspond to the "up to date" frame tree as
seen from the main frame's process. We try to keep our frame tree
aligned with what main process thinks, and ignore events that
reference frames absent in this tree.
Drive-by: handle filechooser exceptions because of async processing.
This changes `text=` and `:text()` selectors to match the element when:
- it's combined text content matches the text;
- combined text content of any immediate child does not match the text.
This allows the following markup to match "Some bold and italics text":
`<div>Some <b>bold</b> and <i>italics</i> text</div>`.
For the reference, "combined text content" is almost equal to `element.textContent`,
but with some changes like using value of `<input type=button>` or ignoring `<head>`.
This also includes some caching optimizations, meaningful in complex matches
that involve multiple calls to the text engine.
Performance changes (measured on large page with ~25000 elements):
- `:has-text()` - 14% faster.
- `text=` - 50% faster.
- `:text()` - 0-35% slower.
- `:text-matches()` - 28% slower.
This patch starts downloading FFMPEG like we download our browsers
instead of bundling it in the NPM package.
With this patch, NPM size is reduced from 8.8MB to 1.7MB.
Consequences:
- `npx playwright` is drastically faster now
- playwright driver for language bindings is way smaller
- projects that bundle Playwright can pass Apple Notorization
Fixes#5193
- We don't need this, since it should propagate from the main frame.
- Forcing focus in oopif immediately focuses it and blurs currently
focused frame. This leads to undesired side effects, e.g. selects
being closed.
This switches vp8 to "realtime" mode that works fast, adapting to
the speed of incoming frames, and produces the best quality in can
given realtime constraints.
In practice, this gives 2x larger video files but no noticible quality
difference. It also eliminates huge delays for encoding the video.
Drive-by: document our ffmpeg option choices and add some links
to documentation for future use.
This change is adding a new property on the BrowserContextOptions class called `_debugName`. This property allows defining a user-friendly name for the browser context, and currently it is being used in one place, the Trace Viewer. When user provides the new value in the following way:
```typescript
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext({ _traceDir: __dirname, _debugName: 'My custom testcase name' });
await context.close();
await browser.close();
})();
```
The `_debugName` will be saved in the `*.trace` file for this browser context, on the `context-created` event, under the key `debugName`.
Later, when such a trace is displayed using Trace Viewer, the `debugName` will be displayed in the dropdown in the top right part of the app instead of the actual trace filename.
Fixes#5157.
- Instead of capturing snapshots on demand, we now stream them
from each frame every 100ms.
- Certain actions can also force snapshots at particular moment using
"checkpoints".
- Trace viewer is able to show the page snapshot at a particular
timestamp, or using a "checkpoint" snapshot.
- Small optimization to not process stylesheets if CSSOM was not used.
There still is a lot of room for improvement.
When `page.reload()` is racing against the renderer-initiated
navigation, we might end up with `waitForNavigation()` being rejected
before the reload implementation is able to catch it.
To avoid that, carefully use Promise.all and await `waitForNavigation`
from the get go.
Same happens to `page.goForward()` and `page.goBack()`.
This changes quoted text selector like `text="Foo Bar"` to perform
normalized whitespace match.
Most of the time users want to match some string visible on the page,
and that always means normalized whitespace.
We keep the case sensitivity and full-string vs substring difference
between quoted and unquoted matches.
This adds `{Page,Frame}.isChecked(selector)` and `ElementHandle.isChecked()` methods.
Useful to do assertions in tests:
```js
await page.click('text="Add TODO"');
expect(await page.isChecked('.item-done')).toBe(false);
```
These methods are useful for verification in tests, e.g.
```js
expect(await page.isEnabled(':text("Remove All")')).toBe(false);
await page.click(':text("Add Item")');
expect(await page.isVisible('.item:text("new item")')).toBe(true);
expect(await page.isEnabled(':text("Remove All")')).toBe(true);
```
When element with position:sticky covers some part of
the scroll container, we could fail to scroll from under it
to perform an action. To fight this, we can try different
scroll alignments and scroll to the top/bottom/center
in the attempt to scroll away from sticky header/footer/sidebar.
Windows 7 was end-of-lifed on January 14, 2020. We don't support this
system, but we'd like to have a best-effort to work there.
It does look like Chromium is missing some libraries on Win 7, however
it still manages to work there. To support this usecase, this patch
starts printing console warning about missing libraries on Win 7 only
instead of refusing to launch.
Fixes#3496
We now default to `text` that does substring case-insensitive match
with normalized whitespace. `text-is` matches the whole string.
`matches-text` is renamed to `text-matches`.
When parsing CSS, we assume everything is a valid CSS function,
unless it is in the list of custom functions. This way we'll parse
future CSS functions automatically.
Currently, we always throw from FrameSession._stopScreencast
when not running with video, and immediately catch it in
CRPage.didClose (thanks to debugger to point that).
Overall, we have code prepared for start/stop API, which
we never did, so it makes sense to simplify code a bit,
and throw if something goes wrong.
This fixes the local -> remote frame swap when
Page.frameDetached arrives before Target.attachedToTarget.
Instead of error-prone logic we do currently, new CDP exposes
frame detach reason that we can use.
New webkit build, generated by 19f21b1bde, changed webkit build
layout: now there are subfolders that contain libraries and executables, and some of the dependencies are no longer bundled.
This patch:
- teaches launch doctor new directories structure: subfolders are now inspected for missing dependencies, and they are also used in the `LD_LIBRARY_PATH`.
- adds `libevent` and `libicudata` libs to the mapping for ubuntu 18.04
This enables filling the input based on the connected label:
```html
<label for=target>Name</label><input id=target>
```
```js
await page.fill('text=Name', 'Alice');
```
Consider the following sequence:
- page opens a popup;
- popup target is attached, we start initializing it;
- user calls browser.close();
- browser is closed, and popup initialization fails;
- we report "errored page" on the already closed context;
- RPC client cannot make sense of this:
"Cannot find parent object BrowserContext@guid to create Frame@guid"
This issue was revealed during Firefox pipe migration.
Certain environments, e.g. Azure Pipelines, override default user
inside container with a custom one, whereas fail to pass proper
seccomp profile for the docker image.
As a result, chromium sandboxing fails.
To ease life of devops deploying tests in various CI's, this patch
disables Chromium sandbox by default.
References #4084
This saves some CPU cycles while waiting for the page to
change the state, e.g. for animations to complete.
Note that retrying logic is only applicable in rare
circumstances like unexpected scroll in the middle of an
action, or some overlay blocking the click. Usually,
action times out in this cases while retrying.
We currently spawn a process per page when recording
video in Chromium. This triggers "too many listeners" on the
process object once you have enough pages open.
- This leaves just `recordVideos` and `videoSize` options on the context.
- Videos are saved to `artifactsPath`. We also save their ids to trace.
- `context.close()` waits for the processed videos.
api(trace): introduce artifacts options
This introduces launch({ artifactsPath }) and newContext({ relativeArtifactsPath, recordTrace }) options.
- artifactsPath option controls the directory where all artifacts go. If not passed, artifacts are not collected.
- relativeArtifactsPath can be used to put context-specific artifacts into a subfolder. If not passed, shared artifactsPath is used.
- recordTrace controls trace recording.
We also expose trace types under playwright/types/trace.d.ts.
In the follow up:
- videos will be put into artifactsPath;
- downloads will be put into artifactsPath, or keep using existing downloadsPath when artifactsPath is not specified.
These methods are the only users of waitForNavigation and
waitForLoadState on the server side. This refactor lifts the
Progress wrapper to the top-most goBack/goForward/reload call
and leaves waitForNavigation/waitForLoadState as internal helpers.
This way we get a single Progress for the actual api call.
We now use 'launch' under the hood, which erroneously throws
when 'port' is present.
Instead, moved validation to the client side where it belongs,
added tests for validation errors.
- Fill and click actions pass metadata to Progress.
- Progress reports success/failure through instrumentation.
- Tracer consumes ActionResult and ActionMetadata and records them.
Currently, only click and fill actions pass metadata to
contain the size of the change. Everything else should follow.
- We do not need the public BrowserType different from BrowserTypeBase anymore.
- Removing 'logName' parameter from runAbortableTask - it will
be used for metadata instead.
This patch:
- moves ffmpeg binaries from `//bin/` to `//third_party/ffmpeg`
- adds [COPYING.GPLv3](https://github.com/FFmpeg/FFmpeg/blob/master/COPYING.GPLv3)
ffmpeg license
- changes npm packaging to include `//third_party/ffmpeg` only in `playwright` and `playwrihgt-chromium` a
We used to do fetch() to decode the file buffer. However, this is
blocked by strict CSP policy. Instead, we can use explicit
string -> bytes conversion, and trade performance for CSP compliance.
As discussed offline, our testing scenarios assume running trusted
web content - so this warning is just a noise for this usecases.
When it comes to dealing with untrusted web content though, automation
authors need to make sure to not launch browsers under root in the first
place.
This is a large rework of selectors:
- Each BrowserContext now has a separate Selectors instance that has its own registrations.
Most of them share a single sharedSelectors instance, but contexts created for a connected
browser have their own instance.
- Connected browser now gets a RemoteBrowser object that encapsulates Selectors and Browser.
This Selectors object is registered with the api selectors.
- Public selectors.register api iterates over all registered Selectors channels
and registers in each of them.
- createSelector testing method migrated to ElementHandle._createSelectorForTest.
This introduces basic tracing enabled in our tests.
What is captured:
- network resources;
- snapshots at the start of most actions;
- snapshot after the test failure.
How this integrates with test runner:
- context fixture calls private method context._initSnapshotter() and uses Tracer to trace all events;
- all tests share a single test-results/trace-storage directory to store blobs;
- each test has its own trace file.
- npm run show-trace opens a bare-minimum trace viewer that renders snapshots.
Root index.js is only used for local development, so
assuming dev mode there is fine. This way we do not have
to worry about calling setUnderTest early enough.
This touches:
- noDefaultViewport;
- ignoreAllDefaultArgs;
- env;
- validateXYZ logic that was copying objects - we do not need that anymore;
- shuffles some converters closer to their usage.
- Never write to console on the server side - we use stdout for
communication. This includes logPolitely and deprecate.
- Pass undefined instead of null in some BrowserContext methods.
- Use explicit _setFileChooserIntercepted instead of on/off magic.
This migrates Firefox to the protocol-based proxy implementation.
Benefits:
- supports secure web proxies (already supported by Chromium)
- unlocks support for SOCKS proxies with authentication
Make sure executable exists before launching it. If it doesn't and
we were launched without custom executable path, print a helpful
instruction to run `npm i playwright` and get browsers downloaded.
Note: there's already a test that makes sure bad executable paths
are treated fairly: 9132d23b2b/test/launcher.jest.js (L54-L59)
This doesn't test missing default browser installation which I think is
fine.
Fixes#3161
WebKit WPE assumes `libglesv2.so` is available on the host system
and uses `dlopen` to open it.
This patch starts using `ldconfig -p` to check if the library
exists on the system.
References #2745
This makes it easier to reason about our packages.
The only difference is what each package downloads.
When the browser is not downloaded, it will fail to launch.
Each browser gets a 'download' attribute in the browser.json file.
The original plan was to rnu some checks against libc version the
binary is compiled with, but these turn out to be a little complicated:
parsing out libc version from both static binary and host system
requires text processing, and it's hard to make sure it works reliably
across distributions.
Instead, let's start with a very particular check against running
Firefox on Ubuntu 16.04.
References #2745
This patch detects Chromium crash with a sandboxing error and re-writes
the error to surface information nicely.
#### Error Before:
```sh
pwuser@23592d09b3bd:~/tmp$ node a.js
(node:324) UnhandledPromiseRejectionWarning: browserType.launch: Protocol error (Browser.getVersion): Target closed.
=========================== logs ===========================
[browser] <launching> /home/pwuser/.cache/ms-playwright/chromium-790602/chrome-linux/chrome --disable-background-networking --enable-features=NetworkService,NetworkServiceInProcess --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-breakpad --disab
le-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-default-apps --disable-dev-shm-usage --disable-extensions --disable-features=TranslateUI,BlinkGenPropertyTrees,ImprovedCookieControls,SameSiteByDefaultCookies --disable-hang-monitor --disab
le-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --disable-sync --force-color-profile=srgb --metrics-recording-only --no-first-run --enable-automation --password-store=basic --use-mock-keychain --user-data-dir=/tmp/playwrig
ht_chromiumdev_profile-mjSfr2 --remote-debugging-pipe --headless --hide-scrollbars --mute-audio --no-startup-window
[browser] <launched> pid=401
[browser] [0722/170825.030020:FATAL:zygote_host_impl_linux.cc(117)] No usable sandbox! Update your kernel or see https://chromium.googlesource.com/chromium/src/+/master/docs/linux/suid_sandbox_development.md for more information on developing with the SUID sandbox. If you want to live
dangerously and need an immediate workaround, you can try using --no-sandbox.
[browser] #0 0x55ac4f8c7be9 base::debug::CollectStackTrace()
[browser] #1 0x55ac4f841c13 base::debug::StackTrace::StackTrace()
[browser] #2 0x55ac4f853680 logging::LogMessage::~LogMessage()
[browser] #3 0x55ac4df2307e content::ZygoteHostImpl::Init()
[browser] #4 0x55ac4f40dd47 content::ContentMainRunnerImpl::Initialize()
[browser] #5 0x55ac4f45c9fa service_manager::Main()
[browser] #6 0x55ac4f40c361 content::ContentMain()
[browser] #7 0x55ac4f45b5bd headless::(anonymous namespace)::RunContentMain()
[browser] #8 0x55ac4f45b2bc headless::HeadlessShellMain()
[browser] #9 0x55ac4ccc22e7 ChromeMain
[browser] #10 0x7f0f3d736b97 __libc_start_main
[browser] #11 0x55ac4ccc212a _start
[browser]
[browser] Received signal 6
[browser] #0 0x55ac4f8c7be9 base::debug::CollectStackTrace()
[browser] #1 0x55ac4f841c13 base::debug::StackTrace::StackTrace()
[browser] #2 0x55ac4f8c7785 base::debug::(anonymous namespace)::StackDumpSignalHandler()
[browser] #3 0x7f0f437b3890 (/lib/x86_64-linux-gnu/libpthread-2.27.so+0x1288f)
[browser] #4 0x7f0f3d753e97 gsignal
[browser] #5 0x7f0f3d755801 abort
[browser] #6 0x55ac4f8c66e5 base::debug::BreakDebugger()
[browser] #7 0x55ac4f853aeb logging::LogMessage::~LogMessage()
[browser] #8 0x55ac4df2307e content::ZygoteHostImpl::Init()
[browser] #9 0x55ac4f40dd47 content::ContentMainRunnerImpl::Initialize()
[browser] #10 0x55ac4f45c9fa service_manager::Main()
[browser] #11 0x55ac4f40c361 content::ContentMain()
[browser] #12 0x55ac4f45b5bd headless::(anonymous namespace)::RunContentMain()
[browser] #13 0x55ac4f45b2bc headless::HeadlessShellMain()
[browser] #14 0x55ac4ccc22e7 ChromeMain
[browser] #15 0x7f0f3d736b97 __libc_start_main
[browser] #16 0x55ac4ccc212a _start
[browser] r8: 0000000000000000 r9: 00007ffd38a863b0 r10: 0000000000000008 r11: 0000000000000246
[browser] r12: 00007ffd38a87680 r13: 00007ffd38a86610 r14: 00007ffd38a87690 r15: aaaaaaaaaaaaaaaa
[browser] di: 0000000000000002 si: 00007ffd38a863b0 bp: 00007ffd38a86600 bx: 00007ffd38a86e44
[browser] dx: 0000000000000000 ax: 0000000000000000 cx: 00007f0f3d753e97 sp: 00007ffd38a863b0
[browser] ip: 00007f0f3d753e97 efl: 0000000000000246 cgf: 002b000000000033 erf: 0000000000000000
[browser] trp: 0000000000000000 msk: 0000000000000000 cr2: 0000000000000000
[browser] [end of stack trace]
[browser] Calling _exit(1). Core file will not be generated.
============================================================
Note: use DEBUG=pw:api environment variable and rerun to capture Playwright logs.Error
at /home/pwuser/tmp/node_modules/playwright/lib/chromium/crConnection.js:131:63
at new Promise (<anonymous>)
at CRSession.send (/home/pwuser/tmp/node_modules/playwright/lib/chromium/crConnection.js:130:16)
at CRSession.send (/home/pwuser/tmp/node_modules/playwright/lib/helper.js:78:31)
at Function.connect (/home/pwuser/tmp/node_modules/playwright/lib/chromium/crBrowser.js:54:39)
at Chromium._connectToTransport (/home/pwuser/tmp/node_modules/playwright/lib/server/chromium.js:52:38)
at Chromium._innerLaunch (/home/pwuser/tmp/node_modules/playwright/lib/server/browserType.js:87:36)
at async ProgressController.run (/home/pwuser/tmp/node_modules/playwright/lib/progress.js:75:28)
at async Chromium.launch (/home/pwuser/tmp/node_modules/playwright/lib/server/browserType.js:60:25)
at async /home/pwuser/tmp/a.js:4:19
(node:324) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise reject
ion, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:324) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
```
#### Error After:
```sh
pwuser@23592d09b3bd:~/tmp$ node a.js
(node:222) UnhandledPromiseRejectionWarning: browserType.launch: Chromium sandboxing failed!
================================
To workaround sandboxing issues, do either of the following:
- (preferred): Configure environment to support sandboxing: https://github.com/microsoft/playwright/blob/master/docs/troubleshooting.md
- (alternative): Launch Chromium without sandbox using 'chromiumSandbox: false' option
================================
Error
at /home/pwuser/tmp/node_modules/playwright/lib/chromium/crConnection.js:131:63
at new Promise (<anonymous>)
at CRSession.send (/home/pwuser/tmp/node_modules/playwright/lib/chromium/crConnection.js:130:16)
at CRSession.send (/home/pwuser/tmp/node_modules/playwright/lib/helper.js:78:31)
at Function.connect (/home/pwuser/tmp/node_modules/playwright/lib/chromium/crBrowser.js:54:27)
at Chromium._connectToTransport (/home/pwuser/tmp/node_modules/playwright/lib/server/chromium.js:53:38)
at Chromium._innerLaunch (/home/pwuser/tmp/node_modules/playwright/lib/server/browserType.js:89:36)
at async ProgressController.run (/home/pwuser/tmp/node_modules/playwright/lib/progress.js:75:28)
at async Chromium.launch (/home/pwuser/tmp/node_modules/playwright/lib/server/browserType.js:61:25)
at async /home/pwuser/tmp/a.js:4:19
(node:222) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise reject
ion, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:222) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
```
References #2745
Drive-by: fix electron issues, exposed by the test using
waitForNavigation.
Drive-by: mark some tests skip(CHANNEL) that were mistakenly
marked skip(USES_HOOKS).
Missing dependencies is #1 problem with launching on Linux.
This patch starts validating browser dependencies before launching
browser on Linux. In case of a missing dependency, we will abandon
launching with an error that lists all missing libs.
References #2745
We always have to reject promises with some error. Otherwise,
our error-rewriting logic in try-catch miserably fails.
With this patch, attempt to launch Firefox when it's missing
dependencies will actually result in a thrown exception with
pretty logs. Without this patch, Playwright throws internal error.
This includes page CDPSession, backgroundPages() and serviceWorkers().
This has also revealed an issue with closing order between the context
and the service worker.
We now use a few helper.waitForEvent calls to wait for internal
events kNavigationEvent and kLifecycleEvent. With these events,
we should be able to replicate logic over rpc.
A progress roughly corresponds to an api call. It is used:
- to collect logs related to the call;
- to handle timeout;
- to provide "cancellation token" behavior so that cancelable process can either
early-exit with progress.throwIfCanceled() or race against it with progress.race();
- to ensure resources are disposed in the case of a failure
with progress.cleanupWhenCanceled();
- (possibly) to log api calls if needed;
- (in the future) to augment async stacks.
When a parent session is detached, we do not always get Target.detachedFromTarget
for child sessions. This is especially true when the socket disconnects, leaving
all child sessions in the maps.
Flakily reproducible by browserType.connect multiclient tests.
There are a few places in the API where we use objects as maps. This patch adds them to docs and the types.
For `env`, we accept booleans and numbers as well because they are often used for their string values.
I don't have a macos 10.13 build to test on, and we aren't set up to compile it for WebKit. However there is a good chance this will work for Chromium and Firefox.
I also improved the error message received when on an unsupported platform.
#1535