Skip to content

Commit

Permalink
Merge pull request TryGhost#27 from kevinansfield/improve-file-upload…
Browse files Browse the repository at this point in the history
…-tests

Improve uploader tests + test subscribers CSV import
  • Loading branch information
ErisDS committed May 27, 2016
2 parents c8afbc5 + 3ce26bc commit 5cec40f
Show file tree
Hide file tree
Showing 12 changed files with 489 additions and 673 deletions.
3 changes: 2 additions & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"-Promise",
"-Notification",
"validator",
"moment"
"moment",
"fileUpload"
],
"browser": true,
"boss": true,
Expand Down
11 changes: 11 additions & 0 deletions app/components/gh-file-input.js
Original file line number Diff line number Diff line change
@@ -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);
}
});
4 changes: 2 additions & 2 deletions app/templates/components/gh-file-uploader.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
4 changes: 2 additions & 2 deletions app/templates/components/gh-image-uploader.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -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'}}>
Expand Down
34 changes: 28 additions & 6 deletions tests/acceptance/subscribers-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
});
});
Expand Down
33 changes: 33 additions & 0 deletions tests/helpers/file-upload.js
Original file line number Diff line number Diff line change
@@ -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]}
);
});
1 change: 1 addition & 0 deletions tests/helpers/start-app.js
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
207 changes: 205 additions & 2 deletions tests/integration/components/gh-file-uploader-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -39,6 +41,7 @@ describeComponent(

beforeEach(function () {
server = new Pretender();
this.set('uploadUrl', '/ghost/api/v0.1/uploads/');
});

afterEach(function () {
Expand All @@ -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);
Expand All @@ -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}}`);

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

0 comments on commit 5cec40f

Please sign in to comment.