const authenticatedUser = { id: 7, email: 'user@example.com', } function interceptLogoutFlow(): void { let authenticated = true cy.intercept('GET', '**/api/me', (request) => { if (authenticated) { request.alias = 'me' request.reply({ statusCode: 200, body: { user: authenticatedUser }, }) return } request.alias = 'loggedOutMe' request.reply({ statusCode: 401, body: { error: 'unauthenticated' }, }) }) cy.intercept('POST', '**/api/logout', (request) => { expect(request.headers.accept).to.equal('application/json') authenticated = false request.reply({ statusCode: 204 }) }).as('logout') } function visitDashboardAndLogout(): void { cy.visit('/dashboard') cy.wait('@me') cy.contains('button', 'Log out').click() cy.wait('@logout') cy.wait('@loggedOutMe') } describe('session authentication', () => { beforeEach(() => { cy.intercept('GET', '**/api/sets', { statusCode: 200, body: { sets: [] }, }) cy.intercept('GET', '**/api/schedules', { statusCode: 200, body: { schedules: [] }, }) }) it('restores an authenticated session on a protected route', () => { cy.intercept('GET', '**/api/me', { statusCode: 200, body: { user: authenticatedUser }, }).as('me') cy.visit('/dashboard') cy.wait('@me') cy.location('pathname').should('equal', '/dashboard') cy.get('h1').should('have.text', 'Available sets') }) it('redirects an unauthenticated protected route to login', () => { cy.intercept('GET', '**/api/me', { statusCode: 401, body: { error: 'unauthenticated' }, }).as('me') cy.visit('/dashboard') cy.wait('@me') cy.location('pathname').should('equal', '/login') cy.location('search').should('include', 'redirect=/dashboard') }) it('redirects a restored session away from a guest-only route', () => { cy.intercept('GET', '**/api/me', { statusCode: 200, body: { user: authenticatedUser }, }).as('me') cy.visit('/login') cy.wait('@me') cy.location('pathname').should('equal', '/dashboard') }) it('redirects a restored session away from the home route', () => { cy.intercept('GET', '**/api/me', { statusCode: 200, body: { user: authenticatedUser }, }).as('me') cy.visit('/') cy.wait('@me') cy.location('pathname').should('equal', '/dashboard') }) it('rejects a malformed authenticated-user response', () => { cy.intercept('GET', '**/api/me', { statusCode: 200, body: { user: { id: 7 } }, }).as('me') cy.visit('/dashboard') cy.wait('@me') cy.location('pathname').should('equal', '/login') }) it('logs out and redirects to login', () => { interceptLogoutFlow() visitDashboardAndLogout() cy.location('pathname').should('equal', '/login') }) it('keeps protected routes inaccessible after logout', () => { interceptLogoutFlow() visitDashboardAndLogout() cy.visit('/dashboard') cy.wait('@loggedOutMe') cy.location('pathname').should('equal', '/login') cy.location('search').should('include', 'redirect=/dashboard') }) })