Skip to content

Commit d9486ea

Browse files
committed
test(files): Make scrolling tests independent from magic values
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent 5c82e1a commit d9486ea

File tree

4 files changed

+185
-80
lines changed

4 files changed

+185
-80
lines changed

cypress/e2e/core-utils.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,43 @@ export enum UnifiedSearchFilter {
4848
export function getUnifiedSearchFilter(filter: UnifiedSearchFilter) {
4949
return getUnifiedSearchModal().find(`[data-cy-unified-search-filters] [data-cy-unified-search-filter="${CSS.escape(filter)}"]`)
5050
}
51+
52+
/**
53+
* Assertion that an element is fully within the current viewport.
54+
* @param $el The element
55+
* @param expected If the element is expected to be fully in viewport or not fully
56+
* @example
57+
* ```js
58+
* cy.get('#my-element')
59+
* .should(beFullyInViewport)
60+
* ```
61+
*/
62+
export function beFullyInViewport($el: JQuery<HTMLElement>, expected = true) {
63+
const { top, left, bottom, right } = $el.get(0)!.getBoundingClientRect()
64+
const innerHeight = Cypress.$('body').innerHeight()!
65+
const innerWidth = Cypress.$('body').innerWidth()!
66+
const fullyVisible = top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth
67+
68+
console.debug(`fullyVisible: ${fullyVisible}, top: ${top >= 0}, left: ${left >= 0}, bottom: ${bottom <= innerHeight}, right: ${right <= innerWidth}`)
69+
70+
if (expected) {
71+
// eslint-disable-next-line no-unused-expressions
72+
expect(fullyVisible, 'Fully within viewport').to.be.true
73+
} else {
74+
// eslint-disable-next-line no-unused-expressions
75+
expect(fullyVisible, 'Not fully within viewport').to.be.false
76+
}
77+
}
78+
79+
/**
80+
* Opposite of `beFullyInViewport` - resolves when element is not or only partially in viewport.
81+
* @param $el The element
82+
* @example
83+
* ```js
84+
* cy.get('#my-element')
85+
* .should(notBeFullyInViewport)
86+
* ```
87+
*/
88+
export function notBeFullyInViewport($el: JQuery<HTMLElement>) {
89+
return beFullyInViewport($el, false)
90+
}

cypress/e2e/files/FilesUtils.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6-
import type { User } from "@nextcloud/cypress"
6+
import type { User } from '@nextcloud/cypress'
77

88
export const getRowForFileId = (fileid: number) => cy.get(`[data-cy-files-list-row-fileid="${fileid}"]`)
99
export const getRowForFile = (filename: string) => cy.get(`[data-cy-files-list-row-name="${CSS.escape(filename)}"]`)
@@ -214,3 +214,45 @@ export const reloadCurrentFolder = () => {
214214
cy.get('[data-cy-files-content-breadcrumbs]').findByRole('button', { description: 'Reload current directory' }).click()
215215
cy.wait('@propfind')
216216
}
217+
218+
/**
219+
* Enable the grid mode for the files list.
220+
* Will fail if already enabled!
221+
*/
222+
export function enableGridMode() {
223+
cy.intercept('**/apps/files/api/v1/config/grid_view').as('setGridMode')
224+
cy.findByRole('button', { name: 'Switch to grid view' })
225+
.should('be.visible')
226+
.click()
227+
cy.wait('@setGridMode')
228+
}
229+
230+
/**
231+
* Calculate the needed viewport height to limit the visible rows of the file list.
232+
* Requires a logged in user.
233+
*
234+
* @param rows The number of rows that should be displayed at the same time
235+
*/
236+
export function calculateViewportHeight(rows: number): Cypress.Chainable<number> {
237+
cy.visit('/apps/files')
238+
239+
return cy.get('[data-cy-files-list]')
240+
.should('be.visible')
241+
.then((filesList) => {
242+
const windowHeight = Cypress.$('body').outerHeight()!
243+
// Size of other page elements
244+
const outerHeight = Math.ceil(windowHeight - filesList.outerHeight()!)
245+
// Size of before and filters
246+
const beforeHeight = Math.ceil(Cypress.$('.files-list__before').outerHeight()!)
247+
const filterHeight = Math.ceil(Cypress.$('.files-list__filters').outerHeight()!)
248+
// Size of the table header
249+
const tableHeaderHeight = Math.ceil(Cypress.$('[data-cy-files-list-thead]').outerHeight()!)
250+
// table row height
251+
const rowHeight = Math.ceil(Cypress.$('[data-cy-files-list-tbody] tr').outerHeight()!)
252+
253+
// sum it up
254+
const viewportHeight = outerHeight + beforeHeight + filterHeight + tableHeaderHeight + rows * rowHeight
255+
cy.log(`Calculated viewport height: ${viewportHeight} (${outerHeight} + ${beforeHeight} + ${filterHeight} + ${tableHeaderHeight} + ${rows} * ${rowHeight})`)
256+
return cy.wrap(viewportHeight)
257+
})
258+
}

cypress/e2e/files/files-renaming.cy.ts

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@
44
*/
55

66
import type { User } from '@nextcloud/cypress'
7-
import { getRowForFile, haveValidity, renameFile, triggerActionForFile } from './FilesUtils'
7+
import { calculateViewportHeight, getRowForFile, haveValidity, renameFile, triggerActionForFile } from './FilesUtils'
88

99
describe('files: Rename nodes', { testIsolation: true }, () => {
1010
let user: User
1111

1212
beforeEach(() => cy.createRandomUser().then(($user) => {
1313
user = $user
1414

15+
// remove welcome file
16+
cy.rm(user, '/welcome.txt')
17+
// create a file called "file.txt"
1518
cy.uploadContent(user, new Blob([]), 'text/plain', '/file.txt')
19+
20+
// login and visit files app
1621
cy.login(user)
1722
cy.visit('/apps/files')
1823
}))
@@ -116,34 +121,6 @@ describe('files: Rename nodes', { testIsolation: true }, () => {
116121
.should('not.exist')
117122
})
118123

119-
/**
120-
* This is a regression test of: https://github.com/nextcloud/server/issues/47438
121-
* The issue was that the renaming state was not reset when the new name moved the file out of the view of the current files list
122-
* due to virtual scrolling the renaming state was not changed then by the UI events (as the component was taken out of DOM before any event handling).
123-
*/
124-
it('correctly resets renaming state', () => {
125-
for (let i = 1; i <= 20; i++) {
126-
cy.uploadContent(user, new Blob([]), 'text/plain', `/file${i}.txt`)
127-
}
128-
cy.viewport(1200, 500) // 500px is smaller then 20 * 50 which is the place that the files take up
129-
cy.login(user)
130-
cy.visit('/apps/files')
131-
132-
getRowForFile('file.txt').should('be.visible')
133-
// Z so it is shown last
134-
renameFile('file.txt', 'zzz.txt')
135-
// not visible any longer
136-
getRowForFile('zzz.txt').should('not.be.visible')
137-
// scroll file list to bottom
138-
cy.get('[data-cy-files-list]').scrollTo('bottom')
139-
cy.screenshot()
140-
// The file is no longer in rename state
141-
getRowForFile('zzz.txt')
142-
.should('be.visible')
143-
.findByRole('textbox', { name: 'Filename' })
144-
.should('not.exist')
145-
})
146-
147124
it('cancel renaming on esc press', () => {
148125
// All are visible by default
149126
getRowForFile('file.txt').should('be.visible')
@@ -182,4 +159,38 @@ describe('files: Rename nodes', { testIsolation: true }, () => {
182159
.find('input[type="text"]')
183160
.should('not.exist')
184161
})
162+
163+
/**
164+
* This is a regression test of: https://github.com/nextcloud/server/issues/47438
165+
* The issue was that the renaming state was not reset when the new name moved the file out of the view of the current files list
166+
* due to virtual scrolling the renaming state was not changed then by the UI events (as the component was taken out of DOM before any event handling).
167+
*/
168+
it('correctly resets renaming state', () => {
169+
// Create 19 additional files
170+
for (let i = 1; i <= 19; i++) {
171+
cy.uploadContent(user, new Blob([]), 'text/plain', `/file${i}.txt`)
172+
}
173+
174+
// Calculate and setup a viewport where only the first 4 files are visible, causing 6 rows to be rendered
175+
cy.viewport(768, 500)
176+
cy.login(user)
177+
calculateViewportHeight(4)
178+
.then((height) => cy.viewport(768, height))
179+
180+
cy.visit('/apps/files')
181+
182+
getRowForFile('file.txt').should('be.visible')
183+
// Z so it is shown last
184+
renameFile('file.txt', 'zzz.txt')
185+
// not visible any longer
186+
getRowForFile('zzz.txt').should('not.exist')
187+
// scroll file list to bottom
188+
cy.get('[data-cy-files-list]').scrollTo('bottom')
189+
cy.screenshot()
190+
// The file is no longer in rename state
191+
getRowForFile('zzz.txt')
192+
.should('be.visible')
193+
.findByRole('textbox', { name: 'Filename' })
194+
.should('not.exist')
195+
})
185196
})

0 commit comments

Comments
 (0)