Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
216 changes: 216 additions & 0 deletions cypress/e2e/FilesSharingUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { closeSidebar, triggerActionForFile } from './files/filesUtils.ts'

export interface ShareSetting {
read: boolean
update: boolean
delete: boolean
create: boolean
share: boolean
download: boolean
note: string
expiryDate: Date
}

export function createShare(fileName: string, username: string, shareSettings: Partial<ShareSetting> = {}) {
openSharingPanel(fileName)
cy.intercept('POST', '**/ocs/v2.php/apps/files_sharing/api/v1/shares').as('createShare')

cy.get('#app-sidebar-vue').within(() => {
cy.intercept({ times: 1, method: 'GET', url: '**/apps/files_sharing/api/v1/sharees?*' }).as('userSearch')
cy.findByRole('combobox', { name: /Search for internal recipients/i })
.type(`{selectAll}${username}`)
cy.wait('@userSearch')
})

cy.get(`[user="${username}"]`).click()

// HACK: Save the share and then update it, as permissions changes are currently not saved for new share.
cy.get('[data-cy-files-sharing-share-editor-action="save"]').click({ scrollBehavior: 'nearest' })
cy.wait('@createShare')
closeSidebar()

updateShare(fileName, 0, shareSettings)
}

export function openSharingDetails(index: number) {
cy.get('#app-sidebar-vue').within(() => {
cy.findAllByRole('button', { name: /open sharing details/i })
.should('have.length.at.least', index + 1)
.eq(index)
.click({ force: true })
cy.get('[data-cy-files-sharing-share-permissions-bundle="custom"]')
.click()
})
}

export function updateShare(fileName: string, index: number, shareSettings: Partial<ShareSetting> = {}) {
openSharingPanel(fileName)
openSharingDetails(index)

cy.intercept({ times: 1, method: 'PUT', url: '**/apps/files_sharing/api/v1/shares/*' }).as('updateShare')

cy.get('#app-sidebar-vue').within(() => {
if (shareSettings.download !== undefined) {
cy.get('[data-cy-files-sharing-share-permissions-checkbox="download"]').find('input').as('downloadCheckbox')
if (shareSettings.download) {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@downloadCheckbox')
.check({ force: true, scrollBehavior: 'nearest' })
cy.get('@downloadCheckbox')
.should('be.checked')
} else {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@downloadCheckbox')
.uncheck({ force: true, scrollBehavior: 'nearest' })
cy.get('@downloadCheckbox')
.should('not.be.checked')
}
}

if (shareSettings.read !== undefined) {
cy.get('[data-cy-files-sharing-share-permissions-checkbox="read"]').find('input').as('readCheckbox')
if (shareSettings.read) {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@readCheckbox').check({ force: true, scrollBehavior: 'nearest' })
} else {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@readCheckbox').uncheck({ force: true, scrollBehavior: 'nearest' })
}
}

if (shareSettings.update !== undefined) {
cy.get('[data-cy-files-sharing-share-permissions-checkbox="update"]').find('input').as('updateCheckbox')
if (shareSettings.update) {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@updateCheckbox').check({ force: true, scrollBehavior: 'nearest' })
} else {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@updateCheckbox').uncheck({ force: true, scrollBehavior: 'nearest' })
}
}

if (shareSettings.create !== undefined) {
cy.get('[data-cy-files-sharing-share-permissions-checkbox="create"]').find('input').as('createCheckbox')
if (shareSettings.create) {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@createCheckbox').check({ force: true, scrollBehavior: 'nearest' })
} else {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@createCheckbox').uncheck({ force: true, scrollBehavior: 'nearest' })
}
}

if (shareSettings.delete !== undefined) {
cy.get('[data-cy-files-sharing-share-permissions-checkbox="delete"]').find('input').as('deleteCheckbox')
if (shareSettings.delete) {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@deleteCheckbox').check({ force: true, scrollBehavior: 'nearest' })
} else {
// Force:true because the checkbox is hidden by the pretty UI.
cy.get('@deleteCheckbox').uncheck({ force: true, scrollBehavior: 'nearest' })
}
}

if (shareSettings.note !== undefined) {
cy.findByRole('checkbox', { name: /note to recipient/i }).check({ force: true, scrollBehavior: 'nearest' })
cy.findByRole('textbox', { name: /note to recipient/i }).type(shareSettings.note)
}

if (shareSettings.expiryDate !== undefined) {
cy.findByRole('checkbox', { name: /expiration date/i })
.check({ force: true, scrollBehavior: 'nearest' })
cy.get('#share-date-picker')
.type(`${shareSettings.expiryDate.getFullYear()}-${String(shareSettings.expiryDate.getMonth() + 1).padStart(2, '0')}-${String(shareSettings.expiryDate.getDate()).padStart(2, '0')}`)
}

cy.get('[data-cy-files-sharing-share-editor-action="save"]').click({ scrollBehavior: 'nearest' })

cy.wait('@updateShare')
})
closeSidebar()
}

export function openSharingPanel(fileName: string) {
triggerActionForFile(fileName, 'details')

cy.get('[data-cy-sidebar]')
.as('sidebar')
.should('be.visible')
cy.get('@sidebar')
.find('[aria-controls="tab-sharing"]')
.click()
}

type FileRequestOptions = {
label?: string
note?: string
password?: string
/* YYYY-MM-DD format */
expiration?: string
}

