Skip to content

Commit

Permalink
121 use cypress best practises (#130)
Browse files Browse the repository at this point in the history
- [x] do not use deleteTestUser at end of tests to clean up state (it
should also not use the ui with cy.)
- [x] use cy.task('db:reset') to clean up state before tests when
required
- [x] do not use createTestUser before tests to login (its using the UI
as well, which should not be)
- [x] use minimal login implementation as command (using fetch and
storing the response (access token) into localStorage

- known issue at Cypress
cypress-io/cypress#25397 (comment)
- workaround as descibred in the cypress comment. (triggering a fetch to
the spa, before all tests run)
  • Loading branch information
RobinMeow committed Apr 2, 2024
2 parents 0165a0d + 23c2e6e commit c00bb1a
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 193 deletions.
43 changes: 41 additions & 2 deletions client/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
/* 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({
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,
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')
Expand All @@ -27,6 +30,42 @@ export default defineConfig({
await client.close()
}
return Promise.resolve(null)
},
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 {
type Recipe = {
_id: string
title: string
createdAt: Date
__v: number
}

await client.connect()
const db = client.db('communitycookbook')
const collection = db.collection<Recipe>('recipes')
await collection.insertOne({
_id: id,
__v: 0,
createdAt: new Date(new Date().toISOString()),
title: title
})
} catch (error) {
console.error(
`Failed to see recipe with id '${id}' and title '${title}'.`,
error
)
} finally {
await client.close()
}
return Promise.resolve(null)
}
})
}
Expand Down
58 changes: 38 additions & 20 deletions client/cypress/e2e/auth/auth_corner_cy.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
describe('auth-corner should', () => {
beforeEach(() => {
cy.visit('/')
})
describe('auth-corner', () => {
describe('when authorized should', () => {
beforeEach(() => {
cy.login()
cy.visit('/')
})

it('navigate to login on login button click', () => {
cy.byTestAttr('login-button').click()
cy.url().should('not.include', 'register')
cy.url().should('include', 'login')
})
it('contain logout button', () => {
cy.byTestAttr('logout-button')
.should('be.visible')
.invoke('text')
.should('have.length.above', 1)
})

it('logout on logoutClick', () => {
cy.byTestAttr('logout-button').click()
cy.byTestAttr('login-button').should('be.visible')
})

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()
const hostUrl = Cypress.env()['baseUrl']
cy.url().should('equal', hostUrl + '/')
cy.byTestAttr('login-button').should('be.visible')
cy.byTestAttr('register-button').should('be.visible')
})
})

it('contain logout button', () => {
cy.createTestUser()
describe('when 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')
})
})
})
53 changes: 0 additions & 53 deletions client/cypress/e2e/auth/auth_cy.ts

This file was deleted.

4 changes: 1 addition & 3 deletions client/cypress/e2e/auth/delete_account_cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
})
40 changes: 28 additions & 12 deletions client/cypress/e2e/auth/login_cy.ts
Original file line number Diff line number Diff line change
@@ -1,16 +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('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.createTestUser()
cy.visit('/login')
cy.url().should('not.include', 'login')
cy.deleteTestUser()
describe('when authorized should', () => {
it('redirect', () => {
cy.login()
cy.visit('/login')
cy.url().should('not.include', 'login')
})
})
})
44 changes: 44 additions & 0 deletions client/cypress/e2e/auth/register_cy.ts
Original file line number Diff line number Diff line change
@@ -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')
})
})
})
12 changes: 0 additions & 12 deletions client/cypress/e2e/config.ts

This file was deleted.

4 changes: 1 addition & 3 deletions client/cypress/e2e/recipe/create_recipe_cy.ts
Original file line number Diff line number Diff line change
@@ -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')

Expand All @@ -20,7 +20,5 @@ describe('create-recipe should', () => {
const newRecipe: { id: string } = stuff.response?.body
cy.url().should('include', 'recipe/' + newRecipe.id)
})

cy.deleteTestUser()
})
})
31 changes: 7 additions & 24 deletions client/cypress/e2e/recipe/view_recipe_cy.ts
Original file line number Diff line number Diff line change
@@ -1,29 +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.createTestUser()
// 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)
})

cy.byTestAttr('title').contains(recipeTitle)

cy.deleteTestUser()
cy.task('db:seed:recipe', { id: recipeId, title: title })
cy.login()
cy.visit('/recipe/' + recipeId)
cy.url().should('include', 'recipe/' + recipeId)
cy.byTestAttr('title').contains(title)
})
})

0 comments on commit c00bb1a

Please sign in to comment.