├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── babel.config.js ├── jest.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── src ├── CancelButton.vue ├── DeleteButton.vue ├── Filename.vue ├── Filesize.vue ├── PauseResumeButton.vue ├── ProgressBar.vue ├── RetryButton.vue ├── Status.vue └── renderless │ ├── CancelButton.vue │ ├── DeleteButton.vue │ ├── FileProgressBar.vue │ ├── Filename.vue │ ├── Filesize.vue │ ├── PauseResumeButton.vue │ ├── RetryButton.vue │ ├── Status.vue │ └── TotalProgressBar.vue ├── tests └── unit │ ├── .eslintrc.js │ ├── CancelButton.spec.js │ ├── DeleteButton.spec.js │ ├── Filename.spec.js │ ├── Filesize.spec.js │ ├── PauseResumeButton.spec.js │ ├── ProgressBar.spec.js │ ├── RenderlessCancelButton.spec.js │ ├── RenderlessDeleteButton.spec.js │ ├── RenderlessFileProgressBar.spec.js │ ├── RenderlessFilename.spec.js │ ├── RenderlessFilesize.spec.js │ ├── RenderlessPauseResumeButton.spec.js │ ├── RenderlessRetryButton.spec.js │ ├── RenderlessStatus.spec.js │ ├── RenderlessTotalProgressBar.spec.js │ ├── RetryButton.spec.js │ ├── Status.spec.js │ └── __snapshots__ │ ├── CancelButton.spec.js.snap │ ├── DeleteButton.spec.js.snap │ ├── Filename.spec.js.snap │ ├── Filesize.spec.js.snap │ ├── PauseResumeButton.spec.js.snap │ ├── ProgressBar.spec.js.snap │ ├── RetryButton.spec.js.snap │ └── Status.spec.js.snap └── vue.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 4 | env: { 5 | node: true, 6 | }, 7 | 8 | extends: ['plugin:vue/recommended', '@vue/prettier'], 9 | 10 | rules: { 11 | 'no-console': 'off', 12 | 'no-debugger': 'off', 13 | }, 14 | 15 | parserOptions: { 16 | parser: 'babel-eslint', 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "jsxBracketSameLine": true, 4 | "printWidth": 80, 5 | "arrowParens": "avoid", 6 | "semi": false, 7 | "singleQuote": true, 8 | "tabWidth": 2, 9 | "trailingComma": "all", 10 | "useTabs": false, 11 | "overrides": [ 12 | { 13 | "files": ".prettierrc", 14 | "options": { 15 | "parser": "json" 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | cache: 4 | directories: 5 | - $HOME/.npm 6 | - node_modules 7 | 8 | node_js: 9 | - "8" 10 | 11 | install: 12 | - npm install 13 | script: 14 | - npm run test:unit 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![npm](https://img.shields.io/npm/v/vue-fineuploader.svg)](https://www.npmjs.com/package/vue-fineuploader) 4 | [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) 5 | [![Build Status](https://travis-ci.org/FineUploader/vue-fineuploader.svg?branch=master)](https://travis-ci.org/FineUploader/vue-fineuploader) 6 | 7 | Makes using [Fine Uploader](http://fineuploader.com) in a VueJS 2 app simple. Drop-in high-level components for a turn-key UI. Use small focused components to build a more custom UI. 8 | 9 | # ❗️ This is a work in progress refactor ️️❗️ 10 | 11 | ### The current working version is on the v2 branch which is [right here](https://github.com/FineUploader/vue-fineuploader/tree/v2). 12 | 13 | I know I never really finished the v2 and that it's full of bugs. This is why I'm restarting from the ground up. I'm not the developer I was 2 years ago when I made this library and I think that I can now offer something way more powerful that its current state. 14 | 15 | ### What will change 16 | 17 | #### 1. The repo is going to be renamed `vue-fine-uploader` to match the naming convention of FineUploader. 18 | 19 | #### 2. Introduction of renderless components to offer full control over the presentation of the different components. 20 | 21 | To avoid forcing everyone to switch to renderless components a set of opiniated components will be created. 22 | 23 | I'm going to try and make them as accessible as possible. 24 | 25 | #### 3. Unit tests. 26 | 27 | It had no unit test until now, which made it fairly complicated to debug an issue and tests PRs. 28 | 29 | #### Improved documentation with CodeSandbox examples. 30 | 31 | I'm going to rewrite the whole documentation and make it user friendlier (is that even a word?) and create CodeSandbox example instead of plain text inside the README. 32 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/app'], 3 | } 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 6 | 'jest-transform-stub', 7 | '^.+\\.jsx?$': 'babel-jest', 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1', 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)', 15 | ], 16 | testURL: 'http://localhost/', 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-fine-uploader", 3 | "version": "3.0.0-alpha.0", 4 | "private": true, 5 | "license": "MIT", 6 | "description": "VueJS 2 components for using Fine Uploader in a Vue based project.", 7 | "homepage": "https://github.com/FineUploader/vue-fine-uploader#readme", 8 | "repository": { 9 | "url": "https://github.com/FineUploader/vue-fine-uploader.git", 10 | "type": "git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/FineUploader/vue-fine-uploader/issues" 14 | }, 15 | "author": { 16 | "name": "Dieter Stinglhamber", 17 | "email": "elhebert@hotmail.be", 18 | "url": "https://dieterstinglhamber.me/" 19 | }, 20 | "keywords": [ 21 | "file", 22 | "blob", 23 | "file-uploader", 24 | "fine-uploader", 25 | "vue", 26 | "upload", 27 | "widget" 28 | ], 29 | "scripts": { 30 | "serve": "vue-cli-service serve", 31 | "build": "vue-cli-service build", 32 | "lint": "vue-cli-service lint", 33 | "test:unit": "vue-cli-service test:unit" 34 | }, 35 | "peerDependencies": { 36 | "vue": "^2.5" 37 | }, 38 | "dependencies": { 39 | "fine-uploader": "^5.16.2", 40 | "fine-uploader-wrappers": "^1.0.1" 41 | }, 42 | "devDependencies": { 43 | "@vue/cli-plugin-babel": "^3.0.1", 44 | "@vue/cli-plugin-eslint": "^3.0.1", 45 | "@vue/cli-plugin-unit-jest": "^3.0.1", 46 | "@vue/cli-service": "^3.0.1", 47 | "@vue/eslint-config-prettier": "^3.0.1", 48 | "@vue/test-utils": "^1.0.0-beta.20", 49 | "babel-core": "7.0.0-bridge.0", 50 | "babel-jest": "^23.0.1", 51 | "vue": "^2.5.17", 52 | "vue-template-compiler": "^2.5.17" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | } 6 | -------------------------------------------------------------------------------- /src/CancelButton.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 42 | -------------------------------------------------------------------------------- /src/DeleteButton.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 42 | -------------------------------------------------------------------------------- /src/Filename.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 30 | -------------------------------------------------------------------------------- /src/Filesize.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 43 | -------------------------------------------------------------------------------- /src/PauseResumeButton.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 47 | -------------------------------------------------------------------------------- /src/ProgressBar.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 52 | -------------------------------------------------------------------------------- /src/RetryButton.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 41 | -------------------------------------------------------------------------------- /src/Status.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 43 | -------------------------------------------------------------------------------- /src/renderless/CancelButton.vue: -------------------------------------------------------------------------------- 1 | 94 | -------------------------------------------------------------------------------- /src/renderless/DeleteButton.vue: -------------------------------------------------------------------------------- 1 | 106 | -------------------------------------------------------------------------------- /src/renderless/FileProgressBar.vue: -------------------------------------------------------------------------------- 1 | 83 | -------------------------------------------------------------------------------- /src/renderless/Filename.vue: -------------------------------------------------------------------------------- 1 | 48 | -------------------------------------------------------------------------------- /src/renderless/Filesize.vue: -------------------------------------------------------------------------------- 1 | 79 | -------------------------------------------------------------------------------- /src/renderless/PauseResumeButton.vue: -------------------------------------------------------------------------------- 1 | 141 | -------------------------------------------------------------------------------- /src/renderless/RetryButton.vue: -------------------------------------------------------------------------------- 1 | 100 | -------------------------------------------------------------------------------- /src/renderless/Status.vue: -------------------------------------------------------------------------------- 1 | 49 | -------------------------------------------------------------------------------- /src/renderless/TotalProgressBar.vue: -------------------------------------------------------------------------------- 1 | 74 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true, 4 | }, 5 | rules: { 6 | 'import/no-extraneous-dependencies': 'off', 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /tests/unit/CancelButton.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import CancelButton from '@/CancelButton.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | import { status } from 'fine-uploader/lib/core/all' 5 | 6 | describe('CancelButton.vue', () => { 7 | let wrapper 8 | let uploader 9 | 10 | const sampleBlob = new Blob(['hi!'], { type: 'text/plain' }) 11 | 12 | beforeEach(done => { 13 | uploader = new FineUploader({ options: { autoUpload: false } }) 14 | uploader.on('submitted', done) 15 | uploader.methods.addFiles(sampleBlob) 16 | 17 | wrapper = mount(CancelButton, { 18 | propsData: { id: 0, uploader }, 19 | }) 20 | }) 21 | 22 | it('renders the button for a submitted file', () => { 23 | expect(wrapper).toMatchSnapshot() 24 | expect(wrapper.html()).toBeDefined() 25 | expect(wrapper.attributes().disabled).toBeUndefined() 26 | }) 27 | 28 | it('cancels the upload if clicked', done => { 29 | wrapper.trigger('click') 30 | 31 | wrapper.vm.$nextTick(() => { 32 | expect(uploader.methods.getUploads()[0].status).toMatch(status.CANCELED) 33 | 34 | done() 35 | }) 36 | }) 37 | 38 | it('removes the button by default if the file can no longer be canceled', done => { 39 | uploader.methods.cancel(0) 40 | 41 | wrapper.vm.$nextTick(() => { 42 | expect(wrapper).toMatchSnapshot() 43 | expect(wrapper.isEmpty()).toBe(true) 44 | 45 | done() 46 | }) 47 | }) 48 | 49 | it('disables the button if requested when the file can no longer be canceled', done => { 50 | wrapper.setProps({ onlyRenderIfCancelable: false }) 51 | uploader.methods.cancel(0) 52 | 53 | wrapper.vm.$nextTick(() => { 54 | expect(wrapper).toMatchSnapshot() 55 | expect(wrapper.html()).toBeDefined() 56 | expect(wrapper.attributes().disabled).toBeDefined() 57 | 58 | done() 59 | }) 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /tests/unit/DeleteButton.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import DeleteButton from '@/DeleteButton.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | import { status } from 'fine-uploader/lib/core/all' 5 | 6 | describe('DeleteButton.vue', () => { 7 | let wrapper 8 | let renderless 9 | let uploader 10 | 11 | beforeEach(() => { 12 | uploader = new FineUploader({ options: {} }) 13 | 14 | wrapper = mount(DeleteButton, { 15 | propsData: { id: 0, uploader }, 16 | }) 17 | 18 | renderless = wrapper.vm.$children[0] 19 | }) 20 | 21 | it('renders the button for a successfully uploaded file', () => { 22 | renderless.onStatusChange(0, null, status.UPLOAD_SUCCESSFUL) 23 | 24 | expect(wrapper).toMatchSnapshot() 25 | expect(wrapper.html()).toBeDefined() 26 | expect(wrapper.attributes().disabled).toBeUndefined() 27 | }) 28 | 29 | it('deletes the file if clicked', () => { 30 | uploader.methods.deleteFile = jest.fn() 31 | 32 | renderless.onStatusChange(0, null, status.UPLOAD_SUCCESSFUL) 33 | 34 | wrapper.trigger('click') 35 | expect(uploader.methods.deleteFile).toHaveBeenCalled() 36 | }) 37 | 38 | it('removes the button if the file can no longer be deleted', () => { 39 | renderless.onStatusChange(0, null, status.DELETED) 40 | 41 | expect(wrapper).toMatchSnapshot() 42 | expect(wrapper.isEmpty()).toBe(true) 43 | }) 44 | 45 | it('disabled the button while the delete is in progress', () => { 46 | renderless.onStatusChange(0, null, status.DELETING) 47 | 48 | expect(wrapper).toMatchSnapshot() 49 | expect(wrapper.attributes().disabled).toBeDefined() 50 | }) 51 | 52 | it('disables the button if requested when the file can no longer be deleted', () => { 53 | wrapper.setProps({ onlyRenderIfDeletable: false }) 54 | renderless.onStatusChange(0, null, status.DELETED) 55 | 56 | expect(wrapper).toMatchSnapshot() 57 | expect(wrapper.html()).toBeDefined() 58 | expect(wrapper.attributes().disabled).toBeDefined() 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /tests/unit/Filename.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import Filename from '@/Filename.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('Filename.vue', () => { 6 | let uploader 7 | let wrapper 8 | 9 | const sampleBlob = new Blob(['hi!'], { type: 'text/plain' }) 10 | const sampleBlobWrapper = { blob: sampleBlob, name: 'test' } 11 | 12 | beforeEach(done => { 13 | uploader = new FineUploader({ options: { autoUpload: false } }) 14 | uploader.on('submitted', done) 15 | uploader.methods.addFiles(sampleBlobWrapper) 16 | 17 | wrapper = mount(Filename, { 18 | propsData: { id: 0, uploader }, 19 | }) 20 | }) 21 | 22 | it('renders initial filename', () => { 23 | expect(wrapper).toMatchSnapshot() 24 | expect(wrapper.text()).toBe('test') 25 | }) 26 | 27 | it('updates filename on setName', () => { 28 | uploader.methods.setName(0, 'new-name') 29 | 30 | expect(wrapper).toMatchSnapshot() 31 | expect(wrapper.text()).toBe('new-name') 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /tests/unit/Filesize.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import Filesize from '@/Filesize.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('Filesize.vue', () => { 6 | const nativeObjectToString = Object.prototype.toString 7 | 8 | beforeEach(() => { 9 | Object.prototype.toString = function() { 10 | if (this && this.type === 'fakeBlob') { 11 | return '[object Blob]' 12 | } 13 | 14 | return nativeObjectToString.apply(this, arguments) 15 | } 16 | }) 17 | 18 | afterEach(() => { 19 | Object.prototype.toString = nativeObjectToString 20 | }) 21 | 22 | it('renders an empty filesize component if size is not known initially', () => { 23 | const uploader = new FineUploader({ options: { autoUpload: false } }) 24 | uploader.methods.addFiles({ type: 'fakeBlob' }) 25 | 26 | const wrapper = mount(Filesize, { 27 | propsData: { id: 0, uploader }, 28 | }) 29 | 30 | expect(wrapper).toMatchSnapshot() 31 | expect(wrapper.text()).toBe('') 32 | }) 33 | 34 | it('renders formatted file size for various sized files', () => { 35 | const uploader = new FineUploader({ options: { autoUpload: false } }) 36 | 37 | uploader.methods.addFiles([ 38 | { size: 1100, type: 'fakeBlob' }, 39 | { size: 1100000, type: 'fakeBlob' }, 40 | { size: 1100000000, type: 'fakeBlob' }, 41 | { size: 1100000000000, type: 'fakeBlob' }, 42 | ]) 43 | 44 | const expectedSizes = [ 45 | { size: '1.10', units: 'KB' }, 46 | { size: '1.10', units: 'MB' }, 47 | { size: '1.10', units: 'GB' }, 48 | { size: '1.10', units: 'TB' }, 49 | ] 50 | 51 | expectedSizes.forEach((expectedSize, id) => { 52 | const wrapper = mount(Filesize, { 53 | propsData: { id, uploader }, 54 | }) 55 | 56 | expect(wrapper).toMatchSnapshot() 57 | expect(wrapper.text()).toBe(`${expectedSize.size} ${expectedSize.units}`) 58 | }) 59 | }) 60 | 61 | it('renders file size at upload time for scaled blobs', () => { 62 | const uploader = new FineUploader({ 63 | options: { 64 | autoUpload: false, 65 | scaling: { sizes: [{ name: 'test', maxSize: 100 }] }, 66 | }, 67 | }) 68 | uploader.methods.addFiles({ type: 'fakeBlob' }) 69 | 70 | const wrapper = mount(Filesize, { 71 | propsData: { id: 0, uploader }, 72 | }) 73 | 74 | expect(wrapper).toMatchSnapshot() 75 | expect(wrapper.text()).toBe('') 76 | 77 | uploader.methods.getSize = jest.fn(() => 1) 78 | 79 | const renderless = wrapper.vm.$children[0] 80 | renderless.onUpload(0) 81 | 82 | expect(uploader.methods.getSize).toHaveBeenCalledWith(0) 83 | expect(wrapper).toMatchSnapshot() 84 | expect(wrapper.text()).toEqual('1 B') 85 | }) 86 | }) 87 | -------------------------------------------------------------------------------- /tests/unit/PauseResumeButton.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import PauseResumeButton from '@/PauseResumeButton.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | import { status as STATUSES } from 'fine-uploader/lib/core/all' 5 | 6 | describe('PauseResumeButton.vue', () => { 7 | let wrapper 8 | let renderless 9 | let uploader 10 | 11 | beforeEach(() => { 12 | uploader = new FineUploader({ options: {} }) 13 | 14 | wrapper = mount(PauseResumeButton, { 15 | propsData: { id: 0, uploader }, 16 | }) 17 | 18 | renderless = wrapper.vm.$children[0] 19 | }) 20 | 21 | it("doesn't render the button until the first chunk has been uploaded", () => { 22 | expect(wrapper.isEmpty()).toBe(true) 23 | expect(wrapper).toMatchSnapshot() 24 | 25 | renderless.onUploadChunk(0, null, { partIndex: 3 }) 26 | 27 | expect(wrapper.html()).toBeDefined() 28 | expect(wrapper).toMatchSnapshot() 29 | }) 30 | 31 | it('removes the pause button when the upload is no longer actionable', () => { 32 | renderless.onUploadChunk(0, null, { partIndex: 1 }) 33 | 34 | expect(wrapper.html()).toBeDefined() 35 | expect(wrapper).toMatchSnapshot() 36 | 37 | renderless.onStatusChange(0, null, STATUSES.DELETED) 38 | 39 | expect(wrapper).toMatchSnapshot() 40 | expect(wrapper.html()).toBeUndefined() 41 | }) 42 | 43 | it('disables the button, if requested, when the upload is no longer actionable', () => { 44 | wrapper.setProps({ onlyRenderIfEnabled: false }) 45 | 46 | renderless.onUploadChunk(0, null, { partIndex: 1 }) 47 | 48 | expect(wrapper.html()).toBeDefined() 49 | expect(wrapper).toMatchSnapshot() 50 | 51 | renderless.onStatusChange(0, null, STATUSES.DELETED) 52 | 53 | expect(wrapper).toMatchSnapshot() 54 | expect(wrapper.html()).toBeDefined() 55 | expect(wrapper.attributes().disabled).toBeDefined() 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /tests/unit/ProgressBar.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import ProgressBar from '@/ProgressBar.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('ProgressBar.vue', () => { 6 | it('hides the progress bar by default', () => { 7 | const uploader = new FineUploader({ options: {} }) 8 | let wrapper = mount(ProgressBar, { propsData: { id: 0, uploader } }) 9 | 10 | expect(wrapper.attributes().hidden).toBeDefined() 11 | expect(wrapper).toMatchSnapshot() 12 | 13 | wrapper = mount(ProgressBar, { propsData: { uploader } }) 14 | 15 | expect(wrapper.attributes().hidden).toBeDefined() 16 | expect(wrapper).toMatchSnapshot() 17 | }) 18 | 19 | it('shows the file progress bar before uploading start if specified', () => { 20 | const uploader = new FineUploader({ options: {} }) 21 | let wrapper = mount(ProgressBar, { 22 | propsData: { id: 0, uploader, hidesWhenNotUploading: false }, 23 | }) 24 | 25 | expect(wrapper.attributes().hidden).toBeUndefined() 26 | expect(wrapper).toMatchSnapshot() 27 | }) 28 | 29 | it('shows the total progress bar before uploading start if specified', () => { 30 | const uploader = new FineUploader({ options: {} }) 31 | let wrapper = mount(ProgressBar, { 32 | propsData: { uploader, hidesWhenNotUploading: false }, 33 | }) 34 | 35 | expect(wrapper.attributes().hidden).toBeUndefined() 36 | expect(wrapper).toMatchSnapshot() 37 | }) 38 | 39 | it('show the file progress if an ID is supplied', () => { 40 | const uploader = new FineUploader({ options: {} }) 41 | const wrapper = mount(ProgressBar, { propsData: { id: 3, uploader } }) 42 | const renderless = wrapper.vm.$children[0] 43 | 44 | renderless.onProgress(3, 'foo.jpeg', 100, 1000) 45 | 46 | expect(wrapper).toMatchSnapshot() 47 | expect(wrapper.attributes().style).toBe('width: 10%;') 48 | }) 49 | 50 | it('show the total progress if no ID is supplied', () => { 51 | const uploader = new FineUploader({ options: {} }) 52 | const wrapper = mount(ProgressBar, { propsData: { uploader } }) 53 | const renderless = wrapper.vm.$children[0] 54 | 55 | renderless.onProgress(100, 1000) 56 | 57 | expect(wrapper).toMatchSnapshot() 58 | expect(wrapper.attributes().style).toBe('width: 10%;') 59 | }) 60 | 61 | it('hides total progress bar after all uploads are complete', () => { 62 | const uploader = new FineUploader({ options: {} }) 63 | const wrapper = mount(ProgressBar, { propsData: { uploader } }) 64 | const renderless = wrapper.vm.$children[0] 65 | 66 | // uploading 67 | uploader.methods['getInProgress'] = jest.fn(() => 1) 68 | renderless.onStatusChange( 69 | 0, 70 | uploader.qq.status.QUEUED, 71 | uploader.qq.status.UPLOADING, 72 | ) 73 | 74 | expect(wrapper).toMatchSnapshot() 75 | expect(wrapper.attributes().hidden).toBeUndefined() 76 | 77 | // done uploading 78 | uploader.methods['getInProgress'] = jest.fn(() => 0) 79 | renderless.onStatusChange( 80 | 0, 81 | uploader.qq.status.UPLOADING, 82 | uploader.qq.status.UPLOAD_SUCCESSFUL, 83 | ) 84 | 85 | expect(wrapper).toMatchSnapshot() 86 | expect(wrapper.attributes().hidden).toBeDefined() 87 | }) 88 | 89 | it('hides file progress bar after all upload is complete', () => { 90 | const uploader = new FineUploader({ options: {} }) 91 | const wrapper = mount(ProgressBar, { propsData: { id: 0, uploader } }) 92 | const renderless = wrapper.vm.$children[0] 93 | 94 | // uploading 95 | renderless.onStatusChange( 96 | 0, 97 | uploader.qq.status.QUEUED, 98 | uploader.qq.status.UPLOADING, 99 | ) 100 | 101 | expect(wrapper).toMatchSnapshot() 102 | expect(wrapper.attributes().hidden).toBeUndefined() 103 | 104 | // done uploading 105 | renderless.onStatusChange( 106 | 0, 107 | uploader.qq.status.UPLOADING, 108 | uploader.qq.status.UPLOAD_SUCCESSFUL, 109 | ) 110 | 111 | expect(wrapper).toMatchSnapshot() 112 | expect(wrapper.attributes().hidden).toBeDefined() 113 | }) 114 | 115 | it('shows total progress bar after all uploads are complete if requested', () => { 116 | const uploader = new FineUploader({ options: {} }) 117 | const wrapper = mount(ProgressBar, { 118 | propsData: { uploader, hidesWhenNotUploading: false }, 119 | }) 120 | const renderless = wrapper.vm.$children[0] 121 | 122 | // uploading 123 | uploader.methods['getInProgress'] = jest.fn(() => 1) 124 | renderless.onStatusChange( 125 | 0, 126 | uploader.qq.status.QUEUED, 127 | uploader.qq.status.UPLOADING, 128 | ) 129 | 130 | expect(wrapper).toMatchSnapshot() 131 | expect(wrapper.attributes().hidden).toBeUndefined() 132 | 133 | // done uploading 134 | uploader.methods['getInProgress'] = jest.fn(() => 0) 135 | renderless.onStatusChange( 136 | 0, 137 | uploader.qq.status.UPLOADING, 138 | uploader.qq.status.UPLOAD_SUCCESSFUL, 139 | ) 140 | 141 | expect(wrapper).toMatchSnapshot() 142 | expect(wrapper.attributes().hidden).toBeUndefined() 143 | }) 144 | 145 | it('shows file progress bar after all upload is complete if requested', () => { 146 | const uploader = new FineUploader({ options: {} }) 147 | const wrapper = mount(ProgressBar, { 148 | propsData: { id: 0, uploader, hidesWhenNotUploading: false }, 149 | }) 150 | const renderless = wrapper.vm.$children[0] 151 | 152 | // uploading 153 | renderless.onStatusChange( 154 | 0, 155 | uploader.qq.status.QUEUED, 156 | uploader.qq.status.UPLOADING, 157 | ) 158 | 159 | expect(wrapper).toMatchSnapshot() 160 | expect(wrapper.attributes().hidden).toBeUndefined() 161 | 162 | // done uploading 163 | renderless.onStatusChange( 164 | 0, 165 | uploader.qq.status.UPLOADING, 166 | uploader.qq.status.UPLOAD_SUCCESSFUL, 167 | ) 168 | 169 | expect(wrapper).toMatchSnapshot() 170 | expect(wrapper.attributes().hidden).toBeUndefined() 171 | }) 172 | }) 173 | -------------------------------------------------------------------------------- /tests/unit/RenderlessCancelButton.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import CancelButton from '@/renderless/CancelButton.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | import { status } from 'fine-uploader/lib/core/all' 5 | 6 | describe('renderless/CancelButton.vue', () => { 7 | let wrapper 8 | let uploader 9 | 10 | const sampleBlob = new Blob(['hi!'], { type: 'text/plain' }) 11 | 12 | beforeEach(done => { 13 | uploader = new FineUploader({ options: { autoUpload: false } }) 14 | uploader.on('submitted', done) 15 | uploader.methods.addFiles(sampleBlob) 16 | 17 | wrapper = shallowMount(CancelButton, { 18 | propsData: { id: 0, uploader }, 19 | scopedSlots: { default: slots => slots }, 20 | }) 21 | }) 22 | 23 | it('makes a file cancelable when added', () => { 24 | expect(wrapper.vm.cancelable).toBe(true) 25 | }) 26 | 27 | it('cancels the upload if onClick is triggered', done => { 28 | wrapper.vm.onClick({ preventDefault: jest.fn() }) 29 | 30 | wrapper.vm.$nextTick(() => { 31 | expect(uploader.methods.getUploads()[0].status).toMatch(status.CANCELED) 32 | expect(wrapper.vm.cancelable).toBe(false) 33 | 34 | done() 35 | }) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /tests/unit/RenderlessDeleteButton.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import DeleteButton from '@/renderless/DeleteButton.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | import { status } from 'fine-uploader/lib/core/all' 5 | 6 | describe('renderless/DeleteButton.vue', () => { 7 | let wrapper 8 | let uploader 9 | 10 | beforeEach(() => { 11 | uploader = new FineUploader({ options: {} }) 12 | 13 | wrapper = shallowMount(DeleteButton, { 14 | propsData: { id: 0, uploader }, 15 | scopedSlots: { default: slots => slots }, 16 | }) 17 | }) 18 | 19 | it('makes the file deletable on successfully uploaded', () => { 20 | wrapper.vm.onStatusChange(0, null, status.UPLOAD_SUCCESSFUL) 21 | 22 | expect(wrapper.vm.deletable).toBe(true) 23 | }) 24 | 25 | it('deletes the file if onClick is trigger', () => { 26 | uploader.methods.deleteFile = jest.fn() 27 | 28 | wrapper.vm.onStatusChange(0, null, status.UPLOAD_SUCCESSFUL) 29 | 30 | wrapper.vm.onClick({ preventDefault: jest.fn() }) 31 | expect(uploader.methods.deleteFile).toHaveBeenCalled() 32 | }) 33 | 34 | it('makes the files undeletable if the file can no longer be deleted', () => { 35 | wrapper.vm.onStatusChange(0, null, status.DELETED) 36 | 37 | expect(wrapper.vm.deleting).toBe(false) 38 | expect(wrapper.vm.deletable).toBe(false) 39 | }) 40 | 41 | it('makes the files undeletable while the delete is in progress', () => { 42 | wrapper.vm.onStatusChange(0, null, status.DELETING) 43 | 44 | expect(wrapper.vm.deleting).toBe(true) 45 | expect(wrapper.vm.deletable).toBe(false) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /tests/unit/RenderlessFileProgressBar.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import ProgressBar from '@/renderless/FileProgressBar.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('renderless/FileProgressBar.vue', () => { 6 | it('is not uploading the file on initialisation', () => { 7 | const uploader = new FineUploader({ options: {} }) 8 | let wrapper = shallowMount(ProgressBar, { 9 | propsData: { id: 0, uploader }, 10 | scopedSlots: { default: slots => slots }, 11 | }) 12 | 13 | expect(wrapper.vm.uploading).toBe(false) 14 | }) 15 | 16 | it('keeps track of the file progress during the upload', () => { 17 | const uploader = new FineUploader({ options: {} }) 18 | let wrapper = shallowMount(ProgressBar, { 19 | propsData: { id: 3, uploader }, 20 | scopedSlots: { default: slots => slots }, 21 | }) 22 | 23 | wrapper.vm.onProgress(3, 'foo.jpeg', 100, 1000) 24 | 25 | expect(wrapper.vm.bytesUploaded).toBe(100) 26 | expect(wrapper.vm.totalSize).toBe(1000) 27 | }) 28 | 29 | it('is no longer uploading when upload is finished', () => { 30 | const uploader = new FineUploader({ options: {} }) 31 | const wrapper = shallowMount(ProgressBar, { 32 | propsData: { id: 0, uploader }, 33 | scopedSlots: { default: slots => slots }, 34 | }) 35 | // uploading 36 | wrapper.vm.onStatusChange( 37 | 0, 38 | uploader.qq.status.QUEUED, 39 | uploader.qq.status.UPLOADING, 40 | ) 41 | 42 | expect(wrapper.vm.uploading).toBe(true) 43 | 44 | // done uploading 45 | wrapper.vm.onStatusChange( 46 | 0, 47 | uploader.qq.status.UPLOADING, 48 | uploader.qq.status.UPLOAD_SUCCESSFUL, 49 | ) 50 | 51 | expect(wrapper.vm.uploading).toBe(false) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /tests/unit/RenderlessFilename.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import Filename from '@/renderless/Filename.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('renderless/Filename.vue', () => { 6 | let uploader 7 | let wrapper 8 | 9 | const sampleBlob = new Blob(['hi!'], { type: 'text/plain' }) 10 | const sampleBlobWrapper = { blob: sampleBlob, name: 'test' } 11 | 12 | beforeEach(done => { 13 | uploader = new FineUploader({ options: { autoUpload: false } }) 14 | uploader.on('submitted', done) 15 | uploader.methods.addFiles(sampleBlobWrapper) 16 | 17 | wrapper = shallowMount(Filename, { 18 | propsData: { id: 0, uploader }, 19 | scopedSlots: { default: object => object }, 20 | }) 21 | }) 22 | 23 | it('set initial filename', () => { 24 | expect(wrapper.vm.filename).toBe('test') 25 | }) 26 | 27 | it('update filename on setName', () => { 28 | uploader.methods.setName(0, 'new-name') 29 | 30 | expect(wrapper.vm.filename).toBe('new-name') 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /tests/unit/RenderlessFilesize.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import Filesize from '@/renderless/Filesize.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('renderless/Filesize.vue', () => { 6 | const sampleBlob = new Blob(['hi!'], { type: 'text/plain' }) 7 | const sampleBlobWrapper = { blob: sampleBlob, name: 'test' } 8 | const nativeObjectToString = Object.prototype.toString 9 | 10 | beforeEach(() => { 11 | Object.prototype.toString = function() { 12 | if (this && this.type === 'fakeBlob') { 13 | return '[object Blob]' 14 | } 15 | 16 | return nativeObjectToString.apply(this, arguments) 17 | } 18 | }) 19 | 20 | afterEach(() => { 21 | Object.prototype.toString = nativeObjectToString 22 | }) 23 | 24 | it('renders file size for tiny file using default units/text', () => { 25 | const uploader = new FineUploader({ options: { autoUpload: false } }) 26 | uploader.methods.addFiles(sampleBlobWrapper) 27 | 28 | const wrapper = shallowMount(Filesize, { 29 | propsData: { id: 0, uploader }, 30 | scopedSlots: { default: object => object }, 31 | }) 32 | 33 | expect(wrapper.vm.size).toBe(sampleBlob.size) 34 | }) 35 | 36 | it('renders formatted file size for various sized files', () => { 37 | const uploader = new FineUploader({ options: { autoUpload: false } }) 38 | 39 | uploader.methods.addFiles([ 40 | { size: 1100, type: 'fakeBlob' }, 41 | { size: 1100000, type: 'fakeBlob' }, 42 | { size: 1100000000, type: 'fakeBlob' }, 43 | { size: 1100000000000, type: 'fakeBlob' }, 44 | ]) 45 | 46 | const expectedSizes = [ 47 | { size: '1.10', units: 'kilobyte' }, 48 | { size: '1.10', units: 'megabyte' }, 49 | { size: '1.10', units: 'gigabyte' }, 50 | { size: '1.10', units: 'terabyte' }, 51 | ] 52 | 53 | expectedSizes.forEach((expectedSize, id) => { 54 | const wrapper = shallowMount(Filesize, { 55 | propsData: { id, uploader }, 56 | scopedSlots: { default: object => object }, 57 | }) 58 | 59 | expect(wrapper.vm.formatted).toEqual({ 60 | size: expectedSize.size, 61 | unit: expectedSize.units, 62 | }) 63 | }) 64 | }) 65 | 66 | it('renders file size at upload time for scaled blobs', () => { 67 | const uploader = new FineUploader({ 68 | options: { 69 | autoUpload: false, 70 | scaling: { sizes: [{ name: 'test', maxSize: 100 }] }, 71 | }, 72 | }) 73 | uploader.methods.addFiles({ type: 'fakeBlob' }) 74 | 75 | const wrapper = shallowMount(Filesize, { 76 | propsData: { id: 0, uploader }, 77 | scopedSlots: { default: object => object }, 78 | }) 79 | 80 | expect(wrapper.vm.size).toBe(-1) 81 | 82 | uploader.methods.getSize = jest.fn(() => 1) 83 | wrapper.vm.onUpload(0) 84 | 85 | expect(uploader.methods.getSize).toHaveBeenCalledWith(0) 86 | expect(wrapper.vm.formatted).toEqual({ size: 1, unit: 'byte' }) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /tests/unit/RenderlessPauseResumeButton.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import PauseResumeButton from '@/renderless/PauseResumeButton.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | import { status } from 'fine-uploader/lib/core/all' 5 | 6 | describe('renderless/PauseResumeButton.vue', () => { 7 | let wrapper 8 | let uploader 9 | 10 | beforeEach(() => { 11 | uploader = new FineUploader({ options: {} }) 12 | 13 | wrapper = shallowMount(PauseResumeButton, { 14 | propsData: { id: 0, uploader }, 15 | scopedSlots: { default: object => object }, 16 | }) 17 | }) 18 | 19 | it('pausable is false until the first chunk has been uploaded', () => { 20 | expect(wrapper.vm.pausable).toBe(false) 21 | 22 | wrapper.vm.onUploadChunk(0, null, { partIndex: 3 }) 23 | 24 | expect(wrapper.vm.pausable).toBe(true) 25 | }) 26 | 27 | it('pausable is false when the upload is no longer actionable', () => { 28 | wrapper.vm.onUploadChunk(0, null, { partIndex: 1 }) 29 | 30 | expect(wrapper.vm.pausable).toBe(true) 31 | 32 | wrapper.vm.onStatusChange(0, null, status.DELETED) 33 | 34 | expect(wrapper.vm.pausable).toBe(false) 35 | }) 36 | 37 | it('allows a paused upload to be resumed and then paused again', () => { 38 | wrapper.vm.onUploadChunk(0, null, { partIndex: 7 }) 39 | wrapper.vm.onStatusChange(0, null, status.PAUSED) 40 | 41 | expect(wrapper.vm.pausable).toBe(false) 42 | expect(wrapper.vm.resumable).toBe(true) 43 | 44 | uploader.methods.continueUpload = jest.fn() 45 | wrapper.vm.onClick({ preventDefault: jest.fn() }) 46 | expect(uploader.methods.continueUpload).toHaveBeenCalledWith(0) 47 | 48 | wrapper.vm.onStatusChange(0, null, status.UPLOADING) 49 | 50 | expect(wrapper.vm.pausable).toBe(true) 51 | expect(wrapper.vm.resumable).toBe(false) 52 | 53 | uploader.methods.pauseUpload = jest.fn() 54 | wrapper.vm.onClick({ preventDefault: jest.fn() }) 55 | expect(uploader.methods.pauseUpload).toHaveBeenCalledWith(0) 56 | 57 | wrapper.vm.onStatusChange(0, null, status.PAUSED) 58 | 59 | expect(wrapper.vm.pausable).toBe(false) 60 | expect(wrapper.vm.resumable).toBe(true) 61 | }) 62 | 63 | it('allows a resumed file to be paused immediately', () => { 64 | wrapper.vm.onResume(0) 65 | 66 | expect(wrapper.vm.pausable).toBe(true) 67 | expect(wrapper.vm.resumable).toBe(false) 68 | }) 69 | }) 70 | -------------------------------------------------------------------------------- /tests/unit/RenderlessRetryButton.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import RetryButton from '@/renderless/RetryButton.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('renderless/RetryButton.vue', () => { 6 | let wrapper 7 | let uploader 8 | 9 | beforeEach(() => { 10 | uploader = new FineUploader({ options: {} }) 11 | 12 | wrapper = shallowMount(RetryButton, { 13 | propsData: { id: 0, uploader }, 14 | scopedSlots: { default: slots => slots }, 15 | }) 16 | }) 17 | 18 | it('is not retryable if upload has not failed', () => { 19 | wrapper.vm.onComplete(0, 'foo.bar', { success: true }) 20 | 21 | expect(wrapper.vm.retryable).toBe(false) 22 | }) 23 | 24 | it('is retryable if upload has failed', () => { 25 | wrapper.vm.onComplete(0, 'foo.bar', { success: false }) 26 | 27 | expect(wrapper.vm.retryable).toBe(true) 28 | }) 29 | 30 | it('retries upload if onClick is triggered', () => { 31 | wrapper.vm.onComplete(0, 'foo.bar', { success: false }) 32 | uploader.methods.retry = jest.fn() 33 | wrapper.vm.onClick() 34 | 35 | expect(uploader.methods.retry).toHaveBeenCalledWith(0) 36 | }) 37 | 38 | it('is not retryable if upload has failed and retries are forbidden (default response property)', () => { 39 | wrapper.vm.onComplete(0, 'foo.bar', { success: false, preventRetry: true }) 40 | 41 | expect(wrapper.vm.retryable).toBe(false) 42 | }) 43 | 44 | it('is not retryable if upload has failed and retries are forbidden (custom response property)', () => { 45 | const uploader = new FineUploader({ 46 | options: { retry: { preventRetryResponseProperty: 'dontDareRetry' } }, 47 | }) 48 | wrapper.setProps({ uploader }) 49 | wrapper.vm.onComplete(0, 'foo.bar', { success: false, dontDareRetry: true }) 50 | 51 | expect(wrapper.vm.retryable).toBe(false) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /tests/unit/RenderlessStatus.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import Status from '@/renderless/Status.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | import { status as STATUSES } from 'fine-uploader/lib/core/all' 5 | 6 | describe('renderless/Status.vue', () => { 7 | let wrapper 8 | let uploader 9 | 10 | beforeEach(() => { 11 | uploader = new FineUploader({ options: {} }) 12 | 13 | wrapper = shallowMount(Status, { 14 | propsData: { id: 0, uploader }, 15 | scopedSlots: { default: slots => slots }, 16 | }) 17 | }) 18 | 19 | it('saves the files new status on status change', () => { 20 | wrapper.vm.onStatusChange(0, STATUSES.UPLOAD_SUCCESSFUL, STATUSES.DELETING) 21 | 22 | expect(wrapper.vm.status).toBe(STATUSES.DELETING) 23 | }) 24 | 25 | it('set the status to null for an untracked file', () => { 26 | wrapper.vm.onStatusChange(1, STATUSES.UPLOAD_SUCCESSFUL, STATUSES.DELETING) 27 | 28 | expect(wrapper.vm.status).toBeNull() 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /tests/unit/RenderlessTotalProgressBar.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import ProgressBar from '@/renderless/TotalProgressBar.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('renderless/FileProgressBar.vue', () => { 6 | it('is not uploading any file on initialisation', () => { 7 | const uploader = new FineUploader({ options: {} }) 8 | let wrapper = shallowMount(ProgressBar, { 9 | propsData: { uploader }, 10 | scopedSlots: { default: slots => slots }, 11 | }) 12 | 13 | expect(wrapper.vm.uploading).toBe(false) 14 | }) 15 | 16 | it('keeps track of the files progress during the upload', () => { 17 | const uploader = new FineUploader({ options: {} }) 18 | let wrapper = shallowMount(ProgressBar, { 19 | propsData: { uploader }, 20 | scopedSlots: { default: slots => slots }, 21 | }) 22 | 23 | wrapper.vm.onProgress(100, 1000) 24 | 25 | expect(wrapper.vm.bytesUploaded).toBe(100) 26 | expect(wrapper.vm.totalSize).toBe(1000) 27 | }) 28 | 29 | it('is no longer uploading when upload is finished', () => { 30 | const uploader = new FineUploader({ options: {} }) 31 | const wrapper = shallowMount(ProgressBar, { 32 | propsData: { uploader }, 33 | scopedSlots: { default: slots => slots }, 34 | }) 35 | // uploading 36 | uploader.methods['getInProgress'] = jest.fn(() => 1) 37 | wrapper.vm.onStatusChange( 38 | 0, 39 | uploader.qq.status.QUEUED, 40 | uploader.qq.status.UPLOADING, 41 | ) 42 | 43 | expect(wrapper.vm.uploading).toBe(true) 44 | 45 | // done uploading 46 | uploader.methods['getInProgress'] = jest.fn(() => 0) 47 | wrapper.vm.onStatusChange( 48 | 0, 49 | uploader.qq.status.UPLOADING, 50 | uploader.qq.status.UPLOAD_SUCCESSFUL, 51 | ) 52 | 53 | expect(wrapper.vm.uploading).toBe(false) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /tests/unit/RetryButton.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import RetryButton from '@/RetryButton.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | 5 | describe('RetryButton.vue', () => { 6 | let wrapper 7 | let renderless 8 | let uploader 9 | 10 | beforeEach(() => { 11 | uploader = new FineUploader({ options: {} }) 12 | 13 | wrapper = mount(RetryButton, { 14 | propsData: { id: 0, uploader }, 15 | }) 16 | renderless = wrapper.vm.$children[0] 17 | }) 18 | 19 | it('does not display retry button by default if upload has not failed', () => { 20 | renderless.onComplete(0, 'foo.bar', { success: true }) 21 | 22 | expect(wrapper).toMatchSnapshot() 23 | expect(wrapper.isEmpty()).toBe(true) 24 | }) 25 | 26 | it('disables retry button if upload has not failed', () => { 27 | wrapper.setProps({ onlyRenderIfRetryable: false }) 28 | renderless.onComplete(0, 'foo.bar', { success: true }) 29 | 30 | expect(wrapper).toMatchSnapshot() 31 | expect(wrapper.attributes().disabled).toBeDefined() 32 | }) 33 | 34 | it('displays retry button if upload has failed', () => { 35 | wrapper = mount(RetryButton, { 36 | propsData: { id: 0, uploader }, 37 | }) 38 | renderless = wrapper.vm.$children[0] 39 | 40 | renderless.onComplete(0, 'foo.bar', { success: false }) 41 | 42 | expect(wrapper).toMatchSnapshot() 43 | expect(wrapper.attributes().disabled).toBeUndefined() 44 | }) 45 | 46 | it('retries upload if button has been clicked', () => { 47 | renderless.onComplete(0, 'foo.bar', { success: false }) 48 | uploader.methods.retry = jest.fn() 49 | wrapper.trigger('click') 50 | 51 | expect(wrapper).toMatchSnapshot() 52 | expect(uploader.methods.retry).toHaveBeenCalledWith(0) 53 | }) 54 | 55 | it('does not display retry button by default if upload has failed and retries are forbidden (default response property)', () => { 56 | renderless.onComplete(0, 'foo.bar', { success: false, preventRetry: true }) 57 | 58 | expect(wrapper).toMatchSnapshot() 59 | expect(wrapper.isEmpty()).toBe(true) 60 | }) 61 | 62 | it('does not display retry button by default if upload has failed and retries are forbidden (custom response property)', () => { 63 | const uploader = new FineUploader({ 64 | options: { retry: { preventRetryResponseProperty: 'dontDareRetry' } }, 65 | }) 66 | wrapper.setProps({ uploader }) 67 | renderless.onComplete(0, 'foo.bar', { success: false, dontDareRetry: true }) 68 | 69 | expect(wrapper).toMatchSnapshot() 70 | expect(wrapper.isEmpty()).toBe(true) 71 | }) 72 | 73 | it('disables retry button if upload has failed and retries are forbidden', () => { 74 | wrapper.setProps({ onlyRenderIfRetryable: false }) 75 | renderless.onComplete(0, 'foo.bar', { success: false, preventRetry: true }) 76 | 77 | expect(wrapper).toMatchSnapshot() 78 | expect(wrapper.attributes().disabled).toBeDefined() 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /tests/unit/Status.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import Status from '@/Status.vue' 3 | import FineUploader from 'fine-uploader-wrappers' 4 | import { status as STATUSES } from 'fine-uploader/lib/core/all' 5 | 6 | describe('Status.vue', () => { 7 | let wrapper 8 | let uploader 9 | let renderless 10 | 11 | beforeEach(() => { 12 | uploader = new FineUploader({ options: {} }) 13 | 14 | wrapper = mount(Status, { 15 | propsData: { id: 0, uploader }, 16 | }) 17 | renderless = wrapper.vm.$children[0] 18 | }) 19 | 20 | it('render nothing for a different file', () => { 21 | renderless.onStatusChange(1, STATUSES.UPLOAD_SUCCESSFUL, STATUSES.DELETING) 22 | 23 | expect(wrapper).toMatchSnapshot() 24 | expect(wrapper.text()).toBe('') 25 | }) 26 | 27 | it('render nothing for an untracked status value', () => { 28 | renderless.onStatusChange(0, STATUSES.DELETING, STATUSES.DELETE_FAILED) 29 | 30 | expect(wrapper).toMatchSnapshot() 31 | expect(wrapper.text()).toBe('') 32 | }) 33 | 34 | it('renders the status for the tracked file', () => { 35 | renderless.onStatusChange(0, STATUSES.UPLOAD_SUCCESSFUL, STATUSES.DELETING) 36 | 37 | expect(wrapper).toMatchSnapshot() 38 | expect(wrapper.text()).toBe('Deleting...') 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /tests/unit/__snapshots__/CancelButton.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CancelButton.vue disables the button if requested when the file can no longer be canceled 1`] = ` 4 | 7 | `; 8 | 9 | exports[`CancelButton.vue removes the button by default if the file can no longer be canceled 1`] = ``; 10 | 11 | exports[`CancelButton.vue renders the button for a submitted file 1`] = ` 12 | 15 | `; 16 | -------------------------------------------------------------------------------- /tests/unit/__snapshots__/DeleteButton.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`DeleteButton.vue disabled the button while the delete is in progress 1`] = ` 4 | 7 | `; 8 | 9 | exports[`DeleteButton.vue disables the button if requested when the file can no longer be deleted 1`] = ` 10 | 13 | `; 14 | 15 | exports[`DeleteButton.vue removes the button if the file can no longer be deleted 1`] = ``; 16 | 17 | exports[`DeleteButton.vue renders the button for a successfully uploaded file 1`] = ` 18 | 21 | `; 22 | -------------------------------------------------------------------------------- /tests/unit/__snapshots__/Filename.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Filename.vue renders initial filename 1`] = `test`; 4 | 5 | exports[`Filename.vue updates filename on setName 1`] = `new-name`; 6 | -------------------------------------------------------------------------------- /tests/unit/__snapshots__/Filesize.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Filesize.vue renders an empty filesize component if size is not known initially 1`] = ``; 4 | 5 | exports[`Filesize.vue renders file size at upload time for scaled blobs 1`] = ``; 6 | 7 | exports[`Filesize.vue renders file size at upload time for scaled blobs 2`] = `1 B`; 8 | 9 | exports[`Filesize.vue renders formatted file size for various sized files 1`] = `1.10 KB`; 10 | 11 | exports[`Filesize.vue renders formatted file size for various sized files 2`] = `1.10 MB`; 12 | 13 | exports[`Filesize.vue renders formatted file size for various sized files 3`] = `1.10 GB`; 14 | 15 | exports[`Filesize.vue renders formatted file size for various sized files 4`] = `1.10 TB`; 16 | -------------------------------------------------------------------------------- /tests/unit/__snapshots__/PauseResumeButton.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`PauseResumeButton.vue disables the button, if requested, when the upload is no longer actionable 1`] = ``; 4 | 5 | exports[`PauseResumeButton.vue disables the button, if requested, when the upload is no longer actionable 2`] = ``; 6 | 7 | exports[`PauseResumeButton.vue doesn't render the button until the first chunk has been uploaded 1`] = ``; 8 | 9 | exports[`PauseResumeButton.vue doesn't render the button until the first chunk has been uploaded 2`] = ``; 10 | 11 | exports[`PauseResumeButton.vue removes the pause button when the upload is no longer actionable 1`] = ``; 12 | 13 | exports[`PauseResumeButton.vue removes the pause button when the upload is no longer actionable 2`] = ``; 14 | -------------------------------------------------------------------------------- /tests/unit/__snapshots__/ProgressBar.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ProgressBar.vue hides file progress bar after all upload is complete 1`] = `
`; 4 | 5 | exports[`ProgressBar.vue hides file progress bar after all upload is complete 2`] = ``; 6 | 7 | exports[`ProgressBar.vue hides the progress bar by default 1`] = ``; 8 | 9 | exports[`ProgressBar.vue hides the progress bar by default 2`] = ``; 10 | 11 | exports[`ProgressBar.vue hides total progress bar after all uploads are complete 1`] = `
`; 12 | 13 | exports[`ProgressBar.vue hides total progress bar after all uploads are complete 2`] = ``; 14 | 15 | exports[`ProgressBar.vue show the file progress if an ID is supplied 1`] = ``; 16 | 17 | exports[`ProgressBar.vue show the total progress if no ID is supplied 1`] = ``; 18 | 19 | exports[`ProgressBar.vue shows file progress bar after all upload is complete if requested 1`] = `
`; 20 | 21 | exports[`ProgressBar.vue shows file progress bar after all upload is complete if requested 2`] = `
`; 22 | 23 | exports[`ProgressBar.vue shows the file progress bar before uploading start if specified 1`] = `
`; 24 | 25 | exports[`ProgressBar.vue shows the total progress bar before uploading start if specified 1`] = `
`; 26 | 27 | exports[`ProgressBar.vue shows total progress bar after all uploads are complete if requested 1`] = `
`; 28 | 29 | exports[`ProgressBar.vue shows total progress bar after all uploads are complete if requested 2`] = `
`; 30 | -------------------------------------------------------------------------------- /tests/unit/__snapshots__/RetryButton.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`RetryButton.vue disables retry button if upload has failed and retries are forbidden 1`] = ` 4 | 7 | `; 8 | 9 | exports[`RetryButton.vue disables retry button if upload has not failed 1`] = ` 10 | 13 | `; 14 | 15 | exports[`RetryButton.vue displays retry button if upload has failed 1`] = ` 16 | 19 | `; 20 | 21 | exports[`RetryButton.vue does not display retry button by default if upload has failed and retries are forbidden (custom response property) 1`] = ``; 22 | 23 | exports[`RetryButton.vue does not display retry button by default if upload has failed and retries are forbidden (default response property) 1`] = ``; 24 | 25 | exports[`RetryButton.vue does not display retry button by default if upload has not failed 1`] = ``; 26 | 27 | exports[`RetryButton.vue retries upload if button has been clicked 1`] = ` 28 | 31 | `; 32 | -------------------------------------------------------------------------------- /tests/unit/__snapshots__/Status.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Status.vue render nothing for a different file 1`] = ``; 4 | 5 | exports[`Status.vue render nothing for an untracked status value 1`] = ``; 6 | 7 | exports[`Status.vue renders the status for the tracked file 1`] = `Deleting...`; 8 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {} 2 | --------------------------------------------------------------------------------