Skip to content

Commit

Permalink
Merge pull request #2650 from nextcloud/feat/add-extensive-cypress-tests
Browse files Browse the repository at this point in the history
Add cypress test for ACLs and trashbin
  • Loading branch information
come-nc authored Dec 4, 2023
2 parents f28589d + 99c7c08 commit cf8e231
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 18 deletions.
1 change: 1 addition & 0 deletions cypress/dockerNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export const configureNextcloud = async function(branch = 'master') {
// Enable the app and give status
await runExec(container, ['php', 'occ', 'app:enable', '--force', 'viewer'], true)
await runExec(container, ['php', 'occ', 'app:enable', 'groupfolders', '--force'], true)
await runExec(container, ['php', 'occ', 'app:enable', 'files_trashbin', '--force'], true)
// await runExec(container, ['php', 'occ', 'app:list'], true)

console.log('└─ Nextcloud is now ready to use πŸŽ‰')
Expand Down
220 changes: 206 additions & 14 deletions cypress/e2e/groupfolders.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,22 @@
*
*/
import {
addACLManagerUser,
addUserToGroup,
createGroup,
createGroupFolder,
deleteGroupFolder,
deleteFile,
enableACLPermissions,
enterFolder,
enterFolderInTrashbin,
fileOrFolderExists,
fileOrFolderDoesNotExist,
fileOrFolderExistsInTrashbin,
fileOrFolderDoesNotExistInTrashbin,
restoreFile,
setACLPermissions,
PERMISSION_DELETE,
PERMISSION_READ,
PERMISSION_WRITE,
} from './groupfoldersUtils'
Expand All @@ -32,40 +44,220 @@ import { randHash } from '../utils'

import type { User } from '@nextcloud/cypress'

describe('Manage groupfolders', () => {
let user: User
describe('Groupfolders ACLs and trashbin behavior', () => {
let user1: User
let user2: User
let managerUser: User
let groupFolderId: string
let groupName: string
let groupFolderName: string

before(() => {
beforeEach(() => {
if (groupFolderId) {
deleteGroupFolder(groupFolderId)
}
groupName = `test_group_${randHash()}`
groupFolderName = `test_group_folder_${randHash()}`

cy.createRandomUser()
.then(_user => {
user = _user
cy.login(user)
user1 = _user
})
cy.createRandomUser()
.then(_user => {
user2 = _user
})
cy.createRandomUser()
.then(_user => {
managerUser = _user

createGroup(groupName)
.then(() => {
addUserToGroup(groupName, user.userId)
createGroupFolder(groupFolderName, groupName, [PERMISSION_READ, PERMISSION_WRITE])
addUserToGroup(groupName, user1.userId)
addUserToGroup(groupName, user2.userId)
addUserToGroup(groupName, managerUser.userId)
createGroupFolder(groupFolderName, groupName, [PERMISSION_READ, PERMISSION_WRITE, PERMISSION_DELETE])
.then(_groupFolderId => {
groupFolderId = _groupFolderId
cy.visit('/apps/files')
enableACLPermissions(groupFolderId)
addACLManagerUser(groupFolderId,managerUser.userId)
})
})
})
})

after(() => {
if (groupFolderId) {
deleteGroupFolder(groupFolderId)
}
it('ACL, delete and restore', () => {
// Create two subfolders and two files as manager
cy.login(managerUser)
cy.mkdir(managerUser, `/${groupFolderName}/subfolder1`)
cy.mkdir(managerUser, `/${groupFolderName}/subfolder1/subfolder2`)
cy.uploadContent(managerUser, new Blob(['Content of the file']), 'text/plain', `/${groupFolderName}/subfolder1/file1.txt`)
cy.uploadContent(managerUser, new Blob(['Content of the file']), 'text/plain', `/${groupFolderName}/subfolder1/subfolder2/file2.txt`)

// Set ACL permissions
setACLPermissions(groupFolderId, '/subfolder1', [`+${PERMISSION_READ}`,`-${PERMISSION_WRITE}`], undefined, user1.userId)
setACLPermissions(groupFolderId, '/subfolder1', [`-${PERMISSION_READ}`], undefined, user2.userId)

// User1 has access
cy.login(user1)
cy.visit('/apps/files')
enterFolder(groupFolderName)
enterFolder('subfolder1')
fileOrFolderExists('file1.txt')
enterFolder('subfolder2')
fileOrFolderExists('file2.txt')

// User2 has no access
cy.login(user2)
cy.visit('/apps/files')
enterFolder(groupFolderName)
fileOrFolderDoesNotExist('subfolder1')

// Delete files
cy.login(managerUser)
cy.visit('/apps/files')
enterFolder(groupFolderName)
enterFolder('subfolder1')
deleteFile('file1.txt')
deleteFile('subfolder2')

// User1 sees it in trash
cy.login(user1)
cy.visit('/apps/files/trashbin')
fileOrFolderExistsInTrashbin('file1.txt')
enterFolderInTrashbin('subfolder2')
fileOrFolderExists('file2.txt')

// User2 does not
cy.login(user2)
cy.visit('/apps/files/trashbin')
fileOrFolderDoesNotExistInTrashbin('file1.txt')
fileOrFolderDoesNotExistInTrashbin('subfolder2')

// Restore files
cy.login(managerUser)
cy.visit('/apps/files/trashbin')
fileOrFolderExistsInTrashbin('file1.txt')
fileOrFolderExistsInTrashbin('subfolder2')
restoreFile('file1.txt')
restoreFile('subfolder2')

// User1 has access
cy.login(user1)
cy.visit('/apps/files')
enterFolder(groupFolderName)
fileOrFolderExists('subfolder1')
enterFolder('subfolder1')
fileOrFolderExists('file1.txt')
enterFolder('subfolder2')
fileOrFolderExists('file2.txt')

// User2 has no access
cy.login(user2)
cy.visit('/apps/files')
enterFolder(groupFolderName)
fileOrFolderDoesNotExist('subfolder1')
})

it.skip('ACL directly on deleted folder', () => {
// Create a subfolders and a file as manager
cy.login(managerUser)
cy.mkdir(managerUser, `/${groupFolderName}/subfolder1`)
cy.uploadContent(managerUser, new Blob(['Content of the file']), 'text/plain', `/${groupFolderName}/subfolder1/file1.txt`)

// Set ACL permissions on subfolder
setACLPermissions(groupFolderId, '/subfolder1', [`+${PERMISSION_READ}`,`-${PERMISSION_WRITE}`], undefined, user1.userId)
setACLPermissions(groupFolderId, '/subfolder1', [`-${PERMISSION_READ}`], undefined, user2.userId)

// Delete subfolder
cy.login(managerUser)
cy.visit('/apps/files')
enterFolder(groupFolderName)
deleteFile('subfolder1')

// User1 sees it in trash
cy.login(user1)
cy.visit('/apps/files/trashbin')
fileOrFolderExistsInTrashbin('subfolder1')
enterFolderInTrashbin('subfolder1')
fileOrFolderExists('file1.txt')

// User2 does not
cy.login(user2)
cy.visit('/apps/files/trashbin')
fileOrFolderDoesNotExistInTrashbin('subfolder1')
})

it('Visite the group folder', () => {
return true
it.skip('Delete, rename parent and restore', () => {
// Create a subfolders and a file as manager
cy.login(managerUser)
cy.mkdir(managerUser, `/${groupFolderName}/subfolder1`)
cy.uploadContent(managerUser, new Blob(['Content of the file']), 'text/plain', `/${groupFolderName}/subfolder1/file1.txt`)

// Delete file
cy.login(managerUser)
cy.visit('/apps/files')
enterFolder(groupFolderName)
enterFolder('subfolder1')
deleteFile('file1.txt')

// Rename subfolder1
cy.visit('/apps/files')
enterFolder(groupFolderName)
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name="subfolder1"] [data-cy-files-list-row-actions]`).click()
cy.get(`[data-cy-files-list] [data-cy-files-list-row-action="rename"]`).scrollIntoView()
cy.get(`[data-cy-files-list] [data-cy-files-list-row-action="rename"]`).click()
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name="subfolder1"] [class="files-list__row-rename"] [class="input-field__input"]`).type('subfolder1renamed{enter}')
fileOrFolderExists('subfolder1renamed')

// Restore from trash
cy.visit('/apps/files/trashbin')
restoreFile('file1.txt')

// File should be restored in renamed folder
cy.login(managerUser)
cy.visit('/apps/files')
enterFolder(groupFolderName)
fileOrFolderExists('subfolder1renamed')
fileOrFolderDoesNotExist('file1.txt')
enterFolder('subfolder1renamed')
fileOrFolderExists('file1.txt')
})

it.skip('Delete, rename parent and check ACL', () => {
// Create a subfolders and a file as manager
cy.login(managerUser)
cy.mkdir(managerUser, `/${groupFolderName}/subfolder1`)
cy.uploadContent(managerUser, new Blob(['Content of the file']), 'text/plain', `/${groupFolderName}/subfolder1/file1.txt`)

// Set ACL permissions
setACLPermissions(groupFolderId, '/subfolder1', [`+${PERMISSION_READ}`,`-${PERMISSION_WRITE}`], undefined, user1.userId)
setACLPermissions(groupFolderId, '/subfolder1', [`-${PERMISSION_READ}`], undefined, user2.userId)

// Delete file
cy.login(managerUser)
cy.visit('/apps/files')
enterFolder(groupFolderName)
enterFolder('subfolder1')
deleteFile('file1.txt')

// Rename subfolder1
cy.visit('/apps/files')
enterFolder(groupFolderName)
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name="subfolder1"] [data-cy-files-list-row-actions]`).click()
cy.get(`[data-cy-files-list] [data-cy-files-list-row-action="rename"]`).scrollIntoView()
cy.get(`[data-cy-files-list] [data-cy-files-list-row-action="rename"]`).click()
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name="subfolder1"] [class="files-list__row-rename"] [class="input-field__input"]`).type('subfolder1renamed{enter}')
fileOrFolderExists('subfolder1renamed')

// User1 sees it in trash
cy.login(user1)
cy.visit('/apps/files/trashbin')
fileOrFolderExistsInTrashbin('file1.txt')

// User2 does not
cy.login(user2)
cy.visit('/apps/files/trashbin')
fileOrFolderDoesNotExistInTrashbin('file1.txt')
})
})
44 changes: 41 additions & 3 deletions cypress/e2e/groupfoldersUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export function disableACLPermissions(groupFolderId: string) {
return cy.runOccCommand(`groupfolders:permissions ${groupFolderId} --disable`)
}

export function addACLManager(groupFolderId: string, groupOrUserName: string) {
return cy.runOccCommand(`groupfolders:permissions ${groupFolderId} --manage-add ${groupOrUserName}`)
export function addACLManagerUser(groupFolderId: string, userName: string) {
return cy.runOccCommand(`groupfolders:permissions ${groupFolderId} --manage-add --user ${userName}`)
}

export function removeACLManager(groupFolderId: string, groupOrUserName: string) {
Expand All @@ -67,9 +67,47 @@ export function setACLPermissions(
userId?: string,
) {
const target = groupId !== undefined ? `--group ${groupId}` : `--user ${userId}`
return cy.runOccCommand(`groupfolders:permissions ${groupFolderId} ${path} ${aclPermissions} ${target}`)
return cy.runOccCommand(`groupfolders:permissions ${groupFolderId} ${path} ${target} -- ${aclPermissions.join(' ')}`)
}

export function deleteGroupFolder(groupFolderId: string) {
return cy.runOccCommand(`groupfolders:delete ${groupFolderId}`)
}

export function fileOrFolderExists(name: string) {
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name="${name}"]`).should('be.visible')
}

export function fileOrFolderDoesNotExist(name: string) {
// Make sure file list is loaded first
cy.get(`[data-cy-files-list-tfoot],[data-cy-files-content-empty]`).should('be.visible')
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name="${name}"]`).should('not.exist')
}

export function fileOrFolderExistsInTrashbin(name: string) {
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name^="${name}.d"]`).should('be.visible')
}

export function fileOrFolderDoesNotExistInTrashbin(name: string) {
// Make sure file list is loaded first
cy.get(`[data-cy-files-list-tfoot],[data-cy-files-content-empty]`).should('be.visible')
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name^="${name}.d"]`).should('not.exist')
}

export function enterFolder(name: string) {
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name="${name}"]`).click()
}

export function enterFolderInTrashbin(name: string) {
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name^="${name}.d"]`).click()
}

export function deleteFile(name: string) {
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name="${name}"] [data-cy-files-list-row-actions]`).click()
cy.get(`[data-cy-files-list] [data-cy-files-list-row-action="delete"]`).scrollIntoView()
cy.get(`[data-cy-files-list] [data-cy-files-list-row-action="delete"]`).click()
}

export function restoreFile(name: string) {
cy.get(`[data-cy-files-list] [data-cy-files-list-row-name^="${name}.d"] [data-cy-files-list-row-action="restore"]`).click()
}
32 changes: 31 additions & 1 deletion cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ declare global {
*/
uploadContent(user: User, content: Blob, mimeType: string, target: string): Cypress.Chainable<void>,

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

/**
* Run an occ command in the docker container.
*/
Expand All @@ -56,6 +62,30 @@ declare global {
const url = (Cypress.config('baseUrl') || '').replace(/\/index.php\/?$/g, '')
Cypress.env('baseUrl', url)


Cypress.Commands.add('mkdir', (user: User, target: string) => {
// eslint-disable-next-line cypress/unsafe-to-chain-command
cy.clearCookies()
.then({timeout:8000}, 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.uploadedFile - uploads a file from the fixtures folder
* TODO: standardise in @nextcloud/cypress
Expand Down Expand Up @@ -85,7 +115,7 @@ Cypress.Commands.add('uploadFile', (user, fixture = 'image.jpg', mimeType = 'ima
*/
Cypress.Commands.add('uploadContent', (user, blob, mimeType, target) => {
cy.clearCookies()
.then(async () => {
.then({timeout:8000}, async () => {
const fileName = basename(target)

// Process paths
Expand Down

0 comments on commit cf8e231

Please sign in to comment.