From 074187583564c8609a7d1de6c9b76d971f350977 Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 12:21:58 +0200 Subject: [PATCH 1/9] feat(auth): support webServer.env --- README.md | 2 + packages/auth/README.md | 2 + .../src/__tests__/configFindAndLoad.test.ts | 31 ++++++++++++++- packages/auth/src/cli/args.ts | 1 + packages/auth/src/cli/main.ts | 38 +++++++++---------- packages/auth/src/cli/webServer.ts | 3 +- packages/auth/src/config/loadAuthConfig.ts | 12 ++++++ packages/auth/src/config/types.ts | 5 +++ 8 files changed, 73 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 16a099e..9ab187e 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,8 @@ export default defineAuthConfig({ // If omitted, the CLI assumes your app is already running at `baseURL`. webServer: { command: "npm run dev", + // Optional: extra env for the server process (merged over process.env). + // env: { PORT: "3000" }, // Optional; defaults to baseURL when omitted. // url: "http://127.0.0.1:3000/login", }, diff --git a/packages/auth/README.md b/packages/auth/README.md index 0992d1c..0f68c79 100644 --- a/packages/auth/README.md +++ b/packages/auth/README.md @@ -88,6 +88,8 @@ export default defineAuthConfig({ // If omitted, the CLI assumes your app is already running at `baseURL`. webServer: { command: "npm run dev", + // Optional: extra env for the server process (merged over process.env). + // env: { PORT: "3000" }, // Optional; defaults to baseURL when omitted. // url: "http://127.0.0.1:3000/login", }, diff --git a/packages/auth/src/__tests__/configFindAndLoad.test.ts b/packages/auth/src/__tests__/configFindAndLoad.test.ts index b9d3e80..4b2758b 100644 --- a/packages/auth/src/__tests__/configFindAndLoad.test.ts +++ b/packages/auth/src/__tests__/configFindAndLoad.test.ts @@ -50,7 +50,7 @@ test("loadAuthConfig allows webServer.url to be omitted when baseURL is set", as [ "export default {", " baseURL: 'http://127.0.0.1:3000',", - " webServer: { command: 'node', args: ['server.js'] },", + " webServer: { command: 'node', args: ['server.js'], env: { FOO: 'bar' } },", " profiles: {", " admin: {", " validateUrl: '/',", @@ -68,6 +68,7 @@ test("loadAuthConfig allows webServer.url to be omitted when baseURL is set", as assert.equal(loaded.configFilePath, configPath); assert.ok(loaded.config.webServer); assert.equal(loaded.config.webServer.command, "node"); + assert.deepEqual(loaded.config.webServer.env, { FOO: "bar" }); }); test("loadAuthConfig requires baseURL when webServer.url is omitted", async () => { @@ -97,6 +98,34 @@ test("loadAuthConfig requires baseURL when webServer.url is omitted", async () = ); }); +test("loadAuthConfig rejects non-string webServer.env values", async () => { + const root = await makeTempDir(); + const configPath = path.join(root, "playwright.auth.config.ts"); + await fs.writeFile( + configPath, + [ + "export default {", + " baseURL: 'http://127.0.0.1:3000',", + " webServer: { command: 'node', url: 'http://127.0.0.1:3000', env: { PORT: 3000 } },", + " profiles: {", + " admin: {", + " validateUrl: '/',", + " async login() {},", + " async validate() { return { ok: true }; },", + " },", + " },", + "};", + "", + ].join("\n"), + "utf8", + ); + + await assert.rejects( + () => loadAuthConfig({ cwd: root }), + (error) => isUserError(error), + ); +}); + test("loadAuthConfig throws a user error when config is missing", async () => { const root = await makeTempDir(); await assert.rejects( diff --git a/packages/auth/src/cli/args.ts b/packages/auth/src/cli/args.ts index 3c8d60d..7cf4137 100644 --- a/packages/auth/src/cli/args.ts +++ b/packages/auth/src/cli/args.ts @@ -13,6 +13,7 @@ export interface WebServerArgs { url: string; timeoutMs: number; reuseExisting: boolean; + env?: Record; } export interface DotenvArgs { diff --git a/packages/auth/src/cli/main.ts b/packages/auth/src/cli/main.ts index 3a619fa..44fcef9 100644 --- a/packages/auth/src/cli/main.ts +++ b/packages/auth/src/cli/main.ts @@ -32,25 +32,25 @@ async function run(argv: string[]): Promise { const loaded = await loadAuthConfig({ cwd: process.cwd(), configPath: parsed.configPath }); console.log(`auth: config ${loaded.configFilePath}`); - const resolvedWebServer: WebServerArgs | undefined = - parsed.webServer ?? - (loaded.config.webServer - ? { - command: loaded.config.webServer.command, - args: loaded.config.webServer.args ?? [], - url: - loaded.config.webServer.url ?? - loaded.config.baseURL ?? - (() => { - throw createUserError( - `Auth config webServer.url is missing and baseURL is not set.`, - ); - })(), - timeoutMs: loaded.config.webServer.timeoutMs ?? DEFAULT_WEB_SERVER_TIMEOUT_MS, - reuseExisting: - loaded.config.webServer.reuseExisting ?? DEFAULT_WEB_SERVER_REUSE_EXISTING, - } - : undefined); + const webServerFromConfig: WebServerArgs | undefined = loaded.config.webServer + ? { + command: loaded.config.webServer.command, + args: loaded.config.webServer.args ?? [], + url: + loaded.config.webServer.url ?? + loaded.config.baseURL ?? + (() => { + throw createUserError(`Auth config webServer.url is missing and baseURL is not set.`); + })(), + timeoutMs: loaded.config.webServer.timeoutMs ?? DEFAULT_WEB_SERVER_TIMEOUT_MS, + reuseExisting: loaded.config.webServer.reuseExisting ?? DEFAULT_WEB_SERVER_REUSE_EXISTING, + env: loaded.config.webServer.env, + } + : undefined; + + const resolvedWebServer: WebServerArgs | undefined = parsed.webServer + ? { ...parsed.webServer, env: parsed.webServer.env ?? webServerFromConfig?.env } + : webServerFromConfig; if (parsed.kind === "setup") { const result = await withWebServer(resolvedWebServer, async () => diff --git a/packages/auth/src/cli/webServer.ts b/packages/auth/src/cli/webServer.ts index 1017fe8..13dd502 100644 --- a/packages/auth/src/cli/webServer.ts +++ b/packages/auth/src/cli/webServer.ts @@ -231,13 +231,14 @@ export async function withWebServer( ? [quoteCmdArg(command), ...webServer.args.map(quoteCmdArg)].join(" ") : command; const argsForSpawn = forceQuotedCommandLine ? [] : webServer.args; + const envForSpawn = webServer.env ? { ...process.env, ...webServer.env } : process.env; let child: ReturnType; try { child = spawn(commandForSpawn, argsForSpawn, { stdio: "inherit", shell: useShell, - env: process.env, + env: envForSpawn, detached: process.platform !== "win32", windowsHide: true, }); diff --git a/packages/auth/src/config/loadAuthConfig.ts b/packages/auth/src/config/loadAuthConfig.ts index 4b1da01..cb19167 100644 --- a/packages/auth/src/config/loadAuthConfig.ts +++ b/packages/auth/src/config/loadAuthConfig.ts @@ -124,6 +124,18 @@ function assertAuthConfig(config: unknown): asserts config is AuthConfig { ) { throw createUserError(`Auth config "webServer.args" must be an array of strings.`); } + if (config.webServer.env !== undefined) { + if (!isObject(config.webServer.env)) { + throw createUserError(`Auth config "webServer.env" must be an object of strings.`); + } + for (const [key, value] of Object.entries(config.webServer.env)) { + if (typeof value !== "string") { + throw createUserError( + `Auth config "webServer.env.${key}" must be a string (got ${typeof value}).`, + ); + } + } + } } if ( diff --git a/packages/auth/src/config/types.ts b/packages/auth/src/config/types.ts index 2484708..4599717 100644 --- a/packages/auth/src/config/types.ts +++ b/packages/auth/src/config/types.ts @@ -17,6 +17,11 @@ export interface AuthWebServerConfig { url?: string; timeoutMs?: number; reuseExisting?: boolean; + /** + * Extra environment variables for the web server process. + * Merged as `{ ...process.env, ...env }`. + */ + env?: Record; } export interface AuthCredentials { From 5a19063909ef1c76cf7c0f2fb44e5aedbc6ed8c8 Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 12:39:42 +0200 Subject: [PATCH 2/9] docs(examples): show webServer.env usage --- examples/next-admin-auth/playwright.auth.config.ts | 3 +++ examples/vite-react-auth/playwright.auth.config.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/examples/next-admin-auth/playwright.auth.config.ts b/examples/next-admin-auth/playwright.auth.config.ts index 8890149..d39de7d 100644 --- a/examples/next-admin-auth/playwright.auth.config.ts +++ b/examples/next-admin-auth/playwright.auth.config.ts @@ -49,5 +49,8 @@ export default defineAuthConfig({ args: ["next", "dev", "-p", "3017"], timeoutMs: 120_000, reuseExisting: true, + env: { + NEXT_TELEMETRY_DISABLED: "1", + }, }, }); diff --git a/examples/vite-react-auth/playwright.auth.config.ts b/examples/vite-react-auth/playwright.auth.config.ts index 15e2311..907bd4d 100644 --- a/examples/vite-react-auth/playwright.auth.config.ts +++ b/examples/vite-react-auth/playwright.auth.config.ts @@ -8,6 +8,9 @@ export default defineAuthConfig({ url: "http://localhost:4173/login", timeoutMs: 60_000, reuseExisting: true, + env: { + PLAYWRIGHT_KIT_EXAMPLE: "vite-react-auth", + }, }, profiles: { admin: { From 5d869b8ed4cabef4671bcfebda8d672796a142bf Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 12:46:38 +0200 Subject: [PATCH 3/9] test(examples): verify webServer.env is applied --- .../vite-react-auth/playwright.auth.config.ts | 24 ++++++++++++++++++- examples/vite-react-auth/vite.config.ts | 18 +++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/examples/vite-react-auth/playwright.auth.config.ts b/examples/vite-react-auth/playwright.auth.config.ts index 907bd4d..986e10e 100644 --- a/examples/vite-react-auth/playwright.auth.config.ts +++ b/examples/vite-react-auth/playwright.auth.config.ts @@ -1,7 +1,27 @@ import { defineAuthConfig } from "@playwright-kit/auth"; +import type { Page } from "playwright"; + +const baseURL = "http://localhost:4173"; + +function isObject(value: unknown): value is Record { + return typeof value === "object" && value !== null; +} + +async function assertExampleEnv(page: Page): Promise { + const res = await page.request.get(`${baseURL}/api/env`); + if (!res.ok()) { + throw new Error(`env check failed: GET /api/env -> ${res.status()}`); + } + const data: unknown = await res.json(); + if (!isObject(data) || data.PLAYWRIGHT_KIT_EXAMPLE !== "vite-react-auth") { + throw new Error( + `env check failed: expected PLAYWRIGHT_KIT_EXAMPLE=vite-react-auth, got ${JSON.stringify(data)}`, + ); + } +} export default defineAuthConfig({ - baseURL: "http://localhost:4173", + baseURL, webServer: { command: "npm", args: ["run", "dev"], @@ -22,6 +42,7 @@ export default defineAuthConfig({ await page.getByRole("button", { name: "Sign in" }).click(); }, async validate(page) { + await assertExampleEnv(page); const ok = await page.getByTestId("whoami").isVisible(); if (!ok) return { ok: false, reason: "whoami not visible" }; const role = await page.getByTestId("whoami").textContent(); @@ -39,6 +60,7 @@ export default defineAuthConfig({ await page.getByRole("button", { name: "Sign in" }).click(); }, async validate(page) { + await assertExampleEnv(page); const ok = await page.getByTestId("whoami").isVisible(); if (!ok) return { ok: false, reason: "whoami not visible" }; const role = await page.getByTestId("whoami").textContent(); diff --git a/examples/vite-react-auth/vite.config.ts b/examples/vite-react-auth/vite.config.ts index 153e631..4d0136d 100644 --- a/examples/vite-react-auth/vite.config.ts +++ b/examples/vite-react-auth/vite.config.ts @@ -17,6 +17,22 @@ function headersApi(): PluginOption { } export default defineConfig({ - plugins: [react(), headersApi()], + plugins: [ + react(), + headersApi(), + { + name: "env-api", + configureServer(server) { + server.middlewares.use("/api/env", (_req, res) => { + res.setHeader("content-type", "application/json; charset=utf-8"); + res.end( + JSON.stringify({ + PLAYWRIGHT_KIT_EXAMPLE: process.env.PLAYWRIGHT_KIT_EXAMPLE ?? null, + }), + ); + }); + }, + }, + ], }); From 5511523846437b6646a48b514cb2cab7bc4c22da Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 12:47:14 +0200 Subject: [PATCH 4/9] Revert "test(examples): verify webServer.env is applied" This reverts commit 5d869b8ed4cabef4671bcfebda8d672796a142bf. --- .../vite-react-auth/playwright.auth.config.ts | 24 +------------------ examples/vite-react-auth/vite.config.ts | 18 +------------- 2 files changed, 2 insertions(+), 40 deletions(-) diff --git a/examples/vite-react-auth/playwright.auth.config.ts b/examples/vite-react-auth/playwright.auth.config.ts index 986e10e..907bd4d 100644 --- a/examples/vite-react-auth/playwright.auth.config.ts +++ b/examples/vite-react-auth/playwright.auth.config.ts @@ -1,27 +1,7 @@ import { defineAuthConfig } from "@playwright-kit/auth"; -import type { Page } from "playwright"; - -const baseURL = "http://localhost:4173"; - -function isObject(value: unknown): value is Record { - return typeof value === "object" && value !== null; -} - -async function assertExampleEnv(page: Page): Promise { - const res = await page.request.get(`${baseURL}/api/env`); - if (!res.ok()) { - throw new Error(`env check failed: GET /api/env -> ${res.status()}`); - } - const data: unknown = await res.json(); - if (!isObject(data) || data.PLAYWRIGHT_KIT_EXAMPLE !== "vite-react-auth") { - throw new Error( - `env check failed: expected PLAYWRIGHT_KIT_EXAMPLE=vite-react-auth, got ${JSON.stringify(data)}`, - ); - } -} export default defineAuthConfig({ - baseURL, + baseURL: "http://localhost:4173", webServer: { command: "npm", args: ["run", "dev"], @@ -42,7 +22,6 @@ export default defineAuthConfig({ await page.getByRole("button", { name: "Sign in" }).click(); }, async validate(page) { - await assertExampleEnv(page); const ok = await page.getByTestId("whoami").isVisible(); if (!ok) return { ok: false, reason: "whoami not visible" }; const role = await page.getByTestId("whoami").textContent(); @@ -60,7 +39,6 @@ export default defineAuthConfig({ await page.getByRole("button", { name: "Sign in" }).click(); }, async validate(page) { - await assertExampleEnv(page); const ok = await page.getByTestId("whoami").isVisible(); if (!ok) return { ok: false, reason: "whoami not visible" }; const role = await page.getByTestId("whoami").textContent(); diff --git a/examples/vite-react-auth/vite.config.ts b/examples/vite-react-auth/vite.config.ts index 4d0136d..153e631 100644 --- a/examples/vite-react-auth/vite.config.ts +++ b/examples/vite-react-auth/vite.config.ts @@ -17,22 +17,6 @@ function headersApi(): PluginOption { } export default defineConfig({ - plugins: [ - react(), - headersApi(), - { - name: "env-api", - configureServer(server) { - server.middlewares.use("/api/env", (_req, res) => { - res.setHeader("content-type", "application/json; charset=utf-8"); - res.end( - JSON.stringify({ - PLAYWRIGHT_KIT_EXAMPLE: process.env.PLAYWRIGHT_KIT_EXAMPLE ?? null, - }), - ); - }); - }, - }, - ], + plugins: [react(), headersApi()], }); From 54804a387b8fd2f0ccc088676009b2ef279a1ed0 Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 12:47:15 +0200 Subject: [PATCH 5/9] Revert "docs(examples): show webServer.env usage" This reverts commit 5a19063909ef1c76cf7c0f2fb44e5aedbc6ed8c8. --- examples/next-admin-auth/playwright.auth.config.ts | 3 --- examples/vite-react-auth/playwright.auth.config.ts | 3 --- 2 files changed, 6 deletions(-) diff --git a/examples/next-admin-auth/playwright.auth.config.ts b/examples/next-admin-auth/playwright.auth.config.ts index d39de7d..8890149 100644 --- a/examples/next-admin-auth/playwright.auth.config.ts +++ b/examples/next-admin-auth/playwright.auth.config.ts @@ -49,8 +49,5 @@ export default defineAuthConfig({ args: ["next", "dev", "-p", "3017"], timeoutMs: 120_000, reuseExisting: true, - env: { - NEXT_TELEMETRY_DISABLED: "1", - }, }, }); diff --git a/examples/vite-react-auth/playwright.auth.config.ts b/examples/vite-react-auth/playwright.auth.config.ts index 907bd4d..15e2311 100644 --- a/examples/vite-react-auth/playwright.auth.config.ts +++ b/examples/vite-react-auth/playwright.auth.config.ts @@ -8,9 +8,6 @@ export default defineAuthConfig({ url: "http://localhost:4173/login", timeoutMs: 60_000, reuseExisting: true, - env: { - PLAYWRIGHT_KIT_EXAMPLE: "vite-react-auth", - }, }, profiles: { admin: { From ed14c7088bb5a1e351ba607191268f1e5d4e5a30 Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 12:48:13 +0200 Subject: [PATCH 6/9] test(auth): assert webServer.env is passed --- packages/auth/src/__tests__/all.test.ts | 1 + .../auth/src/__tests__/webServerEnv.test.ts | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 packages/auth/src/__tests__/webServerEnv.test.ts diff --git a/packages/auth/src/__tests__/all.test.ts b/packages/auth/src/__tests__/all.test.ts index 233398e..d8a5120 100644 --- a/packages/auth/src/__tests__/all.test.ts +++ b/packages/auth/src/__tests__/all.test.ts @@ -1,4 +1,5 @@ import "./envMapping.test"; import "./argParsing.test"; import "./configFindAndLoad.test"; +import "./webServerEnv.test"; import "./lock.test"; diff --git a/packages/auth/src/__tests__/webServerEnv.test.ts b/packages/auth/src/__tests__/webServerEnv.test.ts new file mode 100644 index 0000000..5d934ef --- /dev/null +++ b/packages/auth/src/__tests__/webServerEnv.test.ts @@ -0,0 +1,61 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; + +import { withWebServer } from "../cli/webServer"; + +async function makeTempDir(): Promise { + return fs.mkdtemp(path.join(os.tmpdir(), "playwright-kit-auth-webserver-env-")); +} + +function getFreePort(): number { + return 20_000 + Math.floor(Math.random() * 10_000); +} + +test("withWebServer passes webServer.env to the spawned process", async () => { + const root = await makeTempDir(); + const port = getFreePort(); + const serverPath = path.join(root, "server.mjs"); + await fs.writeFile( + serverPath, + [ + "import http from 'node:http';", + "", + "const port = Number(process.argv[2]);", + "const server = http.createServer((req, res) => {", + " if (req.url === '/env') {", + " res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });", + " res.end(JSON.stringify({ PLAYWRIGHT_KIT_EXAMPLE: process.env.PLAYWRIGHT_KIT_EXAMPLE ?? null }));", + " return;", + " }", + " res.writeHead(404);", + " res.end('not found');", + "});", + "", + "server.listen(port, '127.0.0.1');", + "process.on('SIGINT', () => server.close(() => process.exit(0)));", + "", + ].join("\n"), + "utf8", + ); + + const url = `http://127.0.0.1:${port}/env`; + await withWebServer( + { + command: "node", + args: [serverPath, String(port)], + url, + timeoutMs: 10_000, + reuseExisting: false, + env: { PLAYWRIGHT_KIT_EXAMPLE: "vite-react-auth" }, + }, + async () => { + const res = await fetch(url); + assert.equal(res.status, 200); + const data: unknown = await res.json(); + assert.deepEqual(data, { PLAYWRIGHT_KIT_EXAMPLE: "vite-react-auth" }); + }, + ); +}); From d953279c0be92c673e0251fb12bdf7e9ec11a148 Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 12:55:56 +0200 Subject: [PATCH 7/9] chore(examples): next-admin-auth loads env via @next/env --- examples/next-admin-auth/package.json | 3 +-- examples/next-admin-auth/playwright.auth.config.ts | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/next-admin-auth/package.json b/examples/next-admin-auth/package.json index 99fca28..83303b6 100644 --- a/examples/next-admin-auth/package.json +++ b/examples/next-admin-auth/package.json @@ -5,7 +5,7 @@ "dev": "next dev -p 3017", "build": "next build", "start": "next start -p 3017", - "auth:ensure": "playwright-kit auth ensure --dotenv", + "auth:ensure": "playwright-kit auth ensure", "pretest": "npm run auth:ensure", "test": "playwright test" }, @@ -21,7 +21,6 @@ "@types/node": "^22.10.2", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "dotenv": "^16.4.7", "playwright": "^1.50.1", "typescript": "^5.7.3" } diff --git a/examples/next-admin-auth/playwright.auth.config.ts b/examples/next-admin-auth/playwright.auth.config.ts index 8890149..31eb2ee 100644 --- a/examples/next-admin-auth/playwright.auth.config.ts +++ b/examples/next-admin-auth/playwright.auth.config.ts @@ -1,6 +1,9 @@ import { defineAuthConfig } from "@playwright-kit/auth"; +import { loadEnvConfig } from "@next/env"; import type { Page } from "playwright"; +loadEnvConfig(process.cwd()); + const baseURL = "http://127.0.0.1:3017"; async function login(page: Page, email: string, password: string): Promise { From d1eccaadbef6561d5398a82a6d2b88d6ae4d6747 Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 13:04:22 +0200 Subject: [PATCH 8/9] test(examples): assert next env is loaded --- examples/next-admin-auth/.env.example | 1 + examples/next-admin-auth/pages/api/env.ts | 8 ++++++++ examples/next-admin-auth/tests/env.spec.ts | 8 ++++++++ 3 files changed, 17 insertions(+) create mode 100644 examples/next-admin-auth/pages/api/env.ts create mode 100644 examples/next-admin-auth/tests/env.spec.ts diff --git a/examples/next-admin-auth/.env.example b/examples/next-admin-auth/.env.example index 097cf1c..c4f6c1a 100644 --- a/examples/next-admin-auth/.env.example +++ b/examples/next-admin-auth/.env.example @@ -2,3 +2,4 @@ AUTH_ADMIN_EMAIL=admin@example.com AUTH_ADMIN_PASSWORD=admin AUTH_USER_EMAIL=user@example.com AUTH_USER_PASSWORD=user +PLAYWRIGHT_KIT_EXAMPLE=next-admin-auth diff --git a/examples/next-admin-auth/pages/api/env.ts b/examples/next-admin-auth/pages/api/env.ts new file mode 100644 index 0000000..c7a2b50 --- /dev/null +++ b/examples/next-admin-auth/pages/api/env.ts @@ -0,0 +1,8 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +export default function handler(_req: NextApiRequest, res: NextApiResponse): void { + res.status(200).json({ + PLAYWRIGHT_KIT_EXAMPLE: process.env.PLAYWRIGHT_KIT_EXAMPLE ?? null, + }); +} + diff --git a/examples/next-admin-auth/tests/env.spec.ts b/examples/next-admin-auth/tests/env.spec.ts new file mode 100644 index 0000000..3c9f2cf --- /dev/null +++ b/examples/next-admin-auth/tests/env.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from "@playwright/test"; + +test("Next server exposes PLAYWRIGHT_KIT_EXAMPLE from env", async ({ request }) => { + const res = await request.get("/api/env"); + expect(res.ok()).toBeTruthy(); + await expect(res.json()).resolves.toEqual({ PLAYWRIGHT_KIT_EXAMPLE: "next-admin-auth" }); +}); + From b032b68c1368059080b0a49c7d4f37c7ccd9f4e7 Mon Sep 17 00:00:00 2001 From: Roman Radchenko Date: Sun, 4 Jan 2026 13:07:20 +0200 Subject: [PATCH 9/9] chore(auth): clarify webServer.env validation error --- packages/auth/src/config/loadAuthConfig.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/auth/src/config/loadAuthConfig.ts b/packages/auth/src/config/loadAuthConfig.ts index cb19167..db53ac0 100644 --- a/packages/auth/src/config/loadAuthConfig.ts +++ b/packages/auth/src/config/loadAuthConfig.ts @@ -126,7 +126,9 @@ function assertAuthConfig(config: unknown): asserts config is AuthConfig { } if (config.webServer.env !== undefined) { if (!isObject(config.webServer.env)) { - throw createUserError(`Auth config "webServer.env" must be an object of strings.`); + throw createUserError( + `Auth config "webServer.env" must be an object (key/value pairs) with string values.`, + ); } for (const [key, value] of Object.entries(config.webServer.env)) { if (typeof value !== "string") {