Skip to content

Commit 44c6138

Browse files
test(auth): add unit and e2e tests for auto-redirect
- Unit tests for redirect URL construction and preservation - E2E tests for unauthenticated redirect flow and authenticated redirect Amp-Thread-ID: https://ampcode.com/threads/T-019c17af-6967-74be-8e42-6191059b8d63 Co-authored-by: Amp <amp@ampcode.com>
1 parent 636f2c4 commit 44c6138

2 files changed

Lines changed: 137 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { describe, it, expect } from 'vitest'
2+
3+
describe('Auth redirect URL construction', () => {
4+
it('should construct correct login URL with redirectTo param from pathname', () => {
5+
const request = new Request('http://localhost:3000/dashboard/courses')
6+
const redirectTo = new URL(request.url).pathname
7+
8+
const searchParams = new URLSearchParams([['redirectTo', redirectTo]])
9+
const loginUrl = `/login?${searchParams}`
10+
11+
expect(loginUrl).toBe('/login?redirectTo=%2Fdashboard%2Fcourses')
12+
})
13+
14+
it('should use custom redirectTo when provided', () => {
15+
const redirectTo = '/custom/path'
16+
17+
const searchParams = new URLSearchParams([['redirectTo', redirectTo]])
18+
const loginUrl = `/login?${searchParams}`
19+
20+
expect(loginUrl).toBe('/login?redirectTo=%2Fcustom%2Fpath')
21+
})
22+
23+
it('should handle root path', () => {
24+
const request = new Request('http://localhost:3000/')
25+
const redirectTo = new URL(request.url).pathname
26+
27+
const searchParams = new URLSearchParams([['redirectTo', redirectTo]])
28+
const loginUrl = `/login?${searchParams}`
29+
30+
expect(loginUrl).toBe('/login?redirectTo=%2F')
31+
})
32+
33+
it('should handle paths with query parameters', () => {
34+
const redirectTo = '/dashboard/courses?filter=active'
35+
36+
const searchParams = new URLSearchParams([['redirectTo', redirectTo]])
37+
const loginUrl = `/login?${searchParams}`
38+
39+
expect(loginUrl).toContain('redirectTo=')
40+
expect(decodeURIComponent(loginUrl)).toContain(
41+
'/dashboard/courses?filter=active'
42+
)
43+
})
44+
})
45+
46+
describe('Login redirect URL preservation', () => {
47+
it('should extract redirectTo from URL search params', () => {
48+
const url = new URL(
49+
'http://localhost:3000/login?redirectTo=/dashboard/profile'
50+
)
51+
const redirectTo = url.searchParams.get('redirectTo') ?? '/dashboard'
52+
53+
expect(redirectTo).toBe('/dashboard/profile')
54+
})
55+
56+
it('should default to /dashboard when redirectTo is not provided', () => {
57+
const url = new URL('http://localhost:3000/login')
58+
const redirectTo = url.searchParams.get('redirectTo') ?? '/dashboard'
59+
60+
expect(redirectTo).toBe('/dashboard')
61+
})
62+
63+
it('should preserve redirectTo in success/failure redirects', () => {
64+
const redirectTo = '/dashboard/transactions'
65+
const successRedirect = `/login?redirectTo=${encodeURIComponent(
66+
redirectTo
67+
)}`
68+
const failureRedirect = `/login?redirectTo=${encodeURIComponent(
69+
redirectTo
70+
)}`
71+
72+
expect(successRedirect).toBe(
73+
'/login?redirectTo=%2Fdashboard%2Ftransactions'
74+
)
75+
expect(failureRedirect).toBe(
76+
'/login?redirectTo=%2Fdashboard%2Ftransactions'
77+
)
78+
})
79+
})

e2e/auth-redirect.spec.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { test, expect } from './base-test'
2+
import { authFixtures } from './fixtures'
3+
4+
test.describe('Auto-redirect after authentication', () => {
5+
test.describe('Unauthenticated user', () => {
6+
test.use({
7+
storageState: authFixtures.public,
8+
})
9+
10+
test('redirects to login with redirectTo param when accessing protected route', async ({
11+
page,
12+
}) => {
13+
await page.goto('/dashboard/profile/edit')
14+
15+
await page.waitForURL('**/login**')
16+
const url = new URL(page.url())
17+
expect(url.pathname).toBe('/login')
18+
expect(url.searchParams.get('redirectTo')).toBe('/dashboard/profile/edit')
19+
})
20+
21+
test('login page preserves redirectTo in form action URL', async ({
22+
page,
23+
}) => {
24+
await page.goto('/login?redirectTo=/dashboard/transactions')
25+
26+
await expect(page.getByText('Masuk ke akun Anda')).toBeVisible()
27+
28+
const currentUrl = new URL(page.url())
29+
expect(currentUrl.searchParams.get('redirectTo')).toBe(
30+
'/dashboard/transactions'
31+
)
32+
})
33+
})
34+
35+
test.describe('Authenticated user', () => {
36+
test.use({
37+
storageState: authFixtures.member,
38+
})
39+
40+
test('login page redirects to redirectTo param if already authenticated', async ({
41+
page,
42+
}) => {
43+
await page.goto('/login?redirectTo=/dashboard/profile/edit')
44+
45+
await page.waitForURL('**/dashboard/profile/edit')
46+
expect(page.url()).toContain('/dashboard/profile/edit')
47+
})
48+
49+
test('login page redirects to dashboard by default if already authenticated', async ({
50+
page,
51+
}) => {
52+
await page.goto('/login')
53+
54+
await page.waitForURL('**/dashboard')
55+
expect(page.url()).toContain('/dashboard')
56+
})
57+
})
58+
})

0 commit comments

Comments
 (0)