chore: fix playwright config + gitignore test-results
Deploy to route.crispygoat.com / deploy (push) Failing after 2m3s
Deploy to route.crispygoat.com / deploy (push) Failing after 2m3s
- playwright.config.ts: add testMatch pattern so playwright only picks up *.spec.ts (skipping tests/unit/*.test.ts which are vitest's domain). Before this, 'npx playwright test' would fail trying to run vitest tests. - .gitignore: exclude test-results/ and playwright-report/ (generated by 'npx playwright test', not source files).
This commit is contained in:
@@ -39,6 +39,10 @@ next-env.d.ts
|
|||||||
# Supabase
|
# Supabase
|
||||||
supabase/.temp/
|
supabase/.temp/
|
||||||
|
|
||||||
|
# Playwright test results (generated, not source)
|
||||||
|
test-results/
|
||||||
|
playwright-report/
|
||||||
|
|
||||||
# IDE / local config
|
# IDE / local config
|
||||||
.mcp.json
|
.mcp.json
|
||||||
.env*
|
.env*
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export default defineConfig({
|
|||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
retries: process.env.CI ? 2 : 0,
|
retries: process.env.CI ? 2 : 0,
|
||||||
workers: 1,
|
workers: 1,
|
||||||
|
// Playwright should only run E2E specs — vitest owns everything under
|
||||||
|
// tests/unit/. The glob below matches *.spec.ts at the top of tests/ and
|
||||||
|
// tests/e2e/ and tests/login/ but skips the .test.ts files (vitest).
|
||||||
|
testMatch: /(tests\/(smoke|e2e|login)\/.*|\/[^/]+\.spec\.ts$)/,
|
||||||
reporter: "list",
|
reporter: "list",
|
||||||
use: {
|
use: {
|
||||||
baseURL: LOCAL_BASE,
|
baseURL: LOCAL_BASE,
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"status": "failed",
|
|
||||||
"failedTests": [
|
|
||||||
"9baefabce98ea24ee76d-2f6f670dfe942637faf7",
|
|
||||||
"9baefabce98ea24ee76d-f58eca1309366ec2998a",
|
|
||||||
"9baefabce98ea24ee76d-4a32d345869f5c6ceb12"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
-152
@@ -1,152 +0,0 @@
|
|||||||
# Instructions
|
|
||||||
|
|
||||||
- Following Playwright test failed.
|
|
||||||
- Explain why, be concise, respect Playwright best practices.
|
|
||||||
- Provide a snippet of code with the fix, if possible.
|
|
||||||
|
|
||||||
# Test info
|
|
||||||
|
|
||||||
- Name: login/login-flow.spec.ts >> admin session persists across page reloads
|
|
||||||
- Location: tests/login/login-flow.spec.ts:65:5
|
|
||||||
|
|
||||||
# Error details
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: page.fill: value: expected string, got undefined
|
|
||||||
```
|
|
||||||
|
|
||||||
# Page snapshot
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- generic [active] [ref=e1]:
|
|
||||||
- banner [ref=e2]:
|
|
||||||
- generic [ref=e3]:
|
|
||||||
- link "Route Commerce" [ref=e4] [cursor=pointer]:
|
|
||||||
- /url: /
|
|
||||||
- navigation [ref=e5]:
|
|
||||||
- link "Tuxedo" [ref=e6] [cursor=pointer]:
|
|
||||||
- /url: /tuxedo
|
|
||||||
- link "IRD" [ref=e7] [cursor=pointer]:
|
|
||||||
- /url: /indian-river-direct
|
|
||||||
- link "Admin" [ref=e8] [cursor=pointer]:
|
|
||||||
- /url: /admin
|
|
||||||
- button "Switch to dark mode" [ref=e9]:
|
|
||||||
- img [ref=e10]
|
|
||||||
- link "Cart (0)" [ref=e12] [cursor=pointer]:
|
|
||||||
- /url: /cart
|
|
||||||
- main [ref=e13]:
|
|
||||||
- generic [ref=e14]:
|
|
||||||
- generic [ref=e15]:
|
|
||||||
- heading "Admin Login" [level=1] [ref=e16]
|
|
||||||
- paragraph [ref=e17]: Sign in with your account.
|
|
||||||
- generic [ref=e18]:
|
|
||||||
- generic [ref=e19]:
|
|
||||||
- generic [ref=e20]: Email
|
|
||||||
- textbox "Email" [ref=e21]:
|
|
||||||
- /placeholder: admin@example.com
|
|
||||||
- generic [ref=e22]:
|
|
||||||
- generic [ref=e23]: Password
|
|
||||||
- textbox "Password" [ref=e24]:
|
|
||||||
- /placeholder: ••••••••
|
|
||||||
- button "Sign In" [ref=e25]
|
|
||||||
- button "Forgot password?" [ref=e26]
|
|
||||||
- link "← Back to storefront" [ref=e27] [cursor=pointer]:
|
|
||||||
- /url: /
|
|
||||||
- contentinfo [ref=e28]:
|
|
||||||
- generic [ref=e30]:
|
|
||||||
- paragraph [ref=e31]: © 2026 Route Commerce. All rights reserved.
|
|
||||||
- navigation [ref=e32]:
|
|
||||||
- link "Privacy Policy" [ref=e33] [cursor=pointer]:
|
|
||||||
- /url: /privacy-policy
|
|
||||||
- link "Terms & Conditions" [ref=e34] [cursor=pointer]:
|
|
||||||
- /url: /terms-and-conditions
|
|
||||||
- link "Powered by Route Commerce" [ref=e35] [cursor=pointer]:
|
|
||||||
- /url: https://cielohermosa.com
|
|
||||||
- alert [ref=e36]
|
|
||||||
```
|
|
||||||
|
|
||||||
# Test source
|
|
||||||
|
|
||||||
```ts
|
|
||||||
1 | import { test, expect } from "@playwright/test";
|
|
||||||
2 |
|
|
||||||
3 | // ─────────────────────────────────────────────────────────────
|
|
||||||
4 | // Login flow: credentials → /api/login JSON → redirect to /admin
|
|
||||||
5 | // ─────────────────────────────────────────────────────────────
|
|
||||||
6 | test("login with valid credentials redirects to /admin and shows admin dashboard", async ({
|
|
||||||
7 | page,
|
|
||||||
8 | }) => {
|
|
||||||
9 | // Navigate to login page
|
|
||||||
10 | await page.goto("/login");
|
|
||||||
11 |
|
|
||||||
12 | // Fill credentials
|
|
||||||
13 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
14 | await page.fill("#password", process.env.TEST_ADMIN_PASSWORD!);
|
|
||||||
15 |
|
|
||||||
16 | // Submit form
|
|
||||||
17 | await page.click('button[type="submit"]');
|
|
||||||
18 |
|
|
||||||
19 | // Wait for navigation to /admin — the JSON response + client nav must happen
|
|
||||||
20 | await page.waitForURL("**/admin", { timeout: 10000 });
|
|
||||||
21 |
|
|
||||||
22 | // Admin dashboard must be rendered (Control Center heading or admin layout)
|
|
||||||
23 | await expect(page.locator("body")).not.toContainText("Access Denied");
|
|
||||||
24 | await expect(page.locator("body")).not.toContainText("Login failed");
|
|
||||||
25 | });
|
|
||||||
26 |
|
|
||||||
27 | // ─────────────────────────────────────────────────────────────
|
|
||||||
28 | // Login failure: bad password shows error, stays on /login
|
|
||||||
29 | // ─────────────────────────────────────────────────────────────
|
|
||||||
30 | test("login with wrong password shows error and stays on /login", async ({
|
|
||||||
31 | page,
|
|
||||||
32 | }) => {
|
|
||||||
33 | await page.goto("/login");
|
|
||||||
34 |
|
|
||||||
35 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
36 | await page.fill("#password", "wrongpassword123!");
|
|
||||||
37 |
|
|
||||||
38 | await page.click('button[type="submit"]');
|
|
||||||
39 |
|
|
||||||
40 | // Error message should appear
|
|
||||||
41 | await expect(page.locator('[role="alert"]')).toBeVisible({ timeout: 5000 });
|
|
||||||
42 | // Should NOT navigate away from login
|
|
||||||
43 | await expect(page).toHaveURL(/\/login/);
|
|
||||||
44 | });
|
|
||||||
45 |
|
|
||||||
46 | // ─────────────────────────────────────────────────────────────
|
|
||||||
47 | // Login with missing fields shows validation error
|
|
||||||
48 | // ─────────────────────────────────────────────────────────────
|
|
||||||
49 | test("login with missing email shows validation error", async ({ page }) => {
|
|
||||||
50 | await page.goto("/login");
|
|
||||||
51 |
|
|
||||||
52 | // Don't fill email, only password
|
|
||||||
53 | await page.fill("#password", "something");
|
|
||||||
54 |
|
|
||||||
55 | await page.click('button[type="submit"]');
|
|
||||||
56 |
|
|
||||||
57 | // Browser validation should fire (email required)
|
|
||||||
58 | await expect(page.locator("#email")).toHaveAttribute("required", "");
|
|
||||||
59 | });
|
|
||||||
60 |
|
|
||||||
61 | // ─────────────────────────────────────────────────────────────
|
|
||||||
62 | // Session persistence: after login, navigating to /admin
|
|
||||||
63 | // should load without re-authenticating
|
|
||||||
64 | // ─────────────────────────────────────────────────────────────
|
|
||||||
65 | test("admin session persists across page reloads", async ({ page }) => {
|
|
||||||
66 | // Login first
|
|
||||||
67 | await page.goto("/login");
|
|
||||||
> 68 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
| ^ Error: page.fill: value: expected string, got undefined
|
|
||||||
69 | await page.fill("#password", process.env.TEST_ADMIN_PASSWORD!);
|
|
||||||
70 | await page.click('button[type="submit"]');
|
|
||||||
71 | await page.waitForURL("**/admin", { timeout: 10000 });
|
|
||||||
72 |
|
|
||||||
73 | // Reload the page
|
|
||||||
74 | await page.reload();
|
|
||||||
75 |
|
|
||||||
76 | // Should still be on /admin (session cookie keeps user logged in)
|
|
||||||
77 | await expect(page).toHaveURL(/\/admin/);
|
|
||||||
78 | await expect(page.locator("body")).not.toContainText("Access Denied");
|
|
||||||
79 | });
|
|
||||||
80 |
|
|
||||||
```
|
|
||||||
-152
@@ -1,152 +0,0 @@
|
|||||||
# Instructions
|
|
||||||
|
|
||||||
- Following Playwright test failed.
|
|
||||||
- Explain why, be concise, respect Playwright best practices.
|
|
||||||
- Provide a snippet of code with the fix, if possible.
|
|
||||||
|
|
||||||
# Test info
|
|
||||||
|
|
||||||
- Name: login/login-flow.spec.ts >> login with wrong password shows error and stays on /login
|
|
||||||
- Location: tests/login/login-flow.spec.ts:30:5
|
|
||||||
|
|
||||||
# Error details
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: page.fill: value: expected string, got undefined
|
|
||||||
```
|
|
||||||
|
|
||||||
# Page snapshot
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- generic [active] [ref=e1]:
|
|
||||||
- banner [ref=e2]:
|
|
||||||
- generic [ref=e3]:
|
|
||||||
- link "Route Commerce" [ref=e4] [cursor=pointer]:
|
|
||||||
- /url: /
|
|
||||||
- navigation [ref=e5]:
|
|
||||||
- link "Tuxedo" [ref=e6] [cursor=pointer]:
|
|
||||||
- /url: /tuxedo
|
|
||||||
- link "IRD" [ref=e7] [cursor=pointer]:
|
|
||||||
- /url: /indian-river-direct
|
|
||||||
- link "Admin" [ref=e8] [cursor=pointer]:
|
|
||||||
- /url: /admin
|
|
||||||
- button "Switch to dark mode" [ref=e9]:
|
|
||||||
- img [ref=e10]
|
|
||||||
- link "Cart (0)" [ref=e12] [cursor=pointer]:
|
|
||||||
- /url: /cart
|
|
||||||
- main [ref=e13]:
|
|
||||||
- generic [ref=e14]:
|
|
||||||
- generic [ref=e15]:
|
|
||||||
- heading "Admin Login" [level=1] [ref=e16]
|
|
||||||
- paragraph [ref=e17]: Sign in with your account.
|
|
||||||
- generic [ref=e18]:
|
|
||||||
- generic [ref=e19]:
|
|
||||||
- generic [ref=e20]: Email
|
|
||||||
- textbox "Email" [ref=e21]:
|
|
||||||
- /placeholder: admin@example.com
|
|
||||||
- generic [ref=e22]:
|
|
||||||
- generic [ref=e23]: Password
|
|
||||||
- textbox "Password" [ref=e24]:
|
|
||||||
- /placeholder: ••••••••
|
|
||||||
- button "Sign In" [ref=e25]
|
|
||||||
- button "Forgot password?" [ref=e26]
|
|
||||||
- link "← Back to storefront" [ref=e27] [cursor=pointer]:
|
|
||||||
- /url: /
|
|
||||||
- contentinfo [ref=e28]:
|
|
||||||
- generic [ref=e30]:
|
|
||||||
- paragraph [ref=e31]: © 2026 Route Commerce. All rights reserved.
|
|
||||||
- navigation [ref=e32]:
|
|
||||||
- link "Privacy Policy" [ref=e33] [cursor=pointer]:
|
|
||||||
- /url: /privacy-policy
|
|
||||||
- link "Terms & Conditions" [ref=e34] [cursor=pointer]:
|
|
||||||
- /url: /terms-and-conditions
|
|
||||||
- link "Powered by Route Commerce" [ref=e35] [cursor=pointer]:
|
|
||||||
- /url: https://cielohermosa.com
|
|
||||||
- alert [ref=e36]
|
|
||||||
```
|
|
||||||
|
|
||||||
# Test source
|
|
||||||
|
|
||||||
```ts
|
|
||||||
1 | import { test, expect } from "@playwright/test";
|
|
||||||
2 |
|
|
||||||
3 | // ─────────────────────────────────────────────────────────────
|
|
||||||
4 | // Login flow: credentials → /api/login JSON → redirect to /admin
|
|
||||||
5 | // ─────────────────────────────────────────────────────────────
|
|
||||||
6 | test("login with valid credentials redirects to /admin and shows admin dashboard", async ({
|
|
||||||
7 | page,
|
|
||||||
8 | }) => {
|
|
||||||
9 | // Navigate to login page
|
|
||||||
10 | await page.goto("/login");
|
|
||||||
11 |
|
|
||||||
12 | // Fill credentials
|
|
||||||
13 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
14 | await page.fill("#password", process.env.TEST_ADMIN_PASSWORD!);
|
|
||||||
15 |
|
|
||||||
16 | // Submit form
|
|
||||||
17 | await page.click('button[type="submit"]');
|
|
||||||
18 |
|
|
||||||
19 | // Wait for navigation to /admin — the JSON response + client nav must happen
|
|
||||||
20 | await page.waitForURL("**/admin", { timeout: 10000 });
|
|
||||||
21 |
|
|
||||||
22 | // Admin dashboard must be rendered (Control Center heading or admin layout)
|
|
||||||
23 | await expect(page.locator("body")).not.toContainText("Access Denied");
|
|
||||||
24 | await expect(page.locator("body")).not.toContainText("Login failed");
|
|
||||||
25 | });
|
|
||||||
26 |
|
|
||||||
27 | // ─────────────────────────────────────────────────────────────
|
|
||||||
28 | // Login failure: bad password shows error, stays on /login
|
|
||||||
29 | // ─────────────────────────────────────────────────────────────
|
|
||||||
30 | test("login with wrong password shows error and stays on /login", async ({
|
|
||||||
31 | page,
|
|
||||||
32 | }) => {
|
|
||||||
33 | await page.goto("/login");
|
|
||||||
34 |
|
|
||||||
> 35 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
| ^ Error: page.fill: value: expected string, got undefined
|
|
||||||
36 | await page.fill("#password", "wrongpassword123!");
|
|
||||||
37 |
|
|
||||||
38 | await page.click('button[type="submit"]');
|
|
||||||
39 |
|
|
||||||
40 | // Error message should appear
|
|
||||||
41 | await expect(page.locator('[role="alert"]')).toBeVisible({ timeout: 5000 });
|
|
||||||
42 | // Should NOT navigate away from login
|
|
||||||
43 | await expect(page).toHaveURL(/\/login/);
|
|
||||||
44 | });
|
|
||||||
45 |
|
|
||||||
46 | // ─────────────────────────────────────────────────────────────
|
|
||||||
47 | // Login with missing fields shows validation error
|
|
||||||
48 | // ─────────────────────────────────────────────────────────────
|
|
||||||
49 | test("login with missing email shows validation error", async ({ page }) => {
|
|
||||||
50 | await page.goto("/login");
|
|
||||||
51 |
|
|
||||||
52 | // Don't fill email, only password
|
|
||||||
53 | await page.fill("#password", "something");
|
|
||||||
54 |
|
|
||||||
55 | await page.click('button[type="submit"]');
|
|
||||||
56 |
|
|
||||||
57 | // Browser validation should fire (email required)
|
|
||||||
58 | await expect(page.locator("#email")).toHaveAttribute("required", "");
|
|
||||||
59 | });
|
|
||||||
60 |
|
|
||||||
61 | // ─────────────────────────────────────────────────────────────
|
|
||||||
62 | // Session persistence: after login, navigating to /admin
|
|
||||||
63 | // should load without re-authenticating
|
|
||||||
64 | // ─────────────────────────────────────────────────────────────
|
|
||||||
65 | test("admin session persists across page reloads", async ({ page }) => {
|
|
||||||
66 | // Login first
|
|
||||||
67 | await page.goto("/login");
|
|
||||||
68 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
69 | await page.fill("#password", process.env.TEST_ADMIN_PASSWORD!);
|
|
||||||
70 | await page.click('button[type="submit"]');
|
|
||||||
71 | await page.waitForURL("**/admin", { timeout: 10000 });
|
|
||||||
72 |
|
|
||||||
73 | // Reload the page
|
|
||||||
74 | await page.reload();
|
|
||||||
75 |
|
|
||||||
76 | // Should still be on /admin (session cookie keeps user logged in)
|
|
||||||
77 | await expect(page).toHaveURL(/\/admin/);
|
|
||||||
78 | await expect(page.locator("body")).not.toContainText("Access Denied");
|
|
||||||
79 | });
|
|
||||||
80 |
|
|
||||||
```
|
|
||||||
-152
@@ -1,152 +0,0 @@
|
|||||||
# Instructions
|
|
||||||
|
|
||||||
- Following Playwright test failed.
|
|
||||||
- Explain why, be concise, respect Playwright best practices.
|
|
||||||
- Provide a snippet of code with the fix, if possible.
|
|
||||||
|
|
||||||
# Test info
|
|
||||||
|
|
||||||
- Name: login/login-flow.spec.ts >> login with valid credentials redirects to /admin and shows admin dashboard
|
|
||||||
- Location: tests/login/login-flow.spec.ts:6:5
|
|
||||||
|
|
||||||
# Error details
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: page.fill: value: expected string, got undefined
|
|
||||||
```
|
|
||||||
|
|
||||||
# Page snapshot
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- generic [active] [ref=e1]:
|
|
||||||
- banner [ref=e2]:
|
|
||||||
- generic [ref=e3]:
|
|
||||||
- link "Route Commerce" [ref=e4] [cursor=pointer]:
|
|
||||||
- /url: /
|
|
||||||
- navigation [ref=e5]:
|
|
||||||
- link "Tuxedo" [ref=e6] [cursor=pointer]:
|
|
||||||
- /url: /tuxedo
|
|
||||||
- link "IRD" [ref=e7] [cursor=pointer]:
|
|
||||||
- /url: /indian-river-direct
|
|
||||||
- link "Admin" [ref=e8] [cursor=pointer]:
|
|
||||||
- /url: /admin
|
|
||||||
- button "Switch to dark mode" [ref=e9]:
|
|
||||||
- img [ref=e10]
|
|
||||||
- link "Cart (0)" [ref=e12] [cursor=pointer]:
|
|
||||||
- /url: /cart
|
|
||||||
- main [ref=e13]:
|
|
||||||
- generic [ref=e14]:
|
|
||||||
- generic [ref=e15]:
|
|
||||||
- heading "Admin Login" [level=1] [ref=e16]
|
|
||||||
- paragraph [ref=e17]: Sign in with your account.
|
|
||||||
- generic [ref=e18]:
|
|
||||||
- generic [ref=e19]:
|
|
||||||
- generic [ref=e20]: Email
|
|
||||||
- textbox "Email" [ref=e21]:
|
|
||||||
- /placeholder: admin@example.com
|
|
||||||
- generic [ref=e22]:
|
|
||||||
- generic [ref=e23]: Password
|
|
||||||
- textbox "Password" [ref=e24]:
|
|
||||||
- /placeholder: ••••••••
|
|
||||||
- button "Sign In" [ref=e25]
|
|
||||||
- button "Forgot password?" [ref=e26]
|
|
||||||
- link "← Back to storefront" [ref=e27] [cursor=pointer]:
|
|
||||||
- /url: /
|
|
||||||
- contentinfo [ref=e28]:
|
|
||||||
- generic [ref=e30]:
|
|
||||||
- paragraph [ref=e31]: © 2026 Route Commerce. All rights reserved.
|
|
||||||
- navigation [ref=e32]:
|
|
||||||
- link "Privacy Policy" [ref=e33] [cursor=pointer]:
|
|
||||||
- /url: /privacy-policy
|
|
||||||
- link "Terms & Conditions" [ref=e34] [cursor=pointer]:
|
|
||||||
- /url: /terms-and-conditions
|
|
||||||
- link "Powered by Route Commerce" [ref=e35] [cursor=pointer]:
|
|
||||||
- /url: https://cielohermosa.com
|
|
||||||
- alert [ref=e36]
|
|
||||||
```
|
|
||||||
|
|
||||||
# Test source
|
|
||||||
|
|
||||||
```ts
|
|
||||||
1 | import { test, expect } from "@playwright/test";
|
|
||||||
2 |
|
|
||||||
3 | // ─────────────────────────────────────────────────────────────
|
|
||||||
4 | // Login flow: credentials → /api/login JSON → redirect to /admin
|
|
||||||
5 | // ─────────────────────────────────────────────────────────────
|
|
||||||
6 | test("login with valid credentials redirects to /admin and shows admin dashboard", async ({
|
|
||||||
7 | page,
|
|
||||||
8 | }) => {
|
|
||||||
9 | // Navigate to login page
|
|
||||||
10 | await page.goto("/login");
|
|
||||||
11 |
|
|
||||||
12 | // Fill credentials
|
|
||||||
> 13 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
| ^ Error: page.fill: value: expected string, got undefined
|
|
||||||
14 | await page.fill("#password", process.env.TEST_ADMIN_PASSWORD!);
|
|
||||||
15 |
|
|
||||||
16 | // Submit form
|
|
||||||
17 | await page.click('button[type="submit"]');
|
|
||||||
18 |
|
|
||||||
19 | // Wait for navigation to /admin — the JSON response + client nav must happen
|
|
||||||
20 | await page.waitForURL("**/admin", { timeout: 10000 });
|
|
||||||
21 |
|
|
||||||
22 | // Admin dashboard must be rendered (Control Center heading or admin layout)
|
|
||||||
23 | await expect(page.locator("body")).not.toContainText("Access Denied");
|
|
||||||
24 | await expect(page.locator("body")).not.toContainText("Login failed");
|
|
||||||
25 | });
|
|
||||||
26 |
|
|
||||||
27 | // ─────────────────────────────────────────────────────────────
|
|
||||||
28 | // Login failure: bad password shows error, stays on /login
|
|
||||||
29 | // ─────────────────────────────────────────────────────────────
|
|
||||||
30 | test("login with wrong password shows error and stays on /login", async ({
|
|
||||||
31 | page,
|
|
||||||
32 | }) => {
|
|
||||||
33 | await page.goto("/login");
|
|
||||||
34 |
|
|
||||||
35 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
36 | await page.fill("#password", "wrongpassword123!");
|
|
||||||
37 |
|
|
||||||
38 | await page.click('button[type="submit"]');
|
|
||||||
39 |
|
|
||||||
40 | // Error message should appear
|
|
||||||
41 | await expect(page.locator('[role="alert"]')).toBeVisible({ timeout: 5000 });
|
|
||||||
42 | // Should NOT navigate away from login
|
|
||||||
43 | await expect(page).toHaveURL(/\/login/);
|
|
||||||
44 | });
|
|
||||||
45 |
|
|
||||||
46 | // ─────────────────────────────────────────────────────────────
|
|
||||||
47 | // Login with missing fields shows validation error
|
|
||||||
48 | // ─────────────────────────────────────────────────────────────
|
|
||||||
49 | test("login with missing email shows validation error", async ({ page }) => {
|
|
||||||
50 | await page.goto("/login");
|
|
||||||
51 |
|
|
||||||
52 | // Don't fill email, only password
|
|
||||||
53 | await page.fill("#password", "something");
|
|
||||||
54 |
|
|
||||||
55 | await page.click('button[type="submit"]');
|
|
||||||
56 |
|
|
||||||
57 | // Browser validation should fire (email required)
|
|
||||||
58 | await expect(page.locator("#email")).toHaveAttribute("required", "");
|
|
||||||
59 | });
|
|
||||||
60 |
|
|
||||||
61 | // ─────────────────────────────────────────────────────────────
|
|
||||||
62 | // Session persistence: after login, navigating to /admin
|
|
||||||
63 | // should load without re-authenticating
|
|
||||||
64 | // ─────────────────────────────────────────────────────────────
|
|
||||||
65 | test("admin session persists across page reloads", async ({ page }) => {
|
|
||||||
66 | // Login first
|
|
||||||
67 | await page.goto("/login");
|
|
||||||
68 | await page.fill("#email", process.env.TEST_ADMIN_EMAIL!);
|
|
||||||
69 | await page.fill("#password", process.env.TEST_ADMIN_PASSWORD!);
|
|
||||||
70 | await page.click('button[type="submit"]');
|
|
||||||
71 | await page.waitForURL("**/admin", { timeout: 10000 });
|
|
||||||
72 |
|
|
||||||
73 | // Reload the page
|
|
||||||
74 | await page.reload();
|
|
||||||
75 |
|
|
||||||
76 | // Should still be on /admin (session cookie keeps user logged in)
|
|
||||||
77 | await expect(page).toHaveURL(/\/admin/);
|
|
||||||
78 | await expect(page.locator("body")).not.toContainText("Access Denied");
|
|
||||||
79 | });
|
|
||||||
80 |
|
|
||||||
```
|
|
||||||
Reference in New Issue
Block a user