test: adjust Cypress tests for refactored files app UI

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
Ferdinand Thiessen 2026-01-22 12:21:42 +01:00 committed by backportbot[bot]
parent 3cbfd301b3
commit 6e4dcc6ce0
8 changed files with 209 additions and 141 deletions

View file

@ -162,7 +162,7 @@ export function triggerSelectionAction(actionId: string) {
getSelectionActionButton().click({ force: true })
// the entry might already be a button or a button might its child
getSelectionActionEntry(actionId)
.then(($el) => $el.is('button') ? cy.wrap($el) : cy.wrap($el).findByRole('button').last())
.then(($el) => $el.is('button') ? cy.wrap($el) : cy.wrap($el).findByRole('menuitem').last())
.should('exist')
.click()
}
@ -384,12 +384,24 @@ export function triggerFileListAction(actionId: string) {
}
/**
* Reloads the current folder
*
* @param intercept if true this will wait for the PROPFIND to complete before it resolves
*/
export function reloadCurrentFolder() {
export function reloadCurrentFolder(intercept = true) {
cy.intercept('PROPFIND', /\/remote.php\/dav\//).as('propfind')
cy.get('[data-cy-files-content-breadcrumbs]').findByRole('button', { description: 'Reload current directory' }).click()
cy.wait('@propfind')
cy.findByRole('navigation', { name: 'Current directory path' })
.findAllByRole('button')
.filter('[aria-haspopup="menu"]')
.click()
cy.findByRole('menu')
.should('be.visible')
.findByRole('menuitem', { name: 'Reload content' })
.click()
if (intercept) {
cy.wait('@propfind')
}
}
/**

View file

@ -132,6 +132,14 @@ describe('files: Drag and Drop', { testIsolation: true }, () => {
cy.get('[data-cy-upload-picker] progress').should('not.be.visible')
cy.get('@uploadFile.all').should('have.length', 2)
// see the warning
cy.get('.toast-warning').should('exist')
// close all toasts
cy.get('.toastify')
.findAllByRole('button', { name: 'Close' })
.click({ multiple: true })
getRowForFile('first.txt').should('be.visible')
getRowForFile('second.txt').should('be.visible')
getRowForFile('Foo').should('not.exist')

View file

@ -69,17 +69,17 @@ describe('files: Filter in files list', { testIsolation: true }, () => {
getRowForFile('file.txt').should('be.visible')
getRowForFile('spreadsheet.csv').should('be.visible')
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.should('be.visible')
.click()
cy.findByRole('menuitemcheckbox', { name: 'Spreadsheets' })
.should('be.visible')
.click()
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
filesFilters.triggerFilter('Type')
cy.findByRole('button', { name: 'Spreadsheets' })
.should('be.visible')
.and('have.attr', 'aria-pressed', 'false')
.as('spreadsheetsFilterButton')
.click()
cy.get('@spreadsheetsFilterButton')
.should('have.attr', 'aria-pressed', 'true')
filesFilters.closeFilterMenu()
// See that only the spreadsheet is visible
getRowForFile('spreadsheet.csv').should('be.visible')
@ -91,33 +91,32 @@ describe('files: Filter in files list', { testIsolation: true }, () => {
// All are visible by default
getRowForFile('folder').should('be.visible')
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.should('be.visible')
.click()
cy.findByRole('menuitemcheckbox', { name: 'Spreadsheets' })
.should('be.visible')
.click()
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
filesFilters.triggerFilter('Type')
cy.findByRole('button', { name: 'Spreadsheets' })
.should('be.visible')
.as('spreadsheetsFilterButton')
.click()
cy.get('@spreadsheetsFilterButton')
.should('have.attr', 'aria-pressed', 'true')
filesFilters.closeFilterMenu()
// See folder is not visible
getRowForFile('folder').should('not.exist')
// clear filter
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.should('be.visible')
.click()
cy.findByRole('menuitem', { name: /clear filter/i })
.should('be.visible')
.click()
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
filesFilters.triggerFilter('Type')
cy.findByRole('button', { name: 'Spreadsheets' })
.should('be.visible')
.and('have.attr', 'aria-pressed', 'true')
.as('spreadsheetsFilterButton')
.click()
cy.get('@spreadsheetsFilterButton')
.should('have.attr', 'aria-pressed', 'false')
filesFilters.closeFilterMenu()
// See folder is visible again
getRowForFile('folder').should('be.visible')
@ -127,17 +126,16 @@ describe('files: Filter in files list', { testIsolation: true }, () => {
// All are visible by default
getRowForFile('folder').should('be.visible')
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.should('be.visible')
.click()
cy.findByRole('menuitemcheckbox', { name: 'Spreadsheets' })
.should('be.visible')
.click()
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
filesFilters.triggerFilter('Type')
cy.findByRole('button', { name: 'Spreadsheets' })
.should('be.visible')
.as('spreadsheetsFilterButton')
.click()
cy.get('@spreadsheetsFilterButton')
.should('have.attr', 'aria-pressed', 'true')
filesFilters.closeFilterMenu()
// See folder is not visible
getRowForFile('folder').should('not.exist')
@ -154,16 +152,16 @@ describe('files: Filter in files list', { testIsolation: true }, () => {
getRowForFile('folder').should('be.visible')
getRowForFile('file.txt').should('be.visible')
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
filesFilters.triggerFilter('Type')
cy.findByRole('button', { name: 'Folders' })
.should('be.visible')
.as('spreadsheetsFilterButton')
.click()
cy.findByRole('menuitemcheckbox', { name: 'Folders' })
.should('be.visible')
.click()
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.click()
cy.get('@spreadsheetsFilterButton')
.should('have.attr', 'aria-pressed', 'true')
filesFilters.closeFilterMenu()
// See that only the folder is visible
getRowForFile('folder').should('be.visible')
@ -189,20 +187,16 @@ describe('files: Filter in files list', { testIsolation: true }, () => {
getRowForFile('file.txt').should('be.visible')
// enable type filter for folders
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
filesFilters.triggerFilter('Type')
cy.findByRole('button', { name: 'Folders' })
.should('be.visible')
.as('spreadsheetsFilterButton')
.click()
cy.findByRole('menuitemcheckbox', { name: 'Folders' })
.should('be.visible')
.click()
// assert the button is checked
cy.findByRole('menuitemcheckbox', { name: 'Folders' })
.should('have.attr', 'aria-checked', 'true')
// close the menu
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
.click()
cy.get('@spreadsheetsFilterButton')
.should('have.attr', 'aria-pressed', 'true')
filesFilters.closeFilterMenu()
// See the chips are active
filesFilters.activeFilters()
@ -222,13 +216,13 @@ describe('files: Filter in files list', { testIsolation: true }, () => {
.should('have.length', 1)
.contains(/Folder/).should('be.visible')
// And also the button should be active
filesFilters.filterContainter()
.findByRole('button', { name: 'Type' })
filesFilters.triggerFilter('Type')
cy.findByRole('button', { name: 'Folders' })
.should('be.visible')
.click()
cy.findByRole('menuitemcheckbox', { name: 'Folders' })
.should('be.visible')
.and('have.attr', 'aria-checked', 'true')
.should('have.attr', 'aria-pressed', 'true')
filesFilters.closeFilterMenu()
})
/** Regression test of https://github.com/nextcloud/server/issues/53038 */

View file

@ -19,11 +19,10 @@ describe('files: Set default view', { testIsolation: true }, () => {
// See URL and current view
cy.url().should('match', /\/apps\/files\/files/)
cy.get('[data-cy-files-content-breadcrumbs]')
.findByRole('button', {
name: 'All files',
description: 'Reload current directory',
})
cy.findByRole('navigation', { name: 'Current directory path' })
.findAllByRole('button')
.first()
.should('have.text', 'All files')
// See the option is also selected
// Open the files settings
@ -54,11 +53,10 @@ describe('files: Set default view', { testIsolation: true }, () => {
cy.visit('/apps/files')
cy.url().should('match', /\/apps\/files\/personal/)
cy.get('[data-cy-files-content-breadcrumbs]')
.findByRole('button', {
name: 'Personal files',
description: 'Reload current directory',
})
cy.findByRole('navigation', { name: 'Current directory path' })
.findAllByRole('button')
.first()
.should('have.text', 'Personal files')
})
})

View file

@ -6,13 +6,13 @@
import type { User } from '@nextcloud/e2e-test-server/cypress'
import {
clickOnBreadcrumbs,
copyFile,
createFolder,
getRowForFile,
getRowForFileId,
moveFile,
navigateToFolder,
reloadCurrentFolder,
renameFile,
triggerActionForFile,
triggerInlineActionForFileId,
@ -52,7 +52,7 @@ describe('Files: Live photos', { testIsolation: true }, () => {
it('Copies both files when copying the .jpg', () => {
copyFile(`${randomFileName}.jpg`, '.')
clickOnBreadcrumbs('All files')
reloadCurrentFolder()
getRowForFile(`${randomFileName}.jpg`).should('have.length', 1)
getRowForFile(`${randomFileName}.mov`).should('have.length', 1)
@ -62,7 +62,7 @@ describe('Files: Live photos', { testIsolation: true }, () => {
it('Copies both files when copying the .mov', () => {
copyFile(`${randomFileName}.mov`, '.')
clickOnBreadcrumbs('All files')
reloadCurrentFolder()
getRowForFile(`${randomFileName}.mov`).should('have.length', 1)
getRowForFile(`${randomFileName} (copy).jpg`).should('have.length', 1)
@ -100,7 +100,7 @@ describe('Files: Live photos', { testIsolation: true }, () => {
it('Moves files when moving the .jpg', () => {
renameFile(`${randomFileName}.jpg`, `${randomFileName}_moved.jpg`)
clickOnBreadcrumbs('All files')
reloadCurrentFolder()
getRowForFileId(jpgFileId).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}_moved.jpg`)
getRowForFileId(movFileId).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}_moved.mov`)
@ -108,7 +108,7 @@ describe('Files: Live photos', { testIsolation: true }, () => {
it('Moves files when moving the .mov', () => {
renameFile(`${randomFileName}.mov`, `${randomFileName}_moved.mov`)
clickOnBreadcrumbs('All files')
reloadCurrentFolder()
getRowForFileId(jpgFileId).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}_moved.jpg`)
getRowForFileId(movFileId).invoke('attr', 'data-cy-files-list-row-name').should('equal', `${randomFileName}_moved.mov`)
@ -116,7 +116,7 @@ describe('Files: Live photos', { testIsolation: true }, () => {
it('Deletes files when deleting the .jpg', () => {
triggerActionForFile(`${randomFileName}.jpg`, 'delete')
clickOnBreadcrumbs('All files')
reloadCurrentFolder()
getRowForFile(`${randomFileName}.jpg`).should('have.length', 0)
getRowForFile(`${randomFileName}.mov`).should('have.length', 0)
@ -129,7 +129,7 @@ describe('Files: Live photos', { testIsolation: true }, () => {
it('Block deletion when deleting the .mov', () => {
triggerActionForFile(`${randomFileName}.mov`, 'delete')
clickOnBreadcrumbs('All files')
reloadCurrentFolder()
getRowForFile(`${randomFileName}.jpg`).should('have.length', 1)
getRowForFile(`${randomFileName}.mov`).should('have.length', 1)
@ -143,8 +143,9 @@ describe('Files: Live photos', { testIsolation: true }, () => {
it('Restores files when restoring the .jpg', () => {
triggerActionForFile(`${randomFileName}.jpg`, 'delete')
cy.visit('/apps/files/trashbin')
triggerInlineActionForFileId(jpgFileId, 'restore')
clickOnBreadcrumbs('Deleted files')
reloadCurrentFolder()
getRowForFile(`${randomFileName}.jpg`).should('have.length', 0)
getRowForFile(`${randomFileName}.mov`).should('have.length', 0)
@ -158,8 +159,9 @@ describe('Files: Live photos', { testIsolation: true }, () => {
it('Blocks restoration when restoring the .mov', () => {
triggerActionForFile(`${randomFileName}.jpg`, 'delete')
cy.visit('/apps/files/trashbin')
triggerInlineActionForFileId(movFileId, 'restore')
clickOnBreadcrumbs('Deleted files')
reloadCurrentFolder()
getRowForFileId(jpgFileId).should('have.length', 1)
getRowForFileId(movFileId).should('have.length', 1)

View file

@ -6,7 +6,7 @@
import type { User } from '@nextcloud/e2e-test-server/cypress'
import { FilesNavigationPage } from '../../pages/FilesNavigation.ts'
import { getRowForFile, navigateToFolder } from './FilesUtils.ts'
import { getRowForFile, navigateToFolder, reloadCurrentFolder } from './FilesUtils.ts'
describe('files: search', () => {
let user: User
@ -74,7 +74,7 @@ describe('files: search', () => {
it('See "search everywhere" button', () => {
// Not visible initially
cy.get('[data-cy-files-filters]')
cy.get('.files-list__filters')
.findByRole('button', { name: /Search everywhere/i })
.should('not.to.exist')
@ -82,7 +82,7 @@ describe('files: search', () => {
navigation.searchInput().type('file')
// see its visible
cy.get('[data-cy-files-filters]')
cy.get('.files-list__filters')
.findByRole('button', { name: /Search everywhere/i })
.should('be.visible')
@ -90,7 +90,7 @@ describe('files: search', () => {
navigation.searchClearButton().click()
// see its not visible again
cy.get('[data-cy-files-filters]')
cy.get('.files-list__filters')
.findByRole('button', { name: /Search everywhere/i })
.should('not.to.exist')
})
@ -108,7 +108,7 @@ describe('files: search', () => {
cy.get('[data-cy-files-list-row-fileid]').should('have.length', 3)
// toggle global search
cy.get('[data-cy-files-filters]')
cy.get('.files-list__filters')
.findByRole('button', { name: /Search everywhere/i })
.should('be.visible')
.click()
@ -206,7 +206,7 @@ describe('files: search', () => {
cy.intercept('SEARCH', '**/remote.php/dav/').as('search')
// refresh the view
cy.findByRole('button', { description: /reload current directory/i }).click()
reloadCurrentFolder(false) // no PROPFIND intercept here as we want to wait for SEARCH
// wait for the request
cy.wait('@search')
// see that the search view is reloaded

View file

@ -18,45 +18,6 @@ const files = [
'file5.txt',
]
function resetTags() {
tags = {}
for (let i = 0; i < 5; i++) {
tags[randomBytes(8).toString('base64').slice(0, 6)] = 0
}
// delete any existing tags
cy.runOccCommand('tag:list --output=json').then((output) => {
Object.keys(JSON.parse(output.stdout)).forEach((id) => {
cy.runOccCommand(`tag:delete ${id}`)
})
})
// create tags
Object.keys(tags).forEach((tag) => {
cy.runOccCommand(`tag:add ${tag} public --output=json`).then((output) => {
tags[tag] = JSON.parse(output.stdout).id as number
})
})
cy.log('Using tags', tags)
}
function expectInlineTagForFile(file: string, tags: string[]) {
getRowForFile(file)
.find('[data-systemtags-fileid]')
.findAllByRole('listitem')
.should('have.length', tags.length)
.each((tag) => {
expect(tag.text()).to.be.oneOf(tags)
})
}
function triggerTagManagementDialogAction() {
cy.intercept('PROPFIND', '/remote.php/dav/systemtags/').as('getTagsList')
triggerSelectionAction('systemtags:bulk')
cy.wait('@getTagsList')
cy.get('[data-cy-systemtags-picker]').should('be.visible')
}
describe('Systemtags: Files bulk action', { testIsolation: false }, () => {
let user1: User
let user2: User
@ -98,7 +59,7 @@ describe('Systemtags: Files bulk action', { testIsolation: false }, () => {
cy.intercept('PROPFIND', '/remote.php/dav/systemtags/*/files').as('getTagData')
cy.intercept('PROPPATCH', '/remote.php/dav/systemtags/*/files').as('assignTagData')
const tag = Object.keys(tags)[3]
const tag = Object.keys(tags)[3]!
cy.get(`[data-cy-systemtags-picker-tag=${tags[tag]}]`).should('be.visible')
.findByRole('checkbox').click({ force: true })
cy.get('[data-cy-systemtags-picker-button-submit]').click()
@ -127,9 +88,9 @@ describe('Systemtags: Files bulk action', { testIsolation: false }, () => {
cy.intercept('PROPFIND', '/remote.php/dav/systemtags/*/files').as('getTagData')
cy.intercept('PROPPATCH', '/remote.php/dav/systemtags/*/files').as('assignTagData')
const prevTag = Object.keys(tags)[3]
const tag1 = Object.keys(tags)[1]
const tag2 = Object.keys(tags)[2]
const prevTag = Object.keys(tags)[3]!
const tag1 = Object.keys(tags)[1]!
const tag2 = Object.keys(tags)[2]!
cy.get(`[data-cy-systemtags-picker-tag=${tags[tag1]}]`).should('be.visible')
.findByRole('checkbox').click({ force: true })
cy.get(`[data-cy-systemtags-picker-tag=${tags[tag2]}]`).should('be.visible')
@ -166,9 +127,9 @@ describe('Systemtags: Files bulk action', { testIsolation: false }, () => {
cy.intercept('PROPFIND', '/remote.php/dav/systemtags/*/files').as('getTagData')
cy.intercept('PROPPATCH', '/remote.php/dav/systemtags/*/files').as('assignTagData')
const firstTag = Object.keys(tags)[3]
const tag1 = Object.keys(tags)[1]
const tag2 = Object.keys(tags)[2]
const firstTag = Object.keys(tags)[3]!
const tag1 = Object.keys(tags)[1]!
const tag2 = Object.keys(tags)[2]!
cy.get(`[data-cy-systemtags-picker-tag=${tags[tag2]}]`).should('be.visible')
.findByRole('checkbox').click({ force: true })
cy.get('[data-cy-systemtags-picker-button-submit]').click()
@ -247,8 +208,8 @@ describe('Systemtags: Files bulk action', { testIsolation: false }, () => {
cy.intercept('PROPFIND', '/remote.php/dav/systemtags/*/files').as('getTagData1')
cy.intercept('PROPPATCH', '/remote.php/dav/systemtags/*/files').as('assignTagData1')
const tag1 = Object.keys(tags)[0]
const tag2 = Object.keys(tags)[3]
const tag1 = Object.keys(tags)[0]!
const tag2 = Object.keys(tags)[3]!
cy.get(`[data-cy-systemtags-picker-tag=${tags[tag1]}]`).should('be.visible')
.findByRole('checkbox').click({ force: true })
cy.get(`[data-cy-systemtags-picker-tag=${tags[tag2]}]`).should('be.visible')
@ -466,3 +427,42 @@ describe('Systemtags: Files bulk action', { testIsolation: false }, () => {
})
})
})
function resetTags() {
tags = {}
for (let i = 0; i < 5; i++) {
tags[randomBytes(8).toString('base64').slice(0, 6)] = 0
}
// delete any existing tags
cy.runOccCommand('tag:list --output=json').then((output) => {
Object.keys(JSON.parse(output.stdout)).forEach((id) => {
cy.runOccCommand(`tag:delete ${id}`)
})
})
// create tags
Object.keys(tags).forEach((tag) => {
cy.runOccCommand(`tag:add ${tag} public --output=json`).then((output) => {
tags[tag] = JSON.parse(output.stdout).id as number
})
})
cy.log('Using tags', tags)
}
function expectInlineTagForFile(file: string, tags: string[]) {
getRowForFile(file)
.find('[data-systemtags-fileid]')
.findAllByRole('listitem')
.should('have.length', tags.length)
.each((tag) => {
expect(tag.text()).to.be.oneOf(tags)
})
}
function triggerTagManagementDialogAction() {
cy.intercept('PROPFIND', '/remote.php/dav/systemtags/').as('getTagsList')
triggerSelectionAction('systemtags:bulk')
cy.wait('@getTagsList')
cy.get('[data-cy-systemtags-picker]').should('be.visible')
}

View file

@ -7,8 +7,62 @@
* Page object model for the files filters
*/
export class FilesFilterPage {
filterContainter() {
return cy.get('[data-cy-files-filters]')
/**
* Get the filters menu button (only on narrow and medium widths)
*/
getFiltersMenuToggle() {
return cy.get('[data-test-id="files-list-filters"]')
.findByRole('button', { name: 'Filters' })
}
/**
* Get and trigger the filter within the menu (only on narrow and medium widths)
*
* @param name - The name of the filter button
*/
triggerFilterMenu(name: string | RegExp) {
cy.get('[data-test-id="files-list-filters"]')
.findByRole('button', { name: 'Filters' })
.should('be.visible')
.as('filtersMenuToggle')
.click()
cy.get('@filtersMenuToggle')
.should('have.attr', 'aria-expanded', 'true')
cy.findByRole('menu')
.should('be.visible')
.findByRole('menuitem', { name })
.should('be.visible')
.click()
}
/**
* Get and trigger the filter button if the files list is wide enough to show all filters
*
* @param name - The name of the filter button
*/
triggerFilterButton(name: string | RegExp) {
cy.get('[data-test-id="files-list-filters"]')
.findByRole('button', { name })
.should('be.visible')
.click()
}
triggerFilter(name: string | RegExp) {
cy.get('[data-cy-files-list]')
.should('be.visible')
.if(($el) => expect($el.get(0).clientWidth).to.be.gte(1024))
.then(() => this.triggerFilterButton(name))
.else()
.then(() => this.triggerFilterMenu(name))
}
closeFilterMenu() {
cy.get('[data-test-id="files-list-filters"]')
.findAllByRole('button')
.filter('[aria-expanded="true"]')
.click({ multiple: true })
}
activeFiltersList() {