mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-07 19:46:37 +03:00
Merge pull request #27 from kevinansfield/improve-file-upload-tests
Improve uploader tests + test subscribers CSV import
This commit is contained in:
commit
3e64cbe5f9
@ -6,7 +6,8 @@
|
||||
"-Promise",
|
||||
"-Notification",
|
||||
"validator",
|
||||
"moment"
|
||||
"moment",
|
||||
"fileUpload"
|
||||
],
|
||||
"browser": true,
|
||||
"boss": true,
|
||||
|
11
ghost/admin/app/components/gh-file-input.js
Normal file
11
ghost/admin/app/components/gh-file-input.js
Normal 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);
|
||||
}
|
||||
});
|
@ -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}}
|
||||
|
@ -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'}}>
|
||||
|
@ -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
|
||||
});
|
||||
});
|
||||
|
33
ghost/admin/tests/helpers/file-upload.js
Normal file
33
ghost/admin/tests/helpers/file-upload.js
Normal 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]}
|
||||
);
|
||||
});
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -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 () {
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -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']
|
||||
|
Loading…
Reference in New Issue
Block a user