From 53535861c4ad292c9c9d6aeb85eeee735640d669 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:07:37 +0200 Subject: [PATCH 01/15] replace create and delete test user with login command --- client/cypress/e2e/auth/auth_corner_cy.ts | 57 ++++++++----- client/cypress/e2e/auth/delete_account_cy.ts | 4 +- client/cypress/e2e/auth/login_cy.ts | 3 +- client/cypress/e2e/config.ts | 2 + client/cypress/e2e/recipe/create_recipe_cy.ts | 4 +- client/cypress/e2e/recipe/view_recipe_cy.ts | 4 +- client/cypress/support/commands.ts | 80 ++++++++----------- server/Controllers/Auth/AuthController.cs | 4 +- 8 files changed, 79 insertions(+), 79 deletions(-) diff --git a/client/cypress/e2e/auth/auth_corner_cy.ts b/client/cypress/e2e/auth/auth_corner_cy.ts index e33b4aea..21722c9f 100644 --- a/client/cypress/e2e/auth/auth_corner_cy.ts +++ b/client/cypress/e2e/auth/auth_corner_cy.ts @@ -1,28 +1,45 @@ -describe('auth-corner should', () => { - beforeEach(() => { - cy.visit('/') - }) +import { config } from '../config' - it('navigate to login on login button click', () => { - cy.byTestAttr('login-button').click() - cy.url().should('not.include', 'register') - cy.url().should('include', 'login') - }) +describe('auth-corner', () => { + describe('wehn authorized should', () => { + beforeEach(() => { + cy.login() + cy.visit('/') + }) + + it('contain logout button', () => { + cy.login() + cy.visit('/') + + cy.byTestAttr('logout-button') + .should('be.visible') + .invoke('text') + .should('have.length.above', 1) + }) - it('navigate to register on register button click', () => { - cy.byTestAttr('register-button').click() - cy.url().should('not.include', 'login') - cy.url().should('include', 'register') + it('navigate to home on logout button click', () => { + cy.byTestAttr('logout-button').click() + cy.url().should('equal', config.hostUrl + '/') + cy.byTestAttr('login-button').should('be.visible') + cy.byTestAttr('register-button').should('be.visible') + }) }) - it('contain logout button', () => { - cy.createTestUser() + describe('wehn not authorized should', () => { + beforeEach(() => { + cy.visit('/') + }) - cy.byTestAttr('logout-button') - .should('be.visible') - .invoke('text') - .should('have.length.above', 1) + it('navigate to login on login button click', () => { + cy.byTestAttr('login-button').click() + cy.url().should('not.include', 'register') + cy.url().should('include', 'login') + }) - cy.deleteTestUser() + it('navigate to register on register button click', () => { + cy.byTestAttr('register-button').click() + cy.url().should('not.include', 'login') + cy.url().should('include', 'register') + }) }) }) diff --git a/client/cypress/e2e/auth/delete_account_cy.ts b/client/cypress/e2e/auth/delete_account_cy.ts index 2fa4a75f..c5de5f37 100644 --- a/client/cypress/e2e/auth/delete_account_cy.ts +++ b/client/cypress/e2e/auth/delete_account_cy.ts @@ -5,14 +5,12 @@ describe('delete-chef should', () => { }) it('validates incorrect password', () => { - cy.createTestUser() + cy.login() cy.visit('/delete-chef') cy.byTestAttr('password-input').type('wrong-password{enter}') cy.url().should('include', 'delete-chef') - - cy.deleteTestUser() }) }) diff --git a/client/cypress/e2e/auth/login_cy.ts b/client/cypress/e2e/auth/login_cy.ts index 8f2c9aae..d144c641 100644 --- a/client/cypress/e2e/auth/login_cy.ts +++ b/client/cypress/e2e/auth/login_cy.ts @@ -8,9 +8,8 @@ describe('login should', () => { describe('login redirects', () => { it('when logged in already', () => { - cy.createTestUser() + cy.login() cy.visit('/login') cy.url().should('not.include', 'login') - cy.deleteTestUser() }) }) diff --git a/client/cypress/e2e/config.ts b/client/cypress/e2e/config.ts index 90917635..40fcd20a 100644 --- a/client/cypress/e2e/config.ts +++ b/client/cypress/e2e/config.ts @@ -2,7 +2,9 @@ const apiBaseUrl = 'http://localhost:5126' const authUrl = apiBaseUrl + '/Auth' export const config = { + hostUrl: 'http://localhost:4200', apiUrls: { + base: apiBaseUrl, auth: { registerAsync: authUrl + '/RegisterAsync', loginAsync: authUrl + '/LoginAsync', diff --git a/client/cypress/e2e/recipe/create_recipe_cy.ts b/client/cypress/e2e/recipe/create_recipe_cy.ts index 4968d616..f68a74bf 100644 --- a/client/cypress/e2e/recipe/create_recipe_cy.ts +++ b/client/cypress/e2e/recipe/create_recipe_cy.ts @@ -1,7 +1,7 @@ describe('create-recipe should', () => { it(`redirect to recipe/{recipeId} recipe is created sucessfully`, () => { cy.task('db:reset') - cy.createTestUser() + cy.login() cy.visit('/create-recipe') @@ -20,7 +20,5 @@ describe('create-recipe should', () => { const newRecipe: { id: string } = stuff.response?.body cy.url().should('include', 'recipe/' + newRecipe.id) }) - - cy.deleteTestUser() }) }) diff --git a/client/cypress/e2e/recipe/view_recipe_cy.ts b/client/cypress/e2e/recipe/view_recipe_cy.ts index a2f04155..974a06fd 100644 --- a/client/cypress/e2e/recipe/view_recipe_cy.ts +++ b/client/cypress/e2e/recipe/view_recipe_cy.ts @@ -1,7 +1,7 @@ describe('view-recipe should', () => { it(`redirect to recipe/{recipeId} recipe is created sucessfully`, () => { cy.task('db:reset') - cy.createTestUser() + cy.login() // TODO this should use a seeded database cy.visit('/create-recipe') @@ -23,7 +23,5 @@ describe('view-recipe should', () => { }) cy.byTestAttr('title').contains(recipeTitle) - - cy.deleteTestUser() }) }) diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index 031f4f0d..ca81f149 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -59,8 +59,7 @@ declare namespace Cypress { | undefined ): Cypress.Chainable> - createTestUser(): void - deleteTestUser(): void + login(): void } } @@ -68,8 +67,6 @@ declare namespace Cypress { * @example * * Cy.getByDataCy('my-button').should.be('be.visivle'); - * - * @__PURE__ */ function byTestAttr( selector: string, @@ -87,51 +84,40 @@ function byTestAttr( Cypress.Commands.add('byTestAttr', byTestAttr) -function createTestUser() { +Cypress.Commands.add('login', () => { cy.fixture('test-user.json').as('user') cy.get('@user').then((user) => { const { chefname, password }: any = user + const token = window.localStorage.getItem('token') + if (!token) { + cy.request({ + method: 'POST', + url: 'http://localhost:5126/Auth/RegisterAsync', + body: { + name: chefname, + password: password + }, + failOnStatusCode: false + }).then((req) => { + if (!req.isOkStatusCode) { + if (req.body !== 'Chefname ist bereits vergeben.') { + throw new Error('Failed to login') + } + } + }) - const registerUrl = '/register' - - cy.location('pathname').then((currentPath) => { - if (currentPath !== registerUrl) { - cy.visit(registerUrl) - } - }) - - cy.byTestAttr('register-name-input').type(chefname) - cy.byTestAttr('password-input').type(password) - - cy.intercept({ - path: '/Auth/RegisterAsync', - times: 1 - }).as('registerAsync') - - cy.byTestAttr('register-form').submit() - - cy.wait('@registerAsync') - // I dont know why, but this is required, else the "login redirects when logged in already" - test fails - cy.url().should('not.include', 'register') - }) -} - -Cypress.Commands.add('createTestUser', createTestUser) - -function deleteTestUser() { - cy.fixture('test-user.json').as('user') - - cy.get('@user').then((user) => { - const { password }: any = user - cy.visit('/delete-chef') - cy.byTestAttr('password-input').type(password) - cy.intercept({ - path: '/Auth/DeleteAsync', - times: 1 - }).as('deleteAsync') - cy.byTestAttr('delete-chef-form').submit() - cy.wait('@deleteAsync') + cy.request({ + method: 'POST', + url: 'http://localhost:5126/Auth/LoginAsync', + body: { + name: chefname, + password: password + } + }).then((response) => { + const token = response.body + window.localStorage.setItem('token', token) + return + }) + } }) -} - -Cypress.Commands.add('deleteTestUser', deleteTestUser) +}) diff --git a/server/Controllers/Auth/AuthController.cs b/server/Controllers/Auth/AuthController.cs index fb0cf706..6f7c338a 100644 --- a/server/Controllers/Auth/AuthController.cs +++ b/server/Controllers/Auth/AuthController.cs @@ -25,7 +25,9 @@ IJwtFactory jwtFactory /// [HttpPost(nameof(RegisterAsync))] [ProducesResponseType(StatusCodes.Status201Created)] - public async Task, BadRequest, BadRequest, StatusCodeHttpResult>> RegisterAsync([Required] RegisterChefDto newChef, CancellationToken cancellationToken = default) + public async Task, BadRequest, BadRequest, StatusCodeHttpResult>> RegisterAsync( + [Required] RegisterChefDto newChef, + CancellationToken cancellationToken = default) { string chefname = newChef.Name; From c0fe3af309774d447c3334c8bb8169cd5f23607b Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:12:54 +0200 Subject: [PATCH 02/15] comment on login method --- client/cypress/support/commands.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index ca81f149..a6bcbeef 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -59,6 +59,11 @@ declare namespace Cypress { | undefined ): Cypress.Chainable> + /** + * looks for en existing login token. + * If none found, tries to register (+login). + * If the Chefname already exists will do login with those creds. + */ login(): void } } @@ -99,10 +104,11 @@ Cypress.Commands.add('login', () => { }, failOnStatusCode: false }).then((req) => { - if (!req.isOkStatusCode) { - if (req.body !== 'Chefname ist bereits vergeben.') { - throw new Error('Failed to login') - } + if ( + !req.isOkStatusCode && + req.body !== 'Chefname ist bereits vergeben.' + ) { + throw new Error('Failed to login') } }) From 239b34bc105a7a570342c70bb076cd94efaf0917 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:50:05 +0200 Subject: [PATCH 03/15] seed recipe added and view recipe e2e test adjusted --- client/cypress.config.ts | 27 +++++++++++++++++++++ client/cypress/e2e/recipe/view_recipe_cy.ts | 24 +++--------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/client/cypress.config.ts b/client/cypress.config.ts index 6775376a..cb0324ff 100644 --- a/client/cypress.config.ts +++ b/client/cypress.config.ts @@ -27,6 +27,33 @@ export default defineConfig({ await client.close() } return Promise.resolve(null) + }, + async 'db:seed:recipe'() { + const client = new MongoClient('mongodb://127.0.0.1:27017') + + try { + type Recipe = { + _id: string + title: string + createdAt: Date + __v: number + } + + await client.connect() + const db = client.db('communitycookbook') + const collection = db.collection('recipes') + await collection.insertOne({ + _id: '2302dbb0-5269-4839-8bfa-b39e8c0b4821', + __v: 0, + createdAt: new Date(new Date().toISOString()), + title: 'Cypress Recipe' + }) + } catch (error) { + console.error('Error creating recipe database:', error) + } finally { + await client.close() + } + return Promise.resolve(null) } }) } diff --git a/client/cypress/e2e/recipe/view_recipe_cy.ts b/client/cypress/e2e/recipe/view_recipe_cy.ts index 974a06fd..67b4c277 100644 --- a/client/cypress/e2e/recipe/view_recipe_cy.ts +++ b/client/cypress/e2e/recipe/view_recipe_cy.ts @@ -1,27 +1,11 @@ describe('view-recipe should', () => { it(`redirect to recipe/{recipeId} recipe is created sucessfully`, () => { cy.task('db:reset') + cy.task('db:seed:recipe') cy.login() - // TODO this should use a seeded database - - cy.visit('/create-recipe') - - const recipeTitle = 'valid recipe title' - - cy.byTestAttr('recipe-title-input').type(recipeTitle) - - cy.intercept({ - path: '/Recipe/AddAsync', - times: 1 - }).as('create-recipe') - - cy.byTestAttr('create-recipe-submit-button').click() - - cy.wait('@create-recipe').then((stuff) => { - const newRecipe: { id: string } = stuff.response?.body - cy.url().should('include', 'recipe/' + newRecipe.id) - }) - + const recipeTitle = 'Cypress Recipe' + cy.visit('/recipe/2302dbb0-5269-4839-8bfa-b39e8c0b4821') + cy.url().should('include', 'recipe/2302dbb0-5269-4839-8bfa-b39e8c0b4821') cy.byTestAttr('title').contains(recipeTitle) }) }) From c465629d9806f4f190bf18c929b6621efda0c8d5 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:48:19 +0200 Subject: [PATCH 04/15] paramaterized seeding recipe task --- client/cypress.config.ts | 13 +++-- client/cypress/e2e/auth/auth_corner_cy.ts | 5 +- client/cypress/e2e/config.ts | 14 ----- client/cypress/e2e/recipe/view_recipe_cy.ts | 11 ++-- client/cypress/support/commands.ts | 60 ++++++++++----------- 5 files changed, 47 insertions(+), 56 deletions(-) delete mode 100644 client/cypress/e2e/config.ts diff --git a/client/cypress.config.ts b/client/cypress.config.ts index cb0324ff..218fb28f 100644 --- a/client/cypress.config.ts +++ b/client/cypress.config.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { defineConfig } from 'cypress' import { MongoClient } from 'mongodb' +import { assert } from './src/app/common/assertions/assert' export default defineConfig({ e2e: { @@ -28,7 +29,13 @@ export default defineConfig({ } return Promise.resolve(null) }, - async 'db:seed:recipe'() { + async 'db:seed:recipe'({ id, title }) { + assert(id && typeof id === 'string', 'Id required to create recipe.') + assert( + title && typeof title === 'string', + 'Title required to create recipe.' + ) + const client = new MongoClient('mongodb://127.0.0.1:27017') try { @@ -43,10 +50,10 @@ export default defineConfig({ const db = client.db('communitycookbook') const collection = db.collection('recipes') await collection.insertOne({ - _id: '2302dbb0-5269-4839-8bfa-b39e8c0b4821', + _id: id, __v: 0, createdAt: new Date(new Date().toISOString()), - title: 'Cypress Recipe' + title: title }) } catch (error) { console.error('Error creating recipe database:', error) diff --git a/client/cypress/e2e/auth/auth_corner_cy.ts b/client/cypress/e2e/auth/auth_corner_cy.ts index 21722c9f..e1ea566e 100644 --- a/client/cypress/e2e/auth/auth_corner_cy.ts +++ b/client/cypress/e2e/auth/auth_corner_cy.ts @@ -1,5 +1,3 @@ -import { config } from '../config' - describe('auth-corner', () => { describe('wehn authorized should', () => { beforeEach(() => { @@ -19,7 +17,8 @@ describe('auth-corner', () => { it('navigate to home on logout button click', () => { cy.byTestAttr('logout-button').click() - cy.url().should('equal', config.hostUrl + '/') + const hostUrl = Cypress.env()['baseUrl'] + cy.url().should('equal', hostUrl + '/') cy.byTestAttr('login-button').should('be.visible') cy.byTestAttr('register-button').should('be.visible') }) diff --git a/client/cypress/e2e/config.ts b/client/cypress/e2e/config.ts deleted file mode 100644 index 40fcd20a..00000000 --- a/client/cypress/e2e/config.ts +++ /dev/null @@ -1,14 +0,0 @@ -const apiBaseUrl = 'http://localhost:5126' -const authUrl = apiBaseUrl + '/Auth' - -export const config = { - hostUrl: 'http://localhost:4200', - apiUrls: { - base: apiBaseUrl, - auth: { - registerAsync: authUrl + '/RegisterAsync', - loginAsync: authUrl + '/LoginAsync', - deleteAsync: authUrl + '/DeleteAsync' - } - } -} diff --git a/client/cypress/e2e/recipe/view_recipe_cy.ts b/client/cypress/e2e/recipe/view_recipe_cy.ts index 67b4c277..73ca78e0 100644 --- a/client/cypress/e2e/recipe/view_recipe_cy.ts +++ b/client/cypress/e2e/recipe/view_recipe_cy.ts @@ -1,11 +1,12 @@ describe('view-recipe should', () => { it(`redirect to recipe/{recipeId} recipe is created sucessfully`, () => { + const recipeId = crypto.randomUUID().toString() + const title = 'Cypress Recipe' cy.task('db:reset') - cy.task('db:seed:recipe') + cy.task('db:seed:recipe', { id: recipeId, title: title }) cy.login() - const recipeTitle = 'Cypress Recipe' - cy.visit('/recipe/2302dbb0-5269-4839-8bfa-b39e8c0b4821') - cy.url().should('include', 'recipe/2302dbb0-5269-4839-8bfa-b39e8c0b4821') - cy.byTestAttr('title').contains(recipeTitle) + cy.visit('/recipe/' + recipeId) + cy.url().should('include', 'recipe/' + recipeId) + cy.byTestAttr('title').contains(title) }) }) diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index a6bcbeef..f539e821 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -93,37 +93,35 @@ Cypress.Commands.add('login', () => { cy.fixture('test-user.json').as('user') cy.get('@user').then((user) => { const { chefname, password }: any = user - const token = window.localStorage.getItem('token') - if (!token) { - cy.request({ - method: 'POST', - url: 'http://localhost:5126/Auth/RegisterAsync', - body: { - name: chefname, - password: password - }, - failOnStatusCode: false - }).then((req) => { - if ( - !req.isOkStatusCode && - req.body !== 'Chefname ist bereits vergeben.' - ) { - throw new Error('Failed to login') - } - }) + cy.request({ + method: 'POST', + url: 'http://localhost:5126/Auth/RegisterAsync', + body: { + name: chefname, + password: password + }, + failOnStatusCode: false + }).then((req) => { + if ( + !req.isOkStatusCode && + req.body !== 'Chefname ist bereits vergeben.' + ) { + throw new Error('Failed to login') + } + }) - cy.request({ - method: 'POST', - url: 'http://localhost:5126/Auth/LoginAsync', - body: { - name: chefname, - password: password - } - }).then((response) => { - const token = response.body - window.localStorage.setItem('token', token) - return - }) - } + cy.request({ + method: 'POST', + url: 'http://localhost:5126/Auth/LoginAsync', + body: { + name: chefname, + password: password + } + }).then((response) => { + const token = response.body + window.localStorage.setItem('token', token) + return + }) + return }) }) From d0e1e47592355177c6e593a6fcb59859dba13979 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:12:39 +0200 Subject: [PATCH 05/15] use fetch instead of cy.Request because Cypress **** --- client/cypress/support/commands.ts | 42 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index f539e821..1efaee47 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -64,7 +64,7 @@ declare namespace Cypress { * If none found, tries to register (+login). * If the Chefname already exists will do login with those creds. */ - login(): void + login(): Promise } } @@ -91,37 +91,39 @@ Cypress.Commands.add('byTestAttr', byTestAttr) Cypress.Commands.add('login', () => { cy.fixture('test-user.json').as('user') - cy.get('@user').then((user) => { + cy.get('@user').then(async (user) => { const { chefname, password }: any = user - cy.request({ + + await fetch('http://localhost:5126/Auth/RegisterAsync', { method: 'POST', - url: 'http://localhost:5126/Auth/RegisterAsync', - body: { + body: JSON.stringify({ name: chefname, password: password - }, - failOnStatusCode: false - }).then((req) => { - if ( - !req.isOkStatusCode && - req.body !== 'Chefname ist bereits vergeben.' - ) { + }), + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json' + } + }).catch((response) => { + if (response.body !== 'Chefname ist bereits vergeben.') { throw new Error('Failed to login') } + cy.log('Testuser already created.') }) - cy.request({ + const loginResponse = await fetch('http://localhost:5126/Auth/LoginAsync', { method: 'POST', - url: 'http://localhost:5126/Auth/LoginAsync', - body: { + body: JSON.stringify({ name: chefname, password: password + }), + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json' } - }).then((response) => { - const token = response.body - window.localStorage.setItem('token', token) - return }) - return + + const token = await loginResponse.json() + window.localStorage.setItem('token', token) }) }) From d0754333d7c2029d02569bb44afe67acf2472ee2 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:17:21 +0200 Subject: [PATCH 06/15] set baseUrl after login cuz it seems like it got lost --- client/cypress/support/commands.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index 1efaee47..d30d9458 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -125,5 +125,6 @@ Cypress.Commands.add('login', () => { const token = await loginResponse.json() window.localStorage.setItem('token', token) + Cypress.env({ baseUrl: 'http://localhost:4200' }) }) }) From 4a3a30266853038222230e36c7a77f5ad304942d Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:34:08 +0200 Subject: [PATCH 07/15] use env with base und apiBaseUrl --- client/cypress.config.ts | 5 ++++- client/cypress/support/commands.ts | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/client/cypress.config.ts b/client/cypress.config.ts index 218fb28f..60fa464a 100644 --- a/client/cypress.config.ts +++ b/client/cypress.config.ts @@ -4,10 +4,13 @@ import { MongoClient } from 'mongodb' import { assert } from './src/app/common/assertions/assert' export default defineConfig({ + env: { + apiBaseUrl: 'http://localhost:5126', + baseUrl: 'http://localhost:4200' + }, e2e: { baseUrl: 'http://localhost:4200', experimentalRunAllSpecs: true, - experimentalInteractiveRunEvents: true, specPattern: 'cypress/e2e/**/*_{cy,spec}.{js,jsx,ts,tsx}', async setupNodeEvents( on: Cypress.PluginEvents, diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index d30d9458..19cd53a8 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -90,11 +90,14 @@ function byTestAttr( Cypress.Commands.add('byTestAttr', byTestAttr) Cypress.Commands.add('login', () => { + const apiBaseUrl = Cypress.env('apiBaseUrl') + cy.fixture('test-user.json').as('user') + cy.get('@user').then(async (user) => { const { chefname, password }: any = user - await fetch('http://localhost:5126/Auth/RegisterAsync', { + await fetch(apiBaseUrl + '/Auth/RegisterAsync', { method: 'POST', body: JSON.stringify({ name: chefname, @@ -111,7 +114,7 @@ Cypress.Commands.add('login', () => { cy.log('Testuser already created.') }) - const loginResponse = await fetch('http://localhost:5126/Auth/LoginAsync', { + const loginResponse = await fetch(apiBaseUrl + '/Auth/LoginAsync', { method: 'POST', body: JSON.stringify({ name: chefname, @@ -125,6 +128,5 @@ Cypress.Commands.add('login', () => { const token = await loginResponse.json() window.localStorage.setItem('token', token) - Cypress.env({ baseUrl: 'http://localhost:4200' }) }) }) From beb3236f08aed3a0a481e15d05d98da85a1f10bb Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:36:45 +0200 Subject: [PATCH 08/15] improve error log for failed recipe seeding --- client/cypress.config.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/cypress.config.ts b/client/cypress.config.ts index 60fa464a..d3e72559 100644 --- a/client/cypress.config.ts +++ b/client/cypress.config.ts @@ -59,7 +59,10 @@ export default defineConfig({ title: title }) } catch (error) { - console.error('Error creating recipe database:', error) + console.error( + `Failed to see recipe with id '${id}' and title '${title}'.`, + error + ) } finally { await client.close() } From 1d3d55b7131bb7d7ef5a5d8adb2029e70b12d083 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Mon, 1 Apr 2024 23:46:48 +0200 Subject: [PATCH 09/15] maybe fetching the spa in a beforeEach hook can be the fix to this .. tho slow --- client/cypress/e2e/auth/auth_corner_cy.ts | 3 --- client/cypress/e2e/auth/auth_cy.ts | 24 +---------------------- client/cypress/support/e2e.ts | 6 ++++++ 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/client/cypress/e2e/auth/auth_corner_cy.ts b/client/cypress/e2e/auth/auth_corner_cy.ts index e1ea566e..f67e5994 100644 --- a/client/cypress/e2e/auth/auth_corner_cy.ts +++ b/client/cypress/e2e/auth/auth_corner_cy.ts @@ -6,9 +6,6 @@ describe('auth-corner', () => { }) it('contain logout button', () => { - cy.login() - cy.visit('/') - cy.byTestAttr('logout-button') .should('be.visible') .invoke('text') diff --git a/client/cypress/e2e/auth/auth_cy.ts b/client/cypress/e2e/auth/auth_cy.ts index 1589c6d0..b101734d 100644 --- a/client/cypress/e2e/auth/auth_cy.ts +++ b/client/cypress/e2e/auth/auth_cy.ts @@ -1,6 +1,7 @@ describe('auth should', () => { it('register > logout > login and delete', () => { // Register + cy.task('db:reset') cy.visit('/register') const credentials = { chefname: 'Cypress-Register', @@ -26,28 +27,5 @@ describe('auth should', () => { .as('authc') .byTestAttr('logout-button') .contains('Ausloggen') - - // Logout - cy.get('@authc').byTestAttr('logout-button').click() - - // Login - cy.visit('/login') - cy.byTestAttr('login-name-input').type(credentials.chefname) - cy.byTestAttr('password-input').type(credentials.password) - cy.byTestAttr('login-submit-button').click() // should redirect somewhere in success - cy.url().should('not.include', 'login') - cy.get('@authc').byTestAttr('logout-button').contains('Ausloggen') - - // Delete the just registered account - cy.visit('/delete-chef') - cy.byTestAttr('password-input').type(credentials.password) - cy.intercept({ - path: '/Auth/DeleteAsync', - times: 1 - }).as('delete-http-request') - cy.byTestAttr('delete-chef-form').submit() // redirect on success - cy.wait('@delete-http-request') - cy.url().should('not.include', 'delete-chef') - cy.get('@authc').byTestAttr('login-button') }) }) diff --git a/client/cypress/support/e2e.ts b/client/cypress/support/e2e.ts index d357fd65..5b750b44 100644 --- a/client/cypress/support/e2e.ts +++ b/client/cypress/support/e2e.ts @@ -19,3 +19,9 @@ import './commands' // beforeEach(function () { // code that runs before every spec tests // }) +beforeEach('fetching SPA', async () => { + // make sure, the first http request goes onto the SPA + await fetch(Cypress.env('baseUrl'), { + method: 'GET' + }) +}) From e37b3b332dcc011196e78c53e5da688f18fb344a Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:49:10 +0200 Subject: [PATCH 10/15] WOAH --- client/cypress/support/commands.ts | 27 +++++++++++++++-------- client/cypress/support/e2e.ts | 35 +++++++++--------------------- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index 19cd53a8..4485136e 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -97,25 +97,34 @@ Cypress.Commands.add('login', () => { cy.get('@user').then(async (user) => { const { chefname, password }: any = user - await fetch(apiBaseUrl + '/Auth/RegisterAsync', { + cy.request({ method: 'POST', + url: apiBaseUrl + '/Auth/RegisterAsync', body: JSON.stringify({ name: chefname, password: password }), + failOnStatusCode: false, headers: { 'Content-Type': 'application/json', Accept: 'application/json' } - }).catch((response) => { - if (response.body !== 'Chefname ist bereits vergeben.') { - throw new Error('Failed to login') + }).then((response) => { + if (!response.isOkStatusCode) { + if ( + response.status !== 400 && + response.body !== 'Chefname ist bereits vergeben.' + ) { + throw new Error('Failed to login') + } + + cy.log('Testuser already created.') } - cy.log('Testuser already created.') }) - const loginResponse = await fetch(apiBaseUrl + '/Auth/LoginAsync', { + cy.request({ method: 'POST', + url: apiBaseUrl + '/Auth/LoginAsync', body: JSON.stringify({ name: chefname, password: password @@ -124,9 +133,9 @@ Cypress.Commands.add('login', () => { 'Content-Type': 'application/json', Accept: 'application/json' } + }).then((response) => { + const token = response.body + window.localStorage.setItem('token', token) }) - - const token = await loginResponse.json() - window.localStorage.setItem('token', token) }) }) diff --git a/client/cypress/support/e2e.ts b/client/cypress/support/e2e.ts index 5b750b44..6cfaed66 100644 --- a/client/cypress/support/e2e.ts +++ b/client/cypress/support/e2e.ts @@ -1,27 +1,12 @@ -// *********************************************************** -// This example support/e2e.ts is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// When a command from ./commands is ready to use, import with `import './commands'` syntax import './commands' -// beforeEach(function () { -// code that runs before every spec tests -// }) -beforeEach('fetching SPA', async () => { - // make sure, the first http request goes onto the SPA - await fetch(Cypress.env('baseUrl'), { - method: 'GET' - }) -}) +// this looks hacky and wierd af! +// and it sure is! +// to spare you the time of reading the known bug (https://github.com/cypress-io/cypress/issues/25397) +// which wont get fixed. +// here the short explanation: +// after using cy.request() for login, cy.visit() does not function anymore as intended. +// thise is a workaround to make, sure cy.visit() does function as intended. +fetch(Cypress.env('baseUrl'), { + method: 'GET' +}) // fire and forget From c712a2bc5c18589794787c6f972ca04745c6ccf0 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Tue, 2 Apr 2024 18:11:29 +0200 Subject: [PATCH 11/15] remove definetly not chatgpt comment --- client/cypress.config.ts | 1 - client/cypress/support/e2e.ts | 15 +++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/client/cypress.config.ts b/client/cypress.config.ts index d3e72559..7496fa9a 100644 --- a/client/cypress.config.ts +++ b/client/cypress.config.ts @@ -16,7 +16,6 @@ export default defineConfig({ on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions ) { - // Replace with your actual MongoDB connection details on('task', { async 'db:reset'() { const client = new MongoClient('mongodb://127.0.0.1:27017') diff --git a/client/cypress/support/e2e.ts b/client/cypress/support/e2e.ts index 6cfaed66..ac9a00f1 100644 --- a/client/cypress/support/e2e.ts +++ b/client/cypress/support/e2e.ts @@ -1,12 +1,11 @@ import './commands' -// this looks hacky and wierd af! -// and it sure is! -// to spare you the time of reading the known bug (https://github.com/cypress-io/cypress/issues/25397) -// which wont get fixed. +// this looks hacky and wierd af! ... and it sure is! +// +// known bug since 2023 January (https://github.com/cypress-io/cypress/issues/25397) +// small reproduction done by some kind person: https://github.com/ZachJW34/cypress-request-visit-bug +// // here the short explanation: // after using cy.request() for login, cy.visit() does not function anymore as intended. -// thise is a workaround to make, sure cy.visit() does function as intended. -fetch(Cypress.env('baseUrl'), { - method: 'GET' -}) // fire and forget +// this is a workaround to make, sure cy.visit() does function as intended. +fetch(Cypress.env('baseUrl'), { method: 'GET' }) // fire and forget From 21693e1b479d3abb9f25c1b5c627468600d90317 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Tue, 2 Apr 2024 18:19:52 +0200 Subject: [PATCH 12/15] rewrite login tests --- client/cypress/e2e/auth/login_cy.ts | 37 +++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/client/cypress/e2e/auth/login_cy.ts b/client/cypress/e2e/auth/login_cy.ts index d144c641..aaba530d 100644 --- a/client/cypress/e2e/auth/login_cy.ts +++ b/client/cypress/e2e/auth/login_cy.ts @@ -1,15 +1,32 @@ describe('login should', () => { - it('not log in with empty inputs', () => { - cy.visit('/login') - cy.byTestAttr('login-submit-button').click({ force: true }) - cy.url().should('include', 'login') + describe('when unauthorized should', () => { + beforeEach(() => { + cy.task('db:reset') + cy.visit('/login') + }) + + it('not log in with empty inputs', () => { + cy.byTestAttr('login-submit-button').click({ force: true }) + cy.url().should('include', 'login') + }) + + it('work with enter', () => { + cy.byTestAttr('login-name-input').type('Cypress Testuser') + cy.byTestAttr('password-input').type('iLoveJesus<3!{enter}') + }) + + it('work with submit button', () => { + cy.byTestAttr('login-name-input').type('Cypress Testuser') + cy.byTestAttr('password-input').type('iLoveJesus<3!{enter}') + cy.byTestAttr('login-submit-button').click() + }) }) -}) -describe('login redirects', () => { - it('when logged in already', () => { - cy.login() - cy.visit('/login') - cy.url().should('not.include', 'login') + describe('when authorized should', () => { + it('redirect', () => { + cy.login() + cy.visit('/login') + cy.url().should('not.include', 'login') + }) }) }) From 469511e94fc5b79c1e1beae88981899c203e572a Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Tue, 2 Apr 2024 18:20:02 +0200 Subject: [PATCH 13/15] grammar --- client/cypress/e2e/auth/auth_corner_cy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cypress/e2e/auth/auth_corner_cy.ts b/client/cypress/e2e/auth/auth_corner_cy.ts index f67e5994..745f46c4 100644 --- a/client/cypress/e2e/auth/auth_corner_cy.ts +++ b/client/cypress/e2e/auth/auth_corner_cy.ts @@ -1,5 +1,5 @@ describe('auth-corner', () => { - describe('wehn authorized should', () => { + describe('when authorized should', () => { beforeEach(() => { cy.login() cy.visit('/') @@ -21,7 +21,7 @@ describe('auth-corner', () => { }) }) - describe('wehn not authorized should', () => { + describe('when not authorized should', () => { beforeEach(() => { cy.visit('/') }) From e11b3ab414e36d65d8f6d067a95bc8c8523a83f3 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Tue, 2 Apr 2024 18:26:49 +0200 Subject: [PATCH 14/15] add register specific specs --- client/cypress/e2e/auth/login_cy.ts | 2 +- client/cypress/e2e/auth/register_cy.ts | 44 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 client/cypress/e2e/auth/register_cy.ts diff --git a/client/cypress/e2e/auth/login_cy.ts b/client/cypress/e2e/auth/login_cy.ts index aaba530d..3bc29357 100644 --- a/client/cypress/e2e/auth/login_cy.ts +++ b/client/cypress/e2e/auth/login_cy.ts @@ -1,4 +1,4 @@ -describe('login should', () => { +describe('login', () => { describe('when unauthorized should', () => { beforeEach(() => { cy.task('db:reset') diff --git a/client/cypress/e2e/auth/register_cy.ts b/client/cypress/e2e/auth/register_cy.ts new file mode 100644 index 00000000..cb4af4a5 --- /dev/null +++ b/client/cypress/e2e/auth/register_cy.ts @@ -0,0 +1,44 @@ +describe('register', () => { + describe('when unauthorized should', () => { + beforeEach(() => { + cy.task('db:reset') + cy.visit('/register') + }) + + it('not register with empty inputs', () => { + cy.intercept( + Cypress.env('apiBaseUrl') + '/Auth/RegisterAsync', + cy.spy().as('register') + ) + cy.byTestAttr('register-submit-button').click({ force: true }) + cy.url().should('include', 'register') + cy.get('@register').should('not.have.been.called') + }) + + it('register with enter', () => { + cy.byTestAttr('register-name-input').type('Cypress Testuser') + cy.byTestAttr('password-input').type('iLoveJesus<3!{enter}') + }) + + it('register with submit button', () => { + cy.byTestAttr('register-name-input').type('Cypress Testuser') + cy.byTestAttr('password-input').type('iLoveJesus<3!{enter}') + cy.byTestAttr('register-submit-button').click() + }) + + it('register with email', () => { + cy.byTestAttr('register-name-input').type('Cypress Testuser') + cy.byTestAttr('email-input').type('be.blessed@nd.loved') + cy.byTestAttr('password-input').type('iLoveJesus<3!{enter}') + cy.byTestAttr('register-submit-button').click() + }) + }) + + describe('when authorized should', () => { + it('redirect', () => { + cy.login() + cy.visit('/register') + cy.url().should('not.include', 'register') + }) + }) +}) From 23c2e6e3581f26f0b1f09ad156cb5daf7f920021 Mon Sep 17 00:00:00 2001 From: "robinmeow97@gmail.com" <111490292+RobinMeow@users.noreply.github.com> Date: Tue, 2 Apr 2024 18:30:56 +0200 Subject: [PATCH 15/15] remove auth spec file and add logout test to auth corner --- client/cypress/e2e/auth/auth_corner_cy.ts | 5 ++++ client/cypress/e2e/auth/auth_cy.ts | 31 ----------------------- 2 files changed, 5 insertions(+), 31 deletions(-) delete mode 100644 client/cypress/e2e/auth/auth_cy.ts diff --git a/client/cypress/e2e/auth/auth_corner_cy.ts b/client/cypress/e2e/auth/auth_corner_cy.ts index 745f46c4..c413f1f4 100644 --- a/client/cypress/e2e/auth/auth_corner_cy.ts +++ b/client/cypress/e2e/auth/auth_corner_cy.ts @@ -12,6 +12,11 @@ describe('auth-corner', () => { .should('have.length.above', 1) }) + it('logout on logoutClick', () => { + cy.byTestAttr('logout-button').click() + cy.byTestAttr('login-button').should('be.visible') + }) + it('navigate to home on logout button click', () => { cy.byTestAttr('logout-button').click() const hostUrl = Cypress.env()['baseUrl'] diff --git a/client/cypress/e2e/auth/auth_cy.ts b/client/cypress/e2e/auth/auth_cy.ts deleted file mode 100644 index b101734d..00000000 --- a/client/cypress/e2e/auth/auth_cy.ts +++ /dev/null @@ -1,31 +0,0 @@ -describe('auth should', () => { - it('register > logout > login and delete', () => { - // Register - cy.task('db:reset') - cy.visit('/register') - const credentials = { - chefname: 'Cypress-Register', - password: 'iLoveJesus<3' - } - cy.byTestAttr('register-submit-button').as('submit-btn') - - cy.get('@submit-btn').should('be.disabled') - - cy.byTestAttr('register-name-input').type(credentials.chefname) - cy.byTestAttr('password-input').type(credentials.password) - - cy.get('@submit-btn').should('be.enabled') - cy.intercept({ - path: '/Auth/RegisterAsync', - times: 1 - }).as('register-http-request') - cy.get('@submit-btn').click() // should redirect somewhere in success - cy.wait('@register-http-request') - - cy.url().should('not.include', 'register') - cy.get('auth-corner') - .as('authc') - .byTestAttr('logout-button') - .contains('Ausloggen') - }) -})