Merge pull request #27 from kevinansfield/improve-file-upload-tests

Improve uploader tests + test subscribers CSV import
This commit is contained in:
Hannah Wolfe 2016-05-27 19:29:07 +01:00
commit 3e64cbe5f9
12 changed files with 489 additions and 673 deletions

View File

@ -6,7 +6,8 @@
"-Promise",
"-Notification",
"validator",
"moment"
"moment",
"fileUpload"
],
"browser": true,
"boss": true,

View File

@ -0,0 +1,11 @@
import Ember from 'ember';
import XFileInput from 'emberx-file-input/components/x-file-input';
const {testing} = Ember;
export default XFileInput.extend({
change(e) {
let files = testing ? (e.originalEvent || e).testingFiles : e.target.files;
this.sendAction('action', files);
}
});

View File

@ -13,8 +13,8 @@
{{/if}}
{{else}}
<div class="upload-form">
{{#x-file-input multiple=false alt=labelText action=(action 'fileSelected') accept="text/csv"}}
{{#gh-file-input multiple=false alt=labelText action=(action 'fileSelected') accept="text/csv"}}
<div class="description">{{labelText}}</div>
{{/x-file-input}}
{{/gh-file-input}}
</div>
{{/if}}

View File

@ -15,9 +15,9 @@
{{#if showUploadForm}}
{{!-- file selection/drag-n-drop --}}
<div class="upload-form">
{{#x-file-input multiple=false alt=text action=(action 'fileSelected') accept="image/gif,image/jpg,image/jpeg,image/png,image/svg+xml"}}
{{#gh-file-input multiple=false alt=text action=(action 'fileSelected') accept="image/gif,image/jpg,image/jpeg,image/png,image/svg+xml"}}
<div class="description">{{text}}</div>
{{/x-file-input}}
{{/gh-file-input}}
</div>
<a class="image-url" {{action 'switchForm' 'url-input'}}>

View File

@ -232,6 +232,8 @@ describe('Acceptance: Subscribers', function() {
// it displays the import subscribers modal
expect(find('.fullscreen-modal').length, 'import subscribers modal displayed')
.to.equal(1);
expect(find('.fullscreen-modal input[type="file"]').length, 'import modal contains file input')
.to.equal(1);
});
// cancel the modal
@ -243,13 +245,33 @@ describe('Acceptance: Subscribers', function() {
.to.equal(0);
});
// TODO: how to simulate file upload?
click('.btn:contains("Import CSV")');
fileUpload('.fullscreen-modal input[type="file"]');
andThen(function () {
// modal title changes
expect(find('.fullscreen-modal h1').text().trim(), 'import modal title after import')
.to.equal('Import Successful');
// modal button changes
expect(find('.fullscreen-modal .modal-footer button').text().trim(), 'import modal button text after import')
.to.equal('Close');
// subscriber total is updated
expect(find('#total-subscribers').text().trim(), 'subscribers total after import')
.to.equal('90');
// table is reset
let [lastRequest] = server.pretender.handledRequests.slice(-1);
expect(lastRequest.url, 'endpoint requested after import')
.to.match(/\/subscribers\/\?/);
expect(lastRequest.queryParams.page, 'page requested after import')
.to.equal('1');
expect(find('.subscribers-table .lt-body .lt-row').length, 'number of rows in table after import')
.to.equal(30);
});
// re-open import modal
// upload a file
// modal title changes
// modal button changes
// table is reset
// close modal
});
});

View File

@ -0,0 +1,33 @@
/* global Blob */
import Ember from 'ember';
export function createFile(content = ['test'], options = {}) {
let {
name,
type
} = options;
let file = new Blob(content, {type: type ? type : 'text/plain'});
file.name = name ? name : 'test.txt';
return file;
}
export function fileUpload($element, content, options) {
let file = createFile(content, options);
let event = Ember.$.Event('change', {
testingFiles: [file]
});
$element.trigger(event);
}
export default Ember.Test.registerAsyncHelper('fileUpload', function(app, selector, content, options) {
let file = createFile(content, options);
return triggerEvent(
selector,
'change',
{foor: 'bar', testingFiles: [file]}
);
});

View File

@ -1,6 +1,7 @@
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
import fileUpload from './file-upload';
const {assign, run} = Ember;

View File

@ -8,6 +8,8 @@ import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';
import Pretender from 'pretender';
import wait from 'ember-test-helpers/wait';
import sinon from 'sinon';
import {createFile, fileUpload} from '../../helpers/file-upload';
const {run} = Ember;
@ -39,6 +41,7 @@ describeComponent(
beforeEach(function () {
server = new Pretender();
this.set('uploadUrl', '/ghost/api/v0.1/uploads/');
});
afterEach(function () {
@ -62,10 +65,9 @@ describeComponent(
it('generates request to supplied endpoint', function (done) {
stubSuccessfulUpload(server);
this.set('uploadUrl', '/ghost/api/v0.1/uploads/');
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
this.$('input[type="file"]').trigger('change');
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(server.handledRequests.length).to.equal(1);
@ -74,6 +76,183 @@ describeComponent(
});
});
it('fires uploadSuccess action on successful upload', function (done) {
let uploadSuccess = sinon.spy();
this.set('uploadSuccess', uploadSuccess);
stubSuccessfulUpload(server);
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadSuccess=(action uploadSuccess)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(uploadSuccess.calledOnce).to.be.true;
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
done();
});
});
it('doesn\'t fire uploadSuccess action on failed upload', function (done) {
let uploadSuccess = sinon.spy();
this.set('uploadSuccess', uploadSuccess);
stubFailedUpload(server, 500);
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadSuccess=(action uploadSuccess)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(uploadSuccess.calledOnce).to.be.false;
done();
});
});
it('fires uploadStarted action on upload start', function (done) {
let uploadStarted = sinon.spy();
this.set('uploadStarted', uploadStarted);
stubSuccessfulUpload(server);
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadStarted=(action uploadStarted)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(uploadStarted.calledOnce).to.be.true;
done();
});
});
it('fires uploadFinished action on successful upload', function (done) {
let uploadFinished = sinon.spy();
this.set('uploadFinished', uploadFinished);
stubSuccessfulUpload(server);
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadFinished=(action uploadFinished)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(uploadFinished.calledOnce).to.be.true;
done();
});
});
it('fires uploadFinished action on failed upload', function (done) {
let uploadFinished = sinon.spy();
this.set('uploadFinished', uploadFinished);
stubFailedUpload(server);
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadFinished=(action uploadFinished)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(uploadFinished.calledOnce).to.be.true;
done();
});
});
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"]'));
wait().then(() => {
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/);
expect(this.$('.btn-green').length, 'reset button is displayed').to.equal(1);
expect(this.$('.btn-green').text()).to.equal('Try Again');
done();
});
});
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"]'));
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/The file you uploaded was larger/);
done();
});
});
it('handles file too large error directly from the web server', function (done) {
server.post('/ghost/api/v0.1/uploads/', function () {
return [413, {}, ''];
});
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/The file you uploaded was larger/);
done();
});
});
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"]'));
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/Error: UnknownError/);
done();
});
});
it('handles unknown failure', function (done) {
server.post('/ghost/api/v0.1/uploads/', function () {
return [500, {'Content-Type': 'application/json'}, ''];
});
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/Something went wrong/);
done();
});
});
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"]'));
wait().then(() => {
run(() => {
this.$('.btn-green').click();
});
});
wait().then(() => {
expect(this.$('input[type="file"]').length).to.equal(1);
done();
});
});
it('displays upload progress', function (done) {
this.set('done', done);
// pretender fires a progress event every 50ms
stubSuccessfulUpload(server, 150);
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadFinished=(action done)}}`);
fileUpload(this.$('input[type="file"]'));
// after 75ms we should have had one progress event
run.later(this, function () {
expect(this.$('.progress .bar').length).to.equal(1);
let [_, percentageWidth] = this.$('.progress .bar').attr('style').match(/width: (\d+)%?/);
expect(percentageWidth).to.be.above(0);
expect(percentageWidth).to.be.below(100);
}, 75);
});
it('handles drag over/leave', function () {
this.render(hbs`{{gh-file-uploader}}`);
@ -94,5 +273,29 @@ describeComponent(
expect(this.$('.gh-image-uploader').hasClass('--drag-over'), 'has drag-over class').to.be.false;
});
it('triggers file upload on file drop', function (done) {
let uploadSuccess = sinon.spy();
let drop = Ember.$.Event('drop', {
dataTransfer: {
files: [createFile()]
}
});
this.set('uploadSuccess', uploadSuccess);
stubSuccessfulUpload(server);
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadSuccess=(action uploadSuccess)}}`);
run(() => {
this.$('.gh-image-uploader').trigger(drop);
});
wait().then(() => {
expect(uploadSuccess.calledOnce).to.be.true;
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
done();
});
});
}
);