/**
* Create a file request for a folder
*
* @param path The path of the folder, leading slash is required
* @param options The options for the file request
*/
export function createFileRequest(path: string, options: FileRequestOptions = {}) {
if (!path.startsWith('/')) {
throw new Error('Path must start with a slash')
}

// Navigate to the folder
cy.visit('/apps/files/files?dir=' + path)

// Open the file request dialog
cy.get('[data-cy-upload-picker] .action-item__menutoggle').first().click()
cy.contains('.upload-picker__menu-entry button', 'Create file request').click()
cy.get('[data-cy-file-request-dialog]').should('be.visible')

// Check and fill the first page options
cy.get('[data-cy-file-request-dialog-fieldset="label"]').should('be.visible')
cy.get('[data-cy-file-request-dialog-fieldset="destination"]').should('be.visible')
cy.get('[data-cy-file-request-dialog-fieldset="note"]').should('be.visible')

cy.get('[data-cy-file-request-dialog-fieldset="destination"] input').should('contain.value', path)
if (options.label) {
cy.get('[data-cy-file-request-dialog-fieldset="label"] input').type(`{selectall}${options.label}`)
}
if (options.note) {
cy.get('[data-cy-file-request-dialog-fieldset="note"] textarea').type(`{selectall}${options.note}`)
}

// Go to the next page
cy.get('[data-cy-file-request-dialog-controls="next"]').click()
cy.get('[data-cy-file-request-dialog-fieldset="expiration"] input[type="checkbox"]').should('exist')
cy.get('[data-cy-file-request-dialog-fieldset="expiration"] input[type="date"]').should('not.exist')
cy.get('[data-cy-file-request-dialog-fieldset="password"] input[type="checkbox"]').should('exist')
cy.get('[data-cy-file-request-dialog-fieldset="password"] input[type="password"]').should('not.exist')
if (options.expiration) {
cy.get('[data-cy-file-request-dialog-fieldset="expiration"] input[type="checkbox"]').check({ force: true })
cy.get('[data-cy-file-request-dialog-fieldset="expiration"] input[type="date"]').type(`{selectall}${options.expiration}`)
}
if (options.password) {
cy.get('[data-cy-file-request-dialog-fieldset="password"] input[type="checkbox"]').check({ force: true })
cy.get('[data-cy-file-request-dialog-fieldset="password"] input[type="password"]').type(`{selectall}${options.password}`)
}

// Create the file request
cy.get('[data-cy-file-request-dialog-controls="next"]').click()

// Get the file request URL
cy.get('[data-cy-file-request-dialog-fieldset="link"]').then(($link) => {
const url = $link.val()
cy.log(`File request URL: ${url}`)
cy.wrap(url).as('fileRequestUrl')
})

// Close
cy.get('[data-cy-file-request-dialog-controls="finish"]').click()
}
6 changes: 3 additions & 3 deletions cypress/e2e/files_versions/filesVersionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import type { User } from '@nextcloud/cypress'

import { basename } from '@nextcloud/paths'
import { addUserToGroup, createGroup, createGroupFolder, PERMISSION_DELETE, PERMISSION_READ, PERMISSION_WRITE } from '../groupfoldersUtils'
import { addUserToGroup, createGroup, createGroupFolder, PERMISSION_DELETE, PERMISSION_READ, PERMISSION_SHARE, PERMISSION_WRITE } from '../groupfoldersUtils'
import { navigateToFolder, triggerActionForFile } from '../files/filesUtils'

type SetupInfo = {
export type SetupInfo = {
dataSnapshot: string
dbSnapshot: string
groupName: string
Expand Down Expand Up @@ -37,7 +37,7 @@ export function setupFilesVersions(): Cypress.Chainable<SetupInfo> {

cy.then(() => {
addUserToGroup(setupInfo.groupName, setupInfo.user.userId)
createGroupFolder(setupInfo.groupFolderName, setupInfo.groupName, [PERMISSION_READ, PERMISSION_WRITE, PERMISSION_DELETE])
createGroupFolder(setupInfo.groupFolderName, setupInfo.groupName, [PERMISSION_READ, PERMISSION_WRITE, PERMISSION_DELETE, PERMISSION_SHARE])

uploadThreeVersions(setupInfo.user, setupInfo.filePath)
})
Expand Down
38 changes: 38 additions & 0 deletions cypress/e2e/files_versions/version_access_from_share.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { openVersionsPanel, setupFilesVersions, type SetupInfo } from './filesVersionsUtils'
import { createShare } from '../FilesSharingUtils'
import { navigateToFolder } from '../files/filesUtils'

describe('Versions creation', () => {
let setupInfo: SetupInfo

beforeEach(() => {
setupFilesVersions()
.then((_setupInfo) => {
setupInfo = _setupInfo

cy.createRandomUser().then((bob) => {
cy.login(setupInfo.user)
cy.visit('/apps/files')
navigateToFolder(setupInfo.groupFolderName)
createShare(setupInfo.fileName, bob.userId)

cy.login(bob)
cy.visit('/apps/files')
openVersionsPanel(setupInfo.filePath)
})
})
})

it('List versions as expected', () => {
cy.get('#tab-files_versions').within(() => {
cy.get('[data-files-versions-version]').should('have.length', 3)
cy.get('[data-files-versions-version]').eq(0).contains('Current version')
cy.get('[data-files-versions-version]').eq(2).contains('Initial version')
})
})
})
Loading