Browse Source

Added file upload store tests

Signed-off-by: Vincent Petry <vincent@nextcloud.com>
pull/5522/head
Vincent Petry 5 years ago
parent
commit
a41b98045d
No known key found for this signature in database GPG Key ID: E055D6A4D513575C
  1. 8
      .eslintrc.js
  2. 5
      jest.config.js
  3. 16
      package-lock.json
  4. 5
      package.json
  5. 361
      src/store/fileUploadStore.spec.js

8
.eslintrc.js

@ -2,4 +2,12 @@ module.exports = {
extends: [
'@nextcloud'
],
overrides: [
{
'files': ['**/*.spec.js'],
'rules': {
'node/no-unpublished-import': 0
}
}
]
}

5
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',

16
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",

5
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",

361
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')
})
})
Loading…
Cancel
Save