1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
import { test, expect } from '@playwright/test';
/**
* E2E tests for authentication flows.
*
* These tests verify login behavior both with and without a password configured.
* The current setup assumes no password (default for dev), so the password-required
* tests are marked as skip. To run those, start the backend with --password=testpass.
*/
test.describe('Authentication - No Password Required', () => {
test('should allow direct access to dashboard without login', async ({ page }) => {
// When no password is configured, users should be able to access
// the dashboard directly without seeing the login page
await page.goto('/v2/');
// Should not redirect to login
await expect(page).toHaveURL(/.*\/v2\/?$/);
// Should see the dashboard elements
await expect(page.locator('h1.logo')).toContainText('🐱');
await expect(page.getByText('Logout')).toBeVisible();
});
test('should allow login with empty password', async ({ page }) => {
// Visit login page
await page.goto('/v2/login');
// Submit with empty password
const passwordInput = page.getByLabel(/password/i);
await expect(passwordInput).toBeVisible();
// Leave password empty and submit
await page.click('button[type="submit"]');
// Should redirect to dashboard
await expect(page).toHaveURL(/.*\/v2\/?$/, { timeout: 5000 });
await expect(page.locator('h1.logo')).toContainText('🐱');
});
test('should report authenticated status via API when no password', async ({ request }) => {
// Check auth status
const response = await request.get('/api/auth');
expect(response.ok()).toBeTruthy();
const data = await response.json();
expect(data.authenticated).toBe(true);
});
});
test.describe('Authentication - Password Required', () => {
// These tests require the backend to be started with a password
// Example: neko --password=testpass
// Skip by default since dev environment has no password
test.skip('should redirect to login when accessing protected routes', async ({ page, context }) => {
// Clear any existing cookies
await context.clearCookies();
// Try to access dashboard
await page.goto('/v2/');
// Should redirect to login
await expect(page).toHaveURL(/.*\/login/, { timeout: 5000 });
});
test.skip('should reject incorrect password', async ({ page }) => {
await page.goto('/v2/login');
// Enter wrong password
await page.fill('input[type="password"]', 'wrongpassword');
await page.click('button[type="submit"]');
// Should show error message
await expect(page.getByText(/bad credentials|login failed/i)).toBeVisible({ timeout: 3000 });
// Should still be on login page
await expect(page).toHaveURL(/.*\/login/);
});
test.skip('should accept correct password and redirect to dashboard', async ({ page }) => {
await page.goto('/v2/login');
// Enter correct password (must match what the server was started with)
await page.fill('input[type="password"]', 'testpass');
await page.click('button[type="submit"]');
// Should redirect to dashboard
await expect(page).toHaveURL(/.*\/v2\/?$/, { timeout: 5000 });
await expect(page.locator('h1.logo')).toContainText('🐱');
await expect(page.getByText('Logout')).toBeVisible();
});
test.skip('should persist authentication across page reloads', async ({ page }) => {
// Login first
await page.goto('/v2/login');
await page.fill('input[type="password"]', 'testpass');
await page.click('button[type="submit"]');
await expect(page).toHaveURL(/.*\/v2\/?$/);
// Reload the page
await page.reload();
// Should still be authenticated (not redirected to login)
await expect(page).toHaveURL(/.*\/v2\/?$/);
await expect(page.locator('h1.logo')).toContainText('🐱');
});
test.skip('should logout and redirect to login page', async ({ page }) => {
// Login first
await page.goto('/v2/login');
await page.fill('input[type="password"]', 'testpass');
await page.click('button[type="submit"]');
await expect(page).toHaveURL(/.*\/v2\/?$/);
// Click logout
await page.click('text=Logout');
// Should redirect to login
await expect(page).toHaveURL(/.*\/login/);
// Try to access dashboard again - should redirect to login
await page.goto('/v2/');
await expect(page).toHaveURL(/.*\/login/);
});
test.skip('should report unauthenticated status via API', async ({ request, context }) => {
// Clear cookies
await context.clearCookies();
// Check auth status
const response = await request.get('/api/auth');
expect(response.ok()).toBeTruthy();
const data = await response.json();
expect(data.authenticated).toBe(false);
});
});
test.describe('Authentication - Complete Flow', () => {
test('should handle complete user flow without password', async ({ page }) => {
// 1. Access dashboard directly
await page.goto('/v2/');
await expect(page.locator('h1.logo')).toContainText('🐱');
// 2. Navigate to settings
await page.click('text=Settings');
await expect(page).toHaveURL(/.*\/settings/);
// 3. Add a feed (this tests that API calls work when no password)
const feedUrl = 'http://example.com/rss.xml';
await page.fill('input[type="url"]', feedUrl);
await page.click('text=Add Feed');
// Wait for success (feed should appear)
await expect(page.getByText(feedUrl)).toBeVisible({ timeout: 3000 });
// 4. Navigate back to main view
await page.goto('/v2/');
await expect(page.locator('h1.logo')).toContainText('🐱');
// 5. Logout (should work even with no password)
await page.click('text=Logout');
await expect(page).toHaveURL(/.*\/login/);
});
});
|