diff --git a/apps/files/src/actions/downloadAction.spec.ts b/apps/files/src/actions/downloadAction.spec.ts
index 2d42de5b8b16c..8d5612d982b79 100644
--- a/apps/files/src/actions/downloadAction.spec.ts
+++ b/apps/files/src/actions/downloadAction.spec.ts
@@ -105,7 +105,7 @@ describe('Download action execute tests', () => {
// Silent action
expect(exec).toBe(null)
- expect(link.download).toEqual('')
+ expect(link.download).toBe('foobar.txt')
expect(link.href).toEqual('https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt')
expect(link.click).toHaveBeenCalledTimes(1)
})
@@ -123,7 +123,26 @@ describe('Download action execute tests', () => {
// Silent action
expect(exec).toStrictEqual([null])
- expect(link.download).toEqual('')
+ expect(link.download).toEqual('foobar.txt')
+ expect(link.href).toEqual('https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt')
+ expect(link.click).toHaveBeenCalledTimes(1)
+ })
+
+ test('Download single file with displayname set', async () => {
+ const file = new File({
+ id: 1,
+ source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
+ owner: 'admin',
+ mime: 'text/plain',
+ displayname: 'baz.txt',
+ permissions: Permission.READ,
+ })
+
+ const exec = await action.execBatch!([file], view, '/')
+
+ // Silent action
+ expect(exec).toStrictEqual([null])
+ expect(link.download).toEqual('baz.txt')
expect(link.href).toEqual('https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt')
expect(link.click).toHaveBeenCalledTimes(1)
})
diff --git a/apps/files/src/actions/downloadAction.ts b/apps/files/src/actions/downloadAction.ts
index 19e0b3502fa19..6b0ccbc8cf8a9 100644
--- a/apps/files/src/actions/downloadAction.ts
+++ b/apps/files/src/actions/downloadAction.ts
@@ -8,9 +8,15 @@ import { isDownloadable } from '../utils/permissions'
import ArrowDownSvg from '@mdi/svg/svg/arrow-down.svg?raw'
-const triggerDownload = function(url: string) {
+/**
+ * Trigger downloading a file.
+ *
+ * @param url The url of the asset to download
+ * @param name Optionally the recommended name of the download (browsers might ignore it)
+ */
+function triggerDownload(url: string, name?: string) {
const hiddenElement = document.createElement('a')
- hiddenElement.download = ''
+ hiddenElement.download = name ?? ''
hiddenElement.href = url
hiddenElement.click()
}
@@ -42,7 +48,7 @@ const downloadNodes = function(nodes: Node[]) {
if (nodes.length === 1) {
if (nodes[0].type === FileType.File) {
- return triggerDownload(nodes[0].encodedSource)
+ return triggerDownload(nodes[0].encodedSource, nodes[0].displayname)
} else {
url = new URL(nodes[0].encodedSource)
url.searchParams.append('accept', 'zip')
diff --git a/cypress/e2e/files_sharing/public-share/download-files.cy.ts b/cypress/e2e/files_sharing/public-share/download-files.cy.ts
index a21361bd8b990..09e8d2412b1a8 100644
--- a/cypress/e2e/files_sharing/public-share/download-files.cy.ts
+++ b/cypress/e2e/files_sharing/public-share/download-files.cy.ts
@@ -4,136 +4,169 @@
*/
// @ts-expect-error The package is currently broken - but works...
import { deleteDownloadsFolderBeforeEach } from 'cypress-delete-downloads-folder'
-
+import { createShare, getShareUrl, setupPublicShare, type ShareContext } from './setup-public-share.ts'
+import { getRowForFile, getRowForFileId, triggerActionForFile, triggerActionForFileId } from '../../files/FilesUtils.ts'
import { zipFileContains } from '../../../support/utils/assertions.ts'
-import { getRowForFile, triggerActionForFile } from '../../files/FilesUtils.ts'
-import { getShareUrl, setupPublicShare } from './setup-public-share.ts'
describe('files_sharing: Public share - downloading files', { testIsolation: true }, () => {
- before(() => setupPublicShare())
-
- deleteDownloadsFolderBeforeEach()
-
- beforeEach(() => {
- cy.logout()
- cy.visit(getShareUrl())
- })
-
- it('Can download all files', () => {
- getRowForFile('foo.txt').should('be.visible')
-
- cy.get('[data-cy-files-list]').within(() => {
- cy.findByRole('checkbox', { name: /Toggle selection for all files/i })
- .should('exist')
- .check({ force: true })
-
- // see that two files are selected
- cy.contains('2 selected').should('be.visible')
+ // in general there is no difference except downloading
+ // as file shares have the source of the share token but a different displayname
+ describe('file share', () => {
+ let fileId: number
+
+ before(() => {
+ cy.createRandomUser().then((user) => {
+ const context: ShareContext = { user }
+ cy.uploadContent(user, new Blob(['foo']), 'text/plain', '/file.txt')
+ .then(({ headers }) => { fileId = Number.parseInt(headers['oc-fileid']) })
+ cy.login(user)
+ createShare(context, 'file.txt')
+ .then(() => cy.logout())
+ .then(() => cy.visit(context.url!))
+ })
+ })
- // click download
- cy.findByRole('button', { name: 'Download (selected)' })
+ it('can download the file', () => {
+ getRowForFileId(fileId)
.should('be.visible')
- .click()
-
- // check a file is downloaded
+ .find('[data-cy-files-list-row-name]')
+ .should((el) => expect(el.text()).to.match(/file\s*\.txt/)) // extension is sparated so there might be a space between
+ triggerActionForFileId(fileId, 'download')
+ // check a file is downloaded with the correct name
const downloadsFolder = Cypress.config('downloadsFolder')
- cy.readFile(`${downloadsFolder}/download.zip`, null, { timeout: 15000 })
+ cy.readFile(`${downloadsFolder}/file.txt`, 'utf-8', { timeout: 15000 })
.should('exist')
- .and('have.length.gt', 30)
- // Check all files are included
- .and(zipFileContains([
- 'foo.txt',
- 'subfolder/',
- 'subfolder/bar.txt',
- ]))
+ .and('have.length.gt', 5)
+ .and('contain', 'foo')
})
})
- it('Can download selected files', () => {
- getRowForFile('subfolder')
- .should('be.visible')
+ describe('folder share', () => {
+ before(() => setupPublicShare())
- cy.get('[data-cy-files-list]').within(() => {
- getRowForFile('subfolder')
- .findByRole('checkbox')
- .check({ force: true })
+ deleteDownloadsFolderBeforeEach()
+
+ beforeEach(() => {
+ cy.logout()
+ cy.visit(getShareUrl())
+ })
- // see that two files are selected
- cy.contains('1 selected').should('be.visible')
+ it('Can download all files', () => {
+ getRowForFile('foo.txt').should('be.visible')
+
+ cy.get('[data-cy-files-list]').within(() => {
+ cy.findByRole('checkbox', { name: /Toggle selection for all files/i })
+ .should('exist')
+ .check({ force: true })
+
+ // see that two files are selected
+ cy.contains('2 selected').should('be.visible')
+
+ // click download
+ cy.findByRole('button', { name: 'Download (selected)' })
+ .should('be.visible')
+ .click()
+
+ // check a file is downloaded
+ const downloadsFolder = Cypress.config('downloadsFolder')
+ cy.readFile(`${downloadsFolder}/download.zip`, null, { timeout: 15000 })
+ .should('exist')
+ .and('have.length.gt', 30)
+ // Check all files are included
+ .and(zipFileContains([
+ 'foo.txt',
+ 'subfolder/',
+ 'subfolder/bar.txt',
+ ]))
+ })
+ })
- // click download
- cy.findByRole('button', { name: 'Download (selected)' })
+ it('Can download selected files', () => {
+ getRowForFile('subfolder')
.should('be.visible')
- .click()
- // check a file is downloaded
- const downloadsFolder = Cypress.config('downloadsFolder')
- cy.readFile(`${downloadsFolder}/subfolder.zip`, null, { timeout: 15000 })
- .should('exist')
- .and('have.length.gt', 30)
- // Check all files are included
- .and(zipFileContains([
- 'subfolder/',
- 'subfolder/bar.txt',
- ]))
+ cy.get('[data-cy-files-list]').within(() => {
+ getRowForFile('subfolder')
+ .findByRole('checkbox')
+ .check({ force: true })
+
+ // see that two files are selected
+ cy.contains('1 selected').should('be.visible')
+
+ // click download
+ cy.findByRole('button', { name: 'Download (selected)' })
+ .should('be.visible')
+ .click()
+
+ // check a file is downloaded
+ const downloadsFolder = Cypress.config('downloadsFolder')
+ cy.readFile(`${downloadsFolder}/subfolder.zip`, null, { timeout: 15000 })
+ .should('exist')
+ .and('have.length.gt', 30)
+ // Check all files are included
+ .and(zipFileContains([
+ 'subfolder/',
+ 'subfolder/bar.txt',
+ ]))
+ })
})
- })
- it('Can download folder by action', () => {
- getRowForFile('subfolder')
- .should('be.visible')
-
- cy.get('[data-cy-files-list]').within(() => {
- triggerActionForFile('subfolder', 'download')
+ it('Can download folder by action', () => {
+ getRowForFile('subfolder')
+ .should('be.visible')
- // check a file is downloaded
- const downloadsFolder = Cypress.config('downloadsFolder')
- cy.readFile(`${downloadsFolder}/subfolder.zip`, null, { timeout: 15000 })
- .should('exist')
- .and('have.length.gt', 30)
- // Check all files are included
- .and(zipFileContains([
- 'subfolder/',
- 'subfolder/bar.txt',
- ]))
+ cy.get('[data-cy-files-list]').within(() => {
+ triggerActionForFile('subfolder', 'download')
+
+ // check a file is downloaded
+ const downloadsFolder = Cypress.config('downloadsFolder')
+ cy.readFile(`${downloadsFolder}/subfolder.zip`, null, { timeout: 15000 })
+ .should('exist')
+ .and('have.length.gt', 30)
+ // Check all files are included
+ .and(zipFileContains([
+ 'subfolder/',
+ 'subfolder/bar.txt',
+ ]))
+ })
})
- })
- it('Can download file by action', () => {
- getRowForFile('foo.txt')
- .should('be.visible')
+ it('Can download file by action', () => {
+ getRowForFile('foo.txt')
+ .should('be.visible')
- cy.get('[data-cy-files-list]').within(() => {
- triggerActionForFile('foo.txt', 'download')
+ cy.get('[data-cy-files-list]').within(() => {
+ triggerActionForFile('foo.txt', 'download')
- // check a file is downloaded
- const downloadsFolder = Cypress.config('downloadsFolder')
- cy.readFile(`${downloadsFolder}/foo.txt`, 'utf-8', { timeout: 15000 })
- .should('exist')
- .and('have.length.gt', 5)
- .and('contain', 'foo')
+ // check a file is downloaded
+ const downloadsFolder = Cypress.config('downloadsFolder')
+ cy.readFile(`${downloadsFolder}/foo.txt`, 'utf-8', { timeout: 15000 })
+ .should('exist')
+ .and('have.length.gt', 5)
+ .and('contain', 'foo')
+ })
})
- })
- it('Can download file by selection', () => {
- getRowForFile('foo.txt')
- .should('be.visible')
-
- cy.get('[data-cy-files-list]').within(() => {
+ it('Can download file by selection', () => {
getRowForFile('foo.txt')
- .findByRole('checkbox')
- .check({ force: true })
-
- cy.findByRole('button', { name: 'Download (selected)' })
- .click()
+ .should('be.visible')
- // check a file is downloaded
- const downloadsFolder = Cypress.config('downloadsFolder')
- cy.readFile(`${downloadsFolder}/foo.txt`, 'utf-8', { timeout: 15000 })
- .should('exist')
- .and('have.length.gt', 5)
- .and('contain', 'foo')
+ cy.get('[data-cy-files-list]').within(() => {
+ getRowForFile('foo.txt')
+ .findByRole('checkbox')
+ .check({ force: true })
+
+ cy.findByRole('button', { name: 'Download (selected)' })
+ .click()
+
+ // check a file is downloaded
+ const downloadsFolder = Cypress.config('downloadsFolder')
+ cy.readFile(`${downloadsFolder}/foo.txt`, 'utf-8', { timeout: 15000 })
+ .should('exist')
+ .and('have.length.gt', 5)
+ .and('contain', 'foo')
+ })
})
})
})
diff --git a/cypress/e2e/files_sharing/public-share/setup-public-share.ts b/cypress/e2e/files_sharing/public-share/setup-public-share.ts
index ac40d318592f0..42dedc77183a8 100644
--- a/cypress/e2e/files_sharing/public-share/setup-public-share.ts
+++ b/cypress/e2e/files_sharing/public-share/setup-public-share.ts
@@ -79,9 +79,13 @@ function checkExpirationDateState(enforced: boolean, hasDefault: boolean) {
cy.get('input[data-cy-files-sharing-expiration-date-input]')
.invoke('val')
.then((val) => {
+ // eslint-disable-next-line no-unused-expressions
+ expect(val).to.not.be.undefined
+
+ const inputDate = new Date(typeof val === 'number' ? val : String(val))
const expectedDate = new Date()
expectedDate.setDate(expectedDate.getDate() + 2)
- expect(new Date(val).toDateString()).to.eq(expectedDate.toDateString())
+ expect(inputDate.toDateString()).to.eq(expectedDate.toDateString())
})
}