Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.js8
-rw-r--r--jest.config.js5
-rw-r--r--package-lock.json16
-rw-r--r--package.json5
-rw-r--r--src/store/fileUploadStore.spec.js361
5 files changed, 390 insertions, 5 deletions
diff --git a/.eslintrc.js b/.eslintrc.js
index 949fe0148..663d66a9f 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,4 +2,12 @@ module.exports = {
extends: [
'@nextcloud'
],
+ overrides: [
+ {
+ 'files': ['**/*.spec.js'],
+ 'rules': {
+ 'node/no-unpublished-import': 0
+ }
+ }
+ ]
}
diff --git a/jest.config.js b/jest.config.js
index c8a15b8ef..faa2f2657 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -26,7 +26,10 @@ module.exports = {
testMatch: ['<rootDir>/src/**/*.(spec|test).(ts|js)'],
resetMocks: false,
setupFiles: ['jest-localstorage-mock'],
- setupFilesAfterEnv: ['<rootDir>/src/test-setup.js'],
+ setupFilesAfterEnv: [
+ '<rootDir>/src/test-setup.js',
+ 'jest-mock-console/dist/setupTestFramework.js',
+ ],
transform: {
// process `*.js` files with `babel-jest`
'.*\\.(js)$': 'babel-jest',
diff --git a/package-lock.json b/package-lock.json
index 52c2d0f99..129af354f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4880,7 +4880,8 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
- "dev": true
+ "dev": true,
+ "optional": true
},
"find-cache-dir": {
"version": "3.3.1",
@@ -5167,6 +5168,7 @@
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.2.0.tgz",
"integrity": "sha512-TitGhqSQ61RJljMmhIGvfWzJ2zk9m1Qug049Ugml6QP3t0e95o0XJjk29roNEiPKJQBEi8Ord5hFuSuELzSp8Q==",
"dev": true,
+ "optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
@@ -5178,6 +5180,7 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"dev": true,
+ "optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -5187,13 +5190,15 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
+ "dev": true,
+ "optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
+ "optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@@ -5205,6 +5210,7 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "optional": true,
"requires": {
"has-flag": "^4.0.0"
}
@@ -13607,6 +13613,12 @@
"synchronous-promise": "^2.0.15"
}
},
+ "jest-mock-console": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/jest-mock-console/-/jest-mock-console-1.1.0.tgz",
+ "integrity": "sha512-9/6a0nXw+Iqq8U0LPpR5GrzpkGYFasD4TPNItKuNP36fUN6/rqcUtcNvzFiq2AssfR0PtSTIzrIHdKOipUxe3Q==",
+ "dev": true
+ },
"jest-pnp-resolver": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
diff --git a/package.json b/package.json
index e1bfe49db..642fc570f 100644
--- a/package.json
+++ b/package.json
@@ -6,8 +6,8 @@
"author": "Joas Schilling <coding@schilljs.com>",
"scripts": {
"build": "NODE_ENV=production webpack --progress --config webpack.prod.js",
- "test:unit": "vue-cli-service test:unit",
- "test:watch": "vue-cli-service test:unit --watchAll",
+ "test:unit": "TZ=UTC vue-cli-service test:unit",
+ "test:watch": "TZ=UTC vue-cli-service test:unit --watchAll",
"lint": "eslint --ext .js,.vue src",
"dev": "NODE_ENV=development webpack --config webpack.dev.js",
"lint:fix": "eslint --ext .js,.vue src --fix",
@@ -88,6 +88,7 @@
"eslint-plugin-vue": "^5.2.3",
"jest-localstorage-mock": "^2.4.10",
"jest-mock-axios": "^4.4.0",
+ "jest-mock-console": "^1.1.0",
"node-sass": "^5.0.0",
"regenerator-runtime": "^0.13.7",
"sass-loader": "^10.1.1",
diff --git a/src/store/fileUploadStore.spec.js b/src/store/fileUploadStore.spec.js
new file mode 100644
index 000000000..076fa20ed
--- /dev/null
+++ b/src/store/fileUploadStore.spec.js
@@ -0,0 +1,361 @@
+import mockConsole from 'jest-mock-console'
+import Vuex from 'vuex'
+import { cloneDeep } from 'lodash'
+import { createLocalVue } from '@vue/test-utils'
+
+import client from '../services/DavClient'
+import { findUniquePath, getFileExtension } from '../utils/fileUpload'
+import { shareFile } from '../services/filesSharingServices'
+import { setAttachmentFolder } from '../services/settingsService'
+import { showError } from '@nextcloud/dialogs'
+
+import fileUploadStore from './fileUploadStore'
+
+jest.mock('../services/DavClient')
+jest.mock('./helper')
+jest.mock('../utils/fileUpload', () => ({
+ findUniquePath: jest.fn(),
+ getFileExtension: jest.fn(),
+}))
+jest.mock('../services/filesSharingServices', () => ({
+ shareFile: jest.fn(),
+}))
+jest.mock('../services/settingsService', () => ({
+ setAttachmentFolder: jest.fn(),
+}))
+jest.mock('@nextcloud/dialogs', () => ({
+ showError: jest.fn(),
+}))
+
+describe('fileUploadStore', () => {
+ let localVue = null
+ let storeConfig = null
+ let store = null
+ let mockedActions = null
+
+ beforeEach(() => {
+ let temporaryMessageCount = 0
+
+ localVue = createLocalVue()
+ localVue.use(Vuex)
+
+ mockedActions = {
+ createTemporaryMessage: jest.fn()
+ .mockImplementation((context, { file, index, uploadId, localUrl, token }) => {
+ temporaryMessageCount += 1
+ return {
+ id: temporaryMessageCount,
+ referenceId: 'reference-id-' + temporaryMessageCount,
+ token,
+ messageParameters: {
+ file: {
+ uploadId,
+ index,
+ token,
+ localUrl,
+ file: file,
+ },
+ },
+ }
+ }),
+ addTemporaryMessage: jest.fn(),
+ markTemporaryMessageAsFailed: jest.fn(),
+ }
+
+ global.URL.createObjectURL = jest.fn().mockImplementation((file) => 'local-url:' + file.name)
+ global.OC.MimeType = {
+ getIconUrl: jest.fn().mockImplementation((type) => 'icon-url:' + type),
+ }
+
+ storeConfig = cloneDeep(fileUploadStore)
+ storeConfig.actions = Object.assign(storeConfig.actions, mockedActions)
+ storeConfig.getters.getUserId = jest.fn().mockReturnValue(() => 'current-user')
+ })
+
+ afterEach(() => {
+ jest.clearAllMocks()
+ })
+
+ describe('uploading', () => {
+ let restoreConsole
+
+ beforeEach(() => {
+ storeConfig.getters.getAttachmentFolder = jest.fn().mockReturnValue(() => '/Talk')
+ store = new Vuex.Store(storeConfig)
+ restoreConsole = mockConsole(['error', 'debug'])
+ })
+
+ afterEach(() => {
+ restoreConsole()
+ })
+
+ test('initialises upload for given files', async() => {
+ const files = [
+ {
+ name: 'pngimage.png',
+ type: 'image/png',
+ size: 123,
+ lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
+ },
+ {
+ name: 'jpgimage.jpg',
+ type: 'image/jpeg',
+ size: 456,
+ lastModified: Date.UTC(2021, 3, 26, 15, 30, 0),
+ },
+ {
+ name: 'textfile.txt',
+ type: 'text/plain',
+ size: 111,
+ lastModified: Date.UTC(2021, 3, 25, 15, 30, 0),
+ },
+ ]
+ await store.dispatch('initialiseUpload', {
+ uploadId: 'upload-id1',
+ token: 'XXTOKENXX',
+ files,
+ })
+
+ const uploads = store.getters.getInitialisedUploads('upload-id1')
+ expect(Object.keys(uploads).length).toBe(3)
+
+ for (let i = 0; i < files.length; i++) {
+ expect(mockedActions.createTemporaryMessage.mock.calls[i][1].text).toBe('{file}')
+ expect(mockedActions.createTemporaryMessage.mock.calls[i][1].uploadId).toBe('upload-id1')
+ expect(mockedActions.createTemporaryMessage.mock.calls[i][1].index).toBeDefined()
+ expect(mockedActions.createTemporaryMessage.mock.calls[i][1].file).toBe(files[i])
+ expect(mockedActions.createTemporaryMessage.mock.calls[i][1].token).toBe('XXTOKENXX')
+ }
+
+ expect(mockedActions.createTemporaryMessage.mock.calls[0][1].localUrl).toBe('local-url:pngimage.png')
+ expect(mockedActions.createTemporaryMessage.mock.calls[1][1].localUrl).toBe('local-url:jpgimage.jpg')
+ expect(mockedActions.createTemporaryMessage.mock.calls[2][1].localUrl).toBe('icon-url:text/plain')
+ })
+
+ test('performs upload by uploading then sharing', async() => {
+ const files = [
+ {
+ name: 'pngimage.png',
+ type: 'image/png',
+ size: 123,
+ lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
+ },
+ {
+ name: 'textfile.txt',
+ type: 'text/plain',
+ size: 111,
+ lastModified: Date.UTC(2021, 3, 25, 15, 30, 0),
+ },
+ ]
+
+ await store.dispatch('initialiseUpload', {
+ uploadId: 'upload-id1',
+ token: 'XXTOKENXX',
+ files,
+ })
+
+ expect(store.getters.currentUploadId).toBe('upload-id1')
+
+ findUniquePath
+ .mockResolvedValueOnce('/Talk/' + files[0].name + 'uniq')
+ .mockResolvedValueOnce('/Talk/' + files[1].name + 'uniq')
+ client.putFileContents.mockResolvedValue()
+ shareFile.mockResolvedValue()
+
+ await store.dispatch('uploadFiles', 'upload-id1')
+
+ expect(client.putFileContents).toHaveBeenCalledTimes(2)
+ expect(shareFile).toHaveBeenCalledTimes(2)
+
+ for (let i = 0; i < files.length; i++) {
+ expect(findUniquePath).toHaveBeenCalledWith(client, '/files/current-user', '/Talk/' + files[i].name)
+ expect(client.putFileContents.mock.calls[i][0]).toBe('/files/current-user/Talk/' + files[i].name + 'uniq')
+ expect(client.putFileContents.mock.calls[i][1]).toBe(files[i])
+
+ expect(shareFile.mock.calls[i][0]).toBe('//Talk/' + files[i].name + 'uniq')
+ expect(shareFile.mock.calls[i][1]).toBe('XXTOKENXX')
+ expect(shareFile.mock.calls[i][2]).toBe('reference-id-' + (i + 1))
+ }
+
+ expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(2)
+ expect(store.getters.currentUploadId).not.toBeDefined()
+ })
+
+ test('marks temporary message as failed in case of upload error', async() => {
+ const files = [
+ {
+ name: 'pngimage.png',
+ type: 'image/png',
+ size: 123,
+ lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
+ },
+ ]
+
+ await store.dispatch('initialiseUpload', {
+ uploadId: 'upload-id1',
+ token: 'XXTOKENXX',
+ files,
+ })
+
+ findUniquePath
+ .mockResolvedValueOnce('/Talk/' + files[0].name + 'uniq')
+ client.putFileContents.mockRejectedValueOnce({
+ response: {
+ status: 403,
+ },
+ })
+
+ await store.dispatch('uploadFiles', 'upload-id1')
+
+ expect(client.putFileContents).toHaveBeenCalledTimes(1)
+ expect(shareFile).not.toHaveBeenCalled()
+
+ expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(1)
+ expect(mockedActions.markTemporaryMessageAsFailed).toHaveBeenCalledTimes(1)
+ expect(mockedActions.markTemporaryMessageAsFailed.mock.calls[0][1].message.referenceId).toBe('reference-id-1')
+ expect(mockedActions.markTemporaryMessageAsFailed.mock.calls[0][1].reason).toBe('failed-upload')
+ expect(showError).toHaveBeenCalled()
+ expect(console.error).toHaveBeenCalled()
+ })
+
+ test('marks temporary message as failed in case of sharing error', async() => {
+ const files = [
+ {
+ name: 'pngimage.png',
+ type: 'image/png',
+ size: 123,
+ lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
+ },
+ ]
+
+ await store.dispatch('initialiseUpload', {
+ uploadId: 'upload-id1',
+ token: 'XXTOKENXX',
+ files,
+ })
+
+ findUniquePath
+ .mockResolvedValueOnce('/Talk/' + files[0].name + 'uniq')
+ client.putFileContents.mockResolvedValue()
+ shareFile.mockRejectedValueOnce({
+ response: {
+ status: 403,
+ },
+ })
+
+ await store.dispatch('uploadFiles', 'upload-id1')
+
+ expect(client.putFileContents).toHaveBeenCalledTimes(1)
+ expect(shareFile).toHaveBeenCalledTimes(1)
+
+ expect(mockedActions.addTemporaryMessage).toHaveBeenCalledTimes(1)
+ expect(mockedActions.markTemporaryMessageAsFailed).toHaveBeenCalledTimes(1)
+ expect(mockedActions.markTemporaryMessageAsFailed.mock.calls[0][1].message.referenceId).toBe('reference-id-1')
+ expect(mockedActions.markTemporaryMessageAsFailed.mock.calls[0][1].reason).toBe('failed-share')
+ expect(showError).toHaveBeenCalled()
+ expect(console.error).toHaveBeenCalled()
+ })
+
+ test('removes file from selection', async() => {
+ const files = [
+ {
+ name: 'pngimage.png',
+ type: 'image/png',
+ size: 123,
+ lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
+ },
+ {
+ name: 'textfile.txt',
+ type: 'text/plain',
+ size: 111,
+ lastModified: Date.UTC(2021, 3, 25, 15, 30, 0),
+ },
+ ]
+
+ await store.dispatch('initialiseUpload', {
+ uploadId: 'upload-id1',
+ token: 'XXTOKENXX',
+ files,
+ })
+
+ // temporary message mock uses incremental id
+ await store.dispatch('removeFileFromSelection', 2)
+
+ const uploads = store.getters.getInitialisedUploads('upload-id1')
+ expect(Object.keys(uploads).length).toBe(1)
+
+ expect(Object.values(uploads)[0].file).toBe(files[0])
+ })
+
+ test('discard an entire upload', async() => {
+ const files = [
+ {
+ name: 'pngimage.png',
+ type: 'image/png',
+ size: 123,
+ lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
+ },
+ {
+ name: 'textfile.txt',
+ type: 'text/plain',
+ size: 111,
+ lastModified: Date.UTC(2021, 3, 25, 15, 30, 0),
+ },
+ ]
+
+ await store.dispatch('initialiseUpload', {
+ uploadId: 'upload-id1',
+ token: 'XXTOKENXX',
+ files,
+ })
+
+ await store.dispatch('discardUpload', 'upload-id1')
+
+ const uploads = store.getters.getInitialisedUploads('upload-id1')
+ expect(uploads).toStrictEqual({})
+
+ expect(store.getters.currentUploadId).not.toBeDefined()
+ })
+
+ test('autorenames files using timestamps when requested', async() => {
+ const files = [
+ {
+ name: 'pngimage.png',
+ type: 'image/png',
+ size: 123,
+ lastModified: Date.UTC(2021, 3, 27, 15, 30, 0),
+ },
+ {
+ name: 'textfile.txt',
+ type: 'text/plain',
+ size: 111,
+ lastModified: Date.UTC(2021, 3, 25, 15, 30, 0),
+ },
+ ]
+
+ getFileExtension
+ .mockReturnValueOnce('.png')
+ .mockReturnValueOnce('.txt')
+
+ await store.dispatch('initialiseUpload', {
+ uploadId: 'upload-id1',
+ token: 'XXTOKENXX',
+ files,
+ rename: true,
+ })
+
+ expect(files[0].newName).toBe('20210427_153000.png')
+ expect(files[1].newName).toBe('20210425_153000.txt')
+ })
+ })
+
+ test('set attachment folder', async() => {
+ store = new Vuex.Store(storeConfig)
+
+ setAttachmentFolder.mockResolvedValue()
+ await store.dispatch('setAttachmentFolder', '/Talk-another')
+
+ expect(setAttachmentFolder).toHaveBeenCalledWith('/Talk-another')
+ expect(store.getters.getAttachmentFolder()).toBe('/Talk-another')
+ })
+})