210 lines
7.5 KiB
TypeScript
210 lines
7.5 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { API_KEY_HEADER } from './helpers'
|
|
|
|
test.describe('Onboarding API', () => {
|
|
// Reset onboarding state before each test to ensure isolation
|
|
test.beforeEach(async ({ request }) => {
|
|
await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'reset' },
|
|
})
|
|
})
|
|
|
|
// ── Auth ─────────────────────────────────────
|
|
|
|
test('GET /api/onboarding returns 401 without auth', async ({ request }) => {
|
|
const res = await request.get('/api/onboarding')
|
|
expect(res.status()).toBe(401)
|
|
})
|
|
|
|
test('POST /api/onboarding returns 401 without auth', async ({ request }) => {
|
|
const res = await request.post('/api/onboarding', {
|
|
data: { action: 'reset' },
|
|
})
|
|
expect(res.status()).toBe(401)
|
|
})
|
|
|
|
// ── GET ──────────────────────────────────────
|
|
|
|
test('GET returns onboarding state with valid auth', async ({ request }) => {
|
|
const res = await request.get('/api/onboarding', { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
expect(body).toHaveProperty('showOnboarding')
|
|
expect(body).toHaveProperty('completed')
|
|
expect(body).toHaveProperty('skipped')
|
|
expect(body).toHaveProperty('checklistDismissed')
|
|
expect(body).toHaveProperty('currentStep')
|
|
expect(body).toHaveProperty('steps')
|
|
})
|
|
|
|
test('GET steps array has expected onboarding steps with id/title/completed', async ({ request }) => {
|
|
const res = await request.get('/api/onboarding', { headers: API_KEY_HEADER })
|
|
const body = await res.json()
|
|
expect(body.steps).toHaveLength(4)
|
|
expect(body.steps.map((step: any) => step.id)).toEqual([
|
|
'welcome',
|
|
'interface-mode',
|
|
'gateway-link',
|
|
'credentials',
|
|
])
|
|
for (const step of body.steps) {
|
|
expect(step).toHaveProperty('id')
|
|
expect(step).toHaveProperty('title')
|
|
expect(step).toHaveProperty('completed')
|
|
expect(typeof step.id).toBe('string')
|
|
expect(typeof step.title).toBe('string')
|
|
expect(typeof step.completed).toBe('boolean')
|
|
}
|
|
})
|
|
|
|
// ── POST: complete_step ──────────────────────
|
|
|
|
test('POST complete_step marks step completed', async ({ request }) => {
|
|
const res = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'complete_step', step: 'welcome' },
|
|
})
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
expect(body.ok).toBe(true)
|
|
expect(body.completedSteps).toContain('welcome')
|
|
})
|
|
|
|
test('POST complete_step without step returns 400', async ({ request }) => {
|
|
const res = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'complete_step' },
|
|
})
|
|
expect(res.status()).toBe(400)
|
|
})
|
|
|
|
test('POST complete_step with invalid step returns 400', async ({ request }) => {
|
|
const res = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'complete_step', step: 'nonexistent' },
|
|
})
|
|
expect(res.status()).toBe(400)
|
|
})
|
|
|
|
// ── POST: skip ───────────────────────────────
|
|
|
|
test('POST skip sets skipped, GET reflects showOnboarding=false', async ({ request }) => {
|
|
const skipRes = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'skip' },
|
|
})
|
|
expect(skipRes.status()).toBe(200)
|
|
|
|
const getRes = await request.get('/api/onboarding', { headers: API_KEY_HEADER })
|
|
const state = await getRes.json()
|
|
expect(state.skipped).toBe(true)
|
|
expect(state.showOnboarding).toBe(false)
|
|
})
|
|
|
|
// ── POST: complete ───────────────────────────
|
|
|
|
test('POST complete sets completed', async ({ request }) => {
|
|
const res = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'complete' },
|
|
})
|
|
expect(res.status()).toBe(200)
|
|
|
|
const getRes = await request.get('/api/onboarding', { headers: API_KEY_HEADER })
|
|
const state = await getRes.json()
|
|
expect(state.completed).toBe(true)
|
|
expect(state.showOnboarding).toBe(false)
|
|
})
|
|
|
|
test('POST dismiss_checklist is persisted per user in onboarding state', async ({ request }) => {
|
|
const res = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'dismiss_checklist' },
|
|
})
|
|
expect(res.status()).toBe(200)
|
|
|
|
const getRes = await request.get('/api/onboarding', { headers: API_KEY_HEADER })
|
|
const state = await getRes.json()
|
|
expect(state.checklistDismissed).toBe(true)
|
|
})
|
|
|
|
// ── POST: reset ──────────────────────────────
|
|
|
|
test('POST reset clears all state', async ({ request }) => {
|
|
// First complete onboarding and dismiss checklist
|
|
await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'complete' },
|
|
})
|
|
await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'dismiss_checklist' },
|
|
})
|
|
|
|
// Reset
|
|
const res = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'reset' },
|
|
})
|
|
expect(res.status()).toBe(200)
|
|
|
|
// Verify onboarding state
|
|
const getRes = await request.get('/api/onboarding', { headers: API_KEY_HEADER })
|
|
const state = await getRes.json()
|
|
expect(state.completed).toBe(false)
|
|
expect(state.skipped).toBe(false)
|
|
expect(state.checklistDismissed).toBe(false)
|
|
expect(state.steps.every((s: any) => s.completed === false)).toBe(true)
|
|
})
|
|
|
|
// ── POST: invalid action ─────────────────────
|
|
|
|
test('POST with invalid action returns 400', async ({ request }) => {
|
|
const res = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'invalid_action' },
|
|
})
|
|
expect(res.status()).toBe(400)
|
|
})
|
|
|
|
// ── Full lifecycle ───────────────────────────
|
|
|
|
test('full lifecycle: reset -> steps -> complete -> verify', async ({ request }) => {
|
|
// Reset
|
|
await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'reset' },
|
|
})
|
|
|
|
// Verify initial state shows onboarding
|
|
const initial = await request.get('/api/onboarding', { headers: API_KEY_HEADER })
|
|
const initialState = await initial.json()
|
|
expect(initialState.completed).toBe(false)
|
|
expect(initialState.skipped).toBe(false)
|
|
|
|
// Complete all configured steps
|
|
for (const stepId of ['welcome', 'interface-mode', 'gateway-link', 'credentials']) {
|
|
const res = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'complete_step', step: stepId },
|
|
})
|
|
expect(res.status()).toBe(200)
|
|
}
|
|
|
|
// Mark complete
|
|
const completeRes = await request.post('/api/onboarding', {
|
|
headers: API_KEY_HEADER,
|
|
data: { action: 'complete' },
|
|
})
|
|
expect(completeRes.status()).toBe(200)
|
|
|
|
// Verify done
|
|
const final = await request.get('/api/onboarding', { headers: API_KEY_HEADER })
|
|
const finalState = await final.json()
|
|
expect(finalState.completed).toBe(true)
|
|
expect(finalState.showOnboarding).toBe(false)
|
|
expect(finalState.steps.every((s: any) => s.completed === true)).toBe(true)
|
|
})
|
|
})
|