Skip to content

Commit

Permalink
test(e2e): refactor and add support for SPA app
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr committed Oct 19, 2021
1 parent a69dfd7 commit 7609219
Show file tree
Hide file tree
Showing 21 changed files with 227 additions and 134 deletions.
6 changes: 6 additions & 0 deletions test/e2e/cypress/helpers/express.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {APP_URL} from "./index";

export const routes = {
base: APP_URL,
login: APP_URL + '/login'
}
6 changes: 6 additions & 0 deletions test/e2e/cypress/helpers/react.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {SPA_URL} from "./index";

export const routes = {
base: SPA_URL,
login: SPA_URL + '/login'
}
33 changes: 22 additions & 11 deletions test/e2e/cypress/integration/profiles/email/error/ui.spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import { APP_URL } from '../../../../helpers'
import {routes as express} from "../../../../helpers/express";
import {routes as react} from "../../../../helpers/react";

context('Email Profile', () => {
describe('Self-Service Error UI', () => {
before(() => {
cy.useConfigProfile('email')
})
describe('Handling self-service error flows', () => {
[{
route: express.base,
app: 'express',
profile: 'email'
}, {
route: react.base,
app: 'react',
profile: 'spa'
}].forEach(({route, app,profile}) => {
describe(`for app ${app}`, () => {
before(() => {
cy.useConfigProfile(profile)
});

it('should show the error', () => {
cy.visit(`${APP_URL}/error?id=stub:500`, {
failOnStatusCode: false
})
it('should show the error', () => {
cy.visit(`${route}/error?id=stub:500`, {
failOnStatusCode: false
})

cy.get('code').should('contain.text', 'This is a stub error.')
cy.get('code').should('contain.text', 'This is a stub error.')
})
})
})
})
63 changes: 0 additions & 63 deletions test/e2e/cypress/integration/profiles/email/login/error.spec.js

This file was deleted.

73 changes: 73 additions & 0 deletions test/e2e/cypress/integration/profiles/email/login/error.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {gen} from '../../../../helpers'
import {routes as express} from "../../../../helpers/express";
import {routes as react} from "../../../../helpers/react";

describe('Basic email profile with failing login flows', () => {
[{
route: express.login,
app: 'express', profile: 'email'
}, {
route: react.login,
app: 'react', profile: 'spa'
}].forEach(({route, profile, app}) => {
describe(`for app ${app}`, () => {
before(() => {
cy.useConfigProfile(profile)
});

beforeEach(() => {
cy.visit(route)
})

it('fails when CSRF cookies are missing', () => {
cy.clearCookies()

cy.get('input[name="password_identifier"]').type('i-do-not-exist')
cy.get('input[name="password"]').type('invalid-password')

let initial
cy.location().should((location) => {
initial = location.search
})
cy.get('button[type="submit"]').click()

// We end up at a new flow
cy.location('search').should('not.eq', initial)
})

describe('shows validation errors when invalid signup data is used', () => {
it('should show an error when the identifier is missing', () => {
cy.get('button[type="submit"]').click()
cy.get('*[data-testid="ui.node.message.4000001"]').should(
'contain.text',
'length must be >= 1, but got 0'
)
})

it('should show an error when the password is missing', () => {
const identity = gen.email()
cy.get('input[name="password_identifier"]')
.type(identity)
.should('have.value', identity)

cy.get('button[type="submit"]').click()

cy.get('*[data-testid^="ui.node.message."]').invoke('text').then((text)=> {
expect(text).to.be.oneOf(['length must be >= 1, but got 0', 'Property password is missing.'])
})
})

it('should show fail to sign in', () => {
cy.get('input[name="password_identifier"]').type('i-do-not-exist')
cy.get('input[name="password"]').type('invalid-password')

cy.get('button[type="submit"]').click()
cy.get('*[data-testid="ui.node.message.4000006"]').should(
'contain.text',
'credentials are invalid'
)
})
})
})
})
})
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { APP_URL, gen, website } from '../../../../helpers'
import {routes} from "../../../../helpers/express";

context('Email Profile', () => {
describe('Email Profile', () => {
describe('Login Flow Success', () => {
before(() => {
cy.useConfigProfile('email')
Expand All @@ -15,7 +16,7 @@ context('Email Profile', () => {

beforeEach(() => {
cy.clearCookies()
cy.visit(APP_URL + '/auth/login')
cy.visit(routes.login)
})

it('should sign up and be logged in', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ context('Verification Profile', () => {
it('is able to verify the email address after sign up', () => {
cy.register({ email, password })
cy.login({ email, password })
cy.getSession().then(assertVerifiableAddress({ isVerified: false, email }))
cy.getSession().then(
assertVerifiableAddress({ isVerified: false, email })
)

cy.verifyEmail({ expect: { email } })
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ context('Verification Profile', () => {
'Your changes have been saved!'
)
cy.get('input[name="traits.email"]').should('contain.value', email)
cy.getSession().then(assertVerifiableAddress({ isVerified: false, email }))
cy.getSession().then(
assertVerifiableAddress({ isVerified: false, email })
)

cy.verifyEmail({ expect: { email } })

Expand Down
93 changes: 93 additions & 0 deletions test/e2e/cypress/support/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {Session} from "@ory/kratos-client";
import {gen} from "../helpers";

export interface MailMessage {
fromAddress: string
toAddresses: Array<string>
body:string
subject:string
}

declare global {
namespace Cypress {
interface Chainable {
/**
* Delete mails from the email server.
*
* @param options
*/
deleteMail(options: {
atLeast?: boolean,
}): Chainable<void>

/**
* Fetch the browser's Ory Session.
*
* @param opts
*/
getSession(opts?:
{
expectAal: 'aal2' |'aal1',
expectMethods: Array<'password' | 'webauthn'>,
}
): Chainable<Session>

/**
* Expect that the browser has no valid Ory Kratos Cookie Session.
*/
noSession(): Chainable<Response<any>>

/**
* Set the "privileged session lifespan" to a large value.
*/
longPrivilegedSessionTime(): Chainable<void>

/**
* Return and optionally delete an email from the fake email server.
*
* @param opts
*/
getMail(opts?: { removeMail: boolean }): Chainable<MailMessage>

performEmailVerification(opts?: {expect?: {email?: string, redirectTo?: string}}): Chainable<void>

/**
* Sets the Ory Kratos configuration profile.
*
* @param profile
*/
useConfigProfile(profile: string): Chainable<void>

/**
* Register a new user with email + password via the API.
*
* Recommended to use if you need an Ory Session Token or alternatively the user created.
*
* @param opts
*/
registerApi(opts?: {email:string, password:string, fields: { [key: string]: string }}): Chainable<Session>

/**
* Changes the config so that the login flow lifespan is very short.
*
*
* Useful when testing expiry of login flows.
* @see longLoginLifespan()
*/
shortLoginLifespan(): Chainable<void>

/**
* Changes the config so that the login flow lifespan is very long.
*
* Useful when testing expiry of login flows.
* @see shortLoginLifespan()
*/
longLoginLifespan(): Chainable<void>

/**
* Change the config so that `https://www.ory.sh/` is a allowed return to URL.
*/
browserReturnUrlOry(): Chainable<void>
}
}
}
1 change: 1 addition & 0 deletions test/e2e/cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './commands'
38 changes: 0 additions & 38 deletions test/e2e/cypress/support/index.ts

This file was deleted.

13 changes: 6 additions & 7 deletions test/e2e/cypress/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
"baseUrl": "../../../node_modules",
"target": "es5",
"lib": [
"es5",
"dom",
"es2015.promise"
"es2015",
"dom"
],
"types": ["cypress"],
"esModuleInterop": true,
"typeRoots": [
"./support/index.d.tsx"
]
},
"include": ["**/*.ts"],
"include": [
"**/*.ts",
"support/index.ts",
],
}
2 changes: 1 addition & 1 deletion test/e2e/profiles/email/.kratos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ selfservice:

logout:
after:
default_browser_return_url: http://localhost:4455/auth/login
default_browser_return_url: http://localhost:4455/login

registration:
after:
Expand Down

0 comments on commit 7609219

Please sign in to comment.