mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 14:43:08 +03:00
✨ streamline image uploads in settings/general (#702)
refs TryGhost/Ghost#8455 - ensure `uploadUrls` and `errors` are cleared in `gh-uploader` when new uploads are started - yield `isUploading` in `gh-uploader` component - replace image upload modals in settings/general with in-page uploads
This commit is contained in:
parent
e8ff4ac1dd
commit
8d66430c2a
@ -18,6 +18,13 @@ import run from 'ember-runloop';
|
||||
// "allowMultiple" attribute so that single-image uploads don't allow multiple
|
||||
// simultaneous uploads
|
||||
|
||||
/**
|
||||
* Result from a file upload
|
||||
* @typedef {Object} UploadResult
|
||||
* @property {string} fileName - file name, eg "my-image.png"
|
||||
* @property {string} url - url relative to Ghost root,eg "/content/images/2017/05/my-image.png"
|
||||
*/
|
||||
|
||||
const UploadTracker = EmberObject.extend({
|
||||
file: null,
|
||||
total: 0,
|
||||
@ -153,6 +160,7 @@ export default Component.extend({
|
||||
_uploadFiles: task(function* (files) {
|
||||
let uploads = [];
|
||||
|
||||
this._reset();
|
||||
this.onStart();
|
||||
|
||||
// NOTE: for...of loop results in a transpilation that errors in Edge,
|
||||
@ -239,7 +247,7 @@ export default Component.extend({
|
||||
// NOTE: this is necessary because the API doesn't accept direct file uploads
|
||||
_getFormData(file) {
|
||||
let formData = new FormData();
|
||||
formData.append(this.get('paramName'), file);
|
||||
formData.append(this.get('paramName'), file, file.name);
|
||||
return formData;
|
||||
},
|
||||
|
||||
@ -269,10 +277,11 @@ export default Component.extend({
|
||||
},
|
||||
|
||||
_reset() {
|
||||
this.set('errors', null);
|
||||
this.set('errors', []);
|
||||
this.set('totalSize', 0);
|
||||
this.set('uploadedSize', 0);
|
||||
this.set('uploadPercentage', 0);
|
||||
this.set('uploadUrls', []);
|
||||
this._uploadTrackers = [];
|
||||
},
|
||||
|
||||
|
@ -5,25 +5,26 @@ import observer from 'ember-metal/observer';
|
||||
import run from 'ember-runloop';
|
||||
import randomPassword from 'ghost-admin/utils/random-password';
|
||||
import {task} from 'ember-concurrency';
|
||||
import {
|
||||
IMAGE_MIME_TYPES,
|
||||
IMAGE_EXTENSIONS
|
||||
} from 'ghost-admin/components/gh-image-uploader';
|
||||
|
||||
export default Controller.extend({
|
||||
|
||||
availableTimezones: null,
|
||||
|
||||
showUploadLogoModal: false,
|
||||
showUploadCoverModal: false,
|
||||
showUploadIconModal: false,
|
||||
|
||||
config: injectService(),
|
||||
ghostPaths: injectService(),
|
||||
notifications: injectService(),
|
||||
session: injectService(),
|
||||
|
||||
availableTimezones: null,
|
||||
iconExtensions: ['ico', 'png'],
|
||||
iconMimeTypes: 'image/png,image/x-icon',
|
||||
imageExtensions: IMAGE_EXTENSIONS,
|
||||
imageMimeTypes: IMAGE_MIME_TYPES,
|
||||
|
||||
_scratchFacebook: null,
|
||||
_scratchTwitter: null,
|
||||
|
||||
iconMimeTypes: 'image/png,image/x-icon',
|
||||
iconExtensions: ['ico', 'png'],
|
||||
|
||||
isDatedPermalinks: computed('model.permalinks', {
|
||||
set(key, value) {
|
||||
this.set('model.permalinks', value ? '/:year/:month/:day/:slug/' : '/:slug/');
|
||||
@ -89,16 +90,48 @@ export default Controller.extend({
|
||||
this.set('model.activeTimezone', timezone.name);
|
||||
},
|
||||
|
||||
toggleUploadCoverModal() {
|
||||
this.toggleProperty('showUploadCoverModal');
|
||||
removeImage(image) {
|
||||
// setting `null` here will error as the server treats it as "null"
|
||||
this.get('model').set(image, '');
|
||||
},
|
||||
|
||||
toggleUploadLogoModal() {
|
||||
this.toggleProperty('showUploadLogoModal');
|
||||
/**
|
||||
* Opens a file selection dialog - Triggered by "Upload Image" buttons,
|
||||
* searches for the hidden file input within the .gh-setting element
|
||||
* containing the clicked button then simulates a click
|
||||
* @param {MouseEvent} event - MouseEvent fired by the button click
|
||||
*/
|
||||
triggerFileDialog(event) {
|
||||
let fileInput = event.target
|
||||
.closest('.gh-setting')
|
||||
.querySelector('input[type="file"]');
|
||||
|
||||
if (fileInput) {
|
||||
let click = new MouseEvent('click', {
|
||||
view: window,
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
|
||||
// reset file input value before clicking so that the same image
|
||||
// can be selected again
|
||||
fileInput.value = '';
|
||||
|
||||
// simulate click to open file dialog
|
||||
fileInput.dispatchEvent(click);
|
||||
}
|
||||
},
|
||||
|
||||
toggleUploadIconModal() {
|
||||
this.toggleProperty('showUploadIconModal');
|
||||
/**
|
||||
* Fired after an image upload completes
|
||||
* @param {string} property - Property name to be set on `this.model`
|
||||
* @param {UploadResult[]} results - Array of UploadResult objects
|
||||
* @return {string} The URL that was set on `this.model.property`
|
||||
*/
|
||||
imageUploaded(property, results) {
|
||||
if (results[0]) {
|
||||
return this.get('model').set(property, results[0].url);
|
||||
}
|
||||
},
|
||||
|
||||
validateFacebookUrl() {
|
||||
|
@ -33,6 +33,14 @@
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.gh-setting-error {
|
||||
margin-top: 1em;
|
||||
line-height: 1.3em;
|
||||
color: var(--red);
|
||||
font-weight: 200;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.gh-setting-action {
|
||||
flex-shrink: 0;
|
||||
margin: 1px 0 0 0;
|
||||
@ -40,6 +48,10 @@
|
||||
|
||||
/* Images */
|
||||
|
||||
.gh-setting-action-smallimg {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gh-setting-action-smallimg img {
|
||||
height: 50px;
|
||||
width: auto;
|
||||
@ -57,6 +69,36 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gh-setting-action-smallimg-delete,
|
||||
.gh-setting-action-largeimg-delete {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
color: var(--midgrey);
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
.gh-setting-action-smallimg-delete:hover,
|
||||
.gh-setting-action-largeimg-delete:hover {
|
||||
color: var(--red);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.gh-setting-action .gh-progress-container {
|
||||
width: 113px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.gh-setting-action .gh-progress-container-progress {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gh-setting-action .gh-progress-bar {
|
||||
height: 9px;
|
||||
}
|
||||
|
||||
/* Checkboxes */
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
{{yield (hash
|
||||
progressBar=(component "gh-progress-bar" percentage=uploadPercentage)
|
||||
files=files
|
||||
errors=errors
|
||||
cancel=(action "cancel")
|
||||
errors=errors
|
||||
files=files
|
||||
isUploading=_uploadFiles.isRunning
|
||||
progressBar=(component "gh-progress-bar" percentage=uploadPercentage)
|
||||
)}}
|
||||
|
@ -52,65 +52,111 @@
|
||||
</div>
|
||||
|
||||
<div class="gh-setting-header">Publication identity</div>
|
||||
<div class="gh-setting">
|
||||
<div class="gh-setting" data-test-setting="icon">
|
||||
{{#gh-uploader
|
||||
files=iconUpload
|
||||
extensions=iconExtensions
|
||||
uploadUrl="/uploads/icon/"
|
||||
onComplete=(action "imageUploaded" "icon")
|
||||
as |uploader|
|
||||
}}
|
||||
<div class="gh-setting-content">
|
||||
<div class="gh-setting-title">Publication icon</div>
|
||||
<div class="gh-setting-desc">A square, social icon used in the UI of your publication, at least 60x60px</div>
|
||||
{{#if uploader.errors}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="icon">{{error.message}}</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="gh-setting-action gh-setting-action-smallimg">
|
||||
{{#if model.icon}}
|
||||
<img class="blog-icon" src="{{model.icon}}" alt="icon" role="button" {{action "toggleUploadIconModal"}}>
|
||||
<img class="blog-icon" src="{{model.icon}}" alt="icon" data-test-icon-img>
|
||||
<button type="button" class="gh-setting-action-smallimg-delete" {{action "removeImage" "icon"}} data-test-delete-image="icon">
|
||||
<span>delete</span>
|
||||
</button>
|
||||
{{else if uploader.isUploading}}
|
||||
{{uploader.progressBar}}
|
||||
{{else}}
|
||||
<button type="button" class="gh-btn" {{action "toggleUploadIconModal"}}><span>Upload Image</span></button>
|
||||
{{/if}}
|
||||
|
||||
{{#if showUploadIconModal}}
|
||||
{{gh-fullscreen-modal "upload-image"
|
||||
model=(hash model=model imageProperty="icon" accept=iconMimeTypes extensions=iconExtensions uploadUrl="/uploads/icon/")
|
||||
close=(action "toggleUploadIconModal")
|
||||
modifier="action wide"}}
|
||||
<button type="button" class="gh-btn" onClick={{action "triggerFileDialog"}} data-test-image-upload-btn="icon">
|
||||
<span>Upload Image</span>
|
||||
</button>
|
||||
{{/if}}
|
||||
<div style="display:none">
|
||||
{{gh-file-input multiple=false action=(action (mut iconUpload)) accept=iconMimeTypes data-test-file-input="icon"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/gh-uploader}}
|
||||
</div>
|
||||
<div class="gh-setting">
|
||||
<div class="gh-setting" data-test-setting="logo">
|
||||
{{#gh-uploader
|
||||
files=logoUpload
|
||||
extensions=imageExtensions
|
||||
onComplete=(action "imageUploaded" "logo")
|
||||
as |uploader|
|
||||
}}
|
||||
<div class="gh-setting-content">
|
||||
<div class="gh-setting-title">Publication logo</div>
|
||||
<div class="gh-setting-desc">The primary logo for your brand displayed across your theme, should be transparent and at least 600px x 72px</div>
|
||||
{{#if uploader.errors}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="logo">{{error.message}}</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="gh-setting-action gh-setting-action-smallimg">
|
||||
{{#if model.logo}}
|
||||
<img class="blog-logo" src="{{model.logo}}" alt="logo" role="button" {{action "toggleUploadLogoModal"}}>
|
||||
<img class="blog-logo" src="{{model.logo}}" alt="logo" data-test-logo-img>
|
||||
<button type="button" class="gh-setting-action-smallimg-delete" {{action "removeImage" "logo"}} data-test-delete-image="logo">
|
||||
<span>delete</span>
|
||||
</button>
|
||||
{{else if uploader.isUploading}}
|
||||
{{uploader.progressBar}}
|
||||
{{else}}
|
||||
<button type="button" class="gh-btn" {{action "toggleUploadLogoModal"}}><span>Upload Image</span></button>
|
||||
{{/if}}
|
||||
|
||||
{{#if showUploadLogoModal}}
|
||||
{{gh-fullscreen-modal "upload-image"
|
||||
model=(hash model=model imageProperty="logo")
|
||||
close=(action "toggleUploadLogoModal")
|
||||
modifier="action wide"}}
|
||||
<button type="button" class="gh-btn" onClick={{action "triggerFileDialog"}} data-test-image-upload-btn="logo">
|
||||
<span>Upload Image</span>
|
||||
</button>
|
||||
{{/if}}
|
||||
<div style="display:none">
|
||||
{{gh-file-input multiple=false action=(action (mut logoUpload)) accept=imageMimeTypes data-test-file-input="logo"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/gh-uploader}}
|
||||
</div>
|
||||
<div class="gh-setting">
|
||||
<div class="gh-setting" data-test-setting="coverImage">
|
||||
{{#gh-uploader
|
||||
files=coverImageUpload
|
||||
extensions=imageExtensions
|
||||
onComplete=(action "imageUploaded" "coverImage")
|
||||
as |uploader|
|
||||
}}
|
||||
<div class="gh-setting-content">
|
||||
<div class="gh-setting-title">Publication cover</div>
|
||||
<div class="gh-setting-desc">An optional large background image for your site</div>
|
||||
{{#if uploader.errors}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="gh-setting-error" data-test-error="coverImage">{{error.message}}</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="gh-setting-action gh-setting-action-largeimg">
|
||||
{{#if model.coverImage}}
|
||||
<img class="blog-cover" src="{{model.coverImage}}" alt="cover photo" role="button" {{action "toggleUploadCoverModal"}}>
|
||||
<img class="blog-cover" src="{{model.coverImage}}" alt="cover photo" data-test-cover-img>
|
||||
<button type="button" class="gh-setting-action-largeimg-delete" {{action "removeImage" "coverImage"}} data-test-delete-image="coverImage">
|
||||
<span>delete</span>
|
||||
</button>
|
||||
{{else if uploader.isUploading}}
|
||||
{{uploader.progressBar}}
|
||||
{{else}}
|
||||
<button type="button" class="gh-btn" {{action "toggleUploadCoverModal"}}><span>Upload Image</span></button>
|
||||
{{/if}}
|
||||
|
||||
{{#if showUploadCoverModal}}
|
||||
{{gh-fullscreen-modal "upload-image"
|
||||
model=(hash model=model imageProperty="coverImage")
|
||||
close=(action "toggleUploadCoverModal")
|
||||
modifier="action wide"}}
|
||||
<button type="button" class="gh-btn" onClick={{action "triggerFileDialog"}} data-test-image-upload-btn="coverImage">
|
||||
<span>Upload Image</span>
|
||||
</button>
|
||||
{{/if}}
|
||||
<div style="display:none">
|
||||
{{gh-file-input multiple=false action=(action (mut coverImageUpload)) accept=imageMimeTypes data-test-file-input="coverImage"}}
|
||||
</div>
|
||||
</div>
|
||||
{{/gh-uploader}}
|
||||
</div>
|
||||
|
||||
<div class="gh-setting-header">Social accounts</div>
|
||||
|
@ -8,6 +8,7 @@ import mockSlugs from './config/slugs';
|
||||
import mockSubscribers from './config/subscribers';
|
||||
import mockTags from './config/tags';
|
||||
import mockThemes from './config/themes';
|
||||
import mockUploads from './config/uploads';
|
||||
import mockUsers from './config/users';
|
||||
|
||||
// import {versionMismatchResponse} from 'utils';
|
||||
@ -48,13 +49,14 @@ export function testConfig() {
|
||||
mockSubscribers(this);
|
||||
mockTags(this);
|
||||
mockThemes(this);
|
||||
mockUploads(this);
|
||||
mockUsers(this);
|
||||
|
||||
/* Notifications -------------------------------------------------------- */
|
||||
|
||||
this.get('/notifications/');
|
||||
|
||||
/* Apps - Slack Test Notification --------------------------------------------------------- */
|
||||
/* Apps - Slack Test Notification --------------------------------------- */
|
||||
|
||||
this.post('/slack/test', function () {
|
||||
return {};
|
||||
|
17
ghost/admin/mirage/config/uploads.js
Normal file
17
ghost/admin/mirage/config/uploads.js
Normal file
@ -0,0 +1,17 @@
|
||||
const fileUploadResponse = function (db, {requestBody}) {
|
||||
let [file] = requestBody.getAll('uploadimage');
|
||||
let now = new Date();
|
||||
let year = now.getFullYear();
|
||||
let month = `${now.getMonth()}`;
|
||||
|
||||
if (month.length === 1) {
|
||||
month = `0${month}`;
|
||||
}
|
||||
|
||||
return `"/content/images/${year}/${month}/${file.name}"`;
|
||||
};
|
||||
|
||||
export default function mockUploads(server) {
|
||||
server.post('/uploads/', fileUploadResponse, 200, {timing: 100});
|
||||
server.post('/uploads/icon/', fileUploadResponse, 200, {timing: 100});
|
||||
}
|
@ -12,6 +12,9 @@ import startApp from '../../helpers/start-app';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import {invalidateSession, authenticateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import run from 'ember-runloop';
|
||||
import mockUploads from '../../../mirage/config/uploads';
|
||||
|
||||
describe('Acceptance: Settings - General', function () {
|
||||
let application;
|
||||
@ -59,7 +62,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
return authenticateSession(application);
|
||||
});
|
||||
|
||||
it('it renders, shows image uploader modals', async function () {
|
||||
it('it renders, handles image uploads', async function () {
|
||||
await visit('/settings/general');
|
||||
|
||||
// has correct url
|
||||
@ -72,42 +75,230 @@ describe('Acceptance: Settings - General', function () {
|
||||
expect($('.gh-nav-settings-general').hasClass('active'), 'highlights nav menu item')
|
||||
.to.be.true;
|
||||
|
||||
expect(find(testSelector('save-button')).text().trim(), 'save button text').to.equal('Save settings');
|
||||
expect(
|
||||
find(testSelector('save-button')).text().trim(),
|
||||
'save button text'
|
||||
).to.equal('Save settings');
|
||||
|
||||
expect(find(testSelector('dated-permalinks-checkbox')).prop('checked'), 'date permalinks checkbox').to.be.false;
|
||||
expect(
|
||||
find(testSelector('dated-permalinks-checkbox')).prop('checked'),
|
||||
'date permalinks checkbox'
|
||||
).to.be.false;
|
||||
|
||||
await click(testSelector('toggle-pub-info'));
|
||||
await fillIn(testSelector('title-input'), 'New Blog Title');
|
||||
await click(testSelector('save-button'));
|
||||
expect(document.title, 'page title').to.equal('Settings - General - New Blog Title');
|
||||
|
||||
await click('.blog-logo');
|
||||
expect(find('.fullscreen-modal .modal-content .gh-image-uploader').length, 'modal selector').to.equal(1);
|
||||
// blog icon upload
|
||||
// -------------------------------------------------------------- //
|
||||
|
||||
await click('.fullscreen-modal .modal-content .gh-image-uploader .image-cancel');
|
||||
expect(find(testSelector('file-input-description')).text()).to.equal('Upload an image');
|
||||
// has fixture icon
|
||||
expect(
|
||||
find(testSelector('icon-img')).attr('src'),
|
||||
'initial icon src'
|
||||
).to.equal('/content/images/2014/Feb/favicon.ico');
|
||||
|
||||
// click cancel button
|
||||
await click('.fullscreen-modal .modal-footer .gh-btn');
|
||||
expect(find('.fullscreen-modal').length).to.equal(0);
|
||||
// delete removes icon + shows button
|
||||
await click(testSelector('delete-image', 'icon'));
|
||||
expect(
|
||||
find(testSelector('icon-img')),
|
||||
'icon img after removal'
|
||||
).to.not.exist;
|
||||
expect(
|
||||
find(testSelector('image-upload-btn', 'icon')),
|
||||
'icon upload button after removal'
|
||||
).to.exist;
|
||||
|
||||
await click('.blog-icon');
|
||||
expect(find('.fullscreen-modal .modal-content .gh-image-uploader').length, 'modal selector').to.equal(1);
|
||||
// select file
|
||||
fileUpload(
|
||||
testSelector('file-input', 'icon'),
|
||||
['test'],
|
||||
{name: 'pub-icon.ico', type: 'image/x-icon'}
|
||||
);
|
||||
|
||||
await click('.fullscreen-modal .modal-content .gh-image-uploader .image-cancel');
|
||||
expect(find(testSelector('file-input-description')).text()).to.equal('Upload an image');
|
||||
// check progress bar exists during upload
|
||||
run.later(() => {
|
||||
expect(
|
||||
find(`${testSelector('setting', 'icon')} ${testSelector('progress-bar')}`),
|
||||
'icon upload progress bar'
|
||||
).to.exist;
|
||||
}, 50);
|
||||
|
||||
// click cancel button
|
||||
await click('.fullscreen-modal .modal-footer .gh-btn');
|
||||
expect(find('.fullscreen-modal').length).to.equal(0);
|
||||
// wait for upload to finish and check image is shown
|
||||
await wait();
|
||||
expect(
|
||||
find(testSelector('icon-img')).attr('src'),
|
||||
'icon img after upload'
|
||||
).to.match(/pub-icon\.ico$/);
|
||||
expect(
|
||||
find(testSelector('image-upload-btn', 'icon')),
|
||||
'icon upload button after upload'
|
||||
).to.not.exist;
|
||||
|
||||
await click('.blog-cover');
|
||||
expect(find('.fullscreen-modal .modal-content .gh-image-uploader').length, 'modal selector').to.equal(1);
|
||||
// failed upload shows error
|
||||
server.post('/uploads/icon/', function () {
|
||||
return {
|
||||
errors: [{
|
||||
errorType: 'ValidationError',
|
||||
message: 'Wrong icon size'
|
||||
}]
|
||||
};
|
||||
}, 422);
|
||||
await click(testSelector('delete-image', 'icon'));
|
||||
await fileUpload(
|
||||
testSelector('file-input', 'icon'),
|
||||
['test'],
|
||||
{name: 'pub-icon.ico', type: 'image/x-icon'}
|
||||
);
|
||||
expect(
|
||||
find(testSelector('error', 'icon')).text().trim(),
|
||||
'failed icon upload message'
|
||||
).to.equal('Wrong icon size');
|
||||
|
||||
await click(testSelector('modal-accept-button'));
|
||||
expect(find('.fullscreen-modal').length).to.equal(0);
|
||||
// reset upload endpoints
|
||||
mockUploads(server);
|
||||
|
||||
// blog logo upload
|
||||
// -------------------------------------------------------------- //
|
||||
|
||||
// has fixture icon
|
||||
expect(
|
||||
find(testSelector('logo-img')).attr('src'),
|
||||
'initial logo src'
|
||||
).to.equal('/content/images/2013/Nov/logo.png');
|
||||
|
||||
// delete removes logo + shows button
|
||||
await click(testSelector('delete-image', 'logo'));
|
||||
expect(
|
||||
find(testSelector('logo-img')),
|
||||
'logo img after removal'
|
||||
).to.not.exist;
|
||||
expect(
|
||||
find(testSelector('image-upload-btn', 'logo')),
|
||||
'logo upload button after removal'
|
||||
).to.exist;
|
||||
|
||||
// select file
|
||||
fileUpload(
|
||||
testSelector('file-input', 'logo'),
|
||||
['test'],
|
||||
{name: 'pub-logo.png', type: 'image/png'}
|
||||
);
|
||||
|
||||
// check progress bar exists during upload
|
||||
run.later(() => {
|
||||
expect(
|
||||
find(`${testSelector('setting', 'logo')} ${testSelector('progress-bar')}`),
|
||||
'logo upload progress bar'
|
||||
).to.exist;
|
||||
}, 50);
|
||||
|
||||
// wait for upload to finish and check image is shown
|
||||
await wait();
|
||||
expect(
|
||||
find(testSelector('logo-img')).attr('src'),
|
||||
'logo img after upload'
|
||||
).to.match(/pub-logo\.png$/);
|
||||
expect(
|
||||
find(testSelector('image-upload-btn', 'logo')),
|
||||
'logo upload button after upload'
|
||||
).to.not.exist;
|
||||
|
||||
// failed upload shows error
|
||||
server.post('/uploads/', function () {
|
||||
return {
|
||||
errors: [{
|
||||
errorType: 'ValidationError',
|
||||
message: 'Wrong logo size'
|
||||
}]
|
||||
};
|
||||
}, 422);
|
||||
await click(testSelector('delete-image', 'logo'));
|
||||
await fileUpload(
|
||||
testSelector('file-input', 'logo'),
|
||||
['test'],
|
||||
{name: 'pub-logo.png', type: 'image/png'}
|
||||
);
|
||||
expect(
|
||||
find(testSelector('error', 'logo')).text().trim(),
|
||||
'failed logo upload message'
|
||||
).to.equal('Wrong logo size');
|
||||
|
||||
// reset upload endpoints
|
||||
mockUploads(server);
|
||||
|
||||
// blog cover upload
|
||||
// -------------------------------------------------------------- //
|
||||
|
||||
// has fixture icon
|
||||
expect(
|
||||
find(testSelector('cover-img')).attr('src'),
|
||||
'initial coverImage src'
|
||||
).to.equal('/content/images/2014/Feb/cover.jpg');
|
||||
|
||||
// delete removes coverImage + shows button
|
||||
await click(testSelector('delete-image', 'coverImage'));
|
||||
expect(
|
||||
find(testSelector('coverImage-img')),
|
||||
'coverImage img after removal'
|
||||
).to.not.exist;
|
||||
expect(
|
||||
find(testSelector('image-upload-btn', 'coverImage')),
|
||||
'coverImage upload button after removal'
|
||||
).to.exist;
|
||||
|
||||
// select file
|
||||
fileUpload(
|
||||
testSelector('file-input', 'coverImage'),
|
||||
['test'],
|
||||
{name: 'pub-coverImage.png', type: 'image/png'}
|
||||
);
|
||||
|
||||
// check progress bar exists during upload
|
||||
run.later(() => {
|
||||
expect(
|
||||
find(`${testSelector('setting', 'coverImage')} ${testSelector('progress-bar')}`),
|
||||
'coverImage upload progress bar'
|
||||
).to.exist;
|
||||
}, 50);
|
||||
|
||||
// wait for upload to finish and check image is shown
|
||||
await wait();
|
||||
expect(
|
||||
find(testSelector('cover-img')).attr('src'),
|
||||
'coverImage img after upload'
|
||||
).to.match(/pub-coverImage\.png$/);
|
||||
expect(
|
||||
find(testSelector('image-upload-btn', 'coverImage')),
|
||||
'coverImage upload button after upload'
|
||||
).to.not.exist;
|
||||
|
||||
// failed upload shows error
|
||||
server.post('/uploads/', function () {
|
||||
return {
|
||||
errors: [{
|
||||
errorType: 'ValidationError',
|
||||
message: 'Wrong coverImage size'
|
||||
}]
|
||||
};
|
||||
}, 422);
|
||||
await click(testSelector('delete-image', 'coverImage'));
|
||||
await fileUpload(
|
||||
testSelector('file-input', 'coverImage'),
|
||||
['test'],
|
||||
{name: 'pub-coverImage.png', type: 'image/png'}
|
||||
);
|
||||
expect(
|
||||
find(testSelector('error', 'coverImage')).text().trim(),
|
||||
'failed coverImage upload message'
|
||||
).to.equal('Wrong coverImage size');
|
||||
|
||||
// reset upload endpoints
|
||||
mockUploads(server);
|
||||
|
||||
// CMD-S shortcut works
|
||||
// -------------------------------------------------------------- //
|
||||
await fillIn(testSelector('title-input'), 'CMD-S Test');
|
||||
await triggerEvent('.gh-app', 'keydown', {
|
||||
keyCode: 83, // s
|
||||
|
@ -30,6 +30,6 @@ export default Test.registerAsyncHelper('fileUpload', function(app, selector, co
|
||||
return triggerEvent(
|
||||
selector,
|
||||
'change',
|
||||
{foor: 'bar', testingFiles: [file]}
|
||||
{testingFiles: [file]}
|
||||
);
|
||||
});
|
||||
|
@ -118,6 +118,26 @@ describe('Integration: Component: gh-uploader', function() {
|
||||
expect(result[1].url).to.equal('/content/images/test.png');
|
||||
});
|
||||
|
||||
it('onComplete only passes results for last upload', async function () {
|
||||
this.set('uploadsFinished', sinon.spy());
|
||||
|
||||
this.render(hbs`{{#gh-uploader files=files onComplete=(action uploadsFinished)}}{{/gh-uploader}}`);
|
||||
this.set('files', [
|
||||
createFile(['test'], {name: 'file1.png'})
|
||||
]);
|
||||
await wait();
|
||||
|
||||
this.set('files', [
|
||||
createFile(['test'], {name: 'file2.png'})
|
||||
]);
|
||||
|
||||
await wait();
|
||||
|
||||
let [results] = this.get('uploadsFinished').getCall(1).args;
|
||||
expect(results.length).to.equal(1);
|
||||
expect(results[0].fileName).to.equal('file2.png');
|
||||
});
|
||||
|
||||
it('doesn\'t allow new files to be set whilst uploading', async function () {
|
||||
let errorSpy = sinon.spy(console, 'error');
|
||||
stubSuccessfulUpload(server, 100);
|
||||
@ -142,6 +162,27 @@ describe('Integration: Component: gh-uploader', function() {
|
||||
errorSpy.restore();
|
||||
});
|
||||
|
||||
it('yields isUploading whilst upload is in progress', async function () {
|
||||
stubSuccessfulUpload(server, 200);
|
||||
|
||||
this.render(hbs`
|
||||
{{#gh-uploader files=files as |uploader|}}
|
||||
{{#if uploader.isUploading}}
|
||||
<div class="is-uploading-test"></div>
|
||||
{{/if}}
|
||||
{{/gh-uploader}}`);
|
||||
|
||||
this.set('files', [createFile(), createFile()]);
|
||||
|
||||
run.later(() => {
|
||||
expect(find('.is-uploading-test')).to.exist;
|
||||
}, 100);
|
||||
|
||||
await wait();
|
||||
|
||||
expect(find('.is-uploading-test')).to.not.exist;
|
||||
});
|
||||
|
||||
it('yields progressBar component with total upload progress', async function () {
|
||||
stubSuccessfulUpload(server, 200);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user