170 lines
4.5 KiB
TypeScript
170 lines
4.5 KiB
TypeScript
const authenticatedUser = {
|
|
id: 7,
|
|
email: 'user@example.com',
|
|
}
|
|
|
|
function fillLoginForm(): void {
|
|
cy.get('#login-email').type('user@example.com')
|
|
cy.get('#login-password').type('correct-password')
|
|
}
|
|
|
|
describe('password login', () => {
|
|
beforeEach(() => {
|
|
cy.intercept('GET', '**/api/me', {
|
|
statusCode: 401,
|
|
body: { error: 'unauthenticated' },
|
|
}).as('me')
|
|
})
|
|
|
|
it('logs in and redirects to the dashboard', () => {
|
|
cy.intercept('POST', '**/api/login', (request) => {
|
|
expect(request.body).to.deep.equal({
|
|
email: 'user@example.com',
|
|
password: 'correct-password',
|
|
})
|
|
request.reply({
|
|
statusCode: 200,
|
|
body: { user: authenticatedUser },
|
|
})
|
|
}).as('login')
|
|
|
|
cy.visit('/login')
|
|
fillLoginForm()
|
|
cy.get('form').submit()
|
|
cy.wait('@login')
|
|
|
|
cy.location('pathname').should('equal', '/dashboard')
|
|
})
|
|
|
|
it('returns to a safe internal redirect after login', () => {
|
|
cy.intercept('POST', '**/api/login', {
|
|
statusCode: 200,
|
|
body: { user: authenticatedUser },
|
|
}).as('login')
|
|
|
|
cy.visit('/login?redirect=%2Fdashboard%3Ffocus%3Dtoday')
|
|
fillLoginForm()
|
|
cy.get('form').submit()
|
|
cy.wait('@login')
|
|
|
|
cy.location('pathname').should('equal', '/dashboard')
|
|
cy.location('search').should('equal', '?focus=today')
|
|
})
|
|
|
|
it('ignores an unsafe redirect after login', () => {
|
|
cy.intercept('POST', '**/api/login', {
|
|
statusCode: 200,
|
|
body: { user: authenticatedUser },
|
|
}).as('login')
|
|
|
|
cy.visit('/login?redirect=https%3A%2F%2Fexample.com')
|
|
fillLoginForm()
|
|
cy.get('form').submit()
|
|
cy.wait('@login')
|
|
|
|
cy.location('pathname').should('equal', '/dashboard')
|
|
})
|
|
|
|
it('validates required login fields before submitting', () => {
|
|
cy.visit('/login')
|
|
cy.get('form').submit()
|
|
|
|
cy.get('#login-email-error')
|
|
.should('have.text', 'Enter a valid email address.')
|
|
.and('be.visible')
|
|
cy.get('#login-email').should('have.attr', 'aria-invalid', 'true')
|
|
cy.get('#login-password-error')
|
|
.should('have.text', 'Enter your password.')
|
|
.and('be.visible')
|
|
cy.get('#login-password').should('have.attr', 'aria-invalid', 'true')
|
|
cy.location('pathname').should('equal', '/login')
|
|
})
|
|
|
|
it('shows a generic invalid-credentials error', () => {
|
|
cy.intercept('POST', '**/api/login', {
|
|
statusCode: 401,
|
|
body: { error: 'invalid_credentials' },
|
|
}).as('login')
|
|
|
|
cy.visit('/login')
|
|
fillLoginForm()
|
|
cy.get('form').submit()
|
|
cy.wait('@login')
|
|
|
|
cy.get('[role="alert"]')
|
|
.should('have.text', 'Email or password is incorrect.')
|
|
.and('be.visible')
|
|
cy.location('pathname').should('equal', '/login')
|
|
})
|
|
|
|
it('shows backend field validation errors', () => {
|
|
cy.intercept('POST', '**/api/login', {
|
|
statusCode: 422,
|
|
body: {
|
|
message: 'The email field must be a valid email address.',
|
|
errors: {
|
|
email: ['The email field must be a valid email address.'],
|
|
},
|
|
},
|
|
}).as('login')
|
|
|
|
cy.visit('/login')
|
|
fillLoginForm()
|
|
cy.get('form').submit()
|
|
cy.wait('@login')
|
|
|
|
cy.get('#login-email-error')
|
|
.should(
|
|
'have.text',
|
|
'The email field must be a valid email address.',
|
|
)
|
|
.and('be.visible')
|
|
})
|
|
|
|
it('shows throttling and malformed-response errors', () => {
|
|
cy.intercept('POST', '**/api/login', {
|
|
statusCode: 429,
|
|
body: { message: 'Too Many Attempts.' },
|
|
}).as('throttledLogin')
|
|
|
|
cy.visit('/login')
|
|
fillLoginForm()
|
|
cy.get('form').submit()
|
|
cy.wait('@throttledLogin')
|
|
cy.get('[role="alert"]').should(
|
|
'have.text',
|
|
'Too many login attempts. Try again in a minute.',
|
|
)
|
|
|
|
cy.intercept('POST', '**/api/login', {
|
|
statusCode: 200,
|
|
body: { user: { id: 7 } },
|
|
}).as('malformedLogin')
|
|
|
|
cy.get('form').submit()
|
|
cy.wait('@malformedLogin')
|
|
cy.get('[role="alert"]').should(
|
|
'have.text',
|
|
'Unable to log in. Please try again.',
|
|
)
|
|
})
|
|
|
|
it('disables the form while login is pending', () => {
|
|
cy.intercept('POST', '**/api/login', {
|
|
delay: 500,
|
|
statusCode: 200,
|
|
body: { user: authenticatedUser },
|
|
}).as('login')
|
|
|
|
cy.visit('/login')
|
|
fillLoginForm()
|
|
cy.get('form').submit()
|
|
|
|
cy.get('button[type="submit"]')
|
|
.should('be.disabled')
|
|
.and('have.text', 'Logging in...')
|
|
cy.get('#login-email').should('be.disabled')
|
|
cy.get('#login-password').should('be.disabled')
|
|
cy.wait('@login')
|
|
})
|
|
})
|