Skip to content

Commit 028ef90

Browse files
test(tables): cover pagination across the toolbar and card footer
The existing component test mounts a three row fixture, so the footer pagination never rendered and the two pagination controls were never exercised together. Mount NcTable with 150 generated rows instead and assert that paging from either control moves the other, that the footer only appears beyond the first page, and that it renders in the gallery layout. Removing the event bus subscription from PaginationBlock fails the two sync assertions, so they guard the behaviour rather than the markup. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
1 parent 8ac1935 commit 028ef90

1 file changed

Lines changed: 128 additions & 0 deletions

File tree

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
import NcTable from '../../src/shared/components/ncTable/NcTable.vue'
6+
7+
const ROWS_PER_PAGE = 100
8+
const ROW_COUNT = 150
9+
10+
const TOOLBAR_PAGINATION = '.options .pagination-block'
11+
const FOOTER_PAGINATION = '.pagination-footer'
12+
const PAGE_INPUT = 'input[type="number"]'
13+
14+
describe('Pagination', () => {
15+
let richObject = {}
16+
17+
before(() => {
18+
cy.fixture('widgets/richObject.json')
19+
.then(richObjectFixture => {
20+
richObject = richObjectFixture
21+
})
22+
})
23+
24+
it('shows a footer pagination once the rows span more than one page', () => {
25+
mountTable(richObject, ROW_COUNT)
26+
27+
cy.get(TOOLBAR_PAGINATION).should('exist')
28+
cy.get(FOOTER_PAGINATION).should('exist')
29+
})
30+
31+
it('hides the footer pagination while everything fits on one page', () => {
32+
mountTable(richObject, ROWS_PER_PAGE)
33+
34+
cy.get(TOOLBAR_PAGINATION).should('exist')
35+
cy.get(FOOTER_PAGINATION).should('not.exist')
36+
})
37+
38+
it('keeps the toolbar pagination in sync when paging from the footer', () => {
39+
mountTable(richObject, ROW_COUNT)
40+
41+
cy.get(`${FOOTER_PAGINATION} ${PAGE_INPUT}`).should('have.value', '1')
42+
cy.get(TOOLBAR_PAGINATION).find(PAGE_INPUT).should('have.value', '1')
43+
44+
cy.get(FOOTER_PAGINATION)
45+
.find('button[aria-label="Go to next page"]')
46+
.click()
47+
48+
cy.get(TOOLBAR_PAGINATION).find(PAGE_INPUT).should('have.value', '2')
49+
cy.get(`${FOOTER_PAGINATION} ${PAGE_INPUT}`).should('have.value', '2')
50+
cy.get('tr[data-cy="customTableRow"]').first().should('contain', `Row ${ROWS_PER_PAGE + 1}`)
51+
})
52+
53+
it('keeps the footer pagination in sync when paging from the toolbar', () => {
54+
mountTable(richObject, ROW_COUNT)
55+
56+
cy.get(TOOLBAR_PAGINATION)
57+
.find('button[aria-label="Go to last page"]')
58+
.click()
59+
60+
cy.get(`${FOOTER_PAGINATION} ${PAGE_INPUT}`).should('have.value', '2')
61+
cy.get(TOOLBAR_PAGINATION).find(PAGE_INPUT).should('have.value', '2')
62+
})
63+
64+
it('renders the footer pagination in the gallery layout', () => {
65+
mountTable(richObject, ROW_COUNT, { layout: 'gallery' })
66+
67+
cy.get('[data-cy="galleryLayoutCard"]').should('have.length', ROWS_PER_PAGE)
68+
cy.get(FOOTER_PAGINATION).should('exist')
69+
})
70+
71+
it('falls back to a valid page when the row set shrinks', () => {
72+
mountTable(richObject, ROW_COUNT).then(({ wrapper }) => {
73+
cy.get(FOOTER_PAGINATION)
74+
.find('button[aria-label="Go to last page"]')
75+
.click()
76+
77+
cy.get(`${FOOTER_PAGINATION} ${PAGE_INPUT}`)
78+
.should('have.value', '2')
79+
.then(() => wrapper.setProps({ rows: buildRows(richObject, 10) }))
80+
81+
cy.get(TOOLBAR_PAGINATION).find(PAGE_INPUT).should('have.value', '1')
82+
cy.get('tr[data-cy="customTableRow"]').should('have.length', 10)
83+
})
84+
})
85+
})
86+
87+
/**
88+
* Builds a row set of the requested size from the fixture, labelling every row so that the
89+
* currently rendered page can be identified.
90+
*
91+
* @param {object} richObject the widget fixture holding the column and row templates
92+
* @param {number} count how many rows to build
93+
* @return {Array} the generated rows
94+
*/
95+
function buildRows(richObject, count) {
96+
const [template] = richObject.rows
97+
const labelColumnId = richObject.columns[0].id
98+
99+
return Array.from({ length: count }, (unused, index) => ({
100+
...template,
101+
id: 1000 + index,
102+
data: template.data.map(cell => (
103+
cell.columnId === labelColumnId
104+
? { ...cell, value: `Row ${index + 1}` }
105+
: { ...cell }
106+
)),
107+
}))
108+
}
109+
110+
/**
111+
* Mounts NcTable with a generated row set.
112+
*
113+
* @param {object} richObject the widget fixture holding the column and row templates
114+
* @param {number} rowCount how many rows to render
115+
* @param {object} viewSetting the view setting to render with
116+
* @return {Cypress.Chainable} the mount result
117+
*/
118+
function mountTable(richObject, rowCount, viewSetting = {}) {
119+
return cy.mount(NcTable, {
120+
props: {
121+
rows: buildRows(richObject, rowCount),
122+
columns: richObject.columns,
123+
elementId: richObject.id,
124+
isView: false,
125+
viewSetting,
126+
},
127+
})
128+
}

0 commit comments

Comments
 (0)