mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
✨ validate file uploads against "accept" mime-type before uploading (#214)
closes https://github.com/TryGhost/Ghost/issues/7144 - allow the `accept` attr of `gh-file-uploader` and `gh-image-uploader` to be specified - allows a `validate` action to be passed into `gh-image-uploader` and `gh-file-uploader` components that runs after a file is selected and before the upload starts - adds a default `validate` action to `gh-image-uploader` and `gh-file-uploader` that triggers the normal `UnsupportedFileType` error when the selected file's mime-type does not match the `accept` attribute - adds mime type validation to labs importer (basic implementation, should be replaced with uploader components once they have been refactored)
This commit is contained in:
parent
8cd8d7b4ed
commit
508a473b43
@ -3,6 +3,7 @@ import Component from 'ember-component';
|
||||
export default Component.extend({
|
||||
_file: null,
|
||||
|
||||
acceptEncoding: null,
|
||||
uploadButtonText: 'Text',
|
||||
uploadButtonDisabled: true,
|
||||
|
||||
|
@ -9,7 +9,8 @@ import { invoke, invokeAction } from 'ember-invoke-action';
|
||||
import {
|
||||
isVersionMismatchError,
|
||||
isRequestEntityTooLargeError,
|
||||
isUnsupportedMediaTypeError
|
||||
isUnsupportedMediaTypeError,
|
||||
UnsupportedMediaTypeError
|
||||
} from 'ghost-admin/services/ajax';
|
||||
|
||||
export default Component.extend({
|
||||
@ -20,6 +21,8 @@ export default Component.extend({
|
||||
labelText: 'Select or drag-and-drop a file',
|
||||
url: null,
|
||||
paramName: 'file',
|
||||
accept: 'text/csv',
|
||||
validate: null,
|
||||
|
||||
file: null,
|
||||
response: null,
|
||||
@ -148,12 +151,42 @@ export default Component.extend({
|
||||
invokeAction(this, 'uploadFailed', error);
|
||||
},
|
||||
|
||||
_validate(file) {
|
||||
if (this.get('validate')) {
|
||||
return invokeAction(this, 'validate', file);
|
||||
} else {
|
||||
return this._defaultValidator(file);
|
||||
}
|
||||
},
|
||||
|
||||
_defaultValidator(file) {
|
||||
let accept = this.get('accept');
|
||||
|
||||
if (!isBlank(accept) && file && accept.indexOf(file.type) === -1) {
|
||||
return new UnsupportedMediaTypeError();
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
actions: {
|
||||
fileSelected(fileList) {
|
||||
this.set('file', fileList[0]);
|
||||
run.schedule('actions', this, function () {
|
||||
this.generateRequest();
|
||||
});
|
||||
// can't use array destructuring here as FileList is not a strict
|
||||
// array and fails in Safari
|
||||
// jscs:disable requireArrayDestructuring
|
||||
let file = fileList[0];
|
||||
// jscs:enable requireArrayDestructuring
|
||||
let validationResult = this._validate(file);
|
||||
|
||||
this.set('file', file);
|
||||
|
||||
if (validationResult === true) {
|
||||
run.schedule('actions', this, function () {
|
||||
this.generateRequest();
|
||||
});
|
||||
} else {
|
||||
this._uploadFailed(validationResult);
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
|
@ -10,7 +10,8 @@ import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||
import {
|
||||
isRequestEntityTooLargeError,
|
||||
isUnsupportedMediaTypeError,
|
||||
isVersionMismatchError
|
||||
isVersionMismatchError,
|
||||
UnsupportedMediaTypeError
|
||||
} from 'ghost-admin/services/ajax';
|
||||
|
||||
export default Component.extend({
|
||||
@ -22,6 +23,8 @@ export default Component.extend({
|
||||
text: '',
|
||||
altText: '',
|
||||
saveButton: true,
|
||||
accept: 'image/gif,image/jpg,image/jpeg,image/png,image/svg+xml',
|
||||
validate: null,
|
||||
|
||||
dragClass: null,
|
||||
failureMessage: null,
|
||||
@ -200,12 +203,42 @@ export default Component.extend({
|
||||
});
|
||||
},
|
||||
|
||||
_validate(file) {
|
||||
if (this.get('validate')) {
|
||||
return invokeAction(this, 'validate', file);
|
||||
} else {
|
||||
return this._defaultValidator(file);
|
||||
}
|
||||
},
|
||||
|
||||
_defaultValidator(file) {
|
||||
let accept = this.get('accept');
|
||||
|
||||
if (!isBlank(accept) && file && accept.indexOf(file.type) === -1) {
|
||||
return new UnsupportedMediaTypeError();
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
actions: {
|
||||
fileSelected(fileList) {
|
||||
this.set('file', fileList[0]);
|
||||
run.schedule('actions', this, function () {
|
||||
this.generateRequest();
|
||||
});
|
||||
// can't use array destructuring here as FileList is not a strict
|
||||
// array and fails in Safari
|
||||
// jscs:disable requireArrayDestructuring
|
||||
let file = fileList[0];
|
||||
// jscs:enable requireArrayDestructuring
|
||||
let validationResult = this._validate(file);
|
||||
|
||||
this.set('file', file);
|
||||
|
||||
if (validationResult === true) {
|
||||
run.schedule('actions', this, function () {
|
||||
this.generateRequest();
|
||||
});
|
||||
} else {
|
||||
this._uploadFailed(validationResult);
|
||||
}
|
||||
},
|
||||
|
||||
onInput(url) {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import $ from 'jquery';
|
||||
import Controller from 'ember-controller';
|
||||
import injectService from 'ember-service/inject';
|
||||
import {isBlank} from 'ember-utils';
|
||||
import {isEmberArray} from 'ember-array/utils';
|
||||
import {UnsupportedMediaTypeError} from 'ghost-admin/services/ajax';
|
||||
|
||||
export default Controller.extend({
|
||||
uploadButtonText: 'Import',
|
||||
@ -9,6 +11,8 @@ export default Controller.extend({
|
||||
submitting: false,
|
||||
showDeleteAllModal: false,
|
||||
|
||||
importMimeType: 'application/json',
|
||||
|
||||
ghostPaths: injectService(),
|
||||
notifications: injectService(),
|
||||
session: injectService(),
|
||||
@ -20,10 +24,16 @@ export default Controller.extend({
|
||||
let notifications = this.get('notifications');
|
||||
let currentUserId = this.get('session.user.id');
|
||||
let dbUrl = this.get('ghostPaths.url').api('db');
|
||||
let accept = this.get('importMimeType');
|
||||
|
||||
this.set('uploadButtonText', 'Importing');
|
||||
this.set('importErrors', '');
|
||||
|
||||
if (!isBlank(accept) && file && accept.indexOf(file.type) === -1) {
|
||||
this.set('importErrors', [new UnsupportedMediaTypeError()]);
|
||||
return;
|
||||
}
|
||||
|
||||
formData.append('importfile', file);
|
||||
|
||||
this.get('ajax').post(dbUrl, {
|
||||
|
@ -1,4 +1,4 @@
|
||||
<input data-url="upload" class="gh-input btn-block" type="file" name="importfile" accept="{{options.acceptEncoding}}">
|
||||
<input data-url="upload" class="gh-input btn-block" type="file" name="importfile" accept="{{acceptEncoding}}">
|
||||
<button type="submit" class="btn btn-green btn-block" id="startupload" disabled={{uploadButtonDisabled}} {{action "upload"}}>
|
||||
{{uploadButtonText}}
|
||||
</button>
|
||||
|
@ -13,7 +13,7 @@
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<div class="upload-form">
|
||||
{{#gh-file-input multiple=false alt=labelText action=(action 'fileSelected') accept="text/csv"}}
|
||||
{{#gh-file-input multiple=false alt=labelText action=(action 'fileSelected') accept=accept}}
|
||||
<div class="description">{{labelText}}</div>
|
||||
{{/gh-file-input}}
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
{{#if showUploadForm}}
|
||||
{{!-- file selection/drag-n-drop --}}
|
||||
<div class="upload-form">
|
||||
{{#gh-file-input multiple=false alt=description action=(action 'fileSelected') accept="image/gif,image/jpg,image/jpeg,image/png,image/svg+xml"}}
|
||||
{{#gh-file-input multiple=false alt=description action=(action 'fileSelected') accept=accept}}
|
||||
<div class="description">{{description}}</div>
|
||||
{{/gh-file-input}}
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<div class="form-group">
|
||||
<label>Import</label>
|
||||
{{partial "import-errors"}}
|
||||
{{gh-file-upload id="importfile" classNames="flex" uploadButtonText=uploadButtonText onUpload="onUpload"}}
|
||||
{{gh-file-upload id="importfile" classNames="flex" uploadButtonText=uploadButtonText onUpload="onUpload" acceptEncoding=importMimeType}}
|
||||
<p>Import from another Ghost installation. If you import a user, this will replace the current user & log you out.</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
@ -246,7 +246,7 @@ describe('Acceptance: Subscribers', function() {
|
||||
});
|
||||
|
||||
click('.btn:contains("Import CSV")');
|
||||
fileUpload('.fullscreen-modal input[type="file"]');
|
||||
fileUpload('.fullscreen-modal input[type="file"]', ['test'], {type: 'text/csv'});
|
||||
|
||||
andThen(function () {
|
||||
// modal title changes
|
||||
|
@ -104,7 +104,7 @@ describe('Acceptance: Version Mismatch', function() {
|
||||
|
||||
visit('/subscribers');
|
||||
click('.btn:contains("Import CSV")');
|
||||
fileUpload('.fullscreen-modal input[type="file"]');
|
||||
fileUpload('.fullscreen-modal input[type="file"]', ['test'], {type: 'text/csv'});
|
||||
|
||||
andThen(() => {
|
||||
// alert is shown
|
||||
|
@ -12,6 +12,7 @@ import wait from 'ember-test-helpers/wait';
|
||||
import sinon from 'sinon';
|
||||
import {createFile, fileUpload} from '../../helpers/file-upload';
|
||||
import Service from 'ember-service';
|
||||
import {UnsupportedMediaTypeError} from 'ghost-admin/services/ajax';
|
||||
|
||||
const notificationsStub = Service.extend({
|
||||
showAPIError(error, options) {
|
||||
@ -64,6 +65,20 @@ describeComponent(
|
||||
.to.equal('Select or drag-and-drop a file');
|
||||
});
|
||||
|
||||
it('allows file input "accept" attribute to be changed', function () {
|
||||
this.render(hbs`{{gh-file-uploader}}`);
|
||||
expect(
|
||||
this.$('input[type="file"]').attr('accept'),
|
||||
'default "accept" attribute'
|
||||
).to.equal('text/csv');
|
||||
|
||||
this.render(hbs`{{gh-file-uploader accept="application/zip"}}`);
|
||||
expect(
|
||||
this.$('input[type="file"]').attr('accept'),
|
||||
'specified "accept" attribute'
|
||||
).to.equal('application/zip');
|
||||
});
|
||||
|
||||
it('renders form with supplied label text', function () {
|
||||
this.set('labelText', 'My label');
|
||||
this.render(hbs`{{gh-file-uploader labelText=labelText}}`);
|
||||
@ -76,7 +91,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(server.handledRequests.length).to.equal(1);
|
||||
@ -92,7 +107,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadSuccess=(action uploadSuccess)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
@ -108,7 +123,7 @@ describeComponent(
|
||||
stubFailedUpload(server, 500);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadSuccess=(action uploadSuccess)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadSuccess.calledOnce).to.be.false;
|
||||
@ -123,7 +138,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadStarted=(action uploadStarted)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadStarted.calledOnce).to.be.true;
|
||||
@ -138,7 +153,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadFinished=(action uploadFinished)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
@ -153,7 +168,7 @@ describeComponent(
|
||||
stubFailedUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadFinished=(action uploadFinished)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
@ -164,7 +179,7 @@ describeComponent(
|
||||
it('displays invalid file type error', function (done) {
|
||||
stubFailedUpload(server, 415, 'UnsupportedMediaTypeError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -178,7 +193,7 @@ describeComponent(
|
||||
it('displays file too large for server error', function (done) {
|
||||
stubFailedUpload(server, 413, 'RequestEntityTooLargeError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -192,7 +207,7 @@ describeComponent(
|
||||
return [413, {}, ''];
|
||||
});
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -204,7 +219,7 @@ describeComponent(
|
||||
it('displays other server-side error with message', function (done) {
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -218,7 +233,7 @@ describeComponent(
|
||||
return [500, {'Content-Type': 'application/json'}, ''];
|
||||
});
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -234,7 +249,7 @@ describeComponent(
|
||||
stubFailedUpload(server, 400, 'VersionMismatchError');
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(showAPIError.calledOnce).to.be.true;
|
||||
@ -248,7 +263,7 @@ describeComponent(
|
||||
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(showAPIError.called).to.be.false;
|
||||
@ -259,7 +274,7 @@ describeComponent(
|
||||
it('can be reset after a failed upload', function (done) {
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
run(() => {
|
||||
@ -280,7 +295,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server, 150);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadFinished=(action done)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
// after 75ms we should have had one progress event
|
||||
run.later(this, function () {
|
||||
@ -316,7 +331,7 @@ describeComponent(
|
||||
let uploadSuccess = sinon.spy();
|
||||
let drop = $.Event('drop', {
|
||||
dataTransfer: {
|
||||
files: [createFile()]
|
||||
files: [createFile(['test'], {type: 'text/csv'})]
|
||||
}
|
||||
});
|
||||
|
||||
@ -335,5 +350,82 @@ describeComponent(
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('validates "accept" mime type by default', function (done) {
|
||||
let uploadSuccess = sinon.spy();
|
||||
let uploadFailed = sinon.spy();
|
||||
|
||||
this.set('uploadSuccess', uploadSuccess);
|
||||
this.set('uploadFailed', uploadFailed);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader
|
||||
url=uploadUrl
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
uploadFailed=(action uploadFailed)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'application/plain'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadSuccess.called).to.be.false;
|
||||
expect(uploadFailed.calledOnce).to.be.true;
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(this.$('.failed').text()).to.match(/The file type you uploaded is not supported/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('uploads if validate action supplied and returns true', function (done) {
|
||||
let validate = sinon.stub().returns(true);
|
||||
let uploadSuccess = sinon.spy();
|
||||
|
||||
this.set('validate', validate);
|
||||
this.set('uploadSuccess', uploadSuccess);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader
|
||||
url=uploadUrl
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
validate=(action validate)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('skips upload and displays error if validate action supplied and doesn\'t return true', function (done) {
|
||||
let validate = sinon.stub().returns(new UnsupportedMediaTypeError());
|
||||
let uploadSuccess = sinon.spy();
|
||||
let uploadFailed = sinon.spy();
|
||||
|
||||
this.set('validate', validate);
|
||||
this.set('uploadSuccess', uploadSuccess);
|
||||
this.set('uploadFailed', uploadFailed);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader
|
||||
url=uploadUrl
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
uploadFailed=(action uploadFailed)
|
||||
validate=(action validate)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'text/csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.called).to.be.false;
|
||||
expect(uploadFailed.calledOnce).to.be.true;
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(this.$('.failed').text()).to.match(/The file type you uploaded is not supported/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -12,6 +12,7 @@ import {createFile, fileUpload} from '../../helpers/file-upload';
|
||||
import $ from 'jquery';
|
||||
import run from 'ember-runloop';
|
||||
import Service from 'ember-service';
|
||||
import {UnsupportedMediaTypeError} from 'ghost-admin/services/ajax';
|
||||
|
||||
const keyCodes = {
|
||||
enter: 13
|
||||
@ -144,7 +145,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(server.handledRequests.length).to.equal(1);
|
||||
@ -160,7 +161,7 @@ describeComponent(
|
||||
this.get('sessionService').set('isAuthenticated', true);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
let [request] = server.handledRequests;
|
||||
@ -176,7 +177,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(update.calledOnce).to.be.true;
|
||||
@ -192,7 +193,7 @@ describeComponent(
|
||||
stubFailedUpload(server, 500);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(update.calledOnce).to.be.false;
|
||||
@ -207,7 +208,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image uploadStarted=(action uploadStarted) update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadStarted.calledOnce).to.be.true;
|
||||
@ -222,7 +223,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image uploadFinished=(action uploadFinished) update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
@ -237,7 +238,7 @@ describeComponent(
|
||||
stubFailedUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image uploadFinished=(action uploadFinished) update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
@ -248,7 +249,7 @@ describeComponent(
|
||||
it('displays invalid file type error', function (done) {
|
||||
stubFailedUpload(server, 415, 'UnsupportedMediaTypeError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -262,7 +263,7 @@ describeComponent(
|
||||
it('displays file too large for server error', function (done) {
|
||||
stubFailedUpload(server, 413, 'RequestEntityTooLargeError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -276,7 +277,7 @@ describeComponent(
|
||||
return [413, {}, ''];
|
||||
});
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -288,7 +289,7 @@ describeComponent(
|
||||
it('displays other server-side error with message', function (done) {
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -302,7 +303,7 @@ describeComponent(
|
||||
return [500, {'Content-Type': 'application/json'}, ''];
|
||||
});
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
@ -318,7 +319,7 @@ describeComponent(
|
||||
stubFailedUpload(server, 400, 'VersionMismatchError');
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(showAPIError.calledOnce).to.be.true;
|
||||
@ -332,7 +333,7 @@ describeComponent(
|
||||
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(showAPIError.called).to.be.false;
|
||||
@ -343,7 +344,7 @@ describeComponent(
|
||||
it('can be reset after a failed upload', function (done) {
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
run(() => {
|
||||
@ -364,7 +365,7 @@ describeComponent(
|
||||
stubSuccessfulUpload(server, 150);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image uploadFinished=(action done) update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'));
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
// after 75ms we should have had one progress event
|
||||
run.later(this, function () {
|
||||
@ -402,7 +403,7 @@ describeComponent(
|
||||
let uploadSuccess = sinon.spy();
|
||||
let drop = $.Event('drop', {
|
||||
dataTransfer: {
|
||||
files: [createFile()]
|
||||
files: [createFile(['test'], {type: 'image/png'})]
|
||||
}
|
||||
});
|
||||
|
||||
@ -421,6 +422,80 @@ describeComponent(
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('validates "accept" mime type by default', function (done) {
|
||||
let uploadSuccess = sinon.spy();
|
||||
let uploadFailed = sinon.spy();
|
||||
|
||||
this.set('uploadSuccess', uploadSuccess);
|
||||
this.set('uploadFailed', uploadFailed);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
uploadFailed=(action uploadFailed)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'application/plain'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadSuccess.called).to.be.false;
|
||||
expect(uploadFailed.calledOnce).to.be.true;
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(this.$('.failed').text()).to.match(/The image type you uploaded is not supported/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('uploads if validate action supplied and returns true', function (done) {
|
||||
let validate = sinon.stub().returns(true);
|
||||
let uploadSuccess = sinon.spy();
|
||||
|
||||
this.set('validate', validate);
|
||||
this.set('uploadSuccess', uploadSuccess);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
validate=(action validate)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'application/plain'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('skips upload and displays error if validate action supplied and doesn\'t return true', function (done) {
|
||||
let validate = sinon.stub().returns(new UnsupportedMediaTypeError());
|
||||
let uploadSuccess = sinon.spy();
|
||||
let uploadFailed = sinon.spy();
|
||||
|
||||
this.set('validate', validate);
|
||||
this.set('uploadSuccess', uploadSuccess);
|
||||
this.set('uploadFailed', uploadFailed);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
uploadFailed=(action uploadFailed)
|
||||
validate=(action validate)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'image/png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.called).to.be.false;
|
||||
expect(uploadFailed.calledOnce).to.be.true;
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(this.$('.failed').text()).to.match(/The image type you uploaded is not supported/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('URL input form', function () {
|
||||
|
Loading…
Reference in New Issue
Block a user