Attainly/frontend/website/cypress/e2e/set-layout.cy.ts

183 lines
4.8 KiB
TypeScript

const authenticatedUser = {
id: 7,
email: 'user@example.com',
}
const bibleLayout = {
set: { id: 41, name: 'Bible' },
elements: [
{
id: 1,
name: 'Genesis',
kind: 'book',
children: [
{
id: 3,
name: 'Creation',
kind: 'portion',
children: [
{
id: 4,
name: 'Chapter 1',
kind: 'chapter',
children: [],
},
],
},
{ id: 5, name: 'Noah', kind: 'portion', children: [] },
],
},
{ id: 2, name: 'Exodus', kind: 'book', children: [] },
],
}
function interceptAuthenticatedUser(): void {
cy.intercept('GET', '**/api/me', {
statusCode: 200,
body: { user: authenticatedUser },
}).as('me')
}
describe('set element layout', () => {
beforeEach(() => {
interceptAuthenticatedUser()
})
it('opens a set from the dashboard and shows its full hierarchy', () => {
cy.intercept('GET', '**/api/sets', {
statusCode: 200,
body: { sets: [{ id: 41, name: 'Bible' }] },
}).as('sets')
cy.intercept('GET', '**/api/sets/41', (request) => {
expect(request.headers.accept).to.equal('application/json')
request.reply({ statusCode: 200, body: bibleLayout })
}).as('layout')
cy.visit('/dashboard')
cy.wait('@me')
cy.wait('@sets')
cy.contains('a', 'Bible').click()
cy.wait('@layout')
cy.location('pathname').should('equal', '/sets/41')
cy.get('h1').should('have.text', 'Bible')
cy.contains('a', 'Back to sets').should('have.attr', 'href', '/dashboard')
cy.get('ol[aria-label="Bible element layout"] > li').then(($nodes) => {
expect([...$nodes].map((node) => node.dataset.elementId)).to.deep.equal([
'1',
'2',
])
})
cy.get('[data-element-id="1"] > .element-node__card')
.should('contain.text', 'Genesis')
.and('contain.text', 'book')
cy.get('[data-element-id="1"] > ol > li').then(($nodes) => {
expect([...$nodes].map((node) => node.dataset.elementId)).to.deep.equal([
'3',
'5',
])
})
cy.get('[data-element-id="3"] > ol > li')
.should('have.length', 1)
.and('have.attr', 'data-element-id', '4')
cy.contains('.element-node__card', 'Chapter 1')
.should('be.visible')
.and('contain.text', 'chapter')
})
it('shows loading and empty layout states', () => {
cy.intercept('GET', '**/api/sets/41', {
delay: 500,
statusCode: 200,
body: { set: { id: 41, name: 'Empty set' }, elements: [] },
}).as('layout')
cy.visit('/sets/41')
cy.wait('@me')
cy.get('[role="status"]').should('have.text', 'Loading set layout...')
cy.wait('@layout')
cy.get('h1').should('have.text', 'Empty set')
cy.get('[role="status"]').should(
'have.text',
'This set does not have any elements yet.',
)
})
it('retries after a layout request fails', () => {
let requestCount = 0
cy.intercept('GET', '**/api/sets/41', (request) => {
requestCount += 1
request.alias = `layout${requestCount}`
if (requestCount === 1) {
request.reply({ statusCode: 500 })
return
}
request.reply({ statusCode: 200, body: bibleLayout })
})
cy.visit('/sets/41')
cy.wait('@me')
cy.wait('@layout1')
cy.get('[role="alert"]').should(
'contain.text',
"We couldn't load this set's layout.",
)
cy.contains('button', 'Try again').click()
cy.wait('@layout2')
cy.get('h1').should('have.text', 'Bible')
})
it('distinguishes missing sets from malformed responses', () => {
cy.intercept('GET', '**/api/sets/41', {
statusCode: 404,
body: { error: 'set not found' },
}).as('missing')
cy.visit('/sets/41')
cy.wait('@me')
cy.wait('@missing')
cy.get('h1').should('have.text', 'Set not found')
cy.get('[role="alert"]').should(
'contain.text',
"The set you're looking for is not available.",
)
cy.intercept('GET', '**/api/sets/42', {
statusCode: 200,
body: { set: { id: 42, name: 12 }, elements: [] },
}).as('malformed')
cy.visit('/sets/42')
cy.wait('@malformed')
cy.get('[role="alert"]').should(
'contain.text',
"We couldn't load this set's layout.",
)
cy.contains('button', 'Try again').should('be.visible')
})
it('keeps a deeply nested layout within a mobile viewport', () => {
cy.viewport(390, 844)
cy.intercept('GET', '**/api/sets/41', {
statusCode: 200,
body: bibleLayout,
}).as('layout')
cy.visit('/sets/41')
cy.wait('@me')
cy.wait('@layout')
cy.contains('.element-node__card', 'Chapter 1').should('be.visible')
cy.document().then((document) => {
expect(document.documentElement.scrollWidth).to.be.at.most(
document.documentElement.clientWidth,
)
})
})
})