diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0a7a88586..0097fe35e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -83,7 +83,7 @@ jobs:
${{ runner.os }}-playwright-bin-v1-
- name: Install Playwright
- run: pnpm playwright-core install chromium
+ run: pnpm playwright install chromium
- name: Prepare build environment
run: pnpm dev:prepare
diff --git a/build.config.ts b/build.config.ts
index 73e15ac4f..edf28ea53 100644
--- a/build.config.ts
+++ b/build.config.ts
@@ -6,6 +6,7 @@ export default defineBuildConfig({
declaration: true,
entries: [
'src/e2e',
+ 'src/playwright',
'src/experimental',
'src/config',
'src/module.ts',
diff --git a/examples/app-playwright/.gitignore b/examples/app-playwright/.gitignore
new file mode 100644
index 000000000..aaa9103e4
--- /dev/null
+++ b/examples/app-playwright/.gitignore
@@ -0,0 +1,2 @@
+test-results/
+playwright-report/
diff --git a/examples/app-playwright/app.vue b/examples/app-playwright/app.vue
new file mode 100644
index 000000000..8edd0c5cd
--- /dev/null
+++ b/examples/app-playwright/app.vue
@@ -0,0 +1,5 @@
+
+
+ Welcome to Playwright!
+
+
diff --git a/examples/app-playwright/nuxt.config.ts b/examples/app-playwright/nuxt.config.ts
new file mode 100644
index 000000000..8851e7746
--- /dev/null
+++ b/examples/app-playwright/nuxt.config.ts
@@ -0,0 +1,4 @@
+// https://nuxt.com/docs/api/configuration/nuxt-config
+export default defineNuxtConfig({
+ devtools: { enabled: true }
+})
diff --git a/examples/app-playwright/package.json b/examples/app-playwright/package.json
new file mode 100644
index 000000000..23b28a76a
--- /dev/null
+++ b/examples/app-playwright/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "nuxt-app-playwright",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "build": "nuxt build",
+ "dev": "nuxt dev",
+ "generate": "nuxt generate",
+ "preview": "nuxt preview",
+ "postinstall": "nuxt prepare",
+ "test": "playwright test"
+ },
+ "dependencies": {
+ "nuxt": "^3.9.1",
+ "vue": "^3.4.7",
+ "vue-router": "^4.2.5"
+ },
+ "devDependencies": {
+ "@nuxt/test-utils": "latest",
+ "@playwright/test": "^1.42.1"
+ }
+}
diff --git a/examples/app-playwright/playwright.config.ts b/examples/app-playwright/playwright.config.ts
new file mode 100644
index 000000000..ad0b569db
--- /dev/null
+++ b/examples/app-playwright/playwright.config.ts
@@ -0,0 +1,41 @@
+import { fileURLToPath } from 'node:url'
+import { defineConfig, devices } from '@playwright/test'
+import type { ConfigOptions } from '@nuxt/test-utils/playwright'
+
+const devicesToTest = [
+ 'Desktop Chrome',
+ // Test against other common browser engines.
+ // 'Desktop Firefox',
+ // 'Desktop Safari',
+ // Test against mobile viewports.
+ // 'Pixel 5',
+ // 'iPhone 12',
+ // Test against branded browsers.
+ // { ...devices['Desktop Edge'], channel: 'msedge' },
+ // { ...devices['Desktop Chrome'], channel: 'chrome' },
+] satisfies Array
+
+/* See https://playwright.dev/docs/test-configuration.*/
+export default defineConfig({
+ testDir: './tests',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'html',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ /* Nuxt configuration options */
+ nuxt: {
+ rootDir: fileURLToPath(new URL('.', import.meta.url))
+ }
+ },
+ projects: devicesToTest.map(p => typeof p === 'string' ? ({ name: p, use: devices[p] }) : p),
+})
diff --git a/examples/app-playwright/tests/basic.test.ts b/examples/app-playwright/tests/basic.test.ts
new file mode 100644
index 000000000..b935edd6a
--- /dev/null
+++ b/examples/app-playwright/tests/basic.test.ts
@@ -0,0 +1,6 @@
+import { expect, test } from '@nuxt/test-utils/playwright'
+
+test('test', async ({ page, goto }) => {
+ await goto('/', { waitUntil: 'hydration' })
+ await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!')
+})
diff --git a/examples/app-playwright/tsconfig.json b/examples/app-playwright/tsconfig.json
new file mode 100644
index 000000000..4b34df157
--- /dev/null
+++ b/examples/app-playwright/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "./.nuxt/tsconfig.json"
+}
diff --git a/package.json b/package.json
index d3e014446..1c43997f8 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
".": "./dist/e2e.mjs",
"./config": "./dist/config.mjs",
"./e2e": "./dist/e2e.mjs",
+ "./playwright": "./dist/playwright.mjs",
"./experimental": "./dist/experimental.mjs",
"./module": "./dist/module.mjs",
"./runtime": "./dist/runtime-utils/index.mjs",
@@ -68,6 +69,7 @@
"@nuxt/devtools": "1.0.8",
"@nuxt/eslint-config": "0.2.0",
"@nuxt/module-builder": "0.5.5",
+ "@playwright/test": "1.42.1",
"@testing-library/vue": "8.0.2",
"@types/estree": "1.0.5",
"@types/jsdom": "21.1.6",
@@ -96,6 +98,7 @@
"peerDependencies": {
"@cucumber/cucumber": "^10.3.1",
"@jest/globals": "^29.5.0",
+ "@playwright/test": "^1.42.1",
"@testing-library/vue": "^7.0.0 || ^8.0.1",
"@vitest/ui": "^0.34.6 || ^1.0.0",
"@vue/test-utils": "^2.4.2",
@@ -112,6 +115,9 @@
"@cucumber/cucumber": {
"optional": true
},
+ "@playwright/test": {
+ "optional": true
+ },
"@testing-library/vue": {
"optional": true
},
@@ -149,4 +155,4 @@
"node": "^14.18.0 || >=16.10.0"
},
"packageManager": "pnpm@8.15.4"
-}
\ No newline at end of file
+}
diff --git a/playwright.d.ts b/playwright.d.ts
new file mode 100644
index 000000000..1b71747d5
--- /dev/null
+++ b/playwright.d.ts
@@ -0,0 +1 @@
+export * from './dist/playwright'
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6e2c25a20..393b2a8b8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -110,6 +110,9 @@ importers:
'@nuxt/module-builder':
specifier: 0.5.5
version: 0.5.5(@nuxt/kit@3.10.3)(nuxi@3.10.1)(typescript@5.3.3)
+ '@playwright/test':
+ specifier: 1.42.1
+ version: 1.42.1
'@testing-library/vue':
specifier: 8.0.2
version: 8.0.2(vue@3.4.21)
@@ -239,6 +242,25 @@ importers:
specifier: ^5.3.3
version: 5.3.3
+ examples/app-playwright:
+ dependencies:
+ nuxt:
+ specifier: ^3.9.1
+ version: 3.10.3(eslint@8.57.0)(rollup@4.13.0)(typescript@5.3.3)(vite@5.1.6)(vue-tsc@2.0.6)
+ vue:
+ specifier: ^3.4.21
+ version: 3.4.21(typescript@5.3.3)
+ vue-router:
+ specifier: ^4.2.5
+ version: 4.3.0(vue@3.4.21)
+ devDependencies:
+ '@nuxt/test-utils':
+ specifier: workspace:*
+ version: link:../..
+ '@playwright/test':
+ specifier: ^1.42.1
+ version: 1.42.1
+
examples/app-vitest:
dependencies:
nuxt:
@@ -262,7 +284,7 @@ importers:
version: 12.10.3
playwright-core:
specifier: ^1.40.1
- version: 1.41.0
+ version: 1.42.1
typescript:
specifier: ^5.3.3
version: 5.3.3
@@ -998,7 +1020,6 @@ packages:
cpu: [ppc64]
os: [aix]
requiresBuild: true
- dev: true
optional: true
/@esbuild/android-arm64@0.18.20:
@@ -1024,7 +1045,6 @@ packages:
cpu: [arm64]
os: [android]
requiresBuild: true
- dev: true
optional: true
/@esbuild/android-arm@0.18.20:
@@ -1050,7 +1070,6 @@ packages:
cpu: [arm]
os: [android]
requiresBuild: true
- dev: true
optional: true
/@esbuild/android-x64@0.18.20:
@@ -1076,7 +1095,6 @@ packages:
cpu: [x64]
os: [android]
requiresBuild: true
- dev: true
optional: true
/@esbuild/darwin-arm64@0.18.20:
@@ -1102,7 +1120,6 @@ packages:
cpu: [arm64]
os: [darwin]
requiresBuild: true
- dev: true
optional: true
/@esbuild/darwin-x64@0.18.20:
@@ -1128,7 +1145,6 @@ packages:
cpu: [x64]
os: [darwin]
requiresBuild: true
- dev: true
optional: true
/@esbuild/freebsd-arm64@0.18.20:
@@ -1154,7 +1170,6 @@ packages:
cpu: [arm64]
os: [freebsd]
requiresBuild: true
- dev: true
optional: true
/@esbuild/freebsd-x64@0.18.20:
@@ -1180,7 +1195,6 @@ packages:
cpu: [x64]
os: [freebsd]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-arm64@0.18.20:
@@ -1206,7 +1220,6 @@ packages:
cpu: [arm64]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-arm@0.18.20:
@@ -1232,7 +1245,6 @@ packages:
cpu: [arm]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-ia32@0.18.20:
@@ -1258,7 +1270,6 @@ packages:
cpu: [ia32]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-loong64@0.18.20:
@@ -1284,7 +1295,6 @@ packages:
cpu: [loong64]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-mips64el@0.18.20:
@@ -1310,7 +1320,6 @@ packages:
cpu: [mips64el]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-ppc64@0.18.20:
@@ -1336,7 +1345,6 @@ packages:
cpu: [ppc64]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-riscv64@0.18.20:
@@ -1362,7 +1370,6 @@ packages:
cpu: [riscv64]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-s390x@0.18.20:
@@ -1388,7 +1395,6 @@ packages:
cpu: [s390x]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/linux-x64@0.18.20:
@@ -1414,7 +1420,6 @@ packages:
cpu: [x64]
os: [linux]
requiresBuild: true
- dev: true
optional: true
/@esbuild/netbsd-x64@0.18.20:
@@ -1440,7 +1445,6 @@ packages:
cpu: [x64]
os: [netbsd]
requiresBuild: true
- dev: true
optional: true
/@esbuild/openbsd-x64@0.18.20:
@@ -1466,7 +1470,6 @@ packages:
cpu: [x64]
os: [openbsd]
requiresBuild: true
- dev: true
optional: true
/@esbuild/sunos-x64@0.18.20:
@@ -1492,7 +1495,6 @@ packages:
cpu: [x64]
os: [sunos]
requiresBuild: true
- dev: true
optional: true
/@esbuild/win32-arm64@0.18.20:
@@ -1518,7 +1520,6 @@ packages:
cpu: [arm64]
os: [win32]
requiresBuild: true
- dev: true
optional: true
/@esbuild/win32-ia32@0.18.20:
@@ -1544,7 +1545,6 @@ packages:
cpu: [ia32]
os: [win32]
requiresBuild: true
- dev: true
optional: true
/@esbuild/win32-x64@0.18.20:
@@ -1570,7 +1570,6 @@ packages:
cpu: [x64]
os: [win32]
requiresBuild: true
- dev: true
optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.57.0):
@@ -2271,7 +2270,6 @@ packages:
transitivePeerDependencies:
- rollup
- supports-color
- dev: true
/@nuxt/devtools-kit@1.0.8(nuxt@3.9.1)(rollup@4.13.0)(vite@5.1.6):
resolution: {integrity: sha512-j7bNZmoAXQ1a8qv6j6zk4c/aekrxYqYVQM21o/Hy4XHCUq4fajSgpoc8mjyWJSTfpkOmuLyEzMexpDWiIVSr6A==}
@@ -2453,7 +2451,6 @@ packages:
- rollup
- supports-color
- utf-8-validate
- dev: true
/@nuxt/devtools@1.0.8(nuxt@3.9.1)(rollup@4.13.0)(vite@5.1.6):
resolution: {integrity: sha512-o6aBFEBxc8OgVHV4OPe2g0q9tFIe9HiTxRiJnlTJ+jHvOQsBLS651ArdVtwLChf9UdMouFlpLLJ1HteZqTbtsQ==}
@@ -2688,7 +2685,7 @@ packages:
h3: 1.11.1
knitwork: 1.0.0
magic-string: 0.30.8
- mlly: 1.6.0
+ mlly: 1.6.1
ohash: 1.1.3
pathe: 1.1.2
perfect-debounce: 1.0.0
@@ -2701,7 +2698,7 @@ packages:
unenv: 1.9.0
unplugin: 1.10.0
vite: 5.1.6(@types/node@20.10.5)
- vite-node: 1.3.1(@types/node@20.10.5)
+ vite-node: 1.4.0
vite-plugin-checker: 0.6.4(eslint@8.57.0)(typescript@5.3.3)(vite@5.1.6)(vue-tsc@2.0.6)
vue: 3.4.21(typescript@5.3.3)
vue-bundle-renderer: 2.0.0
@@ -2724,7 +2721,6 @@ packages:
- vls
- vti
- vue-tsc
- dev: true
/@nuxt/vite-builder@3.9.1(eslint@8.57.0)(rollup@4.13.0)(typescript@5.3.3)(vue-tsc@1.8.26)(vue@3.4.21):
resolution: {integrity: sha512-V0GxTYuajNlf+kX3ak7klaFnkQ43MkXY2mAYqdAuUytvjx/S0O4hESy6gU1r/3no7IZAdoaEN27KLB4OOJ7vag==}
@@ -3058,6 +3054,14 @@ packages:
requiresBuild: true
optional: true
+ /@playwright/test@1.42.1:
+ resolution: {integrity: sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==}
+ engines: {node: '>=16'}
+ hasBin: true
+ dependencies:
+ playwright: 1.42.1
+ dev: true
+
/@polka/url@1.0.0-next.24:
resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==}
@@ -5846,7 +5850,6 @@ packages:
'@esbuild/win32-arm64': 0.20.1
'@esbuild/win32-ia32': 0.20.1
'@esbuild/win32-x64': 0.20.1
- dev: true
/escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
@@ -6355,6 +6358,14 @@ packages:
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+ /fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -9085,7 +9096,6 @@ packages:
hasBin: true
optionalDependencies:
fsevents: 2.3.3
- dev: true
/nuxt@3.10.3(eslint@8.57.0)(rollup@4.13.0)(typescript@5.3.3)(vite@5.1.6)(vue-tsc@2.0.6):
resolution: {integrity: sha512-NchGNiiz9g/ErJAb462W/lpX2NqcXYb9hugySKWvLXNdrjeAPiJ2/7mhgwUSiZA9MpjuQg3saiEajr1zlRIOCg==}
@@ -9110,7 +9120,7 @@ packages:
'@unhead/dom': 1.8.10
'@unhead/ssr': 1.8.10
'@unhead/vue': 1.8.10(vue@3.4.21)
- '@vue/shared': 3.4.19
+ '@vue/shared': 3.4.21
acorn: 8.11.3
c12: 1.10.0
chokidar: 3.6.0
@@ -9129,7 +9139,7 @@ packages:
klona: 2.0.6
knitwork: 1.0.0
magic-string: 0.30.8
- mlly: 1.6.0
+ mlly: 1.6.1
nitropack: 2.8.1(idb-keyval@6.2.1)
nuxi: 3.10.1
nypm: 0.3.6
@@ -9191,7 +9201,6 @@ packages:
- vti
- vue-tsc
- xml2js
- dev: true
/nuxt@3.9.1(eslint@8.57.0)(idb-keyval@6.2.1)(rollup@4.13.0)(typescript@5.3.3)(vite@5.1.6)(vue-tsc@1.8.26):
resolution: {integrity: sha512-jyD9E74bx8cdDc3nmYMNsJR0dIOrpcDH/mBlo1FmpB2zeXXMwupOD2tm033MJpHIEBbhQGHYFQlRyd7wHg2DyA==}
@@ -9255,12 +9264,12 @@ packages:
unenv: 1.9.0
unimport: 3.7.1(rollup@4.13.0)
unplugin: 1.6.0
- unplugin-vue-router: 0.7.0(rollup@4.13.0)(vue-router@4.2.5)(vue@3.4.21)
+ unplugin-vue-router: 0.7.0(rollup@4.13.0)(vue-router@4.3.0)(vue@3.4.21)
untyped: 1.4.0
vue: 3.4.21(typescript@5.3.3)
vue-bundle-renderer: 2.0.0
vue-devtools-stub: 0.1.0
- vue-router: 4.2.5(vue@3.4.21)
+ vue-router: 4.3.0(vue@3.4.21)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -9468,12 +9477,12 @@ packages:
unenv: 1.9.0
unimport: 3.7.1(rollup@4.13.0)
unplugin: 1.6.0
- unplugin-vue-router: 0.7.0(rollup@4.13.0)(vue-router@4.2.5)(vue@3.4.21)
+ unplugin-vue-router: 0.7.0(rollup@4.13.0)(vue-router@4.3.0)(vue@3.4.21)
untyped: 1.4.0
vue: 3.4.21(typescript@5.3.3)
vue-bundle-renderer: 2.0.0
vue-devtools-stub: 0.1.0
- vue-router: 4.2.5(vue@3.4.21)
+ vue-router: 4.3.0(vue@3.4.21)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -9877,16 +9886,20 @@ packages:
hasBin: true
dev: true
- /playwright-core@1.41.0:
- resolution: {integrity: sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==}
+ /playwright-core@1.42.1:
+ resolution: {integrity: sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==}
engines: {node: '>=16'}
hasBin: true
dev: true
- /playwright-core@1.42.1:
- resolution: {integrity: sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==}
+ /playwright@1.42.1:
+ resolution: {integrity: sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==}
engines: {node: '>=16'}
hasBin: true
+ dependencies:
+ playwright-core: 1.42.1
+ optionalDependencies:
+ fsevents: 2.3.2
dev: true
/pluralize@8.0.0:
@@ -11598,7 +11611,6 @@ packages:
/ultrahtml@1.5.3:
resolution: {integrity: sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg==}
- dev: true
/unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
@@ -11809,6 +11821,7 @@ packages:
transitivePeerDependencies:
- rollup
- vue
+ dev: false
/unplugin-vue-router@0.7.0(rollup@4.13.0)(vue-router@4.3.0)(vue@3.4.21):
resolution: {integrity: sha512-ddRreGq0t5vlSB7OMy4e4cfU1w2AwBQCwmvW3oP/0IHQiokzbx4hd3TpwBu3eIAFVuhX2cwNQwp1U32UybTVCw==}
@@ -11835,7 +11848,6 @@ packages:
transitivePeerDependencies:
- rollup
- vue
- dev: true
/unplugin@1.10.0:
resolution: {integrity: sha512-CuZtvvO8ua2Wl+9q2jEaqH6m3DoQ38N7pvBYQbbaeNlWGvK2l6GHiKi29aIHDPoSxdUzQ7Unevf1/ugil5X6Pg==}
@@ -12108,7 +12120,6 @@ packages:
- sugarss
- supports-color
- terser
- dev: true
/vite-plugin-checker@0.6.4(eslint@8.57.0)(typescript@5.3.3)(vite@5.1.6)(vue-tsc@1.8.26):
resolution: {integrity: sha512-2zKHH5oxr+ye43nReRbC2fny1nyARwhxdm0uNYp/ERy4YvU9iZpNOsueoi/luXw5gnpqRSvjcEPxXbS153O2wA==}
@@ -12547,7 +12558,6 @@ packages:
dependencies:
'@vue/devtools-api': 6.5.1
vue: 3.4.21(typescript@5.3.3)
- dev: true
/vue-template-compiler@2.7.15:
resolution: {integrity: sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==}
diff --git a/src/core/browser.ts b/src/core/browser.ts
index 45030be0c..e7957a027 100644
--- a/src/core/browser.ts
+++ b/src/core/browser.ts
@@ -1,4 +1,4 @@
-import type { Browser, BrowserContextOptions, Page } from 'playwright-core'
+import type { Browser, BrowserContextOptions, Page, Response } from 'playwright-core'
import { useTestContext } from './context'
import { url } from './server'
@@ -33,12 +33,12 @@ export async function getBrowser (): Promise {
}
type _GotoOptions = NonNullable[1]>
-interface GotoOptions extends Omit<_GotoOptions, 'waitUntil'> {
+export interface GotoOptions extends Omit<_GotoOptions, 'waitUntil'> {
waitUntil?: 'hydration' | 'route' | _GotoOptions['waitUntil']
}
interface NuxtPage extends Omit {
- goto: (url: string, options?: GotoOptions) => Promise
+ goto: (url: string, options?: GotoOptions) => Promise
}
export async function createPage (path?: string, options?: BrowserContextOptions): Promise {
@@ -46,17 +46,13 @@ export async function createPage (path?: string, options?: BrowserContextOptions
const page = await browser.newPage(options) as unknown as NuxtPage
const _goto = page.goto.bind(page)
- page.goto = async (url, options) => {
+ page.goto = async (url, options): Promise => {
const waitUntil = options?.waitUntil
if (waitUntil && ['hydration', 'route'].includes(waitUntil)) {
delete options.waitUntil
}
- const res = await _goto(url, options)
- if (waitUntil === 'hydration') {
- await page.waitForFunction(() => window.useNuxtApp?.().isHydrating === false)
- } else if (waitUntil === 'route') {
- await page.waitForFunction((route) => window.useNuxtApp?.()._route.fullPath === route, path)
- }
+ const res = await _goto(url, options as Parameters[1])
+ await waitForHydration(page, url, waitUntil)
return res
}
@@ -66,3 +62,11 @@ export async function createPage (path?: string, options?: BrowserContextOptions
return page
}
+
+export async function waitForHydration (page: Page, url: string, waitUntil?: GotoOptions['waitUntil']): Promise {
+ if (waitUntil === 'hydration') {
+ await page.waitForFunction(() => window.useNuxtApp?.().isHydrating === false)
+ } else if (waitUntil === 'route') {
+ await page.waitForFunction((route) => window.useNuxtApp?.()._route.fullPath === route, url)
+ }
+}
diff --git a/src/core/context.ts b/src/core/context.ts
index 3e270e4db..67758492a 100644
--- a/src/core/context.ts
+++ b/src/core/context.ts
@@ -15,12 +15,17 @@ export function createTestContext (options: Partial): TestContext {
server: true,
build: (options.browser !== false) || (options.server !== false),
nuxtConfig: {},
- runner: process.env.VITEST === 'true' ? 'vitest' : 'jest',
browserOptions: {
type: 'chromium' as const
}
} satisfies Partial)
+ if (process.env.VITEST === 'true') {
+ _options.runner ||= 'vitest'
+ } else if (process.env.JEST_WORKER_ID) {
+ _options.runner ||= 'jest'
+ }
+
return setTestContext({
options: _options as TestOptions
})
diff --git a/src/playwright.ts b/src/playwright.ts
new file mode 100644
index 000000000..b6866db92
--- /dev/null
+++ b/src/playwright.ts
@@ -0,0 +1,85 @@
+import { test as base } from '@playwright/test'
+import type { Page, Response } from 'playwright-core'
+import type { GotoOptions, TestOptions as SetupOptions, TestHooks } from './e2e'
+import { createTest, url, waitForHydration } from './e2e'
+
+export type ConfigOptions = {
+ nuxt: Partial | undefined
+}
+
+{
+ // Can be removed after Playwright v1.43 is released.
+ // Waiting for https://github.com/microsoft/playwright/pull/29865
+ if (process.env.TEST_WORKER_INDEX) {
+ for (const stream of [process.stdout, process.stderr]) {
+ // Stubs for the rest of the methods to avoid exceptions in user code.
+ if (!(stream as any).clearLine) {
+ stream.clearLine = (dir: any, callback?: () => void) => {
+ callback?.()
+ return true
+ }
+ }
+ if (!(stream as any).cursorTo) {
+ (stream as any).cursorTo = (x: number, y?: number | (() => void), callback?: () => void) => {
+ if (callback)
+ callback()
+ else if (y instanceof Function)
+ y()
+ return true
+ }
+ }
+ }
+ }
+}
+
+type WorkerOptions = {
+ _nuxtHooks: TestHooks
+}
+
+type TestOptions = {
+ goto: (url: string, options?: GotoOptions) => Promise
+}
+
+/**
+ * Use a preconfigured Nuxt fixture.
+ *
+ * You can pass a `nuxt: {}` object in your device configuration, in the `use` key of your config file,
+ * or use the following syntax within your test file to configure your Nuxt fixture:
+ *
+ ```ts
+ test.use({
+ nuxt: {
+ rootDir: fileURLToPath(new URL('.', import.meta.url)),
+ }
+ })
+ ```
+ */
+export const test = base.extend({
+ nuxt: [undefined, { option: true, scope: 'worker' }],
+ _nuxtHooks: [
+ async ({ nuxt }, use) => {
+ const hooks = createTest(nuxt || {})
+ await hooks.setup()
+ await use(hooks)
+ await hooks.afterAll()
+ }, { scope: 'worker' }
+ ],
+ baseURL: async ({ _nuxtHooks }, use) => {
+ _nuxtHooks.beforeEach()
+ await use(url('/'))
+ _nuxtHooks.afterEach()
+ },
+ goto: async ({ page }, use) => {
+ await use(async (url, options) => {
+ const waitUntil = options?.waitUntil
+ if (waitUntil && ['hydration', 'route'].includes(waitUntil)) {
+ delete options.waitUntil
+ }
+ const response = await page.goto(url, options as Parameters[1])
+ await waitForHydration(page, url, waitUntil)
+ return response
+ })
+ },
+})
+
+export { expect } from '@playwright/test'
diff --git a/tsconfig.json b/tsconfig.json
index 7683b60a9..ca6f416b4 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,6 +6,7 @@
"exclude": [
"config.d.ts",
"e2e.d.ts",
+ "playwright.d.ts",
"experimental.d.ts",
"module.d.ts",
"runtime.d.ts",