test frontend password login

This commit is contained in:
Yisroel Baum 2026-07-31 11:41:07 +03:00
parent ebfe147ca4
commit 1dff3f6976
Signed by: yisroelbaum
GPG key ID: 0FA60884F75520A9

View file

@ -0,0 +1,170 @@
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')
})
})