Skip to content

Commit 852011d

Browse files
committed
feat(e2e): add login flow test cases
- Add login-flow.spec.ts with 4 test cases: - First-time user login via GitHub OAuth - CLI responsiveness after login - /usage command after login - Logout and re-login flow
1 parent f5aaaef commit 852011d

File tree

1 file changed

+190
-0
lines changed

1 file changed

+190
-0
lines changed

e2e/flows/login-flow.spec.ts

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/**
2+
* End-to-End Login Flow Tests
3+
*
4+
* Tests the complete login flow: CLI → Browser → GitHub OAuth → Callback → CLI
5+
*
6+
* Prerequisites:
7+
* - Docker must be running
8+
* - SDK must be built: cd sdk && bun run build
9+
* - Playwright browsers installed: bunx playwright install chromium
10+
* - GitHub test credentials configured
11+
*
12+
* Run with: cd e2e && bun run test
13+
*/
14+
15+
import { test, expect } from '@playwright/test'
16+
import { hasRequiredCredentials, logSkipReason } from '../utils/env'
17+
18+
// Check credentials at module load time
19+
const hasCredentials = hasRequiredCredentials()
20+
21+
if (!hasCredentials) {
22+
logSkipReason('GitHub test credentials not configured (GH_TEST_EMAIL, GH_TEST_PASSWORD)')
23+
}
24+
25+
// Only define tests if credentials are available
26+
if (hasCredentials) {
27+
test.describe('E2E Login Flow', () => {
28+
test.describe.configure({ mode: 'serial' }) // Run tests serially
29+
30+
// Lazy-load the heavy fixtures only when tests actually run
31+
let testContext: typeof import('../fixtures/test-context') | null = null
32+
33+
test.beforeAll(async () => {
34+
// Dynamically import the test context (which imports infrastructure)
35+
testContext = await import('../fixtures/test-context')
36+
37+
const prereqs = testContext.checkPrerequisites()
38+
if (!prereqs.ready) {
39+
logSkipReason(prereqs.reason!)
40+
test.skip(true, prereqs.reason)
41+
}
42+
})
43+
44+
test('first-time user can login via GitHub OAuth', async ({ page }) => {
45+
test.skip(!testContext, 'Test context not initialized')
46+
47+
const ctx = await testContext!.createE2ETestContext('first-login')
48+
49+
try {
50+
const { createCLISession, completeOAuth } = ctx
51+
52+
// 1. Launch CLI without existing credentials
53+
console.log('[Test] Launching CLI...')
54+
const cli = await createCLISession()
55+
56+
// 2. Wait for login prompt - auto-login triggers automatically via CODEBUFF_E2E_NO_BROWSER
57+
console.log('[Test] Waiting for login prompt (auto-login will trigger)...')
58+
await cli.waitForText(/Press ENTER|login|sign in/i, { timeout: 30000 })
59+
60+
// 3. Wait for login URL (auto-triggered after 1 second delay)
61+
console.log('[Test] Waiting for login URL...')
62+
const loginUrl = await cli.waitForLoginUrl(30000)
63+
console.log(`[Test] Got login URL: ${loginUrl}`)
64+
65+
expect(loginUrl).toContain('auth_code=')
66+
67+
// 5. Complete OAuth in browser
68+
console.log('[Test] Starting OAuth flow in browser...')
69+
await completeOAuth(page, loginUrl)
70+
71+
// 6. Verify CLI detected successful login
72+
console.log('[Test] Waiting for CLI to detect login...')
73+
await cli.waitForText(/directory|welcome|logged in/i, { timeout: 45000 })
74+
75+
const cliText = await cli.text()
76+
// CLI should show main interface after successful login
77+
expect(cliText.toLowerCase()).toMatch(/directory|welcome|logged in/)
78+
79+
console.log('[Test] Login flow completed successfully!')
80+
} finally {
81+
await ctx.infra.cleanup()
82+
}
83+
})
84+
85+
test('CLI remains responsive after login', async ({ page }) => {
86+
test.skip(!testContext, 'Test context not initialized')
87+
88+
const ctx = await testContext!.createE2ETestContext('responsive')
89+
90+
try {
91+
const { createCLISession, completeOAuth } = ctx
92+
93+
// Complete login first (auto-login via CODEBUFF_E2E_NO_BROWSER)
94+
const cli = await createCLISession()
95+
await cli.waitForText(/Press ENTER|login|sign in/i, { timeout: 30000 })
96+
const loginUrl = await cli.waitForLoginUrl(30000)
97+
await completeOAuth(page, loginUrl)
98+
await cli.waitForText(/directory/i, { timeout: 45000 })
99+
100+
// Test that CLI is responsive
101+
console.log('[Test] Verifying CLI is responsive...')
102+
await cli.type('hello test')
103+
await cli.waitForText('hello test', { timeout: 5000 })
104+
105+
const text = await cli.text()
106+
expect(text).toContain('hello test')
107+
108+
console.log('[Test] CLI is responsive after login!')
109+
} finally {
110+
await ctx.infra.cleanup()
111+
}
112+
})
113+
114+
test('/usage command works after login', async ({ page }) => {
115+
test.skip(!testContext, 'Test context not initialized')
116+
117+
const ctx = await testContext!.createE2ETestContext('usage-cmd')
118+
119+
try {
120+
const { createCLISession, completeOAuth } = ctx
121+
122+
// Complete login first (auto-login via CODEBUFF_E2E_NO_BROWSER)
123+
const cli = await createCLISession()
124+
await cli.waitForText(/Press ENTER|login|sign in/i, { timeout: 30000 })
125+
const loginUrl = await cli.waitForLoginUrl(30000)
126+
await completeOAuth(page, loginUrl)
127+
await cli.waitForText(/directory/i, { timeout: 45000 })
128+
129+
// Test /usage command
130+
console.log('[Test] Testing /usage command...')
131+
await cli.type('/usage')
132+
await cli.press('enter')
133+
134+
await cli.waitForText(/credit|usage|balance/i, { timeout: 15000 })
135+
136+
const text = await cli.text()
137+
expect(text.toLowerCase()).toMatch(/credit|usage|balance/)
138+
139+
console.log('[Test] /usage command works!')
140+
} finally {
141+
await ctx.infra.cleanup()
142+
}
143+
})
144+
145+
test('logout and re-login flow works', async ({ page }) => {
146+
test.skip(!testContext, 'Test context not initialized')
147+
148+
const ctx = await testContext!.createE2ETestContext('logout-relogin')
149+
150+
try {
151+
const { createCLISession, completeOAuth } = ctx
152+
153+
// Complete initial login (auto-login via CODEBUFF_E2E_NO_BROWSER)
154+
const cli = await createCLISession()
155+
await cli.waitForText(/Press ENTER|login|sign in/i, { timeout: 30000 })
156+
let loginUrl = await cli.waitForLoginUrl(30000)
157+
await completeOAuth(page, loginUrl)
158+
await cli.waitForText(/directory/i, { timeout: 45000 })
159+
160+
// Logout
161+
console.log('[Test] Testing logout...')
162+
await cli.type('/logout')
163+
await cli.press('enter')
164+
165+
// Wait for logout to complete and login prompt to reappear
166+
await cli.waitForText(/login|sign in|logged out/i, { timeout: 15000 })
167+
168+
// Re-login
169+
console.log('[Test] Re-logging in...')
170+
await cli.press('enter')
171+
loginUrl = await cli.waitForLoginUrl(30000)
172+
await completeOAuth(page, loginUrl)
173+
await cli.waitForText(/directory/i, { timeout: 45000 })
174+
175+
const text = await cli.text()
176+
expect(text.toLowerCase()).toContain('directory')
177+
178+
console.log('[Test] Logout and re-login flow works!')
179+
} finally {
180+
await ctx.infra.cleanup()
181+
}
182+
})
183+
})
184+
} else {
185+
// No credentials - register a single skipped test to show in the report
186+
test.describe('E2E Login Flow', () => {
187+
test.skip(true, 'GitHub test credentials not configured (GH_TEST_EMAIL, GH_TEST_PASSWORD)')
188+
test('skipped - credentials not configured', () => {})
189+
})
190+
}

0 commit comments

Comments
 (0)