Merge branch 'main' into webserver-sigkill
This commit is contained in:
commit
c6cf6bcabf
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"name": "Playwright",
|
||||
"image": "mcr.microsoft.com/playwright:next",
|
||||
"postCreateCommand": "npm install && npm run build && apt-get update && apt-get install -y software-properties-common && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - && add-apt-repository \"deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\" && apt-get install -y docker-ce-cli",
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash"
|
||||
},
|
||||
"runArgs": [
|
||||
"-v", "/var/run/docker.sock:/var/run/docker.sock"
|
||||
]
|
||||
}
|
||||
14
.github/dependabot.yml
vendored
Normal file
14
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
2
.github/workflows/create_test_report.yml
vendored
2
.github/workflows/create_test_report.yml
vendored
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
run: |
|
||||
npx playwright merge-reports --config .github/workflows/merge.config.ts ./all-blob-reports
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
|
||||
- name: Azure Login
|
||||
uses: azure/login@v2
|
||||
|
|
|
|||
|
|
@ -27,7 +27,11 @@ jobs:
|
|||
repo: context.repo.repo,
|
||||
commit_sha: context.sha,
|
||||
});
|
||||
const commitHeader = data.message.split('\n')[0].replace(/#(\d+)/g, 'https://github.com/microsoft/playwright/pull/$1');
|
||||
const commitHeader = data.message.split('\n')[0];
|
||||
const prMatch = commitHeader.match(/#(\d+)/);
|
||||
const formattedCommit = prMatch
|
||||
? `https://github.com/microsoft/playwright/pull/${prMatch[1]}`
|
||||
: `https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${context.sha} (${commitHeader})`;
|
||||
|
||||
const title = '[Ports]: Backport client side changes for ' + currentPlaywrightVersion;
|
||||
for (const repo of ['playwright-python', 'playwright-java', 'playwright-dotnet']) {
|
||||
|
|
@ -50,11 +54,11 @@ jobs:
|
|||
issueBody = issueCreateData.body;
|
||||
}
|
||||
const newBody = issueBody.trimEnd() + `
|
||||
- [ ] https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${context.sha} (${commitHeader})`;
|
||||
- [ ] ${formattedCommit}`;
|
||||
const data = await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: repo,
|
||||
issue_number: issueNumber,
|
||||
body: newBody
|
||||
})
|
||||
}
|
||||
}
|
||||
8
.github/workflows/tests_bidi.yml
vendored
8
.github/workflows/tests_bidi.yml
vendored
|
|
@ -7,6 +7,7 @@ on:
|
|||
- main
|
||||
paths:
|
||||
- .github/workflows/tests_bidi.yml
|
||||
- packages/playwright-core/src/server/bidi/*
|
||||
schedule:
|
||||
# Run every day at midnight
|
||||
- cron: '0 0 * * *'
|
||||
|
|
@ -43,3 +44,10 @@ jobs:
|
|||
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run biditest -- --project=${{ matrix.channel }}*
|
||||
env:
|
||||
PWTEST_USE_BIDI_EXPECTATIONS: '1'
|
||||
- name: Upload csv report to GitHub
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: csv-report
|
||||
path: test-results/report.csv
|
||||
retention-days: 7
|
||||
|
|
|
|||
52
.github/workflows/tests_secondary.yml
vendored
52
.github/workflows/tests_secondary.yml
vendored
|
|
@ -234,6 +234,27 @@ jobs:
|
|||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree
|
||||
|
||||
chromium_tot_headless_shell:
|
||||
name: Chromium tip-of-tree headless-shell-${{ matrix.os }}
|
||||
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/run-test
|
||||
with:
|
||||
browsers-to-install: chromium-tip-of-tree-headless-shell
|
||||
command: npm run ctest
|
||||
bot-name: "chromium-tip-of-tree-headless-shell-${{ matrix.os }}"
|
||||
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
|
||||
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
|
||||
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-tip-of-tree-headless-shell
|
||||
|
||||
firefox_beta:
|
||||
name: Firefox Beta ${{ matrix.os }}
|
||||
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
|
||||
|
|
@ -268,40 +289,25 @@ jobs:
|
|||
- run: npx playwright install-deps
|
||||
- run: utils/build/build-playwright-driver.sh
|
||||
|
||||
test_linux_chromium_headless_new:
|
||||
name: Linux Chromium Headless New
|
||||
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/run-test
|
||||
with:
|
||||
browsers-to-install: chromium
|
||||
command: npm run ctest
|
||||
bot-name: "headless-new"
|
||||
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
|
||||
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
|
||||
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
|
||||
env:
|
||||
PLAYWRIGHT_CHROMIUM_USE_HEADLESS_NEW: 1
|
||||
|
||||
test_linux_chromium_headless_shell:
|
||||
name: Chromium Headless Shell
|
||||
test_channel_chromium:
|
||||
name: Test channel=chromium
|
||||
environment: ${{ github.event_name == 'push' && 'allow-uploading-flakiness-results' || null }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runs-on: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: [ubuntu-latest, windows-latest, macos-latest]
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/run-test
|
||||
with:
|
||||
browsers-to-install: chromium-headless-shell
|
||||
# TODO: this should pass --no-shell.
|
||||
# However, codegen tests do not inherit the channel and try to launch headless shell.
|
||||
browsers-to-install: chromium
|
||||
command: npm run ctest
|
||||
bot-name: "headless-shell-${{ matrix.runs-on }}"
|
||||
bot-name: "channel-chromium-${{ matrix.runs-on }}"
|
||||
flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }}
|
||||
flakiness-tenant-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_TENANT_ID }}
|
||||
flakiness-subscription-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_SUBSCRIPTION_ID }}
|
||||
env:
|
||||
PWTEST_CHANNEL: chromium-headless-shell
|
||||
PWTEST_CHANNEL: chromium
|
||||
|
|
|
|||
|
|
@ -26,6 +26,16 @@ npm run watch
|
|||
npx playwright install
|
||||
```
|
||||
|
||||
**Experimental dev mode with Hot Module Replacement for recorder/trace-viewer/UI Mode**
|
||||
|
||||
```
|
||||
PW_HMR=1 npm run watch
|
||||
PW_HMR=1 npx playwright show-trace
|
||||
PW_HMR=1 npm run ctest -- --ui
|
||||
PW_HMR=1 npx playwright codegen
|
||||
PW_HMR=1 npx playwright show-report
|
||||
```
|
||||
|
||||
Playwright is a multi-package repository that uses npm workspaces. For browser APIs, look at [`packages/playwright-core`](https://github.com/microsoft/playwright/blob/main/packages/playwright-core). For test runner, see [`packages/playwright`](https://github.com/microsoft/playwright/blob/main/packages/playwright).
|
||||
|
||||
Note that some files are generated by the build, so the watch process might override your changes if done in the wrong file. For example, TypeScript types for the API are generated from the [`docs/src`](https://github.com/microsoft/playwright/blob/main/docs/src).
|
||||
|
|
@ -35,7 +45,7 @@ Coding style is fully defined in [.eslintrc](https://github.com/microsoft/playwr
|
|||
npm run lint
|
||||
```
|
||||
|
||||
Comments should be generally avoided. If the code would not be understood without comments, consider re-writing the code to make it self-explanatory.
|
||||
Comments should have an explicit purpose and should improve readability rather than hinder it. If the code would not be understood without comments, consider re-writing the code to make it self-explanatory.
|
||||
|
||||
### Write documentation
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# 🎭 Playwright
|
||||
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
[](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[](https://webkit.org/)<!-- GEN:stop --> [](https://aka.ms/playwright/discord)
|
||||
|
||||
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
|
||||
|
||||
|
|
@ -8,9 +8,9 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
|
|||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->131.0.6778.13<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->131.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->132.0.6834.46<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->18.2<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->132.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
Headless execution is supported for all browsers on all platforms. Check out [system requirements](https://playwright.dev/docs/intro#system-requirements) for details.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
REMOTE_URL="https://github.com/mozilla/gecko-dev"
|
||||
BASE_BRANCH="release"
|
||||
BASE_REVISION="47bcb6d7d2013f9a3d864678675100e0b3d73c5e"
|
||||
BASE_REVISION="bc78b98043438d8ee2727a483b6e10dedfda883f"
|
||||
|
|
|
|||
|
|
@ -256,6 +256,13 @@ class PageHandler {
|
|||
return await this._contentPage.send('disposeObject', options);
|
||||
}
|
||||
|
||||
async ['Heap.collectGarbage']() {
|
||||
Services.obs.notifyObservers(null, "child-gc-request");
|
||||
Cu.forceGC();
|
||||
Services.obs.notifyObservers(null, "child-cc-request");
|
||||
Cu.forceCC();
|
||||
}
|
||||
|
||||
async ['Network.getResponseBody']({requestId}) {
|
||||
return this._pageNetwork.getResponseBody(requestId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -481,6 +481,17 @@ const Browser = {
|
|||
},
|
||||
};
|
||||
|
||||
const Heap = {
|
||||
targets: ['page'],
|
||||
types: {},
|
||||
events: {},
|
||||
methods: {
|
||||
'collectGarbage': {
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Network = {
|
||||
targets: ['page'],
|
||||
types: networkTypes,
|
||||
|
|
@ -996,7 +1007,7 @@ const Accessibility = {
|
|||
}
|
||||
|
||||
this.protocol = {
|
||||
domains: {Browser, Page, Runtime, Network, Accessibility},
|
||||
domains: {Browser, Heap, Page, Runtime, Network, Accessibility},
|
||||
};
|
||||
this.checkScheme = checkScheme;
|
||||
this.EXPORTED_SYMBOLS = ['protocol', 'checkScheme'];
|
||||
|
|
|
|||
|
|
@ -57,10 +57,10 @@ index 8e9bf2b413585b5a3db9370eee5d57fb6c6716ed..5a3b194b54e3813c89989f13a214c989
|
|||
* Return XPCOM wrapper for the internal accessible.
|
||||
*/
|
||||
diff --git a/browser/app/winlauncher/LauncherProcessWin.cpp b/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
index b40e0fceb567c0d217adf284e13f434e49cc8467..2c4e6d5fbf8da40954ad6a5b15e412493e43b14e 100644
|
||||
index 8167d2b81c918e02ce757f7f448f22e07c29d140..3ae798880acfd8aa965ae08051f2f81855133711 100644
|
||||
--- a/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
+++ b/browser/app/winlauncher/LauncherProcessWin.cpp
|
||||
@@ -22,6 +22,7 @@
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "mozilla/WinHeaderOnlyUtils.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ index b40e0fceb567c0d217adf284e13f434e49cc8467..2c4e6d5fbf8da40954ad6a5b15e41249
|
|||
#include <windows.h>
|
||||
#include <processthreadsapi.h>
|
||||
|
||||
@@ -421,8 +422,18 @@ Maybe<int> LauncherMain(int& argc, wchar_t* argv[],
|
||||
@@ -422,8 +423,18 @@ Maybe<int> LauncherMain(int& argc, wchar_t* argv[],
|
||||
HANDLE stdHandles[] = {::GetStdHandle(STD_INPUT_HANDLE),
|
||||
::GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
::GetStdHandle(STD_ERROR_HANDLE)};
|
||||
|
|
@ -167,7 +167,7 @@ index d49c6fbf1bf83b832795fa674f6b41f223eef812..7ea3540947ff5f61b15f27fbf4b95564
|
|||
const transportProvider = {
|
||||
setListener(upgradeListener) {
|
||||
diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp
|
||||
index db5b5b990727aefcbaa47f89e0f53f4048e60038..bcd2321f46d9bca719fc530054984a2163c21f86 100644
|
||||
index e1721f31d491aa8a7977eaca3d2f7f8a048546de..b3bc2d575dc3f794cbc08c603e70d34bbe69efed 100644
|
||||
--- a/docshell/base/BrowsingContext.cpp
|
||||
+++ b/docshell/base/BrowsingContext.cpp
|
||||
@@ -106,8 +106,15 @@ struct ParamTraits<mozilla::dom::DisplayMode>
|
||||
|
|
@ -188,7 +188,7 @@ index db5b5b990727aefcbaa47f89e0f53f4048e60038..bcd2321f46d9bca719fc530054984a21
|
|||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::ExplicitActiveStatus>
|
||||
@@ -2807,6 +2814,40 @@ void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
|
||||
@@ -2818,6 +2825,40 @@ void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
|
||||
PresContextAffectingFieldChanged();
|
||||
}
|
||||
|
||||
|
|
@ -297,7 +297,7 @@ index 61135ab0d7894c500c3c5d80d107e283c01b6830..cc8eb043f1f78214843ec7b335dd9932
|
|||
|
||||
bool CanSet(FieldIndex<IDX_SuspendMediaWhenInactive>, bool, ContentParent*) {
|
||||
diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp
|
||||
index 18b2bde3da2b1e17938fddda486b1bc4ddcf0e79..793a3d002b10298f7a19a2eae4d377f6f022fd36 100644
|
||||
index f0d8cb25398472d8720fcacc47081d95d3e9887c..a680d4458360c8515712ef0a986415113ae8a4e0 100644
|
||||
--- a/docshell/base/CanonicalBrowsingContext.cpp
|
||||
+++ b/docshell/base/CanonicalBrowsingContext.cpp
|
||||
@@ -324,6 +324,8 @@ void CanonicalBrowsingContext::ReplacedBy(
|
||||
|
|
@ -323,7 +323,7 @@ index 18b2bde3da2b1e17938fddda486b1bc4ddcf0e79..793a3d002b10298f7a19a2eae4d377f6
|
|||
}
|
||||
|
||||
diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
|
||||
index 60cbd5d5b8d202fc30d5ac931ac66030bade65e7..f552a695880c5838c89ce918f61d051577cc5598 100644
|
||||
index c15a424a05d23287ee21726a5fb21ff5691e4c2b..fa9989e313bbb7bf049ce1519733c4032e9f9b4b 100644
|
||||
--- a/docshell/base/nsDocShell.cpp
|
||||
+++ b/docshell/base/nsDocShell.cpp
|
||||
@@ -15,6 +15,12 @@
|
||||
|
|
@ -609,7 +609,7 @@ index 60cbd5d5b8d202fc30d5ac931ac66030bade65e7..f552a695880c5838c89ce918f61d0515
|
|||
if (RefPtr<PresShell> presShell = GetPresShell()) {
|
||||
presShell->ActivenessMaybeChanged();
|
||||
}
|
||||
@@ -6681,6 +6906,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
|
||||
@@ -6688,6 +6913,10 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
|
||||
return false; // no entry to save into
|
||||
}
|
||||
|
||||
|
|
@ -620,7 +620,7 @@ index 60cbd5d5b8d202fc30d5ac931ac66030bade65e7..f552a695880c5838c89ce918f61d0515
|
|||
MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
|
||||
"mOSHE cannot be non-null with SHIP");
|
||||
nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer();
|
||||
@@ -8413,6 +8642,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
|
||||
@@ -8420,6 +8649,12 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
|
||||
true, // aForceNoOpener
|
||||
getter_AddRefs(newBC));
|
||||
MOZ_ASSERT(!newBC);
|
||||
|
|
@ -633,7 +633,7 @@ index 60cbd5d5b8d202fc30d5ac931ac66030bade65e7..f552a695880c5838c89ce918f61d0515
|
|||
return rv;
|
||||
}
|
||||
|
||||
@@ -9549,6 +9784,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
|
||||
@@ -9556,6 +9791,16 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
|
||||
nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
|
||||
|
||||
nsCOMPtr<nsIRequest> req;
|
||||
|
|
@ -650,7 +650,7 @@ index 60cbd5d5b8d202fc30d5ac931ac66030bade65e7..f552a695880c5838c89ce918f61d0515
|
|||
rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
@@ -12747,6 +12992,9 @@ class OnLinkClickEvent : public Runnable {
|
||||
@@ -12754,6 +12999,9 @@ class OnLinkClickEvent : public Runnable {
|
||||
mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
|
||||
mTriggeringPrincipal);
|
||||
}
|
||||
|
|
@ -660,7 +660,7 @@ index 60cbd5d5b8d202fc30d5ac931ac66030bade65e7..f552a695880c5838c89ce918f61d0515
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -12836,6 +13084,8 @@ nsresult nsDocShell::OnLinkClick(
|
||||
@@ -12843,6 +13091,8 @@ nsresult nsDocShell::OnLinkClick(
|
||||
nsCOMPtr<nsIRunnable> ev =
|
||||
new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
|
||||
aIsTrusted, aTriggeringPrincipal);
|
||||
|
|
@ -781,10 +781,10 @@ index fdc04f16c6f547077ad8c872f9357d85d4513c50..199f8fdb0670265c715f99f5cac1a2b2
|
|||
* This attempts to save any applicable layout history state (like
|
||||
* scroll position) in the nsISHEntry. This is normally done
|
||||
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
|
||||
index 235e2fcfccda18b4e923d1c1b02b5e1d9b02b089..e81abc3e18d82fa235a69911eb117ad0dcf54fd2 100644
|
||||
index 79f3524037e954eb693e2882d91a7632e6e1df41..2b75a1eaff4d166f68ca4a943e10cf9c6ab28bbf 100644
|
||||
--- a/dom/base/Document.cpp
|
||||
+++ b/dom/base/Document.cpp
|
||||
@@ -3757,6 +3757,9 @@ void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
|
||||
@@ -3783,6 +3783,9 @@ void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
|
||||
}
|
||||
|
||||
void Document::ApplySettingsFromCSP(bool aSpeculative) {
|
||||
|
|
@ -794,7 +794,7 @@ index 235e2fcfccda18b4e923d1c1b02b5e1d9b02b089..e81abc3e18d82fa235a69911eb117ad0
|
|||
nsresult rv = NS_OK;
|
||||
if (!aSpeculative) {
|
||||
// 1) apply settings from regular CSP
|
||||
@@ -3814,6 +3817,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
|
||||
@@ -3840,6 +3843,11 @@ nsresult Document::InitCSP(nsIChannel* aChannel) {
|
||||
MOZ_ASSERT(!mScriptGlobalObject,
|
||||
"CSP must be initialized before mScriptGlobalObject is set!");
|
||||
|
||||
|
|
@ -806,7 +806,7 @@ index 235e2fcfccda18b4e923d1c1b02b5e1d9b02b089..e81abc3e18d82fa235a69911eb117ad0
|
|||
// If this is a data document - no need to set CSP.
|
||||
if (mLoadedAsData) {
|
||||
return NS_OK;
|
||||
@@ -4613,6 +4621,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
|
||||
@@ -4641,6 +4649,10 @@ bool Document::HasFocus(ErrorResult& rv) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -817,7 +817,7 @@ index 235e2fcfccda18b4e923d1c1b02b5e1d9b02b089..e81abc3e18d82fa235a69911eb117ad0
|
|||
if (!fm->IsInActiveWindow(bc)) {
|
||||
return false;
|
||||
}
|
||||
@@ -19080,6 +19092,66 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
|
||||
@@ -19139,6 +19151,66 @@ ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
|
||||
return PreferenceSheet::PrefsFor(*this).mColorScheme;
|
||||
}
|
||||
|
||||
|
|
@ -885,10 +885,10 @@ index 235e2fcfccda18b4e923d1c1b02b5e1d9b02b089..e81abc3e18d82fa235a69911eb117ad0
|
|||
if (!sLoadingForegroundTopLevelContentDocument) {
|
||||
return false;
|
||||
diff --git a/dom/base/Document.h b/dom/base/Document.h
|
||||
index 0021e452414f9b7dc7b32a1065a82986d12dfdd7..2325b7d65bc1fb98b1dce994724c8e75c902834e 100644
|
||||
index 7a8d8f2a716fc613c4095eaf1a18017887b9b924..e030e6b7ad63ad7c95227ed8f54e946190a638d8 100644
|
||||
--- a/dom/base/Document.h
|
||||
+++ b/dom/base/Document.h
|
||||
@@ -4053,6 +4053,9 @@ class Document : public nsINode,
|
||||
@@ -4077,6 +4077,9 @@ class Document : public nsINode,
|
||||
// color-scheme meta tag.
|
||||
ColorScheme DefaultColorScheme() const;
|
||||
|
||||
|
|
@ -962,10 +962,10 @@ index 6abf6cef230c97815f17f6b7abf9f1b1de274a6f..46ead1f32e0d710b5b32e61dff72a4f7
|
|||
dom::MediaCapabilities* MediaCapabilities();
|
||||
dom::MediaSession* MediaSession();
|
||||
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
|
||||
index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a69257799fec 100644
|
||||
index 8518005d2938d35da7681c1b4230cacbe8fbaf03..e501e7e3351b6f5bdd07020dea658b9f8508b126 100644
|
||||
--- a/dom/base/nsContentUtils.cpp
|
||||
+++ b/dom/base/nsContentUtils.cpp
|
||||
@@ -8829,7 +8829,8 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
@@ -8809,7 +8809,8 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
bool aIgnoreRootScrollFrame, float aPressure,
|
||||
unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
|
||||
PreventDefaultResult* aPreventDefault, bool aIsDOMEventSynthesized,
|
||||
|
|
@ -975,7 +975,7 @@ index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a692
|
|||
nsPoint offset;
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
|
||||
if (!widget) return NS_ERROR_FAILURE;
|
||||
@@ -8837,6 +8838,7 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
@@ -8817,6 +8818,7 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
EventMessage msg;
|
||||
Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
|
||||
bool contextMenuKey = false;
|
||||
|
|
@ -983,7 +983,7 @@ index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a692
|
|||
if (aType.EqualsLiteral("mousedown")) {
|
||||
msg = eMouseDown;
|
||||
} else if (aType.EqualsLiteral("mouseup")) {
|
||||
@@ -8861,6 +8863,12 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
@@ -8841,6 +8843,12 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
msg = eMouseHitTest;
|
||||
} else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
|
||||
msg = eMouseExploreByTouch;
|
||||
|
|
@ -996,7 +996,7 @@ index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a692
|
|||
} else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -8871,7 +8879,14 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
@@ -8851,7 +8859,14 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
|
||||
Maybe<WidgetPointerEvent> pointerEvent;
|
||||
Maybe<WidgetMouseEvent> mouseEvent;
|
||||
|
|
@ -1012,7 +1012,7 @@ index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a692
|
|||
MOZ_ASSERT(!aIsWidgetEventSynthesized,
|
||||
"The event shouldn't be dispatched as a synthesized event");
|
||||
if (MOZ_UNLIKELY(aIsWidgetEventSynthesized)) {
|
||||
@@ -8890,8 +8905,11 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
@@ -8870,8 +8885,11 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
contextMenuKey ? WidgetMouseEvent::eContextMenuKey
|
||||
: WidgetMouseEvent::eNormal);
|
||||
}
|
||||
|
|
@ -1024,7 +1024,7 @@ index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a692
|
|||
mouseOrPointerEvent.pointerId = aIdentifier;
|
||||
mouseOrPointerEvent.mModifiers = GetWidgetModifiers(aModifiers);
|
||||
mouseOrPointerEvent.mButton = aButton;
|
||||
@@ -8904,6 +8922,8 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
@@ -8884,6 +8902,8 @@ nsresult nsContentUtils::SendMouseEvent(
|
||||
mouseOrPointerEvent.mClickCount = aClickCount;
|
||||
mouseOrPointerEvent.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
|
||||
mouseOrPointerEvent.mExitFrom = exitFrom;
|
||||
|
|
@ -1034,10 +1034,10 @@ index 7b7deca251cf20fa4896e63e32d17303dd603263..151dd519433de858673dc1620094a692
|
|||
nsPresContext* presContext = aPresShell->GetPresContext();
|
||||
if (!presContext) return NS_ERROR_FAILURE;
|
||||
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
|
||||
index 3837cce20cfb7cc3c5a93e7b595dee632739de5c..81ccfbe139e7041eb862ab3b085f1dae76bf0a5c 100644
|
||||
index b4b2244ddfbe43efa055788297a103c49989d921..2d22cdf8b25d9ce0e0daabb09f315d61a214a2be 100644
|
||||
--- a/dom/base/nsContentUtils.h
|
||||
+++ b/dom/base/nsContentUtils.h
|
||||
@@ -3093,7 +3093,8 @@ class nsContentUtils {
|
||||
@@ -3047,7 +3047,8 @@ class nsContentUtils {
|
||||
int32_t aModifiers, bool aIgnoreRootScrollFrame, float aPressure,
|
||||
unsigned short aInputSourceArg, uint32_t aIdentifier, bool aToWindow,
|
||||
mozilla::PreventDefaultResult* aPreventDefault,
|
||||
|
|
@ -1048,10 +1048,10 @@ index 3837cce20cfb7cc3c5a93e7b595dee632739de5c..81ccfbe139e7041eb862ab3b085f1dae
|
|||
static void FirePageShowEventForFrameLoaderSwap(
|
||||
nsIDocShellTreeItem* aItem,
|
||||
diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp
|
||||
index e2de2b30c094e30db4d33e6cf8e5fbf83f219876..f937f561c0524e04563129f2cb762ae4127e6462 100644
|
||||
index c77bf80d5e1fc6db342ab47e85b7950f8a15a2d8..2f61c71cdb82b73c1de1a357315d9243a0b8c639 100644
|
||||
--- a/dom/base/nsDOMWindowUtils.cpp
|
||||
+++ b/dom/base/nsDOMWindowUtils.cpp
|
||||
@@ -684,6 +684,26 @@ nsDOMWindowUtils::GetPresShellId(uint32_t* aPresShellId) {
|
||||
@@ -685,6 +685,26 @@ nsDOMWindowUtils::GetPresShellId(uint32_t* aPresShellId) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
@ -1078,7 +1078,7 @@ index e2de2b30c094e30db4d33e6cf8e5fbf83f219876..f937f561c0524e04563129f2cb762ae4
|
|||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendMouseEvent(
|
||||
const nsAString& aType, float aX, float aY, int32_t aButton,
|
||||
@@ -698,7 +718,7 @@ nsDOMWindowUtils::SendMouseEvent(
|
||||
@@ -699,7 +719,7 @@ nsDOMWindowUtils::SendMouseEvent(
|
||||
aOptionalArgCount >= 7 ? aIdentifier : DEFAULT_MOUSE_POINTER_ID, false,
|
||||
aPreventDefault, aOptionalArgCount >= 4 ? aIsDOMEventSynthesized : true,
|
||||
aOptionalArgCount >= 5 ? aIsWidgetEventSynthesized : false,
|
||||
|
|
@ -1087,7 +1087,7 @@ index e2de2b30c094e30db4d33e6cf8e5fbf83f219876..f937f561c0524e04563129f2cb762ae4
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -716,7 +736,7 @@ nsDOMWindowUtils::SendMouseEventToWindow(
|
||||
@@ -717,7 +737,7 @@ nsDOMWindowUtils::SendMouseEventToWindow(
|
||||
aOptionalArgCount >= 7 ? aIdentifier : DEFAULT_MOUSE_POINTER_ID, true,
|
||||
nullptr, aOptionalArgCount >= 4 ? aIsDOMEventSynthesized : true,
|
||||
aOptionalArgCount >= 5 ? aIsWidgetEventSynthesized : false,
|
||||
|
|
@ -1096,7 +1096,7 @@ index e2de2b30c094e30db4d33e6cf8e5fbf83f219876..f937f561c0524e04563129f2cb762ae4
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -725,13 +745,13 @@ nsDOMWindowUtils::SendMouseEventCommon(
|
||||
@@ -726,13 +746,13 @@ nsDOMWindowUtils::SendMouseEventCommon(
|
||||
int32_t aClickCount, int32_t aModifiers, bool aIgnoreRootScrollFrame,
|
||||
float aPressure, unsigned short aInputSourceArg, uint32_t aPointerId,
|
||||
bool aToWindow, bool* aPreventDefault, bool aIsDOMEventSynthesized,
|
||||
|
|
@ -1126,10 +1126,10 @@ index 47ff326b202266b1d7d6af8bdfb72776df8a6a93..b8e084b0c788c46345b1455b8257f171
|
|||
MOZ_CAN_RUN_SCRIPT
|
||||
nsresult SendTouchEventCommon(
|
||||
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
|
||||
index 22c175c93ef7bc81640b0ad71bd6ca9c1082fea6..7d77e91afbfe7aebe0c94793c2e0606715e3acdb 100644
|
||||
index cbd5cb8e4525454cac0470a14bdc63d45bf53b9a..a73297f3faafe5895453f0a6996aa30a77a97267 100644
|
||||
--- a/dom/base/nsFocusManager.cpp
|
||||
+++ b/dom/base/nsFocusManager.cpp
|
||||
@@ -1684,6 +1684,10 @@ Maybe<uint64_t> nsFocusManager::SetFocusInner(Element* aNewContent,
|
||||
@@ -1697,6 +1697,10 @@ Maybe<uint64_t> nsFocusManager::SetFocusInner(Element* aNewContent,
|
||||
(GetActiveBrowsingContext() == newRootBrowsingContext);
|
||||
}
|
||||
|
||||
|
|
@ -1140,7 +1140,7 @@ index 22c175c93ef7bc81640b0ad71bd6ca9c1082fea6..7d77e91afbfe7aebe0c94793c2e06067
|
|||
// Exit fullscreen if a website focuses another window
|
||||
if (StaticPrefs::full_screen_api_exit_on_windowRaise() &&
|
||||
!isElementInActiveWindow && (aFlags & FLAG_RAISE)) {
|
||||
@@ -2269,6 +2273,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
|
||||
@@ -2282,6 +2286,7 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
|
||||
bool aIsLeavingDocument, bool aAdjustWidget,
|
||||
bool aRemainActive, Element* aElementToFocus,
|
||||
uint64_t aActionId) {
|
||||
|
|
@ -1148,7 +1148,7 @@ index 22c175c93ef7bc81640b0ad71bd6ca9c1082fea6..7d77e91afbfe7aebe0c94793c2e06067
|
|||
LOGFOCUS(("<<Blur begin actionid: %" PRIu64 ">>", aActionId));
|
||||
|
||||
// hold a reference to the focused content, which may be null
|
||||
@@ -2315,6 +2320,11 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
|
||||
@@ -2328,6 +2333,11 @@ bool nsFocusManager::BlurImpl(BrowsingContext* aBrowsingContextToClear,
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1160,7 +1160,7 @@ index 22c175c93ef7bc81640b0ad71bd6ca9c1082fea6..7d77e91afbfe7aebe0c94793c2e06067
|
|||
// Keep a ref to presShell since dispatching the DOM event may cause
|
||||
// the document to be destroyed.
|
||||
RefPtr<PresShell> presShell = docShell->GetPresShell();
|
||||
@@ -2992,7 +3002,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
|
||||
@@ -3005,7 +3015,9 @@ void nsFocusManager::RaiseWindow(nsPIDOMWindowOuter* aWindow,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1172,10 +1172,10 @@ index 22c175c93ef7bc81640b0ad71bd6ca9c1082fea6..7d77e91afbfe7aebe0c94793c2e06067
|
|||
// care of lowering the present active window. This happens in
|
||||
// a separate runnable to avoid touching multiple windows in
|
||||
diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp
|
||||
index e47d4979078343102f00e93df913ff778b841804..360ab27a8f3394d18b558de80b5d0bbb963c1391 100644
|
||||
index f2aa07e2c1e6df28e165b1868ad9717248360972..2b1b406c4fdf6d0716b9c29c3e640de210eae749 100644
|
||||
--- a/dom/base/nsGlobalWindowOuter.cpp
|
||||
+++ b/dom/base/nsGlobalWindowOuter.cpp
|
||||
@@ -2514,10 +2514,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
|
||||
@@ -2516,10 +2516,16 @@ nsresult nsGlobalWindowOuter::SetNewDocument(Document* aDocument,
|
||||
}();
|
||||
|
||||
if (!isContentAboutBlankInChromeDocshell) {
|
||||
|
|
@ -1196,7 +1196,7 @@ index e47d4979078343102f00e93df913ff778b841804..360ab27a8f3394d18b558de80b5d0bbb
|
|||
}
|
||||
}
|
||||
|
||||
@@ -2637,6 +2643,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
|
||||
@@ -2639,6 +2645,19 @@ void nsGlobalWindowOuter::DispatchDOMWindowCreated() {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1217,10 +1217,10 @@ index e47d4979078343102f00e93df913ff778b841804..360ab27a8f3394d18b558de80b5d0bbb
|
|||
|
||||
void nsGlobalWindowOuter::SetDocShell(nsDocShell* aDocShell) {
|
||||
diff --git a/dom/base/nsGlobalWindowOuter.h b/dom/base/nsGlobalWindowOuter.h
|
||||
index 0039d6d91b23953afbd6aec2b4d1f064db3c3b1c..7a6c5da16651d34ea60c69331365d94886da1993 100644
|
||||
index e2a2b560b565e6eb3cd5b4e77eb30051afe7a418..81eaca3fb0acfe90bf07acb4115a0db0786c6998 100644
|
||||
--- a/dom/base/nsGlobalWindowOuter.h
|
||||
+++ b/dom/base/nsGlobalWindowOuter.h
|
||||
@@ -314,6 +314,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
|
||||
@@ -317,6 +317,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
|
||||
|
||||
// Outer windows only.
|
||||
void DispatchDOMWindowCreated();
|
||||
|
|
@ -1229,7 +1229,7 @@ index 0039d6d91b23953afbd6aec2b4d1f064db3c3b1c..7a6c5da16651d34ea60c69331365d948
|
|||
// Outer windows only.
|
||||
virtual void EnsureSizeAndPositionUpToDate() override;
|
||||
diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp
|
||||
index 4b54dcd5b4fc9c575552ae82d5ed66f313cdeb72..e75b5f148d55d8f7d7e098a84930fec0e28aa01d 100644
|
||||
index 091d04dd79da3acc33aa22405ddb984ba486dfe2..40bb124fd735c2d214221c1a5fac442e26f7564e 100644
|
||||
--- a/dom/base/nsINode.cpp
|
||||
+++ b/dom/base/nsINode.cpp
|
||||
@@ -1402,6 +1402,61 @@ void nsINode::GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
|
||||
|
|
@ -1295,10 +1295,10 @@ index 4b54dcd5b4fc9c575552ae82d5ed66f313cdeb72..e75b5f148d55d8f7d7e098a84930fec0
|
|||
DOMQuad& aQuad, const GeometryNode& aFrom,
|
||||
const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
|
||||
diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h
|
||||
index 6f980f472aefe147de47212717ca300e62e02952..3d60daf88196ed502fc647cc7b51d2eb70a281ef 100644
|
||||
index 3bc7ff8a3d9e7f3148f51da13f34ea1b3ca2ba77..dcb47740ca93ab237e4f55d4225f77283f5f404b 100644
|
||||
--- a/dom/base/nsINode.h
|
||||
+++ b/dom/base/nsINode.h
|
||||
@@ -2303,6 +2303,10 @@ class nsINode : public mozilla::dom::EventTarget {
|
||||
@@ -2317,6 +2317,10 @@ class nsINode : public mozilla::dom::EventTarget {
|
||||
nsTArray<RefPtr<DOMQuad>>& aResult,
|
||||
ErrorResult& aRv);
|
||||
|
||||
|
|
@ -1310,10 +1310,10 @@ index 6f980f472aefe147de47212717ca300e62e02952..3d60daf88196ed502fc647cc7b51d2eb
|
|||
DOMQuad& aQuad, const TextOrElementOrDocument& aFrom,
|
||||
const ConvertCoordinateOptions& aOptions, CallerType aCallerType,
|
||||
diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp
|
||||
index cf8037cd580013efe5eb578c43f45c0d21946c6a..583460796fdef633e8075013597f7c315ce4ab06 100644
|
||||
index 48df3ae2d30b975269d06e6354b143abd3e5fcd8..87c8d237355668b0ff324f49be879219b1761083 100644
|
||||
--- a/dom/base/nsJSUtils.cpp
|
||||
+++ b/dom/base/nsJSUtils.cpp
|
||||
@@ -177,6 +177,11 @@ bool nsJSUtils::GetScopeChainForElement(
|
||||
@@ -149,6 +149,11 @@ bool nsJSUtils::GetScopeChainForElement(
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1326,10 +1326,10 @@ index cf8037cd580013efe5eb578c43f45c0d21946c6a..583460796fdef633e8075013597f7c31
|
|||
void nsJSUtils::ResetTimeZone() { JS::ResetTimeZone(); }
|
||||
|
||||
diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h
|
||||
index cceb725d393d5e5f83c8f87491089c3fa1d57cc3..e906a7fb7c3fd72554613f640dcc272e6984d929 100644
|
||||
index 8b4c1492c64884d83eb1553bc40b921e0da601b7..ee66eaa21d8e8c208204ef73fca5b3d78abefb24 100644
|
||||
--- a/dom/base/nsJSUtils.h
|
||||
+++ b/dom/base/nsJSUtils.h
|
||||
@@ -79,6 +79,7 @@ class nsJSUtils {
|
||||
@@ -71,6 +71,7 @@ class nsJSUtils {
|
||||
JSContext* aCx, mozilla::dom::Element* aElement,
|
||||
JS::MutableHandleVector<JSObject*> aScopeChain);
|
||||
|
||||
|
|
@ -1479,18 +1479,18 @@ index 7e1af00d05fbafa2d828e2c7e4dcc5c82d115f5b..e85af9718d064e4d2865bc944e9d4ba1
|
|||
~Geolocation();
|
||||
|
||||
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
|
||||
index e2a77a11435a80abbb6381ffabbb5711eca0ac0d..a614efef052ca7c39457726d1f1e66f7cff777f8 100644
|
||||
index d40c2a230c8c86f585935061d05e20b405c906fe..29547e7a0d75fdc8b8b30344db32287424e65fba 100644
|
||||
--- a/dom/html/HTMLInputElement.cpp
|
||||
+++ b/dom/html/HTMLInputElement.cpp
|
||||
@@ -59,6 +59,7 @@
|
||||
@@ -60,6 +60,7 @@
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/HTMLDataListElement.h"
|
||||
#include "mozilla/dom/HTMLOptionElement.h"
|
||||
+#include "nsDocShell.h"
|
||||
#include "nsIFormControlFrame.h"
|
||||
#include "nsITextControlFrame.h"
|
||||
#include "nsIFrame.h"
|
||||
@@ -784,6 +785,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
|
||||
#include "nsRangeFrame.h"
|
||||
#include "nsError.h"
|
||||
@@ -783,6 +784,13 @@ nsresult HTMLInputElement::InitFilePicker(FilePickerType aType) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
@ -1505,7 +1505,7 @@ index e2a77a11435a80abbb6381ffabbb5711eca0ac0d..a614efef052ca7c39457726d1f1e66f7
|
|||
return NS_OK;
|
||||
}
|
||||
diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl
|
||||
index ac0251b4989799e9bb370a8066d10f13154bbc7c..184f4d980c35652c67da06e917e9d0b85ff34cea 100644
|
||||
index 89202fa1ff22593e7cb5e20fc40b3b3b8e114449..61ed40c8454c6e85876cbc7c240496cc96f77239 100644
|
||||
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
|
||||
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
|
||||
@@ -374,6 +374,26 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
|
|
@ -1536,7 +1536,7 @@ index ac0251b4989799e9bb370a8066d10f13154bbc7c..184f4d980c35652c67da06e917e9d0b8
|
|||
* touchstart, touchend, touchmove, and touchcancel
|
||||
*
|
||||
diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp
|
||||
index 204ee71ece1afa8b416caafcb4bdd242344f1a26..8597f2d0c4bd7a6fbfed9f29d002d0c59c8f8ae9 100644
|
||||
index 0335a887fe157210f9eef58bf63be879f7d5de2b..dfbb8dae406f9d9276a2719f515ac5a51f8a671d 100644
|
||||
--- a/dom/ipc/BrowserChild.cpp
|
||||
+++ b/dom/ipc/BrowserChild.cpp
|
||||
@@ -1656,6 +1656,21 @@ void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
|
||||
|
|
@ -1822,7 +1822,7 @@ index 3b39538e51840cd9b1685b2efd2ff2e9ec83608a..c7bf4f2d53b58bbacb22b3ebebf6f3fc
|
|||
|
||||
return aGlobalOrNull;
|
||||
diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp
|
||||
index 4eafb2247d5aa8e989c0359d6d9d864edf73759d..e0d0b5bc78537c6fa8d0cf02cc6c772993008b91 100644
|
||||
index ff2e907c0d8fc05c6e39fae612eceed405b62712..40ec25b5588a1628f9d9bd16886bed83dad49b90 100644
|
||||
--- a/dom/security/nsCSPUtils.cpp
|
||||
+++ b/dom/security/nsCSPUtils.cpp
|
||||
@@ -22,6 +22,7 @@
|
||||
|
|
@ -1869,10 +1869,10 @@ index 2f71b284ee5f7e11f117c447834b48355784448c..2640bd57123c2b03bf4b06a2419cd020
|
|||
* returned quads are further translated relative to the window
|
||||
* origin -- which is not the layout origin. Further translation
|
||||
diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp
|
||||
index 6085248083194be05e85c3be7f0e69fd1928bf3d..23b72e2d0030496d5b05c88f06ed1a30ed33396b 100644
|
||||
index 1ba2051ed316956a5a71f85ed5fa0735d54716e5..c0d6f45ce14040a79cfe134a4f8254434a4c53cc 100644
|
||||
--- a/dom/workers/RuntimeService.cpp
|
||||
+++ b/dom/workers/RuntimeService.cpp
|
||||
@@ -998,7 +998,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
|
||||
@@ -1007,7 +1007,7 @@ void PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsTArray<nsString> languages;
|
||||
|
|
@ -1881,7 +1881,7 @@ index 6085248083194be05e85c3be7f0e69fd1928bf3d..23b72e2d0030496d5b05c88f06ed1a30
|
|||
|
||||
RuntimeService* runtime = RuntimeService::GetService();
|
||||
if (runtime) {
|
||||
@@ -1185,8 +1185,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) {
|
||||
@@ -1194,8 +1194,7 @@ bool RuntimeService::RegisterWorker(WorkerPrivate& aWorkerPrivate) {
|
||||
}
|
||||
|
||||
// The navigator overridden properties should have already been read.
|
||||
|
|
@ -1891,7 +1891,7 @@ index 6085248083194be05e85c3be7f0e69fd1928bf3d..23b72e2d0030496d5b05c88f06ed1a30
|
|||
mNavigatorPropertiesLoaded = true;
|
||||
}
|
||||
|
||||
@@ -1808,6 +1807,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted(
|
||||
@@ -1817,6 +1816,13 @@ void RuntimeService::PropagateStorageAccessPermissionGranted(
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1905,7 +1905,7 @@ index 6085248083194be05e85c3be7f0e69fd1928bf3d..23b72e2d0030496d5b05c88f06ed1a30
|
|||
template <typename Func>
|
||||
void RuntimeService::BroadcastAllWorkers(const Func& aFunc) {
|
||||
AssertIsOnMainThread();
|
||||
@@ -2333,6 +2339,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers(
|
||||
@@ -2342,6 +2348,14 @@ void PropagateStorageAccessPermissionGrantedToWorkers(
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1947,7 +1947,7 @@ index 58894a8361c7ef1dddd481ca5877a209a8b8ff5c..c481d40d79b6397b7f1d571bd9f6ae5c
|
|||
|
||||
bool IsWorkerGlobal(JSObject* global);
|
||||
diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp
|
||||
index 089f42307becf7c6f81199d970fb8870db494818..63fb760ac831bc88415aee1cddf8b59662e55f37 100644
|
||||
index 2b48cc2980165ce51ded62faef96b93b781ede32..d8dc90983353c2f5cc1db56e327c4533d524cc1d 100644
|
||||
--- a/dom/workers/WorkerPrivate.cpp
|
||||
+++ b/dom/workers/WorkerPrivate.cpp
|
||||
@@ -700,6 +700,18 @@ class UpdateContextOptionsRunnable final : public WorkerControlRunnable {
|
||||
|
|
@ -1969,7 +1969,7 @@ index 089f42307becf7c6f81199d970fb8870db494818..63fb760ac831bc88415aee1cddf8b596
|
|||
class UpdateLanguagesRunnable final : public WorkerThreadRunnable {
|
||||
nsTArray<nsString> mLanguages;
|
||||
|
||||
@@ -2108,6 +2120,16 @@ void WorkerPrivate::UpdateContextOptions(
|
||||
@@ -2113,6 +2125,16 @@ void WorkerPrivate::UpdateContextOptions(
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1986,7 +1986,7 @@ index 089f42307becf7c6f81199d970fb8870db494818..63fb760ac831bc88415aee1cddf8b596
|
|||
void WorkerPrivate::UpdateLanguages(const nsTArray<nsString>& aLanguages) {
|
||||
AssertIsOnParentThread();
|
||||
|
||||
@@ -5736,6 +5758,15 @@ void WorkerPrivate::UpdateContextOptionsInternal(
|
||||
@@ -5740,6 +5762,15 @@ void WorkerPrivate::UpdateContextOptionsInternal(
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2003,7 +2003,7 @@ index 089f42307becf7c6f81199d970fb8870db494818..63fb760ac831bc88415aee1cddf8b596
|
|||
const nsTArray<nsString>& aLanguages) {
|
||||
WorkerGlobalScope* globalScope = GlobalScope();
|
||||
diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h
|
||||
index dfb96b7b798785d7b75c683bc0969e39487137a3..a463eec618af51fdbc25db509870598846c0fd66 100644
|
||||
index da25a495a8930f7b88958553164d86fe2869416f..38f92829438ead78d73d96ee48a3fcab46c337d7 100644
|
||||
--- a/dom/workers/WorkerPrivate.h
|
||||
+++ b/dom/workers/WorkerPrivate.h
|
||||
@@ -432,6 +432,8 @@ class WorkerPrivate final
|
||||
|
|
@ -2015,7 +2015,7 @@ index dfb96b7b798785d7b75c683bc0969e39487137a3..a463eec618af51fdbc25db5098705988
|
|||
void UpdateLanguagesInternal(const nsTArray<nsString>& aLanguages);
|
||||
|
||||
void UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key,
|
||||
@@ -1059,6 +1061,8 @@ class WorkerPrivate final
|
||||
@@ -1069,6 +1071,8 @@ class WorkerPrivate final
|
||||
|
||||
void UpdateContextOptions(const JS::ContextOptions& aContextOptions);
|
||||
|
||||
|
|
@ -2247,10 +2247,10 @@ index 0ec6ee3eb37c6493d8a25352fd0e54e1927bceab..885dba71bc5815e5f6f3ec2700c376aa
|
|||
// No boxes to return
|
||||
return;
|
||||
diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
|
||||
index f154e05a8c2e2ebf07565d087a42436feeb17f53..0af8c24c0f391c52fe2acfeb01cacb32358e6861 100644
|
||||
index 2cc3c5673e246ac38cdaddb457ce36ee4c7356ce..61093cd52fc049ad77d84dd837731071fdace69d 100644
|
||||
--- a/layout/base/PresShell.cpp
|
||||
+++ b/layout/base/PresShell.cpp
|
||||
@@ -11064,7 +11064,9 @@ bool PresShell::ComputeActiveness() const {
|
||||
@@ -11163,7 +11163,9 @@ bool PresShell::ComputeActiveness() const {
|
||||
if (!browserChild->IsVisible()) {
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
(" > BrowserChild %p is not visible", browserChild));
|
||||
|
|
@ -2262,7 +2262,7 @@ index f154e05a8c2e2ebf07565d087a42436feeb17f53..0af8c24c0f391c52fe2acfeb01cacb32
|
|||
|
||||
// If the browser is visible but just due to be preserving layers
|
||||
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
|
||||
index 0011b1a1a36da0dec7cc6afa6fd689a4c8710d37..25bebd7b03b0b8dc595607bae07f360f3be3f284 100644
|
||||
index d8995d6d94f5861d8ed839613768eb269b44e362..b946de49a0ecab449a9d1aff24c38f69a431eec0 100644
|
||||
--- a/layout/base/nsLayoutUtils.cpp
|
||||
+++ b/layout/base/nsLayoutUtils.cpp
|
||||
@@ -698,6 +698,10 @@ bool nsLayoutUtils::AllowZoomingForDocument(
|
||||
|
|
@ -2276,7 +2276,7 @@ index 0011b1a1a36da0dec7cc6afa6fd689a4c8710d37..25bebd7b03b0b8dc595607bae07f360f
|
|||
// True if we allow zooming for all documents on this platform, or if we are
|
||||
// in RDM.
|
||||
BrowsingContext* bc = aDocument->GetBrowsingContext();
|
||||
@@ -9791,6 +9795,9 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont,
|
||||
@@ -9768,6 +9772,9 @@ void nsLayoutUtils::ComputeSystemFont(nsFont* aSystemFont,
|
||||
|
||||
/* static */
|
||||
bool nsLayoutUtils::ShouldHandleMetaViewport(const Document* aDocument) {
|
||||
|
|
@ -2287,10 +2287,10 @@ index 0011b1a1a36da0dec7cc6afa6fd689a4c8710d37..25bebd7b03b0b8dc595607bae07f360f
|
|||
return StaticPrefs::dom_meta_viewport_enabled() || (bc && bc->InRDMPane());
|
||||
}
|
||||
diff --git a/layout/style/GeckoBindings.h b/layout/style/GeckoBindings.h
|
||||
index d273793fc8d92b5c19ec0562730eab249cc41eb8..46b4078c6031318265a8338e01f52ab60bd9c0e8 100644
|
||||
index c18d38d8ad2f80bb0d3512d1a9ae965c594bb356..22736c86eb5e3d0a44563c312e34032c157f3abe 100644
|
||||
--- a/layout/style/GeckoBindings.h
|
||||
+++ b/layout/style/GeckoBindings.h
|
||||
@@ -596,6 +596,7 @@ float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*);
|
||||
@@ -595,6 +595,7 @@ float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*);
|
||||
bool Gecko_MediaFeatures_PrefersReducedMotion(const mozilla::dom::Document*);
|
||||
bool Gecko_MediaFeatures_PrefersReducedTransparency(
|
||||
const mozilla::dom::Document*);
|
||||
|
|
@ -2351,7 +2351,7 @@ index 21d5a5e1b4193d058c30268ab73c8d595436b381..11b960ec0ff3ea77857cb915d05bbdbb
|
|||
+
|
||||
} // namespace mozilla::net
|
||||
diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h
|
||||
index 52d867196a459578cbea1a4f626afbe51dd1abd5..2904832cbcad476fdebb54c7e24d5f14b1c0fb4b 100644
|
||||
index 6ba1d8e11efbbf75f4a44d4977587429ad1371f8..d834f1f5528264f59c4547f00825e0a3b433d9dd 100644
|
||||
--- a/netwerk/base/LoadInfo.h
|
||||
+++ b/netwerk/base/LoadInfo.h
|
||||
@@ -413,9 +413,10 @@ class LoadInfo final : public nsILoadInfo {
|
||||
|
|
@ -2360,7 +2360,7 @@ index 52d867196a459578cbea1a4f626afbe51dd1abd5..2904832cbcad476fdebb54c7e24d5f14
|
|||
bool mWasSchemelessInput = false;
|
||||
-
|
||||
nsILoadInfo::HTTPSUpgradeTelemetryType mHttpsUpgradeTelemetry =
|
||||
nsILoadInfo::NO_UPGRADE;
|
||||
nsILoadInfo::NOT_INITIALIZED;
|
||||
+
|
||||
+ uint64_t mJugglerLoadIdentifier = 0;
|
||||
};
|
||||
|
|
@ -2387,10 +2387,10 @@ index 9dc2bb0da6871b905abd17d931e555429977c6c2..b71cf6393492346f16417b3ba745a235
|
|||
} // namespace net
|
||||
} // namespace mozilla
|
||||
diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl
|
||||
index 12f43b911006d5b0bbfa9936070dc0d561bc7bb4..94d20cdca548534ad5e4ef4f937e287c58768870 100644
|
||||
index daccd1dc75fb1f6ba88c8f734e10c14cbdbffe8f..9621ca5dc05f12a8d81da787fa479fe03ea99e4c 100644
|
||||
--- a/netwerk/base/nsILoadInfo.idl
|
||||
+++ b/netwerk/base/nsILoadInfo.idl
|
||||
@@ -1586,4 +1586,5 @@ interface nsILoadInfo : nsISupports
|
||||
@@ -1590,4 +1590,5 @@ interface nsILoadInfo : nsISupports
|
||||
*/
|
||||
[infallible] attribute nsILoadInfo_HTTPSUpgradeTelemetryType httpsUpgradeTelemetry;
|
||||
|
||||
|
|
@ -2409,7 +2409,7 @@ index 7f91d2df6f8bb4020c75c132dc8f6bf26625fa1e..ba6569f4be8fc54ec96ee44d5de45a09
|
|||
/**
|
||||
* Set the status and reason for the forthcoming synthesized response.
|
||||
diff --git a/netwerk/ipc/DocumentLoadListener.cpp b/netwerk/ipc/DocumentLoadListener.cpp
|
||||
index 10f65a549ce886bf7f19de02714482e28a8931a5..f41d32ce90f7345ad5a9bd90e420354865f35235 100644
|
||||
index ef946929c9bbd7903c8e3b32bcb373d5096aed52..a2814c5c891e2877aca9fdb4698385282b8743a9 100644
|
||||
--- a/netwerk/ipc/DocumentLoadListener.cpp
|
||||
+++ b/netwerk/ipc/DocumentLoadListener.cpp
|
||||
@@ -171,6 +171,7 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
|
||||
|
|
@ -2459,10 +2459,10 @@ index e81a4538fd45c13aa60d933de5f4f32ce69fb5f2..d7945f81295c497485a09696f06ce041
|
|||
if (mPump && mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
|
||||
mPump->PeekStream(CallTypeSniffers, static_cast<nsIChannel*>(this));
|
||||
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
|
||||
index f25949e6cc907ff18a76d68fc2e8005bd40146ea..9be4cb34517b06b94c6e145aef8a8ea5d2687d97 100644
|
||||
index 071ed8da4135102b0b1fedce32326bdc0657c3fd..063b516001a674b558046f9191f08352eb671801 100644
|
||||
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
|
||||
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
|
||||
@@ -1389,6 +1389,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
|
||||
@@ -1391,6 +1391,10 @@ void nsHtml5TreeOpExecutor::UpdateReferrerInfoFromMeta(
|
||||
void nsHtml5TreeOpExecutor::AddSpeculationCSP(const nsAString& aCSP) {
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
|
|
@ -2474,10 +2474,10 @@ index f25949e6cc907ff18a76d68fc2e8005bd40146ea..9be4cb34517b06b94c6e145aef8a8ea5
|
|||
nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = mDocument->GetPreloadCsp();
|
||||
if (!preloadCsp) {
|
||||
diff --git a/security/manager/ssl/nsCertOverrideService.cpp b/security/manager/ssl/nsCertOverrideService.cpp
|
||||
index fcc2a45e6de8eaeb1af2404a69bd3df58cf2aec8..d4c1df007bf5993cf9e0dadbe91aa2c38afc42ec 100644
|
||||
index b2e328e7c7d7a89be34b84fd176c306a3620c77c..54f24b213bcdc78c702e15d4d45a3943bc082281 100644
|
||||
--- a/security/manager/ssl/nsCertOverrideService.cpp
|
||||
+++ b/security/manager/ssl/nsCertOverrideService.cpp
|
||||
@@ -437,7 +437,12 @@ nsCertOverrideService::HasMatchingOverride(
|
||||
@@ -439,7 +439,12 @@ nsCertOverrideService::HasMatchingOverride(
|
||||
bool disableAllSecurityCheck = false;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
|
@ -2491,7 +2491,7 @@ index fcc2a45e6de8eaeb1af2404a69bd3df58cf2aec8..d4c1df007bf5993cf9e0dadbe91aa2c3
|
|||
}
|
||||
if (disableAllSecurityCheck) {
|
||||
*aIsTemporary = false;
|
||||
@@ -649,14 +654,24 @@ static bool IsDebugger() {
|
||||
@@ -651,14 +656,24 @@ static bool IsDebugger() {
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCertOverrideService::
|
||||
|
|
@ -2625,10 +2625,10 @@ index 00a5381133f8cec0de452c31c7151801a1acc0b9..5d3e3d6f566dc724f257beaeb994ceda
|
|||
|
||||
if (provider.failed) {
|
||||
diff --git a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
|
||||
index 144628a310662eb393d8c1a4fffbec3cf5fd1dff..69fa66f27d0533f3d90801acbfa23039ef81f7df 100644
|
||||
index 6a40d032449e780bfeb77934ba141317b94e7189..1468d38355058b985f18613bd6e3bc84086fae40 100644
|
||||
--- a/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
|
||||
+++ b/toolkit/components/resistfingerprinting/nsUserCharacteristics.cpp
|
||||
@@ -632,7 +632,7 @@ void PopulateLanguages() {
|
||||
@@ -553,7 +553,7 @@ void PopulateLanguages() {
|
||||
// sufficient to only collect this information as the other properties are
|
||||
// just reformats of Navigator::GetAcceptLanguages.
|
||||
nsTArray<nsString> languages;
|
||||
|
|
@ -2666,10 +2666,10 @@ index 654903fadb709be976b72f36f155e23bc0622152..815b3dc24c9fda6b1db6c4666ac68904
|
|||
int32_t aMaxSelfProgress,
|
||||
int32_t aCurTotalProgress,
|
||||
diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
|
||||
index cdba76dc8ae2206a58d7e5eb6eba97c2c3732513..266fdc6235363eafc6c7b587d5c0f597deee6e59 100644
|
||||
index e3f616c4efd5d7e10ed372afa4b5c4d2d93e6a67..abb7772184c9baf23025c1577d1284b6ed1959fb 100644
|
||||
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
|
||||
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
|
||||
@@ -1865,7 +1865,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent(
|
||||
@@ -1881,7 +1881,11 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForContent(
|
||||
|
||||
// Open a minimal popup.
|
||||
*aIsPopupRequested = true;
|
||||
|
|
@ -2683,10 +2683,10 @@ index cdba76dc8ae2206a58d7e5eb6eba97c2c3732513..266fdc6235363eafc6c7b587d5c0f597
|
|||
|
||||
/**
|
||||
diff --git a/toolkit/mozapps/update/UpdateService.sys.mjs b/toolkit/mozapps/update/UpdateService.sys.mjs
|
||||
index be01248253ee1bcc9435c3e8223ed032f498a023..0f05923c29a023511b72a81ec527300cafa17760 100644
|
||||
index 6c2b400952492266a184c96b4a1ce71e87df7ffe..6e1fb4f59b6a6d70100e1eb1d15c937d8480988a 100644
|
||||
--- a/toolkit/mozapps/update/UpdateService.sys.mjs
|
||||
+++ b/toolkit/mozapps/update/UpdateService.sys.mjs
|
||||
@@ -3888,6 +3888,8 @@ export class UpdateService {
|
||||
@@ -3894,6 +3894,8 @@ export class UpdateService {
|
||||
}
|
||||
|
||||
get disabledForTesting() {
|
||||
|
|
@ -2696,10 +2696,10 @@ index be01248253ee1bcc9435c3e8223ed032f498a023..0f05923c29a023511b72a81ec527300c
|
|||
}
|
||||
|
||||
diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild
|
||||
index 8c2b2bf996bd889651dc7fac1dc351b4c47b567e..07d237eb17a657ce051fd0aa5e53449c0c3159f6 100644
|
||||
index f42ed17a4a75689ae9c8e769d7b6e2c3f654b8ee..5af0877335339407160dd7d10b89bd3d62adff9f 100644
|
||||
--- a/toolkit/toolkit.mozbuild
|
||||
+++ b/toolkit/toolkit.mozbuild
|
||||
@@ -155,6 +155,7 @@ if CONFIG["ENABLE_WEBDRIVER"]:
|
||||
@@ -156,6 +156,7 @@ if CONFIG["ENABLE_WEBDRIVER"]:
|
||||
"/remote",
|
||||
"/testing/firefox-ui",
|
||||
"/testing/marionette",
|
||||
|
|
@ -2760,7 +2760,7 @@ index fe72a2715da8846146377e719559c16e6ef1f7ff..a5959143bac8f62ee359fa3883a844f3
|
|||
// nsDocumentViewer::LoadComplete that doesn't do various things
|
||||
// that are not relevant here because this wasn't an actual
|
||||
diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp
|
||||
index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790eafcbc6a 100644
|
||||
index 139a43a1780dac34a6d8b135accac9cf39beef3f..2a855c3ae87e4e5a431cb1547cda0ee88490ca33 100644
|
||||
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
|
||||
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
|
||||
@@ -112,6 +112,7 @@
|
||||
|
|
@ -2771,7 +2771,7 @@ index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ipc/URIUtils.h"
|
||||
|
||||
@@ -831,6 +832,12 @@ NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(
|
||||
@@ -872,6 +873,12 @@ NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
@ -2784,7 +2784,7 @@ index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790
|
|||
nsresult nsExternalHelperAppService::GetFileTokenForPath(
|
||||
const char16_t* aPlatformAppPath, nsIFile** aFile) {
|
||||
nsDependentString platformAppPath(aPlatformAppPath);
|
||||
@@ -1441,7 +1448,12 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel* aChannel) {
|
||||
@@ -1494,7 +1501,12 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel* aChannel) {
|
||||
// Strip off the ".part" from mTempLeafName
|
||||
mTempLeafName.Truncate(mTempLeafName.Length() - ArrayLength(".part") + 1);
|
||||
|
||||
|
|
@ -2797,7 +2797,7 @@ index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790
|
|||
mSaver =
|
||||
do_CreateInstance(NS_BACKGROUNDFILESAVERSTREAMLISTENER_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@@ -1630,7 +1642,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||
@@ -1683,7 +1695,36 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
@ -2835,7 +2835,7 @@ index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790
|
|||
if (NS_FAILED(rv)) {
|
||||
nsresult transferError = rv;
|
||||
|
||||
@@ -1682,6 +1723,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||
@@ -1744,6 +1785,9 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||
|
||||
bool alwaysAsk = true;
|
||||
mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
|
||||
|
|
@ -2845,7 +2845,7 @@ index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790
|
|||
if (alwaysAsk) {
|
||||
// But we *don't* ask if this mimeInfo didn't come from
|
||||
// our user configuration datastore and the user has said
|
||||
@@ -2198,6 +2242,16 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver,
|
||||
@@ -2260,6 +2304,16 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver* aSaver,
|
||||
NotifyTransfer(aStatus);
|
||||
}
|
||||
|
||||
|
|
@ -2862,7 +2862,7 @@ index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -2679,6 +2733,15 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) {
|
||||
@@ -2743,6 +2797,15 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2879,10 +2879,10 @@ index ad769a235b6a7ccf7791a3d9680f3bf373b30a86..ff18e7516a2bc3258b00d958cbaa4790
|
|||
// OnStartRequest)
|
||||
mDialog = nullptr;
|
||||
diff --git a/uriloader/exthandler/nsExternalHelperAppService.h b/uriloader/exthandler/nsExternalHelperAppService.h
|
||||
index 1f77e095dbfa3acc046779114007d83fc1cfa087..2354abbab7af6f6bdc3bd628722f03ea401d236a 100644
|
||||
index e880b90b2df85fb3b1ab3ba8d2fc181b824e2272..dbadd74dea9b245d68da3b2856e16b7b98c655d0 100644
|
||||
--- a/uriloader/exthandler/nsExternalHelperAppService.h
|
||||
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
|
||||
@@ -257,6 +257,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService,
|
||||
@@ -258,6 +258,8 @@ class nsExternalHelperAppService : public nsIExternalHelperAppService,
|
||||
mozilla::dom::BrowsingContext* aContentContext, bool aForceSave,
|
||||
nsIInterfaceRequestor* aWindowContext,
|
||||
nsIStreamListener** aStreamListener);
|
||||
|
|
@ -2891,7 +2891,7 @@ index 1f77e095dbfa3acc046779114007d83fc1cfa087..2354abbab7af6f6bdc3bd628722f03ea
|
|||
};
|
||||
|
||||
/**
|
||||
@@ -462,6 +464,9 @@ class nsExternalAppHandler final : public nsIStreamListener,
|
||||
@@ -463,6 +465,9 @@ class nsExternalAppHandler final : public nsIStreamListener,
|
||||
* Upon successful return, both mTempFile and mSaver will be valid.
|
||||
*/
|
||||
nsresult SetUpTempFile(nsIChannel* aChannel);
|
||||
|
|
@ -2902,7 +2902,7 @@ index 1f77e095dbfa3acc046779114007d83fc1cfa087..2354abbab7af6f6bdc3bd628722f03ea
|
|||
* When we download a helper app, we are going to retarget all load
|
||||
* notifications into our own docloader and load group instead of
|
||||
diff --git a/uriloader/exthandler/nsIExternalHelperAppService.idl b/uriloader/exthandler/nsIExternalHelperAppService.idl
|
||||
index 4a399acb72d4fd475c9ae43e9eadbc32f261e290..97ace81c82b16a9a993166dd4b0ddb3a721c9872 100644
|
||||
index 53ea934dd4876e4b491b724385c8fbf7d00ee6cd..0b7b88c853b21ce778d8e87fea0a2bfe839ad412 100644
|
||||
--- a/uriloader/exthandler/nsIExternalHelperAppService.idl
|
||||
+++ b/uriloader/exthandler/nsIExternalHelperAppService.idl
|
||||
@@ -6,8 +6,11 @@
|
||||
|
|
@ -2935,10 +2935,11 @@ index 4a399acb72d4fd475c9ae43e9eadbc32f261e290..97ace81c82b16a9a993166dd4b0ddb3a
|
|||
/**
|
||||
* The external helper app service is used for finding and launching
|
||||
* platform specific external applications for a given mime content type.
|
||||
@@ -76,6 +90,7 @@ interface nsIExternalHelperAppService : nsISupports
|
||||
boolean applyDecodingForExtension(in AUTF8String aExtension,
|
||||
in ACString aEncodingType);
|
||||
|
||||
@@ -87,6 +101,8 @@ interface nsIExternalHelperAppService : nsISupports
|
||||
* `DownloadIntegration.sys.mjs`, which is implemented on all platforms.
|
||||
*/
|
||||
nsIFile getPreferredDownloadsDirectory();
|
||||
+
|
||||
+ void setDownloadInterceptor(in nsIDownloadInterceptor interceptor);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
REMOTE_URL="https://github.com/WebKit/WebKit.git"
|
||||
BASE_BRANCH="main"
|
||||
BASE_REVISION="2ea46ab90e6511139bfb94415205038b672381e0"
|
||||
BASE_REVISION="8ceb1da47e75a488ae4c12017a861636904acd4f"
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ const NSActivityOptions ActivityOptions =
|
|||
|
||||
for (NSString *argument in subArray) {
|
||||
if (![argument hasPrefix:@"--"])
|
||||
_initialURL = argument;
|
||||
_initialURL = [argument copy];
|
||||
if ([argument hasPrefix:@"--user-data-dir="]) {
|
||||
NSRange range = NSMakeRange(16, [argument length] - 16);
|
||||
_userDataDir = [[argument substringWithRange:range] copy];
|
||||
|
|
@ -230,7 +230,7 @@ const NSActivityOptions ActivityOptions =
|
|||
configuration = [[WKWebViewConfiguration alloc] init];
|
||||
configuration.websiteDataStore = [self persistentDataStore];
|
||||
configuration._controlledByAutomation = true;
|
||||
configuration.preferences._fullScreenEnabled = YES;
|
||||
configuration.preferences.elementFullscreenEnabled = YES;
|
||||
configuration.preferences._developerExtrasEnabled = YES;
|
||||
configuration.preferences._mediaDevicesEnabled = YES;
|
||||
configuration.preferences._mockCaptureDevicesEnabled = YES;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -93,11 +93,20 @@ Element is considered stable when it has maintained the same bounding box for at
|
|||
|
||||
## Enabled
|
||||
|
||||
Element is considered enabled unless it is a `<button>`, `<select>`, `<input>` or `<textarea>` with a `disabled` property.
|
||||
Element is considered enabled when it is **not disabled**.
|
||||
|
||||
Element is **disabled** when:
|
||||
- it is a `<button>`, `<select>`, `<input>`, `<textarea>`, `<option>` or `<optgroup>` with a `[disabled]` attribute;
|
||||
- it is a `<button>`, `<select>`, `<input>`, `<textarea>`, `<option>` or `<optgroup>` that is a part of a `<fieldset>` with a `[disabled]` attribute;
|
||||
- it is a descendant of an element with `[aria-disabled=true]` attribute.
|
||||
|
||||
## Editable
|
||||
|
||||
Element is considered editable when it is [enabled] and does not have `readonly` property set.
|
||||
Element is considered editable when it is [enabled] and is **not readonly**.
|
||||
|
||||
Element is **readonly** when:
|
||||
- it is a `<select>`, `<input>` or `<textarea>` with a `[readonly]` attribute;
|
||||
- it has an `[aria-readonly=true]` attribute and an aria role that [supports it](https://w3c.github.io/aria/#aria-readonly).
|
||||
|
||||
## Receives Events
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ In case this browser is connected to, clears all created contexts belonging to t
|
|||
browser server.
|
||||
|
||||
:::note
|
||||
This is similar to force quitting the browser. Therefore, you should call [`method: BrowserContext.close`] on any [BrowserContext]'s you explicitly created earlier with [`method: Browser.newContext`] **before** calling [`method: Browser.close`].
|
||||
This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close events, call [`method: BrowserContext.close`] on any [BrowserContext] instances you explicitly created earlier using [`method: Browser.newContext`] **before** calling [`method: Browser.close`].
|
||||
:::
|
||||
|
||||
The [Browser] object itself is considered to be disposed and cannot be used anymore.
|
||||
|
|
|
|||
|
|
@ -963,9 +963,14 @@ specified.
|
|||
* since: v1.8
|
||||
- `permissions` <[Array]<[string]>>
|
||||
|
||||
A permission or an array of permissions to grant. Permissions can be one of the following values:
|
||||
A list of permissions to grant.
|
||||
|
||||
:::danger
|
||||
Supported permissions differ between browsers, and even between different versions of the same browser. Any permission may stop working after an update.
|
||||
:::
|
||||
|
||||
Here are some permissions that may be supported by some browsers:
|
||||
* `'accelerometer'`
|
||||
* `'accessibility-events'`
|
||||
* `'ambient-light-sensor'`
|
||||
* `'background-sync'`
|
||||
* `'camera'`
|
||||
|
|
@ -1407,7 +1412,7 @@ This setting will change the default maximum time for all the methods accepting
|
|||
* since: v1.8
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds
|
||||
Maximum time in milliseconds. Pass `0` to disable timeout.
|
||||
|
||||
## async method: BrowserContext.setExtraHTTPHeaders
|
||||
* since: v1.8
|
||||
|
|
|
|||
|
|
@ -161,6 +161,41 @@ await page.Clock.PauseAtAsync(DateTime.Parse("2020-02-02"));
|
|||
await page.Clock.PauseAtAsync("2020-02-02");
|
||||
```
|
||||
|
||||
For best results, install the clock before navigating the page and set it to a time slightly before the intended test time. This ensures that all timers run normally during page loading, preventing the page from getting stuck. Once the page has fully loaded, you can safely use [`method: Clock.pauseAt`] to pause the clock.
|
||||
|
||||
```js
|
||||
// Initialize clock with some time before the test time and let the page load
|
||||
// naturally. `Date.now` will progress as the timers fire.
|
||||
await page.clock.install({ time: new Date('2024-12-10T08:00:00') });
|
||||
await page.goto('http://localhost:3333');
|
||||
await page.clock.pauseAt(new Date('2024-12-10T10:00:00'));
|
||||
```
|
||||
|
||||
```python async
|
||||
# Initialize clock with some time before the test time and let the page load
|
||||
# naturally. `Date.now` will progress as the timers fire.
|
||||
await page.clock.install(time=datetime.datetime(2024, 12, 10, 8, 0, 0))
|
||||
await page.goto("http://localhost:3333")
|
||||
await page.clock.pause_at(datetime.datetime(2024, 12, 10, 10, 0, 0))
|
||||
```
|
||||
|
||||
```python sync
|
||||
# Initialize clock with some time before the test time and let the page load
|
||||
# naturally. `Date.now` will progress as the timers fire.
|
||||
page.clock.install(time=datetime.datetime(2024, 12, 10, 8, 0, 0))
|
||||
page.goto("http://localhost:3333")
|
||||
page.clock.pause_at(datetime.datetime(2024, 12, 10, 10, 0, 0))
|
||||
```
|
||||
|
||||
```java
|
||||
// Initialize clock with some time before the test time and let the page load
|
||||
// naturally. `Date.now` will progress as the timers fire.
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss");
|
||||
page.clock().install(new Clock.InstallOptions().setTime(format.parse("2024-12-10T08:00:00")));
|
||||
page.navigate("http://localhost:3333");
|
||||
page.clock().pauseAt(format.parse("2024-12-10T10:00:00"));
|
||||
```
|
||||
|
||||
### param: Clock.pauseAt.time
|
||||
* langs: js, java
|
||||
* since: v1.45
|
||||
|
|
|
|||
|
|
@ -154,7 +154,8 @@ Additional locator to match.
|
|||
* since: v1.49
|
||||
- returns: <[string]>
|
||||
|
||||
Captures the aria snapshot of the given element. See [`method: LocatorAssertions.toMatchAriaSnapshot`] for the corresponding assertion.
|
||||
Captures the aria snapshot of the given element.
|
||||
Read more about [aria snapshots](../aria-snapshots.md) and [`method: LocatorAssertions.toMatchAriaSnapshot#2`] for the corresponding assertion.
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
@ -205,6 +206,9 @@ Below is the HTML markup and the respective ARIA snapshot:
|
|||
- link "About"
|
||||
```
|
||||
|
||||
### option: Locator.ariaSnapshot.timeout = %%-input-timeout-%%
|
||||
* since: v1.49
|
||||
|
||||
### option: Locator.ariaSnapshot.timeout = %%-input-timeout-js-%%
|
||||
* since: v1.49
|
||||
|
||||
|
|
@ -1479,7 +1483,7 @@ Boolean disabled = await page.GetByRole(AriaRole.Button).IsDisabledAsync();
|
|||
* since: v1.14
|
||||
- returns: <[boolean]>
|
||||
|
||||
Returns whether the element is [editable](../actionability.md#editable).
|
||||
Returns whether the element is [editable](../actionability.md#editable). If the target element is not an `<input>`, `<textarea>`, `<select>`, `[contenteditable]` and does not have a role allowing `[aria-readonly]`, this method throws an error.
|
||||
|
||||
:::warning[Asserting editable state]
|
||||
If you need to assert that an element is editable, prefer [`method: LocatorAssertions.toBeEditable`] to avoid flakiness. See [assertions guide](../test-assertions.md) for more details.
|
||||
|
|
|
|||
|
|
@ -442,6 +442,23 @@ Expected options currently selected.
|
|||
### option: LocatorAssertions.NotToHaveValues.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.23
|
||||
|
||||
## async method: LocatorAssertions.NotToMatchAriaSnapshot
|
||||
* since: v1.49
|
||||
* langs: python
|
||||
|
||||
The opposite of [`method: LocatorAssertions.toMatchAriaSnapshot#2`].
|
||||
|
||||
### param: LocatorAssertions.NotToMatchAriaSnapshot.expected
|
||||
* since: v1.49
|
||||
- `expected` <string>
|
||||
|
||||
### option: LocatorAssertions.NotToMatchAriaSnapshot.timeout = %%-js-assertions-timeout-%%
|
||||
* since: v1.49
|
||||
|
||||
### option: LocatorAssertions.NotToMatchAriaSnapshot.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.49
|
||||
|
||||
|
||||
|
||||
## async method: LocatorAssertions.toBeAttached
|
||||
* since: v1.33
|
||||
|
|
@ -701,7 +718,7 @@ expect(locator).to_be_enabled()
|
|||
|
||||
```csharp
|
||||
var locator = Page.Locator("button.submit");
|
||||
await Expect(locator).toBeEnabledAsync();
|
||||
await Expect(locator).ToBeEnabledAsync();
|
||||
```
|
||||
|
||||
### option: LocatorAssertions.toBeEnabled.enabled
|
||||
|
|
@ -1181,7 +1198,7 @@ expect(locator).to_have_accessible_description("Save results to disk")
|
|||
|
||||
```csharp
|
||||
var locator = Page.GetByTestId("save-button");
|
||||
await Expect(locator).toHaveAccessibleDescriptionAsync("Save results to disk");
|
||||
await Expect(locator).ToHaveAccessibleDescriptionAsync("Save results to disk");
|
||||
```
|
||||
|
||||
### param: LocatorAssertions.toHaveAccessibleDescription.description
|
||||
|
|
@ -1231,12 +1248,12 @@ expect(locator).to_have_accessible_name("Save to disk")
|
|||
|
||||
```csharp
|
||||
var locator = Page.GetByTestId("save-button");
|
||||
await Expect(locator).toHaveAccessibleNameAsync("Save to disk");
|
||||
await Expect(locator).ToHaveAccessibleNameAsync("Save to disk");
|
||||
```
|
||||
|
||||
### param: LocatorAssertions.toHaveAccessibleName.name
|
||||
* since: v1.44
|
||||
- `name` <[string]|[RegExp]>
|
||||
- `name` <[string]|[RegExp]|[Array]<[string]|[RegExp]>>
|
||||
|
||||
Expected accessible name.
|
||||
|
||||
|
|
@ -2104,12 +2121,63 @@ Expected options currently selected.
|
|||
* since: v1.23
|
||||
|
||||
|
||||
## async method: LocatorAssertions.toMatchAriaSnapshot
|
||||
## async method: LocatorAssertions.toMatchAriaSnapshot#1
|
||||
* since: v1.50
|
||||
* langs:
|
||||
- alias-java: matchesAriaSnapshot
|
||||
|
||||
Asserts that the target element matches the given [accessibility snapshot](../aria-snapshots.md).
|
||||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot();
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ name: 'snapshot' });
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot({ path: '/path/to/snapshot.yml' });
|
||||
```
|
||||
|
||||
```python async
|
||||
await expect(page.locator('body')).to_match_aria_snapshot(path='/path/to/snapshot.yml')
|
||||
```
|
||||
|
||||
```python sync
|
||||
expect(page.locator('body')).to_match_aria_snapshot(path='/path/to/snapshot.yml')
|
||||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page.Locator("body")).ToMatchAriaSnapshotAsync(new { Path = "/path/to/snapshot.yml" });
|
||||
```
|
||||
|
||||
```java
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot(new LocatorAssertions.MatchesAriaSnapshotOptions().setPath("/path/to/snapshot.yml"));
|
||||
```
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#1.name
|
||||
* since: v1.50
|
||||
* langs: js
|
||||
- `name` <[string]>
|
||||
|
||||
Name of the snapshot to store in the snapshot folder corresponding to this test. Generates ordinal name if not specified.
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#1.path
|
||||
* since: v1.50
|
||||
- `path` <[string]>
|
||||
|
||||
Path to the YAML snapshot file.
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#1.timeout = %%-js-assertions-timeout-%%
|
||||
* since: v1.50
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#1.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.50
|
||||
|
||||
|
||||
## async method: LocatorAssertions.toMatchAriaSnapshot#2
|
||||
* since: v1.49
|
||||
* langs:
|
||||
- alias-java: matchesAriaSnapshot
|
||||
|
||||
Asserts that the target element matches the given accessibility snapshot.
|
||||
Asserts that the target element matches the given [accessibility snapshot](../aria-snapshots.md).
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
@ -2122,7 +2190,7 @@ await expect(page.locator('body')).toMatchAriaSnapshot(`
|
|||
```
|
||||
|
||||
```python async
|
||||
await page.goto('https://demo.playwright.dev/todomvc/')
|
||||
await page.goto("https://demo.playwright.dev/todomvc/")
|
||||
await expect(page.locator('body')).to_match_aria_snapshot('''
|
||||
- heading "todos"
|
||||
- textbox "What needs to be done?"
|
||||
|
|
@ -2130,7 +2198,7 @@ await expect(page.locator('body')).to_match_aria_snapshot('''
|
|||
```
|
||||
|
||||
```python sync
|
||||
page.goto('https://demo.playwright.dev/todomvc/')
|
||||
page.goto("https://demo.playwright.dev/todomvc/")
|
||||
expect(page.locator('body')).to_match_aria_snapshot('''
|
||||
- heading "todos"
|
||||
- textbox "What needs to be done?"
|
||||
|
|
@ -2153,9 +2221,12 @@ assertThat(page.locator("body")).matchesAriaSnapshot("""
|
|||
""");
|
||||
```
|
||||
|
||||
### param: LocatorAssertions.toMatchAriaSnapshot.expected
|
||||
### param: LocatorAssertions.toMatchAriaSnapshot#2.expected
|
||||
* since: v1.49
|
||||
- `expected` <string>
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot.timeout = %%-js-assertions-timeout-%%
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#2.timeout = %%-js-assertions-timeout-%%
|
||||
* since: v1.49
|
||||
|
||||
### option: LocatorAssertions.toMatchAriaSnapshot#2.timeout = %%-csharp-java-python-assertions-timeout-%%
|
||||
* since: v1.49
|
||||
|
|
|
|||
|
|
@ -2762,10 +2762,6 @@ This method requires Playwright to be started in a headed mode, with a falsy [`o
|
|||
|
||||
Returns the PDF buffer.
|
||||
|
||||
:::note
|
||||
Generating a pdf is currently only supported in Chromium headless.
|
||||
:::
|
||||
|
||||
`page.pdf()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call
|
||||
[`method: Page.emulateMedia`] before calling `page.pdf()`:
|
||||
|
||||
|
|
@ -3974,7 +3970,7 @@ This setting will change the default maximum time for all the methods accepting
|
|||
* since: v1.8
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds
|
||||
Maximum time in milliseconds. Pass `0` to disable timeout.
|
||||
|
||||
## async method: Page.setExtraHTTPHeaders
|
||||
* since: v1.8
|
||||
|
|
|
|||
|
|
@ -281,6 +281,80 @@ given name prefix inside the [`option: BrowserType.launch.tracesDir`] directory
|
|||
To specify the final trace zip file name, you need to pass `path` option to
|
||||
[`method: Tracing.stopChunk`] instead.
|
||||
|
||||
## async method: Tracing.group
|
||||
* since: v1.49
|
||||
|
||||
:::caution
|
||||
Use `test.step` instead when available.
|
||||
:::
|
||||
|
||||
Creates a new group within the trace, assigning any subsequent API calls to this group, until [`method: Tracing.groupEnd`] is called. Groups can be nested and will be visible in the trace viewer.
|
||||
|
||||
**Usage**
|
||||
|
||||
```js
|
||||
// use test.step instead
|
||||
await test.step('Log in', async () => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
```java
|
||||
// All actions between group and groupEnd
|
||||
// will be shown in the trace viewer as a group.
|
||||
page.context().tracing().group("Open Playwright.dev > API");
|
||||
page.navigate("https://playwright.dev/");
|
||||
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
|
||||
page.context().tracing().groupEnd();
|
||||
```
|
||||
|
||||
```python sync
|
||||
# All actions between group and group_end
|
||||
# will be shown in the trace viewer as a group.
|
||||
page.context.tracing.group("Open Playwright.dev > API")
|
||||
page.goto("https://playwright.dev/")
|
||||
page.get_by_role("link", name="API").click()
|
||||
page.context.tracing.group_end()
|
||||
```
|
||||
|
||||
```python async
|
||||
# All actions between group and group_end
|
||||
# will be shown in the trace viewer as a group.
|
||||
await page.context.tracing.group("Open Playwright.dev > API")
|
||||
await page.goto("https://playwright.dev/")
|
||||
await page.get_by_role("link", name="API").click()
|
||||
await page.context.tracing.group_end()
|
||||
```
|
||||
|
||||
```csharp
|
||||
// All actions between GroupAsync and GroupEndAsync
|
||||
// will be shown in the trace viewer as a group.
|
||||
await Page.Context.Tracing.GroupAsync("Open Playwright.dev > API");
|
||||
await Page.GotoAsync("https://playwright.dev/");
|
||||
await Page.GetByRole(AriaRole.Link, new() { Name = "API" }).ClickAsync();
|
||||
await Page.Context.Tracing.GroupEndAsync();
|
||||
```
|
||||
|
||||
### param: Tracing.group.name
|
||||
* since: v1.49
|
||||
- `name` <[string]>
|
||||
|
||||
Group name shown in the trace viewer.
|
||||
|
||||
### option: Tracing.group.location
|
||||
* since: v1.49
|
||||
- `location` ?<[Object]>
|
||||
- `file` <[string]>
|
||||
- `line` ?<[int]>
|
||||
- `column` ?<[int]>
|
||||
|
||||
Specifies a custom location for the group to be shown in the trace viewer. Defaults to the location of the [`method: Tracing.group`] call.
|
||||
|
||||
## async method: Tracing.groupEnd
|
||||
* since: v1.49
|
||||
|
||||
Closes the last group created by [`method: Tracing.group`].
|
||||
|
||||
## async method: Tracing.stop
|
||||
* since: v1.12
|
||||
|
||||
|
|
|
|||
|
|
@ -1001,7 +1001,11 @@ Additional arguments to pass to the browser instance. The list of Chromium flags
|
|||
## browser-option-channel
|
||||
- `channel` <[string]>
|
||||
|
||||
Browser distribution channel. Supported values are "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", "msedge-canary". Read more about using [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).
|
||||
Browser distribution channel.
|
||||
|
||||
Use "chromium" to [opt in to new headless mode](../browsers.md#opt-in-to-new-headless-mode).
|
||||
|
||||
Use "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", or "msedge-canary" to use branded [Google Chrome and Microsoft Edge](../browsers.md#google-chrome--microsoft-edge).
|
||||
|
||||
## browser-option-chromiumsandbox
|
||||
- `chromiumSandbox` <[boolean]>
|
||||
|
|
|
|||
504
docs/src/aria-snapshots.md
Normal file
504
docs/src/aria-snapshots.md
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
---
|
||||
id: aria-snapshots
|
||||
title: "Snapshot testing"
|
||||
---
|
||||
import LiteYouTube from '@site/src/components/LiteYouTube';
|
||||
|
||||
## Overview
|
||||
|
||||
With the Playwright Snapshot testing you can assert the accessibility tree of a page against a predefined snapshot template.
|
||||
|
||||
```js
|
||||
await page.goto('https://playwright.dev/');
|
||||
await expect(page.getByRole('banner')).toMatchAriaSnapshot(`
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
`);
|
||||
```
|
||||
|
||||
```python sync
|
||||
page.goto('https://playwright.dev/')
|
||||
expect(page.query_selector('banner')).to_match_aria_snapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""")
|
||||
```
|
||||
|
||||
```python async
|
||||
await page.goto('https://playwright.dev/')
|
||||
await expect(page.query_selector('banner')).to_match_aria_snapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""")
|
||||
```
|
||||
|
||||
```java
|
||||
page.navigate("https://playwright.dev/");
|
||||
assertThat(page.locator("banner")).matchesAriaSnapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable end-to-end/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
""");
|
||||
```
|
||||
|
||||
```csharp
|
||||
await page.GotoAsync("https://playwright.dev/");
|
||||
await Expect(page.Locator("banner")).ToMatchAriaSnapshotAsync(@"
|
||||
- banner:
|
||||
- heading ""Playwright enables reliable end-to-end testing for modern web apps."" [level=1]
|
||||
- link ""Get started""
|
||||
- link ""Star microsoft/playwright on GitHub""
|
||||
- link /[\\d]+k\\+ stargazers on GitHub/
|
||||
");
|
||||
```
|
||||
|
||||
<LiteYouTube
|
||||
id="P4R6hnsE0UY"
|
||||
title="Getting started with ARIA Snapshots"
|
||||
/>
|
||||
|
||||
## Assertion testing vs Snapshot testing
|
||||
|
||||
Snapshot testing and assertion testing serve different purposes in test automation:
|
||||
|
||||
### Assertion testing
|
||||
Assertion testing is a targeted approach where you assert specific values or conditions about elements or components. For instance, with Playwright, [`method: LocatorAssertions.toHaveText`]
|
||||
verifies that an element contains the expected text, and [`method: LocatorAssertions.toHaveValue`]
|
||||
confirms that an input field has the expected value.
|
||||
Assertion tests are specific and generally check the current state of an element or property
|
||||
against an expected, predefined state.
|
||||
They work well for predictable, single-value checks but are limited in scope when testing the
|
||||
broader structure or variations.
|
||||
|
||||
**Advantages**
|
||||
- **Clarity**: The intent of the test is explicit and easy to understand.
|
||||
- **Specificity**: Tests focus on particular aspects of functionality, making them more robust
|
||||
against unrelated changes.
|
||||
- **Debugging**: Failures provide targeted feedback, pointing directly to the problematic aspect.
|
||||
|
||||
**Disadvantages**
|
||||
- **Verbose for complex outputs**: Writing assertions for complex data structures or large outputs
|
||||
can be cumbersome and error-prone.
|
||||
- **Maintenance overhead**: As code evolves, manually updating assertions can be time-consuming.
|
||||
|
||||
### Snapshot testing
|
||||
Snapshot testing captures a “snapshot” or representation of the entire
|
||||
state of an element, component, or data at a given moment, which is then saved for future
|
||||
comparisons. When re-running tests, the current state is compared to the snapshot, and if there
|
||||
are differences, the test fails. This approach is especially useful for complex or dynamic
|
||||
structures, where manually asserting each detail would be too time-consuming. Snapshot testing
|
||||
is broader and more holistic than assertion testing, allowing you to track more complex changes over time.
|
||||
|
||||
**Advantages**
|
||||
- **Simplifies complex outputs**: For example, testing a UI component's rendered output can be tedious with traditional assertions. Snapshots capture the entire output for easy comparison.
|
||||
- **Quick Feedback loop**: Developers can easily spot unintended changes in the output.
|
||||
- **Encourages consistency**: Helps maintain consistent output as code evolves.
|
||||
|
||||
**Disadvantages**
|
||||
- **Over-Reliance**: It can be tempting to accept changes to snapshots without fully understanding
|
||||
them, potentially hiding bugs.
|
||||
- **Granularity**: Large snapshots may be hard to interpret when differences arise, especially
|
||||
if minor changes affect large portions of the output.
|
||||
- **Suitability**: Not ideal for highly dynamic content where outputs change frequently or
|
||||
unpredictably.
|
||||
|
||||
### When to use
|
||||
|
||||
- **Snapshot testing** is ideal for:
|
||||
- UI testing of whole pages and components.
|
||||
- Broad structural checks for complex UI components.
|
||||
- Regression testing for outputs that rarely change structure.
|
||||
|
||||
- **Assertion testing** is ideal for:
|
||||
- Core logic validation.
|
||||
- Computed value testing.
|
||||
- Fine-grained tests requiring precise conditions.
|
||||
|
||||
By combining snapshot testing for broad, structural checks and assertion testing for specific functionality, you can achieve a well-rounded testing strategy.
|
||||
|
||||
## Aria snapshots
|
||||
|
||||
In Playwright, aria snapshots provide a YAML representation of the accessibility tree of a page.
|
||||
These snapshots can be stored and compared later to verify if the page structure remains consistent or meets defined
|
||||
expectations.
|
||||
|
||||
The YAML format describes the hierarchical structure of accessible elements on the page, detailing **roles**, **attributes**, **values**, and **text content**.
|
||||
The structure follows a tree-like syntax, where each node represents an accessible element, and indentation indicates
|
||||
nested elements.
|
||||
|
||||
Each accessible element in the tree is represented as a YAML node:
|
||||
|
||||
```yaml
|
||||
- role "name" [attribute=value]
|
||||
```
|
||||
|
||||
- **role**: Specifies the ARIA or HTML role of the element (e.g., `heading`, `list`, `listitem`, `button`).
|
||||
- **"name"**: Accessible name of the element. Quoted strings indicate exact values, `/patterns/` are used for regular expression.
|
||||
- **[attribute=value]**: Attributes and values, in square brackets, represent specific ARIA attributes, such
|
||||
as `checked`, `disabled`, `expanded`, `level`, `pressed`, or `selected`.
|
||||
|
||||
These values are derived from ARIA attributes or calculated based on HTML semantics. To inspect the accessibility tree
|
||||
structure of a page, use the [Chrome DevTools Accessibility Pane](https://developer.chrome.com/docs/devtools/accessibility/reference#pane).
|
||||
|
||||
|
||||
## Snapshot matching
|
||||
|
||||
The [`method: LocatorAssertions.toMatchAriaSnapshot#2`] assertion method in Playwright compares the accessible
|
||||
structure of the locator scope with a predefined aria snapshot template, helping validate the page's state against
|
||||
testing requirements.
|
||||
|
||||
For the following DOM:
|
||||
|
||||
```html
|
||||
<h1>title</h1>
|
||||
```
|
||||
|
||||
You can match it using the following snapshot template:
|
||||
|
||||
```js
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||
- heading "title"
|
||||
`);
|
||||
```
|
||||
|
||||
```python sync
|
||||
expect(page.locator("body")).to_match_aria_snapshot("""
|
||||
- heading "title"
|
||||
""")
|
||||
```
|
||||
|
||||
```python async
|
||||
await expect(page.locator("body")).to_match_aria_snapshot("""
|
||||
- heading "title"
|
||||
""")
|
||||
```
|
||||
|
||||
```java
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("""
|
||||
- heading "title"
|
||||
""");
|
||||
```
|
||||
|
||||
```csharp
|
||||
await Expect(page.Locator("body")).ToMatchAriaSnapshotAsync(@"
|
||||
- heading ""title""
|
||||
");
|
||||
```
|
||||
|
||||
When matching, the snapshot template is compared to the current accessibility tree of the page:
|
||||
|
||||
* If the tree structure matches the template, the test passes; otherwise, it fails, indicating a mismatch between
|
||||
expected and actual accessibility states.
|
||||
* The comparison is case-sensitive and collapses whitespace, so indentation and line breaks are ignored.
|
||||
* The comparison is order-sensitive, meaning the order of elements in the snapshot template must match the order in the
|
||||
page's accessibility tree.
|
||||
|
||||
|
||||
### Partial matching
|
||||
|
||||
You can perform partial matches on nodes by omitting attributes or accessible names, enabling verification of specific
|
||||
parts of the accessibility tree without requiring exact matches. This flexibility is helpful for dynamic or irrelevant
|
||||
attributes.
|
||||
|
||||
```html
|
||||
<button>Submit</button>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- button
|
||||
```
|
||||
|
||||
In this example, the button role is matched, but the accessible name ("Submit") is not specified, allowing the test to
|
||||
pass regardless of the button’s label.
|
||||
|
||||
<hr/>
|
||||
|
||||
For elements with ARIA attributes like `checked` or `disabled`, omitting these attributes allows partial matching,
|
||||
focusing solely on role and hierarchy.
|
||||
|
||||
```html
|
||||
<input type="checkbox" checked>
|
||||
```
|
||||
|
||||
*aria snapshot for partial match*
|
||||
|
||||
```yaml
|
||||
- checkbox
|
||||
```
|
||||
|
||||
In this partial match, the `checked` attribute is ignored, so the test will pass regardless of the checkbox state.
|
||||
|
||||
<hr/>
|
||||
|
||||
Similarly, you can partially match children in lists or groups by omitting specific list items or nested elements.
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>Feature A</li>
|
||||
<li>Feature B</li>
|
||||
<li>Feature C</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
*aria snapshot for partial match*
|
||||
|
||||
```yaml
|
||||
- list
|
||||
- listitem: Feature B
|
||||
```
|
||||
|
||||
Partial matches let you create flexible snapshot tests that verify essential page structure without enforcing
|
||||
specific content or attributes.
|
||||
|
||||
### Matching with regular expressions
|
||||
|
||||
Regular expressions allow flexible matching for elements with dynamic or variable text. Accessible names and text can
|
||||
support regex patterns.
|
||||
|
||||
```html
|
||||
<h1>Issues 12</h1>
|
||||
```
|
||||
|
||||
*aria snapshot with regular expression*
|
||||
|
||||
```yaml
|
||||
- heading /Issues \d+/
|
||||
```
|
||||
|
||||
## Generating snapshots
|
||||
|
||||
Creating aria snapshots in Playwright helps ensure and maintain your application’s structure.
|
||||
You can generate snapshots in various ways depending on your testing setup and workflow.
|
||||
|
||||
### 1. Generating snapshots with the Playwright code generator
|
||||
|
||||
If you’re using Playwright’s [Code Generator](./codegen.md), generating aria snapshots is streamlined with its
|
||||
interactive interface:
|
||||
|
||||
- **"Assert snapshot" Action**: In the code generator, you can use the "Assert snapshot" action to automatically create
|
||||
a snapshot assertion for the selected elements. This is a quick way to capture the aria snapshot as part of your
|
||||
recorded test flow.
|
||||
|
||||
- **"Aria snapshot" Tab**: The "Aria snapshot" tab within the code generator interface visually represents the
|
||||
aria snapshot for a selected locator, letting you explore, inspect, and verify element roles, attributes, and
|
||||
accessible names to aid snapshot creation and review.
|
||||
|
||||
### 2. Updating snapshots with `@playwright/test` and the `--update-snapshots` flag
|
||||
|
||||
When using the Playwright test runner (`@playwright/test`), you can automatically update snapshots by running tests with
|
||||
the `--update-snapshots` flag:
|
||||
|
||||
```bash
|
||||
npx playwright test --update-snapshots
|
||||
```
|
||||
|
||||
This command regenerates snapshots for assertions, including aria snapshots, replacing outdated ones. It’s
|
||||
useful when application structure changes require new snapshots as a baseline. Note that Playwright will wait for the
|
||||
maximum expect timeout specified in the test runner configuration to ensure the
|
||||
page is settled before taking the snapshot. It might be necessary to adjust the `--timeout` if the test hits the timeout
|
||||
while generating snapshots.
|
||||
|
||||
#### Empty template for snapshot generation
|
||||
|
||||
Passing an empty string as the template in an assertion generates a snapshot on-the-fly:
|
||||
|
||||
```js
|
||||
await expect(locator).toMatchAriaSnapshot('');
|
||||
```
|
||||
|
||||
Note that Playwright will wait for the maximum expect timeout specified in the test runner configuration to ensure the
|
||||
page is settled before taking the snapshot. It might be necessary to adjust the `--timeout` if the test hits the timeout
|
||||
while generating snapshots.
|
||||
|
||||
#### Snapshot patch files
|
||||
|
||||
When updating snapshots, Playwright creates patch files that capture differences. These patch files can be reviewed,
|
||||
applied, and committed to source control, allowing teams to track structural changes over time and ensure updates are
|
||||
consistent with application requirements.
|
||||
|
||||
### 3. Using the `Locator.ariaSnapshot` method
|
||||
|
||||
The [`method: Locator.ariaSnapshot`] method allows you to programmatically create a YAML representation of accessible
|
||||
elements within a locator’s scope, especially helpful for generating snapshots dynamically during test execution.
|
||||
|
||||
**Example**:
|
||||
|
||||
```js
|
||||
const snapshot = await page.locator('body').ariaSnapshot();
|
||||
console.log(snapshot);
|
||||
```
|
||||
|
||||
```python sync
|
||||
snapshot = page.locator("body").aria_snapshot()
|
||||
print(snapshot)
|
||||
```
|
||||
|
||||
```python async
|
||||
snapshot = await page.locator("body").aria_snapshot()
|
||||
print(snapshot)
|
||||
```
|
||||
|
||||
```java
|
||||
String snapshot = page.locator("body").ariaSnapshot();
|
||||
System.out.println(snapshot);
|
||||
```
|
||||
|
||||
```csharp
|
||||
var snapshot = await page.Locator("body").AriaSnapshotAsync();
|
||||
Console.WriteLine(snapshot);
|
||||
```
|
||||
|
||||
This command outputs the aria snapshot within the specified locator’s scope in YAML format, which you can validate
|
||||
or store as needed.
|
||||
|
||||
## Accessibility tree examples
|
||||
|
||||
### Headings with level attributes
|
||||
|
||||
Headings can include a `level` attribute indicating their heading level.
|
||||
|
||||
```html
|
||||
<h1>Title</h1>
|
||||
<h2>Subtitle</h2>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- heading "Title" [level=1]
|
||||
- heading "Subtitle" [level=2]
|
||||
```
|
||||
|
||||
### Text nodes
|
||||
|
||||
Standalone or descriptive text elements appear as text nodes.
|
||||
|
||||
```html
|
||||
<div>Sample accessible name</div>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- text: Sample accessible name
|
||||
```
|
||||
|
||||
### Inline multiline text
|
||||
|
||||
Multiline text, such as paragraphs, is normalized in the aria snapshot.
|
||||
|
||||
```html
|
||||
<p>Line 1<br>Line 2</p>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- paragraph: Line 1 Line 2
|
||||
```
|
||||
|
||||
### Links
|
||||
|
||||
Links display their text or composed content from pseudo-elements.
|
||||
|
||||
```html
|
||||
<a href="#more-info">Read more about Accessibility</a>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- link "Read more about Accessibility"
|
||||
```
|
||||
|
||||
### Text boxes
|
||||
|
||||
Input elements of type `text` show their `value` attribute content.
|
||||
|
||||
```html
|
||||
<input type="text" value="Enter your name">
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- textbox: Enter your name
|
||||
```
|
||||
|
||||
### Lists with items
|
||||
|
||||
Ordered and unordered lists include their list items.
|
||||
|
||||
```html
|
||||
<ul aria-label="Main Features">
|
||||
<li>Feature 1</li>
|
||||
<li>Feature 2</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- list "Main Features":
|
||||
- listitem: Feature 1
|
||||
- listitem: Feature 2
|
||||
```
|
||||
|
||||
### Grouped elements
|
||||
|
||||
Groups capture nested elements, such as `<details>` elements with summary content.
|
||||
|
||||
```html
|
||||
<details>
|
||||
<summary>Summary</summary>
|
||||
<p>Detail content here</p>
|
||||
</details>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- group: Summary
|
||||
```
|
||||
|
||||
### Attributes and states
|
||||
|
||||
Commonly used ARIA attributes, like `checked`, `disabled`, `expanded`, `level`, `pressed`, and `selected`, represent
|
||||
control states.
|
||||
|
||||
#### Checkbox with `checked` attribute
|
||||
|
||||
```html
|
||||
<input type="checkbox" checked>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- checkbox [checked]
|
||||
```
|
||||
|
||||
#### Button with `pressed` attribute
|
||||
|
||||
```html
|
||||
<button aria-pressed="true">Toggle</button>
|
||||
```
|
||||
|
||||
*aria snapshot*
|
||||
|
||||
```yaml
|
||||
- button "Toggle" [pressed=true]
|
||||
```
|
||||
|
|
@ -475,6 +475,21 @@ Setup CI/CD and run your tests frequently. The more often you run your tests the
|
|||
|
||||
Use Linux when running your tests on CI as it is cheaper. Developers can use whatever environment when running locally but use linux on CI. Consider setting up [Sharding](./test-sharding.md) to make CI faster.
|
||||
|
||||
|
||||
#### Optimize browser downloads on CI
|
||||
|
||||
Only install the browsers that you actually need, especially on CI. For example, if you're only testing with Chromium, install just Chromium.
|
||||
|
||||
```bash title=".github/workflows/playwright.yml"
|
||||
# Instead of installing all browsers
|
||||
npx playwright install --with-deps
|
||||
|
||||
# Install only Chromium
|
||||
npx playwright install chromium --with-deps
|
||||
```
|
||||
|
||||
This saves both download time and disk space on your CI machines.
|
||||
|
||||
### Lint your tests
|
||||
|
||||
We recommend TypeScript and linting with ESLint for your tests to catch errors early. Use [`@typescript-eslint/no-floating-promises`](https://typescript-eslint.io/rules/no-floating-promises/) [ESLint](https://eslint.org) rule to make sure there are no missing awaits before the asynchronous calls to the Playwright API. On your CI you can run `tsc --noEmit` to ensure that functions are called with the right signature.
|
||||
|
|
|
|||
|
|
@ -338,16 +338,101 @@ dotnet test --settings:webkit.runsettings
|
|||
|
||||
For Google Chrome, Microsoft Edge and other Chromium-based browsers, by default, Playwright uses open source Chromium builds. Since the Chromium project is ahead of the branded browsers, when the world is on Google Chrome N, Playwright already supports Chromium N+1 that will be released in Google Chrome and Microsoft Edge a few weeks later.
|
||||
|
||||
Playwright ships a regular Chromium build for headed operations and a separate [chromium headless shell](https://developer.chrome.com/blog/chrome-headless-shell) for headless mode. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for details.
|
||||
|
||||
#### Optimize download size on CI
|
||||
|
||||
If you are only running tests in headless shell (i.e. the `channel` option is not specified), for example on CI, you can avoid downloading the full Chromium browser by passing `--only-shell` during installation.
|
||||
|
||||
```bash js
|
||||
# only running tests headlessly
|
||||
npx playwright install --with-deps --only-shell
|
||||
```
|
||||
|
||||
```bash java
|
||||
# only running tests headlessly
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps --only-shell"
|
||||
```
|
||||
|
||||
```bash python
|
||||
# only running tests headlessly
|
||||
playwright install --with-deps --only-shell
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
# only running tests headlessly
|
||||
pwsh bin/Debug/netX/playwright.ps1 install --with-deps --only-shell
|
||||
```
|
||||
|
||||
#### Opt-in to new headless mode
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
> New Headless on the other hand is the real Chrome browser, and is thus more authentic, reliable, and offers more features. This makes it more suitable for high-accuracy end-to-end web app testing or browser extension testing.
|
||||
|
||||
See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for details.
|
||||
|
||||
```js
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'], channel: 'chromium' },
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
```java
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class Example {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setChannel("chromium"));
|
||||
Page page = browser.newPage();
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash python
|
||||
pytest test_login.py --browser-channel chromium
|
||||
```
|
||||
|
||||
```xml csharp
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<LaunchOptions>
|
||||
<Channel>chromium</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Channel=chromium
|
||||
```
|
||||
|
||||
### Google Chrome & Microsoft Edge
|
||||
|
||||
While Playwright can download and use the recent Chromium build, it can operate against the branded Google Chrome and Microsoft Edge browsers available on the machine (note that Playwright doesn't install them by default). In particular, the current Playwright version will support Stable and Beta channels of these browsers.
|
||||
|
||||
Available channels are `chrome`, `msedge`, `chrome-beta`, `msedge-beta` or `msedge-dev`.
|
||||
Available channels are `chrome`, `msedge`, `chrome-beta`, `msedge-beta`, `chrome-dev`, `msedge-dev`, `chrome-canary`, `msedge-canary`.
|
||||
|
||||
:::warning
|
||||
Certain Enterprise Browser Policies may impact Playwright's ability to launch and control Google Chrome and Microsoft Edge. Running in an environment with browser policies is outside of the Playwright project's scope.
|
||||
:::
|
||||
|
||||
:::warning
|
||||
Google Chrome and Microsoft Edge have switched to a [new headless mode](https://developer.chrome.com/docs/chromium/headless) implementation that is closer to a regular headed mode. This differs from [chromium headless shell](https://developer.chrome.com/blog/chrome-headless-shell) that is used in Playwright by default when running headless, so expect different behavior in some cases. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) fore details.
|
||||
:::
|
||||
|
||||
```js
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
|
|
@ -401,6 +486,23 @@ pytest test_login.py --browser-channel msedge
|
|||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Channel=msedge
|
||||
```
|
||||
|
||||
######
|
||||
* langs: python
|
||||
|
||||
Alternatively when using the library directly, you can specify the browser [`option: BrowserType.launch.channel`] when launching the browser:
|
||||
|
||||
```python
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
with sync_playwright() as p:
|
||||
# Channel can be "chrome", "msedge", "chrome-beta", "msedge-beta" or "msedge-dev".
|
||||
browser = p.chromium.launch(channel="msedge")
|
||||
page = browser.new_page()
|
||||
page.goto("http://playwright.dev")
|
||||
print(page.title())
|
||||
browser.close()
|
||||
```
|
||||
|
||||
#### Installing Google Chrome & Microsoft Edge
|
||||
|
||||
If Google Chrome or Microsoft Edge is not available on your machine, you can install
|
||||
|
|
|
|||
|
|
@ -214,20 +214,15 @@ def test_popup_page(page: Page, extension_id: str) -> None:
|
|||
|
||||
## Headless mode
|
||||
|
||||
:::danger
|
||||
`headless=new` mode is not officially supported by Playwright and might result in unexpected behavior.
|
||||
:::
|
||||
|
||||
By default, Chrome's headless mode in Playwright does not support Chrome extensions. To overcome this limitation, you can run Chrome's persistent context with a new headless mode by using the following code:
|
||||
By default, Chrome's headless mode in Playwright does not support Chrome extensions. To overcome this limitation, you can run Chrome's persistent context with a new headless mode by using [channel `chromium`](./browsers.md#opt-in-to-new-headless-mode):
|
||||
|
||||
```js title="fixtures.ts"
|
||||
// ...
|
||||
|
||||
const pathToExtension = path.join(__dirname, 'my-extension');
|
||||
const context = await chromium.launchPersistentContext('', {
|
||||
headless: false,
|
||||
channel: 'chromium',
|
||||
args: [
|
||||
`--headless=new`,
|
||||
`--disable-extensions-except=${pathToExtension}`,
|
||||
`--load-extension=${pathToExtension}`,
|
||||
],
|
||||
|
|
@ -239,9 +234,8 @@ const context = await chromium.launchPersistentContext('', {
|
|||
path_to_extension = Path(__file__).parent.joinpath("my-extension")
|
||||
context = playwright.chromium.launch_persistent_context(
|
||||
"",
|
||||
headless=False,
|
||||
channel="chromium",
|
||||
args=[
|
||||
"--headless=new",
|
||||
f"--disable-extensions-except={path_to_extension}",
|
||||
f"--load-extension={path_to_extension}",
|
||||
],
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Playwright tests can be run on any CI provider. This guide covers one way of run
|
|||
## Introduction
|
||||
* langs: python, java, csharp
|
||||
|
||||
Playwright tests can be ran on any CI provider. In this section we will cover running tests on GitHub using GitHub actions. If you would like to see how to configure other CI providers check out our detailed doc on Continuous Integration.
|
||||
Playwright tests can be run on any CI provider. In this section we will cover running tests on GitHub using GitHub actions. If you would like to see how to configure other CI providers check out our detailed doc on Continuous Integration.
|
||||
|
||||
#### You will learn
|
||||
* langs: python, java, csharp
|
||||
|
|
|
|||
|
|
@ -454,11 +454,11 @@ jobs:
|
|||
|
||||
### Docker
|
||||
|
||||
We have a [pre-built Docker image](./docker.md) which can either be used directly, or as a reference to update your existing Docker definitions.
|
||||
We have a [pre-built Docker image](./docker.md) which can either be used directly or as a reference to update your existing Docker definitions.
|
||||
|
||||
Suggested configuration
|
||||
1. Using `--ipc=host` is also recommended when using Chromium. Without it Chromium can run out of memory
|
||||
and crash. Learn more about this option in [Docker docs](https://docs.docker.com/engine/reference/run/#ipc-settings---ipc).
|
||||
and crash. Learn more about this option in [Docker docs](https://docs.docker.com/reference/cli/docker/container/run/#ipc).
|
||||
1. Seeing other weird errors when launching Chromium? Try running your container
|
||||
with `docker run --cap-add=SYS_ADMIN` when developing locally.
|
||||
1. Using `--init` Docker flag or [dumb-init](https://github.com/Yelp/dumb-init) is recommended to avoid special
|
||||
|
|
@ -466,7 +466,7 @@ Suggested configuration
|
|||
|
||||
### Azure Pipelines
|
||||
|
||||
For Windows or macOS agents, no additional configuration required, just install Playwright and run your tests.
|
||||
For Windows or macOS agents, no additional configuration is required, just install Playwright and run your tests.
|
||||
|
||||
For Linux agents, you can use [our Docker container](./docker.md) with Azure
|
||||
Pipelines support [running containerized
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ The recommended approach is to use `setFixedTime` to set the time to a specific
|
|||
- `Event.timeStamp`
|
||||
:::
|
||||
|
||||
:::warning
|
||||
If you call `install` at any point in your test, the call _MUST_ occur before any other clock related calls (see note above for list). Calling these methods out of order will result in undefined behavior. For example, you cannot call `setInterval`, followed by `install`, then `clearInterval`, as `install` overrides the native definition of the clock functions.
|
||||
:::
|
||||
|
||||
## Test with predefined time
|
||||
|
||||
Often you only need to fake `Date.now` while keeping the timers going.
|
||||
|
|
@ -164,11 +168,11 @@ await Page.GotoAsync("http://localhost:3333");
|
|||
await Page.Clock.PauseAtAsync(new DateTime(2024, 2, 2, 10, 0, 0));
|
||||
|
||||
// Assert the page state.
|
||||
await Expect(Page.GetByTestId("current-time")).ToHaveText("2/2/2024, 10:00:00 AM");
|
||||
await Expect(Page.GetByTestId("current-time")).ToHaveTextAsync("2/2/2024, 10:00:00 AM");
|
||||
|
||||
// Close the laptop lid again and open it at 10:30am.
|
||||
await Page.Clock.FastForwardAsync("30:00");
|
||||
await Expect(Page.GetByTestId("current-time")).ToHaveText("2/2/2024, 10:30:00 AM");
|
||||
await Expect(Page.GetByTestId("current-time")).ToHaveTextAsync("2/2/2024, 10:30:00 AM");
|
||||
```
|
||||
|
||||
## Test inactivity monitoring
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Install the VS Code extension and generate tests directly from VS Code. The exte
|
|||
|
||||
### Record a New Test
|
||||
|
||||
To record a test click on the **Record new** button from the Testing sidebar. This will create a `test-1.spec.ts` file as well as open up a browser window.
|
||||
To record a test click on the **Record new** button from the Testing sidebar. This will create a `test-1.spec.ts` file as well as open up a browser window.
|
||||
|
||||
<img width="1385" alt="record new in vs code" src="https://user-images.githubusercontent.com/13063165/220961665-615d0ab8-3f0b-439c-ad0b-0424d9aa154b.png" />
|
||||
|
||||
|
|
@ -58,16 +58,16 @@ In the test file in VS Code you will see your new generated actions added to you
|
|||
|
||||
### Generating locators
|
||||
|
||||
You can generate locators with the test generator.
|
||||
- Click on the **Pick locator** button form the testing sidebar and then hover over elements in the browser window to see the [locator](./locators.md) highlighted underneath each element.
|
||||
- Click the element you require and it will now show up in the **Pick locator** box in VS Code.
|
||||
You can generate locators with the test generator.
|
||||
- Click on the **Pick locator** button form the testing sidebar and then hover over elements in the browser window to see the [locator](./locators.md) highlighted underneath each element.
|
||||
- Click the element you require and it will now show up in the **Pick locator** box in VS Code.
|
||||
- Press <kbd>Enter</kbd> on your keyboard to copy the locator into the clipboard and then paste anywhere in your code. Or press 'escape' if you want to cancel.
|
||||
|
||||
<img width="1641" alt="Pick locators in VS code" src="https://user-images.githubusercontent.com/13063165/220958368-95b03620-3c9b-40a8-be74-01c96ba03cad.png" />
|
||||
|
||||
## Generate tests with the Playwright Inspector
|
||||
|
||||
When running the `codegen` command two windows will be opened, a browser window where you interact with the website you wish to test and the Playwright Inspector window where you can record your tests and then copy them into your editor.
|
||||
When running the `codegen` command two windows will be opened, a browser window where you interact with the website you wish to test and the Playwright Inspector window where you can record your tests and then copy them into your editor.
|
||||
|
||||
### Running Codegen
|
||||
|
||||
|
|
@ -128,10 +128,10 @@ When you have finished interacting with the page, press the **record** button to
|
|||
Use the **clear** button to clear the code to start recording again. Once finished, close the Playwright inspector window or stop the terminal command.
|
||||
|
||||
### Generating locators
|
||||
You can generate [locators](/locators.md) with the test generator.
|
||||
You can generate [locators](/locators.md) with the test generator.
|
||||
|
||||
* Press the `'Record'` button to stop the recording and the `'Pick Locator'` button will appear.
|
||||
* Click on the `'Pick Locator'` button and then hover over elements in the browser window to see the locator highlighted underneath each element.
|
||||
* Click on the `'Pick Locator'` button and then hover over elements in the browser window to see the locator highlighted underneath each element.
|
||||
* To choose a locator, click on the element you would like to locate and the code for that locator will appear in the field next to the Pick Locator button.
|
||||
* You can then edit the locator in this field to fine tune it or use the copy button to copy it and paste it into your code.
|
||||
|
||||
|
|
@ -164,19 +164,19 @@ You can use the test generator to generate tests using emulation so as to genera
|
|||
Playwright opens a browser window with its viewport set to a specific width and height and is not responsive as tests need to be run under the same conditions. Use the `--viewport` option to generate tests with a different viewport size.
|
||||
|
||||
```bash js
|
||||
npx playwright codegen --viewport-size=800,600 playwright.dev
|
||||
npx playwright codegen --viewport-size="800,600" playwright.dev
|
||||
```
|
||||
|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="codegen --viewport-size=800,600 playwright.dev"
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="codegen --viewport-size='800,600' playwright.dev"
|
||||
```
|
||||
|
||||
```bash python
|
||||
playwright codegen --viewport-size=800,600 playwright.dev
|
||||
playwright codegen --viewport-size="800,600" playwright.dev
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 codegen --viewport-size=800,600 playwright.dev
|
||||
pwsh bin/Debug/netX/playwright.ps1 codegen --viewport-size="800,600" playwright.dev
|
||||
```
|
||||
######
|
||||
* langs: js
|
||||
|
|
@ -325,7 +325,7 @@ pwsh bin/Debug/netX/playwright.ps1 codegen --timezone="Europe/Rome" --geolocatio
|
|||
|
||||
### Preserve authenticated state
|
||||
|
||||
Run `codegen` with `--save-storage` to save [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) at the end of the session. This is useful to separately record an authentication step and reuse it later when recording more tests.
|
||||
Run `codegen` with `--save-storage` to save [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) and [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) at the end of the session. This is useful to separately record an authentication step and reuse it later when recording more tests.
|
||||
|
||||
```bash js
|
||||
npx playwright codegen github.com/microsoft/playwright --save-storage=auth.json
|
||||
|
|
@ -367,7 +367,7 @@ pwsh bin/Debug/netX/playwright.ps1 codegen github.com/microsoft/playwright --sav
|
|||
|
||||
#### Login
|
||||
|
||||
After performing authentication and closing the browser, `auth.json` will contain the storage state which you can then reuse in your tests.
|
||||
After performing authentication and closing the browser, `auth.json` will contain the storage state which you can then reuse in your tests.
|
||||
|
||||
<img width="1394" alt="login to GitHub screen" src="https://user-images.githubusercontent.com/13063165/220561688-04b2b984-4ba6-4446-8b0a-8058876e2a02.png" />
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,88 @@ Using `--ipc=host` is recommended when using Chrome ([Docker docs](https://docs.
|
|||
|
||||
See our [Continuous Integration guides](./ci.md) for sample configs.
|
||||
|
||||
### Remote Connection
|
||||
|
||||
You can run Playwright Server in Docker while keeping your tests running on the host system or another machine. This is useful for running tests on unsupported Linux distributions or remote execution scenarios.
|
||||
|
||||
#### Running the Playwright Server
|
||||
|
||||
Start the Playwright Server in Docker:
|
||||
|
||||
```bash
|
||||
docker run -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v%%VERSION%%-noble /bin/sh -c "npx -y playwright@%%VERSION%% run-server --port 3000 --host 0.0.0.0"
|
||||
```
|
||||
|
||||
#### Connecting to the Server
|
||||
* langs: js
|
||||
|
||||
There are two ways to connect to the remote Playwright server:
|
||||
|
||||
1. Using environment variable with `@playwright/test`:
|
||||
|
||||
```bash
|
||||
PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:3000/ npx playwright test
|
||||
```
|
||||
|
||||
2. Using the [`method: BrowserType.connect`] API for other applications:
|
||||
|
||||
```js
|
||||
const browser = await playwright['chromium'].connect('ws://127.0.0.1:3000/');
|
||||
```
|
||||
|
||||
#### Connecting to the Server
|
||||
* langs: python, csharp, java
|
||||
|
||||
```python sync
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.connect("ws://127.0.0.1:3000/")
|
||||
```
|
||||
|
||||
```python async
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.connect("ws://127.0.0.1:3000/")
|
||||
```
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright;
|
||||
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.Chromium.ConnectAsync("ws://127.0.0.1:3000/");
|
||||
```
|
||||
|
||||
```java
|
||||
package org.example;
|
||||
|
||||
import com.microsoft.playwright.*;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().connect("ws://127.0.0.1:3000/");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Network Configuration
|
||||
|
||||
If you need to access local servers from within the Docker container:
|
||||
|
||||
```bash
|
||||
docker run --add-host=hostmachine:host-gateway -p 3000:3000 --rm --init -it --workdir /home/pwuser --user pwuser mcr.microsoft.com/playwright:v%%VERSION%%-noble /bin/sh -c "npx -y playwright@%%VERSION%% run-server --port 3000 --host 0.0.0.0"
|
||||
```
|
||||
|
||||
This makes `hostmachine` point to the host's localhost. Your tests should use `hostmachine` instead of `localhost` when accessing local servers.
|
||||
|
||||
:::note
|
||||
When running tests remotely, ensure the Playwright version in your tests matches the version running in the Docker container.
|
||||
:::
|
||||
|
||||
## Image tags
|
||||
|
||||
See [all available image tags].
|
||||
|
|
@ -132,7 +214,7 @@ Browser builds for Firefox and WebKit are built for the [glibc](https://en.wikip
|
|||
You can use the [.NET install script](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script) in order to install different SDK versions:
|
||||
|
||||
```bash
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --install-dir /usr/share/dotnet --channel 6.0
|
||||
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --install-dir /usr/share/dotnet --channel 9.0
|
||||
```
|
||||
|
||||
## Build your own image
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ title: "Installation"
|
|||
|
||||
Playwright was created specifically to accommodate the needs of end-to-end testing. Playwright supports all modern rendering engines including Chromium, WebKit, and Firefox. Test on Windows, Linux, and macOS, locally or on CI, headless or headed with native mobile emulation.
|
||||
|
||||
You can choose to use [MSTest base classes](./test-runners.md#mstest) or [NUnit base classes](./test-runners.md#nunit) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
|
||||
You can choose to use MSTest, NUnit, or xUnit [base classes](./test-runners.md) that Playwright provides to write end-to-end tests. These classes support running tests on multiple browser engines, parallelizing tests, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box. Alternatively you can use the [library](./library.md) to manually write the testing infrastructure.
|
||||
|
||||
1. Start by creating a new project with `dotnet new`. This will create the `PlaywrightTests` directory which includes a `UnitTest1.cs` file:
|
||||
|
||||
|
|
@ -17,6 +17,7 @@ You can choose to use [MSTest base classes](./test-runners.md#mstest) or [NUnit
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -34,6 +35,14 @@ dotnet new mstest -n PlaywrightTests
|
|||
cd PlaywrightTests
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```bash
|
||||
dotnet new xunit -n PlaywrightTests
|
||||
cd PlaywrightTests
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -45,6 +54,7 @@ cd PlaywrightTests
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -60,6 +70,13 @@ dotnet add package Microsoft.Playwright.NUnit
|
|||
dotnet add package Microsoft.Playwright.MSTest
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```bash
|
||||
dotnet add package Microsoft.Playwright.Xunit
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -87,6 +104,7 @@ Edit the `UnitTest1.cs` file with the code below to create an example end-to-end
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -164,6 +182,41 @@ public class ExampleTest : PageTest
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task HasTitle()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
|
||||
// Click the get started link.
|
||||
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
|
||||
|
||||
// Expects page to have a heading with the name of Installation.
|
||||
await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
## Running the Example Tests
|
||||
|
|
@ -190,4 +243,4 @@ See our doc on [Running and Debugging Tests](./running-tests.md) to learn more a
|
|||
- [Generate tests with Codegen](./codegen-intro.md)
|
||||
- [See a trace of your tests](./trace-viewer-intro.md)
|
||||
- [Run tests on CI](./ci-intro.md)
|
||||
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)
|
||||
- [Learn more about the MSTest, NUnit, and xUnit base classes](./test-runners.md)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ The `tests` folder contains a basic example test to help you get started with te
|
|||
|
||||
## Running the Example Test
|
||||
|
||||
By default tests will be run on all 3 browsers, chromium, firefox and webkit using 3 workers. This can be configured in the [playwright.config file](./test-configuration.md). Tests are run in headless mode meaning no browser will open up when running the tests. Results of the tests and test logs will be shown in the terminal.
|
||||
By default tests will be run on all 3 browsers, Chromium, Firefox and WebKit using 3 workers. This can be configured in the [playwright.config file](./test-configuration.md). Tests are run in headless mode meaning no browser will open up when running the tests. Results of the tests and test logs will be shown in the terminal.
|
||||
|
||||
<Tabs
|
||||
defaultValue="npm"
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ You can choose any testing framework such as JUnit or TestNG based on your proje
|
|||
|
||||
## .NET
|
||||
|
||||
Playwright for .NET comes with [MSTest base classes](https://playwright.dev/dotnet/docs/test-runners#mstest) and [NUnit base classes](https://playwright.dev/dotnet/docs/test-runners#nunit) for writing end-to-end tests.
|
||||
Playwright for .NET comes with MSTest, NUnit, and xUnit [base classes](https://playwright.dev/dotnet/docs/test-runners) for writing end-to-end tests.
|
||||
|
||||
* [Documentation](https://playwright.dev/dotnet/docs/intro)
|
||||
* [GitHub repo](https://github.com/microsoft/playwright-dotnet)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ title: "Getting started - Library"
|
|||
|
||||
## Introduction
|
||||
|
||||
Playwright can either be used with the [MSTest](./test-runners.md#mstest) or [NUnit](./test-runners.md#nunit), or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
|
||||
Playwright can either be used with the [MSTest, NUnit, or xUnit base classes](./test-runners.md) or as a Playwright Library (this guide). If you are working on an application that utilizes Playwright capabilities or you are using Playwright with another test runner, read on.
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
|
|||
|
|
@ -115,8 +115,7 @@ await page.GotoAsync("https://example.com");
|
|||
You can configure pages to load over the HTTP(S) proxy or SOCKSv5. Proxy can be either set globally
|
||||
for the entire browser, or for each browser context individually.
|
||||
|
||||
You can optionally specify username and password for HTTP(S) proxy, you can also specify hosts to
|
||||
bypass proxy for.
|
||||
You can optionally specify username and password for HTTP(S) proxy, you can also specify hosts to bypass the [`option: Browser.newContext.proxy`] for.
|
||||
|
||||
Here is an example of a global proxy:
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,91 @@ toc_max_heading_level: 2
|
|||
---
|
||||
|
||||
|
||||
## Version 1.49
|
||||
|
||||
### Aria snapshots
|
||||
|
||||
New assertion [`method: LocatorAssertions.toMatchAriaSnapshot#2`] verifies page structure by comparing to an expected accessibility tree, represented as YAML.
|
||||
|
||||
```csharp
|
||||
await page.GotoAsync("https://playwright.dev");
|
||||
await Expect(page.Locator("body")).ToMatchAriaSnapshotAsync(@"
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link ""Get started""
|
||||
- link ""Star microsoft/playwright on GitHub""
|
||||
- main:
|
||||
- img ""Browsers (Chromium, Firefox, WebKit)""
|
||||
- heading ""Any browser • Any platform • One API""
|
||||
");
|
||||
```
|
||||
|
||||
You can generate this assertion with [Test Generator](./codegen) or by calling [`method: Locator.ariaSnapshot`].
|
||||
|
||||
Learn more in the [aria snapshots guide](./aria-snapshots).
|
||||
|
||||
### Tracing groups
|
||||
|
||||
New method [`method: Tracing.group`] allows you to visually group actions in the trace viewer.
|
||||
|
||||
```csharp
|
||||
// All actions between GroupAsync and GroupEndAsync
|
||||
// will be shown in the trace viewer as a group.
|
||||
await Page.Context.Tracing.GroupAsync("Open Playwright.dev > API");
|
||||
await Page.GotoAsync("https://playwright.dev/");
|
||||
await Page.GetByRole(AriaRole.Link, new() { Name = "API" }).ClickAsync();
|
||||
await Page.Context.Tracing.GroupEndAsync();
|
||||
```
|
||||
|
||||
### Breaking: `chrome` and `msedge` channels switch to new headless mode
|
||||
|
||||
This change affects you if you're using one of the following channels in your `playwright.config.ts`:
|
||||
- `chrome`, `chrome-dev`, `chrome-beta`, or `chrome-canary`
|
||||
- `msedge`, `msedge-dev`, `msedge-beta`, or `msedge-canary`
|
||||
|
||||
After updating to Playwright v1.49, run your test suite. If it still passes, you're good to go. If not, you will probably need to update your snapshots, and adapt some of your test code around PDF viewers and extensions. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for more details.
|
||||
|
||||
### Try new Chromium headless
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
> New Headless on the other hand is the real Chrome browser, and is thus more authentic, reliable, and offers more features. This makes it more suitable for high-accuracy end-to-end web app testing or browser extension testing.
|
||||
|
||||
See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for the list of possible breakages you could encounter and more details on Chromium headless. Please file an issue if you see any problems after opting in.
|
||||
|
||||
```xml csharp title="runsettings.xml"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<LaunchOptions>
|
||||
<Channel>chromium</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
```bash csharp
|
||||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Channel=chromium
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- There will be no more updates for WebKit on Ubuntu 20.04 and Debian 11. We recommend updating your OS to a later version.
|
||||
- `<canvas>` elements inside a snapshot now draw a preview.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
- Chromium 131.0.6778.33
|
||||
- Mozilla Firefox 132.0
|
||||
- WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
- Google Chrome 130
|
||||
- Microsoft Edge 130
|
||||
|
||||
|
||||
## Version 1.48
|
||||
|
||||
### WebSocket routing
|
||||
|
|
@ -788,7 +873,7 @@ All the same methods are also available on [Locator], [FrameLocator] and [Frame]
|
|||
- [`method: LocatorAssertions.toHaveAttribute`] with an empty value does not match missing attribute anymore. For example, the following snippet will succeed when `button` **does not** have a `disabled` attribute.
|
||||
|
||||
```csharp
|
||||
await Expect(Page.GetByRole(AriaRole.Button)).ToHaveAttribute("disabled", "");
|
||||
await Expect(Page.GetByRole(AriaRole.Button)).ToHaveAttributeAsync("disabled", "");
|
||||
```
|
||||
|
||||
### Browser Versions
|
||||
|
|
|
|||
|
|
@ -4,6 +4,79 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.49
|
||||
|
||||
### Aria snapshots
|
||||
|
||||
New assertion [`method: LocatorAssertions.toMatchAriaSnapshot#2`] verifies page structure by comparing to an expected accessibility tree, represented as YAML.
|
||||
|
||||
```java
|
||||
page.navigate("https://playwright.dev");
|
||||
assertThat(page.locator("body")).matchesAriaSnapshot("""
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- main:
|
||||
- img "Browsers (Chromium, Firefox, WebKit)"
|
||||
- heading "Any browser • Any platform • One API"
|
||||
""");
|
||||
```
|
||||
|
||||
You can generate this assertion with [Test Generator](./codegen) or by calling [`method: Locator.ariaSnapshot`].
|
||||
|
||||
Learn more in the [aria snapshots guide](./aria-snapshots).
|
||||
|
||||
### Tracing groups
|
||||
|
||||
New method [`method: Tracing.group`] allows you to visually group actions in the trace viewer.
|
||||
|
||||
```java
|
||||
// All actions between group and groupEnd
|
||||
// will be shown in the trace viewer as a group.
|
||||
page.context().tracing().group("Open Playwright.dev > API");
|
||||
page.navigate("https://playwright.dev/");
|
||||
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("API")).click();
|
||||
page.context().tracing().groupEnd();
|
||||
```
|
||||
|
||||
### Breaking: `chrome` and `msedge` channels switch to new headless mode
|
||||
|
||||
This change affects you if you're using one of the following channels in your `playwright.config.ts`:
|
||||
- `chrome`, `chrome-dev`, `chrome-beta`, or `chrome-canary`
|
||||
- `msedge`, `msedge-dev`, `msedge-beta`, or `msedge-canary`
|
||||
|
||||
After updating to Playwright v1.49, run your test suite. If it still passes, you're good to go. If not, you will probably need to update your snapshots, and adapt some of your test code around PDF viewers and extensions. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for more details.
|
||||
|
||||
### Try new Chromium headless
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
> New Headless on the other hand is the real Chrome browser, and is thus more authentic, reliable, and offers more features. This makes it more suitable for high-accuracy end-to-end web app testing or browser extension testing.
|
||||
|
||||
See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for the list of possible breakages you could encounter and more details on Chromium headless. Please file an issue if you see any problems after opting in.
|
||||
|
||||
```java
|
||||
Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setChannel("chromium"));
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- There will be no more updates for WebKit on Ubuntu 20.04 and Debian 11. We recommend updating your OS to a later version.
|
||||
- `<canvas>` elements inside a snapshot now draw a preview.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
- Chromium 131.0.6778.33
|
||||
- Mozilla Firefox 132.0
|
||||
- WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
- Google Chrome 130
|
||||
- Microsoft Edge 130
|
||||
|
||||
|
||||
## Version 1.48
|
||||
|
||||
### WebSocket routing
|
||||
|
|
|
|||
|
|
@ -6,6 +6,98 @@ toc_max_heading_level: 2
|
|||
|
||||
import LiteYouTube from '@site/src/components/LiteYouTube';
|
||||
|
||||
## Version 1.49
|
||||
|
||||
<LiteYouTube
|
||||
id="S5wCft-ImKk"
|
||||
title="Playwright 1.49"
|
||||
/>
|
||||
|
||||
### Aria snapshots
|
||||
|
||||
New assertion [`method: LocatorAssertions.toMatchAriaSnapshot#2`] verifies page structure by comparing to an expected accessibility tree, represented as YAML.
|
||||
|
||||
```js
|
||||
await page.goto('https://playwright.dev');
|
||||
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- main:
|
||||
- img "Browsers (Chromium, Firefox, WebKit)"
|
||||
- heading "Any browser • Any platform • One API"
|
||||
`);
|
||||
```
|
||||
|
||||
You can generate this assertion with [Test Generator](./codegen) and update the expected snapshot with `--update-snapshots` command line flag.
|
||||
|
||||
Learn more in the [aria snapshots guide](./aria-snapshots).
|
||||
|
||||
### Test runner
|
||||
|
||||
- New option [`property: TestConfig.tsconfig`] allows to specify a single `tsconfig` to be used for all tests.
|
||||
- New method [`method: Test.fail.only`] to focus on a failing test.
|
||||
- Options [`property: TestConfig.globalSetup`] and [`property: TestConfig.globalTeardown`] now support multiple setups/teardowns.
|
||||
- New value `'on-first-failure'` for [`property: TestOptions.screenshot`].
|
||||
- Added "previous" and "next" buttons to the HTML report to quickly switch between test cases.
|
||||
- New properties [`property: TestInfoError.cause`] and [`property: TestError.cause`] mirroring [`Error.cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause).
|
||||
|
||||
### Breaking: `chrome` and `msedge` channels switch to new headless mode
|
||||
|
||||
This change affects you if you're using one of the following channels in your `playwright.config.ts`:
|
||||
- `chrome`, `chrome-dev`, `chrome-beta`, or `chrome-canary`
|
||||
- `msedge`, `msedge-dev`, `msedge-beta`, or `msedge-canary`
|
||||
|
||||
#### What do I need to do?
|
||||
|
||||
After updating to Playwright v1.49, run your test suite. If it still passes, you're good to go. If not, you will probably need to update your snapshots, and adapt some of your test code around PDF viewers and extensions. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for more details.
|
||||
|
||||
### Other breaking changes
|
||||
|
||||
- There will be no more updates for WebKit on Ubuntu 20.04 and Debian 11. We recommend updating your OS to a later version.
|
||||
- Package `@playwright/experimental-ct-vue2` will no longer be updated.
|
||||
- Package `@playwright/experimental-ct-solid` will no longer be updated.
|
||||
|
||||
### Try new Chromium headless
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
> New Headless on the other hand is the real Chrome browser, and is thus more authentic, reliable, and offers more features. This makes it more suitable for high-accuracy end-to-end web app testing or browser extension testing.
|
||||
|
||||
See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for the list of possible breakages you could encounter and more details on Chromium headless. Please file an issue if you see any problems after opting in.
|
||||
|
||||
```js
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'], channel: 'chromium' },
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- `<canvas>` elements inside a snapshot now draw a preview.
|
||||
- New method [`method: Tracing.group`] to visually group actions in the trace.
|
||||
- Playwright docker images switched from Node.js v20 to Node.js v22 LTS.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
- Chromium 131.0.6778.33
|
||||
- Mozilla Firefox 132.0
|
||||
- WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
- Google Chrome 130
|
||||
- Microsoft Edge 130
|
||||
|
||||
|
||||
## Version 1.48
|
||||
|
||||
<LiteYouTube
|
||||
|
|
|
|||
|
|
@ -4,6 +4,80 @@ title: "Release notes"
|
|||
toc_max_heading_level: 2
|
||||
---
|
||||
|
||||
## Version 1.49
|
||||
|
||||
### Aria snapshots
|
||||
|
||||
New assertion [`method: LocatorAssertions.toMatchAriaSnapshot#2`] verifies page structure by comparing to an expected accessibility tree, represented as YAML.
|
||||
|
||||
```python
|
||||
page.goto("https://playwright.dev")
|
||||
expect(page.locator('body')).to_match_aria_snapshot('''
|
||||
- banner:
|
||||
- heading /Playwright enables reliable/ [level=1]
|
||||
- link "Get started"
|
||||
- link "Star microsoft/playwright on GitHub"
|
||||
- main:
|
||||
- img "Browsers (Chromium, Firefox, WebKit)"
|
||||
- heading "Any browser • Any platform • One API"
|
||||
''')
|
||||
```
|
||||
|
||||
You can generate this assertion with [Test Generator](./codegen) or by calling [`method: Locator.ariaSnapshot`].
|
||||
|
||||
Learn more in the [aria snapshots guide](./aria-snapshots).
|
||||
|
||||
### Tracing groups
|
||||
|
||||
New method [`method: Tracing.group`] allows you to visually group actions in the trace viewer.
|
||||
|
||||
```python
|
||||
# All actions between group and group_end
|
||||
# will be shown in the trace viewer as a group.
|
||||
page.context.tracing.group("Open Playwright.dev > API")
|
||||
page.goto("https://playwright.dev/")
|
||||
page.get_by_role("link", name="API").click()
|
||||
page.context.tracing.group_end()
|
||||
```
|
||||
|
||||
### Breaking: `chrome` and `msedge` channels switch to new headless mode
|
||||
|
||||
This change affects you if you're using one of the following channels in your `playwright.config.ts`:
|
||||
- `chrome`, `chrome-dev`, `chrome-beta`, or `chrome-canary`
|
||||
- `msedge`, `msedge-dev`, `msedge-beta`, or `msedge-canary`
|
||||
|
||||
After updating to Playwright v1.49, run your test suite. If it still passes, you're good to go. If not, you will probably need to update your snapshots, and adapt some of your test code around PDF viewers and extensions. See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for more details.
|
||||
|
||||
### Try new Chromium headless
|
||||
|
||||
You can opt into the new headless mode by using `'chromium'` channel. As [official Chrome documentation puts it](https://developer.chrome.com/blog/chrome-headless-shell):
|
||||
|
||||
> New Headless on the other hand is the real Chrome browser, and is thus more authentic, reliable, and offers more features. This makes it more suitable for high-accuracy end-to-end web app testing or browser extension testing.
|
||||
|
||||
See [issue #33566](https://github.com/microsoft/playwright/issues/33566) for the list of possible breakages you could encounter and more details on Chromium headless. Please file an issue if you see any problems after opting in.
|
||||
|
||||
```bash python
|
||||
pytest test_login.py --browser-channel chromium
|
||||
```
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- There will be no more updates for WebKit on Ubuntu 20.04 and Debian 11. We recommend updating your OS to a later version.
|
||||
- `<canvas>` elements inside a snapshot now draw a preview.
|
||||
- Python 3.8 is not supported anymore.
|
||||
|
||||
### Browser Versions
|
||||
|
||||
- Chromium 131.0.6778.33
|
||||
- Mozilla Firefox 132.0
|
||||
- WebKit 18.2
|
||||
|
||||
This version was also tested against the following stable channels:
|
||||
|
||||
- Google Chrome 130
|
||||
- Microsoft Edge 130
|
||||
|
||||
|
||||
## Version 1.48
|
||||
|
||||
### WebSocket routing
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ dotnet test --filter "Name~GetStartedLink"
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -128,6 +129,19 @@ dotnet test -- NUnit.NumberOfTestWorkers=5
|
|||
dotnet test -- MSTest.Parallelize.Workers=5
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```bash
|
||||
dotnet test -- xUnit.MaxParallelThreads=5
|
||||
```
|
||||
|
||||
See [here](https://xunit.net/docs/running-tests-in-parallel.html) for more information to run tests in parallel with xUnit.
|
||||
|
||||
:::note
|
||||
We recommend xUnit 2.8+ which uses the [`conservative` parallelism algorithm](https://xunit.net/docs/running-tests-in-parallel.html#algorithms) by default.
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ npx playwright test --ui
|
|||
|
||||

|
||||
|
||||
Check out or [detailed guide on UI Mode](./test-ui-mode.md) to learn more about it's features.
|
||||
Check out or [detailed guide on UI Mode](./test-ui-mode.md) to learn more about its features.
|
||||
|
||||
### Run tests in headed mode
|
||||
|
||||
|
|
@ -112,11 +112,11 @@ npx playwright test --ui
|
|||
|
||||

|
||||
|
||||
While debugging you can use the Pick Locator button to select an element on the page and see the locator that Playwright would use to find that element. You can also edit the locator in the locator playground and see it highlighting live on the Browser window. Use the Copy Locator button to copy the locator to your clipboard and then paste it into you test.
|
||||
While debugging you can use the Pick Locator button to select an element on the page and see the locator that Playwright would use to find that element. You can also edit the locator in the locator playground and see it highlighting live on the Browser window. Use the Copy Locator button to copy the locator to your clipboard and then paste it into your test.
|
||||
|
||||

|
||||
|
||||
Check out our [detailed guide on UI Mode](./test-ui-mode.md) to learn more about it's features.
|
||||
Check out our [detailed guide on UI Mode](./test-ui-mode.md) to learn more about its features.
|
||||
|
||||
### Debug tests with the Playwright Inspector
|
||||
|
||||
|
|
|
|||
|
|
@ -114,10 +114,16 @@ See [`property: TestConfig.shard`].
|
|||
|
||||
## property: FullConfig.updateSnapshots
|
||||
* since: v1.10
|
||||
- type: <[UpdateSnapshots]<"all"|"none"|"missing">>
|
||||
- type: <[UpdateSnapshots]<"all"|"changed"|"missing"|"none">>
|
||||
|
||||
See [`property: TestConfig.updateSnapshots`].
|
||||
|
||||
## property: FullConfig.updateSourceMethod
|
||||
* since: v1.50
|
||||
- type: <[UpdateSourceMethod]<"overwrite"|"3way"|"patch">>
|
||||
|
||||
See [`property: TestConfig.updateSourceMethod`].
|
||||
|
||||
## property: FullConfig.version
|
||||
* since: v1.20
|
||||
- type: <[string]>
|
||||
|
|
|
|||
|
|
@ -1767,6 +1767,118 @@ Whether to box the step in the report. Defaults to `false`. When the step is box
|
|||
|
||||
Specifies a custom location for the step to be shown in test reports and trace viewer. By default, location of the [`method: Test.step`] call is shown.
|
||||
|
||||
### option: Test.step.timeout
|
||||
* since: v1.50
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds for the step to finish. Defaults to `0` (no timeout).
|
||||
|
||||
## async method: Test.step.fail
|
||||
* since: v1.50
|
||||
- returns: <[void]>
|
||||
|
||||
Marks a test step as "should fail". Playwright runs this test step and ensures that it actually fails. This is useful for documentation purposes to acknowledge that some functionality is broken until it is fixed.
|
||||
|
||||
:::note
|
||||
If the step exceeds the timeout, a [TimeoutError] is thrown. This indicates the step did not fail as expected.
|
||||
:::
|
||||
|
||||
**Usage**
|
||||
|
||||
You can declare a test step as failing, so that Playwright ensures it actually fails.
|
||||
|
||||
```js
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('my test', async ({ page }) => {
|
||||
// ...
|
||||
await test.step.fail('currently failing', async () => {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### param: Test.step.fail.title
|
||||
* since: v1.50
|
||||
- `title` <[string]>
|
||||
|
||||
Step name.
|
||||
|
||||
### param: Test.step.fail.body
|
||||
* since: v1.50
|
||||
- `body` <[function]\(\):[Promise]<[any]>>
|
||||
|
||||
Step body.
|
||||
|
||||
### option: Test.step.fail.box
|
||||
* since: v1.50
|
||||
- `box` <boolean>
|
||||
|
||||
Whether to box the step in the report. Defaults to `false`. When the step is boxed, errors thrown from the step internals point to the step call site. See below for more details.
|
||||
|
||||
### option: Test.step.fail.location
|
||||
* since: v1.50
|
||||
- `location` <[Location]>
|
||||
|
||||
Specifies a custom location for the step to be shown in test reports and trace viewer. By default, location of the [`method: Test.step`] call is shown.
|
||||
|
||||
### option: Test.step.fail.timeout
|
||||
* since: v1.50
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds for the step to finish. Defaults to `0` (no timeout).
|
||||
|
||||
## async method: Test.step.fixme
|
||||
* since: v1.50
|
||||
- returns: <[void]>
|
||||
|
||||
Mark a test step as "fixme", with the intention to fix it. Playwright will not run the step.
|
||||
|
||||
**Usage**
|
||||
|
||||
You can declare a test step as failing, so that Playwright ensures it actually fails.
|
||||
|
||||
```js
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('my test', async ({ page }) => {
|
||||
// ...
|
||||
await test.step.fixme('not yet ready', async () => {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### param: Test.step.fixme.title
|
||||
* since: v1.50
|
||||
- `title` <[string]>
|
||||
|
||||
Step name.
|
||||
|
||||
### param: Test.step.fixme.body
|
||||
* since: v1.50
|
||||
- `body` <[function]\(\):[Promise]<[any]>>
|
||||
|
||||
Step body.
|
||||
|
||||
### option: Test.step.fixme.box
|
||||
* since: v1.50
|
||||
- `box` <boolean>
|
||||
|
||||
Whether to box the step in the report. Defaults to `false`. When the step is boxed, errors thrown from the step internals point to the step call site. See below for more details.
|
||||
|
||||
### option: Test.step.fixme.location
|
||||
* since: v1.50
|
||||
- `location` <[Location]>
|
||||
|
||||
Specifies a custom location for the step to be shown in test reports and trace viewer. By default, location of the [`method: Test.step`] call is shown.
|
||||
|
||||
### option: Test.step.fixme.timeout
|
||||
* since: v1.50
|
||||
- `timeout` <[float]>
|
||||
|
||||
Maximum time in milliseconds for the step to finish. Defaults to `0` (no timeout).
|
||||
|
||||
## method: Test.use
|
||||
* since: v1.10
|
||||
|
||||
|
|
|
|||
|
|
@ -570,12 +570,13 @@ export default defineConfig({
|
|||
|
||||
## property: TestConfig.updateSnapshots
|
||||
* since: v1.10
|
||||
- type: ?<[UpdateSnapshots]<"all"|"none"|"missing">>
|
||||
- type: ?<[UpdateSnapshots]<"all"|"changed"|"missing"|"none">>
|
||||
|
||||
Whether to update expected snapshots with the actual results produced by the test run. Defaults to `'missing'`.
|
||||
* `'all'` - All tests that are executed will update snapshots that did not match. Matching snapshots will not be updated.
|
||||
* `'none'` - No snapshots are updated.
|
||||
* `'all'` - All tests that are executed will update snapshots.
|
||||
* `'changed'` - All tests that are executed will update snapshots that did not match. Matching snapshots will not be updated.
|
||||
* `'missing'` - Missing snapshots are created, for example when authoring a new test and running it for the first time. This is the default.
|
||||
* `'none'` - No snapshots are updated.
|
||||
|
||||
Learn more about [snapshots](../test-snapshots.md).
|
||||
|
||||
|
|
@ -589,6 +590,15 @@ export default defineConfig({
|
|||
});
|
||||
```
|
||||
|
||||
## property: TestConfig.updateSourceMethod
|
||||
* since: v1.50
|
||||
- type: ?<[UpdateSourceMethod]<"overwrite"|"3way"|"patch">>
|
||||
|
||||
Defines how to update the source code snapshots.
|
||||
* `'overwrite'` - Overwrite the source code snapshot with the actual result.
|
||||
* `'3way'` - Use a three-way merge to update the source code snapshot.
|
||||
* `'patch'` - Use a patch to update the source code snapshot. This is the default.
|
||||
|
||||
## property: TestConfig.use
|
||||
* since: v1.10
|
||||
- type: ?<[TestOptions]>
|
||||
|
|
|
|||
|
|
@ -392,8 +392,11 @@ export default defineConfig({
|
|||
});
|
||||
```
|
||||
|
||||
## property: TestOptions.locale = %%-context-option-locale-%%
|
||||
## property: TestOptions.locale
|
||||
* since: v1.10
|
||||
- type: <[string]>
|
||||
|
||||
Specify user locale, for example `en-GB`, `de-DE`, etc. Locale will affect `navigator.language` value, `Accept-Language` request header value as well as number and date formatting rules. Defaults to `en-US`. Learn more about emulation in our [emulation guide](../emulation.md#locale--timezone).
|
||||
|
||||
**Usage**
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ expect.set_options(timeout=10_000)
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -127,6 +128,24 @@ public class UnitTest1 : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
UnitTest1()
|
||||
{
|
||||
SetDefaultExpectTimeout(10_000);
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ Note that by default `toPass` has timeout 0 and does not respect custom [expect
|
|||
|
||||
You can extend Playwright assertions by providing custom matchers. These matchers will be available on the `expect` object.
|
||||
|
||||
In this example we add a custom `toHaveAmount` function. Custom matcher should return a `message` callback and a `pass` flag indicating whether the assertion passed.
|
||||
In this example we add a custom `toHaveAmount` function. Custom matcher should return a `pass` flag indicating whether the assertion passed, and a `message` callback that's used when the assertion fails.
|
||||
|
||||
```js title="fixtures.ts"
|
||||
import { expect as baseExpect } from '@playwright/test';
|
||||
|
|
@ -279,7 +279,7 @@ export const expect = baseExpect.extend({
|
|||
? () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
|
||||
'\n\n' +
|
||||
`Locator: ${locator}\n` +
|
||||
`Expected: ${this.isNot ? 'not' : ''}${this.utils.printExpected(expected)}\n` +
|
||||
`Expected: not ${this.utils.printExpected(expected)}\n` +
|
||||
(matcherResult ? `Received: ${this.utils.printReceived(matcherResult.actual)}` : '')
|
||||
: () => this.utils.matcherHint(assertionName, undefined, undefined, { isNot: this.isNot }) +
|
||||
'\n\n' +
|
||||
|
|
|
|||
|
|
@ -76,33 +76,40 @@ Here are the most common options available in the command line.
|
|||
|
||||
Complete set of Playwright Test options is available in the [configuration file](./test-use-options.md). Following options can be passed to a command line and take priority over the configuration file:
|
||||
|
||||
<!-- // Note: packages/playwright/src/program.ts is the source of truth. -->
|
||||
|
||||
| Option | Description |
|
||||
| :- | :- |
|
||||
| Non-option arguments | Each argument is treated as a regular expression matched against the full test file path. Only tests from the files matching the pattern will be executed. Special symbols like `$` or `*` should be escaped with `\`. In many shells/terminals you may need to quote the arguments. |
|
||||
| `-c <file>` or `--config <file>`| Configuration file. If not passed, defaults to `playwright.config.ts` or `playwright.config.js` in the current directory. |
|
||||
| `--debug`| Run tests with Playwright Inspector. Shortcut for `PWDEBUG=1` environment variable and `--timeout=0 --max-failures=1 --headed --workers=1` options.|
|
||||
| `--fail-on-flaky-tests` | Fails test runs that contain flaky tests. By default flaky tests count as successes. |
|
||||
| `--forbid-only` | Whether to disallow `test.only`. Useful on CI.|
|
||||
| `--global-timeout <number>` | Total timeout for the whole test run in milliseconds. By default, there is no global timeout. Learn more about [various timeouts](./test-timeouts.md).|
|
||||
| `-g <grep>` or `--grep <grep>` | Only run tests matching this regular expression. For example, this will run `'should add to cart'` when passed `-g "add to cart"`. The regular expression will be tested against the string that consists of the project name, test file name, `test.describe` titles if any, test title and all test tags, separated by spaces, e.g. `chromium my-test.spec.ts my-suite my-test @smoke`. The filter does not apply to the tests from dependency projects, i.e. Playwright will still run all tests from [project dependencies](./test-projects.md#dependencies). |
|
||||
| `--grep-invert <grep>` | Only run tests **not** matching this regular expression. The opposite of `--grep`. The filter does not apply to the tests from dependency projects, i.e. Playwright will still run all tests from [project dependencies](./test-projects.md#dependencies).|
|
||||
| `--headed` | Run tests in headed browsers. Useful for debugging. |
|
||||
| `--ignore-snapshots` | Whether to ignore [snapshots](./test-snapshots.md). Use this when snapshot expectations are known to be different, e.g. running tests on Linux against Windows screenshots. |
|
||||
| `--last-failed` | Only re-run the failures.|
|
||||
| `--list` | list all the tests, but do not run them.|
|
||||
| `--max-failures <N>` or `-x`| Stop after the first `N` test failures. Passing `-x` stops after the first failure.|
|
||||
| `--no-deps` | Ignore the dependencies between projects and behave as if they were not specified. |
|
||||
| `--output <dir>` | Directory for artifacts produced by tests, defaults to `test-results`. |
|
||||
| `--only-changed [ref]` | Only run test files that have been changed between working tree and "ref". Defaults to running all uncommitted changes with ref=HEAD. Only supports Git. |
|
||||
| `--pass-with-no-tests` | Allows the test suite to pass when no files are found. |
|
||||
| `--project <name>` | Only run tests from the specified [projects](./test-projects.md), supports '*' wildcard. Defaults to running all projects defined in the configuration file.|
|
||||
| `--quiet` | Whether to suppress stdout and stderr from the tests. |
|
||||
| `--repeat-each <N>` | Run each test `N` times, defaults to one. |
|
||||
| `--reporter <reporter>` | Choose a reporter: minimalist `dot`, concise `line` or detailed `list`. See [reporters](./test-reporters.md) for more information. You can also pass a path to a [custom reporter](./test-reporters.md#custom-reporters) file. |
|
||||
| `--retries <number>` | The maximum number of [retries](./test-retries.md#retries) for flaky tests, defaults to zero (no retries). |
|
||||
| `--shard <shard>` | [Shard](./test-parallel.md#shard-tests-between-multiple-machines) tests and execute only selected shard, specified in the form `current/all`, 1-based, for example `3/5`.|
|
||||
| `--timeout <number>` | Maximum timeout in milliseconds for each test, defaults to 30 seconds. Learn more about [various timeouts](./test-timeouts.md).|
|
||||
| `--trace <mode>` | Force tracing mode, can be `on`, `off`, `on-first-retry`, `on-all-retries`, `retain-on-failure` |
|
||||
| `--tsconfig <path>` | Path to a single tsconfig applicable to all imported files. See [tsconfig resolution](./test-typescript.md#tsconfig-resolution) for more details. |
|
||||
| `--update-snapshots` or `-u` | Whether to update [snapshots](./test-snapshots.md) with actual results instead of comparing them. Use this when snapshot expectations have changed.|
|
||||
| `--workers <number>` or `-j <number>`| The maximum number of concurrent worker processes that run in [parallel](./test-parallel.md). |
|
||||
| Non-option arguments | Each argument is treated as a regular expression matched against the full test file path. Only tests from files matching the pattern will be executed. Special symbols like `$` or `*` should be escaped with `\`. In many shells/terminals you may need to quote the arguments. |
|
||||
| `-c <file>` or `--config <file>` | Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}". Defaults to `playwright.config.ts` or `playwright.config.js` in the current directory. |
|
||||
| `--debug` | Run tests with Playwright Inspector. Shortcut for `PWDEBUG=1` environment variable and `--timeout=0 --max-failures=1 --headed --workers=1` options. |
|
||||
| `--fail-on-flaky-tests` | Fail if any test is flagged as flaky (default: false). |
|
||||
| `--forbid-only` | Fail if `test.only` is called (default: false). Useful on CI. |
|
||||
| `--fully-parallel` | Run all tests in parallel (default: false). |
|
||||
| `--global-timeout <timeout>` | Maximum time this test suite can run in milliseconds (default: unlimited). |
|
||||
| `-g <grep>` or `--grep <grep>` | Only run tests matching this regular expression (default: ".*"). |
|
||||
| `-gv <grep>` or `--grep-invert <grep>` | Only run tests that do not match this regular expression. |
|
||||
| `--headed` | Run tests in headed browsers (default: headless). |
|
||||
| `--ignore-snapshots` | Ignore screenshot and snapshot expectations. |
|
||||
| `--last-failed` | Only re-run the failures. |
|
||||
| `--list` | Collect all the tests and report them, but do not run. |
|
||||
| `--max-failures <N>` or `-x` | Stop after the first `N` failures. Passing `-x` stops after the first failure. |
|
||||
| `--no-deps` | Do not run project dependencies. |
|
||||
| `--output <dir>` | Folder for output artifacts (default: "test-results"). |
|
||||
| `--only-changed [ref]` | Only run test files that have been changed between 'HEAD' and 'ref'. Defaults to running all uncommitted changes. Only supports Git. |
|
||||
| `--pass-with-no-tests` | Makes test run succeed even if no tests were found. |
|
||||
| `--project <project-name...>` | Only run tests from the specified list of projects, supports '*' wildcard (default: run all projects). |
|
||||
| `--quiet` | Suppress stdio. |
|
||||
| `--repeat-each <N>` | Run each test `N` times (default: 1). |
|
||||
| `--reporter <reporter>` | Reporter to use, comma-separated, can be "dot", "line", "list", or others (default: "list"). You can also pass a path to a custom reporter file. |
|
||||
| `--retries <retries>` | Maximum retry count for flaky tests, zero for no retries (default: no retries). |
|
||||
| `--shard <shard>` | Shard tests and execute only the selected shard, specified in the form "current/all", 1-based, e.g., "3/5". |
|
||||
| `--timeout <timeout>` | Specify test timeout threshold in milliseconds, zero for unlimited (default: 30 seconds). |
|
||||
| `--trace <mode>` | Force tracing mode, can be "on", "off", "on-first-retry", "on-all-retries", "retain-on-failure", "retain-on-first-failure". |
|
||||
| `--tsconfig <path>` | Path to a single tsconfig applicable to all imported files (default: look up tsconfig for each imported file separately). |
|
||||
| `--ui` | Run tests in interactive UI mode. |
|
||||
| `--ui-host <host>` | Host to serve UI on; specifying this option opens UI in a browser tab. |
|
||||
| `--ui-port <port>` | Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab. |
|
||||
| `-u` or `--update-snapshots [mode]` | Update snapshots with actual results. Possible values are "all", "changed", "missing", and "none". Not passing defaults to "missing"; passing without a value defaults to "changed". |
|
||||
| `-j <workers>` or `--workers <workers>` | Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker (default: 50%). |
|
||||
| `-x` | Stop after the first failure. |
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ test('event should work', async ({ mount }) => {
|
|||
|
||||
## How to get started
|
||||
|
||||
Adding Playwright Test to an existing project is easy. Below are the steps to enable Playwright Test for a React, Vue, Svelte or Solid project.
|
||||
Adding Playwright Test to an existing project is easy. Below are the steps to enable Playwright Test for a React, Vue or Svelte project.
|
||||
|
||||
### Step 1: Install Playwright Test for components for your respective framework
|
||||
|
||||
|
|
@ -106,7 +106,6 @@ component is mounted using this script. It can be either a `.js`, `.ts`, `.jsx`
|
|||
defaultValue="react"
|
||||
values={[
|
||||
{label: 'React', value: 'react'},
|
||||
{label: 'Solid', value: 'solid'},
|
||||
{label: 'Svelte', value: 'svelte'},
|
||||
{label: 'Vue', value: 'vue'},
|
||||
]
|
||||
|
|
@ -168,20 +167,6 @@ test('should work', async ({ mount }) => {
|
|||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="solid">
|
||||
|
||||
```js title="app.spec.tsx"
|
||||
import { test, expect } from '@playwright/experimental-ct-solid';
|
||||
import App from './App';
|
||||
|
||||
test('should work', async ({ mount }) => {
|
||||
const component = await mount(<App />);
|
||||
await expect(component).toContainText('Learn Solid');
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Step 3. Run the tests
|
||||
|
|
@ -309,7 +294,6 @@ Provide props to a component when mounted.
|
|||
defaultValue="react"
|
||||
values={[
|
||||
{label: 'React', value: 'react'},
|
||||
{label: 'Solid', value: 'solid'},
|
||||
{label: 'Svelte', value: 'svelte'},
|
||||
{label: 'Vue', value: 'vue'},
|
||||
]
|
||||
|
|
@ -325,17 +309,6 @@ test('props', async ({ mount }) => {
|
|||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solid">
|
||||
|
||||
```js title="component.spec.tsx"
|
||||
import { test } from '@playwright/experimental-ct-solid';
|
||||
|
||||
test('props', async ({ mount }) => {
|
||||
const component = await mount(<Component msg="greetings" />);
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="svelte">
|
||||
|
||||
|
|
@ -379,7 +352,6 @@ Provide callbacks/events to a component when mounted.
|
|||
defaultValue="react"
|
||||
values={[
|
||||
{label: 'React', value: 'react'},
|
||||
{label: 'Solid', value: 'solid'},
|
||||
{label: 'Svelte', value: 'svelte'},
|
||||
{label: 'Vue', value: 'vue'},
|
||||
]
|
||||
|
|
@ -395,17 +367,6 @@ test('callback', async ({ mount }) => {
|
|||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solid">
|
||||
|
||||
```js title="component.spec.tsx"
|
||||
import { test } from '@playwright/experimental-ct-solid';
|
||||
|
||||
test('callback', async ({ mount }) => {
|
||||
const component = await mount(<Component onClick={() => {}} />);
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="svelte">
|
||||
|
||||
|
|
@ -449,7 +410,6 @@ Provide children/slots to a component when mounted.
|
|||
defaultValue="react"
|
||||
values={[
|
||||
{label: 'React', value: 'react'},
|
||||
{label: 'Solid', value: 'solid'},
|
||||
{label: 'Svelte', value: 'svelte'},
|
||||
{label: 'Vue', value: 'vue'},
|
||||
]
|
||||
|
|
@ -465,17 +425,6 @@ test('children', async ({ mount }) => {
|
|||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solid">
|
||||
|
||||
```js title="component.spec.tsx"
|
||||
import { test } from '@playwright/experimental-ct-solid';
|
||||
|
||||
test('children', async ({ mount }) => {
|
||||
const component = await mount(<Component>Child</Component>);
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="svelte">
|
||||
|
||||
|
|
@ -519,7 +468,6 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let
|
|||
defaultValue="react"
|
||||
values={[
|
||||
{label: 'React', value: 'react'},
|
||||
{label: 'Solid', value: 'solid'},
|
||||
{label: 'Vue3', value: 'vue3'},
|
||||
]
|
||||
}>
|
||||
|
|
@ -554,37 +502,6 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let
|
|||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="solid">
|
||||
|
||||
```js title="playwright/index.tsx"
|
||||
import { beforeMount, afterMount } from '@playwright/experimental-ct-solid/hooks';
|
||||
import { Router } from '@solidjs/router';
|
||||
|
||||
export type HooksConfig = {
|
||||
enableRouting?: boolean;
|
||||
}
|
||||
|
||||
beforeMount<HooksConfig>(async ({ App, hooksConfig }) => {
|
||||
if (hooksConfig?.enableRouting)
|
||||
return <Router><App /></Router>;
|
||||
});
|
||||
```
|
||||
|
||||
```js title="src/pages/ProductsPage.spec.tsx"
|
||||
import { test, expect } from '@playwright/experimental-ct-solid';
|
||||
import type { HooksConfig } from '../playwright';
|
||||
import { ProductsPage } from './pages/ProductsPage';
|
||||
|
||||
test('configure routing through hooks config', async ({ page, mount }) => {
|
||||
const component = await mount<HooksConfig>(<ProductsPage />, {
|
||||
hooksConfig: { enableRouting: true },
|
||||
});
|
||||
await expect(component.getByRole('link')).toHaveAttribute('href', '/products/42');
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="vue3">
|
||||
|
||||
```js title="playwright/index.ts"
|
||||
|
|
@ -626,7 +543,6 @@ Unmount the mounted component from the DOM. This is useful for testing the compo
|
|||
defaultValue="react"
|
||||
values={[
|
||||
{label: 'React', value: 'react'},
|
||||
{label: 'Solid', value: 'solid'},
|
||||
{label: 'Svelte', value: 'svelte'},
|
||||
{label: 'Vue', value: 'vue'},
|
||||
]
|
||||
|
|
@ -643,18 +559,6 @@ test('unmount', async ({ mount }) => {
|
|||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solid">
|
||||
|
||||
```js title="component.spec.tsx"
|
||||
import { test } from '@playwright/experimental-ct-solid';
|
||||
|
||||
test('unmount', async ({ mount }) => {
|
||||
const component = await mount(<Component/>);
|
||||
await component.unmount();
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="svelte">
|
||||
|
||||
|
|
@ -700,7 +604,6 @@ Update props, slots/children, and/or events/callbacks of a mounted component. Th
|
|||
defaultValue="react"
|
||||
values={[
|
||||
{label: 'React', value: 'react'},
|
||||
{label: 'Solid', value: 'solid'},
|
||||
{label: 'Svelte', value: 'svelte'},
|
||||
{label: 'Vue', value: 'vue'},
|
||||
]
|
||||
|
|
@ -719,20 +622,6 @@ test('update', async ({ mount }) => {
|
|||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="solid">
|
||||
|
||||
```js title="component.spec.tsx"
|
||||
import { test } from '@playwright/experimental-ct-solid';
|
||||
|
||||
test('update', async ({ mount }) => {
|
||||
const component = await mount(<Component/>);
|
||||
await component.update(
|
||||
<Component msg="greetings" onClick={() => {}}>Child</Component>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="svelte">
|
||||
|
||||
|
|
@ -820,7 +709,7 @@ test('example test', async ({ mount, router }) => {
|
|||
|
||||
## Frequently asked questions
|
||||
|
||||
### What's the difference between `@playwright/test` and `@playwright/experimental-ct-{react,svelte,vue,solid}`?
|
||||
### What's the difference between `@playwright/test` and `@playwright/experimental-ct-{react,svelte,vue}`?
|
||||
|
||||
```js
|
||||
test('…', async ({ mount, page, context }) => {
|
||||
|
|
@ -828,13 +717,12 @@ test('…', async ({ mount, page, context }) => {
|
|||
});
|
||||
```
|
||||
|
||||
`@playwright/experimental-ct-{react,svelte,vue,solid}` wrap `@playwright/test` to provide an additional built-in component-testing specific fixture called `mount`:
|
||||
`@playwright/experimental-ct-{react,svelte,vue}` wrap `@playwright/test` to provide an additional built-in component-testing specific fixture called `mount`:
|
||||
|
||||
<Tabs
|
||||
defaultValue="react"
|
||||
values={[
|
||||
{label: 'React', value: 'react'},
|
||||
{label: 'Solid', value: 'solid'},
|
||||
{label: 'Svelte', value: 'svelte'},
|
||||
{label: 'Vue', value: 'vue'},
|
||||
]
|
||||
|
|
@ -895,22 +783,6 @@ test('should work', async ({ mount }) => {
|
|||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="solid">
|
||||
|
||||
```js
|
||||
import { test, expect } from '@playwright/experimental-ct-solid';
|
||||
import HelloWorld from './HelloWorld';
|
||||
|
||||
test.use({ viewport: { width: 500, height: 500 } });
|
||||
|
||||
test('should work', async ({ mount }) => {
|
||||
const component = await mount(<HelloWorld msg="greetings" />);
|
||||
await expect(component).toContainText('Greetings');
|
||||
});
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
Additionally, it adds some config options you can use in your `playwright-ct.config.{ts,js}`.
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@ test.describe('todo tests', () => {
|
|||
### With fixtures
|
||||
|
||||
Fixtures have a number of advantages over before/after hooks:
|
||||
- Fixtures **encapsulate** setup and teardown in the same place so it is easier to write.
|
||||
- Fixtures are **reusable** between test files - you can define them once and use in all your tests. That's how Playwright's built-in `page` fixture works.
|
||||
- Fixtures **encapsulate** setup and teardown in the same place so it is easier to write. So if you have an after hook that tears down what was created in a before hook, consider turning them into a fixture.
|
||||
- Fixtures are **reusable** between test files - you can define them once and use in all your tests. That's how Playwright's built-in `page` fixture works. So if you have a helper function that is used in multiple tests, consider turning it into a fixture.
|
||||
- Fixtures are **on-demand** - you can define as many fixtures as you'd like, and Playwright Test will setup only the ones needed by your test and nothing else.
|
||||
- Fixtures are **composable** - they can depend on each other to provide complex behaviors.
|
||||
- Fixtures are **flexible**. Tests can use any combinations of the fixtures to tailor precise environment they need, without affecting other tests.
|
||||
|
|
|
|||
|
|
@ -129,7 +129,13 @@ You can use the `globalSetup` option in the [configuration file](./test-configur
|
|||
Similarly, use `globalTeardown` to run something once after all the tests. Alternatively, let `globalSetup` return a function that will be used as a global teardown. You can pass data such as port number, authentication tokens, etc. from your global setup to your tests using environment variables.
|
||||
|
||||
:::note
|
||||
Using `globalSetup` and `globalTeardown` will not produce traces or artifacts, and options like `headless` or `testIdAttribute` specified in the config file are not applied. If you want to produce traces and artifacts and respect config options, use [project dependencies](#option-1-project-dependencies).
|
||||
Beware of `globalSetup` and `globalTeardown` caveats:
|
||||
|
||||
- These methods will not produce traces or artifacts unless explictly enabled, as described in [Capturing trace of failures during global setup](#capturing-trace-of-failures-during-global-setup).
|
||||
- Options sush as `headless` or `testIdAttribute` specified in the config file are not applied,
|
||||
- An uncaught exception thrown in `globalSetup` will prevent Playwright from running tests, and no test results will appear in reporters.
|
||||
|
||||
Consider using [project dependencies](#option-1-project-dependencies) to produce traces, artifacts, respect config options and get test results in reporters even in case of a setup failure.
|
||||
:::
|
||||
|
||||
```js title="playwright.config.ts"
|
||||
|
|
|
|||
|
|
@ -5,19 +5,64 @@ title: "Test Runners"
|
|||
|
||||
## Introduction
|
||||
|
||||
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience the easiest way of getting started is by using the base classes we provide for [MSTest](#mstest) and [NUnit](#nunit). These classes support running tests on multiple browser engines, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box.
|
||||
While Playwright for .NET isn't tied to a particular test runner or testing framework, in our experience the easiest way of getting started is by using the base classes we provide for MSTest, NUnit, or xUnit. These classes support running tests on multiple browser engines, adjusting launch/context options and getting a [Page]/[BrowserContext] instance per test out of the box.
|
||||
|
||||
Playwright and Browser instances will be reused between tests for better performance. We
|
||||
recommend running each test case in a new BrowserContext, this way browser state will be
|
||||
isolated between the tests.
|
||||
|
||||
## MSTest
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
Playwright provides base classes to write tests with MSTest via the [`Microsoft.Playwright.MSTest`](https://www.nuget.org/packages/Microsoft.Playwright.MSTest) package.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
Playwright provides base classes to write tests with xUnit via the [`Microsoft.Playwright.Xunit`](https://www.nuget.org/packages/Microsoft.Playwright.Xunit) package.
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Check out the [installation guide](./intro.md) to get started.
|
||||
|
||||
### Running MSTest tests in Parallel
|
||||
## Running tests in Parallel
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
By default NUnit will run all test files in parallel, while running tests inside each file sequentially (`ParallelScope.Self`). It will create as many processes as there are cores on the host system. You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter.
|
||||
Only `ParallelScope.Self` is supported.
|
||||
|
||||
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
|
||||
|
||||
```bash
|
||||
dotnet test -- NUnit.NumberOfTestWorkers=5
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
By default MSTest will run all classes in parallel, while running tests inside each class sequentially (`ExecutionScope.ClassLevel`). It will create as many processes as there are cores on the host system. You can adjust this behavior by using the following CLI parameter or using a `.runsettings` file, see below.
|
||||
Running tests in parallel at the method level (`ExecutionScope.MethodLevel`) is not supported.
|
||||
|
|
@ -26,7 +71,73 @@ Running tests in parallel at the method level (`ExecutionScope.MethodLevel`) is
|
|||
dotnet test --settings:.runsettings -- MSTest.Parallelize.Workers=4
|
||||
```
|
||||
|
||||
### Customizing [BrowserContext] options
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
By default xUnit will run all classes in parallel, while running tests inside each class sequentially.
|
||||
It will create by default as many processes as there are cores on the system. You can adjust this behavior by using the following CLI parameter or using a `.runsettings` file, see below.
|
||||
|
||||
```bash
|
||||
dotnet test -- xUnit.MaxParallelThreads=5
|
||||
```
|
||||
|
||||
:::note
|
||||
We recommend xUnit 2.8+ which uses the [`conservative` parallelism algorithm](https://xunit.net/docs/running-tests-in-parallel.html#algorithms) by default.
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Customizing [BrowserContext] options
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]
|
||||
public class MyTest : PageTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestWithCustomContextOptions()
|
||||
{
|
||||
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||
await Page.GotoAsync("/login");
|
||||
}
|
||||
|
||||
public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return new BrowserNewContextOptions()
|
||||
{
|
||||
ColorScheme = ColorScheme.Light,
|
||||
ViewportSize = new()
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080
|
||||
},
|
||||
BaseURL = "https://github.com",
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
|
||||
|
||||
|
|
@ -65,7 +176,46 @@ public class ExampleTest : PageTest
|
|||
|
||||
```
|
||||
|
||||
### Customizing [Browser]/launch options
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.Xunit.PageTest` or `Microsoft.Playwright.Xunit.ContextTest`. See the following example:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1 : PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task TestWithCustomContextOptions()
|
||||
{
|
||||
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||
await Page.GotoAsync("/login");
|
||||
}
|
||||
public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return new BrowserNewContextOptions()
|
||||
{
|
||||
ColorScheme = ColorScheme.Light,
|
||||
ViewportSize = new()
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080
|
||||
},
|
||||
BaseURL = "https://github.com",
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Customizing [Browser]/launch options
|
||||
|
||||
[Browser]/launch options can be overridden either using a run settings file or by setting the run settings options directly via the
|
||||
CLI. See the following example:
|
||||
|
|
@ -87,14 +237,56 @@ CLI. See the following example:
|
|||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
|
||||
```
|
||||
|
||||
### Using Verbose API Logs
|
||||
## Using Verbose API Logs
|
||||
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In MSTest, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. Within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
|
||||
### Using the .runsettings file
|
||||
## Using the .runsettings file
|
||||
|
||||
When running tests from Visual Studio, you can take advantage of the `.runsettings` file. The following shows a reference of the supported values.
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
For example, to specify the number of workers you can use `NUnit.NumberOfTestWorkers` or to enable `DEBUG` logs `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<!-- NUnit adapter -->
|
||||
<NUnit>
|
||||
<NumberOfTestWorkers>24</NumberOfTestWorkers>
|
||||
</NUnit>
|
||||
<!-- General run configuration -->
|
||||
<RunConfiguration>
|
||||
<EnvironmentVariables>
|
||||
<!-- For debugging selectors, it's recommend to set the following environment variable -->
|
||||
<DEBUG>pw:api</DEBUG>
|
||||
</EnvironmentVariables>
|
||||
</RunConfiguration>
|
||||
<!-- Playwright -->
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<ExpectTimeout>5000</ExpectTimeout>
|
||||
<LaunchOptions>
|
||||
<Headless>false</Headless>
|
||||
<Channel>msedge</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
For example, to specify the number of workers, you can use `MSTest.Parallelize.Workers`. You can also enable `DEBUG` logs using `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
|
|
@ -125,109 +317,18 @@ For example, to specify the number of workers, you can use `MSTest.Parallelize.W
|
|||
</RunSettings>
|
||||
```
|
||||
|
||||
### Base MSTest classes for Playwright
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.MSTest` namespace:
|
||||
|
||||
|Test |Description|
|
||||
|--------------|-----------|
|
||||
|PageTest |Each test gets a fresh copy of a web [Page] created in its own unique [BrowserContext]. Extending this class is the simplest way of writing a fully-functional Playwright test.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|ContextTest |Each test will get a fresh copy of a [BrowserContext]. You can create as many pages in this context as you'd like. Using this test is the easiest way to test multi-page scenarios where you need more than one tab.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|BrowserTest |Each test will get a browser and can create as many contexts as it likes. Each test is responsible for cleaning up all the contexts it created.|
|
||||
|PlaywrightTest|This gives each test a Playwright object so that the test could start and stop as many browsers as it likes.|
|
||||
|
||||
## NUnit
|
||||
|
||||
Playwright provides base classes to write tests with NUnit via the [`Microsoft.Playwright.NUnit`](https://www.nuget.org/packages/Microsoft.Playwright.NUnit) package.
|
||||
|
||||
Check out the [installation guide](./intro.md) to get started.
|
||||
|
||||
### Running NUnit tests in Parallel
|
||||
|
||||
By default NUnit will run all test files in parallel, while running tests inside each file sequentially (`ParallelScope.Self`). It will create as many processes as there are cores on the host system. You can adjust this behavior using the NUnit.NumberOfTestWorkers parameter.
|
||||
Only `ParallelScope.Self` is supported.
|
||||
|
||||
For CPU-bound tests, we recommend using as many workers as there are cores on your system, divided by 2. For IO-bound tests you can use as many workers as you have cores.
|
||||
|
||||
```bash
|
||||
dotnet test -- NUnit.NumberOfTestWorkers=5
|
||||
```
|
||||
|
||||
### Customizing [BrowserContext] options
|
||||
|
||||
To customize context options, you can override the `ContextOptions` method of your test class derived from `Microsoft.Playwright.MSTest.PageTest` or `Microsoft.Playwright.MSTest.ContextTest`. See the following example:
|
||||
|
||||
```csharp
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[Parallelizable(ParallelScope.Self)]
|
||||
[TestFixture]
|
||||
public class MyTest : PageTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestWithCustomContextOptions()
|
||||
{
|
||||
// The following Page (and BrowserContext) instance has the custom colorScheme, viewport and baseURL set:
|
||||
await Page.GotoAsync("/login");
|
||||
}
|
||||
|
||||
public override BrowserNewContextOptions ContextOptions()
|
||||
{
|
||||
return new BrowserNewContextOptions()
|
||||
{
|
||||
ColorScheme = ColorScheme.Light,
|
||||
ViewportSize = new()
|
||||
{
|
||||
Width = 1920,
|
||||
Height = 1080
|
||||
},
|
||||
BaseURL = "https://github.com",
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Customizing [Browser]/launch options
|
||||
|
||||
[Browser]/launch options can be overridden either using a run settings file or by setting the run settings options directly via the
|
||||
CLI. See the following example:
|
||||
For example, to specify the number of workers, you can use `xUnit.MaxParallelThreads`. You can also enable `DEBUG` logs using `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<Playwright>
|
||||
<BrowserName>chromium</BrowserName>
|
||||
<LaunchOptions>
|
||||
<Headless>false</Headless>
|
||||
<Channel>msedge</Channel>
|
||||
</LaunchOptions>
|
||||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
|
||||
```bash
|
||||
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
|
||||
```
|
||||
|
||||
### Using Verbose API Logs
|
||||
|
||||
When you have enabled the [verbose API log](./debug.md#verbose-api-logs), via the `DEBUG` environment variable, you will see the messages in the standard error stream. In NUnit, within Visual Studio, that will be the `Tests` pane of the `Output` window. It will also be displayed in the `Test Log` for each test.
|
||||
|
||||
### Using the .runsettings file
|
||||
|
||||
When running tests from Visual Studio, you can take advantage of the `.runsettings` file. The following shows a reference of the supported values.
|
||||
|
||||
For example, to specify the amount of workers you can use `NUnit.NumberOfTestWorkers` or to enable `DEBUG` logs `RunConfiguration.EnvironmentVariables`.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RunSettings>
|
||||
<!-- NUnit adapter -->
|
||||
<NUnit>
|
||||
<NumberOfTestWorkers>24</NumberOfTestWorkers>
|
||||
</NUnit>
|
||||
<!-- See https://xunit.net/docs/runsettings -->
|
||||
<xUnit>
|
||||
<MaxParallelThreads>1</MaxParallelThreads>
|
||||
</xUnit>
|
||||
<!-- General run configuration -->
|
||||
<RunConfiguration>
|
||||
<EnvironmentVariables>
|
||||
|
|
@ -246,19 +347,40 @@ For example, to specify the amount of workers you can use `NUnit.NumberOfTestWor
|
|||
</Playwright>
|
||||
</RunSettings>
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Base NUnit classes for Playwright
|
||||
## Base classes for Playwright
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.NUnit` namespace:
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.MSTest` namespace:
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
There are a few base classes available to you in `Microsoft.Playwright.Xunit` namespace:
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|Test |Description|
|
||||
|--------------|-----------|
|
||||
|PageTest |Each test gets a fresh copy of a web [Page] created in its own unique [BrowserContext]. Extending this class is the simplest way of writing a fully-functional Playwright test.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|ContextTest |Each test will get a fresh copy of a [BrowserContext]. You can create as many pages in this context as you'd like. Using this test is the easiest way to test multi-page scenarios where you need more than one tab.<br></br><br></br>Note: You can override the `ContextOptions` method in each test file to control context options, the ones typically passed into the [`method: Browser.newContext`] method. That way you can specify all kinds of emulation options for your test file individually.|
|
||||
|BrowserTest |Each test will get a browser and can create as many contexts as it likes. Each test is responsible for cleaning up all the contexts it created.|
|
||||
|PlaywrightTest|This gives each test a Playwright object so that the test could start and stop as many browsers as it likes.|
|
||||
|
||||
## xUnit support
|
||||
|
||||
While using xUnit is also supported, we do not support running parallel tests. This is a well known problem/design limitation
|
||||
outlined by the maintainers across [several](https://github.com/xunit/xunit/issues/2003) [issues](https://github.com/xunit/xunit/issues/2111#issuecomment-650004247).
|
||||
|
|
|
|||
|
|
@ -111,16 +111,21 @@ def test_visit_admin_dashboard(page: Page):
|
|||
|
||||
If you're using VSCode with Pylance, these types can be inferred by enabling the `python.testing.pytestEnabled` setting so you don't need the type annotation.
|
||||
|
||||
### Configure slow mo
|
||||
### Using multiple contexts
|
||||
|
||||
Run tests with slow mo with the `--slowmo` argument.
|
||||
In order to simulate multiple users, you can create multiple [`BrowserContext`](./browser-contexts) instances.
|
||||
|
||||
```bash
|
||||
pytest --slowmo 100
|
||||
```py title="test_my_application.py"
|
||||
from playwright.sync_api import Page, BrowserContext
|
||||
from pytest_playwright.pytest_playwright import CreateContextCallback
|
||||
|
||||
def test_foo(page: Page, new_context: CreateContextCallback) -> None:
|
||||
page.goto("https://example.com")
|
||||
context = new_context()
|
||||
page2 = context.new_page()
|
||||
# page and page2 are in different contexts
|
||||
```
|
||||
|
||||
Slows down Playwright operations by 100 milliseconds.
|
||||
|
||||
### Skip test by browser
|
||||
|
||||
```py title="test_my_application.py"
|
||||
|
|
@ -198,7 +203,7 @@ def browser_context_args(browser_context_args):
|
|||
}
|
||||
```
|
||||
|
||||
### Device emulation
|
||||
### Device emulation / BrowserContext option overrides
|
||||
|
||||
```py title="conftest.py"
|
||||
import pytest
|
||||
|
|
@ -254,3 +259,18 @@ def test_bing_is_working(page):
|
|||
## Deploy to CI
|
||||
|
||||
See the [guides for CI providers](./ci.md) to deploy your tests to CI/CD.
|
||||
|
||||
## Async Fixtures
|
||||
|
||||
If you want to use async fixtures, you can use the [`pytest-playwright-asyncio`](https://pypi.org/project/pytest-playwright-asyncio/) plugin.
|
||||
Make sure to use `pytest-asyncio>=0.24.0` and make your tests use of [`loop_scope=sesion`](https://pytest-asyncio.readthedocs.io/en/latest/how-to-guides/run_session_tests_in_same_loop.html).
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from playwright.async_api import Page
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
async def test_foo(page: Page):
|
||||
await page.goto("https://github.com")
|
||||
# ...
|
||||
```
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ When `fullyParallel: true` is enabled, Playwright Test runs individual tests in
|
|||
|
||||
**Sharding without fullyParallel**
|
||||
|
||||
Without the fullyParallel setting, Playwright Test defaults to file-level granularity, meaning entire test files are assigned to shards. In this case, the number of tests per file can greatly influence shard distribution. If your test files are not evenly sized (i.e., some files contain many more tests than others), certain shards may end up running significantly more tests, while others may run fewer or even none.
|
||||
Without the fullyParallel setting, Playwright Test defaults to file-level granularity, meaning entire test files are assigned to shards (note that the same file may be assigned to different shards across different projects). In this case, the number of tests per file can greatly influence shard distribution. If your test files are not evenly sized (i.e., some files contain many more tests than others), certain shards may end up running significantly more tests, while others may run fewer or even none.
|
||||
|
||||
**Key Takeaways:**
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ test('example test', async ({ page }) => {
|
|||
});
|
||||
```
|
||||
|
||||
:::warning
|
||||
Browser rendering can vary based on the host OS, version, settings, hardware, power source (battery vs. power adapter), headless mode, and other factors. For consistent screenshots, run tests in the same environment where the baseline screenshots were generated.
|
||||
:::
|
||||
|
||||
## Generating screenshots
|
||||
|
||||
When you run above for the first time, test runner will say:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
|
|||
|
||||
## Introduction
|
||||
|
||||
UI Mode lets you explore, run and debug tests with a time travel experience complete with watch mode. All test files are loaded into the testing sidebar where you can expand each file and describe block to individually run, view, watch and debug each test. Filter tests by **text** or **@tag** or by **passed**, **failed** and **skipped** tests as well as by [**projects**](./test-projects) as set in your `playwright.config` file. See a full trace of your tests and hover back and forward over each action to see what was happening during each step and pop out the DOM snapshot to a separate window for a better debugging experience.
|
||||
UI Mode lets you explore, run, and debug tests with a time travel experience complete with a watch mode. All test files are displayed in the testing sidebar, allowing you to expand each file and describe block to individually run, view, watch, and debug each test. Filter tests by **name**, [**projects**](./test-projects) (set in your `playwright.config` file), **@tag**, or by the execution status of **passed**, **failed**, and **skipped**. See a full trace of your tests and hover back and forward over each action to see what was happening during each step. You can also pop out the DOM snapshot of a given moment into a separate window for a better debugging experience.
|
||||
|
||||
<LiteYouTube
|
||||
id="d0u6XhXknzU"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ Traces can be recorded using the [`property: BrowserContext.tracing`] API as fol
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -112,6 +113,69 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp
|
||||
using System.Reflection;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[WithTestName]
|
||||
public class UnitTest1 : PageTest
|
||||
{
|
||||
public override async Task InitializeAsync()
|
||||
{
|
||||
await base.InitializeAsync().ConfigureAwait(false);
|
||||
await Context.Tracing.StartAsync(new()
|
||||
{
|
||||
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
|
||||
Screenshots = true,
|
||||
Snapshots = true,
|
||||
Sources = true
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
await Context.Tracing.StopAsync(new()
|
||||
{
|
||||
Path = Path.Combine(
|
||||
Environment.CurrentDirectory,
|
||||
"playwright-traces",
|
||||
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
|
||||
)
|
||||
});
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
// ...
|
||||
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
|
||||
}
|
||||
}
|
||||
|
||||
public class WithTestNameAttribute : BeforeAfterTestAttribute
|
||||
{
|
||||
public static string CurrentTestName = string.Empty;
|
||||
public static string CurrentClassName = string.Empty;
|
||||
|
||||
public override void Before(MethodInfo methodInfo)
|
||||
{
|
||||
CurrentTestName = methodInfo.Name;
|
||||
CurrentClassName = methodInfo.DeclaringType!.Name;
|
||||
}
|
||||
|
||||
public override void After(MethodInfo methodInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -134,4 +198,4 @@ Check out our detailed guide on [Trace Viewer](/trace-viewer.md) to learn more a
|
|||
## What's next
|
||||
|
||||
- [Run tests on CI with GitHub Actions](/ci-intro.md)
|
||||
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)
|
||||
- [Learn more about the MSTest, NUnit, and xUnit base classes](./test-runners.md)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ Playwright Trace Viewer is a GUI tool that lets you explore recorded Playwright
|
|||
|
||||
## Recording a Trace
|
||||
|
||||
By default the [playwright.config](./trace-viewer.md#recording-a-trace-on-ci) file will contain the configuration needed to create a `trace.zip` file for each test. Traces are setup to run `on-first-retry` meaning they will be run on the first retry of a failed test. Also `retries` are set to 2 when running on CI and 0 locally. This means the traces will be recorded on the first retry of a failed test but not on the first run and not on the second retry.
|
||||
By default the [playwright.config](./trace-viewer.md#tracing-on-ci) file will contain the configuration needed to create a `trace.zip` file for each test. Traces are setup to run `on-first-retry` meaning they will be run on the first retry of a failed test. Also `retries` are set to 2 when running on CI and 0 locally. This means the traces will be recorded on the first retry of a failed test but not on the first run and not on the second retry.
|
||||
|
||||
```js title="playwright.config.ts"
|
||||
import { defineConfig } from '@playwright/test';
|
||||
|
|
@ -45,7 +45,7 @@ npx playwright test --trace on
|
|||
|
||||
## Opening the HTML report
|
||||
|
||||
The HTML report shows you a report of all your tests that have been ran and on which browsers as well as how long they took. Tests can be filtered by passed tests, failed, flakey or skipped tests. You can also search for a particular test. Clicking on a test will open the detailed view where you can see more information on your tests such as the errors, the test steps and the trace.
|
||||
The HTML report shows you a report of all your tests that have been run and on which browsers as well as how long they took. Tests can be filtered by passed tests, failed, flaky or skipped tests. You can also search for a particular test. Clicking on a test will open the detailed view where you can see more information on your tests such as the errors, the test steps and the trace.
|
||||
|
||||
```bash
|
||||
npx playwright show-report
|
||||
|
|
|
|||
|
|
@ -17,106 +17,67 @@ Playwright Trace Viewer is a GUI tool that helps you explore recorded Playwright
|
|||
title="Viewing Playwright Traces"
|
||||
/>
|
||||
|
||||
## Trace Viewer features
|
||||
### Actions
|
||||
## Opening Trace Viewer
|
||||
|
||||
In the Actions tab you can see what locator was used for every action and how long each one took to run. Hover over each action of your test and visually see the change in the DOM snapshot. Go back and forward in time and click an action to inspect and debug. Use the Before and After tabs to visually see what happened before and after the action.
|
||||
You can open a saved trace using either the Playwright CLI or in the browser at [trace.playwright.dev](https://trace.playwright.dev). Make sure to add the full path to where your `trace.zip` file is located.
|
||||
|
||||

|
||||
```bash js
|
||||
npx playwright show-trace path/to/trace.zip
|
||||
```
|
||||
|
||||
**Selecting each action reveals:**
|
||||
- action snapshots
|
||||
- action log
|
||||
- source code location
|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace trace.zip"
|
||||
```
|
||||
|
||||
### Screenshots
|
||||
```bash python
|
||||
playwright show-trace trace.zip
|
||||
```
|
||||
|
||||
When tracing with the [`option: Tracing.start.screenshots`] option turned on (default), each trace records a screencast and renders it as a film strip. You can hover over the film strip to see a magnified image of for each action and state which helps you easily find the action you want to inspect.
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 show-trace trace.zip
|
||||
```
|
||||
|
||||
Double click on an action to see the time range for that action. You can use the slider in the timeline to increase the actions selected and these will be shown in the Actions tab and all console logs and network logs will be filtered to only show the logs for the actions selected.
|
||||
### Using [trace.playwright.dev](https://trace.playwright.dev)
|
||||
|
||||

|
||||
[trace.playwright.dev](https://trace.playwright.dev) is a statically hosted variant of the Trace Viewer. You can upload trace files using drag and drop or via the `Select file(s)` button.
|
||||
|
||||
Trace Viewer loads the trace entirely in your browser and does not transmit any data externally.
|
||||
|
||||
### Snapshots
|
||||
<img width="1119" alt="Drop Playwright Trace to load" src="https://user-images.githubusercontent.com/13063165/194577918-b4d45726-2692-4093-8a28-9e73552617ef.png" />
|
||||
|
||||
When tracing with the [`option: Tracing.start.snapshots`] option turned on (default), Playwright captures a set of complete DOM snapshots for each action. Depending on the type of the action, it will capture:
|
||||
### Viewing remote traces
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
|Before|A snapshot at the time action is called.|
|
||||
|Action|A snapshot at the moment of the performed input. This type of snapshot is especially useful when exploring where exactly Playwright clicked.|
|
||||
|After|A snapshot after the action.|
|
||||
You can open remote traces directly using its URL. This makes it easy to view the remote trace without having to manually download the file from CI runs, for example.
|
||||
|
||||
Here is what the typical Action snapshot looks like:
|
||||
```bash js
|
||||
npx playwright show-trace https://example.com/trace.zip
|
||||
```
|
||||
|
||||

|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace https://example.com/trace.zip"
|
||||
```
|
||||
|
||||
Notice how it highlights both, the DOM Node as well as the exact click position.
|
||||
```bash python
|
||||
playwright show-trace https://example.com/trace.zip
|
||||
```
|
||||
|
||||
### Source
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 show-trace https://example.com/trace.zip
|
||||
```
|
||||
|
||||
When you click on an action in the sidebar, the line of code for that action is highlighted in the source panel.
|
||||
When using [trace.playwright.dev](https://trace.playwright.dev), you can also pass the URL of your uploaded trace at some accessible storage (e.g. inside your CI) as a query parameter. CORS (Cross-Origin Resource Sharing) rules might apply.
|
||||
|
||||

|
||||
```txt
|
||||
https://trace.playwright.dev/?trace=https://demo.playwright.dev/reports/todomvc/data/cb0fa77ebd9487a5c899f3ae65a7ffdbac681182.zip
|
||||
```
|
||||
|
||||
### Call
|
||||
|
||||
The call tab shows you information about the action such as the time it took, what locator was used, if in strict mode and what key was used.
|
||||
|
||||

|
||||
|
||||
### Log
|
||||
|
||||
See a full log of your test to better understand what Playwright is doing behind the scenes such as scrolling into view, waiting for element to be visible, enabled and stable and performing actions such as click, fill, press etc.
|
||||
|
||||

|
||||
|
||||
### Errors
|
||||
|
||||
If your test fails you will see the error messages for each test in the Errors tab. The timeline will also show a red line highlighting where the error occurred. You can also click on the source tab to see on which line of the source code the error is.
|
||||
|
||||

|
||||
|
||||
### Console
|
||||
|
||||
See console logs from the browser as well as from your test. Different icons are displayed to show you if the console log came from the browser or from the test file.
|
||||
|
||||

|
||||
|
||||
Double click on an action from your test in the actions sidebar. This will filter the console to only show the logs that were made during that action. Click the *Show all* button to see all console logs again.
|
||||
|
||||
Use the timeline to filter actions, by clicking a start point and dragging to an ending point. The console tab will also be filtered to only show the logs that were made during the actions selected.
|
||||
|
||||
|
||||
### Network
|
||||
|
||||
The Network tab shows you all the network requests that were made during your test. You can sort by different types of requests, status code, method, request, content type, duration and size. Click on a request to see more information about it such as the request headers, response headers, request body and response body.
|
||||
|
||||

|
||||
|
||||
Double click on an action from your test in the actions sidebar. This will filter the network requests to only show the requests that were made during that action. Click the *Show all* button to see all network requests again.
|
||||
|
||||
Use the timeline to filter actions, by clicking a start point and dragging to an ending point. The network tab will also be filtered to only show the network requests that were made during the actions selected.
|
||||
|
||||
### Metadata
|
||||
|
||||
Next to the Actions tab you will find the Metadata tab which will show you more information on your test such as the Browser, viewport size, test duration and more.
|
||||
|
||||

|
||||
|
||||
### Attachments
|
||||
## Recording a trace
|
||||
* langs: js
|
||||
|
||||
The "Attachments" tab allows you to explore attachments. If you're doing [visual regression testing](./test-snapshots.md), you'll be able to compare screenshots by examining the image diff, the actual image and the expected image. When you click on the expected image you can use the slider to slide one image over the other so you can easily see the differences in your screenshots.
|
||||
|
||||

|
||||
|
||||
|
||||
## Recording a trace locally
|
||||
### Tracing locally
|
||||
* langs: js
|
||||
|
||||
To record a trace during development mode set the `--trace` flag to `on` when running your tests. You can also use [UI Mode](./test-ui-mode.md) for a better developer experience.
|
||||
To record a trace during development mode set the `--trace` flag to `on` when running your tests. You can also use [UI Mode](./test-ui-mode.md) for a better developer experience, as it traces each test automatically.
|
||||
|
||||
```bash
|
||||
npx playwright test --trace on
|
||||
|
|
@ -126,7 +87,7 @@ You can then open the HTML report and click on the trace icon to open the trace.
|
|||
```bash
|
||||
npx playwright show-report
|
||||
```
|
||||
## Recording a trace on CI
|
||||
### Tracing on CI
|
||||
* langs: js
|
||||
|
||||
Traces should be run on continuous integration on the first retry of a failed test
|
||||
|
|
@ -254,6 +215,7 @@ Traces can be recorded using the [`property: BrowserContext.tracing`] API as fol
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -348,6 +310,70 @@ public class UnitTest1 : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp
|
||||
using System.Reflection;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[WithTestName]
|
||||
public class UnitTest1 : PageTest
|
||||
{
|
||||
public override async Task InitializeAsync()
|
||||
{
|
||||
await base.InitializeAsync().ConfigureAwait(false);
|
||||
await Context.Tracing.StartAsync(new()
|
||||
{
|
||||
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
|
||||
Screenshots = true,
|
||||
Snapshots = true,
|
||||
Sources = true
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
await Context.Tracing.StopAsync(new()
|
||||
{
|
||||
Path = Path.Combine(
|
||||
Environment.CurrentDirectory,
|
||||
"playwright-traces",
|
||||
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
|
||||
)
|
||||
});
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
// ...
|
||||
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
|
||||
}
|
||||
}
|
||||
|
||||
public class WithTestNameAttribute : BeforeAfterTestAttribute
|
||||
{
|
||||
public static string CurrentTestName = string.Empty;
|
||||
public static string CurrentClassName = string.Empty;
|
||||
|
||||
public override void Before(MethodInfo methodInfo)
|
||||
{
|
||||
CurrentTestName = methodInfo.Name;
|
||||
CurrentClassName = methodInfo.DeclaringType!.Name;
|
||||
}
|
||||
|
||||
public override void After(MethodInfo methodInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -365,6 +391,7 @@ Setup your tests to record a trace only when the test fails:
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -459,60 +486,165 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp
|
||||
using System.Reflection;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
[WithTestName]
|
||||
public class UnitTest1 : PageTest
|
||||
{
|
||||
public override async Task InitializeAsync()
|
||||
{
|
||||
await base.InitializeAsync().ConfigureAwait(false);
|
||||
await Context.Tracing.StartAsync(new()
|
||||
{
|
||||
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
|
||||
Screenshots = true,
|
||||
Snapshots = true,
|
||||
Sources = true
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
await Context.Tracing.StopAsync(new()
|
||||
{
|
||||
Path = !TestOk ? Path.Combine(
|
||||
Environment.CurrentDirectory,
|
||||
"playwright-traces",
|
||||
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
|
||||
) : null
|
||||
});
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
// ...
|
||||
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
|
||||
}
|
||||
}
|
||||
|
||||
public class WithTestNameAttribute : BeforeAfterTestAttribute
|
||||
{
|
||||
public static string CurrentTestName = string.Empty;
|
||||
public static string CurrentClassName = string.Empty;
|
||||
|
||||
public override void Before(MethodInfo methodInfo)
|
||||
{
|
||||
CurrentTestName = methodInfo.Name;
|
||||
CurrentClassName = methodInfo.DeclaringType!.Name;
|
||||
}
|
||||
|
||||
public override void After(MethodInfo methodInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Opening the trace
|
||||
## Trace Viewer features
|
||||
### Actions
|
||||
|
||||
You can open the saved trace using the Playwright CLI or in your browser on [`trace.playwright.dev`](https://trace.playwright.dev). Make sure to add the full path to where your `trace.zip` file is located.
|
||||
In the Actions tab you can see what locator was used for every action and how long each one took to run. Hover over each action of your test and visually see the change in the DOM snapshot. Go back and forward in time and click an action to inspect and debug. Use the Before and After tabs to visually see what happened before and after the action.
|
||||
|
||||
```bash js
|
||||
npx playwright show-trace path/to/trace.zip
|
||||
```
|
||||

|
||||
|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace trace.zip"
|
||||
```
|
||||
**Selecting each action reveals:**
|
||||
- Action snapshots
|
||||
- Action log
|
||||
- Source code location
|
||||
|
||||
```bash python
|
||||
playwright show-trace trace.zip
|
||||
```
|
||||
### Screenshots
|
||||
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 show-trace trace.zip
|
||||
```
|
||||
When tracing with the [`option: Tracing.start.screenshots`] option turned on (default), each trace records a screencast and renders it as a film strip. You can hover over the film strip to see a magnified image of for each action and state which helps you easily find the action you want to inspect.
|
||||
|
||||
## Using [trace.playwright.dev](https://trace.playwright.dev)
|
||||
Double click on an action to see the time range for that action. You can use the slider in the timeline to increase the actions selected and these will be shown in the Actions tab and all console logs and network logs will be filtered to only show the logs for the actions selected.
|
||||
|
||||
[trace.playwright.dev](https://trace.playwright.dev) is a statically hosted variant of the Trace Viewer. You can upload trace files using drag and drop.
|
||||

|
||||
|
||||
|
||||
<img width="1119" alt="Drop Playwright Trace to load" src="https://user-images.githubusercontent.com/13063165/194577918-b4d45726-2692-4093-8a28-9e73552617ef.png" />
|
||||
### Snapshots
|
||||
|
||||
## Viewing remote traces
|
||||
When tracing with the [`option: Tracing.start.snapshots`] option turned on (default), Playwright captures a set of complete DOM snapshots for each action. Depending on the type of the action, it will capture:
|
||||
|
||||
You can open remote traces using its URL. They could be generated on a CI run which makes it easy to view the remote trace without having to manually download the file.
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
|Before|A snapshot at the time action is called.|
|
||||
|Action|A snapshot at the moment of the performed input. This type of snapshot is especially useful when exploring where exactly Playwright clicked.|
|
||||
|After|A snapshot after the action.|
|
||||
|
||||
```bash js
|
||||
npx playwright show-trace https://example.com/trace.zip
|
||||
```
|
||||
Here is what the typical Action snapshot looks like:
|
||||
|
||||
```bash java
|
||||
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="show-trace https://example.com/trace.zip"
|
||||
```
|
||||

|
||||
|
||||
```bash python
|
||||
playwright show-trace https://example.com/trace.zip
|
||||
```
|
||||
Notice how it highlights both, the DOM Node as well as the exact click position.
|
||||
|
||||
```bash csharp
|
||||
pwsh bin/Debug/netX/playwright.ps1 show-trace https://example.com/trace.zip
|
||||
```
|
||||
### Source
|
||||
|
||||
When you click on an action in the sidebar, the line of code for that action is highlighted in the source panel.
|
||||
|
||||

|
||||
|
||||
### Call
|
||||
|
||||
The call tab shows you information about the action such as the time it took, what locator was used, if in strict mode and what key was used.
|
||||
|
||||

|
||||
|
||||
### Log
|
||||
|
||||
See a full log of your test to better understand what Playwright is doing behind the scenes such as scrolling into view, waiting for element to be visible, enabled and stable and performing actions such as click, fill, press etc.
|
||||
|
||||

|
||||
|
||||
### Errors
|
||||
|
||||
If your test fails you will see the error messages for each test in the Errors tab. The timeline will also show a red line highlighting where the error occurred. You can also click on the source tab to see on which line of the source code the error is.
|
||||
|
||||

|
||||
|
||||
### Console
|
||||
|
||||
See console logs from the browser as well as from your test. Different icons are displayed to show you if the console log came from the browser or from the test file.
|
||||
|
||||

|
||||
|
||||
Double click on an action from your test in the actions sidebar. This will filter the console to only show the logs that were made during that action. Click the *Show all* button to see all console logs again.
|
||||
|
||||
Use the timeline to filter actions, by clicking a start point and dragging to an ending point. The console tab will also be filtered to only show the logs that were made during the actions selected.
|
||||
|
||||
|
||||
You can also pass the URL of your uploaded trace (e.g. inside your CI) from some accessible storage as a parameter. CORS (Cross-Origin Resource Sharing) rules might apply.
|
||||
### Network
|
||||
|
||||
```txt
|
||||
https://trace.playwright.dev/?trace=https://demo.playwright.dev/reports/todomvc/data/cb0fa77ebd9487a5c899f3ae65a7ffdbac681182.zip
|
||||
```
|
||||
The Network tab shows you all the network requests that were made during your test. You can sort by different types of requests, status code, method, request, content type, duration and size. Click on a request to see more information about it such as the request headers, response headers, request body and response body.
|
||||
|
||||

|
||||
|
||||
Double click on an action from your test in the actions sidebar. This will filter the network requests to only show the requests that were made during that action. Click the *Show all* button to see all network requests again.
|
||||
|
||||
Use the timeline to filter actions, by clicking a start point and dragging to an ending point. The network tab will also be filtered to only show the network requests that were made during the actions selected.
|
||||
|
||||
### Metadata
|
||||
|
||||
Next to the Actions tab you will find the Metadata tab which will show you more information on your test such as the Browser, viewport size, test duration and more.
|
||||
|
||||

|
||||
|
||||
### Attachments
|
||||
* langs: js
|
||||
|
||||
The "Attachments" tab allows you to explore attachments. If you're doing [visual regression testing](./test-snapshots.md), you'll be able to compare screenshots by examining the image diff, the actual image and the expected image. When you click on the expected image you can use the slider to slide one image over the other so you can easily see the differences in your screenshots.
|
||||
|
||||

|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Take a look at the following example to see how to write a test.
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -117,6 +118,40 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task HasTitle()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetStartedLink()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
|
||||
// Click the get started link.
|
||||
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
|
||||
|
||||
// Expects page to have a heading with the name of Installation.
|
||||
await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -204,6 +239,7 @@ The Playwright NUnit and MSTest test framework base classes will isolate each te
|
|||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
|
@ -248,23 +284,43 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task BasicTest()
|
||||
{
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Using Test Hooks
|
||||
|
||||
You can use `SetUp`/`TearDown` in NUnit or `TestInitialize`/`TestCleanup` in MSTest to prepare and clean up your test environment:
|
||||
|
||||
<Tabs
|
||||
groupId="test-runners"
|
||||
defaultValue="mstest"
|
||||
values={[
|
||||
{label: 'MSTest', value: 'mstest'},
|
||||
{label: 'NUnit', value: 'nunit'},
|
||||
{label: 'xUnit', value: 'xunit'},
|
||||
]
|
||||
}>
|
||||
<TabItem value="nunit">
|
||||
|
||||
You can use `SetUp`/`TearDown` to prepare and clean up your test environment:
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Playwright.NUnit;
|
||||
|
|
@ -294,6 +350,8 @@ public class ExampleTest : PageTest
|
|||
</TabItem>
|
||||
<TabItem value="mstest">
|
||||
|
||||
You can use `TestInitialize`/`TestCleanup` to prepare and clean up your test environment:
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Playwright.MSTest;
|
||||
|
|
@ -319,6 +377,39 @@ public class ExampleTest : PageTest
|
|||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="xunit">
|
||||
|
||||
You can use `InitializeAsync`/`DisposeAsync` to prepare and clean up your test environment:
|
||||
|
||||
```csharp title="UnitTest1.cs"
|
||||
using Microsoft.Playwright;
|
||||
using Microsoft.Playwright.Xunit;
|
||||
|
||||
namespace PlaywrightTests;
|
||||
|
||||
public class UnitTest1: PageTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task MainNavigation()
|
||||
{
|
||||
// Assertions use the expect API.
|
||||
await Expect(Page).ToHaveURLAsync("https://playwright.dev/");
|
||||
}
|
||||
|
||||
override public async Task InitializeAsync()
|
||||
{
|
||||
await base.InitializeAsync();
|
||||
await Page.GotoAsync("https://playwright.dev");
|
||||
}
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
Console.WriteLine("After each test cleanup");
|
||||
await base.DisposeAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -328,4 +419,4 @@ public class ExampleTest : PageTest
|
|||
- [Generate tests with Codegen](./codegen-intro.md)
|
||||
- [See a trace of your tests](./trace-viewer-intro.md)
|
||||
- [Run tests on CI](./ci-intro.md)
|
||||
- [Learn more about the MSTest and NUnit base classes](./test-runners.md)
|
||||
- [Learn more about the MSTest, NUnit, or xUnit base classes](./test-runners.md)
|
||||
|
|
|
|||
573
package-lock.json
generated
573
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "playwright-internal",
|
||||
"private": true,
|
||||
"version": "1.49.0-next",
|
||||
"version": "1.50.0-next",
|
||||
"description": "A high-level API to automate web browsers",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
"@types/codemirror": "^5.60.7",
|
||||
"@types/formidable": "^2.0.4",
|
||||
"@types/immutable": "^3.8.7",
|
||||
"@types/node": "^18.19.39",
|
||||
"@types/node": "^18.19.68",
|
||||
"@types/react": "^18.0.12",
|
||||
"@types/react-dom": "^18.0.5",
|
||||
"@types/ws": "^8.5.3",
|
||||
|
|
@ -99,10 +99,10 @@
|
|||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"ssim.js": "^3.5.0",
|
||||
"typescript": "^5.5.3",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^5.4.6",
|
||||
"ws": "^8.17.1",
|
||||
"xml2js": "^0.5.0",
|
||||
"yaml": "^2.5.1"
|
||||
"yaml": "^2.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,5 @@
|
|||
"name": "html-reporter",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build && tsc",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-to-html": "^0.7.2"
|
||||
}
|
||||
"type": "module"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,3 +48,14 @@ test('setExpanded is called', async ({ mount }) => {
|
|||
await component.getByText('Title').click();
|
||||
expect(expandedValues).toEqual([true]);
|
||||
});
|
||||
|
||||
test('setExpanded should work', async ({ mount }) => {
|
||||
const component = await mount(<AutoChip header='Title' initialExpanded={false}>
|
||||
Body
|
||||
</AutoChip>);
|
||||
await component.getByText('Title').click();
|
||||
await expect(component).toMatchAriaSnapshot(`
|
||||
- button "Title" [expanded]
|
||||
- region: Body
|
||||
`);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import './colors.css';
|
|||
import './common.css';
|
||||
import * as icons from './icons';
|
||||
import { clsx } from '@web/uiUtils';
|
||||
import { type AnchorID, useAnchor } from './links';
|
||||
|
||||
export const Chip: React.FC<{
|
||||
header: JSX.Element | string,
|
||||
|
|
@ -28,10 +29,13 @@ export const Chip: React.FC<{
|
|||
setExpanded?: (expanded: boolean) => void,
|
||||
children?: any,
|
||||
dataTestId?: string,
|
||||
targetRef?: React.RefObject<HTMLDivElement>,
|
||||
}> = ({ header, expanded, setExpanded, children, noInsets, dataTestId, targetRef }) => {
|
||||
return <div className='chip' data-testid={dataTestId} ref={targetRef}>
|
||||
}> = ({ header, expanded, setExpanded, children, noInsets, dataTestId }) => {
|
||||
const id = React.useId();
|
||||
return <div className='chip' data-testid={dataTestId}>
|
||||
<div
|
||||
role='button'
|
||||
aria-expanded={!!expanded}
|
||||
aria-controls={id}
|
||||
className={clsx('chip-header', setExpanded && ' expanded-' + expanded)}
|
||||
onClick={() => setExpanded?.(!expanded)}
|
||||
title={typeof header === 'string' ? header : undefined}>
|
||||
|
|
@ -39,7 +43,7 @@ export const Chip: React.FC<{
|
|||
{setExpanded && !expanded && icons.rightArrow()}
|
||||
{header}
|
||||
</div>
|
||||
{(!setExpanded || expanded) && <div className={clsx('chip-body', noInsets && 'chip-body-no-insets')}>{children}</div>}
|
||||
{(!setExpanded || expanded) && <div id={id} role='region' className={clsx('chip-body', noInsets && 'chip-body-no-insets')}>{children}</div>}
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
|
@ -49,16 +53,17 @@ export const AutoChip: React.FC<{
|
|||
noInsets?: boolean,
|
||||
children?: any,
|
||||
dataTestId?: string,
|
||||
targetRef?: React.RefObject<HTMLDivElement>,
|
||||
}> = ({ header, initialExpanded, noInsets, children, dataTestId, targetRef }) => {
|
||||
const [expanded, setExpanded] = React.useState(initialExpanded || initialExpanded === undefined);
|
||||
revealOnAnchorId?: AnchorID,
|
||||
}> = ({ header, initialExpanded, noInsets, children, dataTestId, revealOnAnchorId }) => {
|
||||
const [expanded, setExpanded] = React.useState(initialExpanded ?? true);
|
||||
const onReveal = React.useCallback(() => setExpanded(true), []);
|
||||
useAnchor(revealOnAnchorId, onReveal);
|
||||
return <Chip
|
||||
header={header}
|
||||
expanded={expanded}
|
||||
setExpanded={setExpanded}
|
||||
noInsets={noInsets}
|
||||
dataTestId={dataTestId}
|
||||
targetRef={targetRef}
|
||||
>
|
||||
{children}
|
||||
</Chip>;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ svg {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.d-flex {
|
||||
display: flex !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ test('should render counters', async ({ mount }) => {
|
|||
await expect(component.locator('a', { hasText: 'Failed' }).locator('.counter')).toHaveText('31');
|
||||
await expect(component.locator('a', { hasText: 'Flaky' }).locator('.counter')).toHaveText('17');
|
||||
await expect(component.locator('a', { hasText: 'Skipped' }).locator('.counter')).toHaveText('10');
|
||||
await expect(component).toMatchAriaSnapshot(`
|
||||
- navigation:
|
||||
- link "All 90"
|
||||
- text: Passed 42 Failed 31 Flaky 17 Skipped 10
|
||||
`);
|
||||
});
|
||||
|
||||
test('should toggle filters', async ({ page, mount }) => {
|
||||
|
|
@ -59,6 +64,6 @@ test('should toggle filters', async ({ page, mount }) => {
|
|||
await expect(page).toHaveURL(/#\?q=s:flaky/);
|
||||
await component.locator('a', { hasText: 'Skipped' }).click();
|
||||
await expect(page).toHaveURL(/#\?q=s:skipped/);
|
||||
await component.getByRole('searchbox').fill('annot:annotation type=annotation description');
|
||||
await component.getByRole('textbox').fill('annot:annotation type=annotation description');
|
||||
expect(filters).toEqual(['', 's:passed', 's:failed', 's:flaky', 's:skipped', 'annot:annotation type=annotation description']);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,12 +49,14 @@ export const HeaderView: React.FC<React.PropsWithChildren<{
|
|||
<form className='subnav-search' onSubmit={
|
||||
event => {
|
||||
event.preventDefault();
|
||||
navigate(`#?q=${filterText ? encodeURIComponent(filterText) : ''}`);
|
||||
const url = new URL(window.location.href);
|
||||
url.hash = filterText ? '?' + new URLSearchParams({ q: filterText }) : '';
|
||||
navigate(url);
|
||||
}
|
||||
}>
|
||||
{icons.search()}
|
||||
{/* Use navigationId to reset defaultValue */}
|
||||
<input type='search' spellCheck={false} className='form-control subnav-search-input input-contrast width-full' value={filterText} onChange={e => {
|
||||
<input spellCheck={false} className='form-control subnav-search-input input-contrast width-full' value={filterText} onChange={e => {
|
||||
setFilterText(e.target.value);
|
||||
}}></input>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import type { HTMLReport } from './types';
|
||||
import type zip from '@zip.js/zip.js';
|
||||
import type * as zip from '@zip.js/zip.js';
|
||||
// @ts-ignore
|
||||
import * as zipImport from '@zip.js/zip.js/lib/zip-no-worker-inflate.js';
|
||||
import * as React from 'react';
|
||||
|
|
@ -50,12 +50,33 @@ window.onload = () => {
|
|||
ReactDOM.createRoot(document.querySelector('#root')!).render(<ReportLoader />);
|
||||
};
|
||||
|
||||
const kPlaywrightReportStorageForHMR = 'playwrightReportStorageForHMR';
|
||||
|
||||
class ZipReport implements LoadedReport {
|
||||
private _entries = new Map<string, zip.Entry>();
|
||||
private _json!: HTMLReport;
|
||||
|
||||
async load() {
|
||||
const zipReader = new zipjs.ZipReader(new zipjs.Data64URIReader(window.playwrightReportBase64!), { useWebWorkers: false });
|
||||
const zipURI = await new Promise<string>(resolve => {
|
||||
if (window.playwrightReportBase64)
|
||||
return resolve(window.playwrightReportBase64);
|
||||
if (window.opener) {
|
||||
window.addEventListener('message', event => {
|
||||
if (event.source === window.opener) {
|
||||
localStorage.setItem(kPlaywrightReportStorageForHMR, event.data);
|
||||
resolve(event.data);
|
||||
}
|
||||
}, { once: true });
|
||||
window.opener.postMessage('ready', '*');
|
||||
} else {
|
||||
const oldReport = localStorage.getItem(kPlaywrightReportStorageForHMR);
|
||||
if (oldReport)
|
||||
return resolve(oldReport);
|
||||
alert('couldnt find report, something with HMR is broken');
|
||||
}
|
||||
});
|
||||
|
||||
const zipReader = new zipjs.ZipReader(new zipjs.Data64URIReader(zipURI), { useWebWorkers: false });
|
||||
for (const entry of await zipReader.getEntries())
|
||||
this._entries.set(entry.filename, entry);
|
||||
this._json = await this.entry('report.json') as HTMLReport;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import type { TestAttachment } from './types';
|
||||
import type { TestAttachment, TestCase, TestCaseSummary, TestResult, TestResultSummary } from './types';
|
||||
import * as React from 'react';
|
||||
import * as icons from './icons';
|
||||
import { TreeItem } from './treeItem';
|
||||
|
|
@ -23,7 +23,7 @@ import './links.css';
|
|||
import { linkifyText } from '@web/renderUtils';
|
||||
import { clsx } from '@web/uiUtils';
|
||||
|
||||
export function navigate(href: string) {
|
||||
export function navigate(href: string | URL) {
|
||||
window.history.pushState({}, '', href);
|
||||
const navEvent = new PopStateEvent('popstate');
|
||||
window.dispatchEvent(navEvent);
|
||||
|
|
@ -72,6 +72,7 @@ export const AttachmentLink: React.FunctionComponent<{
|
|||
linkName?: string,
|
||||
openInNewTab?: boolean,
|
||||
}> = ({ attachment, href, linkName, openInNewTab }) => {
|
||||
const isAnchored = useIsAnchored('attachment-' + attachment.name);
|
||||
return <TreeItem title={<span>
|
||||
{attachment.contentType === kMissingContentType ? icons.warning() : icons.attachment()}
|
||||
{attachment.path && <a href={href || attachment.path} download={downloadFileNameForAttachment(attachment)}>{linkName || attachment.name}</a>}
|
||||
|
|
@ -82,7 +83,7 @@ export const AttachmentLink: React.FunctionComponent<{
|
|||
)}
|
||||
</span>} loadChildren={attachment.body ? () => {
|
||||
return [<div key={1} className='attachment-body'><CopyToClipboard value={attachment.body!}/>{linkifyText(attachment.body!)}</div>];
|
||||
} : undefined} depth={0} style={{ lineHeight: '32px' }}></TreeItem>;
|
||||
} : undefined} depth={0} style={{ lineHeight: '32px' }} selected={isAnchored}></TreeItem>;
|
||||
};
|
||||
|
||||
export const SearchParamsContext = React.createContext<URLSearchParams>(new URLSearchParams(window.location.hash.slice(1)));
|
||||
|
|
@ -113,3 +114,49 @@ export function generateTraceUrl(traces: TestAttachment[]) {
|
|||
}
|
||||
|
||||
const kMissingContentType = 'x-playwright/missing';
|
||||
|
||||
export type AnchorID = string | string[] | ((id: string) => boolean) | undefined;
|
||||
|
||||
export function useAnchor(id: AnchorID, onReveal: () => void) {
|
||||
const searchParams = React.useContext(SearchParamsContext);
|
||||
const isAnchored = useIsAnchored(id);
|
||||
React.useEffect(() => {
|
||||
if (isAnchored)
|
||||
onReveal();
|
||||
}, [isAnchored, onReveal, searchParams]);
|
||||
}
|
||||
|
||||
export function useIsAnchored(id: AnchorID) {
|
||||
const searchParams = React.useContext(SearchParamsContext);
|
||||
const anchor = searchParams.get('anchor');
|
||||
if (anchor === null)
|
||||
return false;
|
||||
if (typeof id === 'undefined')
|
||||
return false;
|
||||
if (typeof id === 'string')
|
||||
return id === anchor;
|
||||
if (Array.isArray(id))
|
||||
return id.includes(anchor);
|
||||
return id(anchor);
|
||||
}
|
||||
|
||||
export function Anchor({ id, children }: React.PropsWithChildren<{ id: AnchorID }>) {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const onAnchorReveal = React.useCallback(() => {
|
||||
ref.current?.scrollIntoView({ block: 'start', inline: 'start' });
|
||||
}, []);
|
||||
useAnchor(id, onAnchorReveal);
|
||||
|
||||
return <div ref={ref}>{children}</div>;
|
||||
}
|
||||
|
||||
export function testResultHref({ test, result, anchor }: { test?: TestCase | TestCaseSummary, result?: TestResult | TestResultSummary, anchor?: string }) {
|
||||
const params = new URLSearchParams();
|
||||
if (test)
|
||||
params.set('testId', test.testId);
|
||||
if (test && result)
|
||||
params.set('run', '' + test.results.indexOf(result as any));
|
||||
if (anchor)
|
||||
params.set('anchor', anchor);
|
||||
return `#?` + params;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,6 @@ const TestCaseViewLoader: React.FC<{
|
|||
const searchParams = React.useContext(SearchParamsContext);
|
||||
const [test, setTest] = React.useState<TestCase | undefined>();
|
||||
const testId = searchParams.get('testId');
|
||||
const anchor = (searchParams.get('anchor') || '') as 'video' | 'diff' | '';
|
||||
const run = +(searchParams.get('run') || '0');
|
||||
|
||||
const { prev, next } = React.useMemo(() => {
|
||||
|
|
@ -133,7 +132,6 @@ const TestCaseViewLoader: React.FC<{
|
|||
next={next}
|
||||
prev={prev}
|
||||
test={test}
|
||||
anchor={anchor}
|
||||
run={run}
|
||||
/>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ const testCase: TestCase = {
|
|||
};
|
||||
|
||||
test('should render test case', async ({ mount }) => {
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={testCase} prev={undefined} next={undefined} run={0} anchor=''></TestCaseView>);
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={testCase} prev={undefined} next={undefined} run={0}></TestCaseView>);
|
||||
await expect(component.getByText('Annotation text', { exact: false }).first()).toBeVisible();
|
||||
await expect(component.getByText('Hidden annotation')).toBeHidden();
|
||||
await component.getByText('Annotations').click();
|
||||
|
|
@ -79,7 +79,7 @@ test('should render test case', async ({ mount }) => {
|
|||
test('should render copy buttons for annotations', async ({ mount, page, context }) => {
|
||||
await context.grantPermissions(['clipboard-read', 'clipboard-write']);
|
||||
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={testCase} prev={undefined} next={undefined} run={0} anchor=''></TestCaseView>);
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={testCase} prev={undefined} next={undefined} run={0}></TestCaseView>);
|
||||
await expect(component.getByText('Annotation text', { exact: false }).first()).toBeVisible();
|
||||
await component.getByText('Annotation text', { exact: false }).first().hover();
|
||||
await expect(component.locator('.test-case-annotation').getByLabel('Copy to clipboard').first()).toBeVisible();
|
||||
|
|
@ -108,7 +108,7 @@ const annotationLinkRenderingTestCase: TestCase = {
|
|||
};
|
||||
|
||||
test('should correctly render links in annotations', async ({ mount }) => {
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={annotationLinkRenderingTestCase} prev={undefined} next={undefined} run={0} anchor=''></TestCaseView>);
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={annotationLinkRenderingTestCase} prev={undefined} next={undefined} run={0}></TestCaseView>);
|
||||
|
||||
const firstLink = await component.getByText('https://playwright.dev/docs/intro').first();
|
||||
await expect(firstLink).toBeVisible();
|
||||
|
|
@ -154,7 +154,7 @@ const resultWithAttachment: TestResult = {
|
|||
const attachmentLinkRenderingTestCase: TestCase = {
|
||||
testId: 'testid',
|
||||
title: 'My test',
|
||||
path: [],
|
||||
path: ['group'],
|
||||
projectName: 'chromium',
|
||||
location: { file: 'test.spec.ts', line: 42, column: 0 },
|
||||
tags: [],
|
||||
|
|
@ -181,24 +181,32 @@ const testCaseSummary: TestCaseSummary = {
|
|||
|
||||
|
||||
test('should correctly render links in attachments', async ({ mount }) => {
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={attachmentLinkRenderingTestCase} prev={undefined} next={undefined} run={0} anchor=''></TestCaseView>);
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={attachmentLinkRenderingTestCase} prev={undefined} next={undefined} run={0}></TestCaseView>);
|
||||
await component.getByText('first attachment').click();
|
||||
const body = await component.getByText('The body with https://playwright.dev/docs/intro link');
|
||||
await expect(body).toBeVisible();
|
||||
await expect(body.locator('a').filter({ hasText: 'playwright.dev' })).toHaveAttribute('href', 'https://playwright.dev/docs/intro');
|
||||
await expect(body.locator('a').filter({ hasText: 'github.com' })).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
|
||||
await expect(component).toMatchAriaSnapshot(`
|
||||
- link "https://playwright.dev/docs/intro"
|
||||
- link "https://github.com/microsoft/playwright/issues/31284"
|
||||
`);
|
||||
});
|
||||
|
||||
test('should correctly render links in attachment name', async ({ mount }) => {
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={attachmentLinkRenderingTestCase} prev={undefined} next={undefined} run={0} anchor=''></TestCaseView>);
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={attachmentLinkRenderingTestCase} prev={undefined} next={undefined} run={0}></TestCaseView>);
|
||||
const link = component.getByText('attachment with inline link').locator('a');
|
||||
await expect(link).toHaveAttribute('href', 'https://github.com/microsoft/playwright/issues/31284');
|
||||
await expect(link).toHaveText('https://github.com/microsoft/playwright/issues/31284');
|
||||
await expect(component).toMatchAriaSnapshot(`
|
||||
- link /https:\\/\\/github\\.com\\/microsoft\\/playwright\\/issues\\/\\d+/
|
||||
`);
|
||||
});
|
||||
|
||||
test('should correctly render prev and next', async ({ mount }) => {
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={attachmentLinkRenderingTestCase} prev={testCaseSummary} next={testCaseSummary} run={0} anchor=''></TestCaseView>);
|
||||
const component = await mount(<TestCaseView projectNames={['chromium', 'webkit']} test={attachmentLinkRenderingTestCase} prev={testCaseSummary} next={testCaseSummary} run={0}></TestCaseView>);
|
||||
await expect(component).toMatchAriaSnapshot(`
|
||||
- text: group
|
||||
- link "« previous"
|
||||
- link "next »"
|
||||
- text: "My test test.spec.ts:42 10ms"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import * as React from 'react';
|
|||
import { TabbedPane } from './tabbedPane';
|
||||
import { AutoChip } from './chip';
|
||||
import './common.css';
|
||||
import { Link, ProjectLink, SearchParamsContext } from './links';
|
||||
import { Link, ProjectLink, SearchParamsContext, testResultHref } from './links';
|
||||
import { statusIcon } from './statusIcon';
|
||||
import './testCaseView.css';
|
||||
import { TestResultView } from './testResultView';
|
||||
|
|
@ -33,9 +33,8 @@ export const TestCaseView: React.FC<{
|
|||
test: TestCase | undefined,
|
||||
next: TestCaseSummary | undefined,
|
||||
prev: TestCaseSummary | undefined,
|
||||
anchor: 'video' | 'diff' | '',
|
||||
run: number,
|
||||
}> = ({ projectNames, test, run, anchor, next, prev }) => {
|
||||
}> = ({ projectNames, test, run, next, prev }) => {
|
||||
const [selectedResultIndex, setSelectedResultIndex] = React.useState(run);
|
||||
const searchParams = React.useContext(SearchParamsContext);
|
||||
const filterParam = searchParams.has('q') ? '&q=' + searchParams.get('q') : '';
|
||||
|
|
@ -51,12 +50,13 @@ export const TestCaseView: React.FC<{
|
|||
}, [test?.annotations]);
|
||||
|
||||
return <div className='test-case-column vbox'>
|
||||
<div className='hbox'>
|
||||
{prev && <Link href={`#?testId=${prev.testId}${filterParam}`}>« previous</Link>}
|
||||
{test && <div className='hbox'>
|
||||
<div className='test-case-path'>{test.path.join(' › ')}</div>
|
||||
<div style={{ flex: 'auto' }}></div>
|
||||
{next && <Link href={`#?testId=${next.testId}${filterParam}`}>next »</Link>}
|
||||
</div>
|
||||
{test && <div className='test-case-path'>{test.path.join(' › ')}</div>}
|
||||
<div className={clsx(!prev && 'hidden')}><Link href={testResultHref({ test: prev }) + filterParam}>« previous</Link></div>
|
||||
<div style={{ width: 10 }}></div>
|
||||
<div className={clsx(!next && 'hidden')}><Link href={testResultHref({ test: next }) + filterParam}>next »</Link></div>
|
||||
</div>}
|
||||
{test && <div className='test-case-title'>{test?.title}</div>}
|
||||
{test && <div className='hbox'>
|
||||
<div className='test-case-location'>
|
||||
|
|
@ -78,7 +78,7 @@ export const TestCaseView: React.FC<{
|
|||
test.results.map((result, index) => ({
|
||||
id: String(index),
|
||||
title: <div style={{ display: 'flex', alignItems: 'center' }}>{statusIcon(result.status)} {retryLabel(index)}</div>,
|
||||
render: () => <TestResultView test={test!} result={result} anchor={anchor}></TestResultView>
|
||||
render: () => <TestResultView test={test!} result={result} />
|
||||
})) || []} selectedTab={String(selectedResultIndex)} setSelectedTab={id => setSelectedResultIndex(+id)} />}
|
||||
</div>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
@import '@web/third_party/vscode/colors.css';
|
||||
|
||||
.test-error-view {
|
||||
white-space: pre;
|
||||
overflow: auto;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import ansi2html from 'ansi-to-html';
|
||||
import { ansi2html } from '@web/ansi2html';
|
||||
import * as React from 'react';
|
||||
import './testErrorView.css';
|
||||
import type { ImageDiff } from '@web/shared/imageDiffView';
|
||||
|
|
@ -25,7 +25,7 @@ export const TestErrorView: React.FC<{
|
|||
testId?: string;
|
||||
}> = ({ error, testId }) => {
|
||||
const html = React.useMemo(() => ansiErrorToHtml(error), [error]);
|
||||
return <div className='test-error-view test-error-text' data-testId={testId} dangerouslySetInnerHTML={{ __html: html || '' }}></div>;
|
||||
return <div className='test-error-view test-error-text' data-testid={testId} dangerouslySetInnerHTML={{ __html: html || '' }}></div>;
|
||||
};
|
||||
|
||||
export const TestScreenshotErrorView: React.FC<{
|
||||
|
|
@ -43,33 +43,9 @@ export const TestScreenshotErrorView: React.FC<{
|
|||
};
|
||||
|
||||
function ansiErrorToHtml(text?: string): string {
|
||||
const config: any = {
|
||||
const defaultColors = {
|
||||
bg: 'var(--color-canvas-subtle)',
|
||||
fg: 'var(--color-fg-default)',
|
||||
};
|
||||
config.colors = ansiColors;
|
||||
return new ansi2html(config).toHtml(escapeHTML(text || ''));
|
||||
}
|
||||
|
||||
const ansiColors = {
|
||||
0: '#000',
|
||||
1: '#C00',
|
||||
2: '#0C0',
|
||||
3: '#C50',
|
||||
4: '#00C',
|
||||
5: '#C0C',
|
||||
6: '#0CC',
|
||||
7: '#CCC',
|
||||
8: '#555',
|
||||
9: '#F55',
|
||||
10: '#5F5',
|
||||
11: '#FF5',
|
||||
12: '#55F',
|
||||
13: '#F5F',
|
||||
14: '#5FF',
|
||||
15: '#FFF'
|
||||
};
|
||||
|
||||
function escapeHTML(text: string): string {
|
||||
return text.replace(/[&"<>]/g, c => ({ '&': '&', '"': '"', '<': '<', '>': '>' }[c]!));
|
||||
return ansi2html(text || '', defaultColors);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import * as React from 'react';
|
|||
import { hashStringToInt, msToString } from './utils';
|
||||
import { Chip } from './chip';
|
||||
import { filterWithToken } from './filter';
|
||||
import { generateTraceUrl, Link, navigate, ProjectLink, SearchParamsContext } from './links';
|
||||
import { generateTraceUrl, Link, navigate, ProjectLink, SearchParamsContext, testResultHref } from './links';
|
||||
import { statusIcon } from './statusIcon';
|
||||
import './testFileView.css';
|
||||
import { video, image, trace } from './icons';
|
||||
|
|
@ -48,7 +48,7 @@ export const TestFileView: React.FC<React.PropsWithChildren<{
|
|||
{statusIcon(test.outcome)}
|
||||
</span>
|
||||
<span>
|
||||
<Link href={`#?testId=${test.testId}${filterParam}`} title={[...test.path, test.title].join(' › ')}>
|
||||
<Link href={testResultHref({ test }) + filterParam} title={[...test.path, test.title].join(' › ')}>
|
||||
<span className='test-file-title'>{[...test.path, test.title].join(' › ')}</span>
|
||||
</Link>
|
||||
{projectNames.length > 1 && !!test.projectName &&
|
||||
|
|
@ -59,7 +59,7 @@ export const TestFileView: React.FC<React.PropsWithChildren<{
|
|||
<span data-testid='test-duration' style={{ minWidth: '50px', textAlign: 'right' }}>{msToString(test.duration)}</span>
|
||||
</div>
|
||||
<div className='test-file-details-row'>
|
||||
<Link href={`#?testId=${test.testId}`} title={[...test.path, test.title].join(' › ')} className='test-file-path-link'>
|
||||
<Link href={testResultHref({ test })} title={[...test.path, test.title].join(' › ')} className='test-file-path-link'>
|
||||
<span className='test-file-path'>{test.location.file}:{test.location.line}</span>
|
||||
</Link>
|
||||
{imageDiffBadge(test)}
|
||||
|
|
@ -72,15 +72,17 @@ export const TestFileView: React.FC<React.PropsWithChildren<{
|
|||
};
|
||||
|
||||
function imageDiffBadge(test: TestCaseSummary): JSX.Element | undefined {
|
||||
const resultWithImageDiff = test.results.find(result => result.attachments.some(attachment => {
|
||||
return attachment.contentType.startsWith('image/') && !!attachment.name.match(/-(expected|actual|diff)/);
|
||||
}));
|
||||
return resultWithImageDiff ? <Link href={`#?testId=${test.testId}&anchor=diff&run=${test.results.indexOf(resultWithImageDiff)}`} title='View images' className='test-file-badge'>{image()}</Link> : undefined;
|
||||
for (const result of test.results) {
|
||||
for (const attachment of result.attachments) {
|
||||
if (attachment.contentType.startsWith('image/') && !!attachment.name.match(/-(expected|actual|diff)/))
|
||||
return <Link href={testResultHref({ test, result, anchor: `attachment-${attachment.name}` })} title='View images' className='test-file-badge'>{image()}</Link>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function videoBadge(test: TestCaseSummary): JSX.Element | undefined {
|
||||
const resultWithVideo = test.results.find(result => result.attachments.some(attachment => attachment.name === 'video'));
|
||||
return resultWithVideo ? <Link href={`#?testId=${test.testId}&anchor=video&run=${test.results.indexOf(resultWithVideo)}`} title='View video' className='test-file-badge'>{video()}</Link> : undefined;
|
||||
return resultWithVideo ? <Link href={testResultHref({ test, result: resultWithVideo, anchor: 'attachment-video' })} title='View video' className='test-file-badge'>{video()}</Link> : undefined;
|
||||
}
|
||||
|
||||
function traceBadge(test: TestCaseSummary): JSX.Element | undefined {
|
||||
|
|
|
|||
|
|
@ -20,15 +20,20 @@ import { TreeItem } from './treeItem';
|
|||
import { msToString } from './utils';
|
||||
import { AutoChip } from './chip';
|
||||
import { traceImage } from './images';
|
||||
import { AttachmentLink, generateTraceUrl } from './links';
|
||||
import { Anchor, AttachmentLink, generateTraceUrl, testResultHref } from './links';
|
||||
import { statusIcon } from './statusIcon';
|
||||
import type { ImageDiff } from '@web/shared/imageDiffView';
|
||||
import { ImageDiffView } from '@web/shared/imageDiffView';
|
||||
import { TestErrorView, TestScreenshotErrorView } from './testErrorView';
|
||||
import * as icons from './icons';
|
||||
import './testResultView.css';
|
||||
|
||||
function groupImageDiffs(screenshots: Set<TestAttachment>): ImageDiff[] {
|
||||
const snapshotNameToImageDiff = new Map<string, ImageDiff>();
|
||||
interface ImageDiffWithAnchors extends ImageDiff {
|
||||
anchors: string[];
|
||||
}
|
||||
|
||||
function groupImageDiffs(screenshots: Set<TestAttachment>): ImageDiffWithAnchors[] {
|
||||
const snapshotNameToImageDiff = new Map<string, ImageDiffWithAnchors>();
|
||||
for (const attachment of screenshots) {
|
||||
const match = attachment.name.match(/^(.*)-(expected|actual|diff|previous)(\.[^.]+)?$/);
|
||||
if (!match)
|
||||
|
|
@ -37,9 +42,10 @@ function groupImageDiffs(screenshots: Set<TestAttachment>): ImageDiff[] {
|
|||
const snapshotName = name + extension;
|
||||
let imageDiff = snapshotNameToImageDiff.get(snapshotName);
|
||||
if (!imageDiff) {
|
||||
imageDiff = { name: snapshotName };
|
||||
imageDiff = { name: snapshotName, anchors: [`attachment-${name}`] };
|
||||
snapshotNameToImageDiff.set(snapshotName, imageDiff);
|
||||
}
|
||||
imageDiff.anchors.push(`attachment-${attachment.name}`);
|
||||
if (category === 'actual')
|
||||
imageDiff.actual = { attachment };
|
||||
if (category === 'expected')
|
||||
|
|
@ -64,36 +70,21 @@ function groupImageDiffs(screenshots: Set<TestAttachment>): ImageDiff[] {
|
|||
export const TestResultView: React.FC<{
|
||||
test: TestCase,
|
||||
result: TestResult,
|
||||
anchor: 'video' | 'diff' | '',
|
||||
}> = ({ result, anchor }) => {
|
||||
|
||||
const { screenshots, videos, traces, otherAttachments, diffs, errors, htmls } = React.useMemo(() => {
|
||||
}> = ({ test, result }) => {
|
||||
const { screenshots, videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors } = React.useMemo(() => {
|
||||
const attachments = result?.attachments || [];
|
||||
const screenshots = new Set(attachments.filter(a => a.contentType.startsWith('image/')));
|
||||
const screenshotAnchors = [...screenshots].map(a => `attachment-${a.name}`);
|
||||
const videos = attachments.filter(a => a.contentType.startsWith('video/'));
|
||||
const traces = attachments.filter(a => a.name === 'trace');
|
||||
const htmls = attachments.filter(a => a.contentType.startsWith('text/html'));
|
||||
const otherAttachments = new Set<TestAttachment>(attachments);
|
||||
[...screenshots, ...videos, ...traces, ...htmls].forEach(a => otherAttachments.delete(a));
|
||||
[...screenshots, ...videos, ...traces].forEach(a => otherAttachments.delete(a));
|
||||
const otherAttachmentAnchors = [...otherAttachments].map(a => `attachment-${a.name}`);
|
||||
const diffs = groupImageDiffs(screenshots);
|
||||
const errors = classifyErrors(result.errors, diffs);
|
||||
return { screenshots: [...screenshots], videos, traces, otherAttachments, diffs, errors, htmls };
|
||||
return { screenshots: [...screenshots], videos, traces, otherAttachments, diffs, errors, otherAttachmentAnchors, screenshotAnchors };
|
||||
}, [result]);
|
||||
|
||||
const videoRef = React.useRef<HTMLDivElement>(null);
|
||||
const imageDiffRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const [scrolled, setScrolled] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
if (scrolled)
|
||||
return;
|
||||
setScrolled(true);
|
||||
if (anchor === 'video')
|
||||
videoRef.current?.scrollIntoView({ block: 'start', inline: 'start' });
|
||||
if (anchor === 'diff')
|
||||
imageDiffRef.current?.scrollIntoView({ block: 'start', inline: 'start' });
|
||||
}, [scrolled, anchor, setScrolled, videoRef]);
|
||||
|
||||
return <div className='test-result'>
|
||||
{!!errors.length && <AutoChip header='Errors'>
|
||||
{errors.map((error, index) => {
|
||||
|
|
@ -103,49 +94,52 @@ export const TestResultView: React.FC<{
|
|||
})}
|
||||
</AutoChip>}
|
||||
{!!result.steps.length && <AutoChip header='Test Steps'>
|
||||
{result.steps.map((step, i) => <StepTreeItem key={`step-${i}`} step={step} depth={0}></StepTreeItem>)}
|
||||
{result.steps.map((step, i) => <StepTreeItem key={`step-${i}`} step={step} result={result} test={test} depth={0}/>)}
|
||||
</AutoChip>}
|
||||
|
||||
{diffs.map((diff, index) =>
|
||||
<AutoChip key={`diff-${index}`} dataTestId='test-results-image-diff' header={`Image mismatch: ${diff.name}`} targetRef={imageDiffRef}>
|
||||
<ImageDiffView key='image-diff' diff={diff}></ImageDiffView>
|
||||
</AutoChip>
|
||||
<Anchor key={`diff-${index}`} id={diff.anchors}>
|
||||
<AutoChip dataTestId='test-results-image-diff' header={`Image mismatch: ${diff.name}`} revealOnAnchorId={diff.anchors}>
|
||||
<ImageDiffView diff={diff}/>
|
||||
</AutoChip>
|
||||
</Anchor>
|
||||
)}
|
||||
|
||||
{!!screenshots.length && <AutoChip header='Screenshots'>
|
||||
{!!screenshots.length && <AutoChip header='Screenshots' revealOnAnchorId={screenshotAnchors}>
|
||||
{screenshots.map((a, i) => {
|
||||
return <div key={`screenshot-${i}`}>
|
||||
return <Anchor key={`screenshot-${i}`} id={`attachment-${a.name}`}>
|
||||
<a href={a.path}>
|
||||
<img className='screenshot' src={a.path} />
|
||||
</a>
|
||||
<AttachmentLink attachment={a}></AttachmentLink>
|
||||
</div>;
|
||||
</Anchor>;
|
||||
})}
|
||||
</AutoChip>}
|
||||
|
||||
{!!traces.length && <AutoChip header='Traces'>
|
||||
{!!traces.length && <Anchor id='attachment-trace'><AutoChip header='Traces' revealOnAnchorId='attachment-trace'>
|
||||
{<div>
|
||||
<a href={generateTraceUrl(traces)}>
|
||||
<img className='screenshot' src={traceImage} style={{ width: 192, height: 117, marginLeft: 20 }} />
|
||||
</a>
|
||||
{traces.map((a, i) => <AttachmentLink key={`trace-${i}`} attachment={a} linkName={traces.length === 1 ? 'trace' : `trace-${i + 1}`}></AttachmentLink>)}
|
||||
</div>}
|
||||
</AutoChip>}
|
||||
</AutoChip></Anchor>}
|
||||
|
||||
{!!videos.length && <AutoChip header='Videos' targetRef={videoRef}>
|
||||
{!!videos.length && <Anchor id='attachment-video'><AutoChip header='Videos' revealOnAnchorId='attachment-video'>
|
||||
{videos.map((a, i) => <div key={`video-${i}`}>
|
||||
<video controls>
|
||||
<source src={a.path} type={a.contentType}/>
|
||||
</video>
|
||||
<AttachmentLink attachment={a}></AttachmentLink>
|
||||
</div>)}
|
||||
</AutoChip>}
|
||||
</AutoChip></Anchor>}
|
||||
|
||||
{!!(otherAttachments.size + htmls.length) && <AutoChip header='Attachments'>
|
||||
{[...htmls].map((a, i) => (
|
||||
<AttachmentLink key={`html-link-${i}`} attachment={a} openInNewTab />)
|
||||
{!!otherAttachments.size && <AutoChip header='Attachments' revealOnAnchorId={otherAttachmentAnchors}>
|
||||
{[...otherAttachments].map((a, i) =>
|
||||
<Anchor key={`attachment-link-${i}`} id={`attachment-${a.name}`}>
|
||||
<AttachmentLink attachment={a} openInNewTab={a.contentType.startsWith('text/html')} />
|
||||
</Anchor>
|
||||
)}
|
||||
{[...otherAttachments].map((a, i) => <AttachmentLink key={`attachment-link-${i}`} attachment={a}></AttachmentLink>)}
|
||||
</AutoChip>}
|
||||
</div>;
|
||||
};
|
||||
|
|
@ -175,19 +169,23 @@ function classifyErrors(testErrors: string[], diffs: ImageDiff[]) {
|
|||
}
|
||||
|
||||
const StepTreeItem: React.FC<{
|
||||
test: TestCase;
|
||||
result: TestResult;
|
||||
step: TestStep;
|
||||
depth: number,
|
||||
}> = ({ step, depth }) => {
|
||||
return <TreeItem title={<span>
|
||||
}> = ({ test, step, result, depth }) => {
|
||||
const attachmentName = step.title.match(/^attach "(.*)"$/)?.[1];
|
||||
return <TreeItem title={<span aria-label={step.title}>
|
||||
<span style={{ float: 'right' }}>{msToString(step.duration)}</span>
|
||||
{attachmentName && <a style={{ float: 'right' }} title='link to attachment' href={testResultHref({ test, result, anchor: `attachment-${attachmentName}` })} onClick={evt => { evt.stopPropagation(); }}>{icons.attachment()}</a>}
|
||||
{statusIcon(step.error || step.duration === -1 ? 'failed' : 'passed')}
|
||||
<span>{step.title}</span>
|
||||
{step.count > 1 && <> ✕ <span className='test-result-counter'>{step.count}</span></>}
|
||||
{step.location && <span className='test-result-path'>— {step.location.file}:{step.location.line}</span>}
|
||||
</span>} loadChildren={step.steps.length + (step.snippet ? 1 : 0) ? () => {
|
||||
const children = step.steps.map((s, i) => <StepTreeItem key={i} step={s} depth={depth + 1}></StepTreeItem>);
|
||||
const children = step.steps.map((s, i) => <StepTreeItem key={i} step={s} depth={depth + 1} result={result} test={test} />);
|
||||
if (step.snippet)
|
||||
children.unshift(<TestErrorView testId='test-snippet' key='line' error={step.snippet}></TestErrorView>);
|
||||
children.unshift(<TestErrorView testId='test-snippet' key='line' error={step.snippet}/>);
|
||||
return children;
|
||||
} : undefined} depth={depth}></TreeItem>;
|
||||
} : undefined} depth={depth}/>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tree-item-title.selected {
|
||||
text-decoration: underline var(--color-underlinenav-icon);
|
||||
text-decoration-thickness: 1.5px;
|
||||
}
|
||||
|
||||
.tree-item-body {
|
||||
min-height: 18px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
import * as React from 'react';
|
||||
import './treeItem.css';
|
||||
import * as icons from './icons';
|
||||
import { clsx } from '@web/uiUtils';
|
||||
|
||||
export const TreeItem: React.FunctionComponent<{
|
||||
title: JSX.Element,
|
||||
|
|
@ -28,9 +29,8 @@ export const TreeItem: React.FunctionComponent<{
|
|||
style?: React.CSSProperties,
|
||||
}> = ({ title, loadChildren, onClick, expandByDefault, depth, selected, style }) => {
|
||||
const [expanded, setExpanded] = React.useState(expandByDefault || false);
|
||||
const className = selected ? 'tree-item-title selected' : 'tree-item-title';
|
||||
return <div className={'tree-item'} style={style}>
|
||||
<span className={className} style={{ whiteSpace: 'nowrap', paddingLeft: depth * 22 + 4 }} onClick={() => { onClick?.(); setExpanded(!expanded); }} >
|
||||
<span className={clsx('tree-item-title', selected && 'selected')} style={{ whiteSpace: 'nowrap', paddingLeft: depth * 22 + 4 }} onClick={() => { onClick?.(); setExpanded(!expanded); }} >
|
||||
{loadChildren && !!expanded && icons.downArrow()}
|
||||
{loadChildren && !expanded && icons.rightArrow()}
|
||||
{!loadChildren && <span style={{ visibility: 'hidden' }}>{icons.rightArrow()}</span>}
|
||||
|
|
|
|||
|
|
@ -24,4 +24,4 @@ try {
|
|||
}
|
||||
|
||||
if (install)
|
||||
install(['chromium', 'ffmpeg']);
|
||||
install(['chromium', 'chromium-headless-shell', 'ffmpeg']);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/browser-chromium",
|
||||
"version": "1.49.0-next",
|
||||
"version": "1.50.0-next",
|
||||
"description": "Playwright package that automatically installs Chromium",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.49.0-next"
|
||||
"playwright-core": "1.50.0-next"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/browser-firefox",
|
||||
"version": "1.49.0-next",
|
||||
"version": "1.50.0-next",
|
||||
"description": "Playwright package that automatically installs Firefox",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.49.0-next"
|
||||
"playwright-core": "1.50.0-next"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@playwright/browser-webkit",
|
||||
"version": "1.49.0-next",
|
||||
"version": "1.50.0-next",
|
||||
"description": "Playwright package that automatically installs WebKit",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -27,6 +27,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.49.0-next"
|
||||
"playwright-core": "1.50.0-next"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,4 +24,4 @@ try {
|
|||
}
|
||||
|
||||
if (install)
|
||||
install(['chromium', 'ffmpeg']);
|
||||
install(['chromium', 'chromium-headless-shell', 'ffmpeg']);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "playwright-chromium",
|
||||
"version": "1.49.0-next",
|
||||
"version": "1.50.0-next",
|
||||
"description": "A high-level API to automate Chromium",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -30,6 +30,6 @@
|
|||
"install": "node install.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.49.0-next"
|
||||
"playwright-core": "1.50.0-next"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@ This project incorporates components from the projects listed below. The origina
|
|||
|
||||
- @types/node@17.0.24 (https://github.com/DefinitelyTyped/DefinitelyTyped)
|
||||
- @types/yauzl@2.10.0 (https://github.com/DefinitelyTyped/DefinitelyTyped)
|
||||
- agent-base@7.1.1 (https://github.com/TooTallNate/proxy-agents)
|
||||
- agent-base@7.1.3 (https://github.com/TooTallNate/proxy-agents)
|
||||
- balanced-match@1.0.2 (https://github.com/juliangruber/balanced-match)
|
||||
- brace-expansion@1.1.11 (https://github.com/juliangruber/brace-expansion)
|
||||
- buffer-crc32@0.2.13 (https://github.com/brianloveswords/buffer-crc32)
|
||||
- codemirror-shadow-1@0.0.1 (https://github.com/codemirror/CodeMirror)
|
||||
- codemirror@5.65.18 (https://github.com/codemirror/CodeMirror)
|
||||
- colors@1.4.0 (https://github.com/Marak/colors.js)
|
||||
- commander@8.3.0 (https://github.com/tj/commander.js)
|
||||
- concat-map@0.0.1 (https://github.com/substack/node-concat-map)
|
||||
- debug@4.3.4 (https://github.com/debug-js/debug)
|
||||
- define-lazy-prop@2.0.0 (https://github.com/sindresorhus/define-lazy-prop)
|
||||
- diff-match-patch@1.0.5 (https://github.com/JackuB/diff-match-patch)
|
||||
- diff@7.0.0 (https://github.com/kpdecker/jsdiff)
|
||||
- dotenv@16.4.5 (https://github.com/motdotla/dotenv)
|
||||
- end-of-stream@1.4.4 (https://github.com/mafintosh/end-of-stream)
|
||||
- escape-string-regexp@2.0.0 (https://github.com/sindresorhus/escape-string-regexp)
|
||||
|
|
@ -24,7 +24,7 @@ This project incorporates components from the projects listed below. The origina
|
|||
- fd-slicer@1.1.0 (https://github.com/andrewrk/node-fd-slicer)
|
||||
- get-stream@5.2.0 (https://github.com/sindresorhus/get-stream)
|
||||
- graceful-fs@4.2.10 (https://github.com/isaacs/node-graceful-fs)
|
||||
- https-proxy-agent@7.0.5 (https://github.com/TooTallNate/proxy-agents)
|
||||
- https-proxy-agent@7.0.6 (https://github.com/TooTallNate/proxy-agents)
|
||||
- ip-address@9.0.5 (https://github.com/beaugunderson/ip-address)
|
||||
- is-docker@2.2.1 (https://github.com/sindresorhus/is-docker)
|
||||
- is-wsl@2.2.0 (https://github.com/sindresorhus/is-wsl)
|
||||
|
|
@ -43,7 +43,7 @@ This project incorporates components from the projects listed below. The origina
|
|||
- retry@0.12.0 (https://github.com/tim-kos/node-retry)
|
||||
- signal-exit@3.0.7 (https://github.com/tapjs/signal-exit)
|
||||
- smart-buffer@4.2.0 (https://github.com/JoshGlazebrook/smart-buffer)
|
||||
- socks-proxy-agent@8.0.4 (https://github.com/TooTallNate/proxy-agents)
|
||||
- socks-proxy-agent@8.0.5 (https://github.com/TooTallNate/proxy-agents)
|
||||
- socks@2.8.3 (https://github.com/JoshGlazebrook/socks)
|
||||
- sprintf-js@1.1.3 (https://github.com/alexei/sprintf.js)
|
||||
- stack-utils@2.0.5 (https://github.com/tapjs/stack-utils)
|
||||
|
|
@ -105,7 +105,7 @@ MIT License
|
|||
=========================================
|
||||
END OF @types/yauzl@2.10.0 AND INFORMATION
|
||||
|
||||
%% agent-base@7.1.1 NOTICES AND INFORMATION BEGIN HERE
|
||||
%% agent-base@7.1.3 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
(The MIT License)
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
=========================================
|
||||
END OF agent-base@7.1.1 AND INFORMATION
|
||||
END OF agent-base@7.1.3 AND INFORMATION
|
||||
|
||||
%% balanced-match@1.0.2 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
|
|
@ -208,7 +208,7 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEAL
|
|||
=========================================
|
||||
END OF buffer-crc32@0.2.13 AND INFORMATION
|
||||
|
||||
%% codemirror-shadow-1@0.0.1 NOTICES AND INFORMATION BEGIN HERE
|
||||
%% codemirror@5.65.18 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
MIT License
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
=========================================
|
||||
END OF codemirror-shadow-1@0.0.1 AND INFORMATION
|
||||
END OF codemirror@5.65.18 AND INFORMATION
|
||||
|
||||
%% colors@1.4.0 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
|
|
@ -352,211 +352,39 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|||
=========================================
|
||||
END OF define-lazy-prop@2.0.0 AND INFORMATION
|
||||
|
||||
%% diff-match-patch@1.0.5 NOTICES AND INFORMATION BEGIN HERE
|
||||
%% diff@7.0.0 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
BSD 3-Clause License
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) 2009-2015, Kevin Decker <kpdecker@gmail.com>
|
||||
All rights reserved.
|
||||
|
||||
1. Definitions.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
=========================================
|
||||
END OF diff-match-patch@1.0.5 AND INFORMATION
|
||||
END OF diff@7.0.0 AND INFORMATION
|
||||
|
||||
%% dotenv@16.4.5 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
|
|
@ -714,7 +542,7 @@ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
=========================================
|
||||
END OF graceful-fs@4.2.10 AND INFORMATION
|
||||
|
||||
%% https-proxy-agent@7.0.5 NOTICES AND INFORMATION BEGIN HERE
|
||||
%% https-proxy-agent@7.0.6 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
(The MIT License)
|
||||
|
||||
|
|
@ -739,7 +567,7 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
=========================================
|
||||
END OF https-proxy-agent@7.0.5 AND INFORMATION
|
||||
END OF https-proxy-agent@7.0.6 AND INFORMATION
|
||||
|
||||
%% ip-address@9.0.5 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
|
|
@ -1177,7 +1005,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
=========================================
|
||||
END OF smart-buffer@4.2.0 AND INFORMATION
|
||||
|
||||
%% socks-proxy-agent@8.0.4 NOTICES AND INFORMATION BEGIN HERE
|
||||
%% socks-proxy-agent@8.0.5 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
(The MIT License)
|
||||
|
||||
|
|
@ -1202,7 +1030,7 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
=========================================
|
||||
END OF socks-proxy-agent@8.0.4 AND INFORMATION
|
||||
END OF socks-proxy-agent@8.0.5 AND INFORMATION
|
||||
|
||||
%% socks@2.8.3 NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,2 +0,0 @@
|
|||
See building instructions at [`/browser_patches/winldd/README.md`](../../../browser_patches/winldd/README.md)
|
||||
|
||||
|
|
@ -7,15 +7,17 @@ if [[ $(arch) == "aarch64" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 1. make sure to remove old beta if any.
|
||||
|
|
|
|||
|
|
@ -7,15 +7,17 @@ if [[ $(arch) == "aarch64" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 1. make sure to remove old stable if any.
|
||||
|
|
|
|||
|
|
@ -8,15 +8,17 @@ if [[ $(arch) == "aarch64" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 1. make sure to remove old beta if any.
|
||||
|
|
|
|||
|
|
@ -8,15 +8,17 @@ if [[ $(arch) == "aarch64" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 1. make sure to remove old dev if any.
|
||||
|
|
|
|||
|
|
@ -8,15 +8,17 @@ if [[ $(arch) == "aarch64" ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$PLAYWRIGHT_HOST_PLATFORM_OVERRIDE" ]; then
|
||||
if [[ ! -f "/etc/os-release" ]]; then
|
||||
echo "ERROR: cannot install on unknown linux distribution (/etc/os-release is missing)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
ID=$(bash -c 'source /etc/os-release && echo $ID')
|
||||
if [[ "${ID}" != "ubuntu" && "${ID}" != "debian" ]]; then
|
||||
echo "ERROR: cannot install on $ID distribution - only Ubuntu and Debian are supported"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 1. make sure to remove old stable if any.
|
||||
|
|
|
|||
|
|
@ -3,33 +3,35 @@
|
|||
"browsers": [
|
||||
{
|
||||
"name": "chromium",
|
||||
"revision": "1145",
|
||||
"revision": "1152",
|
||||
"installByDefault": true,
|
||||
"browserVersion": "131.0.6778.13"
|
||||
"browserVersion": "132.0.6834.46"
|
||||
},
|
||||
{
|
||||
"name": "chromium-tip-of-tree",
|
||||
"revision": "1271",
|
||||
"revision": "1288",
|
||||
"installByDefault": false,
|
||||
"browserVersion": "132.0.6791.0"
|
||||
"browserVersion": "133.0.6905.0"
|
||||
},
|
||||
{
|
||||
"name": "firefox",
|
||||
"revision": "1465",
|
||||
"revision": "1466",
|
||||
"installByDefault": true,
|
||||
"browserVersion": "131.0"
|
||||
"browserVersion": "132.0"
|
||||
},
|
||||
{
|
||||
"name": "firefox-beta",
|
||||
"revision": "1465",
|
||||
"revision": "1466",
|
||||
"installByDefault": false,
|
||||
"browserVersion": "132.0b8"
|
||||
"browserVersion": "133.0b9"
|
||||
},
|
||||
{
|
||||
"name": "webkit",
|
||||
"revision": "2098",
|
||||
"revision": "2120",
|
||||
"installByDefault": true,
|
||||
"revisionOverrides": {
|
||||
"debian11-x64": "2105",
|
||||
"debian11-arm64": "2105",
|
||||
"mac10.14": "1446",
|
||||
"mac10.15": "1616",
|
||||
"mac11": "1816",
|
||||
|
|
@ -39,17 +41,22 @@
|
|||
"ubuntu20.04-x64": "2092",
|
||||
"ubuntu20.04-arm64": "2092"
|
||||
},
|
||||
"browserVersion": "18.0"
|
||||
"browserVersion": "18.2"
|
||||
},
|
||||
{
|
||||
"name": "ffmpeg",
|
||||
"revision": "1010",
|
||||
"revision": "1011",
|
||||
"installByDefault": true,
|
||||
"revisionOverrides": {
|
||||
"mac12": "1010",
|
||||
"mac12-arm64": "1010"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "winldd",
|
||||
"revision": "1007",
|
||||
"installByDefault": false
|
||||
},
|
||||
{
|
||||
"name": "android",
|
||||
"revision": "1001",
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
"colors": "1.4.0",
|
||||
"commander": "8.3.0",
|
||||
"debug": "^4.3.4",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"diff": "^7.0.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"graceful-fs": "4.2.10",
|
||||
"https-proxy-agent": "7.0.5",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"jpeg-js": "0.4.4",
|
||||
"mime": "^3.0.0",
|
||||
"minimatch": "^3.1.2",
|
||||
|
|
@ -24,14 +24,14 @@
|
|||
"proxy-from-env": "1.1.0",
|
||||
"retry": "0.12.0",
|
||||
"signal-exit": "3.0.7",
|
||||
"socks-proxy-agent": "8.0.4",
|
||||
"socks-proxy-agent": "8.0.5",
|
||||
"stack-utils": "2.0.5",
|
||||
"ws": "8.17.1",
|
||||
"yaml": "^2.5.1"
|
||||
"yaml": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/diff-match-patch": "^1.0.36",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/mime": "^2.0.3",
|
||||
"@types/minimatch": "^3.0.5",
|
||||
"@types/pngjs": "^6.0.1",
|
||||
|
|
@ -51,11 +51,12 @@
|
|||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/diff-match-patch": {
|
||||
"version": "1.0.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz",
|
||||
"integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==",
|
||||
"dev": true
|
||||
"node_modules/@types/diff": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/diff/-/diff-6.0.0.tgz",
|
||||
"integrity": "sha512-dhVCYGv3ZSbzmQaBSagrv1WJ6rXCdkyTcDyoNu1MD8JohI7pR7k8wdZEm+mvdxRKXyHVwckFzWU1vJc+Z29MlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "2.0.3",
|
||||
|
|
@ -139,13 +140,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
|
||||
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
|
|
@ -209,10 +207,14 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/diff-match-patch": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
|
||||
"integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
|
||||
"node_modules/diff": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
|
||||
"integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.4.5",
|
||||
|
|
@ -239,12 +241,12 @@
|
|||
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
|
||||
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.0.2",
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -398,12 +400,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/socks-proxy-agent": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
|
||||
"integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
|
||||
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.1",
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"socks": "^2.8.3"
|
||||
},
|
||||
|
|
@ -469,10 +471,10 @@
|
|||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"@types/diff-match-patch": {
|
||||
"version": "1.0.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz",
|
||||
"integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==",
|
||||
"@types/diff": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/diff/-/diff-6.0.0.tgz",
|
||||
"integrity": "sha512-dhVCYGv3ZSbzmQaBSagrv1WJ6rXCdkyTcDyoNu1MD8JohI7pR7k8wdZEm+mvdxRKXyHVwckFzWU1vJc+Z29MlA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mime": {
|
||||
|
|
@ -557,12 +559,9 @@
|
|||
}
|
||||
},
|
||||
"agent-base": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
|
||||
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
|
||||
"requires": {
|
||||
"debug": "^4.3.4"
|
||||
}
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -606,10 +605,10 @@
|
|||
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
|
||||
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="
|
||||
},
|
||||
"diff-match-patch": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
|
||||
"integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
|
||||
"diff": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
|
||||
"integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "16.4.5",
|
||||
|
|
@ -627,11 +626,11 @@
|
|||
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
|
||||
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||
"requires": {
|
||||
"agent-base": "^7.0.2",
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "4"
|
||||
}
|
||||
},
|
||||
|
|
@ -735,11 +734,11 @@
|
|||
}
|
||||
},
|
||||
"socks-proxy-agent": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz",
|
||||
"integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==",
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
|
||||
"integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
|
||||
"requires": {
|
||||
"agent-base": "^7.1.1",
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "^4.3.4",
|
||||
"socks": "^2.8.3"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@
|
|||
"colors": "1.4.0",
|
||||
"commander": "8.3.0",
|
||||
"debug": "^4.3.4",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"diff": "^7.0.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"graceful-fs": "4.2.10",
|
||||
"https-proxy-agent": "7.0.5",
|
||||
"https-proxy-agent": "7.0.6",
|
||||
"jpeg-js": "0.4.4",
|
||||
"mime": "^3.0.0",
|
||||
"minimatch": "^3.1.2",
|
||||
|
|
@ -25,14 +25,14 @@
|
|||
"proxy-from-env": "1.1.0",
|
||||
"retry": "0.12.0",
|
||||
"signal-exit": "3.0.7",
|
||||
"socks-proxy-agent": "8.0.4",
|
||||
"socks-proxy-agent": "8.0.5",
|
||||
"stack-utils": "2.0.5",
|
||||
"ws": "8.17.1",
|
||||
"yaml": "^2.5.1"
|
||||
"yaml": "^2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/diff-match-patch": "^1.0.36",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/mime": "^2.0.3",
|
||||
"@types/minimatch": "^3.0.5",
|
||||
"@types/pngjs": "^6.0.1",
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue