230 lines
6.4 KiB
TypeScript
230 lines
6.4 KiB
TypeScript
const authenticatedUser = {
|
|
id: 7,
|
|
email: 'user@example.com',
|
|
}
|
|
|
|
const bibleLayout = {
|
|
set: { id: 41, name: 'Bible' },
|
|
levels: [
|
|
{ id: 1, kind: 'book', depth: 0, elementCount: 2 },
|
|
{ id: 2, kind: 'portion', depth: 1, elementCount: 2 },
|
|
{ id: 3, kind: 'Chapter_sections-v2', depth: 2, elementCount: 1 },
|
|
],
|
|
elements: [
|
|
{
|
|
id: 1,
|
|
name: 'Genesis',
|
|
kind: 'book',
|
|
levelId: 1,
|
|
children: [
|
|
{
|
|
id: 3,
|
|
name: 'Creation',
|
|
kind: 'portion',
|
|
levelId: 2,
|
|
children: [
|
|
{
|
|
id: 4,
|
|
name: 'Chapter 1',
|
|
kind: 'Chapter_sections-v2',
|
|
levelId: 3,
|
|
children: [],
|
|
},
|
|
],
|
|
},
|
|
{ id: 5, name: 'Noah', kind: 'portion', levelId: 2, children: [] },
|
|
],
|
|
},
|
|
{ id: 2, name: 'Exodus', kind: 'book', levelId: 1, 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 with a foldable element 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 dashboard').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')
|
|
.and('have.attr', 'aria-expanded', 'false')
|
|
.and('have.attr', 'aria-controls', 'element-children-1')
|
|
cy.get('[data-element-id="2"] > .element-node__card').should(
|
|
'not.match',
|
|
'button',
|
|
)
|
|
cy.get('[data-element-id="3"]').should('not.exist')
|
|
|
|
cy.get('[data-element-id="1"] > button.element-node__card').click()
|
|
cy.get('[data-element-id="1"] > .element-node__card').should(
|
|
'have.attr',
|
|
'aria-expanded',
|
|
'true',
|
|
)
|
|
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="4"]').should('not.exist')
|
|
|
|
cy.get('[data-element-id="3"] > button.element-node__card').click()
|
|
cy.get('[data-element-id="3"] > .element-node__card').should(
|
|
'have.attr',
|
|
'aria-expanded',
|
|
'true',
|
|
)
|
|
cy.get('#element-children-3 > li')
|
|
.should('have.length', 1)
|
|
.and('have.attr', 'data-element-id', '4')
|
|
cy.contains('.element-node__card', 'Chapter 1')
|
|
.should('be.visible')
|
|
.find('.element-node__kind')
|
|
.should('have.text', 'Chapter_sections-v2')
|
|
.and('have.css', 'text-transform', 'none')
|
|
|
|
cy.get('[data-element-id="1"] > button.element-node__card').click()
|
|
cy.get('[data-element-id="3"]').should('not.exist')
|
|
|
|
cy.get('[data-element-id="1"] > button.element-node__card').click()
|
|
cy.get('[data-element-id="4"] > .element-node__card').should(
|
|
'contain.text',
|
|
'Chapter 1',
|
|
)
|
|
})
|
|
|
|
it('shows loading and empty layout states', () => {
|
|
cy.intercept('GET', '**/api/sets/41', {
|
|
delay: 500,
|
|
statusCode: 200,
|
|
body: { set: { id: 41, name: 'Empty set' }, levels: [], 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.get('[data-element-id="1"] > button.element-node__card').click()
|
|
cy.get('[data-element-id="3"] > button.element-node__card').click()
|
|
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,
|
|
)
|
|
})
|
|
})
|
|
})
|