Files
schedule-planner/frontend/cypress/e2e/sleep-buddy.cy.js
Hera Zhao d3f3b4f37b
Some checks failed
Test / build-check (push) Successful in 3s
PR Preview / test (pull_request) Successful in 3s
PR Preview / teardown-preview (pull_request) Has been skipped
Test / e2e-test (push) Failing after 55s
PR Preview / deploy-preview (pull_request) Failing after 40s
Refactor to Vue 3 + FastAPI + SQLite architecture
- Backend: FastAPI + SQLite (WAL mode), 22 tables, ~40 API endpoints
- Frontend: Vue 3 + Vite + Pinia + Vue Router, 8 views, 3 stores
- Database: migrate from JSON file to SQLite with proper schema
- Dockerfile: multi-stage build (node + python)
- Deploy: K8s manifests (namespace, deployment, service, ingress, pvc, backup)
- CI/CD: Gitea Actions (test, deploy, PR preview at pr-$id.planner.oci.euphon.net)
- Tests: 20 Cypress E2E test files, 196 test cases, ~85% coverage
- Doc: test-coverage.md with full feature coverage report

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 21:18:22 +00:00

129 lines
4.5 KiB
JavaScript

describe('Sleep Buddy (睡眠打卡)', () => {
beforeEach(() => {
cy.visit('/sleep-buddy', {
onBeforeLoad(win) {
win.localStorage.setItem('sp_login_expires', String(Date.now() + 86400000))
}
})
})
it('shows login form when not logged in as buddy', () => {
cy.get('.buddy-login').should('be.visible')
cy.get('.buddy-login-logo').should('contain', '🌙')
cy.contains('睡眠打卡').should('be.visible')
cy.contains('和好友一起早睡').should('be.visible')
})
it('has username and password fields', () => {
cy.get('.buddy-login-card input').should('have.length.gte', 2)
cy.get('.buddy-login-card input[type="password"]').should('exist')
})
it('toggle between login and register mode', () => {
cy.get('.buddy-toggle-btn').should('contain', '没有账号?注册')
cy.get('.buddy-toggle-btn').click()
cy.get('.buddy-main-btn').should('contain', '注册')
cy.get('.buddy-login-card input').should('have.length', 3) // username, password, confirm
cy.get('.buddy-toggle-btn').should('contain', '已有账号?登录')
})
it('register mode shows confirm password', () => {
cy.get('.buddy-toggle-btn').click()
cy.get('.buddy-login-card input[type="password"]').should('have.length', 2)
})
it('shows error for mismatched passwords during register', () => {
cy.get('.buddy-toggle-btn').click()
cy.get('.buddy-login-card input').eq(0).type('testuser')
cy.get('.buddy-login-card input').eq(1).type('pass1')
cy.get('.buddy-login-card input').eq(2).type('pass2')
cy.get('.buddy-main-btn').click()
cy.get('.buddy-error').should('contain', '密码不一致')
})
it('register then login flow', () => {
const user = 'testuser_' + Date.now()
// Register
cy.get('.buddy-toggle-btn').click()
cy.get('.buddy-login-card input').eq(0).type(user)
cy.get('.buddy-login-card input').eq(1).type('testpass')
cy.get('.buddy-login-card input').eq(2).type('testpass')
cy.get('.buddy-main-btn').click()
// Should be logged in
cy.get('.buddy-main', { timeout: 5000 }).should('be.visible')
cy.contains(user).should('be.visible')
})
// Tests that require buddy login
describe('when logged in', () => {
const user = 'cy_test_' + Math.random().toString(36).slice(2, 8)
beforeEach(() => {
cy.visit('/sleep-buddy', {
onBeforeLoad(win) {
win.localStorage.setItem('sp_login_expires', String(Date.now() + 86400000))
win.localStorage.setItem('buddy_session', JSON.stringify({
username: user,
exp: Date.now() + 86400000
}))
}
})
// Register the user via API first
cy.window().then(async (win) => {
const buf = await win.crypto.subtle.digest('SHA-256', new TextEncoder().encode('testpass'))
const hash = Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('')
try {
await fetch('/api/buddy-register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: user, hash })
})
} catch {}
})
})
it('shows main buddy interface', () => {
cy.get('.buddy-main', { timeout: 5000 }).should('be.visible')
cy.get('.sleep-btn').should('contain', '我去睡觉啦')
})
it('shows target time card', () => {
cy.get('.target-card').should('be.visible')
cy.get('.target-time').should('not.be.empty')
})
it('shows record input', () => {
cy.get('.record-card').should('be.visible')
cy.get('.capture-row input').should('be.visible')
})
it('records sleep time', () => {
cy.get('.capture-row input').type('22:30')
cy.get('.capture-row button').click()
cy.get('.buddy-hint').should('contain', '已记录')
})
it('shows error for unrecognized input', () => {
cy.get('.capture-row input').type('乱七八糟')
cy.get('.capture-row button').click()
cy.get('.buddy-hint').should('contain', '无法识别')
})
it('go sleep button sends notification', () => {
cy.get('.sleep-btn').click()
cy.get('.buddy-hint').should('contain', '晚安')
})
it('user menu shows logout', () => {
cy.get('.user-chip').click()
cy.get('.user-menu button').should('contain', '退出登录')
})
it('logout returns to login form', () => {
cy.get('.user-chip').click()
cy.contains('退出登录').click()
cy.get('.buddy-login').should('be.visible')
})
})
})