Add comprehensive testing covering 420 unit/handler tests across 24 Vitest files, 9 Playwright E2E specs, accessibility scans, and visual regression. Includes GitHub Actions CI, Husky pre-push hook, and TESTING.md docs.
89 lines
2.8 KiB
JavaScript
89 lines
2.8 KiB
JavaScript
import { describe, it, expect } from 'vitest'
|
|
import { readFileSync } from 'node:fs'
|
|
import { resolve } from 'node:path'
|
|
|
|
const eventsDir = resolve(import.meta.dirname, '../../../server/api/events/[id]')
|
|
|
|
describe('register.post.js', () => {
|
|
const source = readFileSync(resolve(eventsDir, 'register.post.js'), 'utf-8')
|
|
|
|
it('uses validateBody for input validation', () => {
|
|
expect(source).toContain('validateBody(event')
|
|
})
|
|
|
|
it('checks for duplicate registration with case-insensitive email', () => {
|
|
expect(source).toContain('email.toLowerCase()')
|
|
})
|
|
|
|
it('checks membersOnly restriction', () => {
|
|
expect(source).toContain('membersOnly')
|
|
})
|
|
|
|
it('checks capacity via maxAttendees', () => {
|
|
expect(source).toContain('maxAttendees')
|
|
})
|
|
|
|
it('does not let email failure block registration', () => {
|
|
// The await call (not the import) must be wrapped in try/catch
|
|
const emailCallIndex = source.indexOf('await sendEventRegistrationEmail')
|
|
expect(emailCallIndex).toBeGreaterThan(-1)
|
|
|
|
// A try block immediately precedes the email call
|
|
const precedingSource = source.slice(0, emailCallIndex)
|
|
const lastTryIndex = precedingSource.lastIndexOf('try')
|
|
expect(lastTryIndex).toBeGreaterThan(-1)
|
|
|
|
// The catch after the email call should log but not re-throw
|
|
const afterEmail = source.slice(emailCallIndex)
|
|
const catchBlock = afterEmail.match(/catch\s*\(\w+\)\s*\{[^}]*\}/s)
|
|
expect(catchBlock).not.toBeNull()
|
|
expect(catchBlock[0]).toContain('console.error')
|
|
})
|
|
})
|
|
|
|
describe('guest-register.post.js', () => {
|
|
const source = readFileSync(resolve(eventsDir, 'guest-register.post.js'), 'utf-8')
|
|
|
|
it('uses validateBody for input validation', () => {
|
|
expect(source).toContain('validateBody(event')
|
|
})
|
|
|
|
it('checks membersOnly restriction with 403', () => {
|
|
expect(source).toContain('membersOnly')
|
|
expect(source).toContain('403')
|
|
})
|
|
|
|
it('checks payment requirement with 402', () => {
|
|
expect(source).toContain('paymentRequired')
|
|
expect(source).toContain('402')
|
|
})
|
|
|
|
it('checks capacity via maxAttendees', () => {
|
|
expect(source).toContain('maxAttendees')
|
|
})
|
|
|
|
it('does not require auth', () => {
|
|
expect(source).not.toContain('requireAuth')
|
|
})
|
|
})
|
|
|
|
describe('cancel-registration.post.js', () => {
|
|
const source = readFileSync(resolve(eventsDir, 'cancel-registration.post.js'), 'utf-8')
|
|
|
|
it('uses validateBody for input validation', () => {
|
|
expect(source).toContain('validateBody(event')
|
|
})
|
|
|
|
it('finds registration by email', () => {
|
|
expect(source).toContain('email.toLowerCase()')
|
|
})
|
|
|
|
it('notifies waitlist after cancellation', () => {
|
|
expect(source).toContain('waitlist')
|
|
expect(source).toContain('sendWaitlistNotificationEmail')
|
|
})
|
|
|
|
it('does not require auth', () => {
|
|
expect(source).not.toContain('requireAuth')
|
|
})
|
|
})
|