Add comprehensive test suite: 105 unit + 167 E2E tests
- Vitest unit tests: smart paste parsing (37), cost calculations (21),
oil translation (16), dialog system (12), with production data fixtures
- Cypress E2E tests: API CRUD (27), auth flow (8), recipe detail (10),
search (12), oil reference (4), favorites (6), inventory (6),
recipe management (10), diary (11), bug tracker (8), user management (13),
cost parity (6), data integrity (8), responsive (9), performance (6),
navigation (8), admin flow (5)
- Test coverage doc with prioritized gap analysis
- Found backend bug: POST /api/bug-reports/{id}/comment deletes the bug
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
216
frontend/cypress/e2e/diary-flow.cy.js
Normal file
216
frontend/cypress/e2e/diary-flow.cy.js
Normal file
@@ -0,0 +1,216 @@
|
||||
describe('Diary Flow', () => {
|
||||
const ADMIN_TOKEN = 'c86ae7afbe10fabe3c1d5e1a7fee74feaadfd5dc7be2ab62'
|
||||
const authHeaders = { Authorization: `Bearer ${ADMIN_TOKEN}` }
|
||||
let testDiaryId = null
|
||||
|
||||
describe('API: full diary lifecycle', () => {
|
||||
it('creates a diary entry via API', () => {
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: '/api/diary',
|
||||
headers: authHeaders,
|
||||
body: {
|
||||
name: 'Cypress_Diary_Test_日记',
|
||||
ingredients: [
|
||||
{ oil: '薰衣草', drops: 3 },
|
||||
{ oil: '茶树', drops: 2 }
|
||||
],
|
||||
note: '这是E2E测试创建的日记'
|
||||
}
|
||||
}).then(res => {
|
||||
expect(res.status).to.be.oneOf([200, 201])
|
||||
testDiaryId = res.body.id || res.body._id
|
||||
expect(testDiaryId).to.exist
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies diary entry appears in GET /api/diary', () => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
expect(res.body).to.be.an('array')
|
||||
const found = res.body.find(d => d.name === 'Cypress_Diary_Test_日记')
|
||||
expect(found).to.exist
|
||||
expect(found.ingredients).to.have.length(2)
|
||||
expect(found.note).to.eq('这是E2E测试创建的日记')
|
||||
testDiaryId = found.id || found._id
|
||||
})
|
||||
})
|
||||
|
||||
it('updates the diary entry via PUT', () => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
const found = res.body.find(d => d.name === 'Cypress_Diary_Test_日记')
|
||||
testDiaryId = found.id || found._id
|
||||
cy.request({
|
||||
method: 'PUT',
|
||||
url: `/api/diary/${testDiaryId}`,
|
||||
headers: authHeaders,
|
||||
body: {
|
||||
name: 'Cypress_Diary_Updated_日记',
|
||||
ingredients: [
|
||||
{ oil: '薰衣草', drops: 5 },
|
||||
{ oil: '乳香', drops: 3 }
|
||||
],
|
||||
note: '已更新的日记'
|
||||
}
|
||||
}).then(res => {
|
||||
expect(res.status).to.eq(200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies the update took effect', () => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
const found = res.body.find(d => d.name === 'Cypress_Diary_Updated_日记')
|
||||
expect(found).to.exist
|
||||
expect(found.note).to.eq('已更新的日记')
|
||||
expect(found.ingredients).to.have.length(2)
|
||||
testDiaryId = found.id || found._id
|
||||
})
|
||||
})
|
||||
|
||||
it('adds a journal entry to the diary', () => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
const found = res.body.find(d => d.name === 'Cypress_Diary_Updated_日记')
|
||||
testDiaryId = found.id || found._id
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/diary/${testDiaryId}/entries`,
|
||||
headers: authHeaders,
|
||||
body: {
|
||||
content: 'Cypress测试日志: 使用后感觉很好'
|
||||
}
|
||||
}).then(res => {
|
||||
expect(res.status).to.be.oneOf([200, 201])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies journal entry exists in diary', () => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
const found = res.body.find(d => d.name === 'Cypress_Diary_Updated_日记')
|
||||
expect(found).to.exist
|
||||
expect(found.entries).to.be.an('array')
|
||||
expect(found.entries.length).to.be.gte(1)
|
||||
const entry = found.entries.find(e =>
|
||||
(e.text || e.content || '').includes('Cypress测试日志')
|
||||
)
|
||||
expect(entry).to.exist
|
||||
})
|
||||
})
|
||||
|
||||
it('deletes the journal entry', () => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
const found = res.body.find(d => d.name === 'Cypress_Diary_Updated_日记')
|
||||
const entry = found.entries.find(e =>
|
||||
(e.text || e.content || '').includes('Cypress测试日志')
|
||||
)
|
||||
const entryId = entry.id || entry._id
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `/api/diary/entries/${entryId}`,
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
expect(res.status).to.eq(200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('deletes the diary entry', () => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
const found = res.body.find(d => d.name === 'Cypress_Diary_Updated_日记')
|
||||
if (found) {
|
||||
const id = found.id || found._id
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `/api/diary/${id}`,
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
expect(res.status).to.eq(200)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('verifies diary entry is gone', () => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders
|
||||
}).then(res => {
|
||||
const found = res.body.find(d =>
|
||||
d.name === 'Cypress_Diary_Updated_日记' || d.name === 'Cypress_Diary_Test_日记'
|
||||
)
|
||||
expect(found).to.not.exist
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('UI: diary page renders', () => {
|
||||
it('visits /mydiary and verifies page renders', () => {
|
||||
cy.visit('/mydiary', {
|
||||
onBeforeLoad(win) {
|
||||
win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN)
|
||||
}
|
||||
})
|
||||
cy.get('.my-diary', { timeout: 10000 }).should('exist')
|
||||
// Should show diary sub-tabs
|
||||
cy.get('.sub-tab').should('have.length', 3)
|
||||
cy.contains('配方日记').should('be.visible')
|
||||
cy.contains('Brand').should('be.visible')
|
||||
cy.contains('Account').should('be.visible')
|
||||
})
|
||||
|
||||
it('diary grid is visible on diary tab', () => {
|
||||
cy.visit('/mydiary', {
|
||||
onBeforeLoad(win) {
|
||||
win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN)
|
||||
}
|
||||
})
|
||||
cy.get('.my-diary', { timeout: 10000 }).should('exist')
|
||||
// Diary grid or empty hint should be present
|
||||
cy.get('.diary-grid, .empty-hint').should('exist')
|
||||
})
|
||||
})
|
||||
|
||||
// Safety cleanup in case tests fail mid-way
|
||||
after(() => {
|
||||
cy.request({
|
||||
url: '/api/diary',
|
||||
headers: authHeaders,
|
||||
failOnStatusCode: false
|
||||
}).then(res => {
|
||||
if (res.status === 200 && Array.isArray(res.body)) {
|
||||
const testEntries = res.body.filter(d =>
|
||||
d.name && (d.name.includes('Cypress_Diary_Test') || d.name.includes('Cypress_Diary_Updated'))
|
||||
)
|
||||
testEntries.forEach(entry => {
|
||||
cy.request({
|
||||
method: 'DELETE',
|
||||
url: `/api/diary/${entry.id || entry._id}`,
|
||||
headers: authHeaders,
|
||||
failOnStatusCode: false
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user