From adc38cfd8b6428a1fa063d785513454503789b06 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Thu, 24 Oct 2024 16:49:35 +0200 Subject: [PATCH 001/165] test: rebaseline test expectations (#33270) --- tests/library/capabilities.spec.ts | 1 + tests/page/page-screenshot.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/library/capabilities.spec.ts b/tests/library/capabilities.spec.ts index cdc97d1523..72eb73724c 100644 --- a/tests/library/capabilities.spec.ts +++ b/tests/library/capabilities.spec.ts @@ -401,6 +401,7 @@ it('should be able to render avif images', { }, async ({ page, server, browserName, platform }) => { it.fixme(browserName === 'webkit' && platform === 'win32'); it.skip(browserName === 'webkit' && hostPlatform.startsWith('ubuntu20.04'), 'Ubuntu 20.04 is frozen'); + it.skip(browserName === 'webkit' && hostPlatform.startsWith('debian11'), 'Debian 11 is too old'); await page.goto(server.EMPTY_PAGE); await page.setContent(``); await expect.poll(() => page.locator('img').boundingBox()).toEqual(expect.objectContaining({ diff --git a/tests/page/page-screenshot.spec.ts b/tests/page/page-screenshot.spec.ts index 879307aa91..7e6856c24c 100644 --- a/tests/page/page-screenshot.spec.ts +++ b/tests/page/page-screenshot.spec.ts @@ -286,7 +286,7 @@ it.describe('page screenshot', () => { await page.goto(server.PREFIX + '/screenshots/canvas.html'); const screenshot = await page.screenshot(); if ((!headless && browserName === 'chromium' && isMac && os.arch() === 'arm64' && macVersion >= 14) || - (browserName === 'webkit' && isLinux)) + (browserName === 'webkit' && isLinux && os.arch() === 'x64')) expect(screenshot).toMatchSnapshot('screenshot-canvas-with-accurate-corners.png'); else expect(screenshot).toMatchSnapshot('screenshot-canvas.png'); From 0509eca9b690aa0efc47fd7bb0b6cd555d1de848 Mon Sep 17 00:00:00 2001 From: Playwright Service <89237858+playwrightmachine@users.noreply.github.com> Date: Thu, 24 Oct 2024 08:47:34 -0700 Subject: [PATCH 002/165] feat(chromium): roll to r1145 (#33274) Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- README.md | 4 +- packages/playwright-core/browsers.json | 4 +- .../src/server/deviceDescriptorsSource.json | 96 +++++++++---------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index e4f15a6d97..df47df9121 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🎭 Playwright -[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-131.0.6778.3-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-131.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-18.0-blue.svg?logo=safari)](https://webkit.org/) [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord) +[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) [![Chromium version](https://img.shields.io/badge/chromium-131.0.6778.13-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-131.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-18.0-blue.svg?logo=safari)](https://webkit.org/) [![Join Discord](https://img.shields.io/badge/join-discord-infomational)](https://aka.ms/playwright/discord) ## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright) @@ -8,7 +8,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 131.0.6778.3 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Chromium 131.0.6778.13 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 18.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | Firefox 131.0 | :white_check_mark: | :white_check_mark: | :white_check_mark: | diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index 6cd5bc321d..5677f6ec7f 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -3,9 +3,9 @@ "browsers": [ { "name": "chromium", - "revision": "1143", + "revision": "1145", "installByDefault": true, - "browserVersion": "131.0.6778.3" + "browserVersion": "131.0.6778.13" }, { "name": "chromium-tip-of-tree", diff --git a/packages/playwright-core/src/server/deviceDescriptorsSource.json b/packages/playwright-core/src/server/deviceDescriptorsSource.json index aef1b4c62b..90608d052d 100644 --- a/packages/playwright-core/src/server/deviceDescriptorsSource.json +++ b/packages/playwright-core/src/server/deviceDescriptorsSource.json @@ -110,7 +110,7 @@ "defaultBrowserType": "webkit" }, "Galaxy S5": { - "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 360, "height": 640 @@ -121,7 +121,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S5 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 640, "height": 360 @@ -132,7 +132,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S8": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 360, "height": 740 @@ -143,7 +143,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S8 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 740, "height": 360 @@ -154,7 +154,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S9+": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 320, "height": 658 @@ -165,7 +165,7 @@ "defaultBrowserType": "chromium" }, "Galaxy S9+ landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 658, "height": 320 @@ -176,7 +176,7 @@ "defaultBrowserType": "chromium" }, "Galaxy Tab S4": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36", "viewport": { "width": 712, "height": 1138 @@ -187,7 +187,7 @@ "defaultBrowserType": "chromium" }, "Galaxy Tab S4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36", "viewport": { "width": 1138, "height": 712 @@ -1098,7 +1098,7 @@ "defaultBrowserType": "webkit" }, "LG Optimus L70": { - "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 384, "height": 640 @@ -1109,7 +1109,7 @@ "defaultBrowserType": "chromium" }, "LG Optimus L70 landscape": { - "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 640, "height": 384 @@ -1120,7 +1120,7 @@ "defaultBrowserType": "chromium" }, "Microsoft Lumia 550": { - "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36 Edge/14.14263", + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 640, "height": 360 @@ -1131,7 +1131,7 @@ "defaultBrowserType": "chromium" }, "Microsoft Lumia 550 landscape": { - "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36 Edge/14.14263", + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 360, "height": 640 @@ -1142,7 +1142,7 @@ "defaultBrowserType": "chromium" }, "Microsoft Lumia 950": { - "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36 Edge/14.14263", + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 360, "height": 640 @@ -1153,7 +1153,7 @@ "defaultBrowserType": "chromium" }, "Microsoft Lumia 950 landscape": { - "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36 Edge/14.14263", + "userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36 Edge/14.14263", "viewport": { "width": 640, "height": 360 @@ -1164,7 +1164,7 @@ "defaultBrowserType": "chromium" }, "Nexus 10": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36", "viewport": { "width": 800, "height": 1280 @@ -1175,7 +1175,7 @@ "defaultBrowserType": "chromium" }, "Nexus 10 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36", "viewport": { "width": 1280, "height": 800 @@ -1186,7 +1186,7 @@ "defaultBrowserType": "chromium" }, "Nexus 4": { - "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 384, "height": 640 @@ -1197,7 +1197,7 @@ "defaultBrowserType": "chromium" }, "Nexus 4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 640, "height": 384 @@ -1208,7 +1208,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 360, "height": 640 @@ -1219,7 +1219,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 640, "height": 360 @@ -1230,7 +1230,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5X": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1241,7 +1241,7 @@ "defaultBrowserType": "chromium" }, "Nexus 5X landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1252,7 +1252,7 @@ "defaultBrowserType": "chromium" }, "Nexus 6": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1263,7 +1263,7 @@ "defaultBrowserType": "chromium" }, "Nexus 6 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1274,7 +1274,7 @@ "defaultBrowserType": "chromium" }, "Nexus 6P": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 412, "height": 732 @@ -1285,7 +1285,7 @@ "defaultBrowserType": "chromium" }, "Nexus 6P landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 732, "height": 412 @@ -1296,7 +1296,7 @@ "defaultBrowserType": "chromium" }, "Nexus 7": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36", "viewport": { "width": 600, "height": 960 @@ -1307,7 +1307,7 @@ "defaultBrowserType": "chromium" }, "Nexus 7 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36", "viewport": { "width": 960, "height": 600 @@ -1362,7 +1362,7 @@ "defaultBrowserType": "webkit" }, "Pixel 2": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 411, "height": 731 @@ -1373,7 +1373,7 @@ "defaultBrowserType": "chromium" }, "Pixel 2 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 731, "height": 411 @@ -1384,7 +1384,7 @@ "defaultBrowserType": "chromium" }, "Pixel 2 XL": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 411, "height": 823 @@ -1395,7 +1395,7 @@ "defaultBrowserType": "chromium" }, "Pixel 2 XL landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 823, "height": 411 @@ -1406,7 +1406,7 @@ "defaultBrowserType": "chromium" }, "Pixel 3": { - "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 393, "height": 786 @@ -1417,7 +1417,7 @@ "defaultBrowserType": "chromium" }, "Pixel 3 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 786, "height": 393 @@ -1428,7 +1428,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4": { - "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 353, "height": 745 @@ -1439,7 +1439,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 745, "height": 353 @@ -1450,7 +1450,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4a (5G)": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "screen": { "width": 412, "height": 892 @@ -1465,7 +1465,7 @@ "defaultBrowserType": "chromium" }, "Pixel 4a (5G) landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "screen": { "height": 892, "width": 412 @@ -1480,7 +1480,7 @@ "defaultBrowserType": "chromium" }, "Pixel 5": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "screen": { "width": 393, "height": 851 @@ -1495,7 +1495,7 @@ "defaultBrowserType": "chromium" }, "Pixel 5 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "screen": { "width": 851, "height": 393 @@ -1510,7 +1510,7 @@ "defaultBrowserType": "chromium" }, "Pixel 7": { - "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "screen": { "width": 412, "height": 915 @@ -1525,7 +1525,7 @@ "defaultBrowserType": "chromium" }, "Pixel 7 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "screen": { "width": 915, "height": 412 @@ -1540,7 +1540,7 @@ "defaultBrowserType": "chromium" }, "Moto G4": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 360, "height": 640 @@ -1551,7 +1551,7 @@ "defaultBrowserType": "chromium" }, "Moto G4 landscape": { - "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Mobile Safari/537.36", + "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Mobile Safari/537.36", "viewport": { "width": 640, "height": 360 @@ -1562,7 +1562,7 @@ "defaultBrowserType": "chromium" }, "Desktop Chrome HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36", "screen": { "width": 1792, "height": 1120 @@ -1577,7 +1577,7 @@ "defaultBrowserType": "chromium" }, "Desktop Edge HiDPI": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36 Edg/131.0.6778.3", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36 Edg/131.0.6778.13", "screen": { "width": 1792, "height": 1120 @@ -1622,7 +1622,7 @@ "defaultBrowserType": "webkit" }, "Desktop Chrome": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36", "screen": { "width": 1920, "height": 1080 @@ -1637,7 +1637,7 @@ "defaultBrowserType": "chromium" }, "Desktop Edge": { - "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.3 Safari/537.36 Edg/131.0.6778.3", + "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.13 Safari/537.36 Edg/131.0.6778.13", "screen": { "width": 1920, "height": 1080 From a2dec8da63b171b75a32ae66a50336b059c90383 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Thu, 24 Oct 2024 19:23:19 +0200 Subject: [PATCH 003/165] fix(codegen): SIGINT handling was leading to zombie processes (#33269) --- packages/playwright-core/src/cli/program.ts | 2 ++ packages/playwright-core/src/protocol/validator.ts | 1 + packages/playwright-core/src/server/recorder.ts | 2 ++ packages/playwright-core/src/server/recorder/recorderApp.ts | 2 +- .../playwright-core/src/server/recorder/recorderFrontend.ts | 1 + packages/protocol/src/channels.ts | 2 ++ packages/protocol/src/protocol.yml | 1 + 7 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index 1895f2dfcf..59ad0cd9f8 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -554,6 +554,7 @@ async function open(options: Options, url: string | undefined, language: string) contextOptions, device: options.device, saveStorage: options.saveStorage, + handleSIGINT: false, }); await openPage(context, url); } @@ -577,6 +578,7 @@ async function codegen(options: Options & { target: string, output?: string, tes codegenMode: process.env.PW_RECORDER_IS_TRACE_VIEWER ? 'trace-events' : 'actions', testIdAttributeName, outputFile: outputFile ? path.resolve(outputFile) : undefined, + handleSIGINT: false, }); await openPage(context, url); } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 24ddf0014c..7bad26f498 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -976,6 +976,7 @@ scheme.BrowserContextEnableRecorderParams = tObject({ device: tOptional(tString), saveStorage: tOptional(tString), outputFile: tOptional(tString), + handleSIGINT: tOptional(tBoolean), omitCallTracking: tOptional(tBoolean), }); scheme.BrowserContextEnableRecorderResult = tOptional(tObject({})); diff --git a/packages/playwright-core/src/server/recorder.ts b/packages/playwright-core/src/server/recorder.ts index 74ebefee86..a2be2992e6 100644 --- a/packages/playwright-core/src/server/recorder.ts +++ b/packages/playwright-core/src/server/recorder.ts @@ -34,6 +34,7 @@ import { buildFullSelector } from '../utils/isomorphic/recorderUtils'; const recorderSymbol = Symbol('recorderSymbol'); export class Recorder implements InstrumentationListener, IRecorder { + readonly handleSIGINT: boolean | undefined; private _context: BrowserContext; private _mode: Mode; private _highlightedSelector = ''; @@ -75,6 +76,7 @@ export class Recorder implements InstrumentationListener, IRecorder { constructor(codegenMode: 'actions' | 'trace-events', context: BrowserContext, params: channels.BrowserContextEnableRecorderParams) { this._mode = params.mode || 'none'; + this.handleSIGINT = params.handleSIGINT; this._contextRecorder = new ContextRecorder(codegenMode, context, params, {}); this._context = context; this._omitCallTracking = !!params.omitCallTracking; diff --git a/packages/playwright-core/src/server/recorder/recorderApp.ts b/packages/playwright-core/src/server/recorder/recorderApp.ts index df27a96c6c..6b6b73e241 100644 --- a/packages/playwright-core/src/server/recorder/recorderApp.ts +++ b/packages/playwright-core/src/server/recorder/recorderApp.ts @@ -111,7 +111,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { noDefaultViewport: true, headless: !!process.env.PWTEST_CLI_HEADLESS || (isUnderTest() && !headed), useWebSocket: isUnderTest(), - handleSIGINT: false, + handleSIGINT: recorder.handleSIGINT, executablePath: inspectedContext._browser.options.isChromium ? inspectedContext._browser.options.customExecutablePath : undefined, } }); diff --git a/packages/playwright-core/src/server/recorder/recorderFrontend.ts b/packages/playwright-core/src/server/recorder/recorderFrontend.ts index dbba78ceeb..b3dc0daad9 100644 --- a/packages/playwright-core/src/server/recorder/recorderFrontend.ts +++ b/packages/playwright-core/src/server/recorder/recorderFrontend.ts @@ -21,6 +21,7 @@ import type { EventEmitter } from 'events'; export interface IRecorder { setMode(mode: Mode): void; mode(): Mode; + readonly handleSIGINT: boolean | undefined; } export interface IRecorderApp extends EventEmitter { diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index 5ecc2f4077..7fcb815468 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -1777,6 +1777,7 @@ export type BrowserContextEnableRecorderParams = { device?: string, saveStorage?: string, outputFile?: string, + handleSIGINT?: boolean, omitCallTracking?: boolean, }; export type BrowserContextEnableRecorderOptions = { @@ -1790,6 +1791,7 @@ export type BrowserContextEnableRecorderOptions = { device?: string, saveStorage?: string, outputFile?: string, + handleSIGINT?: boolean, omitCallTracking?: boolean, }; export type BrowserContextEnableRecorderResult = void; diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index c91cecbe6c..98428cae2f 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1208,6 +1208,7 @@ BrowserContext: device: string? saveStorage: string? outputFile: string? + handleSIGINT: boolean? omitCallTracking: boolean? newCDPSession: From ff5f1628dc8dbbb0217554adc3105bff4e0a504c Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Thu, 24 Oct 2024 16:49:10 -0700 Subject: [PATCH 004/165] chore: allow aria snapshot rebaselines (#33256) --- .../src/server/injected/ariaSnapshot.ts | 23 +++-- packages/playwright/src/common/ipc.ts | 1 + packages/playwright/src/matchers/expect.ts | 11 ++- .../playwright/src/matchers/matcherHint.ts | 3 +- .../src/matchers/toMatchAriaSnapshot.ts | 21 +++- packages/playwright/src/runner/dispatcher.ts | 3 + packages/playwright/src/runner/rebase.ts | 95 +++++++++++++++++++ packages/playwright/src/runner/runner.ts | 3 + packages/playwright/src/worker/testInfo.ts | 3 +- tests/page/page-aria-snapshot.spec.ts | 24 ++--- tests/page/to-match-aria-snapshot.spec.ts | 8 +- .../playwright-test/ui-mode-test-run.spec.ts | 76 +++++++-------- .../update-aria-snapshot.spec.ts | 48 ++++++++++ 13 files changed, 245 insertions(+), 74 deletions(-) create mode 100644 packages/playwright/src/runner/rebase.ts create mode 100644 tests/playwright-test/update-aria-snapshot.spec.ts diff --git a/packages/playwright-core/src/server/injected/ariaSnapshot.ts b/packages/playwright-core/src/server/injected/ariaSnapshot.ts index d544c0a82c..a8d9e81bcd 100644 --- a/packages/playwright-core/src/server/injected/ariaSnapshot.ts +++ b/packages/playwright-core/src/server/injected/ariaSnapshot.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { escapeWithQuotes } from '@isomorphic/stringUtils'; import * as roleUtils from './roleUtils'; import { getElementComputedStyle } from './domUtils'; import type { AriaRole } from './roleUtils'; @@ -184,7 +183,7 @@ function matchesText(text: string | undefined, template: RegExp | string | undef export function matchesAriaTree(rootElement: Element, template: AriaTemplateNode): { matches: boolean, received: string } { const root = generateAriaTree(rootElement); const matches = matchesNodeDeep(root, template); - return { matches, received: renderAriaTree(root, { noText: true }) }; + return { matches, received: renderAriaTree(root) }; } function matchesNode(node: AriaNode | string, template: AriaTemplateNode | RegExp | string, depth: number): boolean { @@ -252,17 +251,16 @@ function matchesNodeDeep(root: AriaNode, template: AriaTemplateNode): boolean { return !!results.length; } -export function renderAriaTree(ariaNode: AriaNode, options?: { noText?: boolean }): string { +export function renderAriaTree(ariaNode: AriaNode): string { const lines: string[] = []; const visit = (ariaNode: AriaNode | string, indent: string) => { if (typeof ariaNode === 'string') { - if (!options?.noText) - lines.push(indent + '- text: ' + quoteYamlString(ariaNode)); + lines.push(indent + '- text: ' + quoteYamlString(ariaNode)); return; } let line = `${indent}- ${ariaNode.role}`; if (ariaNode.name) - line += ` ${escapeWithQuotes(ariaNode.name, '"')}`; + line += ` ${quoteYamlString(ariaNode.name)}`; if (ariaNode.checked === 'mixed') line += ` [checked=mixed]`; @@ -281,9 +279,16 @@ export function renderAriaTree(ariaNode: AriaNode, options?: { noText?: boolean if (ariaNode.selected === true) line += ` [selected]`; - lines.push(line + (ariaNode.children.length ? ':' : '')); - for (const child of ariaNode.children || []) - visit(child, indent + ' '); + if (!ariaNode.children.length) { + lines.push(line); + } else if (ariaNode.children.length === 1 && typeof ariaNode.children[0] === 'string') { + line += ': ' + quoteYamlString(ariaNode.children[0]); + lines.push(line); + } else { + lines.push(line + ':'); + for (const child of ariaNode.children || []) + visit(child, indent + ' '); + } }; if (ariaNode.role === 'fragment') { diff --git a/packages/playwright/src/common/ipc.ts b/packages/playwright/src/common/ipc.ts index 82538bb6ed..dcde2b28d4 100644 --- a/packages/playwright/src/common/ipc.ts +++ b/packages/playwright/src/common/ipc.ts @@ -106,6 +106,7 @@ export type StepEndPayload = { stepId: string; wallTime: number; // milliseconds since unix epoch error?: TestInfoErrorImpl; + suggestedRebaseline?: string; }; export type TestEntry = { diff --git a/packages/playwright/src/matchers/expect.ts b/packages/playwright/src/matchers/expect.ts index 0d276d4101..0bd116e7a1 100644 --- a/packages/playwright/src/matchers/expect.ts +++ b/packages/playwright/src/matchers/expect.ts @@ -61,7 +61,7 @@ import { } from '../common/expectBundle'; import { zones } from 'playwright-core/lib/utils'; import { TestInfoImpl } from '../worker/testInfo'; -import { ExpectError, isExpectError } from './matcherHint'; +import { ExpectError, isJestError } from './matcherHint'; import { toMatchAriaSnapshot } from './toMatchAriaSnapshot'; // #region @@ -323,8 +323,13 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler { const step = testInfo._addStep(stepInfo); - const reportStepError = (jestError: Error | unknown) => { - const error = isExpectError(jestError) ? new ExpectError(jestError, customMessage, stackFrames) : jestError; + const reportStepError = (e: Error | unknown) => { + const jestError = isJestError(e) ? e : null; + const error = jestError ? new ExpectError(jestError, customMessage, stackFrames) : e; + if (jestError?.matcherResult.suggestedRebaseline) { + step.complete({ suggestedRebaseline: jestError?.matcherResult.suggestedRebaseline }); + return; + } step.complete({ error }); if (this._info.isSoft) testInfo._failWithError(error); diff --git a/packages/playwright/src/matchers/matcherHint.ts b/packages/playwright/src/matchers/matcherHint.ts index 200501c1bc..c4f5afd4b4 100644 --- a/packages/playwright/src/matchers/matcherHint.ts +++ b/packages/playwright/src/matchers/matcherHint.ts @@ -43,6 +43,7 @@ export type MatcherResult = { printedReceived?: string; printedExpected?: string; printedDiff?: string; + suggestedRebaseline?: string; }; export type MatcherResultProperty = Omit, 'message'> & { @@ -69,6 +70,6 @@ export class ExpectError extends Error { } } -export function isExpectError(e: unknown): e is ExpectError { +export function isJestError(e: unknown): e is JestError { return e instanceof Error && 'matcherResult' in e; } diff --git a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts index 5b2c204410..2c92d562d6 100644 --- a/packages/playwright/src/matchers/toMatchAriaSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchAriaSnapshot.ts @@ -22,6 +22,7 @@ import { colors } from 'playwright-core/lib/utilsBundle'; import { EXPECTED_COLOR } from '../common/expectBundle'; import { callLogText } from '../util'; import { printReceivedStringContainExpectedSubstring } from './expect'; +import { currentTestInfo } from '../common/globals'; export async function toMatchAriaSnapshot( this: ExpectMatcherState, @@ -31,6 +32,15 @@ export async function toMatchAriaSnapshot( ): Promise> { const matcherName = 'toMatchAriaSnapshot'; + const testInfo = currentTestInfo(); + if (!testInfo) + throw new Error(`toMatchSnapshot() must be called during the test`); + + if (testInfo._projectInternal.ignoreSnapshots) + return { pass: !this.isNot, message: () => '', name: 'toMatchSnapshot', expected }; + + const updateSnapshots = testInfo.config.updateSnapshots; + const matcherOptions = { isNot: this.isNot, promise: this.promise, @@ -65,6 +75,12 @@ export async function toMatchAriaSnapshot( } }; + let suggestedRebaseline: string | undefined; + if (!this.isNot && pass === this.isNot) { + if (updateSnapshots === 'all' || (updateSnapshots === 'missing' && !expected.trim())) + suggestedRebaseline = `toMatchAriaSnapshot(\`\n${unshift(received, '${indent} ')}\n\${indent}\`)`; + } + return { name: matcherName, expected, @@ -72,6 +88,7 @@ export async function toMatchAriaSnapshot( pass, actual: received, log, + suggestedRebaseline, timeout: timedOut ? timeout : undefined, }; } @@ -80,7 +97,7 @@ function escapePrivateUsePoints(str: string) { return str.replace(/[\uE000-\uF8FF]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`); } -function unshift(snapshot: string): string { +function unshift(snapshot: string, indent: string = ''): string { const lines = snapshot.split('\n'); let whitespacePrefixLength = 100; for (const line of lines) { @@ -91,5 +108,5 @@ function unshift(snapshot: string): string { whitespacePrefixLength = match[1].length; break; } - return lines.filter(t => t.trim()).map(line => line.substring(whitespacePrefixLength)).join('\n'); + return lines.filter(t => t.trim()).map(line => indent + line.substring(whitespacePrefixLength)).join('\n'); } diff --git a/packages/playwright/src/runner/dispatcher.ts b/packages/playwright/src/runner/dispatcher.ts index 4e971f2475..98e0ec1546 100644 --- a/packages/playwright/src/runner/dispatcher.ts +++ b/packages/playwright/src/runner/dispatcher.ts @@ -27,6 +27,7 @@ import type { FullConfigInternal } from '../common/config'; import type { ReporterV2 } from '../reporters/reporterV2'; import type { FailureTracker } from './failureTracker'; import { colors } from 'playwright-core/lib/utilsBundle'; +import { addSuggestedRebaseline } from './rebase'; export type EnvByProjectId = Map>; @@ -341,6 +342,8 @@ class JobDispatcher { step.duration = params.wallTime - step.startTime.getTime(); if (params.error) step.error = params.error; + if (params.suggestedRebaseline) + addSuggestedRebaseline(step.location!, params.suggestedRebaseline); steps.delete(params.stepId); this._reporter.onStepEnd?.(test, result, step); } diff --git a/packages/playwright/src/runner/rebase.ts b/packages/playwright/src/runner/rebase.ts new file mode 100644 index 0000000000..17717e977e --- /dev/null +++ b/packages/playwright/src/runner/rebase.ts @@ -0,0 +1,95 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import path from 'path'; +import fs from 'fs'; +import type { T } from '../transform/babelBundle'; +import { types, traverse, parse } from '../transform/babelBundle'; +import { MultiMap } from 'playwright-core/lib/utils'; +import { generateUnifiedDiff } from 'playwright-core/lib/utils'; +import type { FullConfigInternal } from '../common/config'; +import { filterProjects } from './projectUtils'; +const t: typeof T = types; + +type Location = { + file: string; + line: number; + column: number; +}; + +type Replacement = { + // Points to the call expression. + location: Location; + code: string; +}; + +const suggestedRebaselines = new MultiMap(); + +export function addSuggestedRebaseline(location: Location, suggestedRebaseline: string) { + suggestedRebaselines.set(location.file, { location, code: suggestedRebaseline }); +} + +export async function applySuggestedRebaselines(config: FullConfigInternal) { + if (config.config.updateSnapshots !== 'all' && config.config.updateSnapshots !== 'missing') + return; + const [project] = filterProjects(config.projects, config.cliProjectFilter); + if (!project) + return; + + for (const fileName of suggestedRebaselines.keys()) { + const source = await fs.promises.readFile(fileName, 'utf8'); + const lines = source.split('\n'); + const replacements = suggestedRebaselines.get(fileName); + const fileNode = parse(source, { sourceType: 'module' }); + const ranges: { start: number, end: number, oldText: string, newText: string }[] = []; + + traverse(fileNode, { + CallExpression: path => { + const node = path.node; + if (node.arguments.length !== 1) + return; + if (!t.isMemberExpression(node.callee)) + return; + const argument = node.arguments[0]; + if (!t.isStringLiteral(argument) && !t.isTemplateLiteral(argument)) + return; + + const matcher = node.callee.property; + for (const replacement of replacements) { + // In Babel, rows are 1-based, columns are 0-based. + if (matcher.loc!.start.line !== replacement.location.line) + continue; + if (matcher.loc!.start.column + 1 !== replacement.location.column) + continue; + const indent = lines[matcher.loc!.start.line - 1].match(/^\s*/)![0]; + const newText = replacement.code.replace(/\$\{indent\}/g, indent); + ranges.push({ start: matcher.start!, end: node.end!, oldText: source.substring(matcher.start!, node.end!), newText }); + } + } + }); + + ranges.sort((a, b) => b.start - a.start); + let result = source; + for (const range of ranges) + result = result.substring(0, range.start) + range.newText + result.substring(range.end); + + const relativeName = path.relative(process.cwd(), fileName); + + const patchFile = path.join(project.project.outputDir, 'rebaselines.patch'); + await fs.promises.mkdir(path.dirname(patchFile), { recursive: true }); + await fs.promises.writeFile(patchFile, generateUnifiedDiff(source, result, relativeName)); + } +} diff --git a/packages/playwright/src/runner/runner.ts b/packages/playwright/src/runner/runner.ts index 923bf36072..966fb13e92 100644 --- a/packages/playwright/src/runner/runner.ts +++ b/packages/playwright/src/runner/runner.ts @@ -24,6 +24,7 @@ import type { FullConfigInternal } from '../common/config'; import { affectedTestFiles } from '../transform/compilationCache'; import { InternalReporter } from '../reporters/internalReporter'; import { LastRunReporter } from './lastRun'; +import { applySuggestedRebaselines } from './rebase'; type ProjectConfigWithFiles = { name: string; @@ -88,6 +89,8 @@ export class Runner { ]; const status = await runTasks(new TestRun(config, reporter), tasks, config.config.globalTimeout); + await applySuggestedRebaselines(config); + // Calling process.exit() might truncate large stdout/stderr output. // See https://github.com/nodejs/node/issues/6456. // See https://github.com/nodejs/node/issues/12921 diff --git a/packages/playwright/src/worker/testInfo.ts b/packages/playwright/src/worker/testInfo.ts index ed71b1a751..b5b1010ff2 100644 --- a/packages/playwright/src/worker/testInfo.ts +++ b/packages/playwright/src/worker/testInfo.ts @@ -31,7 +31,7 @@ import type { StackFrame } from '@protocol/channels'; import { testInfoError } from './util'; export interface TestStepInternal { - complete(result: { error?: Error | unknown, attachments?: Attachment[] }): void; + complete(result: { error?: Error | unknown, attachments?: Attachment[], suggestedRebaseline?: string }): void; stepId: string; title: string; category: 'hook' | 'fixture' | 'test.step' | 'expect' | 'attach' | string; @@ -297,6 +297,7 @@ export class TestInfoImpl implements TestInfo { stepId, wallTime: step.endWallTime, error: step.error, + suggestedRebaseline: result.suggestedRebaseline, }; this._onStepEnd(payload); const errorForTrace = step.error ? { name: '', message: step.error.message || '', stack: step.error.stack } : undefined; diff --git a/tests/page/page-aria-snapshot.spec.ts b/tests/page/page-aria-snapshot.spec.ts index 2b8790589a..e4ee122b0c 100644 --- a/tests/page/page-aria-snapshot.spec.ts +++ b/tests/page/page-aria-snapshot.spec.ts @@ -64,10 +64,8 @@ it('should snapshot list with accessible name', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - list "my list": - - listitem: - - text: "one" - - listitem: - - text: "two" + - listitem: "one" + - listitem: "two" `); }); @@ -107,8 +105,7 @@ it('should snapshot details visibility', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - group: - - text: "Summary" + - group: "Summary" `); }); @@ -151,8 +148,7 @@ it('should snapshot integration', async ({ page }) => { - text: "Open source projects and samples from Microsoft" - list: - listitem: - - group: - - text: "Verified" + - group: "Verified" - listitem: - link "Sponsor" `); @@ -168,12 +164,10 @@ it('should support multiline text', async ({ page }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - paragraph: - - text: "Line 1 Line 2 Line 3" + - paragraph: "Line 1 Line 2 Line 3" `); await expect(page.locator('body')).toMatchAriaSnapshot(` - - paragraph: - - text: | + - paragraph: | Line 1 Line 2 Line 3 @@ -388,8 +382,7 @@ it('should include pseudo codepoints', async ({ page, server }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - paragraph: - - text: "\ueab2hello" + - paragraph: "\ueab2hello" `); }); @@ -403,7 +396,6 @@ it('check aria-hidden text', async ({ page, server }) => { `); await checkAndMatchSnapshot(page.locator('body'), ` - - paragraph: - - text: "hello" + - paragraph: "hello" `); }); diff --git a/tests/page/to-match-aria-snapshot.spec.ts b/tests/page/to-match-aria-snapshot.spec.ts index 1335be5a59..8050c3b569 100644 --- a/tests/page/to-match-aria-snapshot.spec.ts +++ b/tests/page/to-match-aria-snapshot.spec.ts @@ -43,8 +43,8 @@ test('should match list with accessible name', async ({ page }) => { `); await expect(page.locator('body')).toMatchAriaSnapshot(` - list "my list": - - listitem: one - - listitem: two + - listitem: "one" + - listitem: "two" `); }); @@ -90,7 +90,7 @@ test('should allow text nodes', async ({ page }) => { await expect(page.locator('body')).toMatchAriaSnapshot(` - heading "Microsoft" - - text: Open source projects and samples from Microsoft + - text: "Open source projects and samples from Microsoft" `); }); @@ -103,7 +103,7 @@ test('details visibility', async ({ page }) => { `); await expect(page.locator('body')).toMatchAriaSnapshot(` - - group: Summary + - group: "Summary" `); }); diff --git a/tests/playwright-test/ui-mode-test-run.spec.ts b/tests/playwright-test/ui-mode-test-run.spec.ts index 0da2940e96..b30f901654 100644 --- a/tests/playwright-test/ui-mode-test-run.spec.ts +++ b/tests/playwright-test/ui-mode-test-run.spec.ts @@ -65,19 +65,19 @@ test('should run visible', async ({ runUITest }) => { - tree: - treeitem "[icon-error] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} - - treeitem ${/\[icon-error\] fails \d+ms/} [selected]: + - treeitem ${/\[icon-check\] passes/} + - treeitem ${/\[icon-error\] fails/} [selected]: - button "Run" - button "Show source" - button "Watch" - treeitem "[icon-error] suite" - treeitem "[icon-error] b.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} - - treeitem ${/\[icon-error\] fails \d+ms/} + - treeitem ${/\[icon-check\] passes/} + - treeitem ${/\[icon-error\] fails/} - treeitem "[icon-check] c.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} + - treeitem ${/\[icon-check\] passes/} - treeitem "[icon-circle-slash] skipped" `); @@ -125,7 +125,7 @@ test('should run on hover', async ({ runUITest }) => { - tree: - treeitem "[icon-circle-outline] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/}: + - treeitem ${/\[icon-check\] passes/}: - button "Run" - button "Show source" - button "Watch" @@ -185,7 +185,7 @@ test('should run on Enter', async ({ runUITest }) => { - treeitem "[icon-error] a.test.ts" [expanded]: - group: - treeitem "[icon-circle-outline] passes" - - treeitem ${/\[icon-error\] fails \d+ms/} [selected]: + - treeitem ${/\[icon-error\] fails/} [selected]: - button "Run" - button "Show source" - button "Watch" @@ -225,19 +225,19 @@ test('should run by project', async ({ runUITest }) => { - tree: - treeitem "[icon-error] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} - - treeitem ${/\[icon-error\] fails \d+ms/} [selected]: + - treeitem ${/\[icon-check\] passes/} + - treeitem ${/\[icon-error\] fails/} [selected]: - button "Run" - button "Show source" - button "Watch" - treeitem "[icon-error] suite" - treeitem "[icon-error] b.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} - - treeitem ${/\[icon-error\] fails \d+ms/} + - treeitem ${/\[icon-check\] passes/} + - treeitem ${/\[icon-error\] fails/} - treeitem "[icon-check] c.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} + - treeitem ${/\[icon-check\] passes/} - treeitem "[icon-circle-slash] skipped" `); @@ -299,14 +299,14 @@ test('should run by project', async ({ runUITest }) => { - tree: - treeitem "[icon-error] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-circle-outline\] passes \d+ms/} [expanded] [selected]: + - treeitem ${/\[icon-circle-outline\] passes/} [expanded] [selected]: - button "Run" - button "Show source" - button "Watch" - group: - - treeitem ${/\[icon-check\] foo \d+ms/} + - treeitem ${/\[icon-check\] foo/} - treeitem ${/\[icon-circle-outline\] bar/} - - treeitem ${/\[icon-error\] fails \d+ms/} + - treeitem ${/\[icon-error\] fails/} `); await expect(page.getByText('Projects: foo bar')).toBeVisible(); @@ -333,17 +333,17 @@ test('should run by project', async ({ runUITest }) => { - tree: - treeitem "[icon-error] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} [expanded]: + - treeitem ${/\[icon-check\] passes/} [expanded]: - group: - - treeitem ${/\[icon-check\] foo \d+ms/} - - treeitem ${/\[icon-check\] bar \d+ms/} - - treeitem ${/\[icon-error\] fails \d+ms/} [expanded]: + - treeitem ${/\[icon-check\] foo/} + - treeitem ${/\[icon-check\] bar/} + - treeitem ${/\[icon-error\] fails/} [expanded]: - group: - - treeitem ${/\[icon-error\] foo \d+ms/} [selected]: + - treeitem ${/\[icon-error\] foo/} [selected]: - button "Run" - button "Show source" - button "Watch" - - treeitem ${/\[icon-error\] bar \d+ms/} + - treeitem ${/\[icon-error\] bar/} - treeitem ${/\[icon-error\] suite/} - treeitem "[icon-error] b.test.ts" [expanded]: - group: @@ -385,7 +385,7 @@ test('should stop', async ({ runUITest }) => { - treeitem "[icon-loading] a.test.ts" [expanded]: - group: - treeitem "[icon-circle-slash] test 0" - - treeitem ${/\[icon-check\] test 1 \d+ms/} + - treeitem ${/\[icon-check\] test 1/} - treeitem ${/\[icon-loading\] test 2/} - treeitem ${/\[icon-clock\] test 3/} `); @@ -408,7 +408,7 @@ test('should stop', async ({ runUITest }) => { - treeitem "[icon-circle-outline] a.test.ts" [expanded]: - group: - treeitem "[icon-circle-slash] test 0" - - treeitem ${/\[icon-check\] test 1 \d+ms/} + - treeitem ${/\[icon-check\] test 1/} - treeitem ${/\[icon-circle-outline\] test 2/} - treeitem ${/\[icon-circle-outline\] test 3/} `); @@ -478,19 +478,19 @@ test('should show time', async ({ runUITest }) => { - tree: - treeitem "[icon-error] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} - - treeitem ${/\[icon-error\] fails \d+ms/} [selected]: + - treeitem ${/\[icon-check\] passes \d+m?s/} + - treeitem ${/\[icon-error\] fails \d+m?s/} [selected]: - button "Run" - button "Show source" - button "Watch" - treeitem "[icon-error] suite" - treeitem "[icon-error] b.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} - - treeitem ${/\[icon-error\] fails \d+ms/} + - treeitem ${/\[icon-check\] passes \d+m?s/} + - treeitem ${/\[icon-error\] fails \d+m?s/} - treeitem "[icon-check] c.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] passes \d+ms/} + - treeitem ${/\[icon-check\] passes \d+m?s/} - treeitem "[icon-circle-slash] skipped" `); @@ -522,7 +522,7 @@ test('should show test.fail as passing', async ({ runUITest }) => { - tree: - treeitem "[icon-check] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] should fail \d+ms/} + - treeitem ${/\[icon-check\] should fail \d+m?s/} `); await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); @@ -558,7 +558,7 @@ test('should ignore repeatEach', async ({ runUITest }) => { - tree: - treeitem "[icon-check] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] should pass \d+ms/} + - treeitem ${/\[icon-check\] should pass/} `); await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); @@ -593,7 +593,7 @@ test('should remove output folder before test run', async ({ runUITest }) => { - tree: - treeitem "[icon-check] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] should pass \d+ms/} + - treeitem ${/\[icon-check\] should pass/} `); await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); @@ -608,7 +608,7 @@ test('should remove output folder before test run', async ({ runUITest }) => { - tree: - treeitem "[icon-check] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] should pass \d+ms/} + - treeitem ${/\[icon-check\] should pass/} `); await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); @@ -656,7 +656,7 @@ test('should show proper total when using deps', async ({ runUITest }) => { - tree: - treeitem "[icon-circle-outline] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] run @setup setup \d+ms/} [selected]: + - treeitem ${/\[icon-check\] run @setup setup/} [selected]: - button "Run" - button "Show source" - button "Watch" @@ -676,8 +676,8 @@ test('should show proper total when using deps', async ({ runUITest }) => { - tree: - treeitem "[icon-check] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] run @setup setup \d+ms/} - - treeitem ${/\[icon-check\] run @chromium chromium \d+ms/} [selected]: + - treeitem ${/\[icon-check\] run @setup setup/} + - treeitem ${/\[icon-check\] run @chromium chromium/} [selected]: - button "Run" - button "Show source" - button "Watch" @@ -746,7 +746,7 @@ test('should respect --tsconfig option', { - tree: - treeitem "[icon-check] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] test \d+ms/} + - treeitem ${/\[icon-check\] test/} `); await expect(page.getByTestId('status-line')).toHaveText('1/1 passed (100%)'); @@ -775,7 +775,7 @@ test('should respect --ignore-snapshots option', { - tree: - treeitem "[icon-check] a.test.ts" [expanded]: - group: - - treeitem ${/\[icon-check\] snapshot \d+ms/} + - treeitem ${/\[icon-check\] snapshot/} `); }); @@ -788,4 +788,4 @@ test('should show funny messages', async ({ runUITest }) => { await expect(schmettingsHeader).toBeVisible(); await schmettingsHeader.click(); await expect(page.getByRole('checkbox', { name: 'Fart mode' })).toBeVisible(); -}); \ No newline at end of file +}); diff --git a/tests/playwright-test/update-aria-snapshot.spec.ts b/tests/playwright-test/update-aria-snapshot.spec.ts new file mode 100644 index 0000000000..092d408191 --- /dev/null +++ b/tests/playwright-test/update-aria-snapshot.spec.ts @@ -0,0 +1,48 @@ +/** + * Copyright Microsoft Corporation. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from 'fs'; +import { test, expect } from './playwright-test-fixtures'; + +test('should update snapshot with the update-snapshots flag', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'a.spec.ts': ` + import { test, expect } from '@playwright/test'; + test('test', async ({ page }) => { + await page.setContent(\`

hello

\`); + await expect(page.locator('body')).toMatchAriaSnapshot(\` + - heading "world" + \`); + }); + ` + }, { 'update-snapshots': true }); + + expect(result.exitCode).toBe(0); + const patchPath = testInfo.outputPath('test-results/rebaselines.patch'); + const data = fs.readFileSync(patchPath, 'utf-8'); + expect(data).toBe(`--- a/a.spec.ts ++++ b/a.spec.ts +@@ -3,7 +3,7 @@ + test('test', async ({ page }) => { + await page.setContent(\`

hello

\`); + await expect(page.locator('body')).toMatchAriaSnapshot(\` +- - heading "world" ++ - heading "hello" [level=1] + \`); + }); + +`); +}); From 9707e978671bb77b44537086839610b730eca544 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 25 Oct 2024 00:36:03 -0700 Subject: [PATCH 005/165] chore: revert #33228 - it needs a proper review (#33284) --- .../src/server/trace/viewer/traceViewer.ts | 9 +------ packages/trace-viewer/.gitignore | 2 -- packages/trace-viewer/src/embedded.tsx | 2 +- packages/trace-viewer/src/index.tsx | 2 +- packages/trace-viewer/src/recorder.tsx | 2 +- .../trace-viewer/src/sw/traceModelBackends.ts | 27 +++++++------------ packages/trace-viewer/src/ui/uiModeView.tsx | 7 ++--- packages/trace-viewer/src/uiMode.tsx | 2 +- packages/trace-viewer/vite.config.ts | 3 --- packages/trace-viewer/vite.sw.config.ts | 2 +- .../playwright-test/ui-mode-test-run.spec.ts | 11 -------- utils/build/build.js | 26 ------------------ 12 files changed, 18 insertions(+), 77 deletions(-) diff --git a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts index 32cbba0982..a49148e061 100644 --- a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts +++ b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts @@ -125,14 +125,7 @@ export async function installRootRedirect(server: HttpServer, traceUrls: string[ for (const reporter of options.reporter || []) params.append('reporter', reporter); - let baseUrl = ''; - if (process.env.PW_HMR === '1') { - params.set('testServerPort', '' + server.port()); - baseUrl = 'http://localhost:44223'; // port is hardcoded in build.js - } - - const urlPath = `${baseUrl}/trace/${options.webApp || 'index.html'}?${params.toString()}`; - + const urlPath = `./trace/${options.webApp || 'index.html'}?${params.toString()}`; server.routePath('/', (_, response) => { response.statusCode = 302; response.setHeader('Location', urlPath); diff --git a/packages/trace-viewer/.gitignore b/packages/trace-viewer/.gitignore index 1e3942879c..a547bf36d8 100644 --- a/packages/trace-viewer/.gitignore +++ b/packages/trace-viewer/.gitignore @@ -22,5 +22,3 @@ dist-ssr *.njsproj *.sln *.sw? - -public/sw.bundle.js* diff --git a/packages/trace-viewer/src/embedded.tsx b/packages/trace-viewer/src/embedded.tsx index cc61703baf..4f1503dcf2 100644 --- a/packages/trace-viewer/src/embedded.tsx +++ b/packages/trace-viewer/src/embedded.tsx @@ -45,7 +45,7 @@ import { EmbeddedWorkbenchLoader } from './ui/embeddedWorkbenchLoader'; if (window.location.protocol !== 'file:') { if (!navigator.serviceWorker) throw new Error(`Service workers are not supported.\nMake sure to serve the Trace Viewer (${window.location}) via HTTPS or localhost.`); - navigator.serviceWorker.register('sw.bundle.js' + window.location.search); + navigator.serviceWorker.register('sw.bundle.js'); if (!navigator.serviceWorker.controller) { await new Promise(f => { navigator.serviceWorker.oncontrollerchange = () => f(); diff --git a/packages/trace-viewer/src/index.tsx b/packages/trace-viewer/src/index.tsx index 2296cb0090..a737d9017f 100644 --- a/packages/trace-viewer/src/index.tsx +++ b/packages/trace-viewer/src/index.tsx @@ -27,7 +27,7 @@ import { WorkbenchLoader } from './ui/workbenchLoader'; await new Promise(f => setTimeout(f, 1000)); if (!navigator.serviceWorker) throw new Error(`Service workers are not supported.\nMake sure to serve the Trace Viewer (${window.location}) via HTTPS or localhost.`); - navigator.serviceWorker.register('sw.bundle.js' + window.location.search); + navigator.serviceWorker.register('sw.bundle.js'); if (!navigator.serviceWorker.controller) { await new Promise(f => { navigator.serviceWorker.oncontrollerchange = () => f(); diff --git a/packages/trace-viewer/src/recorder.tsx b/packages/trace-viewer/src/recorder.tsx index 6239df86ab..5e6b9764e3 100644 --- a/packages/trace-viewer/src/recorder.tsx +++ b/packages/trace-viewer/src/recorder.tsx @@ -26,7 +26,7 @@ import { RecorderView } from './ui/recorder/recorderView'; if (window.location.protocol !== 'file:') { if (!navigator.serviceWorker) throw new Error(`Service workers are not supported.\nMake sure to serve the Recorder (${window.location}) via HTTPS or localhost.`); - navigator.serviceWorker.register('sw.bundle.js' + window.location.search); + navigator.serviceWorker.register('sw.bundle.js'); if (!navigator.serviceWorker.controller) { await new Promise(f => { navigator.serviceWorker.oncontrollerchange = () => f(); diff --git a/packages/trace-viewer/src/sw/traceModelBackends.ts b/packages/trace-viewer/src/sw/traceModelBackends.ts index 4f5bd49624..19c5fc2dee 100644 --- a/packages/trace-viewer/src/sw/traceModelBackends.ts +++ b/packages/trace-viewer/src/sw/traceModelBackends.ts @@ -30,8 +30,9 @@ export class ZipTraceModelBackend implements TraceModelBackend { constructor(traceURL: string, progress: Progress) { this._traceURL = traceURL; + zipjs.configure({ baseURL: self.location.href } as any); this._zipReader = new zipjs.ZipReader( - new zipjs.HttpReader(formatTraceFileUrl(traceURL), { mode: 'cors', preventHeadRequest: true } as any), + new zipjs.HttpReader(formatUrl(traceURL), { mode: 'cors', preventHeadRequest: true } as any), { useWebWorkers: false }); this._entriesPromise = this._zipReader.getEntries({ onprogress: progress }).then(entries => { const map = new Map(); @@ -86,7 +87,7 @@ export class FetchTraceModelBackend implements TraceModelBackend { constructor(traceURL: string) { this._traceURL = traceURL; - this._entriesPromise = fetch(formatTraceFileUrl(traceURL)).then(async response => { + this._entriesPromise = fetch('/trace/file?path=' + encodeURIComponent(traceURL)).then(async response => { const json = JSON.parse(await response.text()); const entries = new Map(); for (const entry of json.entries) @@ -128,22 +129,14 @@ export class FetchTraceModelBackend implements TraceModelBackend { const fileName = entries.get(entryName); if (!fileName) return; - - return fetch(formatTraceFileUrl(fileName)); + return fetch('/trace/file?path=' + encodeURIComponent(fileName)); } } -const baseURL = new URL(self.location.href); -baseURL.port = baseURL.searchParams.get('testServerPort') ?? baseURL.port; - -function formatTraceFileUrl(trace: string) { - if (trace.startsWith('https://www.dropbox.com/')) - return 'https://dl.dropboxusercontent.com/' + trace.substring('https://www.dropbox.com/'.length); - - if (trace.startsWith('http') || trace.startsWith('blob')) - return trace; - - const url = new URL('/trace/file', baseURL); - url.searchParams.set('path', trace); - return url.toString(); +function formatUrl(trace: string) { + let url = trace.startsWith('http') || trace.startsWith('blob') ? trace : `file?path=${encodeURIComponent(trace)}`; + // Dropbox does not support cors. + if (url.startsWith('https://www.dropbox.com/')) + url = 'https://dl.dropboxusercontent.com/' + url.substring('https://www.dropbox.com/'.length); + return url; } diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index 37b6a6bcf6..69a5988641 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -50,7 +50,6 @@ const searchParams = new URLSearchParams(window.location.search); const guid = searchParams.get('ws'); const wsURL = new URL(`../${guid}`, window.location.toString()); wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:'); -wsURL.port = searchParams.get('testServerPort') ?? window.location.port; const queryParams = { args: searchParams.getAll('arg'), grep: searchParams.get('grep') || undefined, @@ -69,7 +68,6 @@ const isMac = navigator.platform === 'MacIntel'; export const UIModeView: React.FC<{}> = ({ }) => { - const isJokesDay = new Date().getMonth() === 3 && new Date().getDate() === 1; const [filterText, setFilterText] = React.useState(''); const [isShowingOutput, setIsShowingOutput] = React.useState(false); const [outputContainsError, setOutputContainsError] = React.useState(false); @@ -442,7 +440,7 @@ export const UIModeView: React.FC<{}> = ({ sidebar={
Playwright logo -
{isJokesDay ? 'Claywright' : 'Playwright'}
+
Playwright
reloadTests()} disabled={isRunningTest || isLoading}>
{ setIsShowingOutput(!isShowingOutput); }} /> @@ -518,11 +516,10 @@ export const UIModeView: React.FC<{}> = ({ style={{ marginLeft: 5 }} title={settingsVisible ? 'Hide Settings' : 'Show Settings'} /> -
{isJokesDay ? 'Schmettings' : 'Settings'}
+
Settings
{settingsVisible && }
} diff --git a/packages/trace-viewer/src/uiMode.tsx b/packages/trace-viewer/src/uiMode.tsx index 4702621e5d..5dac2082e8 100644 --- a/packages/trace-viewer/src/uiMode.tsx +++ b/packages/trace-viewer/src/uiMode.tsx @@ -27,7 +27,7 @@ import { UIModeView } from './ui/uiModeView'; await new Promise(f => setTimeout(f, 1000)); if (!navigator.serviceWorker) throw new Error(`Service workers are not supported.\nMake sure to serve the website (${window.location}) via HTTPS or localhost.`); - navigator.serviceWorker.register('sw.bundle.js' + window.location.search); + navigator.serviceWorker.register('sw.bundle.js'); if (!navigator.serviceWorker.controller) { await new Promise(f => { navigator.serviceWorker.oncontrollerchange = () => f(); diff --git a/packages/trace-viewer/vite.config.ts b/packages/trace-viewer/vite.config.ts index 541b1ab6b6..0e2e9cb642 100644 --- a/packages/trace-viewer/vite.config.ts +++ b/packages/trace-viewer/vite.config.ts @@ -26,9 +26,6 @@ export default defineConfig({ react(), bundle() ], - define: { - 'process.env': {}, - }, resolve: { alias: { '@injected': path.resolve(__dirname, '../playwright-core/src/server/injected'), diff --git a/packages/trace-viewer/vite.sw.config.ts b/packages/trace-viewer/vite.sw.config.ts index a0dd99036a..dc621448b9 100644 --- a/packages/trace-viewer/vite.sw.config.ts +++ b/packages/trace-viewer/vite.sw.config.ts @@ -36,7 +36,7 @@ export default defineConfig({ }, }, build: { - outDir: path.resolve(__dirname, 'public'), + outDir: path.resolve(__dirname, '../playwright-core/lib/vite/traceViewer'), // Output dir is shared with vite.config.ts, clearing it here is racy. emptyOutDir: false, rollupOptions: { diff --git a/tests/playwright-test/ui-mode-test-run.spec.ts b/tests/playwright-test/ui-mode-test-run.spec.ts index b30f901654..72eeda4655 100644 --- a/tests/playwright-test/ui-mode-test-run.spec.ts +++ b/tests/playwright-test/ui-mode-test-run.spec.ts @@ -778,14 +778,3 @@ test('should respect --ignore-snapshots option', { - treeitem ${/\[icon-check\] snapshot/} `); }); - -test('should show funny messages', async ({ runUITest }) => { - const { page } = await runUITest(basicTestTree); - await page.clock.setFixedTime('2025-04-01'); - - await expect(page.getByText('Claywright')).toBeVisible(); - const schmettingsHeader = page.getByText('Schmettings'); - await expect(schmettingsHeader).toBeVisible(); - await schmettingsHeader.click(); - await expect(page.getByRole('checkbox', { name: 'Fart mode' })).toBeVisible(); -}); diff --git a/utils/build/build.js b/utils/build/build.js index a1ba052d51..7a289bd5ed 100644 --- a/utils/build/build.js +++ b/utils/build/build.js @@ -275,21 +275,6 @@ for (const bundle of bundles) { }); } -// initial service worker build. -steps.push({ - command: 'npx', - args: [ - 'vite', - '--config', - 'vite.sw.config.ts', - 'build', - ...(withSourceMaps ? ['--sourcemap=inline'] : []), - ], - shell: true, - cwd: path.join(__dirname, '..', '..', 'packages', 'trace-viewer'), - concurrent: false, -}); - // Build/watch web packages. for (const webPackage of ['html-reporter', 'recorder', 'trace-viewer']) { steps.push({ @@ -305,7 +290,6 @@ for (const webPackage of ['html-reporter', 'recorder', 'trace-viewer']) { concurrent: true, }); } - // Build/watch trace viewer service worker. steps.push({ command: 'npx', @@ -322,16 +306,6 @@ steps.push({ concurrent: true, }); -// web packages dev server -if (watchMode) { - steps.push({ - command: 'npx', - args: ['vite', '--port', '44223', '--base', '/trace/'], - shell: true, - cwd: path.join(__dirname, '..', '..', 'packages', 'trace-viewer'), - concurrent: true, - }); -} // Generate injected. onChanges.push({ From 7e00112fec03d4f7581c065d8796159488aa6fb5 Mon Sep 17 00:00:00 2001 From: DetachHead <57028336+DetachHead@users.noreply.github.com> Date: Fri, 25 Oct 2024 23:27:14 +1000 Subject: [PATCH 006/165] docs(python): improve the typing example with the pytest plugin (#33296) --- docs/src/test-runners-python.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/src/test-runners-python.md b/docs/src/test-runners-python.md index 0ad4043066..d07fa2e53f 100644 --- a/docs/src/test-runners-python.md +++ b/docs/src/test-runners-python.md @@ -99,7 +99,7 @@ See [Running Tests](./running-tests.md) for general information on `pytest` opti ## Examples -### Configure Mypy typings for auto-completion +### Configure typings for auto-completion ```py title="test_my_application.py" from playwright.sync_api import Page @@ -109,6 +109,8 @@ 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 Run tests with slow mo with the `--slowmo` argument. From f98531baee31163d1b64a5120c70a0707379c4ed Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 25 Oct 2024 10:33:43 -0700 Subject: [PATCH 007/165] chore: remove check for node < 16 (#33301) --- packages/playwright-core/src/utilsBundle.ts | 4 ---- packages/playwright/src/common/configLoader.ts | 5 ----- tests/config/browserTest.ts | 6 ++++++ tests/library/browsercontext-fetch.spec.ts | 9 ++++----- tests/library/browsercontext-storage-state.spec.ts | 4 ++-- tests/library/browsertype-launch.spec.ts | 5 ++--- tests/library/client-certificates.spec.ts | 4 ++-- 7 files changed, 16 insertions(+), 21 deletions(-) diff --git a/packages/playwright-core/src/utilsBundle.ts b/packages/playwright-core/src/utilsBundle.ts index f9dc8347f7..f57a6346f7 100644 --- a/packages/playwright-core/src/utilsBundle.ts +++ b/packages/playwright-core/src/utilsBundle.ts @@ -43,12 +43,8 @@ import type { StackFrame } from '@protocol/channels'; const StackUtils: typeof import('../bundles/utils/node_modules/@types/stack-utils') = require('./utilsBundleImpl').StackUtils; const stackUtils = new StackUtils({ internals: StackUtils.nodeInternals() }); -const nodeInternals = StackUtils.nodeInternals(); -const nodeMajorVersion = +process.versions.node.split('.')[0]; export function parseStackTraceLine(line: string): StackFrame | null { - if (!process.env.PWDEBUGIMPL && nodeMajorVersion < 16 && nodeInternals.some(internal => internal.test(line))) - return null; const frame = stackUtils.parseLine(line); if (!frame) return null; diff --git a/packages/playwright/src/common/configLoader.ts b/packages/playwright/src/common/configLoader.ts index 37a886d3e8..13d7eac187 100644 --- a/packages/playwright/src/common/configLoader.ts +++ b/packages/playwright/src/common/configLoader.ts @@ -378,11 +378,6 @@ export function restartWithExperimentalTsEsm(configFile: string | undefined, for // Now check for the newer API presence. if (!require('node:module').register) { - // Older API is experimental, only supported on Node 16+. - const nodeVersion = +process.versions.node.split('.')[0]; - if (nodeVersion < 16) - return false; - // With older API requiring a process restart, do so conditionally on the config. const configIsModule = !!configFile && fileIsModule(configFile); if (!force && !configIsModule) diff --git a/tests/config/browserTest.ts b/tests/config/browserTest.ts index 6a178bbaf2..8e79da1f8e 100644 --- a/tests/config/browserTest.ts +++ b/tests/config/browserTest.ts @@ -36,6 +36,7 @@ export type BrowserTestWorkerFixtures = PageWorkerFixtures & { browserType: BrowserType; isAndroid: boolean; isElectron: boolean; + nodeVersion: { major: number, minor: number, patch: number }; bidiTestSkipPredicate: (info: TestInfo) => boolean; }; @@ -96,6 +97,11 @@ const test = baseTest.extend await run(Number(browserVersion.split('.')[0])); }, { scope: 'worker' }], + nodeVersion: [async ({}, use) => { + const [major, minor, patch] = process.versions.node.split('.'); + await use({ major: +major, minor: +minor, patch: +patch }); + }, { scope: 'worker' }], + isAndroid: [false, { scope: 'worker' }], isElectron: [false, { scope: 'worker' }], electronMajorVersion: [0, { scope: 'worker' }], diff --git a/tests/library/browsercontext-fetch.spec.ts b/tests/library/browsercontext-fetch.spec.ts index ae5cf5e7d5..abbc4f64a4 100644 --- a/tests/library/browsercontext-fetch.spec.ts +++ b/tests/library/browsercontext-fetch.spec.ts @@ -880,9 +880,9 @@ it('should respect timeout after redirects', async function({ context, server }) expect(error.message).toContain(`Request timed out after 100ms`); }); -it('should not hang on a brotli encoded Range request', async ({ context, server }) => { +it('should not hang on a brotli encoded Range request', async ({ context, server, nodeVersion }) => { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/18190' }); - it.skip(+process.versions.node.split('.')[0] < 18); + it.skip(nodeVersion.major < 18); const encodedRequestPayload = zlib.brotliCompressSync(Buffer.from('A')); server.setRoute('/brotli', (req, res) => { @@ -1094,10 +1094,9 @@ it('should support multipart/form-data and keep the order', async function({ con expect(response.status()).toBe(200); }); -it('should support repeating names in multipart/form-data', async function({ context, server }) { +it('should support repeating names in multipart/form-data', async function({ context, server, nodeVersion }) { it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/28070' }); - const nodeVersion = +process.versions.node.split('.')[0]; - it.skip(nodeVersion < 20, 'File is not available in Node.js < 20. FormData is not available in Node.js < 18'); + it.skip(nodeVersion.major < 20, 'File is not available in Node.js < 20. FormData is not available in Node.js < 18'); const postBodyPromise = new Promise(resolve => { server.setRoute('/empty.html', async (req, res) => { resolve((await req.postBody).toString('utf-8')); diff --git a/tests/library/browsercontext-storage-state.spec.ts b/tests/library/browsercontext-storage-state.spec.ts index 02db42506d..d71657d1be 100644 --- a/tests/library/browsercontext-storage-state.spec.ts +++ b/tests/library/browsercontext-storage-state.spec.ts @@ -204,13 +204,13 @@ it('should handle missing file', async ({ contextFactory }, testInfo) => { expect(error.message).toContain(`Error reading storage state from ${file}:\nENOENT`); }); -it('should handle malformed file', async ({ contextFactory }, testInfo) => { +it('should handle malformed file', async ({ contextFactory, nodeVersion }, testInfo) => { const file = testInfo.outputPath('state.json'); fs.writeFileSync(file, 'not-json', 'utf-8'); const error = await contextFactory({ storageState: file, }).catch(e => e); - if (+process.versions.node.split('.')[0] > 18) + if (nodeVersion.major > 18) expect(error.message).toContain(`Error reading storage state from ${file}:\nUnexpected token 'o', \"not-json\" is not valid JSON`); else expect(error.message).toContain(`Error reading storage state from ${file}:\nUnexpected token o in JSON at position 1`); diff --git a/tests/library/browsertype-launch.spec.ts b/tests/library/browsertype-launch.spec.ts index 011bc8a50d..75198f4f8b 100644 --- a/tests/library/browsertype-launch.spec.ts +++ b/tests/library/browsertype-launch.spec.ts @@ -130,9 +130,8 @@ it('should be callable twice', async ({ browserType }) => { await browser.close(); }); -it('should allow await using', async ({ browserType }) => { - const nodeVersion = +process.versions.node.split('.')[0]; - it.skip(nodeVersion < 18); +it('should allow await using', async ({ browserType, nodeVersion }) => { + it.skip(nodeVersion.major < 18); let b: Browser; let c: BrowserContext; diff --git a/tests/library/client-certificates.spec.ts b/tests/library/client-certificates.spec.ts index 8e0a4cdf47..648fef0d75 100644 --- a/tests/library/client-certificates.spec.ts +++ b/tests/library/client-certificates.spec.ts @@ -727,9 +727,9 @@ test.describe('browser', () => { await browser.close(); }); - test('should return target connection errors when using http2', async ({ browser, startCCServer, asset, browserName, isMac, isLinux }) => { + test('should return target connection errors when using http2', async ({ browser, startCCServer, asset, browserName, isMac, nodeVersion }) => { test.skip(browserName === 'webkit' && isMac, 'WebKit on macOS does not proxy localhost'); - test.skip(+process.versions.node.split('.')[0] < 20, 'http2.performServerHandshake is not supported in older Node.js versions'); + test.skip(nodeVersion.major < 20, 'http2.performServerHandshake is not supported in older Node.js versions'); const serverURL = await startCCServer({ http2: true }); const page = await browser.newPage({ From 1e8884621a2a151837fc7d80617b0c5a669013e3 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 25 Oct 2024 12:36:39 -0700 Subject: [PATCH 008/165] chore: unify toHaveScreenshot error formatting (#33300) --- packages/html-reporter/src/testResultView.tsx | 3 +- packages/playwright-core/src/client/page.ts | 3 +- .../playwright-core/src/protocol/validator.ts | 3 +- packages/playwright-core/src/server/page.ts | 3 +- .../src/matchers/toMatchSnapshot.ts | 39 ++++++++++--------- packages/protocol/src/channels.ts | 4 +- packages/protocol/src/protocol.yml | 3 +- tests/page/expect-matcher-result.spec.ts | 4 +- tests/playwright-test/golden.spec.ts | 5 ++- tests/playwright-test/reporter-html.spec.ts | 4 +- .../to-have-screenshot.spec.ts | 2 +- 11 files changed, 41 insertions(+), 32 deletions(-) diff --git a/packages/html-reporter/src/testResultView.tsx b/packages/html-reporter/src/testResultView.tsx index bb18422dd0..3a562f3fcf 100644 --- a/packages/html-reporter/src/testResultView.tsx +++ b/packages/html-reporter/src/testResultView.tsx @@ -152,7 +152,8 @@ export const TestResultView: React.FC<{ function classifyErrors(testErrors: string[], diffs: ImageDiff[]) { return testErrors.map(error => { - if (error.includes('Screenshot comparison failed:')) { + const firstLine = error.split('\n')[0]; + if (firstLine.includes('toHaveScreenshot') || firstLine.includes('toMatchSnapshot')) { const matchingDiff = diffs.find(diff => { const attachmentName = diff.actual?.attachment.name; return attachmentName && error.includes(attachmentName); diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 6654294edd..c8d816f62a 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -63,6 +63,7 @@ type PDFOptions = Omit & export type ExpectScreenshotOptions = Omit & { expected?: Buffer, locator?: api.Locator, + timeout: number, isNot: boolean, mask?: api.Locator[], }; @@ -589,7 +590,7 @@ export class Page extends ChannelOwner implements api.Page return result.binary; } - async _expectScreenshot(options: ExpectScreenshotOptions): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[]}> { + async _expectScreenshot(options: ExpectScreenshotOptions): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[], timedOut?: boolean}> { const mask = options?.mask ? options?.mask.map(locator => ({ frame: (locator as Locator)._frame._channel, selector: (locator as Locator)._selector, diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 7bad26f498..fd30bbba09 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -1165,7 +1165,7 @@ scheme.PageReloadResult = tObject({ }); scheme.PageExpectScreenshotParams = tObject({ expected: tOptional(tBinary), - timeout: tOptional(tNumber), + timeout: tNumber, isNot: tBoolean, locator: tOptional(tObject({ frame: tChannel(['Frame']), @@ -1193,6 +1193,7 @@ scheme.PageExpectScreenshotResult = tObject({ errorMessage: tOptional(tString), actual: tOptional(tBinary), previous: tOptional(tBinary), + timedOut: tOptional(tBoolean), log: tOptional(tArray(tString)), }); scheme.PageScreenshotParams = tObject({ diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index b78bd91ee1..55f99250c8 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -674,11 +674,12 @@ export class Page extends SdkObject { throw e; let errorMessage = e.message; if (e instanceof TimeoutError && intermediateResult?.previous) - errorMessage = `Failed to take two consecutive stable screenshots. ${e.message}`; + errorMessage = `Failed to take two consecutive stable screenshots.`; return { log: e.message ? [...metadata.log, e.message] : metadata.log, ...intermediateResult, errorMessage, + timedOut: (e instanceof TimeoutError), }; }); } diff --git a/packages/playwright/src/matchers/toMatchSnapshot.ts b/packages/playwright/src/matchers/toMatchSnapshot.ts index 86504062d3..24b387f3ea 100644 --- a/packages/playwright/src/matchers/toMatchSnapshot.ts +++ b/packages/playwright/src/matchers/toMatchSnapshot.ts @@ -18,7 +18,7 @@ import type { Locator, Page } from 'playwright-core'; import type { ExpectScreenshotOptions, Page as PageEx } from 'playwright-core/lib/client/page'; import { currentTestInfo } from '../common/globals'; import type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils'; -import { getComparator, sanitizeForFilePath } from 'playwright-core/lib/utils'; +import { getComparator, isString, sanitizeForFilePath } from 'playwright-core/lib/utils'; import { addSuffixToFilePath, trimLongString, callLogText, @@ -31,7 +31,7 @@ import path from 'path'; import { mime } from 'playwright-core/lib/utilsBundle'; import type { TestInfoImpl } from '../worker/testInfo'; import type { ExpectMatcherState } from '../../types/test'; -import type { MatcherResult } from './matcherHint'; +import { matcherHint, type MatcherResult } from './matcherHint'; import type { FullProjectInternal } from '../common/config'; type NameOrSegments = string | string[]; @@ -250,16 +250,10 @@ class SnapshotHelper { expected: Buffer | string | undefined, previous: Buffer | string | undefined, diff: Buffer | string | undefined, - diffError: string | undefined, - log: string[] | undefined, - title = `${this.kind} comparison failed:`): ImageMatcherResult { - const output = [ - colors.red(title), - '', - ]; - if (diffError) - output.push(indent(diffError, ' ')); - + header: string, + diffError: string, + log: string[] | undefined): ImageMatcherResult { + const output = [`${header}${indent(diffError, ' ')}`]; if (expected !== undefined) { // Copy the expectation inside the `test-results/` folder for backwards compatibility, // so that one can upload `test-results/` directory and have all the data inside. @@ -338,7 +332,9 @@ export function toMatchSnapshot( return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true); } - return helper.handleDifferent(received, expected, undefined, result.diff, result.errorMessage, undefined); + const receiver = isString(received) ? 'string' : 'Buffer'; + const header = matcherHint(this, undefined, 'toMatchSnapshot', receiver, undefined, undefined); + return helper.handleDifferent(received, expected, undefined, result.diff, header, result.errorMessage, undefined); } export function toHaveScreenshotStepTitle( @@ -374,6 +370,7 @@ export async function toHaveScreenshot( throw new Error(`Screenshot name "${path.basename(helper.expectedPath)}" must have '.png' extension`); expectTypes(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot'); const style = await loadScreenshotStyles(helper.options.stylePath); + const timeout = helper.options.timeout ?? this.timeout; const expectScreenshotOptions: ExpectScreenshotOptions = { locator, animations: helper.options.animations ?? 'disabled', @@ -386,7 +383,7 @@ export async function toHaveScreenshot( scale: helper.options.scale ?? 'css', style, isNot: !!this.isNot, - timeout: helper.options.timeout ?? this.timeout, + timeout, comparator: helper.options.comparator, maxDiffPixels: helper.options.maxDiffPixels, maxDiffPixelRatio: helper.options.maxDiffPixelRatio, @@ -410,13 +407,16 @@ export async function toHaveScreenshot( if (helper.updateSnapshots === 'none' && !hasSnapshot) return helper.createMatcherResult(`A snapshot doesn't exist at ${helper.expectedPath}.`, false); + const receiver = locator ? 'locator' : 'page'; if (!hasSnapshot) { // Regenerate a new screenshot by waiting until two screenshots are the same. - const { actual, previous, diff, errorMessage, log } = await page._expectScreenshot(expectScreenshotOptions); + const { actual, previous, diff, errorMessage, log, timedOut } = await page._expectScreenshot(expectScreenshotOptions); // We tried re-generating new snapshot but failed. // This can be due to e.g. spinning animation, so we want to show it as a diff. - if (errorMessage) - return helper.handleDifferent(actual, undefined, previous, diff, undefined, log, errorMessage); + if (errorMessage) { + const header = matcherHint(this, locator, 'toHaveScreenshot', receiver, undefined, undefined, timedOut ? timeout : undefined); + return helper.handleDifferent(actual, undefined, previous, diff, header, errorMessage, log); + } // We successfully generated new screenshot. return helper.handleMissing(actual!); @@ -427,7 +427,7 @@ export async function toHaveScreenshot( // - regular matcher (i.e. not a `.not`) // - perhaps an 'all' flag to update non-matching screenshots expectScreenshotOptions.expected = await fs.promises.readFile(helper.expectedPath); - const { actual, previous, diff, errorMessage, log } = await page._expectScreenshot(expectScreenshotOptions); + const { actual, previous, diff, errorMessage, log, timedOut } = await page._expectScreenshot(expectScreenshotOptions); if (!errorMessage) return helper.handleMatching(); @@ -440,7 +440,8 @@ export async function toHaveScreenshot( return helper.createMatcherResult(helper.expectedPath + ' running with --update-snapshots, writing actual.', true); } - return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, errorMessage, log); + const header = matcherHint(this, undefined, 'toHaveScreenshot', receiver, undefined, undefined, timedOut ? timeout : undefined); + return helper.handleDifferent(actual, expectScreenshotOptions.expected, previous, diff, header, errorMessage, log); } function writeFileSync(aPath: string, content: Buffer | string) { diff --git a/packages/protocol/src/channels.ts b/packages/protocol/src/channels.ts index 7fcb815468..43e878a97a 100644 --- a/packages/protocol/src/channels.ts +++ b/packages/protocol/src/channels.ts @@ -2141,7 +2141,7 @@ export type PageReloadResult = { }; export type PageExpectScreenshotParams = { expected?: Binary, - timeout?: number, + timeout: number, isNot: boolean, locator?: { frame: FrameChannel, @@ -2166,7 +2166,6 @@ export type PageExpectScreenshotParams = { }; export type PageExpectScreenshotOptions = { expected?: Binary, - timeout?: number, locator?: { frame: FrameChannel, selector: string, @@ -2193,6 +2192,7 @@ export type PageExpectScreenshotResult = { errorMessage?: string, actual?: Binary, previous?: Binary, + timedOut?: boolean, log?: string[], }; export type PageScreenshotParams = { diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 98428cae2f..69830cf8e8 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -1482,7 +1482,7 @@ Page: expectScreenshot: parameters: expected: binary? - timeout: number? + timeout: number isNot: boolean locator: type: object? @@ -1501,6 +1501,7 @@ Page: errorMessage: string? actual: binary? previous: binary? + timedOut: boolean? log: type: array? items: string diff --git a/tests/page/expect-matcher-result.spec.ts b/tests/page/expect-matcher-result.spec.ts index 7767ecf5f6..fe983c6f63 100644 --- a/tests/page/expect-matcher-result.spec.ts +++ b/tests/page/expect-matcher-result.spec.ts @@ -295,7 +295,7 @@ test('toHaveScreenshot should populate matcherResult', async ({ page, server, is actual: expect.stringContaining('screenshot-sanity-actual'), expected: expect.stringContaining('screenshot-sanity-'), diff: expect.stringContaining('screenshot-sanity-diff'), - message: expect.stringContaining(`Screenshot comparison failed`), + message: expect.stringContaining(`expect(page).toHaveScreenshot(expected)`), name: 'toHaveScreenshot', pass: false, log: expect.any(Array), @@ -303,7 +303,7 @@ test('toHaveScreenshot should populate matcherResult', async ({ page, server, is printedReceived: expect.stringContaining('screenshot-sanity-actual'), }); - expect.soft(stripAnsi(e.toString())).toContain(`Error: Screenshot comparison failed: + expect.soft(stripAnsi(e.toString())).toContain(`Error: expect(page).toHaveScreenshot(expected) 23362 pixels (ratio 0.10 of all image pixels) are different. diff --git a/tests/playwright-test/golden.spec.ts b/tests/playwright-test/golden.spec.ts index 205eeee7f0..340851e384 100644 --- a/tests/playwright-test/golden.spec.ts +++ b/tests/playwright-test/golden.spec.ts @@ -223,7 +223,7 @@ test('should write detailed failure result to an output folder', async ({ runInl expect(result.exitCode).toBe(1); const outputText = result.output; - expect(outputText).toContain('Snapshot comparison failed:'); + expect(outputText).toContain('Error: expect(string).toMatchSnapshot(expected)'); const expectedSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-expected.txt'); const actualSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-actual.txt'); expect(outputText).toMatch(/Expected:.*a\.spec\.js-snapshots.snapshot\.txt/); @@ -635,7 +635,8 @@ test('should compare different PNG images', async ({ runInlineTest }, testInfo) const outputText = result.output; expect(result.exitCode).toBe(1); - expect(outputText).toContain('Screenshot comparison failed:'); + expect(outputText).toContain('Error: expect(Buffer).toMatchSnapshot(expected)'); + expect(outputText).toContain('1 pixels (ratio 1.00 of all image pixels) are different.'); const expectedSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-expected.png'); const actualSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-actual.png'); const diffSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-diff.png'); diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index 11169fadff..51734be9ff 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -330,7 +330,9 @@ for (const useIntermediateMergeReport of [true, false] as const) { await expect(page.locator('text=Image mismatch')).toHaveCount(1); await expect(page.locator('text=Snapshot mismatch')).toHaveCount(0); await expect(page.locator('.chip-header', { hasText: 'Screenshots' })).toHaveCount(0); - await expect(page.getByTestId('test-result-image-mismatch-tabs').locator('div')).toHaveText([ + const errorChip = page.getByTestId('test-screenshot-error-view'); + await expect(errorChip).toContainText('Failed to take two consecutive stable screenshots.'); + await expect(errorChip.getByTestId('test-result-image-mismatch-tabs').locator('div')).toHaveText([ 'Diff', 'Actual', 'Previous', diff --git a/tests/playwright-test/to-have-screenshot.spec.ts b/tests/playwright-test/to-have-screenshot.spec.ts index a4eb131f07..ee053cc130 100644 --- a/tests/playwright-test/to-have-screenshot.spec.ts +++ b/tests/playwright-test/to-have-screenshot.spec.ts @@ -551,7 +551,7 @@ test('should fail when screenshot is different pixels', async ({ runInlineTest } ` }); expect(result.exitCode).toBe(1); - expect(result.output).toContain('Screenshot comparison failed'); + expect(result.output).toContain('Error: expect(page).toHaveScreenshot(expected)'); expect(result.output).toContain('12345 pixels'); expect(result.output).toContain('Call log'); expect(result.output).toContain('ratio 0.02'); From 87b896e597f3d60f55505dae5b7e9f57abe03790 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 25 Oct 2024 13:31:55 -0700 Subject: [PATCH 009/165] chore: remove playwright-ct-vue2 (#33302) --- docs/src/test-components-js.md | 35 --- package-lock.json | 80 ------- packages/playwright-ct-vue2/.npmignore | 12 - packages/playwright-ct-vue2/README.md | 3 - packages/playwright-ct-vue2/cli.js | 20 -- packages/playwright-ct-vue2/hooks.d.ts | 37 --- packages/playwright-ct-vue2/hooks.mjs | 29 --- packages/playwright-ct-vue2/index.d.ts | 66 ------ packages/playwright-ct-vue2/index.js | 33 --- packages/playwright-ct-vue2/package.json | 42 ---- packages/playwright-ct-vue2/register.d.ts | 24 -- packages/playwright-ct-vue2/register.mjs | 21 -- .../playwright-ct-vue2/registerSource.mjs | 212 ------------------ tests/components/ct-vue2-cli/.gitignore | 23 -- tests/components/ct-vue2-cli/README.md | 24 -- tests/components/ct-vue2-cli/babel.config.js | 5 - tests/components/ct-vue2-cli/package.json | 45 ---- .../ct-vue2-cli/playwright.config.ts | 49 ---- .../ct-vue2-cli/playwright/index.html | 12 - .../ct-vue2-cli/playwright/index.js | 17 -- .../components/ct-vue2-cli/public/favicon.ico | Bin 4286 -> 0 bytes .../components/ct-vue2-cli/public/index.html | 17 -- tests/components/ct-vue2-cli/src/App.vue | 10 - .../ct-vue2-cli/src/assets/index.css | 20 -- .../ct-vue2-cli/src/assets/logo.png | Bin 6849 -> 0 bytes .../ct-vue2-cli/src/components/Button.vue | 10 - .../ct-vue2-cli/src/components/Component.vue | 3 - .../ct-vue2-cli/src/components/Counter.vue | 23 -- .../src/components/DefaultSlot.vue | 11 - .../src/components/EmptyTemplate.vue | 8 - .../ct-vue2-cli/src/components/NamedSlots.vue | 13 -- tests/components/ct-vue2-cli/src/main.js | 14 -- .../ct-vue2-cli/src/pages/DashboardPage.vue | 3 - .../ct-vue2-cli/src/pages/LoginPage.vue | 3 - .../ct-vue2-cli/src/router/index.js | 12 - .../components/ct-vue2-cli/src/shims-vue.d.ts | 4 - .../ct-vue2-cli/tests/events/events.spec.ts | 16 -- .../ct-vue2-cli/tests/events/events.spec.tsx | 28 --- .../ct-vue2-cli/tests/render/render.spec.ts | 25 --- .../ct-vue2-cli/tests/render/render.spec.tsx | 21 -- .../ct-vue2-cli/tests/slots/slots.spec.ts | 35 --- .../ct-vue2-cli/tests/slots/slots.spec.tsx | 48 ---- .../ct-vue2-cli/tests/unmount/unmount.spec.ts | 38 ---- .../tests/unmount/unmount.spec.tsx | 22 -- .../ct-vue2-cli/tests/update/update.spec.ts | 52 ----- .../ct-vue2-cli/tests/update/update.spec.tsx | 53 ----- .../tests/vue-router/vue-router.spec.ts | 13 -- .../tests/vue-router/vue-router.spec.tsx | 13 -- .../components/ct-vue2-cli/tsconfig.app.json | 12 - .../ct-vue2-cli/tsconfig.config.json | 8 - tests/components/ct-vue2-cli/tsconfig.json | 14 -- .../components/ct-vue2-cli/tsconfig.test.json | 11 - tests/components/ct-vue2-cli/vue.config.js | 4 - utils/workspace.js | 5 - 54 files changed, 1358 deletions(-) delete mode 100644 packages/playwright-ct-vue2/.npmignore delete mode 100644 packages/playwright-ct-vue2/README.md delete mode 100755 packages/playwright-ct-vue2/cli.js delete mode 100644 packages/playwright-ct-vue2/hooks.d.ts delete mode 100644 packages/playwright-ct-vue2/hooks.mjs delete mode 100644 packages/playwright-ct-vue2/index.d.ts delete mode 100644 packages/playwright-ct-vue2/index.js delete mode 100644 packages/playwright-ct-vue2/package.json delete mode 100644 packages/playwright-ct-vue2/register.d.ts delete mode 100644 packages/playwright-ct-vue2/register.mjs delete mode 100644 packages/playwright-ct-vue2/registerSource.mjs delete mode 100644 tests/components/ct-vue2-cli/.gitignore delete mode 100644 tests/components/ct-vue2-cli/README.md delete mode 100644 tests/components/ct-vue2-cli/babel.config.js delete mode 100644 tests/components/ct-vue2-cli/package.json delete mode 100644 tests/components/ct-vue2-cli/playwright.config.ts delete mode 100644 tests/components/ct-vue2-cli/playwright/index.html delete mode 100644 tests/components/ct-vue2-cli/playwright/index.js delete mode 100644 tests/components/ct-vue2-cli/public/favicon.ico delete mode 100644 tests/components/ct-vue2-cli/public/index.html delete mode 100644 tests/components/ct-vue2-cli/src/App.vue delete mode 100644 tests/components/ct-vue2-cli/src/assets/index.css delete mode 100644 tests/components/ct-vue2-cli/src/assets/logo.png delete mode 100644 tests/components/ct-vue2-cli/src/components/Button.vue delete mode 100644 tests/components/ct-vue2-cli/src/components/Component.vue delete mode 100644 tests/components/ct-vue2-cli/src/components/Counter.vue delete mode 100644 tests/components/ct-vue2-cli/src/components/DefaultSlot.vue delete mode 100644 tests/components/ct-vue2-cli/src/components/EmptyTemplate.vue delete mode 100644 tests/components/ct-vue2-cli/src/components/NamedSlots.vue delete mode 100644 tests/components/ct-vue2-cli/src/main.js delete mode 100644 tests/components/ct-vue2-cli/src/pages/DashboardPage.vue delete mode 100644 tests/components/ct-vue2-cli/src/pages/LoginPage.vue delete mode 100644 tests/components/ct-vue2-cli/src/router/index.js delete mode 100644 tests/components/ct-vue2-cli/src/shims-vue.d.ts delete mode 100644 tests/components/ct-vue2-cli/tests/events/events.spec.ts delete mode 100644 tests/components/ct-vue2-cli/tests/events/events.spec.tsx delete mode 100644 tests/components/ct-vue2-cli/tests/render/render.spec.ts delete mode 100644 tests/components/ct-vue2-cli/tests/render/render.spec.tsx delete mode 100644 tests/components/ct-vue2-cli/tests/slots/slots.spec.ts delete mode 100644 tests/components/ct-vue2-cli/tests/slots/slots.spec.tsx delete mode 100644 tests/components/ct-vue2-cli/tests/unmount/unmount.spec.ts delete mode 100644 tests/components/ct-vue2-cli/tests/unmount/unmount.spec.tsx delete mode 100644 tests/components/ct-vue2-cli/tests/update/update.spec.ts delete mode 100644 tests/components/ct-vue2-cli/tests/update/update.spec.tsx delete mode 100644 tests/components/ct-vue2-cli/tests/vue-router/vue-router.spec.ts delete mode 100644 tests/components/ct-vue2-cli/tests/vue-router/vue-router.spec.tsx delete mode 100644 tests/components/ct-vue2-cli/tsconfig.app.json delete mode 100644 tests/components/ct-vue2-cli/tsconfig.config.json delete mode 100644 tests/components/ct-vue2-cli/tsconfig.json delete mode 100644 tests/components/ct-vue2-cli/tsconfig.test.json delete mode 100644 tests/components/ct-vue2-cli/vue.config.js diff --git a/docs/src/test-components-js.md b/docs/src/test-components-js.md index 91d1860a26..e4f19d6fc2 100644 --- a/docs/src/test-components-js.md +++ b/docs/src/test-components-js.md @@ -521,7 +521,6 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let {label: 'React', value: 'react'}, {label: 'Solid', value: 'solid'}, {label: 'Vue3', value: 'vue3'}, - {label: 'Vue2', value: 'vue2'}, ] }> @@ -617,40 +616,6 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let - - - ```js title="playwright/index.ts" - import { beforeMount, afterMount } from '@playwright/experimental-ct-vue2/hooks'; - import Router from 'vue-router'; - import { router } from '../src/router'; - - export type HooksConfig = { - enableRouting?: boolean; - } - - beforeMount(async ({ app, hooksConfig }) => { - if (hooksConfig?.enableRouting) { - Vue.use(Router); - return { router } - } - }); - ``` - - ```js title="src/pages/ProductsPage.spec.ts" - import { test, expect } from '@playwright/experimental-ct-vue2'; - import type { HooksConfig } from '../playwright'; - import ProductsPage from './pages/ProductsPage.vue'; - - test('configure routing through hooks config', async ({ page, mount }) => { - const component = await mount(ProductsPage, { - hooksConfig: { enableRouting: true }, - }); - await expect(component.getByRole('link')).toHaveAttribute('href', '/products/42'); - }); - ``` - - - ### unmount diff --git a/package-lock.json b/package-lock.json index 57046ec2f8..3f503e4c09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1508,10 +1508,6 @@ "resolved": "packages/playwright-ct-vue", "link": true }, - "node_modules/@playwright/experimental-ct-vue2": { - "resolved": "packages/playwright-ct-vue2", - "link": true - }, "node_modules/@playwright/test": { "resolved": "packages/playwright-test", "link": true @@ -5964,21 +5960,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "optional": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -6593,14 +6574,6 @@ "solid-js": "^1.3" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -8097,59 +8070,6 @@ "node": ">=18" } }, - "packages/playwright-ct-vue2": { - "name": "@playwright/experimental-ct-vue2", - "version": "1.49.0-next", - "license": "Apache-2.0", - "dependencies": { - "@playwright/experimental-ct-core": "1.49.0-next", - "@vitejs/plugin-vue2": "^2.2.0" - }, - "bin": { - "playwright": "cli.js" - }, - "devDependencies": { - "vue": "^2.7.14" - }, - "engines": { - "node": ">=18" - } - }, - "packages/playwright-ct-vue2/node_modules/@vitejs/plugin-vue2": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue2/-/plugin-vue2-2.3.1.tgz", - "integrity": "sha512-/ksaaz2SRLN11JQhLdEUhDzOn909WEk99q9t9w+N12GjQCljzv7GyvAbD/p20aBUjHkvpGOoQ+FCOkG+mjDF4A==", - "engines": { - "node": "^14.18.0 || >= 16.0.0" - }, - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0", - "vue": "^2.7.0-0" - } - }, - "packages/playwright-ct-vue2/node_modules/@vue/compiler-sfc": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", - "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", - "dependencies": { - "@babel/parser": "^7.23.5", - "postcss": "^8.4.14", - "source-map": "^0.6.1" - }, - "optionalDependencies": { - "prettier": "^1.18.2 || ^2.0.0" - } - }, - "packages/playwright-ct-vue2/node_modules/vue": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", - "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", - "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", - "dependencies": { - "@vue/compiler-sfc": "2.7.16", - "csstype": "^3.1.0" - } - }, "packages/playwright-firefox": { "version": "1.49.0-next", "hasInstallScript": true, diff --git a/packages/playwright-ct-vue2/.npmignore b/packages/playwright-ct-vue2/.npmignore deleted file mode 100644 index 62701eb493..0000000000 --- a/packages/playwright-ct-vue2/.npmignore +++ /dev/null @@ -1,12 +0,0 @@ -**/* - -!README.md -!LICENSE -!cli.js -!register.d.ts -!register.mjs -!registerSource.mjs -!index.d.ts -!index.js -!hooks.d.ts -!hooks.mjs diff --git a/packages/playwright-ct-vue2/README.md b/packages/playwright-ct-vue2/README.md deleted file mode 100644 index 23ae82312d..0000000000 --- a/packages/playwright-ct-vue2/README.md +++ /dev/null @@ -1,3 +0,0 @@ -> **BEWARE** This package is EXPERIMENTAL and does not respect semver. - -Read more at https://playwright.dev/docs/test-components diff --git a/packages/playwright-ct-vue2/cli.js b/packages/playwright-ct-vue2/cli.js deleted file mode 100755 index 9cc834ee95..0000000000 --- a/packages/playwright-ct-vue2/cli.js +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env node -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { program } = require('@playwright/experimental-ct-core/lib/program'); - -program.parse(process.argv); diff --git a/packages/playwright-ct-vue2/hooks.d.ts b/packages/playwright-ct-vue2/hooks.d.ts deleted file mode 100644 index 5009f44348..0000000000 --- a/packages/playwright-ct-vue2/hooks.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ComponentOptions } from 'vue'; -import type { CombinedVueInstance, Vue, VueConstructor } from 'vue/types/vue'; - -export declare function beforeMount( - callback: (params: { - hooksConfig?: HooksConfig, - Vue: VueConstructor, - }) => Promise & Record> -): void; -export declare function afterMount( - callback: (params: { - hooksConfig?: HooksConfig; - instance: CombinedVueInstance< - Vue, - object, - object, - object, - Record - >; - }) => Promise -): void; diff --git a/packages/playwright-ct-vue2/hooks.mjs b/packages/playwright-ct-vue2/hooks.mjs deleted file mode 100644 index b7cea242c4..0000000000 --- a/packages/playwright-ct-vue2/hooks.mjs +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const __pw_hooks_before_mount = []; -const __pw_hooks_after_mount = []; - -window.__pw_hooks_before_mount = __pw_hooks_before_mount; -window.__pw_hooks_after_mount = __pw_hooks_after_mount; - -export const beforeMount = callback => { - __pw_hooks_before_mount.push(callback); -}; - -export const afterMount = callback => { - __pw_hooks_after_mount.push(callback); -}; diff --git a/packages/playwright-ct-vue2/index.d.ts b/packages/playwright-ct-vue2/index.d.ts deleted file mode 100644 index 133b4a60f2..0000000000 --- a/packages/playwright-ct-vue2/index.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { TestType, Locator } from '@playwright/experimental-ct-core'; - -type Slot = string | string[]; -type ComponentSlots = Record & { default?: Slot }; - -type ComponentEvents = Record; - -// Copied from: https://github.com/vuejs/language-tools/blob/master/packages/vue-component-type-helpers/index.d.ts#L10-L13 -type ComponentProps = - T extends new (...angs: any) => { $props: infer P; } ? NonNullable

: - T extends (props: infer P, ...args: any) => any ? P : - {}; - -export interface MountOptions { - props?: ComponentProps; - slots?: ComponentSlots; - on?: ComponentEvents; - hooksConfig?: HooksConfig; -} - -export interface MountOptionsJsx { - hooksConfig?: HooksConfig; -} - -export interface MountResult extends Locator { - unmount(): Promise; - update(options: { - props?: Partial>; - slots?: Partial; - on?: Partial; - }): Promise; -} - -export interface MountResultJsx extends Locator { - unmount(): Promise; - update(component: JSX.Element): Promise; -} - -export const test: TestType<{ - mount( - component: JSX.Element, - options?: MountOptionsJsx - ): Promise; - mount( - component: Component, - options?: MountOptions - ): Promise>; -}>; - -export { defineConfig, PlaywrightTestConfig, expect, devices } from '@playwright/experimental-ct-core'; diff --git a/packages/playwright-ct-vue2/index.js b/packages/playwright-ct-vue2/index.js deleted file mode 100644 index 2eeabb0d08..0000000000 --- a/packages/playwright-ct-vue2/index.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { test, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core'); -const path = require('path'); - -const defineConfig = (config, ...configs) => { - return originalDefineConfig({ - ...config, - '@playwright/test': { - packageJSON: require.resolve('./package.json'), - }, - '@playwright/experimental-ct-core': { - registerSourceFile: path.join(__dirname, 'registerSource.mjs'), - frameworkPluginFactory: () => import('@vitejs/plugin-vue2').then(plugin => plugin.default()), - }, - }, ...configs); -}; - -module.exports = { test, expect, devices, defineConfig }; diff --git a/packages/playwright-ct-vue2/package.json b/packages/playwright-ct-vue2/package.json deleted file mode 100644 index 9ec30191ad..0000000000 --- a/packages/playwright-ct-vue2/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "@playwright/experimental-ct-vue2", - "version": "1.49.0-next", - "description": "Playwright Component Testing for Vue2", - "repository": { - "type": "git", - "url": "git+https://github.com/microsoft/playwright.git" - }, - "homepage": "https://playwright.dev", - "engines": { - "node": ">=18" - }, - "author": { - "name": "Microsoft Corporation" - }, - "license": "Apache-2.0", - "exports": { - ".": { - "types": "./index.d.ts", - "default": "./index.js" - }, - "./register": { - "types": "./register.d.ts", - "default": "./register.mjs" - }, - "./hooks": { - "types": "./hooks.d.ts", - "default": "./hooks.mjs" - }, - "./package.json": "./package.json" - }, - "dependencies": { - "@playwright/experimental-ct-core": "1.49.0-next", - "@vitejs/plugin-vue2": "^2.2.0" - }, - "devDependencies": { - "vue": "^2.7.14" - }, - "bin": { - "playwright": "cli.js" - } -} diff --git a/packages/playwright-ct-vue2/register.d.ts b/packages/playwright-ct-vue2/register.d.ts deleted file mode 100644 index f88e9be59d..0000000000 --- a/packages/playwright-ct-vue2/register.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export default function pwRegister( - components: Record, - options?: { - createApp: any, - setDevtoolsHook: any, - h: any, - } -): void; diff --git a/packages/playwright-ct-vue2/register.mjs b/packages/playwright-ct-vue2/register.mjs deleted file mode 100644 index ca6a6a12d9..0000000000 --- a/packages/playwright-ct-vue2/register.mjs +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { pwRegister } from './registerSource.mjs'; - -export default components => { - pwRegister(components); -}; diff --git a/packages/playwright-ct-vue2/registerSource.mjs b/packages/playwright-ct-vue2/registerSource.mjs deleted file mode 100644 index 19b4d41c08..0000000000 --- a/packages/playwright-ct-vue2/registerSource.mjs +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// @ts-check - -// This file is injected into the registry as text, no dependencies are allowed. - -import __pwVue, { h as __pwH } from 'vue'; - -/** @typedef {import('../playwright-ct-core/types/component').Component} Component */ -/** @typedef {import('../playwright-ct-core/types/component').JsxComponent} JsxComponent */ -/** @typedef {import('../playwright-ct-core/types/component').ObjectComponent} ObjectComponent */ -/** @typedef {import('vue').Component} FrameworkComponent */ - -/** - * @param {any} component - * @returns {component is ObjectComponent} - */ -function isObjectComponent(component) { - return typeof component === 'object' && component && component.__pw_type === 'object-component'; -} - -/** - * @param {any} component - * @returns {component is JsxComponent} - */ -function isJsxComponent(component) { - return typeof component === 'object' && component && component.__pw_type === 'jsx'; -} - -/** - * @param {any} child - */ -function __pwCreateChild(child) { - if (Array.isArray(child)) - return child.map(grandChild => __pwCreateChild(grandChild)); - if (isJsxComponent(child) || isObjectComponent(child)) - return __pwCreateWrapper(child); - return child; -} - -/** - * Exists to support fallthrough attributes: - * https://vuejs.org/guide/components/attrs.html#fallthrough-attributes - * @param {any} Component - * @param {string} key - * @return {boolean} - */ -function __pwComponentHasKeyInProps(Component, key) { - return typeof Component.props === 'object' && Component.props && key in Component.props; -} - -/** - * @param {JsxComponent} component - * @returns {any[] | undefined} - */ -function __pwJsxChildArray(component) { - if (!component.props.children) - return; - if (Array.isArray(component.props.children)) - return component.props.children; - return [component.props.children]; -} - -/** - * @param {Component} component - */ -function __pwCreateComponent(component) { - const isVueComponent = typeof component.type !== 'string'; - - /** - * @type {(import('vue').VNode | string)[]} - */ - const children = []; - - /** @type {import('vue').VNodeData} */ - const nodeData = {}; - nodeData.attrs = {}; - nodeData.props = {}; - nodeData.scopedSlots = {}; - nodeData.on = {}; - - if (component.__pw_type === 'jsx') { - for (const child of __pwJsxChildArray(component) || []) { - if (isJsxComponent(child) && child.type === 'template') { - const slotProperty = Object.keys(child.props).find(k => k.startsWith('v-slot:')); - const slot = slotProperty ? slotProperty.substring('v-slot:'.length) : 'default'; - nodeData.scopedSlots[slot] = () => __pwJsxChildArray(child)?.map(c => __pwCreateChild(c)); - } else { - children.push(__pwCreateChild(child)); - } - } - - for (const [key, value] of Object.entries(component.props)) { - if (key.startsWith('v-on:')) { - const event = key.substring('v-on:'.length); - nodeData.on[event] = value; - } else { - if (isVueComponent && __pwComponentHasKeyInProps(component.type, key)) - nodeData.props[key] = value; - else - nodeData.attrs[key] = value; - } - } - } - - if (component.__pw_type === 'object-component') { - // Vue test util syntax. - for (const [key, value] of Object.entries(component.slots || {})) { - const list = (Array.isArray(value) ? value : [value]).map(v => __pwCreateChild(v)); - if (key === 'default') - children.push(...list); - else - nodeData.scopedSlots[key] = () => list; - } - nodeData.props = component.props || {}; - for (const [key, value] of Object.entries(component.on || {})) - nodeData.on[key] = value; - } - - /** @type {(string|import('vue').VNode)[] | undefined} */ - let lastArg; - if (Object.entries(nodeData.scopedSlots).length) { - if (children.length) - nodeData.scopedSlots.default = () => children; - } else if (children.length) { - lastArg = children; - } - - return { Component: component.type, nodeData, slots: lastArg }; -} - -/** - * @param {Component} component - * @returns {import('vue').VNode} - */ -function __pwCreateWrapper(component) { - const { Component, nodeData, slots } = __pwCreateComponent(component); - const wrapper = __pwH(Component, nodeData, slots); - return wrapper; -} - -const instanceKey = Symbol('instanceKey'); -const wrapperKey = Symbol('wrapperKey'); - -window.playwrightMount = async (component, rootElement, hooksConfig) => { - let options = {}; - for (const hook of window.__pw_hooks_before_mount || []) - options = await hook({ hooksConfig, Vue: __pwVue }); - - const instance = new __pwVue({ - ...options, - render: () => { - const wrapper = __pwCreateWrapper(component); - /** @type {any} */ (rootElement)[wrapperKey] = wrapper; - return wrapper; - }, - }).$mount(); - rootElement.appendChild(instance.$el); - /** @type {any} */ (rootElement)[instanceKey] = instance; - - for (const hook of window.__pw_hooks_after_mount || []) - await hook({ hooksConfig, instance }); -}; - -window.playwrightUnmount = async rootElement => { - const component = rootElement[instanceKey]; - if (!component) - throw new Error('Component was not mounted'); - component.$destroy(); - component.$el.remove(); - delete rootElement[instanceKey]; -}; - -window.playwrightUpdate = async (element, options) => { - const wrapper = /** @type {any} */(element)[wrapperKey]; - if (!wrapper) - throw new Error('Component was not mounted'); - - const component = wrapper.componentInstance; - if (!component) - throw new Error('Updating a native HTML element is not supported'); - - const { nodeData, slots } = __pwCreateComponent(options); - - for (const [name, value] of Object.entries(nodeData.on || {})) { - component.$on(name, value); - component.$listeners[name] = value; - } - - Object.assign(component.$scopedSlots, nodeData.scopedSlots); - component.$slots.default = slots; - - for (const [key, value] of Object.entries(nodeData.props || {})) - component[key] = value; - - if (!Object.keys(nodeData.props || {}).length) - component.$forceUpdate(); -}; diff --git a/tests/components/ct-vue2-cli/.gitignore b/tests/components/ct-vue2-cli/.gitignore deleted file mode 100644 index 7e2a946d8e..0000000000 --- a/tests/components/ct-vue2-cli/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -.DS_Store -node_modules -/dist -*.tsbuildinfo - -# local env files -.env.local -.env.*.local - -# Log files -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# Editor directories and files -.idea -.vscode -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/tests/components/ct-vue2-cli/README.md b/tests/components/ct-vue2-cli/README.md deleted file mode 100644 index c9a7da714e..0000000000 --- a/tests/components/ct-vue2-cli/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# ct-vue2-cli - -## Project setup -``` -npm install -``` - -### Compiles and hot-reloads for development -``` -npm run serve -``` - -### Compiles and minifies for production -``` -npm run build -``` - -### Lints and fixes files -``` -npm run lint -``` - -### Customize configuration -See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/tests/components/ct-vue2-cli/babel.config.js b/tests/components/ct-vue2-cli/babel.config.js deleted file mode 100644 index e9558405fd..0000000000 --- a/tests/components/ct-vue2-cli/babel.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - presets: [ - '@vue/cli-plugin-babel/preset' - ] -} diff --git a/tests/components/ct-vue2-cli/package.json b/tests/components/ct-vue2-cli/package.json deleted file mode 100644 index 472d30f92b..0000000000 --- a/tests/components/ct-vue2-cli/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "ct-vue2-cli", - "version": "0.1.0", - "private": true, - "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", - "lint": "vue-cli-service lint", - "typecheck": "tsc --noEmit --project tsconfig.test.json" - }, - "dependencies": { - "core-js": "^3.8.3", - "vue": "^2.7.13", - "vue-router": "^3.6.5" - }, - "devDependencies": { - "@babel/core": "^7.23.2", - "@babel/eslint-parser": "^7.22.15", - "@vue/cli-plugin-babel": "~5.0.0", - "@vue/cli-plugin-eslint": "~5.0.0", - "@vue/cli-service": "~5.0.0", - "@vue/tsconfig": "^0.1.3", - "eslint": "^7.32.0", - "eslint-plugin-vue": "^8.0.3" - }, - "eslintConfig": { - "root": true, - "env": { - "node": true - }, - "extends": [ - "plugin:vue/essential", - "eslint:recommended" - ], - "parserOptions": { - "parser": "@babel/eslint-parser" - }, - "rules": {} - }, - "browserslist": [ - "> 1%", - "last 2 versions", - "not dead" - ] -} diff --git a/tests/components/ct-vue2-cli/playwright.config.ts b/tests/components/ct-vue2-cli/playwright.config.ts deleted file mode 100644 index 307bc9726d..0000000000 --- a/tests/components/ct-vue2-cli/playwright.config.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { defineConfig, devices } from '@playwright/experimental-ct-vue2'; -import { resolve } from 'path'; - -export default defineConfig({ - testDir: 'tests', - forbidOnly: !!process.env.CI, - retries: process.env.CI ? 2 : 0, - reporter: process.env.CI ? 'html' : 'line', - use: { - trace: 'on-first-retry', - ctViteConfig: { - resolve: { - alias: { - '@': resolve(__dirname, './src'), - } - } - } - }, - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - }, - { - name: 'firefox', - use: { ...devices['Desktop Firefox'] }, - }, - { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, - }, - ], -}); diff --git a/tests/components/ct-vue2-cli/playwright/index.html b/tests/components/ct-vue2-cli/playwright/index.html deleted file mode 100644 index 9ee6c8206e..0000000000 --- a/tests/components/ct-vue2-cli/playwright/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - -

- - - diff --git a/tests/components/ct-vue2-cli/playwright/index.js b/tests/components/ct-vue2-cli/playwright/index.js deleted file mode 100644 index f5bd21d70d..0000000000 --- a/tests/components/ct-vue2-cli/playwright/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import { beforeMount, afterMount } from '@playwright/experimental-ct-vue2/hooks'; -import Router from 'vue-router'; -import { router } from '../src/router'; -import '../src/assets/index.css'; - -beforeMount(async ({ Vue, hooksConfig }) => { - console.log(`Before mount: ${JSON.stringify(hooksConfig)}`); - - if (hooksConfig?.routing) { - Vue.use(Router); - return { router } - } -}); - -afterMount(async ({ instance }) => { - console.log(`After mount el: ${instance.$el.constructor.name}`); -}); diff --git a/tests/components/ct-vue2-cli/public/favicon.ico b/tests/components/ct-vue2-cli/public/favicon.ico deleted file mode 100644 index df36fcfb72584e00488330b560ebcf34a41c64c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56 zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp= zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8 zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3 z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6 z={Xwx{TKxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S diff --git a/tests/components/ct-vue2-cli/public/index.html b/tests/components/ct-vue2-cli/public/index.html deleted file mode 100644 index 3e5a139621..0000000000 --- a/tests/components/ct-vue2-cli/public/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - <%= htmlWebpackPlugin.options.title %> - - - -
- - - diff --git a/tests/components/ct-vue2-cli/src/App.vue b/tests/components/ct-vue2-cli/src/App.vue deleted file mode 100644 index c2209eb5d7..0000000000 --- a/tests/components/ct-vue2-cli/src/App.vue +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/tests/components/ct-vue2-cli/src/assets/index.css b/tests/components/ct-vue2-cli/src/assets/index.css deleted file mode 100644 index 97495c44b8..0000000000 --- a/tests/components/ct-vue2-cli/src/assets/index.css +++ /dev/null @@ -1,20 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} - -@media (prefers-color-scheme: light) { - :root { - color: #e3e3e3; - background-color: #1b1b1d; - } -} diff --git a/tests/components/ct-vue2-cli/src/assets/logo.png b/tests/components/ct-vue2-cli/src/assets/logo.png deleted file mode 100644 index f3d2503fc2a44b5053b0837ebea6e87a2d339a43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6849 zcmaKRcUV(fvo}bjDT-7nLI_nlK}sT_69H+`qzVWDA|yaU?}j417wLi^B1KB1SLsC& zL0ag7$U(XW5YR7p&Ux?sP$d4lvMt8C^+TcQu4F zQqv!UF!I+kw)c0jhd6+g6oCr9P?7)?!qX1ui*iL{p}sKCAGuJ{{W)0z1pLF|=>h}& zt(2Lr0Z`2ig8<5i%Zk}cO5Fm=LByqGWaS`oqChZdEFmc`0hSb#gg|Aap^{+WKOYcj zHjINK)KDG%&s?Mt4CL(T=?;~U@bU2x_mLKN!#GJuK_CzbNw5SMEJorG!}_5;?R>@1 zSl)jns3WlU7^J%=(hUtfmuUCU&C3%8B5C^f5>W2Cy8jW3#{Od{lF1}|?c61##3dzA zsPlFG;l_FzBK}8>|H_Ru_H#!_7$UH4UKo3lKOA}g1(R&|e@}GINYVzX?q=_WLZCgh z)L|eJMce`D0EIwgRaNETDsr+?vQknSGAi=7H00r`QnI%oQnFxm`G2umXso9l+8*&Q z7WqF|$p49js$mdzo^BXpH#gURy=UO;=IMrYc5?@+sR4y_?d*~0^YP7d+y0{}0)zBM zIKVM(DBvICK#~7N0a+PY6)7;u=dutmNqK3AlsrUU9U`d;msiucB_|8|2kY=(7XA;G zwDA8AR)VCA#JOkxm#6oHNS^YVuOU;8p$N)2{`;oF|rQ?B~K$%rHDxXs+_G zF5|-uqHZvSzq}L;5Kcy_P+x0${33}Ofb6+TX&=y;;PkEOpz%+_bCw_{<&~ zeLV|!bP%l1qxywfVr9Z9JI+++EO^x>ZuCK);=$VIG1`kxK8F2M8AdC$iOe3cj1fo(ce4l-9 z7*zKy3={MixvUk=enQE;ED~7tv%qh&3lR<0m??@w{ILF|e#QOyPkFYK!&Up7xWNtL zOW%1QMC<3o;G9_S1;NkPB6bqbCOjeztEc6TsBM<(q9((JKiH{01+Ud=uw9B@{;(JJ z-DxI2*{pMq`q1RQc;V8@gYAY44Z!%#W~M9pRxI(R?SJ7sy7em=Z5DbuDlr@*q|25V)($-f}9c#?D%dU^RS<(wz?{P zFFHtCab*!rl(~j@0(Nadvwg8q|4!}L^>d?0al6}Rrv9$0M#^&@zjbfJy_n!%mVHK4 z6pLRIQ^Uq~dnyy$`ay51Us6WaP%&O;@49m&{G3z7xV3dLtt1VTOMYl3UW~Rm{Eq4m zF?Zl_v;?7EFx1_+#WFUXxcK78IV)FO>42@cm@}2I%pVbZqQ}3;p;sDIm&knay03a^ zn$5}Q$G!@fTwD$e(x-~aWP0h+4NRz$KlnO_H2c< z(XX#lPuW_%H#Q+c&(nRyX1-IadKR-%$4FYC0fsCmL9ky3 zKpxyjd^JFR+vg2!=HWf}2Z?@Td`0EG`kU?{8zKrvtsm)|7>pPk9nu@2^z96aU2<#` z2QhvH5w&V;wER?mopu+nqu*n8p~(%QkwSs&*0eJwa zMXR05`OSFpfyRb!Y_+H@O%Y z0=K^y6B8Gcbl?SA)qMP3Z+=C(?8zL@=74R=EVnE?vY!1BQy2@q*RUgRx4yJ$k}MnL zs!?74QciNb-LcG*&o<9=DSL>1n}ZNd)w1z3-0Pd^4ED1{qd=9|!!N?xnXjM!EuylY z5=!H>&hSofh8V?Jofyd!h`xDI1fYAuV(sZwwN~{$a}MX^=+0TH*SFp$vyxmUv7C*W zv^3Gl0+eTFgBi3FVD;$nhcp)ka*4gSskYIqQ&+M}xP9yLAkWzBI^I%zR^l1e?bW_6 zIn{mo{dD=)9@V?s^fa55jh78rP*Ze<3`tRCN4*mpO$@7a^*2B*7N_|A(Ve2VB|)_o z$=#_=aBkhe(ifX}MLT()@5?OV+~7cXC3r!%{QJxriXo9I%*3q4KT4Xxzyd{ z9;_%=W%q!Vw$Z7F3lUnY+1HZ*lO;4;VR2+i4+D(m#01OYq|L_fbnT;KN<^dkkCwtd zF7n+O7KvAw8c`JUh6LmeIrk4`F3o|AagKSMK3))_5Cv~y2Bb2!Ibg9BO7Vkz?pAYX zoI=B}+$R22&IL`NCYUYjrdhwjnMx_v=-Qcx-jmtN>!Zqf|n1^SWrHy zK|MwJ?Z#^>)rfT5YSY{qjZ&`Fjd;^vv&gF-Yj6$9-Dy$<6zeP4s+78gS2|t%Z309b z0^fp~ue_}i`U9j!<|qF92_3oB09NqgAoehQ`)<)dSfKoJl_A6Ec#*Mx9Cpd-p#$Ez z={AM*r-bQs6*z$!*VA4|QE7bf@-4vb?Q+pPKLkY2{yKsw{&udv_2v8{Dbd zm~8VAv!G~s)`O3|Q6vFUV%8%+?ZSVUa(;fhPNg#vab@J*9XE4#D%)$UU-T5`fwjz! z6&gA^`OGu6aUk{l*h9eB?opVdrHK>Q@U>&JQ_2pR%}TyOXGq_6s56_`U(WoOaAb+K zXQr#6H}>a-GYs9^bGP2Y&hSP5gEtW+GVC4=wy0wQk=~%CSXj=GH6q z-T#s!BV`xZVxm{~jr_ezYRpqqIcXC=Oq`b{lu`Rt(IYr4B91hhVC?yg{ol4WUr3v9 zOAk2LG>CIECZ-WIs0$N}F#eoIUEtZudc7DPYIjzGqDLWk_A4#(LgacooD z2K4IWs@N`Bddm-{%oy}!k0^i6Yh)uJ1S*90>|bm3TOZxcV|ywHUb(+CeX-o1|LTZM zwU>dY3R&U)T(}5#Neh?-CWT~@{6Ke@sI)uSuzoah8COy)w)B)aslJmp`WUcjdia-0 zl2Y}&L~XfA`uYQboAJ1;J{XLhYjH){cObH3FDva+^8ioOQy%Z=xyjGLmWMrzfFoH; zEi3AG`_v+%)&lDJE;iJWJDI@-X9K5O)LD~j*PBe(wu+|%ar~C+LK1+-+lK=t# z+Xc+J7qp~5q=B~rD!x78)?1+KUIbYr^5rcl&tB-cTtj+e%{gpZZ4G~6r15+d|J(ky zjg@@UzMW0k9@S#W(1H{u;Nq(7llJbq;;4t$awM;l&(2s+$l!Ay9^Ge|34CVhr7|BG z?dAR83smef^frq9V(OH+a+ki#q&-7TkWfFM=5bsGbU(8mC;>QTCWL5ydz9s6k@?+V zcjiH`VI=59P-(-DWXZ~5DH>B^_H~;4$)KUhnmGo*G!Tq8^LjfUDO)lASN*=#AY_yS zqW9UX(VOCO&p@kHdUUgsBO0KhXxn1sprK5h8}+>IhX(nSXZKwlNsjk^M|RAaqmCZB zHBolOHYBas@&{PT=R+?d8pZu zUHfyucQ`(umXSW7o?HQ3H21M`ZJal+%*)SH1B1j6rxTlG3hx1IGJN^M7{$j(9V;MZ zRKybgVuxKo#XVM+?*yTy{W+XHaU5Jbt-UG33x{u(N-2wmw;zzPH&4DE103HV@ER86 z|FZEmQb|&1s5#`$4!Cm}&`^{(4V}OP$bk`}v6q6rm;P!H)W|2i^e{7lTk2W@jo_9q z*aw|U7#+g59Fv(5qI`#O-qPj#@_P>PC#I(GSp3DLv7x-dmYK=C7lPF8a)bxb=@)B1 zUZ`EqpXV2dR}B&r`uM}N(TS99ZT0UB%IN|0H%DcVO#T%L_chrgn#m6%x4KE*IMfjX zJ%4veCEqbXZ`H`F_+fELMC@wuy_ch%t*+Z+1I}wN#C+dRrf2X{1C8=yZ_%Pt6wL_~ zZ2NN-hXOT4P4n$QFO7yYHS-4wF1Xfr-meG9Pn;uK51?hfel`d38k{W)F*|gJLT2#T z<~>spMu4(mul-8Q3*pf=N4DcI)zzjqAgbE2eOT7~&f1W3VsdD44Ffe;3mJp-V@8UC z)|qnPc12o~$X-+U@L_lWqv-RtvB~%hLF($%Ew5w>^NR82qC_0FB z)=hP1-OEx?lLi#jnLzH}a;Nvr@JDO-zQWd}#k^an$Kwml;MrD&)sC5b`s0ZkVyPkb zt}-jOq^%_9>YZe7Y}PhW{a)c39G`kg(P4@kxjcYfgB4XOOcmezdUI7j-!gs7oAo2o zx(Ph{G+YZ`a%~kzK!HTAA5NXE-7vOFRr5oqY$rH>WI6SFvWmahFav!CfRMM3%8J&c z*p+%|-fNS_@QrFr(at!JY9jCg9F-%5{nb5Bo~z@Y9m&SHYV`49GAJjA5h~h4(G!Se zZmK{Bo7ivCfvl}@A-ptkFGcWXAzj3xfl{evi-OG(TaCn1FAHxRc{}B|x+Ua1D=I6M z!C^ZIvK6aS_c&(=OQDZfm>O`Nxsw{ta&yiYPA~@e#c%N>>#rq)k6Aru-qD4(D^v)y z*>Rs;YUbD1S8^D(ps6Jbj0K3wJw>L4m)0e(6Pee3Y?gy9i0^bZO?$*sv+xKV?WBlh zAp*;v6w!a8;A7sLB*g-^<$Z4L7|5jXxxP1}hQZ<55f9<^KJ>^mKlWSGaLcO0=$jem zWyZkRwe~u{{tU63DlCaS9$Y4CP4f?+wwa(&1ou)b>72ydrFvm`Rj-0`kBJgK@nd(*Eh!(NC{F-@=FnF&Y!q`7){YsLLHf0_B6aHc# z>WIuHTyJwIH{BJ4)2RtEauC7Yq7Cytc|S)4^*t8Va3HR zg=~sN^tp9re@w=GTx$;zOWMjcg-7X3Wk^N$n;&Kf1RgVG2}2L-(0o)54C509C&77i zrjSi{X*WV=%C17((N^6R4Ya*4#6s_L99RtQ>m(%#nQ#wrRC8Y%yxkH;d!MdY+Tw@r zjpSnK`;C-U{ATcgaxoEpP0Gf+tx);buOMlK=01D|J+ROu37qc*rD(w`#O=3*O*w9?biwNoq3WN1`&Wp8TvKj3C z3HR9ssH7a&Vr<6waJrU zdLg!ieYz%U^bmpn%;(V%%ugMk92&?_XX1K@mwnVSE6!&%P%Wdi7_h`CpScvspMx?N zQUR>oadnG17#hNc$pkTp+9lW+MBKHRZ~74XWUryd)4yd zj98$%XmIL4(9OnoeO5Fnyn&fpQ9b0h4e6EHHw*l68j;>(ya`g^S&y2{O8U>1*>4zR zq*WSI_2o$CHQ?x0!wl9bpx|Cm2+kFMR)oMud1%n2=qn5nE&t@Fgr#=Zv2?}wtEz^T z9rrj=?IH*qI5{G@Rn&}^Z{+TW}mQeb9=8b<_a`&Cm#n%n~ zU47MvCBsdXFB1+adOO)03+nczfWa#vwk#r{o{dF)QWya9v2nv43Zp3%Ps}($lA02*_g25t;|T{A5snSY?3A zrRQ~(Ygh_ebltHo1VCbJb*eOAr;4cnlXLvI>*$-#AVsGg6B1r7@;g^L zFlJ_th0vxO7;-opU@WAFe;<}?!2q?RBrFK5U{*ai@NLKZ^};Ul}beukveh?TQn;$%9=R+DX07m82gP$=}Uo_%&ngV`}Hyv8g{u z3SWzTGV|cwQuFIs7ZDOqO_fGf8Q`8MwL}eUp>q?4eqCmOTcwQuXtQckPy|4F1on8l zP*h>d+cH#XQf|+6c|S{7SF(Lg>bR~l(0uY?O{OEVlaxa5@e%T&xju=o1`=OD#qc16 zSvyH*my(dcp6~VqR;o(#@m44Lug@~_qw+HA=mS#Z^4reBy8iV?H~I;{LQWk3aKK8$bLRyt$g?- - - - - diff --git a/tests/components/ct-vue2-cli/src/components/Component.vue b/tests/components/ct-vue2-cli/src/components/Component.vue deleted file mode 100644 index 133c8e2670..0000000000 --- a/tests/components/ct-vue2-cli/src/components/Component.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/tests/components/ct-vue2-cli/src/components/Counter.vue b/tests/components/ct-vue2-cli/src/components/Counter.vue deleted file mode 100644 index a048a43535..0000000000 --- a/tests/components/ct-vue2-cli/src/components/Counter.vue +++ /dev/null @@ -1,23 +0,0 @@ - - - diff --git a/tests/components/ct-vue2-cli/src/components/DefaultSlot.vue b/tests/components/ct-vue2-cli/src/components/DefaultSlot.vue deleted file mode 100644 index 287b84beaf..0000000000 --- a/tests/components/ct-vue2-cli/src/components/DefaultSlot.vue +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/tests/components/ct-vue2-cli/src/components/EmptyTemplate.vue b/tests/components/ct-vue2-cli/src/components/EmptyTemplate.vue deleted file mode 100644 index 9b4c59695e..0000000000 --- a/tests/components/ct-vue2-cli/src/components/EmptyTemplate.vue +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/tests/components/ct-vue2-cli/src/components/NamedSlots.vue b/tests/components/ct-vue2-cli/src/components/NamedSlots.vue deleted file mode 100644 index 582608df32..0000000000 --- a/tests/components/ct-vue2-cli/src/components/NamedSlots.vue +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/tests/components/ct-vue2-cli/src/main.js b/tests/components/ct-vue2-cli/src/main.js deleted file mode 100644 index b92d6e3e04..0000000000 --- a/tests/components/ct-vue2-cli/src/main.js +++ /dev/null @@ -1,14 +0,0 @@ -import Vue from 'vue'; -import Router from 'vue-router'; -import App from './App.vue'; -import { router } from './router'; -import './assets/index.css'; - -Vue.config.productionTip = false; - -Vue.use(Router); - -new Vue({ - router, - render: h => h(App), -}).$mount('#app'); diff --git a/tests/components/ct-vue2-cli/src/pages/DashboardPage.vue b/tests/components/ct-vue2-cli/src/pages/DashboardPage.vue deleted file mode 100644 index 0dcf8cc990..0000000000 --- a/tests/components/ct-vue2-cli/src/pages/DashboardPage.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/tests/components/ct-vue2-cli/src/pages/LoginPage.vue b/tests/components/ct-vue2-cli/src/pages/LoginPage.vue deleted file mode 100644 index aaa18be8ab..0000000000 --- a/tests/components/ct-vue2-cli/src/pages/LoginPage.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/tests/components/ct-vue2-cli/src/router/index.js b/tests/components/ct-vue2-cli/src/router/index.js deleted file mode 100644 index 3e5680ca8e..0000000000 --- a/tests/components/ct-vue2-cli/src/router/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import Router from 'vue-router'; -import LoginPage from '../pages/LoginPage.vue'; -import DashboardPage from '../pages/DashboardPage.vue'; - -export const router = new Router({ - mode: 'history', - base: '/', - routes: [ - { path: '/', component: LoginPage }, - { path: '/dashboard', component: DashboardPage } - ] -}); diff --git a/tests/components/ct-vue2-cli/src/shims-vue.d.ts b/tests/components/ct-vue2-cli/src/shims-vue.d.ts deleted file mode 100644 index 8f6f410263..0000000000 --- a/tests/components/ct-vue2-cli/src/shims-vue.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.vue' { - import Vue from 'vue'; - export default Vue; -} diff --git a/tests/components/ct-vue2-cli/tests/events/events.spec.ts b/tests/components/ct-vue2-cli/tests/events/events.spec.ts deleted file mode 100644 index 71c4c958a9..0000000000 --- a/tests/components/ct-vue2-cli/tests/events/events.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { test, expect } from '@playwright/experimental-ct-vue2'; -import Button from '@/components/Button.vue'; - -test('emit an submit event when the button is clicked', async ({ mount }) => { - const messages: string[] = []; - const component = await mount(Button, { - props: { - title: 'Submit', - }, - on: { - submit: (data: string) => messages.push(data), - }, - }); - await component.click(); - expect(messages).toEqual(['hello']); -}); diff --git a/tests/components/ct-vue2-cli/tests/events/events.spec.tsx b/tests/components/ct-vue2-cli/tests/events/events.spec.tsx deleted file mode 100644 index 9d31d708fb..0000000000 --- a/tests/components/ct-vue2-cli/tests/events/events.spec.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { test, expect } from '@playwright/experimental-ct-vue2'; -import Button from '@/components/Button.vue'; -import DefaultSlot from '@/components/DefaultSlot.vue'; - -test('emit an submit event when the button is clicked', async ({ mount }) => { - const messages: string[] = []; - const component = await mount( -
} sidebar={} />; diff --git a/packages/trace-viewer/src/ui/uiModeFiltersView.tsx b/packages/trace-viewer/src/ui/uiModeFiltersView.tsx index 7671c2e9ba..314fce3a96 100644 --- a/packages/trace-viewer/src/ui/uiModeFiltersView.tsx +++ b/packages/trace-viewer/src/ui/uiModeFiltersView.tsx @@ -58,9 +58,9 @@ export const FiltersView: React.FC<{ Projects: {projectsLine} {expanded &&
-
+
{[...statusFilters.entries()].map(([status, value]) => { - return
+ return
; })}
-
+
{[...projectFilters.entries()].map(([projectName, value]) => { - return
+ return