Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(files): Add cypress e2e tests
Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Nov 16, 2023
commit 205801adc6911e0fe368f6cc3a10a559c6aa0ebf
12 changes: 3 additions & 9 deletions cypress/e2e/files.cy.ts → cypress/e2e/files/files.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
describe('Login with a new user and open the files app', function() {
before(function() {
describe('Files', { testIsolation: true }, () => {
beforeEach(() => {
cy.createRandomUser().then((user) => {
cy.login(user)
})
})

after(function() {
cy.logout()
})

it('See the default file welcome.txt in the files list', function() {
it('Login with a user and open the files app', () => {
cy.visit('/apps/files')
cy.get('[data-cy-files-list] [data-cy-files-list-row-name="welcome.txt"]').should('be.visible')
// eslint-disable-next-line cypress/no-unnecessary-waiting -- Wait for all to finish loading
cy.wait(500)
})
})
262 changes: 262 additions & 0 deletions cypress/e2e/files/files_sorting.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/**
* @copyright Copyright (c) 2022 John Molakvoæ <[email protected]>
*
* @author John Molakvoæ <[email protected]>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
describe('Files: Sorting the file list', { testIsolation: true }, () => {
let currentUser
beforeEach(() => {
cy.createRandomUser().then((user) => {
currentUser = user
cy.login(user)
})
})

it('Files are sorted by name ascending by default', () => {
cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1 first.txt')
.uploadContent(currentUser, new Blob(), 'text/plain', '/z last.txt')
.uploadContent(currentUser, new Blob(), 'text/plain', '/A.txt')
.uploadContent(currentUser, new Blob(), 'text/plain', '/Ä.txt')
.mkdir(currentUser, '/m')
.mkdir(currentUser, '/4')
cy.login(currentUser)
cy.visit('/apps/files')

cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('4')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('m')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 first.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('A.txt')
break
case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('Ä.txt')
break
case 5: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
case 6: expect($row.attr('data-cy-files-list-row-name')).to.eq('z last.txt')
break
}
})
})

it('Can sort by size', () => {
cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1 tiny.txt')
.uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z big.txt')
.uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a medium.txt')
.mkdir(currentUser, '/folder')
cy.login(currentUser)
cy.visit('/apps/files')

// click sort button
cy.get('th').contains('button', 'Size').click()
// sorting is set
cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'ascending')
// Files are sorted
cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 tiny.txt')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('folder')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('a medium.txt')
break
case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('z big.txt')
break
}
})

// click sort button
cy.get('th').contains('button', 'Size').click()
// sorting is set
cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'descending')
// Files are sorted
cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('z big.txt')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('a medium.txt')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('folder')
break
case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 tiny.txt')
break
}
})
})

it('Can sort by mtime', () => {
cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1.txt', Date.now() / 1000 - 86400 - 1000)
.uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z.txt', Date.now() / 1000 - 86400)
.uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a.txt', Date.now() / 1000 - 86400 - 500)
cy.login(currentUser)
cy.visit('/apps/files')

// click sort button
cy.get('th').contains('button', 'Modified').click()
// sorting is set
cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'ascending')
// Files are sorted
cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt') // uploaded right now
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt') // fake time of yesterday
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt') // fake time of yesterday and few minutes
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt') // fake time of yesterday and ~15 minutes ago
break
}
})

// reverse order
cy.get('th').contains('button', 'Modified').click()
cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'descending')
cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt') // uploaded right now
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt') // fake time of yesterday
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt') // fake time of yesterday and few minutes
break
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt') // fake time of yesterday and ~15 minutes ago
break
}
})
})

it('Favorites are sorted first', () => {
cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1.txt', Date.now() / 1000 - 86400 - 1000)
.uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z.txt', Date.now() / 1000 - 86400)
.uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a.txt', Date.now() / 1000 - 86400 - 500)
.setFileAsFavorite(currentUser, '/a.txt')
cy.login(currentUser)
cy.visit('/apps/files')

cy.log('By name - ascending')
cy.get('th').contains('button', 'Name').click()
cy.contains('th', 'Name').should('have.attr', 'aria-sort', 'ascending')

cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
break
}
})

cy.log('By name - descending')
cy.get('th').contains('button', 'Name').click()
cy.contains('th', 'Name').should('have.attr', 'aria-sort', 'descending')

cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
break
}
})

cy.log('By size - ascending')
cy.get('th').contains('button', 'Size').click()
cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'ascending')

cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
break
}
})

cy.log('By size - descending')
cy.get('th').contains('button', 'Size').click()
cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'descending')

cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
break
}
})

cy.log('By mtime - ascending')
cy.get('th').contains('button', 'Modified').click()
cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'ascending')

cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
break
}
})

cy.log('By mtime - descending')
cy.get('th').contains('button', 'Modified').click()
cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'descending')

cy.get('[data-cy-files-list-row]').each(($row, index) => {
switch (index) {
case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
break
case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
break
case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
break
case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
break
}
})
})
})
73 changes: 71 additions & 2 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,18 @@ declare global {
* Upload a raw content to a given user storage.
* **Warning**: Using this function will reset the previous session
*/
uploadContent(user: User, content: Blob, mimeType: string, target: string): Cypress.Chainable<void>,
uploadContent(user: User, content: Blob, mimeType: string, target: string, mtime?: number): Cypress.Chainable<void>,

/**
* Create a new directory
* **Warning**: Using this function will reset the previous session
*/
mkdir(user: User, target: string): Cypress.Chainable<void>,

/**
* Set a file as favorite (or remove from favorite)
*/
setFileAsFavorite(user: User, target: string, favorite?: boolean): Cypress.Chainable<void>,

/**
* Reset the admin theming entirely.
Expand Down Expand Up @@ -121,6 +132,63 @@ Cypress.Commands.add('uploadFile', (user, fixture = 'image.jpg', mimeType = 'ima
})
})

Cypress.Commands.add('setFileAsFavorite', (user: User, target: string, favorite = true) => {
// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.clearAllCookies()
.then(async () => {
try {
const rootPath = `${Cypress.env('baseUrl')}/remote.php/dav/files/${encodeURIComponent(user.userId)}`
const filePath = target.split('/').map(encodeURIComponent).join('/')
const response = await axios({
url: `${rootPath}${filePath}`,
method: 'PROPPATCH',
auth: {
username: user.userId,
password: user.password,
},
headers: {
'Content-Type': 'application/xml',
},
data: `<?xml version="1.0"?>
<d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:set>
<d:prop>
<oc:favorite>${favorite ? 1 : 0}</oc:favorite>
</d:prop>
</d:set>
</d:propertyupdate>`
})
cy.log(`Created directory ${target}`, response)
} catch (error) {
cy.log('error', error)
throw new Error('Unable to process fixture')
}
})
})

Cypress.Commands.add('mkdir', (user: User, target: string) => {
// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.clearCookies()
.then(async () => {
try {
const rootPath = `${Cypress.env('baseUrl')}/remote.php/dav/files/${encodeURIComponent(user.userId)}`
const filePath = target.split('/').map(encodeURIComponent).join('/')
const response = await axios({
url: `${rootPath}${filePath}`,
method: 'MKCOL',
auth: {
username: user.userId,
password: user.password,
},
})
cy.log(`Created directory ${target}`, response)
} catch (error) {
cy.log('error', error)
throw new Error('Unable to process fixture')
}
})
})

/**
* cy.uploadedContent - uploads a raw content
* TODO: standardise in @nextcloud/cypress
Expand All @@ -130,7 +198,7 @@ Cypress.Commands.add('uploadFile', (user, fixture = 'image.jpg', mimeType = 'ima
* @param {string} mimeType e.g. image/png
* @param {string} target the target of the file relative to the user root
*/
Cypress.Commands.add('uploadContent', (user, blob, mimeType, target) => {
Cypress.Commands.add('uploadContent', (user, blob, mimeType, target, mtime = undefined) => {
// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.clearCookies()
.then(async () => {
Expand All @@ -147,6 +215,7 @@ Cypress.Commands.add('uploadContent', (user, blob, mimeType, target) => {
data: file,
headers: {
'Content-Type': mimeType,
'X-OC-MTime': mtime ? `${mtime}` : undefined,
},
auth: {
username: user.userId,
Expand Down