describe('Favorites System', () => { const ADMIN_TOKEN = 'c86ae7afbe10fabe3c1d5e1a7fee74feaadfd5dc7be2ab62' describe('API Level', () => { let firstRecipeId before(() => { cy.request('/api/recipes').then(res => { firstRecipeId = res.body[0].id }) }) it('can add a favorite via API', () => { cy.request({ method: 'POST', url: `/api/favorites/${firstRecipeId}`, headers: { Authorization: `Bearer ${ADMIN_TOKEN}` }, body: {} }).then(res => { expect(res.status).to.be.oneOf([200, 201]) }) }) it('lists the favorite', () => { cy.request({ url: '/api/favorites', headers: { Authorization: `Bearer ${ADMIN_TOKEN}` } }).then(res => { expect(res.body).to.include(firstRecipeId) }) }) it('can remove the favorite via API', () => { cy.request({ method: 'DELETE', url: `/api/favorites/${firstRecipeId}`, headers: { Authorization: `Bearer ${ADMIN_TOKEN}` } }).then(res => { expect(res.status).to.eq(200) }) }) it('favorite is removed from list', () => { cy.request({ url: '/api/favorites', headers: { Authorization: `Bearer ${ADMIN_TOKEN}` } }).then(res => { expect(res.body).to.not.include(firstRecipeId) }) }) }) describe('UI Level', () => { it('recipe cards have star buttons for logged-in users', () => { cy.visit('/', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.recipe-card', { timeout: 10000 }).should('have.length.gte', 1) // Stars should be present on cards cy.get('.recipe-card').first().within(() => { cy.contains(/★|☆/).should('exist') }) }) it('clicking star toggles favorite state', () => { cy.visit('/', { onBeforeLoad(win) { win.localStorage.setItem('oil_auth_token', ADMIN_TOKEN) } }) cy.get('.recipe-card', { timeout: 10000 }).first().within(() => { cy.contains(/★|☆/).then($star => { const wasFav = $star.text().includes('★') $star.trigger('click') // Star text should have toggled cy.wait(500) cy.contains(/★|☆/).invoke('text').should(text => { if (wasFav) expect(text).to.include('☆') else expect(text).to.include('★') }) }) }) }) }) })