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:
Kevin Ansfield 2016-08-22 12:45:33 +01:00 committed by Austin Burdine
parent 8cd8d7b4ed
commit 508a473b43
12 changed files with 293 additions and 49 deletions

View File

@ -3,6 +3,7 @@ import Component from 'ember-component';
export default Component.extend({
_file: null,
acceptEncoding: null,
uploadButtonText: 'Text',
uploadButtonDisabled: true,

View File

@ -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() {

View File

@ -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) {

View File

@ -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, {

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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();
});
});
}
);

View File

@ -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 () {