From ea58dc6f85e96c20e6d6534fb1e1abf3d425b88c Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Fri, 3 Mar 2017 08:59:01 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20remove=20URL=20input=20option=20?= =?UTF-8?q?from=20image=20upload=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refs https://github.com/TryGhost/Ghost/issues/8032 - `fileStorage: false` config is going away, it predates storage engines and will simplify future image optimisation work - simplifies UI, it can be brought back in the future in a more robust fashion if required --- .../gh-image-uploader-with-preview.js | 12 - .../admin/app/components/gh-image-uploader.js | 49 +- .../admin/app/components/gh-profile-image.js | 1 - .../admin/app/styles/components/uploader.css | 45 -- .../templates/components/gh-ed-preview.hbs | 3 +- .../gh-image-uploader-with-preview.hbs | 2 - .../components/gh-image-uploader.hbs | 35 +- .../templates/components/gh-profile-image.hbs | 2 +- .../components/modals/upload-image.hbs | 2 - .../admin/app/templates/settings/general.hbs | 6 +- ghost/admin/app/templates/setup/two.hbs | 2 +- ghost/admin/app/templates/signup.hbs | 2 +- ghost/admin/app/templates/team/user.hbs | 4 +- .../addon/components/cards/image-card.js | 47 +- .../addon/templates/components/image-card.hbs | 32 +- .../tests/acceptance/settings/general-test.js | 2 - .../components/gh-image-uploader-test.js | 701 ++++++++---------- .../components/gh-profile-image-test.js | 11 - 18 files changed, 318 insertions(+), 640 deletions(-) diff --git a/ghost/admin/app/components/gh-image-uploader-with-preview.js b/ghost/admin/app/components/gh-image-uploader-with-preview.js index 538e48d4c9..af31b62e76 100644 --- a/ghost/admin/app/components/gh-image-uploader-with-preview.js +++ b/ghost/admin/app/components/gh-image-uploader-with-preview.js @@ -9,12 +9,6 @@ export default Component.extend({ } }, - onInput() { - if (typeof this.attrs.onInput === 'function') { - this.attrs.onInput(...arguments); - } - }, - uploadStarted() { if (typeof this.attrs.uploadStarted === 'function') { this.attrs.uploadStarted(...arguments); @@ -27,12 +21,6 @@ export default Component.extend({ } }, - formChanged() { - if (typeof this.attrs.formChanged === 'function') { - this.attrs.formChanged(...arguments); - } - }, - remove() { invokeAction(this, 'remove'); } diff --git a/ghost/admin/app/components/gh-image-uploader.js b/ghost/admin/app/components/gh-image-uploader.js index bf5f21841c..69ebdc57d1 100644 --- a/ghost/admin/app/components/gh-image-uploader.js +++ b/ghost/admin/app/components/gh-image-uploader.js @@ -5,7 +5,6 @@ import {htmlSafe} from 'ember-string'; import {isBlank} from 'ember-utils'; import {isEmberArray} from 'ember-array/utils'; import run from 'ember-runloop'; - import {invokeAction} from 'ember-invoke-action'; import ghostPaths from 'ghost-admin/utils/ghost-paths'; import { @@ -27,18 +26,15 @@ export default Component.extend({ accept: null, extensions: null, uploadUrl: null, - allowUrlInput: true, validate: null, dragClass: null, failureMessage: null, file: null, - formType: 'upload', url: null, uploadPercentage: 0, ajax: injectService(), - config: injectService(), notifications: injectService(), _defaultAccept: 'image/gif,image/jpg,image/jpeg,image/png,image/svg+xml', @@ -75,17 +71,6 @@ export default Component.extend({ return htmlSafe(`width: ${width}`); }), - canShowUploadForm: computed('config.fileStorage', function () { - return this.get('config.fileStorage') !== false; - }), - - showUploadForm: computed('formType', function () { - let canShowUploadForm = this.get('canShowUploadForm'); - let formType = this.get('formType'); - - return formType === 'upload' && canShowUploadForm; - }), - didReceiveAttrs() { let image = this.get('image'); this.set('url', image); @@ -102,8 +87,6 @@ export default Component.extend({ }, dragOver(event) { - let showUploadForm = this.get('showUploadForm'); - if (!event.dataTransfer) { return; } @@ -116,32 +99,21 @@ export default Component.extend({ event.stopPropagation(); event.preventDefault(); - if (showUploadForm) { - this.set('dragClass', '-drag-over'); - } + this.set('dragClass', '-drag-over'); }, dragLeave(event) { - let showUploadForm = this.get('showUploadForm'); - event.preventDefault(); - - if (showUploadForm) { - this.set('dragClass', null); - } + this.set('dragClass', null); }, drop(event) { - let showUploadForm = this.get('showUploadForm'); - event.preventDefault(); this.set('dragClass', null); - if (showUploadForm) { - if (event.dataTransfer.files) { - this.send('fileSelected', event.dataTransfer.files); - } + if (event.dataTransfer.files) { + this.send('fileSelected', event.dataTransfer.files); } }, @@ -269,24 +241,11 @@ export default Component.extend({ } }, - onInput(url) { - this.set('url', url); - invokeAction(this, 'onInput', url); - }, - reset() { this.set('file', null); this.set('uploadPercentage', 0); }, - switchForm(formType) { - this.set('formType', formType); - - run.scheduleOnce('afterRender', this, function () { - invokeAction(this, 'formChanged', formType); - }); - }, - saveUrl() { let url = this.get('url'); invokeAction(this, 'update', url); diff --git a/ghost/admin/app/components/gh-profile-image.js b/ghost/admin/app/components/gh-profile-image.js index 2854f1f713..3f69c75735 100644 --- a/ghost/admin/app/components/gh-profile-image.js +++ b/ghost/admin/app/components/gh-profile-image.js @@ -29,7 +29,6 @@ export default Component.extend({ validEmail: '', hasUploadedImage: false, - fileStorage: true, ajax: AjaxService.create(), config: injectService(), diff --git a/ghost/admin/app/styles/components/uploader.css b/ghost/admin/app/styles/components/uploader.css index 5186e00b50..ce0babe611 100644 --- a/ghost/admin/app/styles/components/uploader.css +++ b/ghost/admin/app/styles/components/uploader.css @@ -73,51 +73,6 @@ font-size: 1.6rem; } -.gh-image-uploader .image-upload, -.gh-image-uploader .image-url { - position: absolute; - bottom: 0; - left: 0; - display: block; - padding: 10px; - color: var(--midgrey); - text-decoration: none; - font-size: 14px; - line-height: 12px; -} - -.gh-image-uploader a { - color: var(--midgrey); - text-decoration: none; -} - -.gh-image-uploader a:hover { - color: var(--darkgrey); -} -.gh-image-uploader .image-upload:hover, -.gh-image-uploader .image-url:hover { - cursor: pointer; -} - -.gh-image-uploader form { - padding: 55px 60px; - width: 100%; -} - -.gh-image-uploader input.url { - margin: 0 0 10px 0; - padding: 9px 7px; - outline: 0; - background: #fff; - vertical-align: middle; - font: -webkit-small-control; - font-size: 1.4rem; -} - -.gh-image-uploader input.url + .gh-btn.gh-btn-blue { - color: #fff; -} - .gh-image-uploader .image-cancel:hover { background: var(--red); color: #fff; diff --git a/ghost/admin/app/templates/components/gh-ed-preview.hbs b/ghost/admin/app/templates/components/gh-ed-preview.hbs index 6b7dc86f93..ad6da656ba 100644 --- a/ghost/admin/app/templates/components/gh-ed-preview.hbs +++ b/ghost/admin/app/templates/components/gh-ed-preview.hbs @@ -8,7 +8,6 @@ update=(action "updateImageSrc" uploader.index) remove=(action "updateImageSrc" uploader.index "") uploadStarted=uploadStarted - uploadFinished=uploadFinished - formChanged=(action "updateHeight")}} + uploadFinished=uploadFinished}} {{/ember-wormhole}} {{/each}} diff --git a/ghost/admin/app/templates/components/gh-image-uploader-with-preview.hbs b/ghost/admin/app/templates/components/gh-image-uploader-with-preview.hbs index b6a3b46a32..4469273c74 100644 --- a/ghost/admin/app/templates/components/gh-image-uploader-with-preview.hbs +++ b/ghost/admin/app/templates/components/gh-image-uploader-with-preview.hbs @@ -10,9 +10,7 @@ text=text altText=altText update=(action 'update') - onInput=(action 'onInput') uploadStarted=(action 'uploadStarted') uploadFinished=(action 'uploadFinished') - formChanged=(action 'formChanged') }} {{/if}} diff --git a/ghost/admin/app/templates/components/gh-image-uploader.hbs b/ghost/admin/app/templates/components/gh-image-uploader.hbs index 16749155de..0b62f60dc5 100644 --- a/ghost/admin/app/templates/components/gh-image-uploader.hbs +++ b/ghost/admin/app/templates/components/gh-image-uploader.hbs @@ -12,33 +12,10 @@ {{/if}} {{else}} - {{#if showUploadForm}} - {{!-- file selection/drag-n-drop --}} -
- {{#gh-file-input multiple=false alt=description action=(action "fileSelected") accept=accept}} -
{{description}}
- {{/gh-file-input}} -
- {{#if allowUrlInput}} - - - - {{/if}} - {{else}} - {{!-- URL input --}} -
- {{gh-input url class="url" placeholder="http://" update=(action "onInput") onenter=(action "saveUrl")}} - {{#if saveButton}} - - {{else}} -
{{description}}
- {{/if}} -
- - {{#if canShowUploadForm}} - - - - {{/if}} - {{/if}} + {{!-- file selection/drag-n-drop --}} +
+ {{#gh-file-input multiple=false alt=description action=(action "fileSelected") accept=accept}} +
{{description}}
+ {{/gh-file-input}} +
{{/if}} diff --git a/ghost/admin/app/templates/components/gh-profile-image.hbs b/ghost/admin/app/templates/components/gh-profile-image.hbs index 0a3ca9b4b1..08bcff725a 100644 --- a/ghost/admin/app/templates/components/gh-profile-image.hbs +++ b/ghost/admin/app/templates/components/gh-profile-image.hbs @@ -13,5 +13,5 @@ Upload an image - {{#if fileStorage}}{{/if}} + diff --git a/ghost/admin/app/templates/components/modals/upload-image.hbs b/ghost/admin/app/templates/components/modals/upload-image.hbs index 3ff365faad..5ecd71c4d3 100644 --- a/ghost/admin/app/templates/components/modals/upload-image.hbs +++ b/ghost/admin/app/templates/components/modals/upload-image.hbs @@ -11,10 +11,8 @@ image=newUrl saveButton=false update=(action 'fileUploaded') - onInput=(action (mut newUrl)) accept=model.accept extensions=model.extensions - allowUrlInput=model.allowUrlInput uploadUrl=model.uploadUrl }} {{/if}} diff --git a/ghost/admin/app/templates/settings/general.hbs b/ghost/admin/app/templates/settings/general.hbs index f29316453d..da9e8e54ae 100644 --- a/ghost/admin/app/templates/settings/general.hbs +++ b/ghost/admin/app/templates/settings/general.hbs @@ -66,7 +66,7 @@ {{#if showUploadIconModal}} {{gh-fullscreen-modal "upload-image" - model=(hash model=model imageProperty="icon" accept=iconMimeTypes extensions=iconExtensions allowUrlInput=false uploadUrl="/uploads/icon/") + model=(hash model=model imageProperty="icon" accept=iconMimeTypes extensions=iconExtensions uploadUrl="/uploads/icon/") close=(action "toggleUploadIconModal") modifier="action wide"}} {{/if}} @@ -86,7 +86,7 @@ {{#if showUploadLogoModal}} {{gh-fullscreen-modal "upload-image" - model=(hash model=model imageProperty="logo" allowUrlInput=true) + model=(hash model=model imageProperty="logo") close=(action "toggleUploadLogoModal") modifier="action wide"}} {{/if}} @@ -106,7 +106,7 @@ {{#if showUploadCoverModal}} {{gh-fullscreen-modal "upload-image" - model=(hash model=model imageProperty="cover" allowUrlInput=true) + model=(hash model=model imageProperty="cover") close=(action "toggleUploadCoverModal") modifier="action wide"}} {{/if}} diff --git a/ghost/admin/app/templates/setup/two.hbs b/ghost/admin/app/templates/setup/two.hbs index 93bcf13152..37d123bb72 100644 --- a/ghost/admin/app/templates/setup/two.hbs +++ b/ghost/admin/app/templates/setup/two.hbs @@ -38,7 +38,7 @@ - {{gh-profile-image fileStorage=config.fileStorage email=email setImage="setImage"}} + {{gh-profile-image email=email setImage="setImage"}} {{#gh-form-group errors=errors hasValidated=hasValidated property="email"}} diff --git a/ghost/admin/app/templates/signup.hbs b/ghost/admin/app/templates/signup.hbs index 06eaf65d56..d64c13b4d8 100644 --- a/ghost/admin/app/templates/signup.hbs +++ b/ghost/admin/app/templates/signup.hbs @@ -26,7 +26,7 @@ - {{gh-profile-image fileStorage=config.fileStorage email=model.email setImage="setImage"}} + {{gh-profile-image email=model.email setImage="setImage"}} {{#gh-form-group}} diff --git a/ghost/admin/app/templates/team/user.hbs b/ghost/admin/app/templates/team/user.hbs index 42003cd319..1f500ce82f 100644 --- a/ghost/admin/app/templates/team/user.hbs +++ b/ghost/admin/app/templates/team/user.hbs @@ -55,7 +55,7 @@ {{#if showUploadCoverModal}} {{gh-fullscreen-modal "upload-image" - model=(hash model=user imageProperty="cover" allowUrlInput=true) + model=(hash model=user imageProperty="cover") close=(action "toggleUploadCoverModal") modifier="action wide"}} {{/if}} @@ -74,7 +74,7 @@ {{#if showUploadImageModal}} {{gh-fullscreen-modal "upload-image" - model=(hash model=user imageProperty="image" allowUrlInput=true) + model=(hash model=user imageProperty="image") close=(action "toggleUploadImageModal") modifier="action wide"}} {{/if}} diff --git a/ghost/admin/lib/gh-koenig/addon/components/cards/image-card.js b/ghost/admin/lib/gh-koenig/addon/components/cards/image-card.js index cd4bfe478a..5951d6c623 100644 --- a/ghost/admin/lib/gh-koenig/addon/components/cards/image-card.js +++ b/ghost/admin/lib/gh-koenig/addon/components/cards/image-card.js @@ -33,12 +33,10 @@ export default Component.extend({ dragClass: null, failureMessage: null, file: null, - formType: 'upload', url: null, uploadPercentage: 0, ajax: injectService(), - config: injectService(), notifications: injectService(), // TODO: this wouldn't be necessary if the server could accept direct @@ -70,17 +68,6 @@ export default Component.extend({ return htmlSafe(`width: ${width}`); }), - canShowUploadForm: computed('config.fileStorage', function () { - return this.get('config.fileStorage') !== false; - }), - - showUploadForm: computed('formType', function () { - let canShowUploadForm = this.get('canShowUploadForm'); - let formType = this.get('formType'); - - return formType === 'upload' && canShowUploadForm; - }), - didReceiveAttrs() { let image = this.get('payload'); if (image.img) { @@ -93,8 +80,6 @@ export default Component.extend({ }, dragOver(event) { - let showUploadForm = this.get('showUploadForm'); - if (!event.dataTransfer) { return; } @@ -107,32 +92,21 @@ export default Component.extend({ event.stopPropagation(); event.preventDefault(); - if (showUploadForm) { - this.set('dragClass', '-drag-over'); - } + this.set('dragClass', '-drag-over'); }, dragLeave(event) { - let showUploadForm = this.get('showUploadForm'); - event.preventDefault(); - - if (showUploadForm) { - this.set('dragClass', null); - } + this.set('dragClass', null); }, drop(event) { - let showUploadForm = this.get('showUploadForm'); - event.preventDefault(); this.set('dragClass', null); - if (showUploadForm) { - if (event.dataTransfer.files) { - this.send('fileSelected', event.dataTransfer.files); - } + if (event.dataTransfer.files) { + this.send('fileSelected', event.dataTransfer.files); } }, @@ -272,24 +246,11 @@ export default Component.extend({ } }, - onInput(url) { - this.set('url', url); - invokeAction(this, 'onInput', url); - }, - reset() { this.set('file', null); this.set('uploadPercentage', 0); }, - switchForm(formType) { - this.set('formType', formType); - - run.scheduleOnce('afterRender', this, function () { - invokeAction(this, 'formChanged', formType); - }); - }, - saveUrl() { let url = this.get('url'); invokeAction(this, 'update', url); diff --git a/ghost/admin/lib/gh-koenig/addon/templates/components/image-card.hbs b/ghost/admin/lib/gh-koenig/addon/templates/components/image-card.hbs index 3da8a11f6e..4a6540c654 100644 --- a/ghost/admin/lib/gh-koenig/addon/templates/components/image-card.hbs +++ b/ghost/admin/lib/gh-koenig/addon/templates/components/image-card.hbs @@ -15,32 +15,10 @@ {{/if}} {{else}} - {{#if showUploadForm}} {{!-- file selection/drag-n-drop --}} -
- {{#gh-file-input multiple=false alt=description action=(action 'fileSelected') accept=accept}} -
{{description}}
- {{/gh-file-input}} -
- - - - - {{else}} - {{!-- URL input --}} -
- {{gh-input url class="url" placeholder="http://" update=(action "onInput") onenter=(action "saveUrl")}} - {{#if saveButton}} - - {{else}} -
{{description}}
- {{/if}} -
- - {{#if canShowUploadForm}} - - - - {{/if}} - {{/if}} +
+ {{#gh-file-input multiple=false alt=description action=(action 'fileSelected') accept=accept}} +
{{description}}
+ {{/gh-file-input}} +
{{/if}} diff --git a/ghost/admin/tests/acceptance/settings/general-test.js b/ghost/admin/tests/acceptance/settings/general-test.js index dbb5feb650..b1ff49cf9b 100644 --- a/ghost/admin/tests/acceptance/settings/general-test.js +++ b/ghost/admin/tests/acceptance/settings/general-test.js @@ -101,7 +101,6 @@ describe('Acceptance: Settings - General', function () { andThen(() => { expect(find('.fullscreen-modal .modal-content .gh-image-uploader .description').text()).to.equal('Upload an image'); - expect(find('.fullscreen-modal .modal-content .gh-image-uploader .image-url').length, 'url upload').to.equal(1); }); // click cancel button @@ -121,7 +120,6 @@ describe('Acceptance: Settings - General', function () { andThen(() => { expect(find('.fullscreen-modal .modal-content .gh-image-uploader .description').text()).to.equal('Upload an image'); - expect(find('.fullscreen-modal .modal-content .gh-image-uploader .image-url').length, 'url upload').to.equal(0); }); // click cancel button diff --git a/ghost/admin/tests/integration/components/gh-image-uploader-test.js b/ghost/admin/tests/integration/components/gh-image-uploader-test.js index bbc00b7acd..6f326bca6a 100644 --- a/ghost/admin/tests/integration/components/gh-image-uploader-test.js +++ b/ghost/admin/tests/integration/components/gh-image-uploader-test.js @@ -12,14 +12,6 @@ import run from 'ember-runloop'; import Service from 'ember-service'; import {UnsupportedMediaTypeError} from 'ghost-admin/services/ajax'; -const keyCodes = { - enter: 13 -}; - -const configStub = Service.extend({ - fileStorage: true -}); - const notificationsStub = Service.extend({ showAPIError(/* error, options */) { // noop - to be stubbed @@ -60,10 +52,8 @@ describe('Integration: Component: gh-image-uploader', function() { let server; beforeEach(function () { - this.register('service:config', configStub); this.register('service:session', sessionStub); this.register('service:notifications', notificationsStub); - this.inject.service('config', {as: 'configService'}); this.inject.service('session', {as: 'sessionService'}); this.inject.service('notifications', {as: 'notifications'}); this.set('update', function () {}); @@ -80,498 +70,387 @@ describe('Integration: Component: gh-image-uploader', function() { expect(this.$()).to.have.length(1); }); - it('defaults to upload form', function () { - this.render(hbs`{{gh-image-uploader image=image}}`); - expect(this.$('input[type="file"]').length).to.equal(1); + it('renders form with supplied alt text', function () { + this.render(hbs`{{gh-image-uploader image=image altText="text test"}}`); + expect(this.$('.description').text().trim()).to.equal('Upload image of "text test"'); }); - it('defaults to url form with no filestorage config', function () { - this.set('configService.fileStorage', false); - this.render(hbs`{{gh-image-uploader image=image}}`); - expect(this.$('input[type="file"]').length).to.equal(0); - expect(this.$('input[type="text"].url').length).to.equal(1); + it('renders form with supplied text', function () { + this.render(hbs`{{gh-image-uploader image=image text="text test"}}`); + expect(this.$('.description').text().trim()).to.equal('text test'); }); - it('can switch between form types', function () { - this.render(hbs`{{gh-image-uploader image=image}}`); - expect(this.$('input[type="file"]').length).to.equal(1); - expect(this.$('input[type="text"].url').length).to.equal(0); + it('generates request to correct endpoint', function (done) { + stubSuccessfulUpload(server); - this.$('a.image-url').click(); + this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - expect(this.$('input[type="file"]').length, 'upload form is visible after switch to url form') - .to.equal(0); - expect(this.$('input[type="text"].url').length, 'url form is visible after switch to url form') - .to.equal(1); - - this.$('a.image-upload').click(); - - expect(this.$('input[type="file"]').length, 'upload form is visible after switch to upload form') - .to.equal(1); - expect(this.$('input[type="text"].url').length, 'url form is visible after switch to upload form') - .to.equal(0); + wait().then(() => { + expect(server.handledRequests.length).to.equal(1); + expect(server.handledRequests[0].url).to.equal('/ghost/api/v0.1/uploads/'); + expect(server.handledRequests[0].requestHeaders.Authorization).to.be.undefined; + done(); + }); }); - it('triggers formChanged action when switching between forms', function () { - let formChanged = sinon.spy(); - this.set('formChanged', formChanged); + it('adds authentication headers to request', function (done) { + stubSuccessfulUpload(server); - this.render(hbs`{{gh-image-uploader image=image formChanged=(action formChanged)}}`); + this.get('sessionService').set('isAuthenticated', true); - this.$('a.image-url').click(); - this.$('a.image-upload').click(); + this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - expect(formChanged.calledTwice).to.be.true; - expect(formChanged.firstCall.args[0]).to.equal('url-input'); - expect(formChanged.secondCall.args[0]).to.equal('upload'); + wait().then(() => { + let [request] = server.handledRequests; + expect(request.requestHeaders.Authorization).to.equal('Bearer token'); + done(); + }); }); - describe('file upload form', function () { - it('renders form with supplied alt text', function () { - this.render(hbs`{{gh-image-uploader image=image altText="text test"}}`); - expect(this.$('.description').text().trim()).to.equal('Upload image of "text test"'); + 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"]'), ['test'], {name: 'test.png'}); + + wait().then(() => { + expect(update.calledOnce).to.be.true; + expect(update.firstCall.args[0]).to.equal('/content/images/test.png'); + done(); }); + }); - it('renders form with supplied text', function () { - this.render(hbs`{{gh-image-uploader image=image text="text test"}}`); - expect(this.$('.description').text().trim()).to.equal('text test'); + 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"]'), ['test'], {name: 'test.png'}); + + wait().then(() => { + expect(update.calledOnce).to.be.false; + done(); }); + }); - it('generates request to correct endpoint', function (done) { - stubSuccessfulUpload(server); + it('fires fileSelected action on file selection', function (done) { + let fileSelected = sinon.spy(); + this.set('fileSelected', fileSelected); - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); + stubSuccessfulUpload(server); - wait().then(() => { - expect(server.handledRequests.length).to.equal(1); - expect(server.handledRequests[0].url).to.equal('/ghost/api/v0.1/uploads/'); - expect(server.handledRequests[0].requestHeaders.Authorization).to.be.undefined; - done(); - }); + this.render(hbs`{{gh-image-uploader image=image fileSelected=(action fileSelected) update=(action update)}}`); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); + + wait().then(() => { + expect(fileSelected.calledOnce).to.be.true; + expect(fileSelected.args[0]).to.not.be.blank; + done(); }); + }); - it('adds authentication headers to request', function (done) { - stubSuccessfulUpload(server); + it('fires uploadStarted action on upload start', function (done) { + let uploadStarted = sinon.spy(); + this.set('uploadStarted', uploadStarted); - this.get('sessionService').set('isAuthenticated', true); + stubSuccessfulUpload(server); - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); + this.render(hbs`{{gh-image-uploader image=image uploadStarted=(action uploadStarted) update=(action update)}}`); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - wait().then(() => { - let [request] = server.handledRequests; - expect(request.requestHeaders.Authorization).to.equal('Bearer token'); - done(); - }); + wait().then(() => { + expect(uploadStarted.calledOnce).to.be.true; + done(); }); + }); - it('fires update action on successful upload', function (done) { - let update = sinon.spy(); - this.set('update', update); + it('fires uploadFinished action on successful upload', function (done) { + let uploadFinished = sinon.spy(); + this.set('uploadFinished', uploadFinished); - stubSuccessfulUpload(server); + stubSuccessfulUpload(server); - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); + this.render(hbs`{{gh-image-uploader image=image uploadFinished=(action uploadFinished) update=(action update)}}`); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - wait().then(() => { - expect(update.calledOnce).to.be.true; - expect(update.firstCall.args[0]).to.equal('/content/images/test.png'); - done(); - }); + wait().then(() => { + expect(uploadFinished.calledOnce).to.be.true; + done(); }); + }); - it('doesn\'t fire update action on failed upload', function (done) { - let update = sinon.spy(); - this.set('update', update); + it('fires uploadFinished action on failed upload', function (done) { + let uploadFinished = sinon.spy(); + this.set('uploadFinished', uploadFinished); - stubFailedUpload(server, 500); + stubFailedUpload(server); - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); + this.render(hbs`{{gh-image-uploader image=image uploadFinished=(action uploadFinished) update=(action update)}}`); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - wait().then(() => { - expect(update.calledOnce).to.be.false; - done(); - }); + wait().then(() => { + expect(uploadFinished.calledOnce).to.be.true; + done(); }); + }); - it('fires fileSelected action on file selection', function (done) { - let fileSelected = sinon.spy(); - this.set('fileSelected', fileSelected); + 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"]'), ['test'], {name: 'test.png'}); - stubSuccessfulUpload(server); - - this.render(hbs`{{gh-image-uploader image=image fileSelected=(action fileSelected) update=(action update)}}`); - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - - wait().then(() => { - expect(fileSelected.calledOnce).to.be.true; - expect(fileSelected.args[0]).to.not.be.blank; - done(); - }); + 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.$('.gh-btn-green').length, 'reset button is displayed').to.equal(1); + expect(this.$('.gh-btn-green').text()).to.equal('Try Again'); + done(); }); + }); - it('fires uploadStarted action on upload start', function (done) { - let uploadStarted = sinon.spy(); - this.set('uploadStarted', uploadStarted); + 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"]'), ['test'], {name: 'test.png'}); - stubSuccessfulUpload(server); - - this.render(hbs`{{gh-image-uploader image=image uploadStarted=(action uploadStarted) update=(action update)}}`); - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - - wait().then(() => { - expect(uploadStarted.calledOnce).to.be.true; - done(); - }); + 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('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"]'), ['test'], {name: 'test.png'}); - - wait().then(() => { - expect(uploadFinished.calledOnce).to.be.true; - 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"]'), ['test'], {name: 'test.png'}); - 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"]'), ['test'], {name: 'test.png'}); - - wait().then(() => { - expect(uploadFinished.calledOnce).to.be.true; - done(); - }); + 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 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"]'), ['test'], {name: 'test.png'}); + 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"]'), ['test'], {name: 'test.png'}); - 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.$('.gh-btn-green').length, 'reset button is displayed').to.equal(1); - expect(this.$('.gh-btn-green').text()).to.equal('Try Again'); - done(); - }); + wait().then(() => { + expect(this.$('.failed').length, 'error message is displayed').to.equal(1); + expect(this.$('.failed').text()).to.match(/Error: UnknownError/); + 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"]'), ['test'], {name: 'test.png'}); - - 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 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"]'), ['test'], {name: 'test.png'}); - 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"]'), ['test'], {name: 'test.png'}); - - 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(); - }); + wait().then(() => { + expect(this.$('.failed').length, 'error message is displayed').to.equal(1); + expect(this.$('.failed').text()).to.match(/Something went wrong/); + 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"]'), ['test'], {name: 'test.png'}); + it('triggers notifications.showAPIError for VersionMismatchError', function (done) { + let showAPIError = sinon.spy(); + this.set('notifications.showAPIError', showAPIError); - wait().then(() => { - expect(this.$('.failed').length, 'error message is displayed').to.equal(1); - expect(this.$('.failed').text()).to.match(/Error: UnknownError/); - done(); - }); + stubFailedUpload(server, 400, 'VersionMismatchError'); + + this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); + + wait().then(() => { + expect(showAPIError.calledOnce).to.be.true; + 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"]'), ['test'], {name: 'test.png'}); + it('doesn\'t trigger notifications.showAPIError for other errors', function (done) { + let showAPIError = sinon.spy(); + this.set('notifications.showAPIError', showAPIError); - wait().then(() => { - expect(this.$('.failed').length, 'error message is displayed').to.equal(1); - expect(this.$('.failed').text()).to.match(/Something went wrong/); - done(); - }); + stubFailedUpload(server, 400, 'UnknownError'); + this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); + + wait().then(() => { + expect(showAPIError.called).to.be.false; + done(); }); + }); - it('triggers notifications.showAPIError for VersionMismatchError', function (done) { - let showAPIError = sinon.spy(); - this.set('notifications.showAPIError', showAPIError); - - stubFailedUpload(server, 400, 'VersionMismatchError'); - - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - - wait().then(() => { - expect(showAPIError.calledOnce).to.be.true; - done(); - }); - }); - - it('doesn\'t trigger notifications.showAPIError for other errors', function (done) { - let showAPIError = sinon.spy(); - this.set('notifications.showAPIError', showAPIError); - - stubFailedUpload(server, 400, 'UnknownError'); - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - - wait().then(() => { - expect(showAPIError.called).to.be.false; - 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"]'), ['test'], {type: 'test.png'}); - - wait().then(() => { - run(() => { - this.$('.gh-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"]'), ['test'], {name: 'test.png'}); - - // 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); - - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); + 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"]'), ['test'], {type: 'test.png'}); + wait().then(() => { run(() => { - // eslint-disable-next-line new-cap - let dragover = $.Event('dragover', { - dataTransfer: { - files: [] - } - }); - this.$('.gh-image-uploader').trigger(dragover); + this.$('.gh-btn-green').click(); }); - - expect(this.$('.gh-image-uploader').hasClass('-drag-over'), 'has drag-over class').to.be.true; - - run(() => { - this.$('.gh-image-uploader').trigger('dragleave'); - }); - - 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(); + 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"]'), ['test'], {name: 'test.png'}); + + // 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); + + this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); + + run(() => { // eslint-disable-next-line new-cap - let drop = $.Event('drop', { + let dragover = $.Event('dragover', { dataTransfer: { - files: [createFile(['test'], {name: 'test.png'})] + files: [] } }); - - 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(); - }); + this.$('.gh-image-uploader').trigger(dragover); }); - it('validates extension by default', function (done) { - let uploadSuccess = sinon.spy(); - let uploadFailed = sinon.spy(); + expect(this.$('.gh-image-uploader').hasClass('-drag-over'), 'has drag-over class').to.be.true; - 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'], {name: 'test.json'}); - - 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(); - }); + run(() => { + this.$('.gh-image-uploader').trigger('dragleave'); }); - it('uploads if validate action supplied and returns true', function (done) { - let validate = sinon.stub().returns(true); - let uploadSuccess = sinon.spy(); + expect(this.$('.gh-image-uploader').hasClass('-drag-over'), 'has drag-over class').to.be.false; + }); - 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'], {name: 'test.txt'}); - - wait().then(() => { - expect(validate.calledOnce).to.be.true; - expect(uploadSuccess.calledOnce).to.be.true; - done(); - }); + it('triggers file upload on file drop', function (done) { + let uploadSuccess = sinon.spy(); + // eslint-disable-next-line new-cap + let drop = $.Event('drop', { + dataTransfer: { + files: [createFile(['test'], {name: 'test.png'})] + } }); - 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('uploadSuccess', uploadSuccess); - this.set('validate', validate); - this.set('uploadSuccess', uploadSuccess); - this.set('uploadFailed', uploadFailed); + stubSuccessfulUpload(server); + this.render(hbs`{{gh-image-uploader uploadSuccess=(action uploadSuccess)}}`); - stubSuccessfulUpload(server); + run(() => { + this.$('.gh-image-uploader').trigger(drop); + }); - this.render(hbs`{{gh-image-uploader - uploadSuccess=(action uploadSuccess) - uploadFailed=(action uploadFailed) - validate=(action validate)}}`); - - fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.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(); - }); + 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 () { - beforeEach(function () { - this.set('configService.fileStorage', false); + it('validates extension 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'], {name: 'test.json'}); + + 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('displays save button by default', function () { - this.set('image', 'http://example.com/test.png'); - this.render(hbs`{{gh-image-uploader image=image text="text test"}}`); - expect(this.$('button').length).to.equal(1); - expect(this.$('input[type="text"]').val()).to.equal('http://example.com/test.png'); + 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'], {name: 'test.txt'}); + + wait().then(() => { + expect(validate.calledOnce).to.be.true; + expect(uploadSuccess.calledOnce).to.be.true; + done(); }); + }); - it('can render without a save button', function () { - this.render(hbs`{{gh-image-uploader image=image saveButton=false text="text test"}}`); - expect(this.$('button').length).to.equal(0); - expect(this.$('.description').text().trim()).to.equal('text test'); - }); + 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(); - it('fires update action when save button clicked', function () { - let update = sinon.spy(); - this.set('update', update); + this.set('validate', validate); + this.set('uploadSuccess', uploadSuccess); + this.set('uploadFailed', uploadFailed); - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); + stubSuccessfulUpload(server); - this.$('input[type="text"]').val('saved url'); - this.$('input[type="text"]').change(); - this.$('button.gh-btn-blue').click(); + this.render(hbs`{{gh-image-uploader + uploadSuccess=(action uploadSuccess) + uploadFailed=(action uploadFailed) + validate=(action validate)}}`); - expect(update.calledOnce).to.be.true; - expect(update.firstCall.args[0]).to.equal('saved url'); - }); + fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'}); - it('fires onInput action when typing URL', function () { - let onInput = sinon.spy(); - this.set('onInput', onInput); - - this.render(hbs`{{gh-image-uploader image=image onInput=(action onInput)}}`); - - this.$('input[type="text"]').val('input url'); - this.$('input[type="text"]').change(); - - expect(onInput.calledOnce).to.be.true; - expect(onInput.firstCall.args[0]).to.equal('input url'); - }); - - it('saves on enter key', function () { - let update = sinon.spy(); - this.set('update', update); - - this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`); - - this.$('input[type="text"]').val('saved url'); - this.$('input[type="text"]').change(); - this.$('input[type="text"]').trigger( - // eslint-disable-next-line new-cap - $.Event('keyup', {keyCode: keyCodes.enter, which: keyCodes.enter}) - ); - - expect(update.calledOnce).to.be.true; - expect(update.firstCall.args[0]).to.equal('saved url'); + 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(); }); }); }); diff --git a/ghost/admin/tests/integration/components/gh-profile-image-test.js b/ghost/admin/tests/integration/components/gh-profile-image-test.js index e2ca5b62b7..7ca7617959 100644 --- a/ghost/admin/tests/integration/components/gh-profile-image-test.js +++ b/ghost/admin/tests/integration/components/gh-profile-image-test.js @@ -61,17 +61,6 @@ describe('Integration: Component: gh-profile-image', function () { expect(this.$()).to.have.length(1); }); - it('renders and tears down ok with fileStorage:false', function () { - this.set('fileStorage', false); - - this.render(hbs` - {{gh-profile-image fileStorage=fileStorage}} - `); - - expect(this.$()).to.have.length(1); - expect(this.$('input')).to.have.length(0); - }), - it('renders default image if no email supplied', function () { this.set('email', null);