View File

@ -9,6 +9,7 @@ import {
import hbs from 'htmlbars-inline-precompile';
import Pretender from 'pretender';
import wait from 'ember-test-helpers/wait';
import {createFile, fileUpload} from '../../helpers/file-upload';
const {run} = Ember;
@ -130,7 +131,7 @@ describeComponent(
stubSuccessfulUpload(server);
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
this.$('input[type="file"]').trigger('change');
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(server.handledRequests.length).to.equal(1);
@ -146,7 +147,7 @@ describeComponent(
this.get('sessionService').set('isAuthenticated', true);
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
this.$('input[type="file"]').trigger('change');
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
let [request] = server.handledRequests;
@ -155,6 +156,183 @@ describeComponent(
});
});
it('fires update action on successful upload', function (done) {
let update = sinon.spy();
this.set('update', update);
stubSuccessfulUpload(server);
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(update.calledOnce).to.be.true;
expect(update.firstCall.args[0]).to.equal('/content/images/test.png');
done();
});
});
it('doesn\'t fire update action on failed upload', function (done) {
let update = sinon.spy();
this.set('update', update);
stubFailedUpload(server, 500);
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(update.calledOnce).to.be.false;
done();
});
});
it('fires uploadStarted action on upload start', function (done) {
let uploadStarted = sinon.spy();
this.set('uploadStarted', uploadStarted);
stubSuccessfulUpload(server);
this.render(hbs`{{gh-image-uploader image=image uploadStarted=(action uploadStarted) update=(action update)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(uploadStarted.calledOnce).to.be.true;
done();
});
});
it('fires uploadFinished action on successful upload', function (done) {
let uploadFinished = sinon.spy();
this.set('uploadFinished', uploadFinished);
stubSuccessfulUpload(server);
this.render(hbs`{{gh-image-uploader image=image uploadFinished=(action uploadFinished) update=(action update)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(uploadFinished.calledOnce).to.be.true;
done();
});
});
it('fires uploadFinished action on failed upload', function (done) {
let uploadFinished = sinon.spy();
this.set('uploadFinished', uploadFinished);
stubFailedUpload(server);
this.render(hbs`{{gh-image-uploader image=image uploadFinished=(action uploadFinished) update=(action update)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(uploadFinished.calledOnce).to.be.true;
done();
});
});
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"]'));
wait().then(() => {
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/);
expect(this.$('.btn-green').length, 'reset button is displayed').to.equal(1);
expect(this.$('.btn-green').text()).to.equal('Try Again');
done();
});
});
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"]'));
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/The image you uploaded was larger/);
done();
});
});
it('handles file too large error directly from the web server', function (done) {
server.post('/ghost/api/v0.1/uploads/', function () {
return [413, {}, ''];
});
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/The image you uploaded was larger/);
done();
});
});
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"]'));
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/Error: UnknownError/);
done();
});
});
it('handles unknown failure', function (done) {
server.post('/ghost/api/v0.1/uploads/', function () {
return [500, {'Content-Type': 'application/json'}, ''];
});
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
fileUpload(this.$('input[type="file"]'));
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/Something went wrong/);
done();
});
});
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"]'));
wait().then(() => {
run(() => {
this.$('.btn-green').click();
});
});
wait().then(() => {
expect(this.$('input[type="file"]').length).to.equal(1);
done();
});
});
it('displays upload progress', function (done) {
this.set('done', done);
// pretender fires a progress event every 50ms
stubSuccessfulUpload(server, 150);
this.render(hbs`{{gh-image-uploader image=image uploadFinished=(action done) update=(action update)}}`);
fileUpload(this.$('input[type="file"]'));
// after 75ms we should have had one progress event
run.later(this, function () {
expect(this.$('.progress .bar').length).to.equal(1);
let [_, percentageWidth] = this.$('.progress .bar').attr('style').match(/width: (\d+)%?/);
expect(percentageWidth).to.be.above(0);
expect(percentageWidth).to.be.below(100);
}, 75);
});
it('handles drag over/leave', function () {
stubSuccessfulUpload(server);
@ -177,6 +355,30 @@ describeComponent(
expect(this.$('.gh-image-uploader').hasClass('--drag-over'), 'has drag-over class').to.be.false;
});
it('triggers file upload on file drop', function (done) {
let uploadSuccess = sinon.spy();
let drop = Ember.$.Event('drop', {
dataTransfer: {
files: [createFile()]
}
});
this.set('uploadSuccess', uploadSuccess);
stubSuccessfulUpload(server);
this.render(hbs`{{gh-image-uploader uploadSuccess=(action uploadSuccess)}}`);
run(() => {
this.$('.gh-image-uploader').trigger(drop);
});
wait().then(() => {
expect(uploadSuccess.calledOnce).to.be.true;
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
done();
});
});
});
describe('URL input form', function () {

View File

@ -1,313 +0,0 @@
/* jshint expr:true */
/* global Blob */
import { expect } from 'chai';
import {
describeComponent,
it
} from 'ember-mocha';
import Ember from 'ember';
import sinon from 'sinon';
import Pretender from 'pretender';
import wait from 'ember-test-helpers/wait';
const {run} = Ember;
const createFile = function (content = ['test'], options = {}) {
let {
name,
type,
lastModifiedDate
} = options;
let file = new Blob(content, {type: type ? type : 'text/plain'});
file.name = name ? name : 'text.txt';
return file;
};
const stubSuccessfulUpload = function (server, delay = 0) {
server.post('/ghost/api/v0.1/uploads/', function () {
return [200, {'Content-Type': 'application/json'}, '"/content/images/test.png"'];
}, delay);
};
const stubFailedUpload = function (server, code, error, delay = 0) {
server.post('/ghost/api/v0.1/uploads/', function () {
return [code, {'Content-Type': 'application/json'}, JSON.stringify({
errors: [{
errorType: error,
message: `Error: ${error}`
}]
})];
}, delay);
};
describeComponent(
'gh-file-uploader',
'Unit: Component: gh-file-uploader',
{
needs: [
'service:ajax',
'service:session', // used by ajax service
'service:feature',
'component:x-file-input'
],
unit: true
},
function() {
let server, url;
beforeEach(function () {
server = new Pretender();
url = '/ghost/api/v0.1/uploads/';
});
afterEach(function () {
server.shutdown();
});
it('renders', function() {
// creates the component instance
let component = this.subject();
// renders the component on the page
this.render();
expect(component).to.be.ok;
expect(this.$()).to.have.length(1);
});
it('fires uploadSuccess action on successful upload', function (done) {
let uploadSuccess = sinon.spy();
let component = this.subject({url, uploadSuccess});
let file = createFile();
stubSuccessfulUpload(server);
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(uploadSuccess.calledOnce).to.be.true;
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
done();
});
});
it('fires uploadStarted action on upload start', function (done) {
let uploadStarted = sinon.spy();
let component = this.subject({url, uploadStarted});
let file = createFile();
stubSuccessfulUpload(server);
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(uploadStarted.calledOnce).to.be.true;
done();
});
});
it('fires uploadFinished action on successful upload', function (done) {
let uploadFinished = sinon.spy();
let component = this.subject({url, uploadFinished});
let file = createFile();
stubSuccessfulUpload(server);
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(uploadFinished.calledOnce).to.be.true;
done();
});
});
it('fires uploadFinished action on failed upload', function (done) {
let uploadFinished = sinon.spy();
let component = this.subject({url, uploadFinished});
let file = createFile();
stubFailedUpload(server);
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(uploadFinished.calledOnce).to.be.true;
done();
});
});
it('displays invalid file type error', function (done) {
let component = this.subject({url});
let file = createFile();
stubFailedUpload(server, 415, 'UnsupportedMediaTypeError');
this.render();
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
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/);
expect(this.$('.btn-green').length, 'reset button is displayed').to.equal(1);
expect(this.$('.btn-green').text()).to.equal('Try Again');
done();
});
});
it('displays file too large for server error', function (done) {
let component = this.subject({url});
let file = createFile();
stubFailedUpload(server, 413, 'RequestEntityTooLargeError');
this.render();
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/The file you uploaded was larger/);
done();
});
});
it('handles file too large error directly from the web server', function (done) {
let component = this.subject({url});
let file = createFile();
server.post('/ghost/api/v0.1/uploads/', function () {
return [413, {}, ''];
});
this.render();
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/The file you uploaded was larger/);
done();
});
});
it('displays other server-side error with message', function (done) {
let component = this.subject({url});
let file = createFile();
stubFailedUpload(server, 400, 'UnknownError');
this.render();
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/Error: UnknownError/);
done();
});
});
it('handles unknown failure', function (done) {
let component = this.subject({url});
let file = createFile();
server.post('/ghost/api/v0.1/uploads/', function () {
return [500, {'Content-Type': 'application/json'}, ''];
});
this.render();
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/Something went wrong/);
done();
});
});
it('can be reset after a failed upload', function (done) {
let component = this.subject({url});
let file = createFile();
stubFailedUpload(server, 400, 'UnknownError');
this.render();
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
run(() => {
this.$('.btn-green').click();
});
});
wait().then(() => {
expect(this.$('input[type="file"]').length).to.equal(1);
done();
});
});
it('displays upload progress', function (done) {
let component = this.subject({url, uploadFinished: done});
let file = createFile();
// pretender fires a progress event every 50ms
stubSuccessfulUpload(server, 150);
this.render();
run(() => {
component.send('fileSelected', [file]);
});
// after 75ms we should have had one progress event
run.later(this, function () {
expect(this.$('.progress .bar').length).to.equal(1);
let [_, percentageWidth] = this.$('.progress .bar').attr('style').match(/width: (\d+)%?/);
expect(percentageWidth).to.be.above(0);
expect(percentageWidth).to.be.below(100);
}, 75);
});
it('triggers file upload on file drop', function (done) {
let uploadSuccess = sinon.spy();
let component = this.subject({url, uploadSuccess});
let file = createFile();
let drop = Ember.$.Event('drop', {
dataTransfer: {
files: [file]
}
});
stubSuccessfulUpload(server);
this.render();
run(() => {
this.$().trigger(drop);
});
wait().then(() => {
expect(uploadSuccess.calledOnce).to.be.true;
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
done();
});
});
}
);

View File

@ -1,344 +0,0 @@
/* jshint expr:true */
/* global Blob */
import { expect } from 'chai';
import {
describeComponent,
it
} from 'ember-mocha';
import Ember from 'ember';
import sinon from 'sinon';
import Pretender from 'pretender';
import wait from 'ember-test-helpers/wait';
const {run} = Ember;
const createFile = function (content = ['test'], options = {}) {
let {
name,
type,
lastModifiedDate
} = options;
let file = new Blob(content, {type: type ? type : 'text/plain'});
file.name = name ? name : 'text.txt';
return file;
};
const stubSuccessfulUpload = function (server, delay = 0) {
server.post('/ghost/api/v0.1/uploads/', function () {
return [200, {'Content-Type': 'application/json'}, '"/content/images/test.png"'];
}, delay);
};
const stubFailedUpload = function (server, code, error, delay = 0) {
server.post('/ghost/api/v0.1/uploads/', function () {
return [code, {'Content-Type': 'application/json'}, JSON.stringify({
errors: [{
errorType: error,
message: `Error: ${error}`
}]
})];
}, delay);
};
describeComponent(
'gh-image-uploader',
'Unit: Component: gh-image-uploader',
{
needs: [
'service:config',
'service:session',
'service:ajax',
'service:feature',
'component:x-file-input',
'component:one-way-input'
],
unit: true
},
function() {
let server;
beforeEach(function () {
server = new Pretender();
});
afterEach(function () {
server.shutdown();
});
it('renders', function() {
// creates the component instance
let component = this.subject();
// renders the component on the page
this.render();
expect(component).to.be.ok;
expect(this.$()).to.have.length(1);
});
it('fires update action on successful upload', function (done) {
let component = this.subject();
let update = sinon.spy();
let file = createFile();
stubSuccessfulUpload(server);
this.render();
component.attrs.update = update;
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(update.calledOnce).to.be.true;
expect(update.firstCall.args[0]).to.equal('/content/images/test.png');
done();
});
});
it('fires uploadStarted action on upload start', function (done) {
let component = this.subject();
let uploadStarted = sinon.spy();
let file = createFile();
stubSuccessfulUpload(server);
this.render();
component.attrs.update = () => {};
component.attrs.uploadStarted = uploadStarted;
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(uploadStarted.calledOnce).to.be.true;
done();
});
});
it('fires uploadFinished action on successful upload', function (done) {
let component = this.subject();
let uploadFinished = sinon.spy();
let file = createFile();
stubSuccessfulUpload(server);
this.render();
component.attrs.update = () => {};
component.attrs.uploadFinished = uploadFinished;
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(uploadFinished.calledOnce).to.be.true;
done();
});
});
it('fires uploadFinished action on failed upload', function (done) {
let component = this.subject();
let uploadFinished = sinon.spy();
let file = createFile();
stubFailedUpload(server);
this.render();
component.attrs.update = () => {};
component.attrs.uploadFinished = uploadFinished;
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(uploadFinished.calledOnce).to.be.true;
done();
});
});
it('displays invalid file type error', function (done) {
let component = this.subject();
let file = createFile();
stubFailedUpload(server, 415, 'UnsupportedMediaTypeError');
this.render();
component.attrs.update = () => {};
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
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/);
expect(this.$('.btn-green').length, 'reset button is displayed').to.equal(1);
expect(this.$('.btn-green').text()).to.equal('Try Again');
done();
});
});
it('displays file too large for server error', function (done) {
let component = this.subject();
let file = createFile();
stubFailedUpload(server, 413, 'RequestEntityTooLargeError');
this.render();
component.attrs.update = () => {};
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/The image you uploaded was larger/);
done();
});
});
it('handles file too large error directly from the web server', function (done) {
let component = this.subject();
let file = createFile();
server.post('/ghost/api/v0.1/uploads/', function () {
return [413, {}, ''];
});
this.render();
component.attrs.update = () => {};
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/The image you uploaded was larger/);
done();
});
});
it('displays other server-side error with message', function (done) {
let component = this.subject();
let file = createFile();
stubFailedUpload(server, 400, 'UnknownError');
this.render();
component.attrs.update = () => {};
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/Error: UnknownError/);
done();
});
});
it('handles unknown failure', function (done) {
let component = this.subject();
let file = createFile();
server.post('/ghost/api/v0.1/uploads/', function () {
return [500, {'Content-Type': 'application/json'}, ''];
});
this.render();
component.attrs.update = () => {};
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
expect(this.$('.failed').text()).to.match(/Something went wrong/);
done();
});
});
it('can be reset after a failed upload', function (done) {
let component = this.subject();
let file = createFile();
stubFailedUpload(server, 400, 'UnknownError');
this.render();
component.attrs.update = () => {};
run(() => {
component.send('fileSelected', [file]);
});
wait().then(() => {
run(() => {
this.$('.btn-green').click();
});
});
wait().then(() => {
expect(this.$('input[type="file"]').length).to.equal(1);
done();
});
});
it('displays upload progress', function (done) {
let component = this.subject();
let file = createFile();
// pretender fires a progress event every 50ms
stubSuccessfulUpload(server, 150);
this.render();
component.attrs.update = () => {};
component.attrs.uploadFinished = done;
run(() => {
component.send('fileSelected', [file]);
});
// after 75ms we should have had one progress event
run.later(this, function () {
expect(this.$('.progress .bar').length).to.equal(1);
let [_, percentageWidth] = this.$('.progress .bar').attr('style').match(/width: (\d+)%?/);
expect(percentageWidth).to.be.above(0);
expect(percentageWidth).to.be.below(100);
}, 75);
});
it('triggers file upload on file drop', function (done) {
let component = this.subject();
let file = createFile();
let update = sinon.spy();
let drop = Ember.$.Event('drop', {
dataTransfer: {
files: [file]
}
});
stubSuccessfulUpload(server);
this.render();
component.attrs.update = update;
run(() => {
this.$().trigger(drop);
});
wait().then(() => {
expect(update.calledOnce).to.be.true;
expect(update.firstCall.args[0]).to.equal('/content/images/test.png');
done();
});
});
}
);

View File

@ -7,7 +7,7 @@ import {
describeModule(
'route:subscribers/import',
'SubscribersImportRoute',
'Unit: Route: subscribers/import',
{
// Specify the other units that are required for this test.
needs: ['service:notifications']