diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 0000000..0edaf5a --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,13 @@ +const { defineConfig } = require('cypress'); + +module.exports = defineConfig({ + e2e: { + baseUrl: 'http://localhost:80', + supportFile: 'cypress/support/e2e.js', + specPattern: 'cypress/e2e/**/*.cy.js', + + env: { + apiUrl: 'http://localhost:80/api', + }, + }, +}); diff --git a/cypress/e2e/sharedFlows/auth.cy.js b/cypress/e2e/sharedFlows/auth.cy.js new file mode 100644 index 0000000..997f5a4 --- /dev/null +++ b/cypress/e2e/sharedFlows/auth.cy.js @@ -0,0 +1,18 @@ +describe('Shared Auth Flow', () => { + it('logs in successfully', () => { + cy.request({ + method: 'POST', + url: '/api/auth/login', + body: { + email: 'weeramann@gmail.com', + password: 'Pass1234@', + }, + }).then((response) => { + expect(response.status).to.eq(200); + + expect(response.headers).to.have.property('x-request-id'); + + cy.captureRequestId(response); + }); + }); +}); diff --git a/cypress/e2e/sharedFlows/errorhandling.cy.js b/cypress/e2e/sharedFlows/errorhandling.cy.js new file mode 100644 index 0000000..4b5ece7 --- /dev/null +++ b/cypress/e2e/sharedFlows/errorhandling.cy.js @@ -0,0 +1,15 @@ +describe('Error Handling', () => { + it('returns request ID on failures', () => { + cy.request({ + method: 'GET', + url: '/api/auth/profile', + failOnStatusCode: false, + }).then(response => { + expect(response.status).to.eq(401); + + expect(response.headers).to.have.property('x-request-id'); + + cy.captureRequestId(response); + }); + }); +}); diff --git a/cypress/e2e/sharedFlows/mealplan.cy.js b/cypress/e2e/sharedFlows/mealplan.cy.js new file mode 100644 index 0000000..58c2757 --- /dev/null +++ b/cypress/e2e/sharedFlows/mealplan.cy.js @@ -0,0 +1,17 @@ +describe('Mealplan Flow', () => { + beforeEach(() => { + cy.login(); + }); + + it('fetches recommendations', () => { + cy.request({ + method: 'GET', + url: '/api/fooddata/mealplan', + headers: { + Authorization: `Bearer ${window.localStorage.getItem('token')}`, + }, + }).then((response) => { + cy.log(JSON.stringify(res.body)); + }); + }); +}); diff --git a/cypress/e2e/sharedFlows/preferences.cy.js b/cypress/e2e/sharedFlows/preferences.cy.js new file mode 100644 index 0000000..749b5a6 --- /dev/null +++ b/cypress/e2e/sharedFlows/preferences.cy.js @@ -0,0 +1,28 @@ +describe('Preferences Flow', () => { + beforeEach(() => { + cy.login(); + }); + + it('updates preferences safely', () => { + cy.request({ + method: 'POST', + url: '/api/user/preferences', + headers: { + Authorization: `Bearer ${window.localStorage.getItem('token')}`, + }, + body: { + dietary_requirements: [1, 2, 4], + allergies: [1], + cuisines: [2, 5], + dislikes: [4], + health_conditions: [], + spice_levels: [1, 2], + cooking_methods: [1, 4, 5], + }, + }).then((response) => { + expect(response.status).to.eq(200); + + cy.captureRequestId(response); + }); + }); +}); diff --git a/cypress/e2e/sharedFlows/session.cy.js b/cypress/e2e/sharedFlows/session.cy.js new file mode 100644 index 0000000..c67d31f --- /dev/null +++ b/cypress/e2e/sharedFlows/session.cy.js @@ -0,0 +1,19 @@ +describe('Protected Session Flow', () => { + beforeEach(() => { + cy.login(); + }); + + it('loads authenticated profile', () => { + cy.request({ + method: 'GET', + url: '/api/account', + headers: { + Authorization: `Bearer ${window.localStorage.getItem('token')}`, + }, + }).then((response) => { + expect(response.status).to.eq(200); + + cy.captureRequestId(response); + }); + }); +}); diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 0000000..7834f24 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,27 @@ +Cypress.Commands.add('login', () => { + cy.request({ + method: 'POST', + url: '/api/auth/login', + body: { + email: 'weeramann@gmail.com', + password: 'Pass1234@', + }, + }).then((response) => { + expect(response.status).to.eq(200); + + // Save token if backend returns one + const token = response.body?.token || response.body?.accessToken; + + if (token) { + window.localStorage.setItem('token', token); + } + + // Save request ID for tracing/debugging + const requestId = response.headers['x-request-id']; + + if (requestId) { + Cypress.env('requestId', requestId); + cy.log(`Request ID: ${requestId}`); + } + }); +}); diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js new file mode 100644 index 0000000..a5d6ba8 --- /dev/null +++ b/cypress/support/e2e.js @@ -0,0 +1,11 @@ +import './commands'; + +Cypress.Commands.add('captureRequestId', (response) => { + const requestId = response.headers['x-request-id']; + + if (requestId) { + cy.log(`Request ID: ${requestId}`); + + Cypress.env('lastRequestId', requestId); + } +}); diff --git a/cypress/support/failureReporter.js b/cypress/support/failureReporter.js new file mode 100644 index 0000000..afcac9a --- /dev/null +++ b/cypress/support/failureReporter.js @@ -0,0 +1,9 @@ +Cypress.on('fail', error => { + const requestId = Cypress.env('lastRequestId'); + + if (requestId) { + error.message += `\n\nRequest ID: ${requestId}`; + } + + throw error; +});