test(e2e): add playwright smoke coverage
This commit is contained in:
@@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
/playwright-report/
|
||||||
|
/test-results/
|
||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
/.next/
|
/.next/
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# E2E tests
|
||||||
|
|
||||||
|
This project uses Playwright for end-to-end tests.
|
||||||
|
|
||||||
|
## Test tiers
|
||||||
|
|
||||||
|
- `mock`: deterministic browser tests against the local Next.js app with mocked backend APIs.
|
||||||
|
- `real-backend`: smoke tests against a deployed frontend and real backend APIs.
|
||||||
|
- `prod-smoke`: production smoke tests. Keep these non-destructive: no real OAuth completion, no real payment submission, and no high-volume chat actions.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm test:e2e
|
||||||
|
pnpm test:e2e:chrome
|
||||||
|
pnpm test:e2e:real
|
||||||
|
pnpm test:e2e:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
`pnpm test:e2e` uses Playwright-managed Chromium. If the browser binary is not installed yet, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm exec playwright install chromium
|
||||||
|
```
|
||||||
|
|
||||||
|
For local development on macOS, `pnpm test:e2e:chrome` can reuse the installed Google Chrome.
|
||||||
|
|
||||||
|
## Environment overrides
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PLAYWRIGHT_BASE_URL=https://frontend-test.banlv-ai.com \
|
||||||
|
E2E_API_BASE_URL=https://proapi.banlv-ai.com \
|
||||||
|
pnpm test:e2e:real
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PLAYWRIGHT_BASE_URL=https://cozsweet.com \
|
||||||
|
E2E_API_BASE_URL=https://proapi.banlv-ai.com \
|
||||||
|
pnpm test:e2e:prod
|
||||||
|
```
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import type { Page } from "@playwright/test";
|
||||||
|
|
||||||
|
import {
|
||||||
|
apiEnvelope,
|
||||||
|
chatSendResponse,
|
||||||
|
emptyChatHistoryResponse,
|
||||||
|
guestLoginResponse,
|
||||||
|
paymentPlansResponse,
|
||||||
|
vipStatusResponse,
|
||||||
|
} from "./test-data";
|
||||||
|
|
||||||
|
export async function mockCoreApis(page: Page): Promise<void> {
|
||||||
|
await page.route("**/api/auth/guest", async (route) => {
|
||||||
|
await route.fulfill({ json: apiEnvelope(guestLoginResponse) });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route("**/api/chat/history**", async (route) => {
|
||||||
|
await route.fulfill({ json: apiEnvelope(emptyChatHistoryResponse) });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route("**/api/chat/send", async (route) => {
|
||||||
|
await route.fulfill({ json: apiEnvelope(chatSendResponse) });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route("**/api/payment/plans**", async (route) => {
|
||||||
|
await route.fulfill({ json: apiEnvelope(paymentPlansResponse) });
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.route("**/api/payment/vip-status**", async (route) => {
|
||||||
|
await route.fulfill({ json: apiEnvelope(vipStatusResponse) });
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
export function apiEnvelope<T>(data: T) {
|
||||||
|
return {
|
||||||
|
code: 200,
|
||||||
|
message: "success",
|
||||||
|
success: true,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const e2eUser = {
|
||||||
|
id: "user_e2e",
|
||||||
|
username: "E2E User",
|
||||||
|
email: "e2e@example.com",
|
||||||
|
platform: "web",
|
||||||
|
intimacy: 42,
|
||||||
|
dolBalance: 0,
|
||||||
|
relationshipStage: "close_friend",
|
||||||
|
currentMood: "happy",
|
||||||
|
preferredLanguage: "en",
|
||||||
|
createdAt: "2026-06-25T00:00:00.000Z",
|
||||||
|
lastMessageAt: "",
|
||||||
|
avatarUrl: "",
|
||||||
|
loginProvider: "guest",
|
||||||
|
isGuest: true,
|
||||||
|
isVip: false,
|
||||||
|
voiceMinutesRemaining: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const guestLoginResponse = {
|
||||||
|
token: "e2e-guest-token",
|
||||||
|
deviceId: "e2e-device-id",
|
||||||
|
userId: e2eUser.id,
|
||||||
|
isDeviceUser: true,
|
||||||
|
expiresIn: 2_592_000,
|
||||||
|
user: e2eUser,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const emptyChatHistoryResponse = {
|
||||||
|
messages: [],
|
||||||
|
total: 0,
|
||||||
|
limit: 50,
|
||||||
|
offset: 0,
|
||||||
|
isVip: false,
|
||||||
|
privateFreeLimit: 0,
|
||||||
|
privateUsedToday: 0,
|
||||||
|
privateCanViewFree: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const chatSendResponse = {
|
||||||
|
reply: "Hello from the E2E boyfriend.",
|
||||||
|
audioUrl: "",
|
||||||
|
messageId: "msg_e2e_reply_001",
|
||||||
|
isGuest: true,
|
||||||
|
timestamp: 1_782_356_425_363,
|
||||||
|
image: {
|
||||||
|
type: null,
|
||||||
|
url: null,
|
||||||
|
},
|
||||||
|
lockDetail: {
|
||||||
|
locked: false,
|
||||||
|
showContent: true,
|
||||||
|
showUpgrade: false,
|
||||||
|
reason: null,
|
||||||
|
hint: null,
|
||||||
|
detail: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const paymentPlansResponse = {
|
||||||
|
plans: [
|
||||||
|
{
|
||||||
|
plan_id: "vip_monthly",
|
||||||
|
plan_name: "Monthly",
|
||||||
|
order_type: "vip_monthly",
|
||||||
|
amount_cents: 999,
|
||||||
|
original_amount_cents: 1999,
|
||||||
|
daily_price_cents: 33,
|
||||||
|
currency: "USD",
|
||||||
|
vip_days: 30,
|
||||||
|
dol_amount: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plan_id: "vip_quarterly",
|
||||||
|
plan_name: "Quarterly",
|
||||||
|
order_type: "vip_quarterly",
|
||||||
|
amount_cents: 2499,
|
||||||
|
original_amount_cents: 5999,
|
||||||
|
daily_price_cents: 28,
|
||||||
|
currency: "USD",
|
||||||
|
vip_days: 90,
|
||||||
|
dol_amount: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
plan_id: "dol_voice_30",
|
||||||
|
plan_name: "Voice Pack",
|
||||||
|
order_type: "dol_voice",
|
||||||
|
amount_cents: 499,
|
||||||
|
original_amount_cents: null,
|
||||||
|
daily_price_cents: null,
|
||||||
|
currency: "USD",
|
||||||
|
vip_days: null,
|
||||||
|
dol_amount: 30,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const vipStatusResponse = {
|
||||||
|
isVip: false,
|
||||||
|
expiresAt: null,
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
import { mockCoreApis } from "../../fixtures/api-mocks";
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context, page }) => {
|
||||||
|
await context.clearCookies();
|
||||||
|
await mockCoreApis(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("guest can skip splash, enter chat, and send a message", async ({ page }) => {
|
||||||
|
await page.goto("/splash");
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Skip" }).click();
|
||||||
|
await expect(page).toHaveURL(/\/chat$/);
|
||||||
|
|
||||||
|
const messageInput = page.getByRole("textbox", { name: "Message" });
|
||||||
|
await expect(messageInput).toBeVisible();
|
||||||
|
|
||||||
|
await messageInput.fill("hello e2e");
|
||||||
|
await page.getByRole("button", { name: "Send message" }).click();
|
||||||
|
|
||||||
|
await expect(page.getByText("hello e2e")).toBeVisible();
|
||||||
|
await expect(page.getByText("Hello from the E2E boyfriend.")).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("subscription page renders mocked plans without touching real payment", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.goto("/splash");
|
||||||
|
await page.getByRole("button", { name: "Skip" }).click();
|
||||||
|
await expect(page).toHaveURL(/\/chat$/);
|
||||||
|
|
||||||
|
await page.goto("/subscription?type=vip");
|
||||||
|
|
||||||
|
await expect(page.getByText("Subscribe to become the VIP Member")).toBeVisible();
|
||||||
|
await expect(page.getByText("Monthly")).toBeVisible();
|
||||||
|
await expect(page.getByText("Quarterly")).toBeVisible();
|
||||||
|
await expect(page.getByRole("button", { name: /Pay and Activiate/i })).toBeVisible();
|
||||||
|
});
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
|
const apiBaseURL = process.env.E2E_API_BASE_URL;
|
||||||
|
|
||||||
|
test.describe("real backend smoke", () => {
|
||||||
|
test("splash page can be opened", async ({ page }) => {
|
||||||
|
await page.goto("/splash", { waitUntil: "domcontentloaded" });
|
||||||
|
|
||||||
|
await expect(page.getByRole("button", { name: "Skip" })).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.getByRole("button", { name: "Login with Facebook" }),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("protected subscription page redirects anonymous visitors safely", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.goto("/subscription?type=vip", {
|
||||||
|
waitUntil: "domcontentloaded",
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(page.getByRole("button", { name: "Skip" })).toBeVisible();
|
||||||
|
await expect(page).toHaveURL(/\/splash(?:\?.*)?$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("real payment plans API returns a usable plan list", async ({
|
||||||
|
request,
|
||||||
|
}) => {
|
||||||
|
test.skip(!apiBaseURL, "Set E2E_API_BASE_URL to test real backend APIs.");
|
||||||
|
|
||||||
|
const response = await request.get(`${apiBaseURL}/api/payment/plans`);
|
||||||
|
expect(response.ok()).toBe(true);
|
||||||
|
const body = await response.json();
|
||||||
|
|
||||||
|
expect(body.success).toBe(true);
|
||||||
|
expect(Array.isArray(body.data?.plans)).toBe(true);
|
||||||
|
expect(body.data.plans.length).toBeGreaterThan(0);
|
||||||
|
expect(body.data.plans[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
plan_id: expect.any(String),
|
||||||
|
plan_name: expect.any(String),
|
||||||
|
amount_cents: expect.any(Number),
|
||||||
|
currency: expect.any(String),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("auth page exposes the social login entry without completing OAuth", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await page.goto("/auth", { waitUntil: "domcontentloaded" });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole("button", { name: /facebook/i }),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -16,6 +16,12 @@
|
|||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"test:ui": "vitest --ui",
|
"test:ui": "vitest --ui",
|
||||||
|
"test:e2e": "playwright test --project=mock-chromium",
|
||||||
|
"test:e2e:chrome": "PLAYWRIGHT_USE_SYSTEM_CHROME=1 playwright test --project=mock-chromium",
|
||||||
|
"test:e2e:headed": "playwright test --project=mock-chromium --headed",
|
||||||
|
"test:e2e:ui": "playwright test --ui",
|
||||||
|
"test:e2e:real": "E2E_REAL_BACKEND=1 PLAYWRIGHT_BASE_URL=${PLAYWRIGHT_BASE_URL:-https://frontend-test.banlv-ai.com} E2E_API_BASE_URL=${E2E_API_BASE_URL:-https://proapi.banlv-ai.com} playwright test --project=real-backend",
|
||||||
|
"test:e2e:prod": "E2E_REAL_BACKEND=1 PLAYWRIGHT_BASE_URL=${PLAYWRIGHT_BASE_URL:-https://cozsweet.com} E2E_API_BASE_URL=${E2E_API_BASE_URL:-https://proapi.banlv-ai.com} playwright test --project=prod-smoke",
|
||||||
"generate-barrels": "barrelsby --delete -c barrelsby.json"
|
"generate-barrels": "barrelsby --delete -c barrelsby.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -40,6 +46,7 @@
|
|||||||
"zod": "^4.4.3"
|
"zod": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.61.1",
|
||||||
"@serwist/turbopack": "^9.0.0",
|
"@serwist/turbopack": "^9.0.0",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
|
||||||
|
const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? "http://127.0.0.1:3000";
|
||||||
|
const isRealBackendRun = process.env.E2E_REAL_BACKEND === "1";
|
||||||
|
const shouldStartLocalServer =
|
||||||
|
!isRealBackendRun && baseURL.startsWith("http://127.0.0.1");
|
||||||
|
const browserChannel =
|
||||||
|
process.env.PLAYWRIGHT_USE_SYSTEM_CHROME === "1" ? "chrome" : undefined;
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: "./e2e/specs",
|
||||||
|
fullyParallel: true,
|
||||||
|
forbidOnly: Boolean(process.env.CI),
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
reporter: process.env.CI
|
||||||
|
? [["list"], ["html", { open: "never" }]]
|
||||||
|
: [["list"], ["html", { open: "never" }]],
|
||||||
|
use: {
|
||||||
|
baseURL,
|
||||||
|
trace: "on-first-retry",
|
||||||
|
screenshot: "only-on-failure",
|
||||||
|
video: "retain-on-failure",
|
||||||
|
},
|
||||||
|
webServer: shouldStartLocalServer
|
||||||
|
? {
|
||||||
|
command: "pnpm dev -H 127.0.0.1 -p 3000",
|
||||||
|
url: baseURL,
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
timeout: 120_000,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "mock-chromium",
|
||||||
|
testMatch: /mock\/.*\.spec\.ts/,
|
||||||
|
use: {
|
||||||
|
...devices["Desktop Chrome"],
|
||||||
|
channel: browserChannel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mock-mobile-chrome",
|
||||||
|
testMatch: /mock\/.*\.spec\.ts/,
|
||||||
|
use: {
|
||||||
|
...devices["Pixel 7"],
|
||||||
|
channel: browserChannel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "real-backend",
|
||||||
|
testMatch: /real\/.*\.spec\.ts/,
|
||||||
|
use: {
|
||||||
|
...devices["Pixel 7"],
|
||||||
|
channel: browserChannel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prod-smoke",
|
||||||
|
testMatch: /real\/.*\.spec\.ts/,
|
||||||
|
use: {
|
||||||
|
...devices["Pixel 7"],
|
||||||
|
channel: browserChannel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
Generated
+47
-8
@@ -34,10 +34,10 @@ importers:
|
|||||||
version: 1.18.0(react@19.2.4)
|
version: 1.18.0(react@19.2.4)
|
||||||
next:
|
next:
|
||||||
specifier: 16.2.7
|
specifier: 16.2.7
|
||||||
version: 16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 16.2.7(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
next-auth:
|
next-auth:
|
||||||
specifier: ^4.24.14
|
specifier: ^4.24.14
|
||||||
version: 4.24.14(next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 4.24.14(next@16.2.7(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
ofetch:
|
ofetch:
|
||||||
specifier: ^1.5.1
|
specifier: ^1.5.1
|
||||||
version: 1.5.1
|
version: 1.5.1
|
||||||
@@ -66,9 +66,12 @@ importers:
|
|||||||
specifier: ^4.4.3
|
specifier: ^4.4.3
|
||||||
version: 4.4.3
|
version: 4.4.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@playwright/test':
|
||||||
|
specifier: ^1.61.1
|
||||||
|
version: 1.61.1
|
||||||
'@serwist/turbopack':
|
'@serwist/turbopack':
|
||||||
specifier: ^9.0.0
|
specifier: ^9.0.0
|
||||||
version: 9.5.11(esbuild@0.25.12)(next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
version: 9.5.11(esbuild@0.25.12)(next@16.2.7(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
|
||||||
'@tailwindcss/postcss':
|
'@tailwindcss/postcss':
|
||||||
specifier: ^4
|
specifier: ^4
|
||||||
version: 4.3.0
|
version: 4.3.0
|
||||||
@@ -747,6 +750,11 @@ packages:
|
|||||||
'@pinojs/redact@0.4.0':
|
'@pinojs/redact@0.4.0':
|
||||||
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
|
resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
|
||||||
|
|
||||||
|
'@playwright/test@1.61.1':
|
||||||
|
resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.29':
|
'@polka/url@1.0.0-next.29':
|
||||||
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
|
||||||
|
|
||||||
@@ -1925,6 +1933,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
@@ -2662,6 +2675,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==}
|
resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
playwright-core@1.61.1:
|
||||||
|
resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
playwright@1.61.1:
|
||||||
|
resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0:
|
possible-typed-array-names@1.1.0:
|
||||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -3884,6 +3907,10 @@ snapshots:
|
|||||||
|
|
||||||
'@pinojs/redact@0.4.0': {}
|
'@pinojs/redact@0.4.0': {}
|
||||||
|
|
||||||
|
'@playwright/test@1.61.1':
|
||||||
|
dependencies:
|
||||||
|
playwright: 1.61.1
|
||||||
|
|
||||||
'@polka/url@1.0.0-next.29': {}
|
'@polka/url@1.0.0-next.29': {}
|
||||||
|
|
||||||
'@rolldown/binding-android-arm64@1.0.3':
|
'@rolldown/binding-android-arm64@1.0.3':
|
||||||
@@ -3953,7 +3980,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- browserslist
|
- browserslist
|
||||||
|
|
||||||
'@serwist/turbopack@9.5.11(esbuild@0.25.12)(next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)':
|
'@serwist/turbopack@9.5.11(esbuild@0.25.12)(next@16.2.7(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@serwist/build': 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
|
'@serwist/build': 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
|
||||||
'@serwist/utils': 9.5.11(browserslist@4.28.2)
|
'@serwist/utils': 9.5.11(browserslist@4.28.2)
|
||||||
@@ -3961,7 +3988,7 @@ snapshots:
|
|||||||
'@swc/core': 1.15.32
|
'@swc/core': 1.15.32
|
||||||
browserslist: 4.28.2
|
browserslist: 4.28.2
|
||||||
kolorist: 1.8.0
|
kolorist: 1.8.0
|
||||||
next: 16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.2.7(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
semver: 7.7.4
|
semver: 7.7.4
|
||||||
serwist: 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
|
serwist: 9.5.11(browserslist@4.28.2)(typescript@5.9.3)
|
||||||
@@ -5148,6 +5175,9 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
optional: true
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -5662,13 +5692,13 @@ snapshots:
|
|||||||
|
|
||||||
natural-compare@1.4.0: {}
|
natural-compare@1.4.0: {}
|
||||||
|
|
||||||
next-auth@4.24.14(next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
next-auth@4.24.14(next@16.2.7(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.29.7
|
'@babel/runtime': 7.29.7
|
||||||
'@panva/hkdf': 1.2.1
|
'@panva/hkdf': 1.2.1
|
||||||
cookie: 0.7.2
|
cookie: 0.7.2
|
||||||
jose: 4.15.9
|
jose: 4.15.9
|
||||||
next: 16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.2.7(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
oauth: 0.9.15
|
oauth: 0.9.15
|
||||||
openid-client: 5.7.1
|
openid-client: 5.7.1
|
||||||
preact: 10.29.2
|
preact: 10.29.2
|
||||||
@@ -5677,7 +5707,7 @@ snapshots:
|
|||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
|
|
||||||
next@16.2.7(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
next@16.2.7(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 16.2.7
|
'@next/env': 16.2.7
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
@@ -5696,6 +5726,7 @@ snapshots:
|
|||||||
'@next/swc-linux-x64-musl': 16.2.7
|
'@next/swc-linux-x64-musl': 16.2.7
|
||||||
'@next/swc-win32-arm64-msvc': 16.2.7
|
'@next/swc-win32-arm64-msvc': 16.2.7
|
||||||
'@next/swc-win32-x64-msvc': 16.2.7
|
'@next/swc-win32-x64-msvc': 16.2.7
|
||||||
|
'@playwright/test': 1.61.1
|
||||||
sharp: 0.34.5
|
sharp: 0.34.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
@@ -5875,6 +5906,14 @@ snapshots:
|
|||||||
find-up: 2.1.0
|
find-up: 2.1.0
|
||||||
load-json-file: 4.0.0
|
load-json-file: 4.0.0
|
||||||
|
|
||||||
|
playwright-core@1.61.1: {}
|
||||||
|
|
||||||
|
playwright@1.61.1:
|
||||||
|
dependencies:
|
||||||
|
playwright-core: 1.61.1
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|
||||||
postcss@8.4.31:
|
postcss@8.4.31:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
environment: "jsdom",
|
environment: "jsdom",
|
||||||
|
exclude: ["**/node_modules/**", "**/dist/**", "**/.next/**", "e2e/**"],
|
||||||
globals: true,
|
globals: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user