@dargmuesli/nuxt-vio-testing 23.1.1 → 23.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/e2e/utils/tests.mjs
CHANGED
|
@@ -410,11 +410,15 @@ const testMetadata = async ({ page, path, title }) => {
|
|
|
410
410
|
};
|
|
411
411
|
const testOgImage = (paths) => vioTest.describe("visual regression", () => {
|
|
412
412
|
vioTest("generates the open graph image", async ({ page }) => {
|
|
413
|
+
const base = new URL(SITE_URL);
|
|
413
414
|
for (const path of [paths.en, paths.de]) {
|
|
414
415
|
await page.goto(path);
|
|
415
416
|
const ogImageUrl = await page.locator("meta[property=\"og:image\"]").getAttribute("content");
|
|
416
417
|
if (!ogImageUrl) throw new Error("The og:image url is not given.");
|
|
417
|
-
|
|
418
|
+
const ogImage = new URL(ogImageUrl);
|
|
419
|
+
ogImage.protocol = base.protocol;
|
|
420
|
+
ogImage.host = base.host;
|
|
421
|
+
await page.goto(ogImage.toString());
|
|
418
422
|
await expect(page).toHaveScreenshot();
|
|
419
423
|
}
|
|
420
424
|
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { SITE_URL, TIMEOUT } from "./e2e/utils/constants.mjs";
|
|
2
2
|
import { defineConfig, devices } from "@playwright/test";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
//#region playwright.config.ts
|
|
4
5
|
const VIO_SERVER = process.env.VIO_SERVER || "dev";
|
|
6
|
+
const WARM_DEV_SERVER_SCRIPT = fileURLToPath(new URL("./scripts/warm-dev-server.mjs", import.meta.url));
|
|
5
7
|
/**
|
|
6
8
|
* Read environment variables from file.
|
|
7
9
|
* https://github.com/motdotla/dotenv
|
|
@@ -37,7 +39,7 @@ var playwright_config_default = defineConfig({
|
|
|
37
39
|
trace: "on-first-retry"
|
|
38
40
|
},
|
|
39
41
|
webServer: {
|
|
40
|
-
command: VIO_SERVER === "dev" ?
|
|
42
|
+
command: VIO_SERVER === "dev" ? `pnpm run --dir ../src certificates && node ${WARM_DEV_SERVER_SCRIPT}` : `pnpm run --dir ../src certificates && pnpm run --dir ../src start:${VIO_SERVER}`,
|
|
41
43
|
env: {
|
|
42
44
|
NUXT_APP_SECRET: "vio-testing-app-secret-0123456789",
|
|
43
45
|
NUXT_PUBLIC_VIO_IS_TESTING: "true"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Runs the dev server on an internal port, warms it up, then proxies the
|
|
2
|
+
// public port to it transparently.
|
|
3
|
+
//
|
|
4
|
+
// nuxt dev answers 503 "Starting Nuxt..." while it starts up, and a request
|
|
5
|
+
// that reaches it during the switch to the real app can hang without ever
|
|
6
|
+
// being answered. Playwright's webServer availability check has no
|
|
7
|
+
// per-request timeout and does not retry while one is in flight, so a single
|
|
8
|
+
// check landing in that window would exhaust the whole timeout even though
|
|
9
|
+
// the site is up. Keeping the server on an internal port until it has
|
|
10
|
+
// answered real page loads twice in a row means the checks and the browsers
|
|
11
|
+
// only ever talk to a warm server; until the proxy opens they get connection
|
|
12
|
+
// refused, which they retry without hanging. The warmup polls use a fresh
|
|
13
|
+
// connection and a hard timeout per attempt, so a single orphaned request
|
|
14
|
+
// cannot stall them.
|
|
15
|
+
//
|
|
16
|
+
// Usage: node warm-dev-server.mjs
|
|
17
|
+
// The public port comes from PORT (as set by the e2e script); the dev server
|
|
18
|
+
// runs on PORT + 1000.
|
|
19
|
+
|
|
20
|
+
import { spawn } from 'node:child_process'
|
|
21
|
+
import https from 'node:https'
|
|
22
|
+
import net from 'node:net'
|
|
23
|
+
|
|
24
|
+
const host = process.env.HOST || 'app.localhost'
|
|
25
|
+
const publicPort = Number(process.env.PORT || 3000)
|
|
26
|
+
const internalPort = publicPort + 1000
|
|
27
|
+
const warmupBudgetMs = 120_000
|
|
28
|
+
|
|
29
|
+
const server = spawn('pnpm run --dir ../src start:dev', {
|
|
30
|
+
env: { ...process.env, PORT: String(internalPort) },
|
|
31
|
+
shell: true,
|
|
32
|
+
stdio: 'inherit',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
function stop(code) {
|
|
36
|
+
server.kill('SIGTERM')
|
|
37
|
+
process.exit(code)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
process.on('SIGTERM', () => stop(0))
|
|
41
|
+
process.on('SIGINT', () => stop(0))
|
|
42
|
+
server.on('exit', (code) => stop(code ?? 1))
|
|
43
|
+
|
|
44
|
+
// matches playwright's own availability check: 200 to 403
|
|
45
|
+
function probe() {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
const request = https.get(
|
|
48
|
+
{
|
|
49
|
+
host,
|
|
50
|
+
path: '/',
|
|
51
|
+
port: internalPort,
|
|
52
|
+
// talks only to the local dev server we just spawned on an internal, un-exposed
|
|
53
|
+
// loopback port, with our own mkcert dev cert, so there is no untrusted peer to
|
|
54
|
+
// authenticate; mirrors the e2e's ignoreHTTPSErrors. We only care that it is up.
|
|
55
|
+
rejectUnauthorized: false,
|
|
56
|
+
timeout: 2500,
|
|
57
|
+
},
|
|
58
|
+
(response) => {
|
|
59
|
+
const available =
|
|
60
|
+
response.statusCode >= 200 && response.statusCode < 404
|
|
61
|
+
response.resume()
|
|
62
|
+
response.on('end', () => resolve(available))
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
request.on('error', () => resolve(false))
|
|
66
|
+
request.on('timeout', () => {
|
|
67
|
+
request.destroy()
|
|
68
|
+
resolve(false)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function waitForWarmup() {
|
|
74
|
+
const start = Date.now()
|
|
75
|
+
let consecutive = 0
|
|
76
|
+
while (Date.now() - start < warmupBudgetMs) {
|
|
77
|
+
if (await probe()) consecutive += 1
|
|
78
|
+
else consecutive = 0
|
|
79
|
+
if (consecutive >= 2) return true
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
81
|
+
}
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function openProxy() {
|
|
86
|
+
const proxy = net.createServer((socket) => {
|
|
87
|
+
const upstream = net.connect({ host: '127.0.0.1', port: internalPort })
|
|
88
|
+
socket.pipe(upstream)
|
|
89
|
+
upstream.pipe(socket)
|
|
90
|
+
socket.on('error', () => upstream.destroy())
|
|
91
|
+
upstream.on('error', () => socket.destroy())
|
|
92
|
+
})
|
|
93
|
+
await new Promise((resolve, reject) => {
|
|
94
|
+
proxy.once('error', reject)
|
|
95
|
+
proxy.listen(publicPort, resolve)
|
|
96
|
+
})
|
|
97
|
+
return proxy
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!(await waitForWarmup())) {
|
|
101
|
+
process.stderr.write('dev server did not warm up in time\n')
|
|
102
|
+
stop(1)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let proxy
|
|
106
|
+
try {
|
|
107
|
+
proxy = await openProxy()
|
|
108
|
+
} catch (error) {
|
|
109
|
+
process.stderr.write(
|
|
110
|
+
`could not open the proxy on port ${publicPort}: ${error.message}\n`,
|
|
111
|
+
)
|
|
112
|
+
stop(1)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
proxy.on('close', () => stop(0))
|
package/package.json
CHANGED
|
@@ -56,9 +56,10 @@
|
|
|
56
56
|
"url": "git+https://github.com/dargmuesli/vio.git"
|
|
57
57
|
},
|
|
58
58
|
"type": "module",
|
|
59
|
-
"version": "23.1.
|
|
59
|
+
"version": "23.1.3",
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "tsdown",
|
|
62
|
+
"postbuild": "node -e \"import('node:fs').then(fs=>{fs.mkdirSync('dist/scripts',{recursive:true});fs.copyFileSync('scripts/warm-dev-server.mjs','dist/scripts/warm-dev-server.mjs')})\"",
|
|
62
63
|
"build:watch": "tsdown --watch",
|
|
63
64
|
"lint": "pnpm run lint:js && pnpm run lint:ts",
|
|
64
65
|
"lint:fix": "pnpm run lint:js --fix",
|