mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 14:43:08 +03:00
Migrate to latest ember, ember-mocha and modern ember testing (#1044)
no issue - upgrade to latest `ember-source` and related dependencies including `ember-cli` - upgrade to latest `ember-mocha` and modern ember testing setup - https://github.com/emberjs/rfcs/blob/master/text/0268-acceptance-testing-refactor.md - switch from using global acceptance test helpers and `native-dom-helpers` to using the new `ember-test-helpers` methods - use [`chai-dom`](https://github.com/nathanboktae/chai-dom) assertions where in some places (still a lot of places in the tests that could use these) - pin `ember-in-viewport` to 3.0.x to work around incompatibilities between different versions used in `ember-light-table`, `ember-infinity`, and `ember-sticky-element` - incompatibilities manifested as "Invalid value used as weak map key" errors thrown when using `ember-light-table` (subscribers screen) - pin `ember-power-datepicker` to unreleased version that contains a move from global acceptance test helpers to modern test helpers
This commit is contained in:
parent
56fa37de9e
commit
73daa80b7f
18
ghost/admin/.eslintignore
Normal file
18
ghost/admin/.eslintignore
Normal file
@ -0,0 +1,18 @@
|
||||
# unconventional js
|
||||
/blueprints/*/files/
|
||||
/vendor/
|
||||
|
||||
# compiled output
|
||||
/dist/
|
||||
/tmp/
|
||||
|
||||
# dependencies
|
||||
/bower_components/
|
||||
|
||||
# misc
|
||||
/coverage/
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
/bower.json.ember-try
|
||||
/package.json.ember-try
|
4
ghost/admin/.gitignore
vendored
4
ghost/admin/.gitignore
vendored
@ -46,8 +46,8 @@ Session.vim
|
||||
|
||||
# misc
|
||||
/connect.lock
|
||||
/coverage/*
|
||||
/coverage/
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/testem.log
|
||||
/concat-stats-for
|
||||
jsconfig.json
|
||||
|
@ -32,8 +32,8 @@ const CmEditorComponent = Component.extend({
|
||||
_value: boundOneWay('value'), // make sure a value exists
|
||||
|
||||
didReceiveAttrs() {
|
||||
if (this.get('value') === null || undefined) {
|
||||
this.set('value', '');
|
||||
if (this.get('_value') === null || undefined) {
|
||||
this.set('_value', '');
|
||||
}
|
||||
},
|
||||
|
||||
@ -69,9 +69,7 @@ const CmEditorComponent = Component.extend({
|
||||
loader.loadScript('codemirror', 'assets/codemirror/codemirror.js')
|
||||
]);
|
||||
|
||||
scheduleOnce('afterRender', this, function () {
|
||||
this._initCodeMirror();
|
||||
});
|
||||
scheduleOnce('afterRender', this, this._initCodeMirror);
|
||||
}),
|
||||
|
||||
_initCodeMirror() {
|
||||
|
6
ghost/admin/app/components/gh-infinity-loader.js
Normal file
6
ghost/admin/app/components/gh-infinity-loader.js
Normal file
@ -0,0 +1,6 @@
|
||||
import InfinityLoader from 'ember-infinity/components/infinity-loader';
|
||||
import layout from '../templates/components/infinity-loader';
|
||||
|
||||
export default InfinityLoader.extend({
|
||||
layout
|
||||
});
|
@ -27,30 +27,24 @@ export default TextField.extend({
|
||||
classNames: 'gh-input',
|
||||
|
||||
// Allowed actions
|
||||
clearErrors: () => {},
|
||||
update() {},
|
||||
clearErrors() {},
|
||||
|
||||
isBaseUrl: computed('baseUrl', 'value', function () {
|
||||
return this.get('baseUrl') === this.get('value');
|
||||
return this.baseUrl === this.value;
|
||||
}),
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
let baseUrl = this.get('baseUrl');
|
||||
let url = this.get('url');
|
||||
|
||||
// if we have a relative url, create the absolute url to be displayed in the input
|
||||
if (isRelative(url)) {
|
||||
url = joinUrlParts(baseUrl, url);
|
||||
}
|
||||
|
||||
this.set('value', url);
|
||||
// value coming is likely to be relative but we always want to show
|
||||
// absolute urls in the input fields
|
||||
this.set('value', this._makeAbsoluteUrl(this.url));
|
||||
},
|
||||
|
||||
focusIn(event) {
|
||||
this.set('hasFocus', true);
|
||||
|
||||
if (this.get('isBaseUrl')) {
|
||||
if (this.isBaseUrl) {
|
||||
// position the cursor at the end of the input
|
||||
run.next(function (el) {
|
||||
let {length} = el.value;
|
||||
@ -62,7 +56,7 @@ export default TextField.extend({
|
||||
|
||||
keyDown(event) {
|
||||
// delete the "placeholder" value all at once
|
||||
if (this.get('isBaseUrl') && (event.keyCode === 8 || event.keyCode === 46)) {
|
||||
if (this.isBaseUrl && (event.keyCode === 8 || event.keyCode === 46)) {
|
||||
this.set('value', '');
|
||||
|
||||
event.preventDefault();
|
||||
@ -92,9 +86,9 @@ export default TextField.extend({
|
||||
},
|
||||
|
||||
notifyUrlChanged() {
|
||||
let url = this.get('value').trim();
|
||||
let url = this.value.trim();
|
||||
let urlURI = URI.parse(url);
|
||||
let baseUrl = this.get('baseUrl');
|
||||
let baseUrl = this.baseUrl;
|
||||
let baseURI = URI.parse(baseUrl);
|
||||
|
||||
function getHost(uri) {
|
||||
@ -115,18 +109,12 @@ export default TextField.extend({
|
||||
|
||||
// if we have an email address, add the mailto:
|
||||
if (validator.isEmail(url)) {
|
||||
url = `mailto:${url}`;
|
||||
this.set('value', url);
|
||||
}
|
||||
|
||||
// if we have a relative url, create the absolute url to be displayed in the input
|
||||
if (isRelative(url)) {
|
||||
url = joinUrlParts(baseUrl, url);
|
||||
url = this.update(`mailto:${url}`);
|
||||
this.set('value', url);
|
||||
return;
|
||||
}
|
||||
|
||||
// get our baseUrl relativity checks in order
|
||||
let isOnSameHost = urlHost === baseHost;
|
||||
let isAnchorLink = url.match(/^#/);
|
||||
let isRelativeToBasePath = urlURI.getPath() && urlURI.getPath().indexOf(baseURI.getPath()) === 0;
|
||||
|
||||
@ -135,6 +123,8 @@ export default TextField.extend({
|
||||
isRelativeToBasePath = true;
|
||||
}
|
||||
|
||||
let isOnSameHost = urlHost === baseHost || (!urlHost && isRelativeToBasePath);
|
||||
|
||||
// if relative to baseUrl, remove the base url before sending to action
|
||||
if (!isAnchorLink && isOnSameHost && isRelativeToBasePath) {
|
||||
url = url.replace(/^[a-zA-Z0-9-]+:/, '');
|
||||
@ -147,7 +137,7 @@ export default TextField.extend({
|
||||
url = url.replace(baseURI.getPath().slice(0, -1), '');
|
||||
}
|
||||
|
||||
if (url !== '' || !this.get('isNew')) {
|
||||
if (url !== '' || !this.isNew) {
|
||||
if (!url.match(/^\//)) {
|
||||
url = `/${url}`;
|
||||
}
|
||||
@ -158,9 +148,19 @@ export default TextField.extend({
|
||||
}
|
||||
}
|
||||
|
||||
let action = this.get('update');
|
||||
if (action) {
|
||||
action(url);
|
||||
// we update with the relative URL but then transform it back to absolute
|
||||
// for the input value. This avoids problems where the underlying relative
|
||||
// value hasn't changed even though the input value has
|
||||
if (url.match(/^(\/\/|#|[a-zA-Z0-9-]+:)/) || validator.isURL(url) || validator.isURL(`${baseHost}${url}`)) {
|
||||
url = this.update(url);
|
||||
this.set('value', this._makeAbsoluteUrl(url));
|
||||
}
|
||||
},
|
||||
|
||||
_makeAbsoluteUrl(url) {
|
||||
if (isRelative(url)) {
|
||||
url = joinUrlParts(this.baseUrl, url);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
});
|
||||
|
@ -10,6 +10,12 @@ export default Component.extend(ValidationState, {
|
||||
|
||||
new: false,
|
||||
|
||||
// closure actions
|
||||
addItem() {},
|
||||
deleteItem() {},
|
||||
updateUrl() {},
|
||||
updateLabel() {},
|
||||
|
||||
errors: readOnly('navItem.errors'),
|
||||
|
||||
errorClass: computed('hasError', function () {
|
||||
@ -20,31 +26,19 @@ export default Component.extend(ValidationState, {
|
||||
|
||||
actions: {
|
||||
addItem() {
|
||||
let action = this.get('addItem');
|
||||
if (action) {
|
||||
action();
|
||||
}
|
||||
this.addItem();
|
||||
},
|
||||
|
||||
deleteItem(item) {
|
||||
let action = this.get('deleteItem');
|
||||
if (action) {
|
||||
action(item);
|
||||
}
|
||||
this.deleteItem(item);
|
||||
},
|
||||
|
||||
updateUrl(value) {
|
||||
let action = this.get('updateUrl');
|
||||
if (action) {
|
||||
action(value, this.get('navItem'));
|
||||
}
|
||||
return this.updateUrl(value, this.navItem);
|
||||
},
|
||||
|
||||
updateLabel(value) {
|
||||
let action = this.get('updateLabel');
|
||||
if (action) {
|
||||
action(value, this.get('navItem'));
|
||||
}
|
||||
return this.updateLabel(value, this.navItem);
|
||||
},
|
||||
|
||||
clearLabelErrors() {
|
||||
|
@ -15,7 +15,8 @@ export default Component.extend({
|
||||
_availableTags: null,
|
||||
|
||||
availableTags: sort('_availableTags.[]', function (tagA, tagB) {
|
||||
return tagA.name.localeCompare(tagB.name);
|
||||
// ignorePunctuation means the # in internal tag names is ignored
|
||||
return tagA.name.localeCompare(tagB.name, undefined, {ignorePunctuation: true});
|
||||
}),
|
||||
|
||||
availableTagNames: computed('availableTags.@each.name', function () {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import $ from 'jquery';
|
||||
import Component from '@ember/component';
|
||||
import {htmlSafe} from '@ember/string';
|
||||
|
||||
@ -15,21 +14,25 @@ export default Component.extend({
|
||||
href: htmlSafe('javascript:;'),
|
||||
|
||||
click() {
|
||||
let anchor = this.get('anchor');
|
||||
let $el = $(anchor);
|
||||
let el = document.querySelector(this.anchor);
|
||||
|
||||
if ($el) {
|
||||
if (el) {
|
||||
// Scrolls to the top of main content or whatever
|
||||
// is passed to the anchor attribute
|
||||
$('body').scrollTop($el.offset().top);
|
||||
document.body.scrollTop = el.getBoundingClientRect().top;
|
||||
|
||||
let removeTabindex = function () {
|
||||
el.removeAttribute('tabindex');
|
||||
};
|
||||
|
||||
// This sets focus on the content which was skipped to
|
||||
// upon losing focus, the tabindex should be removed
|
||||
// so that normal keyboard navigation picks up from focused
|
||||
// element
|
||||
$($el).attr('tabindex', -1).on('blur focusout', function () {
|
||||
$(this).removeAttr('tabindex');
|
||||
}).focus();
|
||||
el.setAttribute('tabindex', -1);
|
||||
el.focus();
|
||||
el.addEventListener('blur', removeTabindex);
|
||||
el.addEventListener('focusout', removeTabindex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
6
ghost/admin/app/components/infinity-loader.js
Normal file
6
ghost/admin/app/components/infinity-loader.js
Normal file
@ -0,0 +1,6 @@
|
||||
import InfinityLoader from 'ember-infinity/components/infinity-loader';
|
||||
import layout from '../templates/components/gh-infinity-loader';
|
||||
|
||||
export default InfinityLoader.extend({
|
||||
layout
|
||||
});
|
@ -5,7 +5,9 @@ export default ModalComponent.extend({
|
||||
actions: {
|
||||
confirm() {
|
||||
this.confirm().finally(() => {
|
||||
this.send('closeModal');
|
||||
if (!this.isDestroyed && !this.isDestroying) {
|
||||
this.send('closeModal');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -84,7 +84,7 @@ export default Controller.extend({
|
||||
availableTags: computed('_availableTags.[]', function () {
|
||||
let tags = this.get('_availableTags')
|
||||
.filter(tag => tag.get('id') !== null)
|
||||
.sort((tagA, tagB) => tagA.name.localeCompare(tagB.name));
|
||||
.sort((tagA, tagB) => tagA.name.localeCompare(tagB.name, undefined, {ignorePunctuation: true}));
|
||||
let options = tags.toArray();
|
||||
|
||||
options.unshiftObject({name: 'All tags', slug: null});
|
||||
|
@ -81,6 +81,8 @@ export default Controller.extend({
|
||||
|
||||
navItem.set('url', url);
|
||||
this.set('dirtyAttributes', true);
|
||||
|
||||
return url;
|
||||
},
|
||||
|
||||
toggleLeaveSettingsModal(transition) {
|
||||
|
@ -19,7 +19,8 @@ export default Controller.extend({
|
||||
|
||||
// tags are sorted by name
|
||||
sortedTags: sort('filteredTags', function (tagA, tagB) {
|
||||
return tagA.name.localeCompare(tagB.name);
|
||||
// ignorePunctuation means the # in internal tag names is ignored
|
||||
return tagA.name.localeCompare(tagB.name, undefined, {ignorePunctuation: true});
|
||||
}),
|
||||
|
||||
actions: {
|
||||
|
@ -45,7 +45,9 @@ export default AuthenticatedRoute.extend(CurrentUserSettings, ShortcutsRoute, {
|
||||
|
||||
deactivate() {
|
||||
this._super(...arguments);
|
||||
this.send('resetShortcutsScope');
|
||||
if (!this.isDestroyed && !this.isDestroying) {
|
||||
this.send('resetShortcutsScope');
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
@ -19,8 +19,14 @@ import {
|
||||
// specificity to re-use keys for i18n lookups
|
||||
|
||||
export default Service.extend({
|
||||
delayedNotifications: emberA(),
|
||||
content: emberA(),
|
||||
delayedNotifications: null,
|
||||
content: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.delayedNotifications = emberA();
|
||||
this.content = emberA();
|
||||
},
|
||||
|
||||
upgradeStatus: service(),
|
||||
|
||||
|
@ -5,11 +5,11 @@ import {inject as service} from '@ember/service';
|
||||
|
||||
export default SessionService.extend({
|
||||
feature: service(),
|
||||
store: service(),
|
||||
dataStore: service('store'), // SessionService.store already exists
|
||||
tour: service(),
|
||||
|
||||
user: computed(function () {
|
||||
return this.get('store').queryRecord('user', {id: 'me'});
|
||||
return this.get('dataStore').queryRecord('user', {id: 'me'});
|
||||
}),
|
||||
|
||||
authenticate() {
|
||||
|
@ -11,9 +11,11 @@
|
||||
}}
|
||||
{{/if}}
|
||||
|
||||
{{#gh-main onMouseEnter=(action "closeAutoNav" target=ui)}}
|
||||
{{!-- {{#gh-main onMouseEnter=(action "closeAutoNav" target=ui)}} --}}
|
||||
<main class="gh-main" role="main">
|
||||
{{outlet}}
|
||||
{{/gh-main}}
|
||||
</main>
|
||||
{{!-- {{/gh-main}} --}}
|
||||
|
||||
|
||||
{{gh-notifications}}
|
||||
|
@ -1,8 +1,8 @@
|
||||
{{#if hasBlock}}
|
||||
{{yield}}
|
||||
{{yield infinityModelContent}}
|
||||
{{else}}
|
||||
{{#if infinityModel.reachedInfinity}}
|
||||
{{#if isDoneLoading}}
|
||||
{{else}}
|
||||
<div class="gh-loading-spinner"></div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
@ -14,12 +14,20 @@
|
||||
input=(action (mut navItem.label) value="target.value")
|
||||
keyPress=(action "clearLabelErrors")
|
||||
focus-out=(action "updateLabel" navItem.label)
|
||||
data-test-input="label"
|
||||
}}
|
||||
{{gh-error-message errors=navItem.errors property="label"}}
|
||||
{{gh-error-message errors=navItem.errors property="label" data-test-error="label"}}
|
||||
{{/gh-validation-status-container}}
|
||||
{{#gh-validation-status-container tagName="span" class="gh-blognav-url" errors=navItem.errors property="url" hasValidated=navItem.hasValidated}}
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=navItem.url isNew=navItem.isNew update=(action "updateUrl") clearErrors=(action "clearUrlErrors")}}
|
||||
{{gh-error-message errors=navItem.errors property="url"}}
|
||||
{{gh-navitem-url-input
|
||||
baseUrl=baseUrl
|
||||
isNew=navItem.isNew
|
||||
url=(readonly navItem.url)
|
||||
update=(action "updateUrl")
|
||||
clearErrors=(action "clearUrlErrors")
|
||||
data-test-input="url"
|
||||
}}
|
||||
{{gh-error-message errors=navItem.errors property="url" data-test-error="url"}}
|
||||
{{/gh-validation-status-container}}
|
||||
</div>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<header class="modal-header">
|
||||
<header class="modal-header" data-test-modal="delete-subscriber">
|
||||
<h1>Are you sure?</h1>
|
||||
</header>
|
||||
<a class="close" href="" title="Close" {{action "closeModal"}}>{{svg-jar "close"}}<span class="hidden">Close</span></a>
|
||||
@ -8,6 +8,12 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="gh-btn"><span>Cancel</span></button>
|
||||
{{gh-task-button "Delete" successText="Deleted" task=deleteSubscriber class="gh-btn gh-btn-red gh-btn-icon"}}
|
||||
<button {{action "closeModal"}} class="gh-btn" data-test-button="cancel-delete-subscriber">
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
{{gh-task-button "Delete"
|
||||
successText="Deleted"
|
||||
task=deleteSubscriber
|
||||
class="gh-btn gh-btn-red gh-btn-icon"
|
||||
data-test-button="confirm-delete-subscriber"}}
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<header class="modal-header">
|
||||
<header class="modal-header" data-test-modal="delete-user">
|
||||
<h1>Are you sure you want to delete this user?</h1>
|
||||
</header>
|
||||
<a class="close" href="" title="Close" {{action "closeModal"}}>{{svg-jar "close"}}<span class="hidden">Close</span></a>
|
||||
@ -8,7 +8,7 @@
|
||||
{{#if user.count.posts}}
|
||||
<ul>
|
||||
<li>The user will not have access to this blog anymore</li>
|
||||
<li><strong>{{pluralize user.count.posts 'post'}}</strong> created by this user will be deleted</li>
|
||||
<li><strong data-test-text="user-post-count">{{pluralize user.count.posts 'post'}}</strong> created by this user will be deleted</li>
|
||||
<li>All other user data will be deleted</li>
|
||||
</ul>
|
||||
{{else}}
|
||||
@ -20,10 +20,12 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="gh-btn"><span>Cancel</span></button>
|
||||
{{#if user.count.posts}}
|
||||
{{gh-task-button "Delete user and their posts" successText="Deleted" task=deleteUser class="gh-btn gh-btn-red gh-btn-icon"}}
|
||||
{{else}}
|
||||
{{gh-task-button "Delete user" successText="Deleted" task=deleteUser class="gh-btn gh-btn-red gh-btn-icon"}}
|
||||
{{/if}}
|
||||
<button {{action "closeModal"}} class="gh-btn" data-test-button="cancel-delete-user">
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
{{gh-task-button (if user.count.posts "Delete user and their posts" "Delete user")
|
||||
successText="Deleted"
|
||||
task=deleteUser
|
||||
class="gh-btn gh-btn-red gh-btn-icon"
|
||||
data-test-button="confirm-delete-user"}}
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<header class="modal-header">
|
||||
<header class="modal-header" data-test-modal="import-subscribers">
|
||||
<h1>
|
||||
{{#if response}}
|
||||
Import Successful
|
||||
@ -14,18 +14,18 @@
|
||||
<table class="subscribers-import-results">
|
||||
<tr>
|
||||
<td>Imported:</td>
|
||||
<td align="left">{{response.imported}}</td>
|
||||
<td align="left" data-test-text="import-subscribers-imported">{{response.imported}}</td>
|
||||
</tr>
|
||||
{{#if response.duplicates}}
|
||||
<tr>
|
||||
<td>Duplicates:</td>
|
||||
<td align="left">{{response.duplicates}}</td>
|
||||
<td align="left" data-test-text="import-subscribers-duplicates">{{response.duplicates}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if response.invalid}}
|
||||
<tr>
|
||||
<td>Invalid:</td>
|
||||
<td align="left">{{response.invalid}}</td>
|
||||
<td align="left" data-test-text="import-subscribers-invalid">{{response.invalid}}</td>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
@ -41,7 +41,7 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} disabled={{closeDisabled}} class="gh-btn">
|
||||
<button {{action "closeModal"}} disabled={{closeDisabled}} class="gh-btn" data-test-button="close-import-subscribers">
|
||||
<span>{{#if response}}Close{{else}}Cancel{{/if}}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -33,7 +33,7 @@
|
||||
{{one-way-select
|
||||
id="new-user-role"
|
||||
name="role"
|
||||
options=roles
|
||||
options=(readonly roles)
|
||||
optionValuePath="id"
|
||||
optionLabelPath="name"
|
||||
value=role
|
||||
|
@ -1,4 +1,4 @@
|
||||
<header class="modal-header" data-modal="unsaved-settings">
|
||||
<header class="modal-header" data-test-modal="unsaved-settings">
|
||||
<h1>Are you sure you want to leave this page?</h1>
|
||||
</header>
|
||||
<a class="close" href="" title="Close" {{action "closeModal"}}>{{svg-jar "close"}}<span class="hidden">Close</span></a>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<header class="modal-header">
|
||||
<header class="modal-header" data-test-modal="new-subscriber">
|
||||
<h1>Add a Subscriber</h1>
|
||||
</header>
|
||||
<a class="close" href="" title="Close" {{action "closeModal"}}>{{svg-jar "close"}}<span class="hidden">Close</span></a>
|
||||
@ -16,14 +16,21 @@
|
||||
name="email"
|
||||
autofocus="autofocus"
|
||||
autocapitalize="off"
|
||||
autocorrect="off">
|
||||
{{gh-error-message errors=subscriber.errors property="email"}}
|
||||
autocorrect="off"
|
||||
data-test-input="new-subscriber-email">
|
||||
{{gh-error-message errors=subscriber.errors property="email" data-test-error="new-subscriber-email"}}
|
||||
{{/gh-form-group}}
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="gh-btn"><span>Cancel</span></button>
|
||||
{{gh-task-button "Add" successText="Added" task=addSubscriber class="gh-btn gh-btn-green gh-btn-icon"}}
|
||||
<button {{action "closeModal"}} class="gh-btn" data-test-button="cancel-new-subscriber">
|
||||
<span>Cancel</span>
|
||||
</button>
|
||||
{{gh-task-button "Add"
|
||||
successText="Added"
|
||||
task=addSubscriber
|
||||
class="gh-btn gh-btn-green gh-btn-icon"
|
||||
data-test-button="create-subscriber"}}
|
||||
</div>
|
||||
|
@ -108,7 +108,7 @@
|
||||
{{/each}}
|
||||
</ol>
|
||||
|
||||
{{infinity-loader
|
||||
{{gh-infinity-loader
|
||||
infinityModel=postsInfinityModel
|
||||
scrollable=".gh-main"
|
||||
triggerOffset=1000}}
|
||||
|
@ -19,13 +19,25 @@
|
||||
<div class="gh-blognav-container">
|
||||
<form id="settings-navigation" class="gh-blognav" novalidate="novalidate">
|
||||
{{#sortable-objects sortableObjectList=settings.navigation useSwap=false}}
|
||||
{{#each settings.navigation as |navItem|}}
|
||||
{{#each settings.navigation as |navItem index|}}
|
||||
{{#draggable-object content=navItem dragHandle=".gh-blognav-grab" isSortable=true}}
|
||||
{{gh-navitem navItem=navItem baseUrl=blogUrl addItem=(action "addNavItem") deleteItem=(action "deleteNavItem") updateUrl=(action "updateUrl") updateLabel=(action "updateLabel")}}
|
||||
{{gh-navitem
|
||||
navItem=navItem
|
||||
baseUrl=blogUrl
|
||||
addItem=(action "addNavItem")
|
||||
deleteItem=(action "deleteNavItem")
|
||||
updateUrl=(action "updateUrl")
|
||||
updateLabel=(action "updateLabel")
|
||||
data-test-navitem=index}}
|
||||
{{/draggable-object}}
|
||||
{{/each}}
|
||||
{{/sortable-objects}}
|
||||
{{gh-navitem navItem=newNavItem baseUrl=blogUrl addItem=(action "addNavItem") updateUrl=(action "updateUrl")}}
|
||||
{{gh-navitem
|
||||
navItem=newNavItem
|
||||
baseUrl=blogUrl
|
||||
addItem=(action "addNavItem")
|
||||
updateUrl=(action "updateUrl")
|
||||
data-test-navitem="new"}}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
||||
{{#unless slackSettings.errors.url}}
|
||||
<p>Set up a new incoming webhook <a href="https://my.slack.com/apps/new/A0F7XDUAZ-incoming-webhooks" target="_blank">here</a>, and grab the URL.</p>
|
||||
{{else}}
|
||||
{{gh-error-message errors=slackSettings.errors property="url"}}
|
||||
{{gh-error-message errors=slackSettings.errors property="url" data-test-error="slack-url"}}
|
||||
{{/unless}}
|
||||
{{/gh-form-group}}
|
||||
</div>
|
||||
|
@ -92,7 +92,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{infinity-loader
|
||||
{{gh-infinity-loader
|
||||
infinityModel=activeUsers
|
||||
scrollable=".gh-main"
|
||||
triggerOffset=500}}
|
||||
|
@ -136,7 +136,7 @@
|
||||
data-test-name-input=true
|
||||
}}
|
||||
{{#if user.errors.name}}
|
||||
{{gh-error-message errors=user.errors property="name"}}
|
||||
{{gh-error-message errors=user.errors property="name" data-test-error="user-name"}}
|
||||
{{else}}
|
||||
<p>Use your real name so people can recognise you</p>
|
||||
{{/if}}
|
||||
@ -157,7 +157,7 @@
|
||||
data-test-slug-input=true
|
||||
}}
|
||||
<p>{{gh-blog-url}}/author/{{slugValue}}</p>
|
||||
{{gh-error-message errors=user.errors property="slug"}}
|
||||
{{gh-error-message errors=user.errors property="slug" data-test-error="user-slug"}}
|
||||
{{/gh-form-group}}
|
||||
|
||||
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="email"}}
|
||||
@ -176,7 +176,7 @@
|
||||
input=(action (mut user.email) value="target.value")
|
||||
focus-out=(action "validate" "email" target=user)
|
||||
data-test-email-input=true}}
|
||||
{{gh-error-message errors=user.errors property="email"}}
|
||||
{{gh-error-message errors=user.errors property="email" data-test-error="user-email"}}
|
||||
{{else}}
|
||||
<span>{{user.email}}</span>
|
||||
{{/if}}
|
||||
@ -208,7 +208,7 @@
|
||||
input=(action (mut user.location) value="target.value")
|
||||
focus-out=(action "validate" "location" target=user)
|
||||
data-test-location-input=true}}
|
||||
{{gh-error-message errors=user.errors property="location" data-test-location-error=true}}
|
||||
{{gh-error-message errors=user.errors property="location" data-test-error="user-location"}}
|
||||
<p>Where in the world do you live?</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
@ -224,7 +224,7 @@
|
||||
input=(action (mut user.website) value="target.value")
|
||||
focus-out=(action "validate" "website" target=user)
|
||||
data-test-website-input=true}}
|
||||
{{gh-error-message errors=user.errors property="website" data-test-website-error=true}}
|
||||
{{gh-error-message errors=user.errors property="website" data-test-error="user-website"}}
|
||||
<p>Have a website or blog other than this one? Link it!</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
@ -241,7 +241,7 @@
|
||||
focus-out=(action "validateFacebookUrl")
|
||||
data-test-facebook-input=true
|
||||
}}
|
||||
{{gh-error-message errors=user.errors property="facebook" data-test-facebook-error=true}}
|
||||
{{gh-error-message errors=user.errors property="facebook" data-test-error="user-facebook"}}
|
||||
<p>URL of your personal Facebook Profile</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
@ -258,7 +258,7 @@
|
||||
focus-out=(action "validateTwitterUrl")
|
||||
data-test-twitter-input=true
|
||||
}}
|
||||
{{gh-error-message errors=user.errors property="twitter" data-test-twitter-error=true}}
|
||||
{{gh-error-message errors=user.errors property="twitter" data-test-error="user-twitter"}}
|
||||
<p>URL of your personal Twitter profile</p>
|
||||
{{/gh-form-group}}
|
||||
|
||||
@ -271,7 +271,7 @@
|
||||
focus-out=(action "validate" "bio" target=user)
|
||||
data-test-bio-input=true
|
||||
}}
|
||||
{{gh-error-message errors=user.errors property="bio" data-test-bio-error=true}}
|
||||
{{gh-error-message errors=user.errors property="bio" data-test-error="user-bio"}}
|
||||
<p>
|
||||
Write about you, in 200 characters or less.
|
||||
{{gh-count-characters user.bio}}
|
||||
@ -284,7 +284,7 @@
|
||||
|
||||
</form> {{! user details form }}
|
||||
|
||||
{{!-- If an administrator is viewing Owner's profile or we're using Ghost.org OAuth then hide inputs for change password --}}
|
||||
{{!-- If an administrator is viewing Owner's profile then hide inputs for change password --}}
|
||||
{{#if canChangePassword}}
|
||||
<form id="password-reset" class="user-profile" novalidate="novalidate" autocomplete="off" {{action (perform user.saveNewPassword) on="submit"}}>
|
||||
<fieldset>
|
||||
@ -301,7 +301,7 @@
|
||||
)
|
||||
data-test-old-pass-input=true
|
||||
}}
|
||||
{{gh-error-message errors=user.errors property="password"}}
|
||||
{{gh-error-message errors=user.errors property="password" data-test-error="user-old-pass"}}
|
||||
{{/gh-form-group}}
|
||||
{{/unless}}
|
||||
|
||||
@ -317,7 +317,7 @@
|
||||
)
|
||||
data-test-new-pass-input=true
|
||||
}}
|
||||
{{gh-error-message errors=user.errors property="newPassword"}}
|
||||
{{gh-error-message errors=user.errors property="newPassword" data-test-error="user-new-pass"}}
|
||||
{{/gh-form-group}}
|
||||
|
||||
{{#gh-form-group errors=user.errors hasValidated=user.hasValidated property="ne2Password"}}
|
||||
@ -332,7 +332,7 @@
|
||||
)
|
||||
data-test-ne2-pass-input=true
|
||||
}}
|
||||
{{gh-error-message errors=user.errors property="ne2Password"}}
|
||||
{{gh-error-message errors=user.errors property="ne2Password" data-test-error="user-ne2-pass"}}
|
||||
{{/gh-form-group}}
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -66,6 +66,7 @@ module.exports = function (environment) {
|
||||
ENV.APP.LOG_VIEW_LOOKUPS = false;
|
||||
|
||||
ENV.APP.rootElement = '#ember-testing';
|
||||
ENV.APP.autoboot = false;
|
||||
|
||||
// This is needed so that browserify dependencies in tests work correctly
|
||||
// See https://github.com/ef4/ember-browserify/issues/14
|
||||
|
@ -1,3 +1,4 @@
|
||||
{
|
||||
"application-template-wrapper": false
|
||||
"application-template-wrapper": false,
|
||||
"jquery-integration": true
|
||||
}
|
||||
|
@ -12,6 +12,6 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"ember-cli-babel": "^6.11.0",
|
||||
"ember-cli-htmlbars": "^2.0.3"
|
||||
"ember-cli-htmlbars": "^3.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -19,24 +19,24 @@
|
||||
"start": "ember serve",
|
||||
"build": "ember build",
|
||||
"test": "ember test",
|
||||
"lint:js": "eslint ./*.js app config lib mirage server tests",
|
||||
"lint:js": "eslint .",
|
||||
"coverage": "cat ./coverage/lcov.info | coveralls"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ember/optional-features": "0.6.1",
|
||||
"@ember/optional-features": "0.6.4",
|
||||
"@html-next/vertical-collection": "1.0.0-beta.12",
|
||||
"@tryghost/mobiledoc-kit": "0.11.1-ghost.5",
|
||||
"blueimp-md5": "2.10.0",
|
||||
"broccoli-asset-rev": "2.7.0",
|
||||
"broccoli-clean-css": "^2.0.1",
|
||||
"broccoli-concat": "3.2.2",
|
||||
"broccoli-concat": "3.7.3",
|
||||
"broccoli-funnel": "2.0.1",
|
||||
"broccoli-merge-trees": "3.0.0",
|
||||
"broccoli-merge-trees": "3.0.1",
|
||||
"broccoli-uglify-sourcemap": "2.2.0",
|
||||
"chai-jquery": "2.0.0",
|
||||
"chai-dom": "1.8.1",
|
||||
"codemirror": "5.39.0",
|
||||
"coveralls": "3.0.2",
|
||||
"csscomb": "4.2.0",
|
||||
@ -45,21 +45,20 @@
|
||||
"ember-ajax": "3.1.1",
|
||||
"ember-assign-helper": "0.1.2",
|
||||
"ember-browserify": "1.2.2",
|
||||
"ember-cli": "3.1.4",
|
||||
"ember-cli": "3.4.1",
|
||||
"ember-cli-app-version": "3.2.0",
|
||||
"ember-cli-babel": "6.15.0",
|
||||
"ember-cli-babel": "6.16.0",
|
||||
"ember-cli-chai": "0.5.0",
|
||||
"ember-cli-cjs-transform": "1.3.0",
|
||||
"ember-cli-code-coverage": "0.4.2",
|
||||
"ember-cli-dependency-checker": "2.1.1",
|
||||
"ember-cli-dependency-checker": "3.0.0",
|
||||
"ember-cli-es6-transform": "^0.0.3",
|
||||
"ember-cli-eslint": "4.2.3",
|
||||
"ember-cli-ghost-spirit": "0.0.6",
|
||||
"ember-cli-htmlbars": "2.0.3",
|
||||
"ember-cli-htmlbars-inline-precompile": "1.0.3",
|
||||
"ember-cli-htmlbars": "3.0.1",
|
||||
"ember-cli-htmlbars-inline-precompile": "1.0.5",
|
||||
"ember-cli-inject-live-reload": "1.7.0",
|
||||
"ember-cli-mirage": "0.4.7",
|
||||
"ember-cli-mocha": "^0.15.0",
|
||||
"ember-cli-mirage": "0.4.9",
|
||||
"ember-cli-moment-shim": "3.7.1",
|
||||
"ember-cli-node-assets": "0.2.2",
|
||||
"ember-cli-postcss": "4.0.0",
|
||||
@ -69,28 +68,30 @@
|
||||
"ember-cli-test-loader": "2.2.0",
|
||||
"ember-cli-uglify": "2.1.0",
|
||||
"ember-composable-helpers": "2.1.0",
|
||||
"ember-concurrency": "0.8.19",
|
||||
"ember-data": "3.2.0",
|
||||
"ember-concurrency": "0.8.22",
|
||||
"ember-data": "3.4.0",
|
||||
"ember-drag-drop": "0.4.8",
|
||||
"ember-export-application-global": "2.0.0",
|
||||
"ember-fetch": "5.0.0",
|
||||
"ember-in-viewport": "3.0.3",
|
||||
"ember-infinity": "1.0.1",
|
||||
"ember-light-table": "1.13.1",
|
||||
"ember-fetch": "5.1.3",
|
||||
"ember-in-viewport": "~3.0.3",
|
||||
"ember-infinity": "1.2.6",
|
||||
"ember-light-table": "1.13.2",
|
||||
"ember-load": "0.0.12",
|
||||
"ember-load-initializers": "1.1.0",
|
||||
"ember-native-dom-helpers": "0.6.2",
|
||||
"ember-mocha": "0.14.0",
|
||||
"ember-moment": "7.8.0",
|
||||
"ember-one-way-select": "4.0.0",
|
||||
"ember-power-datepicker": "0.4.0",
|
||||
"ember-power-select": "1.10.4",
|
||||
"ember-resolver": "4.5.6",
|
||||
"ember-power-calendar-moment": "0.1.3",
|
||||
"ember-power-datepicker": "https://github.com/cibernox/ember-power-datepicker/tarball/cd4dffa8852236a3312f6dc7040719c0e9196bc0",
|
||||
"ember-power-select": "2.0.9",
|
||||
"ember-resolver": "5.0.1",
|
||||
"ember-responsive": "2.0.5",
|
||||
"ember-route-action-helper": "2.0.6",
|
||||
"ember-simple-auth": "1.6.0",
|
||||
"ember-sinon": "2.1.0",
|
||||
"ember-source": "3.1.2",
|
||||
"ember-sticky-element": "0.2.2",
|
||||
"ember-svg-jar": "1.2.0",
|
||||
"ember-source": "3.5.1",
|
||||
"ember-sticky-element": "0.2.3",
|
||||
"ember-svg-jar": "1.2.2",
|
||||
"ember-test-selectors": "1.0.0",
|
||||
"ember-truth-helpers": "2.1.0",
|
||||
"ember-useragent": "0.6.1",
|
||||
@ -118,7 +119,7 @@
|
||||
"password-generator": "2.2.0",
|
||||
"reframe.js": "2.2.1",
|
||||
"simplemde": "https://github.com/kevinansfield/simplemde-markdown-editor.git#ghost",
|
||||
"testem": "2.8.2",
|
||||
"testem": "2.14.0",
|
||||
"top-gh-contribs": "2.0.4",
|
||||
"validator": "7.2.0",
|
||||
"walk-sync": "0.3.2"
|
||||
|
@ -14,13 +14,14 @@ module.exports = {
|
||||
],
|
||||
browser_args: {
|
||||
Chrome: {
|
||||
mode: 'ci',
|
||||
args: [
|
||||
ci: [
|
||||
// --no-sandbox is needed when running Chrome inside a container
|
||||
process.env.TRAVIS ? '--no-sandbox' : null,
|
||||
|
||||
'--disable-gpu',
|
||||
process.env.CI ? '--no-sandbox' : null,
|
||||
'--headless',
|
||||
'--disable-gpu',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-software-rasterizer',
|
||||
'--mute-audio',
|
||||
'--remote-debugging-port=0',
|
||||
'--window-size=1440,900'
|
||||
].filter(Boolean)
|
||||
|
@ -3,19 +3,5 @@ module.exports = {
|
||||
env: {
|
||||
'embertest': true,
|
||||
'mocha': true
|
||||
},
|
||||
globals: {
|
||||
server: false,
|
||||
expect: false,
|
||||
fileUpload: false,
|
||||
|
||||
// ember-power-select test helpers
|
||||
selectChoose: false,
|
||||
selectSearch: false,
|
||||
removeMultipleOption: false,
|
||||
clearSelected: false,
|
||||
|
||||
// ember-power-datepicker test helpers
|
||||
datepickerSelect: false
|
||||
}
|
||||
};
|
||||
|
@ -1,29 +1,24 @@
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import startApp from '../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import windowProxy from 'ghost-admin/utils/window-proxy';
|
||||
import {Response} from 'ember-cli-mirage';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {click, currentRouteName, currentURL, fillIn, findAll, visit} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
|
||||
describe('Acceptance: Authentication', function () {
|
||||
let application,
|
||||
originalReplaceLocation;
|
||||
let originalReplaceLocation;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
describe('setup redirect', function () {
|
||||
beforeEach(function () {
|
||||
// ensure the /users/me route doesn't error
|
||||
server.create('user');
|
||||
server.get('authentication/setup', function () {
|
||||
this.server.create('user');
|
||||
this.server.get('authentication/setup', function () {
|
||||
return {setup: [{status: false}]};
|
||||
});
|
||||
});
|
||||
@ -44,8 +39,8 @@ describe('Acceptance: Authentication', function () {
|
||||
};
|
||||
newLocation = undefined;
|
||||
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
@ -54,13 +49,13 @@ describe('Acceptance: Authentication', function () {
|
||||
|
||||
it('invalidates session on 401 API response', async function () {
|
||||
// return a 401 when attempting to retrieve users
|
||||
server.get('/users/', () => new Response(401, {}, {
|
||||
this.server.get('/users/', () => new Response(401, {}, {
|
||||
errors: [
|
||||
{message: 'Access denied.', errorType: 'UnauthorizedError'}
|
||||
]
|
||||
}));
|
||||
|
||||
await authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/team');
|
||||
|
||||
// running `visit(url)` inside windowProxy.replaceLocation breaks
|
||||
@ -74,27 +69,27 @@ describe('Acceptance: Authentication', function () {
|
||||
});
|
||||
|
||||
it('doesn\'t show navigation menu on invalid url when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
|
||||
await visit('/');
|
||||
|
||||
expect(currentURL(), 'current url').to.equal('/signin');
|
||||
expect(find('nav.gh-nav').length, 'nav menu presence').to.equal(0);
|
||||
expect(findAll('nav.gh-nav').length, 'nav menu presence').to.equal(0);
|
||||
|
||||
await visit('/signin/invalidurl/');
|
||||
|
||||
expect(currentURL(), 'url after invalid url').to.equal('/signin/invalidurl/');
|
||||
expect(currentPath(), 'path after invalid url').to.equal('error404');
|
||||
expect(find('nav.gh-nav').length, 'nav menu presence').to.equal(0);
|
||||
expect(currentRouteName(), 'path after invalid url').to.equal('error404');
|
||||
expect(findAll('nav.gh-nav').length, 'nav menu presence').to.equal(0);
|
||||
});
|
||||
|
||||
it('shows nav menu on invalid url when authenticated', async function () {
|
||||
await authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/signin/invalidurl/');
|
||||
|
||||
expect(currentURL(), 'url after invalid url').to.equal('/signin/invalidurl/');
|
||||
expect(currentPath(), 'path after invalid url').to.equal('error404');
|
||||
expect(find('nav.gh-nav').length, 'nav menu presence').to.equal(1);
|
||||
expect(currentRouteName(), 'path after invalid url').to.equal('error404');
|
||||
expect(findAll('nav.gh-nav').length, 'nav menu presence').to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -110,11 +105,11 @@ describe('Acceptance: Authentication', function () {
|
||||
});
|
||||
|
||||
it('displays re-auth modal attempting to save with invalid session', async function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
// simulate an invalid session when saving the edited post
|
||||
server.put('/posts/:id/', function ({posts}, {params}) {
|
||||
this.server.put('/posts/:id/', function ({posts}, {params}) {
|
||||
let post = posts.find(params.id);
|
||||
let attrs = this.normalizedRequestAttrs();
|
||||
|
||||
@ -129,7 +124,7 @@ describe('Acceptance: Authentication', function () {
|
||||
}
|
||||
});
|
||||
|
||||
await authenticateSession(application);
|
||||
await authenticateSession();
|
||||
|
||||
await visit('/editor');
|
||||
|
||||
@ -139,16 +134,16 @@ describe('Acceptance: Authentication', function () {
|
||||
await click('.js-publish-button');
|
||||
|
||||
// we shouldn't have a modal at this point
|
||||
expect(find('.modal-container #login').length, 'modal exists').to.equal(0);
|
||||
expect(findAll('.modal-container #login').length, 'modal exists').to.equal(0);
|
||||
// we also shouldn't have any alerts
|
||||
expect(find('.gh-alert').length, 'no of alerts').to.equal(0);
|
||||
expect(findAll('.gh-alert').length, 'no of alerts').to.equal(0);
|
||||
|
||||
// update the post
|
||||
await fillIn('.__mobiledoc-editor', 'Edited post body');
|
||||
await click('.js-publish-button');
|
||||
|
||||
// we should see a re-auth modal
|
||||
expect(find('.fullscreen-modal #login').length, 'modal exists').to.equal(1);
|
||||
expect(findAll('.fullscreen-modal #login').length, 'modal exists').to.equal(1);
|
||||
});
|
||||
|
||||
// don't clobber debounce/throttle for future tests
|
||||
|
@ -1,26 +1,17 @@
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import startApp from '../helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {
|
||||
authenticateSession,
|
||||
invalidateSession
|
||||
} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {clickTrigger} from 'ember-power-select/test-support/helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {clickTrigger, selectChoose} from 'ember-power-select/test-support/helpers';
|
||||
import {currentURL, find, findAll, triggerEvent, visit} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
|
||||
describe('Acceptance: Content', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/');
|
||||
|
||||
expect(currentURL()).to.equal('/signin');
|
||||
@ -30,47 +21,47 @@ describe('Acceptance: Content', function () {
|
||||
let admin, editor,
|
||||
publishedPost, scheduledPost, draftPost, publishedPage, authorPost;
|
||||
|
||||
beforeEach(function () {
|
||||
let adminRole = server.create('role', {name: 'Administrator'});
|
||||
admin = server.create('user', {roles: [adminRole]});
|
||||
let editorRole = server.create('role', {name: 'Editor'});
|
||||
editor = server.create('user', {roles: [editorRole]});
|
||||
beforeEach(async function () {
|
||||
let adminRole = this.server.create('role', {name: 'Administrator'});
|
||||
admin = this.server.create('user', {roles: [adminRole]});
|
||||
let editorRole = this.server.create('role', {name: 'Editor'});
|
||||
editor = this.server.create('user', {roles: [editorRole]});
|
||||
|
||||
publishedPost = server.create('post', {authors: [admin], status: 'published', title: 'Published Post'});
|
||||
scheduledPost = server.create('post', {authors: [admin], status: 'scheduled', title: 'Scheduled Post'});
|
||||
draftPost = server.create('post', {authors: [admin], status: 'draft', title: 'Draft Post'});
|
||||
publishedPage = server.create('post', {authors: [admin], status: 'published', page: true, title: 'Published Page'});
|
||||
authorPost = server.create('post', {authors: [editor], status: 'published', title: 'Editor Published Post'});
|
||||
publishedPost = this.server.create('post', {authors: [admin], status: 'published', title: 'Published Post'});
|
||||
scheduledPost = this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Scheduled Post'});
|
||||
draftPost = this.server.create('post', {authors: [admin], status: 'draft', title: 'Draft Post'});
|
||||
publishedPage = this.server.create('post', {authors: [admin], status: 'published', page: true, title: 'Published Page'});
|
||||
authorPost = this.server.create('post', {authors: [editor], status: 'published', title: 'Editor Published Post'});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('displays and filters posts', async function () {
|
||||
await visit('/');
|
||||
// Not checking request here as it won't be the last request made
|
||||
// Displays all posts + pages
|
||||
expect(find('[data-test-post-id]').length, 'all posts count').to.equal(5);
|
||||
expect(findAll('[data-test-post-id]').length, 'all posts count').to.equal(5);
|
||||
|
||||
// show draft posts
|
||||
await selectChoose('[data-test-type-select]', 'Draft posts');
|
||||
|
||||
// API request is correct
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"drafts" request status filter').to.have.string('status:draft');
|
||||
expect(lastRequest.queryParams.filter, '"drafts" request page filter').to.have.string('page:false');
|
||||
// Displays draft post
|
||||
expect(find('[data-test-post-id]').length, 'drafts count').to.equal(1);
|
||||
expect(findAll('[data-test-post-id]').length, 'drafts count').to.equal(1);
|
||||
expect(find(`[data-test-post-id="${draftPost.id}"]`), 'draft post').to.exist;
|
||||
|
||||
// show published posts
|
||||
await selectChoose('[data-test-type-select]', 'Published posts');
|
||||
|
||||
// API request is correct
|
||||
[lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"published" request status filter').to.have.string('status:published');
|
||||
expect(lastRequest.queryParams.filter, '"published" request page filter').to.have.string('page:false');
|
||||
// Displays three published posts + pages
|
||||
expect(find('[data-test-post-id]').length, 'published count').to.equal(2);
|
||||
expect(findAll('[data-test-post-id]').length, 'published count').to.equal(2);
|
||||
expect(find(`[data-test-post-id="${publishedPost.id}"]`), 'admin published post').to.exist;
|
||||
expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author published post').to.exist;
|
||||
|
||||
@ -78,29 +69,29 @@ describe('Acceptance: Content', function () {
|
||||
await selectChoose('[data-test-type-select]', 'Scheduled posts');
|
||||
|
||||
// API request is correct
|
||||
[lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"scheduled" request status filter').to.have.string('status:scheduled');
|
||||
expect(lastRequest.queryParams.filter, '"scheduled" request page filter').to.have.string('page:false');
|
||||
// Displays scheduled post
|
||||
expect(find('[data-test-post-id]').length, 'scheduled count').to.equal(1);
|
||||
expect(findAll('[data-test-post-id]').length, 'scheduled count').to.equal(1);
|
||||
expect(find(`[data-test-post-id="${scheduledPost.id}"]`), 'scheduled post').to.exist;
|
||||
|
||||
// show pages
|
||||
await selectChoose('[data-test-type-select]', 'Pages');
|
||||
|
||||
// API request is correct
|
||||
[lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"pages" request status filter').to.have.string('status:[draft,scheduled,published]');
|
||||
expect(lastRequest.queryParams.filter, '"pages" request page filter').to.have.string('page:true');
|
||||
// Displays page
|
||||
expect(find('[data-test-post-id]').length, 'pages count').to.equal(1);
|
||||
expect(findAll('[data-test-post-id]').length, 'pages count').to.equal(1);
|
||||
expect(find(`[data-test-post-id="${publishedPage.id}"]`), 'page post').to.exist;
|
||||
|
||||
// show all posts
|
||||
await selectChoose('[data-test-type-select]', 'All posts');
|
||||
|
||||
// API request is correct
|
||||
[lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"all" request status filter').to.have.string('status:[draft,scheduled,published]');
|
||||
expect(lastRequest.queryParams.filter, '"all" request page filter').to.have.string('page:[true,false]');
|
||||
|
||||
@ -108,7 +99,7 @@ describe('Acceptance: Content', function () {
|
||||
await selectChoose('[data-test-author-select]', editor.name);
|
||||
|
||||
// API request is correct
|
||||
[lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter, '"editor" request status filter').to.have.string('status:[draft,scheduled,published]');
|
||||
expect(lastRequest.queryParams.filter, '"editor" request page filter').to.have.string('page:[true,false]');
|
||||
expect(lastRequest.queryParams.filter, '"editor" request filter param').to.have.string(`authors:${editor.slug}`);
|
||||
@ -127,14 +118,14 @@ describe('Acceptance: Content', function () {
|
||||
});
|
||||
|
||||
it('sorts tags filter alphabetically', async function () {
|
||||
server.create('tag', {name: 'B - Second', slug: 'second'});
|
||||
server.create('tag', {name: 'Z - Last', slug: 'last'});
|
||||
server.create('tag', {name: 'A - First', slug: 'first'});
|
||||
this.server.create('tag', {name: 'B - Second', slug: 'second'});
|
||||
this.server.create('tag', {name: 'Z - Last', slug: 'last'});
|
||||
this.server.create('tag', {name: 'A - First', slug: 'first'});
|
||||
|
||||
await visit('/');
|
||||
await clickTrigger('[data-test-tag-select]');
|
||||
|
||||
let options = find('.ember-power-select-option');
|
||||
let options = findAll('.ember-power-select-option');
|
||||
|
||||
expect(options[0].textContent.trim()).to.equal('All tags');
|
||||
expect(options[1].textContent.trim()).to.equal('A - First');
|
||||
@ -146,17 +137,17 @@ describe('Acceptance: Content', function () {
|
||||
describe('as author', function () {
|
||||
let author, authorPost;
|
||||
|
||||
beforeEach(function () {
|
||||
let authorRole = server.create('role', {name: 'Author'});
|
||||
author = server.create('user', {roles: [authorRole]});
|
||||
let adminRole = server.create('role', {name: 'Administrator'});
|
||||
let admin = server.create('user', {roles: [adminRole]});
|
||||
beforeEach(async function () {
|
||||
let authorRole = this.server.create('role', {name: 'Author'});
|
||||
author = this.server.create('user', {roles: [authorRole]});
|
||||
let adminRole = this.server.create('role', {name: 'Administrator'});
|
||||
let admin = this.server.create('user', {roles: [adminRole]});
|
||||
|
||||
// create posts
|
||||
authorPost = server.create('post', {authors: [author], status: 'published', title: 'Author Post'});
|
||||
server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'});
|
||||
authorPost = this.server.create('post', {authors: [author], status: 'published', title: 'Author Post'});
|
||||
this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('only fetches the author\'s posts', async function () {
|
||||
@ -165,11 +156,11 @@ describe('Acceptance: Content', function () {
|
||||
await selectChoose('[data-test-type-select]', 'Published posts');
|
||||
|
||||
// API request includes author filter
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter).to.have.string(`authors:${author.slug}`);
|
||||
|
||||
// only author's post is shown
|
||||
expect(find('[data-test-post-id]').length, 'post count').to.equal(1);
|
||||
expect(findAll('[data-test-post-id]').length, 'post count').to.equal(1);
|
||||
expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author post').to.exist;
|
||||
});
|
||||
});
|
||||
@ -177,18 +168,18 @@ describe('Acceptance: Content', function () {
|
||||
describe('as contributor', function () {
|
||||
let contributor, contributorPost;
|
||||
|
||||
beforeEach(function () {
|
||||
let contributorRole = server.create('role', {name: 'Contributor'});
|
||||
contributor = server.create('user', {roles: [contributorRole]});
|
||||
let adminRole = server.create('role', {name: 'Administrator'});
|
||||
let admin = server.create('user', {roles: [adminRole]});
|
||||
beforeEach(async function () {
|
||||
let contributorRole = this.server.create('role', {name: 'Contributor'});
|
||||
contributor = this.server.create('user', {roles: [contributorRole]});
|
||||
let adminRole = this.server.create('role', {name: 'Administrator'});
|
||||
let admin = this.server.create('user', {roles: [adminRole]});
|
||||
|
||||
// Create posts
|
||||
contributorPost = server.create('post', {authors: [contributor], status: 'draft', title: 'Contributor Post Draft'});
|
||||
server.create('post', {authors: [contributor], status: 'published', title: 'Contributor Published Post'});
|
||||
server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'});
|
||||
contributorPost = this.server.create('post', {authors: [contributor], status: 'draft', title: 'Contributor Post Draft'});
|
||||
this.server.create('post', {authors: [contributor], status: 'published', title: 'Contributor Published Post'});
|
||||
this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('only fetches the contributor\'s draft posts', async function () {
|
||||
@ -203,11 +194,11 @@ describe('Acceptance: Content', function () {
|
||||
await selectChoose('[data-test-order-select]', 'Oldest');
|
||||
|
||||
// API request includes author filter
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.filter).to.have.string(`authors:${contributor.slug}`);
|
||||
|
||||
// only contributor's post is shown
|
||||
expect(find('[data-test-post-id]').length, 'post count').to.equal(1);
|
||||
expect(findAll('[data-test-post-id]').length, 'post count').to.equal(1);
|
||||
expect(find(`[data-test-post-id="${contributorPost.id}"]`), 'author post').to.exist;
|
||||
});
|
||||
});
|
||||
|
@ -1,35 +1,30 @@
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import destroyApp from 'ghost-admin/tests/helpers/destroy-app';
|
||||
import startApp from 'ghost-admin/tests/helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {click, fillIn, find, keyEvent, visit} from 'ember-native-dom-helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {click, fillIn, find, triggerKeyEvent, visit} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
|
||||
// keyCodes
|
||||
const KEY_S = 83;
|
||||
|
||||
describe('Acceptance: Custom Post Templates', function () {
|
||||
let application;
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
beforeEach(async function () {
|
||||
this.server.loadFixtures('settings');
|
||||
|
||||
server.loadFixtures('settings');
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
|
||||
authenticateSession(application);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
describe('with custom templates', function () {
|
||||
beforeEach(function () {
|
||||
server.create('theme', {
|
||||
this.server.create('theme', {
|
||||
active: true,
|
||||
name: 'example-theme',
|
||||
package: {
|
||||
@ -66,7 +61,7 @@ describe('Acceptance: Custom Post Templates', function () {
|
||||
});
|
||||
|
||||
it('can change selected template', async function () {
|
||||
let post = server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
let post = this.server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
|
||||
await visit('/editor/1');
|
||||
await click('[data-test-psm-trigger]');
|
||||
@ -91,13 +86,13 @@ describe('Acceptance: Custom Post Templates', function () {
|
||||
await fillIn(select, '');
|
||||
|
||||
// save then check server record
|
||||
await keyEvent('.gh-app', 'keydown', KEY_S, {
|
||||
await triggerKeyEvent('.gh-app', 'keydown', KEY_S, {
|
||||
metaKey: ctrlOrCmd === 'command',
|
||||
ctrlKey: ctrlOrCmd === 'ctrl'
|
||||
});
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).customTemplate,
|
||||
this.server.db.posts.find(post.id).customTemplate,
|
||||
'saved custom template'
|
||||
).to.equal('');
|
||||
});
|
||||
@ -105,13 +100,14 @@ describe('Acceptance: Custom Post Templates', function () {
|
||||
it('disables template selector if slug matches slug-based template');
|
||||
|
||||
it('doesn\'t query themes endpoint unncessarily', async function () {
|
||||
function themeRequests() {
|
||||
return server.pretender.handledRequests.filter(function (request) {
|
||||
// eslint-disable-next-line
|
||||
let themeRequests = () => {
|
||||
return this.server.pretender.handledRequests.filter(function (request) {
|
||||
return request.url.match(/\/themes\//);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
this.server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
|
||||
await visit('/editor/1');
|
||||
await click('[data-test-psm-trigger]');
|
||||
@ -127,7 +123,7 @@ describe('Acceptance: Custom Post Templates', function () {
|
||||
|
||||
describe('without custom templates', function () {
|
||||
beforeEach(function () {
|
||||
server.create('theme', {
|
||||
this.server.create('theme', {
|
||||
active: true,
|
||||
name: 'example-theme',
|
||||
package: {
|
||||
@ -139,7 +135,7 @@ describe('Acceptance: Custom Post Templates', function () {
|
||||
});
|
||||
|
||||
it('doesn\'t show template selector', async function () {
|
||||
server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
this.server.create('post', {customTemplate: 'custom-news-bulletin.hbs'});
|
||||
|
||||
await visit('/editor/1');
|
||||
await click('[data-test-psm-trigger]');
|
||||
|
@ -1,84 +1,83 @@
|
||||
import Mirage from 'ember-cli-mirage';
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import moment from 'moment';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import sinon from 'sinon';
|
||||
import startApp from '../helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {blur, click, currentRouteName, currentURL, fillIn, find, findAll, triggerEvent} from '@ember/test-helpers';
|
||||
import {datepickerSelect} from 'ember-power-datepicker/test-support';
|
||||
import {expect} from 'chai';
|
||||
// import {selectChoose} from 'ember-power-select/test-support';
|
||||
import {selectChoose} from 'ember-power-select/test-support';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../helpers/visit';
|
||||
|
||||
// TODO: update ember-power-datepicker to expose modern test helpers
|
||||
// https://github.com/cibernox/ember-power-datepicker/issues/30
|
||||
|
||||
describe('Acceptance: Editor', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
let author = server.create('user'); // necesary for post-author association
|
||||
server.create('post', {authors: [author]});
|
||||
let author = this.server.create('user'); // necesary for post-author association
|
||||
this.server.create('post', {authors: [author]});
|
||||
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/editor/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('does not redirect to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
let author = server.create('user', {roles: [role], slug: 'test-user'});
|
||||
server.create('post', {authors: [author]});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
let author = this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
this.server.create('post', {authors: [author]});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/editor/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/1');
|
||||
});
|
||||
|
||||
it('does not redirect to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
let author = server.create('user', {roles: [role], slug: 'test-user'});
|
||||
server.create('post', {authors: [author]});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
let author = this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
this.server.create('post', {authors: [author]});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/editor/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/1');
|
||||
});
|
||||
|
||||
it('does not redirect to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
let author = server.create('user', {roles: [role], slug: 'test-user'});
|
||||
server.create('post', {authors: [author]});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
let author = this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
this.server.create('post', {authors: [author]});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/editor/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/1');
|
||||
});
|
||||
|
||||
it('displays 404 when post does not exist', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/editor/1');
|
||||
|
||||
expect(currentPath()).to.equal('error404');
|
||||
expect(currentRouteName()).to.equal('error404');
|
||||
expect(currentURL()).to.equal('/editor/1');
|
||||
});
|
||||
|
||||
it('when logged in as a contributor, renders a save button instead of a publish menu & hides tags input', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
let author = server.create('user', {roles: [role]});
|
||||
server.createList('post', 2, {authors: [author]});
|
||||
server.loadFixtures('settings');
|
||||
authenticateSession(application);
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
let author = this.server.create('user', {roles: [role]});
|
||||
this.server.createList('post', 2, {authors: [author]});
|
||||
this.server.loadFixtures('settings');
|
||||
await authenticateSession();
|
||||
|
||||
// post id 1 is a draft, checking for draft behaviour now
|
||||
await visit('/editor/1');
|
||||
@ -109,16 +108,16 @@ describe('Acceptance: Editor', function () {
|
||||
describe('when logged in', function () {
|
||||
let author;
|
||||
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
author = server.create('user', {roles: [role]});
|
||||
server.loadFixtures('settings');
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
author = this.server.create('user', {roles: [role]});
|
||||
this.server.loadFixtures('settings');
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('renders the editor correctly, PSM Publish Date and Save Button', async function () {
|
||||
let [post1] = server.createList('post', 2, {authors: [author]});
|
||||
let [post1] = this.server.createList('post', 2, {authors: [author]});
|
||||
let futureTime = moment().tz('Etc/UTC').add(10, 'minutes');
|
||||
|
||||
// post id 1 is a draft, checking for draft behaviour now
|
||||
@ -132,19 +131,19 @@ describe('Acceptance: Editor', function () {
|
||||
|
||||
// should error, if the publish time is in the wrong format
|
||||
await fillIn('[data-test-date-time-picker-time-input]', 'foo');
|
||||
await triggerEvent('[data-test-date-time-picker-time-input]', 'blur');
|
||||
await blur('[data-test-date-time-picker-time-input]');
|
||||
|
||||
expect(find('[data-test-date-time-picker-error]').text().trim(), 'inline error response for invalid time')
|
||||
expect(find('[data-test-date-time-picker-error]').textContent.trim(), 'inline error response for invalid time')
|
||||
.to.equal('Must be in format: "15:00"');
|
||||
|
||||
// should error, if the publish time is in the future
|
||||
// NOTE: date must be selected first, changing the time first will save
|
||||
// with the new time
|
||||
await datepickerSelect('[data-test-date-time-picker-datepicker]', moment.tz('Etc/UTC'));
|
||||
await datepickerSelect('[data-test-date-time-picker-datepicker]', moment.tz('Etc/UTC').toDate());
|
||||
await fillIn('[data-test-date-time-picker-time-input]', futureTime.format('HH:mm'));
|
||||
await triggerEvent('[data-test-date-time-picker-time-input]', 'blur');
|
||||
await blur('[data-test-date-time-picker-time-input]');
|
||||
|
||||
expect(find('[data-test-date-time-picker-error]').text().trim(), 'inline error response for future time')
|
||||
expect(find('[data-test-date-time-picker-error]').textContent.trim(), 'inline error response for future time')
|
||||
.to.equal('Must be in the past');
|
||||
|
||||
// closing the PSM will reset the invalid date/time
|
||||
@ -152,37 +151,37 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-psm-trigger]');
|
||||
|
||||
expect(
|
||||
find('[data-test-date-time-picker-error]').text().trim(),
|
||||
find('[data-test-date-time-picker-error]'),
|
||||
'date picker error after closing PSM'
|
||||
).to.equal('');
|
||||
).to.not.exist;
|
||||
|
||||
expect(
|
||||
find('[data-test-date-time-picker-date-input]').val(),
|
||||
find('[data-test-date-time-picker-date-input]').value,
|
||||
'PSM date value after closing with invalid date'
|
||||
).to.equal(moment(post1.publishedAt).tz('Etc/UTC').format('MM/DD/YYYY'));
|
||||
|
||||
expect(
|
||||
find('[data-test-date-time-picker-time-input]').val(),
|
||||
find('[data-test-date-time-picker-time-input]').value,
|
||||
'PSM time value after closing with invalid date'
|
||||
).to.equal(moment(post1.publishedAt).tz('Etc/UTC').format('HH:mm'));
|
||||
|
||||
// saves the post with the new date
|
||||
let validTime = moment('2017-04-09 12:00').tz('Etc/UTC');
|
||||
await fillIn('[data-test-date-time-picker-time-input]', validTime.format('HH:mm'));
|
||||
await triggerEvent('[data-test-date-time-picker-time-input]', 'blur');
|
||||
await datepickerSelect('[data-test-date-time-picker-datepicker]', validTime);
|
||||
await blur('[data-test-date-time-picker-time-input]');
|
||||
await datepickerSelect('[data-test-date-time-picker-datepicker]', validTime.toDate());
|
||||
|
||||
// hide psm
|
||||
await click('[data-test-close-settings-menu]');
|
||||
|
||||
// checking the flow of the saving button for a draft
|
||||
expect(
|
||||
find('[data-test-publishmenu-trigger]').text().trim(),
|
||||
find('[data-test-publishmenu-trigger]').textContent.trim(),
|
||||
'draft publish button text'
|
||||
).to.equal('Publish');
|
||||
|
||||
expect(
|
||||
find('[data-test-editor-post-status]').text().trim(),
|
||||
find('[data-test-editor-post-status]').textContent.trim(),
|
||||
'draft status text'
|
||||
).to.equal('Draft');
|
||||
|
||||
@ -197,14 +196,14 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-publishmenu-scheduled-option]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'draft post schedule button text'
|
||||
).to.equal('Schedule');
|
||||
|
||||
await click('[data-test-publishmenu-published-option]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'draft post publish button text'
|
||||
).to.equal('Publish');
|
||||
|
||||
@ -212,7 +211,7 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'publish menu save button updated after draft is published'
|
||||
).to.equal('Published');
|
||||
|
||||
@ -222,7 +221,7 @@ describe('Acceptance: Editor', function () {
|
||||
).to.exist;
|
||||
|
||||
expect(
|
||||
find('[data-test-editor-post-status]').text().trim(),
|
||||
find('[data-test-editor-post-status]').textContent.trim(),
|
||||
'post status updated after draft published'
|
||||
).to.equal('Published');
|
||||
|
||||
@ -231,7 +230,7 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-publishmenu-unpublished-option]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'published post unpublish button text'
|
||||
).to.equal('Unpublish');
|
||||
|
||||
@ -239,25 +238,25 @@ describe('Acceptance: Editor', function () {
|
||||
await visit('/editor/2');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/editor/2');
|
||||
expect(find('[data-test-date-time-picker-date-input]').val()).to.equal('12/19/2015');
|
||||
expect(find('[data-test-date-time-picker-time-input]').val()).to.equal('16:25');
|
||||
expect(find('[data-test-date-time-picker-date-input]').value).to.equal('12/19/2015');
|
||||
expect(find('[data-test-date-time-picker-time-input]').value).to.equal('16:25');
|
||||
|
||||
// saves the post with a new date
|
||||
await datepickerSelect('[data-test-date-time-picker-datepicker]', moment('2016-05-10 10:00'));
|
||||
await datepickerSelect('[data-test-date-time-picker-datepicker]', moment('2016-05-10 10:00').toDate());
|
||||
await fillIn('[data-test-date-time-picker-time-input]', '10:00');
|
||||
await triggerEvent('[data-test-date-time-picker-time-input]', 'blur');
|
||||
await blur('[data-test-date-time-picker-time-input]');
|
||||
// saving
|
||||
await click('[data-test-publishmenu-trigger]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'published button text'
|
||||
).to.equal('Update');
|
||||
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'publish menu save button updated after published post is updated'
|
||||
).to.equal('Updated');
|
||||
|
||||
@ -267,17 +266,17 @@ describe('Acceptance: Editor', function () {
|
||||
|
||||
expect(currentURL(), 'currentURL for settings')
|
||||
.to.equal('/settings/general');
|
||||
expect(find('#activeTimezone option:selected').text().trim(), 'default timezone')
|
||||
expect(find('#activeTimezone option:checked').textContent.trim(), 'default timezone')
|
||||
.to.equal('(GMT) UTC');
|
||||
|
||||
// select a new timezone
|
||||
find('#activeTimezone option[value="Pacific/Kwajalein"]').prop('selected', true);
|
||||
find('#activeTimezone option[value="Pacific/Kwajalein"]').selected = true;
|
||||
|
||||
await triggerEvent('#activeTimezone', 'change');
|
||||
// save the settings
|
||||
await click('.gh-btn.gh-btn-blue');
|
||||
|
||||
expect(find('#activeTimezone option:selected').text().trim(), 'new timezone after saving')
|
||||
expect(find('#activeTimezone option:checked').textContent.trim(), 'new timezone after saving')
|
||||
.to.equal('(GMT +12:00) International Date Line West');
|
||||
|
||||
// and now go back to the editor
|
||||
@ -287,12 +286,12 @@ describe('Acceptance: Editor', function () {
|
||||
.to.equal('/editor/2');
|
||||
|
||||
expect(
|
||||
find('[data-test-date-time-picker-date-input]').val(),
|
||||
find('[data-test-date-time-picker-date-input]').value,
|
||||
'date after timezone change'
|
||||
).to.equal('05/10/2016');
|
||||
|
||||
expect(
|
||||
find('[data-test-date-time-picker-time-input]').val(),
|
||||
find('[data-test-date-time-picker-time-input]').value,
|
||||
'time after timezone change'
|
||||
).to.equal('22:00');
|
||||
|
||||
@ -301,14 +300,14 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-publishmenu-unpublished-option]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'published post unpublish button text'
|
||||
).to.equal('Unpublish');
|
||||
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'publish menu save button updated after published post is unpublished'
|
||||
).to.equal('Unpublished');
|
||||
|
||||
@ -318,7 +317,7 @@ describe('Acceptance: Editor', function () {
|
||||
).to.exist;
|
||||
|
||||
expect(
|
||||
find('[data-test-editor-post-status]').text().trim(),
|
||||
find('[data-test-editor-post-status]').textContent.trim(),
|
||||
'post status updated after unpublished'
|
||||
).to.equal('Draft');
|
||||
|
||||
@ -330,15 +329,15 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-publishmenu-scheduled-option]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'draft post, schedule button text'
|
||||
).to.equal('Schedule');
|
||||
|
||||
await datepickerSelect('[data-test-publishmenu-draft] [data-test-date-time-picker-datepicker]', newFutureTime);
|
||||
await datepickerSelect('[data-test-publishmenu-draft] [data-test-date-time-picker-datepicker]', new Date(newFutureTime.format().replace(/\+.*$/, '')));
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'publish menu save button updated after draft is scheduled'
|
||||
).to.equal('Scheduled');
|
||||
|
||||
@ -350,16 +349,16 @@ describe('Acceptance: Editor', function () {
|
||||
).to.not.exist;
|
||||
|
||||
// expect countdown to show warning, that post will go live in x minutes
|
||||
expect(find('[data-test-schedule-countdown]').text().trim(), 'notification countdown')
|
||||
expect(find('[data-test-schedule-countdown]').textContent.trim(), 'notification countdown')
|
||||
.to.contain('Post will go live in');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-trigger]').text().trim(),
|
||||
find('[data-test-publishmenu-trigger]').textContent.trim(),
|
||||
'scheduled publish button text'
|
||||
).to.equal('Scheduled');
|
||||
|
||||
expect(
|
||||
find('[data-test-editor-post-status]').text().trim(),
|
||||
find('[data-test-editor-post-status]').textContent.trim(),
|
||||
'scheduled post status'
|
||||
).to.equal('Scheduled');
|
||||
|
||||
@ -367,14 +366,14 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-publishmenu-trigger]');
|
||||
await click('[data-test-publishmenu-scheduled-option]');
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'scheduled post button reschedule text'
|
||||
).to.equal('Reschedule');
|
||||
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'publish menu save button text for a rescheduled post'
|
||||
).to.equal('Rescheduled');
|
||||
|
||||
@ -386,7 +385,7 @@ describe('Acceptance: Editor', function () {
|
||||
).to.not.exist;
|
||||
|
||||
expect(
|
||||
find('[data-test-editor-post-status]').text().trim(),
|
||||
find('[data-test-editor-post-status]').textContent.trim(),
|
||||
'scheduled status text'
|
||||
).to.equal('Scheduled');
|
||||
|
||||
@ -395,26 +394,26 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-publishmenu-draft-option]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'publish menu save button updated after scheduled post is unscheduled'
|
||||
).to.equal('Unschedule');
|
||||
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-save]').text().trim(),
|
||||
find('[data-test-publishmenu-save]').textContent.trim(),
|
||||
'publish menu save button updated after scheduled post is unscheduled'
|
||||
).to.equal('Unscheduled');
|
||||
|
||||
await click('[data-test-publishmenu-cancel]');
|
||||
|
||||
expect(
|
||||
find('[data-test-publishmenu-trigger]').text().trim(),
|
||||
find('[data-test-publishmenu-trigger]').textContent.trim(),
|
||||
'publish button text after unschedule'
|
||||
).to.equal('Publish');
|
||||
|
||||
expect(
|
||||
find('[data-test-editor-post-status]').text().trim(),
|
||||
find('[data-test-editor-post-status]').textContent.trim(),
|
||||
'status text after unschedule'
|
||||
).to.equal('Draft');
|
||||
|
||||
@ -425,7 +424,7 @@ describe('Acceptance: Editor', function () {
|
||||
});
|
||||
|
||||
it('handles validation errors when scheduling', async function () {
|
||||
server.put('/posts/:id/', function () {
|
||||
this.server.put('/posts/:id/', function () {
|
||||
return new Mirage.Response(422, {}, {
|
||||
errors: [{
|
||||
errorType: 'ValidationError',
|
||||
@ -434,32 +433,32 @@ describe('Acceptance: Editor', function () {
|
||||
});
|
||||
});
|
||||
|
||||
let post = server.create('post', 1, {authors: [author], status: 'draft'});
|
||||
let post = this.server.create('post', 1, {authors: [author], status: 'draft'});
|
||||
let plusTenMin = moment().utc().add(10, 'minutes');
|
||||
|
||||
await visit(`/editor/${post.id}`);
|
||||
|
||||
await click('[data-test-publishmenu-trigger]');
|
||||
await click('[data-test-publishmenu-scheduled-option]');
|
||||
await datepickerSelect('[data-test-publishmenu-draft] [data-test-date-time-picker-datepicker]', plusTenMin);
|
||||
await datepickerSelect('[data-test-publishmenu-draft] [data-test-date-time-picker-datepicker]', plusTenMin.toDate());
|
||||
await fillIn('[data-test-publishmenu-draft] [data-test-date-time-picker-time-input]', plusTenMin.format('HH:mm'));
|
||||
await triggerEvent('[data-test-publishmenu-draft] [data-test-date-time-picker-time-input]', 'blur');
|
||||
await blur('[data-test-publishmenu-draft] [data-test-date-time-picker-time-input]');
|
||||
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
expect(
|
||||
find('.gh-alert').length,
|
||||
findAll('.gh-alert').length,
|
||||
'number of alerts after failed schedule'
|
||||
).to.equal(1);
|
||||
|
||||
expect(
|
||||
find('.gh-alert').text(),
|
||||
find('.gh-alert').textContent,
|
||||
'alert text after failed schedule'
|
||||
).to.match(/Saving failed: Error test/);
|
||||
});
|
||||
|
||||
it('handles title validation errors correctly', async function () {
|
||||
server.create('post', {authors: [author]});
|
||||
this.server.create('post', {authors: [author]});
|
||||
|
||||
// post id 1 is a draft, checking for draft behaviour now
|
||||
await visit('/editor/1');
|
||||
@ -472,19 +471,19 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
expect(
|
||||
find('.gh-alert').length,
|
||||
findAll('.gh-alert').length,
|
||||
'number of alerts after invalid title'
|
||||
).to.equal(1);
|
||||
|
||||
expect(
|
||||
find('.gh-alert').text(),
|
||||
find('.gh-alert').textContent,
|
||||
'alert text after invalid title'
|
||||
).to.match(/Title cannot be longer than 255 characters/);
|
||||
});
|
||||
|
||||
// NOTE: these tests are specific to the mobiledoc editor
|
||||
// it('inserts a placeholder if the title is blank', async function () {
|
||||
// server.createList('post', 1);
|
||||
// this.server.createList('post', 1);
|
||||
//
|
||||
// // post id 1 is a draft, checking for draft behaviour now
|
||||
// await visit('/editor/1');
|
||||
@ -506,7 +505,7 @@ describe('Acceptance: Editor', function () {
|
||||
// });
|
||||
//
|
||||
// it('removes HTML from the title.', async function () {
|
||||
// server.createList('post', 1);
|
||||
// this.server.createList('post', 1);
|
||||
//
|
||||
// // post id 1 is a draft, checking for draft behaviour now
|
||||
// await visit('/editor/1');
|
||||
@ -526,32 +525,32 @@ describe('Acceptance: Editor', function () {
|
||||
let compareDate = moment().tz('Etc/UTC').add(4, 'minutes');
|
||||
let compareDateString = compareDate.format('MM/DD/YYYY');
|
||||
let compareTimeString = compareDate.format('HH:mm');
|
||||
server.create('post', {publishedAt: moment.utc().add(4, 'minutes'), status: 'scheduled', authors: [author]});
|
||||
server.create('setting', {activeTimezone: 'Europe/Dublin'});
|
||||
this.server.create('post', {publishedAt: moment.utc().add(4, 'minutes'), status: 'scheduled', authors: [author]});
|
||||
this.server.create('setting', {activeTimezone: 'Europe/Dublin'});
|
||||
clock.restore();
|
||||
|
||||
await visit('/editor/1');
|
||||
|
||||
expect(currentURL(), 'currentURL')
|
||||
.to.equal('/editor/1');
|
||||
expect(find('[data-test-date-time-picker-date-input]').val(), 'scheduled date')
|
||||
expect(find('[data-test-date-time-picker-date-input]').value, 'scheduled date')
|
||||
.to.equal(compareDateString);
|
||||
expect(find('[data-test-date-time-picker-time-input]').val(), 'scheduled time')
|
||||
expect(find('[data-test-date-time-picker-time-input]').value, 'scheduled time')
|
||||
.to.equal(compareTimeString);
|
||||
// Dropdown menu should be 'Update Post' and 'Unschedule'
|
||||
expect(find('[data-test-publishmenu-trigger]').text().trim(), 'text in save button for scheduled post')
|
||||
expect(find('[data-test-publishmenu-trigger]').textContent.trim(), 'text in save button for scheduled post')
|
||||
.to.equal('Scheduled');
|
||||
// expect countdown to show warning, that post will go live in x minutes
|
||||
expect(find('[data-test-schedule-countdown]').text().trim(), 'notification countdown')
|
||||
expect(find('[data-test-schedule-countdown]').textContent.trim(), 'notification countdown')
|
||||
.to.contain('Post will go live in');
|
||||
});
|
||||
|
||||
it('shows author token input and allows changing of authors in PSM', async function () {
|
||||
let adminRole = server.create('role', {name: 'Adminstrator'});
|
||||
let authorRole = server.create('role', {name: 'Author'});
|
||||
let user1 = server.create('user', {name: 'Primary', roles: [adminRole]});
|
||||
server.create('user', {name: 'Waldo', roles: [authorRole]});
|
||||
server.create('post', {authors: [user1]});
|
||||
let adminRole = this.server.create('role', {name: 'Adminstrator'});
|
||||
let authorRole = this.server.create('role', {name: 'Author'});
|
||||
let user1 = this.server.create('user', {name: 'Primary', roles: [adminRole]});
|
||||
this.server.create('user', {name: 'Waldo', roles: [authorRole]});
|
||||
this.server.create('post', {authors: [user1]});
|
||||
|
||||
await visit('/editor/1');
|
||||
|
||||
@ -560,14 +559,14 @@ describe('Acceptance: Editor', function () {
|
||||
|
||||
await click('button.post-settings');
|
||||
|
||||
let tokens = find('[data-test-input="authors"] .ember-power-select-multiple-option');
|
||||
let tokens = findAll('[data-test-input="authors"] .ember-power-select-multiple-option');
|
||||
|
||||
expect(tokens.length).to.equal(1);
|
||||
expect(tokens[0].textContent.trim()).to.have.string('Primary');
|
||||
|
||||
await selectChoose('[data-test-input="authors"]', 'Waldo');
|
||||
|
||||
let savedAuthors = server.schema.posts.find('1').authors.models;
|
||||
let savedAuthors = this.server.schema.posts.find('1').authors.models;
|
||||
|
||||
expect(savedAuthors.length).to.equal(2);
|
||||
expect(savedAuthors[0].name).to.equal('Primary');
|
||||
@ -575,8 +574,8 @@ describe('Acceptance: Editor', function () {
|
||||
});
|
||||
|
||||
it('autosaves when title loses focus', async function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {name: 'Admin', roles: [role]});
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {name: 'Admin', roles: [role]});
|
||||
|
||||
await visit('/editor');
|
||||
|
||||
@ -589,10 +588,11 @@ describe('Acceptance: Editor', function () {
|
||||
'url on initial visit'
|
||||
).to.equal('/editor');
|
||||
|
||||
await triggerEvent('[data-test-editor-title-input]', 'blur');
|
||||
await click('[data-test-editor-title-input]');
|
||||
await blur('[data-test-editor-title-input]');
|
||||
|
||||
expect(
|
||||
find('[data-test-editor-title-input]').val(),
|
||||
find('[data-test-editor-title-input]').value,
|
||||
'title value after autosave'
|
||||
).to.equal('(Untitled)');
|
||||
|
||||
@ -603,7 +603,7 @@ describe('Acceptance: Editor', function () {
|
||||
});
|
||||
|
||||
it('saves post settings fields', async function () {
|
||||
let post = server.create('post', {authors: [author]});
|
||||
let post = this.server.create('post', {authors: [author]});
|
||||
|
||||
await visit(`/editor/${post.id}`);
|
||||
|
||||
@ -613,24 +613,24 @@ describe('Acceptance: Editor', function () {
|
||||
|
||||
// excerpt has validation
|
||||
await fillIn('[data-test-field="custom-excerpt"]', Array(302).join('a'));
|
||||
await triggerEvent('[data-test-field="custom-excerpt"]', 'blur');
|
||||
await blur('[data-test-field="custom-excerpt"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="custom-excerpt"]').text().trim(),
|
||||
find('[data-test-error="custom-excerpt"]').textContent.trim(),
|
||||
'excerpt too long error'
|
||||
).to.match(/cannot be longer than 300/);
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).customExcerpt,
|
||||
this.server.db.posts.find(post.id).customExcerpt,
|
||||
'saved excerpt after validation error'
|
||||
).to.be.null;
|
||||
|
||||
// changing custom excerpt auto-saves
|
||||
await fillIn('[data-test-field="custom-excerpt"]', 'Testing excerpt');
|
||||
await triggerEvent('[data-test-field="custom-excerpt"]', 'blur');
|
||||
await blur('[data-test-field="custom-excerpt"]');
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).customExcerpt,
|
||||
this.server.db.posts.find(post.id).customExcerpt,
|
||||
'saved excerpt'
|
||||
).to.equal('Testing excerpt');
|
||||
|
||||
@ -640,50 +640,54 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-button="codeinjection"]');
|
||||
|
||||
// header injection has validation
|
||||
let headerCM = find('[data-test-field="codeinjection-head"] .CodeMirror')[0].CodeMirror;
|
||||
let headerCM = find('[data-test-field="codeinjection-head"] .CodeMirror').CodeMirror;
|
||||
await headerCM.setValue(Array(65540).join('a'));
|
||||
await triggerEvent(headerCM.getInputField(), 'blur');
|
||||
await click(headerCM.getInputField());
|
||||
await blur(headerCM.getInputField());
|
||||
|
||||
expect(
|
||||
find('[data-test-error="codeinjection-head"]').text().trim(),
|
||||
find('[data-test-error="codeinjection-head"]').textContent.trim(),
|
||||
'header injection too long error'
|
||||
).to.match(/cannot be longer than 65535/);
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).codeinjectionHead,
|
||||
this.server.db.posts.find(post.id).codeinjectionHead,
|
||||
'saved header injection after validation error'
|
||||
).to.be.null;
|
||||
|
||||
// changing header injection auto-saves
|
||||
await headerCM.setValue('<script src="http://example.com/inject-head.js"></script>');
|
||||
await triggerEvent(headerCM.getInputField(), 'blur');
|
||||
await click(headerCM.getInputField());
|
||||
await blur(headerCM.getInputField());
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).codeinjectionHead,
|
||||
this.server.db.posts.find(post.id).codeinjectionHead,
|
||||
'saved header injection'
|
||||
).to.equal('<script src="http://example.com/inject-head.js"></script>');
|
||||
|
||||
// footer injection has validation
|
||||
let footerCM = find('[data-test-field="codeinjection-foot"] .CodeMirror')[0].CodeMirror;
|
||||
let footerCM = find('[data-test-field="codeinjection-foot"] .CodeMirror').CodeMirror;
|
||||
await footerCM.setValue(Array(65540).join('a'));
|
||||
await triggerEvent(footerCM.getInputField(), 'blur');
|
||||
await click(footerCM.getInputField());
|
||||
await blur(footerCM.getInputField());
|
||||
|
||||
expect(
|
||||
find('[data-test-error="codeinjection-foot"]').text().trim(),
|
||||
find('[data-test-error="codeinjection-foot"]').textContent.trim(),
|
||||
'footer injection too long error'
|
||||
).to.match(/cannot be longer than 65535/);
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).codeinjectionFoot,
|
||||
this.server.db.posts.find(post.id).codeinjectionFoot,
|
||||
'saved footer injection after validation error'
|
||||
).to.be.null;
|
||||
|
||||
// changing footer injection auto-saves
|
||||
await footerCM.setValue('<script src="http://example.com/inject-foot.js"></script>');
|
||||
await triggerEvent(footerCM.getInputField(), 'blur');
|
||||
await click(footerCM.getInputField());
|
||||
await blur(footerCM.getInputField());
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).codeinjectionFoot,
|
||||
this.server.db.posts.find(post.id).codeinjectionFoot,
|
||||
'saved footer injection'
|
||||
).to.equal('<script src="http://example.com/inject-foot.js"></script>');
|
||||
|
||||
@ -691,7 +695,7 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-button="close-psm-subview"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-field="codeinjection-head"]').length,
|
||||
findAll('[data-test-field="codeinjection-head"]').length,
|
||||
'header injection not present after closing subview'
|
||||
).to.equal(0);
|
||||
|
||||
@ -701,50 +705,54 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-button="twitter-data"]');
|
||||
|
||||
// twitter title has validation
|
||||
await click('[data-test-field="twitter-title"]');
|
||||
await fillIn('[data-test-field="twitter-title"]', Array(302).join('a'));
|
||||
await triggerEvent('[data-test-field="twitter-title"]', 'blur');
|
||||
await blur('[data-test-field="twitter-title"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="twitter-title"]').text().trim(),
|
||||
find('[data-test-error="twitter-title"]').textContent.trim(),
|
||||
'twitter title too long error'
|
||||
).to.match(/cannot be longer than 300/);
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).twitterTitle,
|
||||
this.server.db.posts.find(post.id).twitterTitle,
|
||||
'saved twitter title after validation error'
|
||||
).to.be.null;
|
||||
|
||||
// changing twitter title auto-saves
|
||||
// twitter title has validation
|
||||
await click('[data-test-field="twitter-title"]');
|
||||
await fillIn('[data-test-field="twitter-title"]', 'Test Twitter Title');
|
||||
await triggerEvent('[data-test-field="twitter-title"]', 'blur');
|
||||
await blur('[data-test-field="twitter-title"]');
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).twitterTitle,
|
||||
this.server.db.posts.find(post.id).twitterTitle,
|
||||
'saved twitter title'
|
||||
).to.equal('Test Twitter Title');
|
||||
|
||||
// twitter description has validation
|
||||
await click('[data-test-field="twitter-description"]');
|
||||
await fillIn('[data-test-field="twitter-description"]', Array(505).join('a'));
|
||||
await triggerEvent('[data-test-field="twitter-description"]', 'blur');
|
||||
await blur('[data-test-field="twitter-description"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="twitter-description"]').text().trim(),
|
||||
find('[data-test-error="twitter-description"]').textContent.trim(),
|
||||
'twitter description too long error'
|
||||
).to.match(/cannot be longer than 500/);
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).twitterDescription,
|
||||
this.server.db.posts.find(post.id).twitterDescription,
|
||||
'saved twitter description after validation error'
|
||||
).to.be.null;
|
||||
|
||||
// changing twitter description auto-saves
|
||||
// twitter description has validation
|
||||
await click('[data-test-field="twitter-description"]');
|
||||
await fillIn('[data-test-field="twitter-description"]', 'Test Twitter Description');
|
||||
await triggerEvent('[data-test-field="twitter-description"]', 'blur');
|
||||
await blur('[data-test-field="twitter-description"]');
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).twitterDescription,
|
||||
this.server.db.posts.find(post.id).twitterDescription,
|
||||
'saved twitter description'
|
||||
).to.equal('Test Twitter Description');
|
||||
|
||||
@ -752,7 +760,7 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-button="close-psm-subview"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-field="twitter-title"]').length,
|
||||
findAll('[data-test-field="twitter-title"]').length,
|
||||
'twitter title not present after closing subview'
|
||||
).to.equal(0);
|
||||
|
||||
@ -762,50 +770,54 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-button="facebook-data"]');
|
||||
|
||||
// facebook title has validation
|
||||
await click('[data-test-field="og-title"]');
|
||||
await fillIn('[data-test-field="og-title"]', Array(302).join('a'));
|
||||
await triggerEvent('[data-test-field="og-title"]', 'blur');
|
||||
await blur('[data-test-field="og-title"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="og-title"]').text().trim(),
|
||||
find('[data-test-error="og-title"]').textContent.trim(),
|
||||
'facebook title too long error'
|
||||
).to.match(/cannot be longer than 300/);
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).ogTitle,
|
||||
this.server.db.posts.find(post.id).ogTitle,
|
||||
'saved facebook title after validation error'
|
||||
).to.be.null;
|
||||
|
||||
// changing facebook title auto-saves
|
||||
// facebook title has validation
|
||||
await click('[data-test-field="og-title"]');
|
||||
await fillIn('[data-test-field="og-title"]', 'Test Facebook Title');
|
||||
await triggerEvent('[data-test-field="og-title"]', 'blur');
|
||||
await blur('[data-test-field="og-title"]');
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).ogTitle,
|
||||
this.server.db.posts.find(post.id).ogTitle,
|
||||
'saved facebook title'
|
||||
).to.equal('Test Facebook Title');
|
||||
|
||||
// facebook description has validation
|
||||
await click('[data-test-field="og-description"]');
|
||||
await fillIn('[data-test-field="og-description"]', Array(505).join('a'));
|
||||
await triggerEvent('[data-test-field="og-description"]', 'blur');
|
||||
await blur('[data-test-field="og-description"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="og-description"]').text().trim(),
|
||||
find('[data-test-error="og-description"]').textContent.trim(),
|
||||
'facebook description too long error'
|
||||
).to.match(/cannot be longer than 500/);
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).ogDescription,
|
||||
this.server.db.posts.find(post.id).ogDescription,
|
||||
'saved facebook description after validation error'
|
||||
).to.be.null;
|
||||
|
||||
// changing facebook description auto-saves
|
||||
// facebook description has validation
|
||||
await click('[data-test-field="og-description"]');
|
||||
await fillIn('[data-test-field="og-description"]', 'Test Facebook Description');
|
||||
await triggerEvent('[data-test-field="og-description"]', 'blur');
|
||||
await blur('[data-test-field="og-description"]');
|
||||
|
||||
expect(
|
||||
server.db.posts.find(post.id).ogDescription,
|
||||
this.server.db.posts.find(post.id).ogDescription,
|
||||
'saved facebook description'
|
||||
).to.equal('Test Facebook Description');
|
||||
|
||||
@ -813,7 +825,7 @@ describe('Acceptance: Editor', function () {
|
||||
await click('[data-test-button="close-psm-subview"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-field="og-title"]').length,
|
||||
findAll('[data-test-field="og-title"]').length,
|
||||
'facebook title not present after closing subview'
|
||||
).to.equal(0);
|
||||
});
|
||||
|
@ -1,9 +1,11 @@
|
||||
import Mirage from 'ember-cli-mirage';
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import startApp from '../helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {click, currentRouteName, fillIn, find, findAll, visit} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {fileUpload} from '../helpers/file-upload';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {versionMismatchResponse} from 'ghost-admin/mirage/utils';
|
||||
|
||||
let htmlErrorResponse = function () {
|
||||
@ -15,30 +17,23 @@ let htmlErrorResponse = function () {
|
||||
};
|
||||
|
||||
describe('Acceptance: Error Handling', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
describe('VersionMismatch errors', function () {
|
||||
describe('logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('displays an alert and disables navigation when saving', async function () {
|
||||
server.createList('post', 3);
|
||||
this.server.createList('post', 3);
|
||||
|
||||
// mock the post save endpoint to return version mismatch
|
||||
server.put('/posts/:id', versionMismatchResponse);
|
||||
this.server.put('/posts/:id', versionMismatchResponse);
|
||||
|
||||
await visit('/');
|
||||
await click('.posts-list li:nth-of-type(2) a'); // select second post
|
||||
@ -46,61 +41,61 @@ describe('Acceptance: Error Handling', function () {
|
||||
await click('[data-test-publishmenu-save]'); // "Save post"
|
||||
|
||||
// has the refresh to update alert
|
||||
expect(find('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').text()).to.match(/refresh/);
|
||||
expect(findAll('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').textContent).to.match(/refresh/);
|
||||
|
||||
// try navigating back to the content list
|
||||
await click('[data-test-link="stories"]');
|
||||
|
||||
expect(currentPath()).to.equal('editor.edit');
|
||||
expect(currentRouteName()).to.equal('editor.edit');
|
||||
});
|
||||
|
||||
it('displays alert and aborts the transition when navigating', async function () {
|
||||
await visit('/');
|
||||
|
||||
// mock the tags endpoint to return version mismatch
|
||||
server.get('/tags/', versionMismatchResponse);
|
||||
this.server.get('/tags/', versionMismatchResponse);
|
||||
|
||||
await click('[data-test-nav="tags"]');
|
||||
|
||||
// navigation is blocked on loading screen
|
||||
expect(currentPath()).to.equal('settings.tags_loading');
|
||||
expect(currentRouteName()).to.equal('settings.tags_loading');
|
||||
|
||||
// has the refresh to update alert
|
||||
expect(find('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').text()).to.match(/refresh/);
|
||||
expect(findAll('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').textContent).to.match(/refresh/);
|
||||
});
|
||||
|
||||
it('displays alert and aborts the transition when an ember-ajax error is thrown whilst navigating', async function () {
|
||||
server.get('/configuration/timezones/', versionMismatchResponse);
|
||||
this.server.get('/configuration/timezones/', versionMismatchResponse);
|
||||
|
||||
await visit('/settings/tags');
|
||||
await click('[data-test-nav="settings"]');
|
||||
|
||||
// navigation is blocked
|
||||
expect(currentPath()).to.equal('settings.general_loading');
|
||||
expect(currentRouteName()).to.equal('settings.general_loading');
|
||||
|
||||
// has the refresh to update alert
|
||||
expect(find('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').text()).to.match(/refresh/);
|
||||
expect(findAll('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').textContent).to.match(/refresh/);
|
||||
});
|
||||
|
||||
it('can be triggered when passed in to a component', async function () {
|
||||
server.post('/subscribers/csv/', versionMismatchResponse);
|
||||
this.server.post('/subscribers/csv/', versionMismatchResponse);
|
||||
|
||||
await visit('/subscribers');
|
||||
await click('[data-test-link="import-csv"]');
|
||||
await fileUpload('.fullscreen-modal input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
// alert is shown
|
||||
expect(find('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').text()).to.match(/refresh/);
|
||||
expect(findAll('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').textContent).to.match(/refresh/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('logged out', function () {
|
||||
it('displays alert', async function () {
|
||||
server.post('/session', versionMismatchResponse);
|
||||
this.server.post('/session', versionMismatchResponse);
|
||||
|
||||
await visit('/signin');
|
||||
await fillIn('[name="identification"]', 'test@example.com');
|
||||
@ -108,49 +103,45 @@ describe('Acceptance: Error Handling', function () {
|
||||
await click('.gh-btn-blue');
|
||||
|
||||
// has the refresh to update alert
|
||||
expect(find('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').text()).to.match(/refresh/);
|
||||
expect(findAll('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').textContent).to.match(/refresh/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('CloudFlare errors', function () {
|
||||
beforeEach(function () {
|
||||
let [role] = server.db.roles.where({name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
this.server.loadFixtures();
|
||||
|
||||
server.loadFixtures();
|
||||
let roles = this.server.schema.roles.where({name: 'Administrator'});
|
||||
this.server.create('user', {roles});
|
||||
|
||||
authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('handles Ember Data HTML response', async function () {
|
||||
server.put('/posts/1/', htmlErrorResponse);
|
||||
server.create('post');
|
||||
this.server.put('/posts/1/', htmlErrorResponse);
|
||||
this.server.create('post');
|
||||
|
||||
await visit('/editor/1');
|
||||
await click('[data-test-publishmenu-trigger]');
|
||||
await click('[data-test-publishmenu-save]');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').text()).to.not.match(/html>/);
|
||||
expect(find('.gh-alert').text()).to.match(/Request was rejected due to server error/);
|
||||
});
|
||||
expect(findAll('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').textContent).to.not.match(/html>/);
|
||||
expect(find('.gh-alert').textContent).to.match(/Request was rejected due to server error/);
|
||||
});
|
||||
|
||||
it('handles ember-ajax HTML response', async function () {
|
||||
server.del('/themes/foo/', htmlErrorResponse);
|
||||
this.server.del('/themes/foo/', htmlErrorResponse);
|
||||
|
||||
await visit('/settings/design');
|
||||
await click('[data-test-theme-id="foo"] [data-test-theme-delete-button]');
|
||||
await click('.fullscreen-modal [data-test-delete-button]');
|
||||
|
||||
andThen(() => {
|
||||
expect(find('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').text()).to.not.match(/html>/);
|
||||
expect(find('.gh-alert').text()).to.match(/Request was rejected due to server error/);
|
||||
});
|
||||
expect(findAll('.gh-alert').length).to.equal(1);
|
||||
expect(find('.gh-alert').textContent).to.not.match(/html>/);
|
||||
expect(find('.gh-alert').textContent).to.match(/Request was rejected due to server error/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,27 +1,23 @@
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import startApp from '../helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {click, fillIn, find, findAll, visit} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
|
||||
describe('Acceptance: Password Reset', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
describe('request reset', function () {
|
||||
it('is successful with valid data', async function () {
|
||||
await invalidateSession();
|
||||
await visit('/signin');
|
||||
await fillIn('input[name="identification"]', 'test@example.com');
|
||||
await click('.forgotten-link');
|
||||
|
||||
// an alert with instructions is displayed
|
||||
expect(find('.gh-alert-blue').length, 'alert count')
|
||||
expect(findAll('.gh-alert-blue').length, 'alert count')
|
||||
.to.equal(1);
|
||||
});
|
||||
|
||||
@ -33,18 +29,18 @@ describe('Acceptance: Password Reset', function () {
|
||||
|
||||
// email field is invalid
|
||||
expect(
|
||||
find('input[name="identification"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="identification"]').closest('.form-group'),
|
||||
'email field has error class (no email)'
|
||||
).to.be.true;
|
||||
).to.match('.error');
|
||||
|
||||
// password field is valid
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="password"]').closest('.form-group'),
|
||||
'password field has error class (no email)'
|
||||
).to.be.false;
|
||||
).to.not.match('.error');
|
||||
|
||||
// error message shown
|
||||
expect(find('p.main-error').text().trim(), 'error message')
|
||||
expect(find('p.main-error').textContent.trim(), 'error message')
|
||||
.to.equal('We need your email address to reset your password!');
|
||||
|
||||
// invalid email provided
|
||||
@ -53,18 +49,18 @@ describe('Acceptance: Password Reset', function () {
|
||||
|
||||
// email field is invalid
|
||||
expect(
|
||||
find('input[name="identification"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="identification"]').closest('.form-group'),
|
||||
'email field has error class (invalid email)'
|
||||
).to.be.true;
|
||||
).to.match('.error');
|
||||
|
||||
// password field is valid
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="password"]').closest('.form-group'),
|
||||
'password field has error class (invalid email)'
|
||||
).to.be.false;
|
||||
).to.not.match('.error');
|
||||
|
||||
// error message
|
||||
expect(find('p.main-error').text().trim(), 'error message')
|
||||
expect(find('p.main-error').textContent.trim(), 'error message')
|
||||
.to.equal('We need your email address to reset your password!');
|
||||
|
||||
// unknown email provided
|
||||
@ -73,18 +69,18 @@ describe('Acceptance: Password Reset', function () {
|
||||
|
||||
// email field is invalid
|
||||
expect(
|
||||
find('input[name="identification"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="identification"]').closest('.form-group'),
|
||||
'email field has error class (unknown email)'
|
||||
).to.be.true;
|
||||
).to.match('.error');
|
||||
|
||||
// password field is valid
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="password"]').closest('.form-group'),
|
||||
'password field has error class (unknown email)'
|
||||
).to.be.false;
|
||||
).to.not.match('.error');
|
||||
|
||||
// error message
|
||||
expect(find('p.main-error').text().trim(), 'error message')
|
||||
expect(find('p.main-error').textContent.trim(), 'error message')
|
||||
.to.equal('There is no user with that email address.');
|
||||
});
|
||||
});
|
||||
|
@ -1,69 +1,63 @@
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
it
|
||||
} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {click, currentURL, find, findAll, triggerEvent} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
describe('Acceptance: Settings - Integrations - AMP', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/integrations/amp');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/amp');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/amp');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/amp');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
});
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('it enables or disables AMP properly and saves it', async function () {
|
||||
@ -73,15 +67,15 @@ describe('Acceptance: Settings - Integrations - AMP', function () {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/amp');
|
||||
|
||||
// AMP is enabled by default
|
||||
expect(find('[data-test-amp-checkbox]').prop('checked'), 'AMP checkbox').to.be.true;
|
||||
expect(find('[data-test-amp-checkbox]').checked, 'AMP checkbox').to.be.true;
|
||||
|
||||
await click('[data-test-amp-checkbox]');
|
||||
|
||||
expect(find('[data-test-amp-checkbox]').prop('checked'), 'AMP checkbox').to.be.false;
|
||||
expect(find('[data-test-amp-checkbox]').checked, 'AMP checkbox').to.be.false;
|
||||
|
||||
await click('[data-test-save-button]');
|
||||
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
let params = JSON.parse(lastRequest.requestBody);
|
||||
|
||||
expect(params.settings.findBy('key', 'amp').value).to.equal(false);
|
||||
@ -96,10 +90,10 @@ describe('Acceptance: Settings - Integrations - AMP', function () {
|
||||
|
||||
// we've already saved in this test so there's no on-screen indication
|
||||
// that we've had another save, check the request was fired instead
|
||||
let [newRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [newRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
params = JSON.parse(newRequest.requestBody);
|
||||
|
||||
expect(find('[data-test-amp-checkbox]').prop('checked'), 'AMP checkbox').to.be.true;
|
||||
expect(find('[data-test-amp-checkbox]').checked, 'AMP checkbox').to.be.true;
|
||||
expect(params.settings.findBy('key', 'amp').value).to.equal(true);
|
||||
});
|
||||
|
||||
@ -110,27 +104,27 @@ describe('Acceptance: Settings - Integrations - AMP', function () {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/amp');
|
||||
|
||||
// AMP is enabled by default
|
||||
expect(find('[data-test-amp-checkbox]').prop('checked'), 'AMP checkbox').to.be.true;
|
||||
expect(find('[data-test-amp-checkbox]').checked, 'AMP checkbox default').to.be.true;
|
||||
|
||||
await click('[data-test-amp-checkbox]');
|
||||
|
||||
expect(find('[data-test-amp-checkbox]').prop('checked'), 'AMP checkbox').to.be.false;
|
||||
expect(find('[data-test-amp-checkbox]').checked, 'AMP checkbox after click').to.be.false;
|
||||
|
||||
await visit('/team');
|
||||
|
||||
expect(find('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
expect(findAll('.fullscreen-modal').length, 'unsaved changes modal exists').to.equal(1);
|
||||
|
||||
// Leave without saving
|
||||
await (click('.fullscreen-modal [data-test-leave-button]'), 'leave without saving');
|
||||
await click('.fullscreen-modal [data-test-leave-button]');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
expect(currentURL(), 'currentURL after leave without saving').to.equal('/team');
|
||||
|
||||
await visit('/settings/integrations/amp');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/amp');
|
||||
expect(currentURL(), 'currentURL after return').to.equal('/settings/integrations/amp');
|
||||
|
||||
// settings were not saved
|
||||
expect(find('[data-test-amp-checkbox]').prop('checked'), 'AMP checkbox').to.be.true;
|
||||
expect(find('[data-test-amp-checkbox]').checked, 'AMP checkbox').to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,70 +1,63 @@
|
||||
import $ from 'jquery';
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
it
|
||||
} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {click, currentURL, find, findAll, triggerEvent} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
describe('Acceptance: Settings - Code-Injection', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/code-injection');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/code-injection');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/code-injection');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/code-injection');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
});
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('it renders, loads and saves editors correctly', async function () {
|
||||
@ -77,24 +70,24 @@ describe('Acceptance: Settings - Code-Injection', function () {
|
||||
expect(document.title, 'page title').to.equal('Settings - Code injection - Test Blog');
|
||||
|
||||
// highlights nav menu
|
||||
expect($('[data-test-nav="code-injection"]').hasClass('active'), 'highlights nav menu item')
|
||||
.to.be.true;
|
||||
expect(find('[data-test-nav="code-injection"]'), 'highlights nav menu item')
|
||||
.to.have.class('active');
|
||||
|
||||
expect(find('[data-test-save-button]').text().trim(), 'save button text').to.equal('Save');
|
||||
expect(find('[data-test-save-button]').textContent.trim(), 'save button text').to.equal('Save');
|
||||
|
||||
expect(find('#ghost-head .CodeMirror').length, 'ghost head codemirror element').to.equal(1);
|
||||
expect($('#ghost-head .CodeMirror').hasClass('cm-s-xq-light'), 'ghost head editor theme').to.be.true;
|
||||
expect(findAll('#ghost-head .CodeMirror').length, 'ghost head codemirror element').to.equal(1);
|
||||
expect(find('#ghost-head .CodeMirror'), 'ghost head editor theme').to.have.class('cm-s-xq-light');
|
||||
|
||||
expect(find('#ghost-foot .CodeMirror').length, 'ghost head codemirror element').to.equal(1);
|
||||
expect($('#ghost-foot .CodeMirror').hasClass('cm-s-xq-light'), 'ghost head editor theme').to.be.true;
|
||||
expect(findAll('#ghost-foot .CodeMirror').length, 'ghost head codemirror element').to.equal(1);
|
||||
expect(find('#ghost-foot .CodeMirror'), 'ghost head editor theme').to.have.class('cm-s-xq-light');
|
||||
|
||||
await click('[data-test-save-button]');
|
||||
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
let params = JSON.parse(lastRequest.requestBody);
|
||||
|
||||
expect(params.settings.findBy('key', 'ghost_head').value).to.equal('');
|
||||
expect(find('[data-test-save-button]').text().trim(), 'save button text').to.equal('Saved');
|
||||
expect(find('[data-test-save-button]').textContent.trim(), 'save button text').to.equal('Saved');
|
||||
|
||||
// CMD-S shortcut works
|
||||
await triggerEvent('.gh-app', 'keydown', {
|
||||
@ -104,11 +97,11 @@ describe('Acceptance: Settings - Code-Injection', function () {
|
||||
});
|
||||
// we've already saved in this test so there's no on-screen indication
|
||||
// that we've had another save, check the request was fired instead
|
||||
let [newRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [newRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
params = JSON.parse(newRequest.requestBody);
|
||||
|
||||
expect(params.settings.findBy('key', 'ghost_head').value).to.equal('');
|
||||
expect(find('[data-test-save-button]').text().trim(), 'save button text').to.equal('Saved');
|
||||
expect(find('[data-test-save-button]').textContent.trim(), 'save button text').to.equal('Saved');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,141 +1,136 @@
|
||||
/* eslint-disable camelcase */
|
||||
import Mirage from 'ember-cli-mirage';
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import mockThemes from 'ghost-admin/mirage/config/themes';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {blur, click, currentRouteName, currentURL, fillIn, find, findAll, triggerEvent, typeIn} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {fileUpload} from '../../helpers/file-upload';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
// simulate jQuery's `:visible` pseudo-selector
|
||||
function withText(elements) {
|
||||
return Array.from(elements).filter(elem => elem.textContent.trim() !== '');
|
||||
}
|
||||
|
||||
describe('Acceptance: Settings - Design', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/design');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/design');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/design');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
});
|
||||
|
||||
it('can visit /settings/design', async function () {
|
||||
await visit('/settings/design');
|
||||
|
||||
expect(currentPath()).to.equal('settings.design.index');
|
||||
expect(find('[data-test-save-button]').text().trim(), 'save button text').to.equal('Save');
|
||||
expect(currentRouteName()).to.equal('settings.design.index');
|
||||
expect(find('[data-test-save-button]').textContent.trim(), 'save button text').to.equal('Save');
|
||||
|
||||
// fixtures contain two nav items, check for three rows as we
|
||||
// should have one extra that's blank
|
||||
expect(
|
||||
find('.gh-blognav-item').length,
|
||||
findAll('[data-test-navitem]').length,
|
||||
'navigation items count'
|
||||
).to.equal(3);
|
||||
});
|
||||
|
||||
it('saves navigation settings', async function () {
|
||||
await visit('/settings/design');
|
||||
await fillIn('.gh-blognav-label:first input', 'Test');
|
||||
await fillIn('.gh-blognav-url:first input', '/test');
|
||||
await triggerEvent('.gh-blognav-url:first input', 'blur');
|
||||
|
||||
await fillIn('[data-test-navitem="0"] [data-test-input="label"]', 'Test');
|
||||
await typeIn('[data-test-navitem="0"] [data-test-input="url"]', '/test');
|
||||
await click('[data-test-save-button]');
|
||||
|
||||
let [navSetting] = server.db.settings.where({key: 'navigation'});
|
||||
let [navSetting] = this.server.db.settings.where({key: 'navigation'});
|
||||
|
||||
expect(navSetting.value).to.equal('[{"label":"Test","url":"/test/"},{"label":"About","url":"/about"}]');
|
||||
|
||||
// don't test against .error directly as it will pick up failed
|
||||
// tests "pre.error" elements
|
||||
expect(find('span.error').length, 'error fields count').to.equal(0);
|
||||
expect(find('.gh-alert').length, 'alerts count').to.equal(0);
|
||||
expect(find('.response:visible').length, 'validation errors count')
|
||||
expect(findAll('span.error').length, 'error messages count').to.equal(0);
|
||||
expect(findAll('.gh-alert').length, 'alerts count').to.equal(0);
|
||||
expect(withText(findAll('[data-test-error]')).length, 'validation errors count')
|
||||
.to.equal(0);
|
||||
});
|
||||
|
||||
it('validates new item correctly on save', async function () {
|
||||
await visit('/settings/design');
|
||||
|
||||
await click('[data-test-save-button]');
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-item').length,
|
||||
findAll('[data-test-navitem]').length,
|
||||
'number of nav items after saving with blank new item'
|
||||
).to.equal(3);
|
||||
|
||||
await fillIn('.gh-blognav-label:last input', 'Test');
|
||||
await fillIn('.gh-blognav-url:last input', 'http://invalid domain/');
|
||||
await triggerEvent('.gh-blognav-url:last input', 'blur');
|
||||
await fillIn('[data-test-navitem="new"] [data-test-input="label"]', 'Test');
|
||||
await fillIn('[data-test-navitem="new"] [data-test-input="url"]', '');
|
||||
await typeIn('[data-test-navitem="new"] [data-test-input="url"]', 'http://invalid domain/');
|
||||
|
||||
await click('[data-test-save-button]');
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-item').length,
|
||||
findAll('[data-test-navitem]').length,
|
||||
'number of nav items after saving with invalid new item'
|
||||
).to.equal(3);
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-item:last .error').length,
|
||||
withText(findAll('[data-test-navitem="new"] [data-test-error]')).length,
|
||||
'number of invalid fields in new item'
|
||||
).to.equal(1);
|
||||
});
|
||||
|
||||
it('clears unsaved settings when navigating away but warns with a confirmation dialog', async function () {
|
||||
await visit('/settings/design');
|
||||
await fillIn('.gh-blognav-label:first input', 'Test');
|
||||
await triggerEvent('.gh-blognav-label:first input', 'blur');
|
||||
await fillIn('[data-test-navitem="0"] [data-test-input="label"]', 'Test');
|
||||
await blur('[data-test-navitem="0"] [data-test-input="label"]');
|
||||
|
||||
expect(find('.gh-blognav-label:first input').val()).to.equal('Test');
|
||||
// this.timeout(0);
|
||||
// return pauseTest();
|
||||
expect(find('[data-test-navitem="0"] [data-test-input="label"]').value).to.equal('Test');
|
||||
|
||||
await visit('/settings/code-injection');
|
||||
|
||||
expect(find('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
expect(findAll('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
|
||||
// Leave without saving
|
||||
await (click('.fullscreen-modal [data-test-leave-button]'), 'leave without saving');
|
||||
await click('.fullscreen-modal [data-test-leave-button]'), 'leave without saving';
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/code-injection');
|
||||
|
||||
await visit('/settings/design');
|
||||
|
||||
expect(find('.gh-blognav-label:first input').val()).to.equal('Home');
|
||||
expect(find('[data-test-navitem="0"] [data-test-input="label"]').value).to.equal('Home');
|
||||
});
|
||||
|
||||
it('can add and remove items', async function () {
|
||||
@ -143,57 +138,57 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await click('.gh-blognav-add');
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-label:last .response').is(':visible'),
|
||||
find('[data-test-navitem="new"] [data-test-error="label"]').textContent.trim(),
|
||||
'blank label has validation error'
|
||||
).to.be.true;
|
||||
).to.not.be.empty;
|
||||
|
||||
await fillIn('.gh-blognav-label:last input', 'New');
|
||||
await triggerEvent('.gh-blognav-label:last input', 'keypress', {});
|
||||
await fillIn('[data-test-navitem="new"] [data-test-input="label"]', '');
|
||||
await typeIn('[data-test-navitem="new"] [data-test-input="label"]', 'New');
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-label:last .response').is(':visible'),
|
||||
find('[data-test-navitem="new"] [data-test-error="label"]').textContent.trim(),
|
||||
'label validation is visible after typing'
|
||||
).to.be.false;
|
||||
).to.be.empty;
|
||||
|
||||
await fillIn('.gh-blognav-url:last input', '/new');
|
||||
await triggerEvent('.gh-blognav-url:last input', 'keypress', {});
|
||||
await triggerEvent('.gh-blognav-url:last input', 'blur');
|
||||
await fillIn('[data-test-navitem="new"] [data-test-input="url"]', '');
|
||||
await typeIn('[data-test-navitem="new"] [data-test-input="url"]', '/new');
|
||||
await blur('[data-test-navitem="new"] [data-test-input="url"]');
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-url:last .response').is(':visible'),
|
||||
find('[data-test-navitem="new"] [data-test-error="url"]').textContent.trim(),
|
||||
'url validation is visible after typing'
|
||||
).to.be.false;
|
||||
).to.be.empty;
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-url:last input').val()
|
||||
).to.equal(`${window.location.origin}/new`);
|
||||
find('[data-test-navitem="new"] [data-test-input="url"]').value
|
||||
).to.equal(`${window.location.origin}/new/`);
|
||||
|
||||
await click('.gh-blognav-add');
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-item').length,
|
||||
findAll('[data-test-navitem]').length,
|
||||
'number of nav items after successful add'
|
||||
).to.equal(4);
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-label:last input').val(),
|
||||
find('[data-test-navitem="new"] [data-test-input="label"]').value,
|
||||
'new item label value after successful add'
|
||||
).to.be.empty;
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-url:last input').val(),
|
||||
find('[data-test-navitem="new"] [data-test-input="url"]').value,
|
||||
'new item url value after successful add'
|
||||
).to.equal(`${window.location.origin}/`);
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-item .response:visible').length,
|
||||
withText(findAll('[data-test-navitem] [data-test-error]')).length,
|
||||
'number or validation errors shown after successful add'
|
||||
).to.equal(0);
|
||||
|
||||
await click('.gh-blognav-item:first .gh-blognav-delete');
|
||||
await click('[data-test-navitem="0"] .gh-blognav-delete');
|
||||
|
||||
expect(
|
||||
find('.gh-blognav-item').length,
|
||||
findAll('[data-test-navitem]').length,
|
||||
'number of nav items after successful remove'
|
||||
).to.equal(3);
|
||||
|
||||
@ -204,7 +199,7 @@ describe('Acceptance: Settings - Design', function () {
|
||||
ctrlKey: ctrlOrCmd === 'ctrl'
|
||||
});
|
||||
|
||||
let [navSetting] = server.db.settings.where({key: 'navigation'});
|
||||
let [navSetting] = this.server.db.settings.where({key: 'navigation'});
|
||||
|
||||
expect(navSetting.value).to.equal('[{"label":"About","url":"/about"},{"label":"New","url":"/new/"}]');
|
||||
});
|
||||
@ -228,39 +223,40 @@ describe('Acceptance: Settings - Design', function () {
|
||||
// - displays modal
|
||||
// - deletes theme and refreshes list
|
||||
|
||||
server.loadFixtures('themes');
|
||||
this.server.loadFixtures('themes');
|
||||
await visit('/settings/design');
|
||||
|
||||
// lists available themes (themes are specified in mirage/fixtures/settings)
|
||||
expect(
|
||||
find('[data-test-theme-id]').length,
|
||||
findAll('[data-test-theme-id]').length,
|
||||
'shows correct number of themes'
|
||||
).to.equal(3);
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-active="true"] [data-test-theme-title]').text().trim(),
|
||||
find('[data-test-theme-active="true"] [data-test-theme-title]').textContent.trim(),
|
||||
'Blog theme marked as active'
|
||||
).to.equal('Blog (default)');
|
||||
|
||||
// theme upload displays modal
|
||||
await click('[data-test-upload-theme-button]');
|
||||
expect(
|
||||
find('[data-test-modal="upload-theme"]').length,
|
||||
findAll('[data-test-modal="upload-theme"]').length,
|
||||
'theme upload modal displayed after button click'
|
||||
).to.equal(1);
|
||||
|
||||
// cancelling theme upload closes modal
|
||||
await click('.fullscreen-modal [data-test-close-button]');
|
||||
expect(
|
||||
find('.fullscreen-modal').length === 0,
|
||||
findAll('.fullscreen-modal').length === 0,
|
||||
'upload theme modal is closed when cancelling'
|
||||
).to.be.true;
|
||||
|
||||
// theme upload validates mime type
|
||||
await click('[data-test-upload-theme-button]');
|
||||
await fileUpload('.fullscreen-modal input[type="file"]', ['test'], {type: 'text/csv'});
|
||||
|
||||
expect(
|
||||
find('.fullscreen-modal .failed').text(),
|
||||
find('.fullscreen-modal .failed').textContent,
|
||||
'validation error is shown for invalid mime type'
|
||||
).to.match(/is not supported/);
|
||||
|
||||
@ -268,12 +264,12 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await click('[data-test-upload-try-again-button]');
|
||||
await fileUpload('.fullscreen-modal input[type="file"]', ['test'], {name: 'casper.zip', type: 'application/zip'});
|
||||
expect(
|
||||
find('.fullscreen-modal .failed').text(),
|
||||
find('.fullscreen-modal .failed').textContent,
|
||||
'validation error is shown when uploading casper.zip'
|
||||
).to.match(/default Casper theme cannot be overwritten/);
|
||||
|
||||
// theme upload handles upload errors
|
||||
server.post('/themes/upload/', function () {
|
||||
this.server.post('/themes/upload/', function () {
|
||||
return new Mirage.Response(422, {}, {
|
||||
errors: [{
|
||||
message: 'Invalid theme'
|
||||
@ -282,16 +278,17 @@ describe('Acceptance: Settings - Design', function () {
|
||||
});
|
||||
await click('[data-test-upload-try-again-button]');
|
||||
await fileUpload('.fullscreen-modal input[type="file"]', ['test'], {name: 'error.zip', type: 'application/zip'});
|
||||
|
||||
expect(
|
||||
find('.fullscreen-modal .failed').text().trim(),
|
||||
find('.fullscreen-modal .failed').textContent.trim(),
|
||||
'validation error is passed through from server'
|
||||
).to.equal('Invalid theme');
|
||||
|
||||
// reset to default mirage handlers
|
||||
mockThemes(server);
|
||||
mockThemes(this.server);
|
||||
|
||||
// theme upload handles validation errors
|
||||
server.post('/themes/upload/', function () {
|
||||
this.server.post('/themes/upload/', function () {
|
||||
return new Mirage.Response(422, {}, {
|
||||
errors: [
|
||||
{
|
||||
@ -332,48 +329,48 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await fileUpload('.fullscreen-modal input[type="file"]', ['test'], {name: 'bad-theme.zip', type: 'application/zip'});
|
||||
|
||||
expect(
|
||||
find('.fullscreen-modal h1').text().trim(),
|
||||
find('.fullscreen-modal h1').textContent.trim(),
|
||||
'modal title after uploading invalid theme'
|
||||
).to.equal('Invalid theme');
|
||||
|
||||
expect(
|
||||
find('.theme-validation-rule-text').text(),
|
||||
findAll('.theme-validation-rule-text')[1].textContent,
|
||||
'top-level errors are displayed'
|
||||
).to.match(/Templates must contain valid Handlebars/);
|
||||
|
||||
await click('[data-test-toggle-details]');
|
||||
|
||||
expect(
|
||||
find('.theme-validation-details').text(),
|
||||
find('.theme-validation-details').textContent,
|
||||
'top-level errors do not escape HTML'
|
||||
).to.match(/The listed files should be included using the {{asset}} helper/);
|
||||
|
||||
expect(
|
||||
find('.theme-validation-list ul li').text(),
|
||||
find('.theme-validation-list ul li').textContent,
|
||||
'individual failures are displayed'
|
||||
).to.match(/\/assets\/javascripts\/ui\.js/);
|
||||
|
||||
// reset to default mirage handlers
|
||||
mockThemes(server);
|
||||
mockThemes(this.server);
|
||||
|
||||
await click('.fullscreen-modal [data-test-try-again-button]');
|
||||
expect(
|
||||
find('.theme-validation-errors').length,
|
||||
findAll('.theme-validation-errors').length,
|
||||
'"Try Again" resets form after theme validation error'
|
||||
).to.equal(0);
|
||||
|
||||
expect(
|
||||
find('.gh-image-uploader').length,
|
||||
findAll('.gh-image-uploader').length,
|
||||
'"Try Again" resets form after theme validation error'
|
||||
).to.equal(1);
|
||||
|
||||
expect(
|
||||
find('.fullscreen-modal h1').text().trim(),
|
||||
find('.fullscreen-modal h1').textContent.trim(),
|
||||
'"Try Again" resets form after theme validation error'
|
||||
).to.equal('Upload a theme');
|
||||
|
||||
// theme upload handles validation warnings
|
||||
server.post('/themes/upload/', function ({themes}) {
|
||||
this.server.post('/themes/upload/', function ({themes}) {
|
||||
let theme = {
|
||||
name: 'blackpalm',
|
||||
package: {
|
||||
@ -413,24 +410,24 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await fileUpload('.fullscreen-modal input[type="file"]', ['test'], {name: 'warning-theme.zip', type: 'application/zip'});
|
||||
|
||||
expect(
|
||||
find('.fullscreen-modal h1').text().trim(),
|
||||
find('.fullscreen-modal h1').textContent.trim(),
|
||||
'modal title after uploading theme with warnings'
|
||||
).to.equal('Upload successful with warnings');
|
||||
|
||||
await click('[data-test-toggle-details]');
|
||||
|
||||
expect(
|
||||
find('.theme-validation-details').text(),
|
||||
find('.theme-validation-details').textContent,
|
||||
'top-level warnings are displayed'
|
||||
).to.match(/The listed files should be included using the {{asset}} helper/);
|
||||
|
||||
expect(
|
||||
find('.theme-validation-list ul li').text(),
|
||||
find('.theme-validation-list ul li').textContent,
|
||||
'individual warning failures are displayed'
|
||||
).to.match(/\/assets\/dist\/img\/apple-touch-icon\.png/);
|
||||
|
||||
// reset to default mirage handlers
|
||||
mockThemes(server);
|
||||
mockThemes(this.server);
|
||||
|
||||
await click('.fullscreen-modal [data-test-close-button]');
|
||||
|
||||
@ -439,22 +436,22 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await fileUpload('.fullscreen-modal input[type="file"]', ['test'], {name: 'theme-1.zip', type: 'application/zip'});
|
||||
|
||||
expect(
|
||||
find('.fullscreen-modal h1').text().trim(),
|
||||
find('.fullscreen-modal h1').textContent.trim(),
|
||||
'modal header after successful upload'
|
||||
).to.equal('Upload successful!');
|
||||
|
||||
expect(
|
||||
find('.modal-body').text(),
|
||||
find('.modal-body').textContent,
|
||||
'modal displays theme name after successful upload'
|
||||
).to.match(/"Test 1 - 0\.1" uploaded successfully/);
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-id]').length,
|
||||
findAll('[data-test-theme-id]').length,
|
||||
'number of themes in list grows after upload'
|
||||
).to.equal(5);
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-active="true"] [data-test-theme-title]').text().trim(),
|
||||
find('[data-test-theme-active="true"] [data-test-theme-title]').textContent.trim(),
|
||||
'newly uploaded theme is not active'
|
||||
).to.equal('Blog (default)');
|
||||
|
||||
@ -466,12 +463,12 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await click('.fullscreen-modal [data-test-activate-now-button]');
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-id]').length,
|
||||
findAll('[data-test-theme-id]').length,
|
||||
'number of themes in list grows after upload and activate'
|
||||
).to.equal(6);
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-active="true"] [data-test-theme-title]').text().trim(),
|
||||
find('[data-test-theme-active="true"] [data-test-theme-title]').textContent.trim(),
|
||||
'newly uploaded+activated theme is active'
|
||||
).to.equal('Test 2');
|
||||
|
||||
@ -479,17 +476,17 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await click('[data-test-theme-id="casper"] [data-test-theme-activate-button]');
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-id="test-2"] .apps-card-app').hasClass('theme-list-item--active'),
|
||||
find('[data-test-theme-id="test-2"] .apps-card-app').classList.contains('theme-list-item--active'),
|
||||
'previously active theme is not active'
|
||||
).to.be.false;
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-id="casper"] .apps-card-app').hasClass('theme-list-item--active'),
|
||||
find('[data-test-theme-id="casper"] .apps-card-app').classList.contains('theme-list-item--active'),
|
||||
'activated theme is active'
|
||||
).to.be.true;
|
||||
|
||||
// theme activation shows errors
|
||||
server.put('themes/:theme/activate', function () {
|
||||
this.server.put('themes/:theme/activate', function () {
|
||||
return new Mirage.Response(422, {}, {
|
||||
errors: [
|
||||
{
|
||||
@ -531,35 +528,35 @@ describe('Acceptance: Settings - Design', function () {
|
||||
expect(find('[data-test-theme-warnings-modal]')).to.exist;
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-warnings-title]').text().trim(),
|
||||
find('[data-test-theme-warnings-title]').textContent.trim(),
|
||||
'modal title after activating invalid theme'
|
||||
).to.equal('Activation failed');
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-warnings]').text(),
|
||||
find('[data-test-theme-warnings]').textContent,
|
||||
'top-level errors are displayed in activation errors'
|
||||
).to.match(/Templates must contain valid Handlebars/);
|
||||
|
||||
await click('[data-test-toggle-details]');
|
||||
|
||||
expect(
|
||||
find('.theme-validation-details').text(),
|
||||
find('.theme-validation-details').textContent,
|
||||
'top-level errors do not escape HTML in activation errors'
|
||||
).to.match(/The listed files should be included using the {{asset}} helper/);
|
||||
|
||||
expect(
|
||||
find('.theme-validation-list ul li').text(),
|
||||
find('.theme-validation-list ul li').textContent,
|
||||
'individual failures are displayed in activation errors'
|
||||
).to.match(/\/assets\/javascripts\/ui\.js/);
|
||||
|
||||
// restore default mirage handlers
|
||||
mockThemes(server);
|
||||
mockThemes(this.server);
|
||||
|
||||
await click('[data-test-modal-close-button]');
|
||||
expect(find('[data-test-theme-warnings-modal]')).to.not.exist;
|
||||
|
||||
// theme activation shows warnings
|
||||
server.put('themes/:theme/activate', function ({themes}, {params}) {
|
||||
this.server.put('themes/:theme/activate', function ({themes}, {params}) {
|
||||
themes.all().update('active', false);
|
||||
let theme = themes.findBy({name: params.theme}).update({active: true});
|
||||
|
||||
@ -592,24 +589,24 @@ describe('Acceptance: Settings - Design', function () {
|
||||
expect(find('[data-test-theme-warnings-modal]')).to.exist;
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-warnings-title]').text().trim(),
|
||||
find('[data-test-theme-warnings-title]').textContent.trim(),
|
||||
'modal title after activating theme with warnings'
|
||||
).to.equal('Activation successful with warnings');
|
||||
|
||||
await click('[data-test-toggle-details]');
|
||||
|
||||
expect(
|
||||
find('.theme-validation-details').text(),
|
||||
find('.theme-validation-details').textContent,
|
||||
'top-level warnings are displayed in activation warnings'
|
||||
).to.match(/The listed files should be included using the {{asset}} helper/);
|
||||
|
||||
expect(
|
||||
find('.theme-validation-list ul li').text(),
|
||||
find('.theme-validation-list ul li').textContent,
|
||||
'individual warning failures are displayed in activation warnings'
|
||||
).to.match(/\/assets\/dist\/img\/apple-touch-icon\.png/);
|
||||
|
||||
// restore default mirage handlers
|
||||
mockThemes(server);
|
||||
mockThemes(this.server);
|
||||
|
||||
await click('[data-test-modal-close-button]');
|
||||
// reactivate casper to continue tests
|
||||
@ -618,14 +615,14 @@ describe('Acceptance: Settings - Design', function () {
|
||||
// theme deletion displays modal
|
||||
await click('[data-test-theme-id="test-1"] [data-test-theme-delete-button]');
|
||||
expect(
|
||||
find('[data-test-delete-theme-modal]').length,
|
||||
findAll('[data-test-delete-theme-modal]').length,
|
||||
'theme deletion modal displayed after button click'
|
||||
).to.equal(1);
|
||||
|
||||
// cancelling theme deletion closes modal
|
||||
await click('.fullscreen-modal [data-test-cancel-button]');
|
||||
expect(
|
||||
find('.fullscreen-modal').length === 0,
|
||||
findAll('.fullscreen-modal').length === 0,
|
||||
'delete theme modal is closed when cancelling'
|
||||
).to.be.true;
|
||||
|
||||
@ -633,22 +630,22 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await click('[data-test-theme-id="test-1"] [data-test-theme-delete-button]');
|
||||
await click('.fullscreen-modal [data-test-delete-button]');
|
||||
expect(
|
||||
find('.fullscreen-modal').length === 0,
|
||||
findAll('.fullscreen-modal').length === 0,
|
||||
'delete theme modal closes after deletion'
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-id]').length,
|
||||
findAll('[data-test-theme-id]').length,
|
||||
'number of themes in list shrinks after delete'
|
||||
).to.equal(5);
|
||||
|
||||
expect(
|
||||
find('[data-test-theme-title]').text(),
|
||||
find('[data-test-theme-title]').textContent,
|
||||
'correct theme is removed from theme list after deletion'
|
||||
).to.not.match(/Test 1/);
|
||||
|
||||
// validation errors are handled when deleting a theme
|
||||
server.del('/themes/:theme/', function () {
|
||||
this.server.del('/themes/:theme/', function () {
|
||||
return new Mirage.Response(422, {}, {
|
||||
errors: [{
|
||||
message: 'Can\'t delete theme'
|
||||
@ -660,29 +657,29 @@ describe('Acceptance: Settings - Design', function () {
|
||||
await click('.fullscreen-modal [data-test-delete-button]');
|
||||
|
||||
expect(
|
||||
find('.fullscreen-modal').length === 0,
|
||||
findAll('.fullscreen-modal').length === 0,
|
||||
'delete theme modal closes after failed deletion'
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
find('.gh-alert').length,
|
||||
findAll('.gh-alert').length,
|
||||
'alert is shown when deletion fails'
|
||||
).to.equal(1);
|
||||
|
||||
expect(
|
||||
find('.gh-alert').text(),
|
||||
find('.gh-alert').textContent,
|
||||
'failed deletion alert has correct text'
|
||||
).to.match(/Can't delete theme/);
|
||||
|
||||
// restore default mirage handlers
|
||||
mockThemes(server);
|
||||
mockThemes(this.server);
|
||||
});
|
||||
|
||||
it('can delete then re-upload the same theme', async function () {
|
||||
server.loadFixtures('themes');
|
||||
this.server.loadFixtures('themes');
|
||||
|
||||
// mock theme upload to emulate uploading theme with same id
|
||||
server.post('/themes/upload/', function ({themes}) {
|
||||
this.server.post('/themes/upload/', function ({themes}) {
|
||||
let theme = themes.create({
|
||||
name: 'foo',
|
||||
package: {
|
||||
|
@ -1,68 +1,63 @@
|
||||
import $ from 'jquery';
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import mockUploads from '../../../mirage/config/uploads';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {blur, click, currentURL, fillIn, find, findAll, focus, triggerEvent} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {fileUpload} from '../../helpers/file-upload';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
describe('Acceptance: Settings - General', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/general');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/general');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/general');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/general');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
});
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('it renders, handles image uploads', async function () {
|
||||
@ -75,11 +70,11 @@ describe('Acceptance: Settings - General', function () {
|
||||
expect(document.title, 'page title').to.equal('Settings - General - Test Blog');
|
||||
|
||||
// highlights nav menu
|
||||
expect($('[data-test-nav="settings"]').hasClass('active'), 'highlights nav menu item')
|
||||
.to.be.true;
|
||||
expect(find('[data-test-nav="settings"]'), 'highlights nav menu item')
|
||||
.to.have.class('active');
|
||||
|
||||
expect(
|
||||
find('[data-test-save-button]').text().trim(),
|
||||
find('[data-test-save-button]').textContent.trim(),
|
||||
'save button text'
|
||||
).to.equal('Save settings');
|
||||
|
||||
@ -93,7 +88,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
|
||||
// has fixture icon
|
||||
expect(
|
||||
find('[data-test-icon-img]').attr('src'),
|
||||
find('[data-test-icon-img]').getAttribute('src'),
|
||||
'initial icon src'
|
||||
).to.equal('/content/images/2014/Feb/favicon.ico');
|
||||
|
||||
@ -110,7 +105,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
|
||||
// select file
|
||||
fileUpload(
|
||||
'[data-test-file-input="icon"]',
|
||||
'[data-test-file-input="icon"] input',
|
||||
['test'],
|
||||
{name: 'pub-icon.ico', type: 'image/x-icon'}
|
||||
);
|
||||
@ -126,7 +121,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
// wait for upload to finish and check image is shown
|
||||
await wait();
|
||||
expect(
|
||||
find('[data-test-icon-img]').attr('src'),
|
||||
find('[data-test-icon-img]').getAttribute('src'),
|
||||
'icon img after upload'
|
||||
).to.match(/pub-icon\.ico$/);
|
||||
expect(
|
||||
@ -135,7 +130,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
).to.not.exist;
|
||||
|
||||
// failed upload shows error
|
||||
server.post('/uploads/icon/', function () {
|
||||
this.server.post('/uploads/icon/', function () {
|
||||
return {
|
||||
errors: [{
|
||||
errorType: 'ValidationError',
|
||||
@ -145,24 +140,24 @@ describe('Acceptance: Settings - General', function () {
|
||||
}, 422);
|
||||
await click('[data-test-delete-image="icon"]');
|
||||
await fileUpload(
|
||||
'[data-test-file-input="icon"]',
|
||||
'[data-test-file-input="icon"] input',
|
||||
['test'],
|
||||
{name: 'pub-icon.ico', type: 'image/x-icon'}
|
||||
);
|
||||
expect(
|
||||
find('[data-test-error="icon"]').text().trim(),
|
||||
find('[data-test-error="icon"]').textContent.trim(),
|
||||
'failed icon upload message'
|
||||
).to.equal('Wrong icon size');
|
||||
|
||||
// reset upload endpoints
|
||||
mockUploads(server);
|
||||
mockUploads(this.server);
|
||||
|
||||
// blog logo upload
|
||||
// -------------------------------------------------------------- //
|
||||
|
||||
// has fixture icon
|
||||
expect(
|
||||
find('[data-test-logo-img]').attr('src'),
|
||||
find('[data-test-logo-img]').getAttribute('src'),
|
||||
'initial logo src'
|
||||
).to.equal('/content/images/2013/Nov/logo.png');
|
||||
|
||||
@ -179,7 +174,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
|
||||
// select file
|
||||
fileUpload(
|
||||
'[data-test-file-input="logo"]',
|
||||
'[data-test-file-input="logo"] input',
|
||||
['test'],
|
||||
{name: 'pub-logo.png', type: 'image/png'}
|
||||
);
|
||||
@ -195,7 +190,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
// wait for upload to finish and check image is shown
|
||||
await wait();
|
||||
expect(
|
||||
find('[data-test-logo-img]').attr('src'),
|
||||
find('[data-test-logo-img]').getAttribute('src'),
|
||||
'logo img after upload'
|
||||
).to.match(/pub-logo\.png$/);
|
||||
expect(
|
||||
@ -204,7 +199,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
).to.not.exist;
|
||||
|
||||
// failed upload shows error
|
||||
server.post('/uploads/', function () {
|
||||
this.server.post('/uploads/', function () {
|
||||
return {
|
||||
errors: [{
|
||||
errorType: 'ValidationError',
|
||||
@ -214,24 +209,24 @@ describe('Acceptance: Settings - General', function () {
|
||||
}, 422);
|
||||
await click('[data-test-delete-image="logo"]');
|
||||
await fileUpload(
|
||||
'[data-test-file-input="logo"]',
|
||||
'[data-test-file-input="logo"] input',
|
||||
['test'],
|
||||
{name: 'pub-logo.png', type: 'image/png'}
|
||||
);
|
||||
expect(
|
||||
find('[data-test-error="logo"]').text().trim(),
|
||||
find('[data-test-error="logo"]').textContent.trim(),
|
||||
'failed logo upload message'
|
||||
).to.equal('Wrong logo size');
|
||||
|
||||
// reset upload endpoints
|
||||
mockUploads(server);
|
||||
mockUploads(this.server);
|
||||
|
||||
// blog cover upload
|
||||
// -------------------------------------------------------------- //
|
||||
|
||||
// has fixture icon
|
||||
expect(
|
||||
find('[data-test-cover-img]').attr('src'),
|
||||
find('[data-test-cover-img]').getAttribute('src'),
|
||||
'initial coverImage src'
|
||||
).to.equal('/content/images/2014/Feb/cover.jpg');
|
||||
|
||||
@ -248,7 +243,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
|
||||
// select file
|
||||
fileUpload(
|
||||
'[data-test-file-input="coverImage"]',
|
||||
'[data-test-file-input="coverImage"] input',
|
||||
['test'],
|
||||
{name: 'pub-coverImage.png', type: 'image/png'}
|
||||
);
|
||||
@ -264,7 +259,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
// wait for upload to finish and check image is shown
|
||||
await wait();
|
||||
expect(
|
||||
find('[data-test-cover-img]').attr('src'),
|
||||
find('[data-test-cover-img]').getAttribute('src'),
|
||||
'coverImage img after upload'
|
||||
).to.match(/pub-coverImage\.png$/);
|
||||
expect(
|
||||
@ -273,7 +268,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
).to.not.exist;
|
||||
|
||||
// failed upload shows error
|
||||
server.post('/uploads/', function () {
|
||||
this.server.post('/uploads/', function () {
|
||||
return {
|
||||
errors: [{
|
||||
errorType: 'ValidationError',
|
||||
@ -283,17 +278,17 @@ describe('Acceptance: Settings - General', function () {
|
||||
}, 422);
|
||||
await click('[data-test-delete-image="coverImage"]');
|
||||
await fileUpload(
|
||||
'[data-test-file-input="coverImage"]',
|
||||
'[data-test-file-input="coverImage"] input',
|
||||
['test'],
|
||||
{name: 'pub-coverImage.png', type: 'image/png'}
|
||||
);
|
||||
expect(
|
||||
find('[data-test-error="coverImage"]').text().trim(),
|
||||
find('[data-test-error="coverImage"]').textContent.trim(),
|
||||
'failed coverImage upload message'
|
||||
).to.equal('Wrong coverImage size');
|
||||
|
||||
// reset upload endpoints
|
||||
mockUploads(server);
|
||||
mockUploads(this.server);
|
||||
|
||||
// CMD-S shortcut works
|
||||
// -------------------------------------------------------------- //
|
||||
@ -305,7 +300,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
});
|
||||
// we've already saved in this test so there's no on-screen indication
|
||||
// that we've had another save, check the request was fired instead
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
let params = JSON.parse(lastRequest.requestBody);
|
||||
expect(params.settings.findBy('key', 'title').value).to.equal('CMD-S Test');
|
||||
});
|
||||
@ -316,57 +311,57 @@ describe('Acceptance: Settings - General', function () {
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/general');
|
||||
|
||||
expect(find('#activeTimezone option').length, 'available timezones').to.equal(66);
|
||||
expect(find('#activeTimezone option:selected').text().trim()).to.equal('(GMT) UTC');
|
||||
find('#activeTimezone option[value="Africa/Cairo"]').prop('selected', true);
|
||||
expect(findAll('#activeTimezone option').length, 'available timezones').to.equal(66);
|
||||
expect(find('#activeTimezone option:checked').textContent.trim()).to.equal('(GMT) UTC');
|
||||
find('#activeTimezone option[value="Africa/Cairo"]').selected = true;
|
||||
|
||||
await triggerEvent('#activeTimezone', 'change');
|
||||
await click('[data-test-save-button]');
|
||||
expect(find('#activeTimezone option:selected').text().trim()).to.equal('(GMT +2:00) Cairo, Egypt');
|
||||
expect(find('#activeTimezone option:checked').textContent.trim()).to.equal('(GMT +2:00) Cairo, Egypt');
|
||||
});
|
||||
|
||||
it('handles private blog settings correctly', async function () {
|
||||
await visit('/settings/general');
|
||||
|
||||
// handles private blog settings correctly
|
||||
expect(find('[data-test-private-checkbox]').prop('checked'), 'isPrivate checkbox').to.be.false;
|
||||
expect(find('[data-test-private-checkbox]').checked, 'isPrivate checkbox').to.be.false;
|
||||
|
||||
await click('[data-test-private-checkbox]');
|
||||
|
||||
expect(find('[data-test-private-checkbox]').prop('checked'), 'isPrivate checkbox').to.be.true;
|
||||
expect(find('[data-test-password-input]').length, 'password input').to.equal(1);
|
||||
expect(find('[data-test-password-input]').val(), 'password default value').to.not.equal('');
|
||||
expect(find('[data-test-private-checkbox]').checked, 'isPrivate checkbox').to.be.true;
|
||||
expect(findAll('[data-test-password-input]').length, 'password input').to.equal(1);
|
||||
expect(find('[data-test-password-input]').value, 'password default value').to.not.equal('');
|
||||
|
||||
await fillIn('[data-test-password-input]', '');
|
||||
await triggerEvent('[data-test-password-input]', 'blur');
|
||||
await blur('[data-test-password-input]');
|
||||
|
||||
expect(find('[data-test-password-error]').text().trim(), 'empty password error')
|
||||
expect(find('[data-test-password-error]').textContent.trim(), 'empty password error')
|
||||
.to.equal('Password must be supplied');
|
||||
|
||||
await fillIn('[data-test-password-input]', 'asdfg');
|
||||
await triggerEvent('[data-test-password-input]', 'blur');
|
||||
await blur('[data-test-password-input]');
|
||||
|
||||
expect(find('[data-test-password-error]').text().trim(), 'present password error')
|
||||
expect(find('[data-test-password-error]').textContent.trim(), 'present password error')
|
||||
.to.equal('');
|
||||
});
|
||||
|
||||
it('handles social blog settings correctly', async function () {
|
||||
let testSocialInput = async function (type, input, expectedValue, expectedError = '') {
|
||||
await fillIn(`[data-test-${type}-input]`, input);
|
||||
await triggerEvent(`[data-test-${type}-input]`, 'blur');
|
||||
await blur(`[data-test-${type}-input]`);
|
||||
|
||||
expect(
|
||||
find(`[data-test-${type}-input]`).val(),
|
||||
find(`[data-test-${type}-input]`).value,
|
||||
`${type} value for ${input}`
|
||||
).to.equal(expectedValue);
|
||||
|
||||
expect(
|
||||
find(`[data-test-${type}-error]`).text().trim(),
|
||||
find(`[data-test-${type}-error]`).textContent.trim(),
|
||||
`${type} validation response for ${input}`
|
||||
).to.equal(expectedError);
|
||||
|
||||
expect(
|
||||
find(`[data-test-${type}-input]`).closest('.form-group').hasClass('error'),
|
||||
find(`[data-test-${type}-input]`).closest('.form-group').classList.contains('error'),
|
||||
`${type} input should be in error state with '${input}'`
|
||||
).to.equal(!!expectedError);
|
||||
};
|
||||
@ -379,15 +374,15 @@ describe('Acceptance: Settings - General', function () {
|
||||
|
||||
// validates a facebook url correctly
|
||||
// loads fixtures and performs transform
|
||||
expect(find('[data-test-facebook-input]').val(), 'initial facebook value')
|
||||
expect(find('[data-test-facebook-input]').value, 'initial facebook value')
|
||||
.to.equal('https://www.facebook.com/test');
|
||||
|
||||
await triggerEvent('[data-test-facebook-input]', 'focus');
|
||||
await triggerEvent('[data-test-facebook-input]', 'blur');
|
||||
await focus('[data-test-facebook-input]');
|
||||
await blur('[data-test-facebook-input]');
|
||||
|
||||
// regression test: we still have a value after the input is
|
||||
// focused and then blurred without any changes
|
||||
expect(find('[data-test-facebook-input]').val(), 'facebook value after blur with no change')
|
||||
expect(find('[data-test-facebook-input]').value, 'facebook value after blur with no change')
|
||||
.to.equal('https://www.facebook.com/test');
|
||||
|
||||
await testFacebookValidation(
|
||||
@ -431,15 +426,15 @@ describe('Acceptance: Settings - General', function () {
|
||||
// validates a twitter url correctly
|
||||
|
||||
// loads fixtures and performs transform
|
||||
expect(find('[data-test-twitter-input]').val(), 'initial twitter value')
|
||||
expect(find('[data-test-twitter-input]').value, 'initial twitter value')
|
||||
.to.equal('https://twitter.com/test');
|
||||
|
||||
await triggerEvent('[data-test-twitter-input]', 'focus');
|
||||
await triggerEvent('[data-test-twitter-input]', 'blur');
|
||||
await focus('[data-test-twitter-input]');
|
||||
await blur('[data-test-twitter-input]');
|
||||
|
||||
// regression test: we still have a value after the input is
|
||||
// focused and then blurred without any changes
|
||||
expect(find('[data-test-twitter-input]').val(), 'twitter value after blur with no change')
|
||||
expect(find('[data-test-twitter-input]').value, 'twitter value after blur with no change')
|
||||
.to.equal('https://twitter.com/test');
|
||||
|
||||
await testTwitterValidation(
|
||||
@ -469,7 +464,7 @@ describe('Acceptance: Settings - General', function () {
|
||||
await visit('/settings/general');
|
||||
|
||||
expect(
|
||||
find('[data-test-private-checkbox]').prop('checked'),
|
||||
find('[data-test-private-checkbox]').checked,
|
||||
'private blog checkbox'
|
||||
).to.be.false;
|
||||
|
||||
@ -479,16 +474,16 @@ describe('Acceptance: Settings - General', function () {
|
||||
await click('[data-test-private-checkbox]');
|
||||
|
||||
expect(
|
||||
find('[data-test-private-checkbox]').prop('checked'),
|
||||
find('[data-test-private-checkbox]').checked,
|
||||
'private blog checkbox'
|
||||
).to.be.true;
|
||||
|
||||
await visit('/settings/team');
|
||||
|
||||
expect(find('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
expect(findAll('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
|
||||
// Leave without saving
|
||||
await (click('.fullscreen-modal [data-test-leave-button]'), 'leave without saving');
|
||||
await click('.fullscreen-modal [data-test-leave-button]');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/team');
|
||||
|
||||
@ -498,12 +493,12 @@ describe('Acceptance: Settings - General', function () {
|
||||
|
||||
// settings were not saved
|
||||
expect(
|
||||
find('[data-test-private-checkbox]').prop('checked'),
|
||||
find('[data-test-private-checkbox]').checked,
|
||||
'private blog checkbox'
|
||||
).to.be.false;
|
||||
|
||||
expect(
|
||||
find('[data-test-title-input]').text().trim(),
|
||||
find('[data-test-title-input]').textContent.trim(),
|
||||
'Blog title'
|
||||
).to.equal('');
|
||||
});
|
||||
|
@ -1,99 +1,89 @@
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
it
|
||||
} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {click, currentRouteName, currentURL, fillIn, find, findAll, triggerEvent} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
describe('Acceptance: Settings - Integrations', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
describe('Acceptance: Settings - Integrations - Custom', function () {
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
describe('access permissions', function () {
|
||||
beforeEach(function () {
|
||||
server.create('integration', {name: 'Test'});
|
||||
this.server.create('integration', {name: 'Test'});
|
||||
});
|
||||
|
||||
it('redirects /integrations/ to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/integrations');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects /integrations/ to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects /integrations/ to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects /integrations/ to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
});
|
||||
|
||||
it('redirects /integrations/:id/ to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/integrations/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects /integrations/:id/ to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects /integrations/:id/ to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects /integrations/:id/ to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
@ -101,11 +91,11 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
});
|
||||
|
||||
describe('navigation', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('renders correctly', async function () {
|
||||
@ -113,13 +103,13 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
|
||||
// slack is not configured in the fixtures
|
||||
expect(
|
||||
find('[data-test-app="slack"] [data-test-app-status]').text().trim(),
|
||||
find('[data-test-app="slack"] [data-test-app-status]').textContent.trim(),
|
||||
'slack app status'
|
||||
).to.equal('Configure');
|
||||
|
||||
// amp is enabled in the fixtures
|
||||
expect(
|
||||
find('[data-test-app="amp"] [data-test-app-status]').text().trim(),
|
||||
find('[data-test-app="amp"] [data-test-app-status]').textContent.trim(),
|
||||
'amp app status'
|
||||
).to.equal('Active');
|
||||
});
|
||||
@ -162,32 +152,32 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
});
|
||||
|
||||
describe('custom integrations', function () {
|
||||
beforeEach(function () {
|
||||
server.loadFixtures('configurations');
|
||||
let config = server.schema.configurations.first();
|
||||
beforeEach(async function () {
|
||||
this.server.loadFixtures('configurations');
|
||||
let config = this.server.schema.configurations.first();
|
||||
config.update({
|
||||
enableDeveloperExperiments: true
|
||||
});
|
||||
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('handles 404', async function () {
|
||||
await visit('/settings/integrations/1');
|
||||
expect(currentPath()).to.equal('error404');
|
||||
expect(currentRouteName()).to.equal('error404');
|
||||
});
|
||||
|
||||
it('can add new integration', async function () {
|
||||
// sanity check
|
||||
expect(
|
||||
server.db.integrations.length,
|
||||
this.server.db.integrations.length,
|
||||
'number of integrations in db at start'
|
||||
).to.equal(0);
|
||||
expect(
|
||||
server.db.apiKeys.length,
|
||||
this.server.db.apiKeys.length,
|
||||
'number of apiKeys in db at start'
|
||||
).to.equal(0);
|
||||
|
||||
@ -220,7 +210,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await click('[data-test-button="create-integration"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="new-integration-name"]').text(),
|
||||
find('[data-test-error="new-integration-name"]').textContent,
|
||||
'name error after create with blank field'
|
||||
).to.have.string('enter a name');
|
||||
|
||||
@ -228,7 +218,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await click('[data-test-button="create-integration"]');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="new-integration-name"]').text(),
|
||||
find('[data-test-error="new-integration-name"]').textContent,
|
||||
'name error after create with duplicate name'
|
||||
).to.have.string('already been used');
|
||||
|
||||
@ -236,7 +226,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await fillIn('[data-test-input="new-integration-name"]', 'Test');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="new-integration-name"]').text().trim(),
|
||||
find('[data-test-error="new-integration-name"]').textContent.trim(),
|
||||
'name error after typing in field'
|
||||
).to.be.empty;
|
||||
|
||||
@ -248,12 +238,12 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
).to.not.exist;
|
||||
|
||||
expect(
|
||||
server.db.integrations.length,
|
||||
this.server.db.integrations.length,
|
||||
'number of integrations in db after create'
|
||||
).to.equal(1);
|
||||
// mirage sanity check
|
||||
expect(
|
||||
server.db.apiKeys.length,
|
||||
this.server.db.apiKeys.length,
|
||||
'number of api keys in db after create'
|
||||
).to.equal(2);
|
||||
|
||||
@ -276,7 +266,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
).to.not.exist;
|
||||
|
||||
expect(
|
||||
find('[data-test-custom-integration]').length,
|
||||
findAll('[data-test-custom-integration]').length,
|
||||
'number of custom integrations after creation'
|
||||
).to.equal(1);
|
||||
|
||||
@ -289,7 +279,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
});
|
||||
|
||||
it('can manage an integration', async function () {
|
||||
server.create('integration');
|
||||
this.server.create('integration');
|
||||
|
||||
await visit('/settings/integrations/1');
|
||||
|
||||
@ -299,7 +289,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
).to.equal('/settings/integrations/1');
|
||||
|
||||
expect(
|
||||
find('[data-test-screen-title]').text(),
|
||||
find('[data-test-screen-title]').textContent,
|
||||
'screen title'
|
||||
).to.have.string('Integration 1');
|
||||
|
||||
@ -307,29 +297,29 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
// TODO: add test for logo
|
||||
|
||||
expect(
|
||||
find('[data-test-input="name"]').val(),
|
||||
find('[data-test-input="name"]').value,
|
||||
'initial name value'
|
||||
).to.equal('Integration 1');
|
||||
|
||||
expect(
|
||||
find('[data-test-input="description"]').val(),
|
||||
find('[data-test-input="description"]').value,
|
||||
'initial description value'
|
||||
).to.equal('');
|
||||
|
||||
expect(
|
||||
find('[data-test-input="content_key"]').val(),
|
||||
find('[data-test-input="content_key"]').value,
|
||||
'content key input value'
|
||||
).to.equal('integration-1_content_key-12345');
|
||||
|
||||
expect(
|
||||
find('[data-test-input="admin_key"]').val(),
|
||||
find('[data-test-input="admin_key"]').value,
|
||||
'admin key input value'
|
||||
).to.equal('integration-1_admin_key-12345');
|
||||
|
||||
// it can modify integration fields and has validation
|
||||
|
||||
expect(
|
||||
find('[data-test-error="name"]').text().trim(),
|
||||
find('[data-test-error="name"]').textContent.trim(),
|
||||
'initial name error'
|
||||
).to.be.empty;
|
||||
|
||||
@ -337,14 +327,14 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await triggerEvent('[data-test-input="name"]', 'blur');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="name"]').text(),
|
||||
find('[data-test-error="name"]').textContent,
|
||||
'name validation for blank string'
|
||||
).to.have.string('enter a name');
|
||||
|
||||
await click('[data-test-button="save"]');
|
||||
|
||||
expect(
|
||||
server.schema.integrations.first().name,
|
||||
this.server.schema.integrations.first().name,
|
||||
'db integration name after failed save'
|
||||
).to.equal('Integration 1');
|
||||
|
||||
@ -352,7 +342,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await triggerEvent('[data-test-input="name"]', 'blur');
|
||||
|
||||
expect(
|
||||
find('[data-test-error="name"]').text().trim(),
|
||||
find('[data-test-error="name"]').textContent.trim(),
|
||||
'name error after valid entry'
|
||||
).to.be.empty;
|
||||
|
||||
@ -370,12 +360,12 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
).to.equal('/settings/integrations');
|
||||
|
||||
expect(
|
||||
find('[data-test-integration="1"] [data-test-text="name"]').text().trim(),
|
||||
find('[data-test-integration="1"] [data-test-text="name"]').textContent.trim(),
|
||||
'integration name after save'
|
||||
).to.equal('Test Integration');
|
||||
|
||||
expect(
|
||||
find('[data-test-integration="1"] [data-test-text="description"]').text().trim(),
|
||||
find('[data-test-integration="1"] [data-test-text="description"]').textContent.trim(),
|
||||
'integration description after save'
|
||||
).to.equal('Description for Test Integration');
|
||||
|
||||
@ -387,14 +377,14 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await click('[data-test-link="integrations-back"]');
|
||||
|
||||
expect(
|
||||
find('[data-modal="unsaved-settings"]'),
|
||||
find('[data-test-modal="unsaved-settings"]'),
|
||||
'modal shown when navigating with unsaved changes'
|
||||
).to.exist;
|
||||
|
||||
await click('[data-test-stay-button]');
|
||||
|
||||
expect(
|
||||
find('[data-modal="unsaved-settings"]'),
|
||||
find('[data-test-modal="unsaved-settings"]'),
|
||||
'modal is closed after clicking "stay"'
|
||||
).to.not.exist;
|
||||
|
||||
@ -407,7 +397,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await click('[data-test-leave-button]');
|
||||
|
||||
expect(
|
||||
find('[data-modal="unsaved-settings"]'),
|
||||
find('[data-test-modal="unsaved-settings"]'),
|
||||
'modal is closed after clicking "leave"'
|
||||
).to.not.exist;
|
||||
|
||||
@ -417,27 +407,27 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
).to.equal('/settings/integrations');
|
||||
|
||||
expect(
|
||||
find('[data-test-integration="1"] [data-test-text="name"]').text().trim(),
|
||||
find('[data-test-integration="1"] [data-test-text="name"]').textContent.trim(),
|
||||
'integration name after leaving unsaved changes'
|
||||
).to.equal('Test Integration');
|
||||
});
|
||||
|
||||
it('can manage an integration\'s webhooks', async function () {
|
||||
server.create('integration');
|
||||
this.server.create('integration');
|
||||
|
||||
await visit('/settings/integrations/1');
|
||||
|
||||
expect(find('[data-test-webhooks-blank-slate]').length).to.equal(1);
|
||||
expect(find('[data-test-webhooks-blank-slate]')).to.exist;
|
||||
|
||||
// open new webhook modal
|
||||
await click('[data-test-link="add-webhook"]');
|
||||
expect(find('[data-test-modal="webhook-form"]').length).to.equal(1);
|
||||
expect(find('[data-test-modal="webhook-form"] [data-test-text="title"]').text())
|
||||
expect(find('[data-test-modal="webhook-form"]')).to.exist;
|
||||
expect(find('[data-test-modal="webhook-form"] [data-test-text="title"]').textContent)
|
||||
.to.have.string('New webhook');
|
||||
|
||||
// can cancel new webhook
|
||||
await click('[data-test-button="cancel-webhook"]');
|
||||
expect(find('[data-test-modal="webhook-form"]').length).to.equal(0);
|
||||
expect(find('[data-test-modal="webhook-form"]')).to.not.exist;
|
||||
|
||||
// create new webhook
|
||||
await click('[data-test-link="add-webhook"]');
|
||||
@ -447,30 +437,30 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await click('[data-test-button="save-webhook"]');
|
||||
|
||||
// modal closed and 1 webhook listed with correct details
|
||||
expect(find('[data-test-modal="webhook-form"]').length).to.equal(0);
|
||||
expect(find('[data-test-webhook-row]').length).to.equal(1);
|
||||
expect(find('[data-test-modal="webhook-form"]')).to.not.exist;
|
||||
expect(find('[data-test-webhook-row]')).to.exist;
|
||||
let row = find('[data-test-webhook-row="1"]');
|
||||
expect(row.find('[data-test-text="name"]').text())
|
||||
expect(row.querySelector('[data-test-text="name"]').textContent)
|
||||
.to.have.string('First webhook');
|
||||
expect(row.find('[data-test-text="event"]').text())
|
||||
expect(row.querySelector('[data-test-text="event"]').textContent)
|
||||
.to.have.string('Site Changed (rebuild)');
|
||||
expect(row.find('[data-test-text="targetUrl"]').text())
|
||||
expect(row.querySelector('[data-test-text="targetUrl"]').textContent)
|
||||
.to.have.string('https://example.com/first-webhook');
|
||||
expect(row.find('[data-test-text="last-triggered"]').text())
|
||||
expect(row.querySelector('[data-test-text="last-triggered"]').textContent)
|
||||
.to.have.string('Not triggered');
|
||||
|
||||
// click edit webhook link
|
||||
await click('[data-test-webhook-row="1"] [data-test-link="edit-webhook"]');
|
||||
|
||||
// modal appears and has correct title
|
||||
expect(find('[data-test-modal="webhook-form"]').length).to.equal(1);
|
||||
expect(find('[data-test-modal="webhook-form"] [data-test-text="title"]').text())
|
||||
expect(find('[data-test-modal="webhook-form"]')).to.exist;
|
||||
expect(find('[data-test-modal="webhook-form"] [data-test-text="title"]').textContent)
|
||||
.to.have.string('Edit webhook');
|
||||
});
|
||||
|
||||
// test to ensure the `value=description` passed to `gh-text-input` is `readonly`
|
||||
it('doesn\'t show unsaved changes modal after placing focus on description field', async function () {
|
||||
server.create('integration');
|
||||
this.server.create('integration');
|
||||
|
||||
await visit('/settings/integrations/1');
|
||||
await click('[data-test-input="description"]');
|
||||
@ -478,7 +468,7 @@ describe('Acceptance: Settings - Integrations', function () {
|
||||
await click('[data-test-link="integrations-back"]');
|
||||
|
||||
expect(
|
||||
find('[data-modal="unsaved-settings"]'),
|
||||
find('[data-test-modal="unsaved-settings"]'),
|
||||
'unsaved changes modal is not shown'
|
||||
).to.not.exist;
|
||||
|
||||
|
@ -1,66 +1,61 @@
|
||||
import $ from 'jquery';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
// import wait from 'ember-test-helpers/wait';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {click, currentURL, find, findAll} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {fileUpload} from '../../helpers/file-upload';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
// import wait from 'ember-test-helpers/wait';
|
||||
// import {timeout} from 'ember-concurrency';
|
||||
|
||||
describe('Acceptance: Settings - Labs', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/labs');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/labs');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/labs');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/labs');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
});
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it.skip('it renders, loads modals correctly', async function () {
|
||||
@ -73,24 +68,24 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
expect(document.title, 'page title').to.equal('Settings - Labs - Test Blog');
|
||||
|
||||
// highlights nav menu
|
||||
expect($('[data-test-nav="labs"]').hasClass('active'), 'highlights nav menu item')
|
||||
.to.be.true;
|
||||
expect(find('[data-test-nav="labs"]'), 'highlights nav menu item')
|
||||
.to.have.class('active');
|
||||
|
||||
await click('#settings-resetdb .js-delete');
|
||||
expect(find('.fullscreen-modal .modal-content').length, 'modal element').to.equal(1);
|
||||
expect(findAll('.fullscreen-modal .modal-content').length, 'modal element').to.equal(1);
|
||||
|
||||
await click('.fullscreen-modal .modal-footer .gh-btn');
|
||||
expect(find('.fullscreen-modal').length, 'modal element').to.equal(0);
|
||||
expect(findAll('.fullscreen-modal').length, 'modal element').to.equal(0);
|
||||
});
|
||||
|
||||
it('can upload/download redirects', async function () {
|
||||
await visit('/settings/labs');
|
||||
|
||||
// successful upload
|
||||
server.post('/redirects/json/', {}, 200);
|
||||
this.server.post('/redirects/json/', {}, 200);
|
||||
|
||||
await fileUpload(
|
||||
'[data-test-file-input="redirects"]',
|
||||
'[data-test-file-input="redirects"] input',
|
||||
['test'],
|
||||
{name: 'redirects.json', type: 'application/json'}
|
||||
);
|
||||
@ -102,33 +97,33 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
// await timeout(50);
|
||||
//
|
||||
// // shows success button
|
||||
// let button = find('[data-test-button="upload-redirects"]');
|
||||
// expect(button.length, 'no of success buttons').to.equal(1);
|
||||
// let buttons = findAll('[data-test-button="upload-redirects"]');
|
||||
// expect(buttons.length, 'no of success buttons').to.equal(1);
|
||||
// expect(
|
||||
// button.hasClass('gh-btn-green'),
|
||||
// buttons[0],
|
||||
// 'success button is green'
|
||||
// ).to.be.true;
|
||||
// ).to.have.class('gh-btn-green);
|
||||
// expect(
|
||||
// button.text().trim(),
|
||||
// button.textContent,
|
||||
// 'success button text'
|
||||
// ).to.have.string('Uploaded');
|
||||
//
|
||||
// await wait();
|
||||
|
||||
// returned to normal button
|
||||
let button = find('[data-test-button="upload-redirects"]');
|
||||
expect(button.length, 'no of post-success buttons').to.equal(1);
|
||||
let buttons = findAll('[data-test-button="upload-redirects"]');
|
||||
expect(buttons.length, 'no of post-success buttons').to.equal(1);
|
||||
expect(
|
||||
button.hasClass('gh-btn-green'),
|
||||
buttons[0],
|
||||
'post-success button doesn\'t have success class'
|
||||
).to.be.false;
|
||||
).to.not.have.class('gh-btn-green');
|
||||
expect(
|
||||
button.text().trim(),
|
||||
buttons[0].textContent,
|
||||
'post-success button text'
|
||||
).to.have.string('Upload redirects');
|
||||
|
||||
// failed upload
|
||||
server.post('/redirects/json/', {
|
||||
this.server.post('/redirects/json/', {
|
||||
errors: [{
|
||||
errorType: 'BadRequestError',
|
||||
message: 'Test failure message'
|
||||
@ -136,7 +131,7 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
}, 400);
|
||||
|
||||
await fileUpload(
|
||||
'[data-test-file-input="redirects"]',
|
||||
'[data-test-file-input="redirects"] input',
|
||||
['test'],
|
||||
{name: 'redirects-bad.json', type: 'application/json'}
|
||||
);
|
||||
@ -148,14 +143,14 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
// await timeout(50);
|
||||
//
|
||||
// shows failure button
|
||||
// button = find('[data-test-button="upload-redirects"]');
|
||||
// expect(button.length, 'no of failure buttons').to.equal(1);
|
||||
// buttons = findAll('[data-test-button="upload-redirects"]');
|
||||
// expect(buttons.length, 'no of failure buttons').to.equal(1);
|
||||
// expect(
|
||||
// button.hasClass('gh-btn-red'),
|
||||
// buttons[0],
|
||||
// 'failure button is red'
|
||||
// ).to.be.true;
|
||||
// ).to.have.class('gh-btn-red);
|
||||
// expect(
|
||||
// button.text().trim(),
|
||||
// buttons[0].textContent,
|
||||
// 'failure button text'
|
||||
// ).to.have.string('Upload Failed');
|
||||
//
|
||||
@ -163,26 +158,26 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
|
||||
// shows error message
|
||||
expect(
|
||||
find('[data-test-error="redirects"]').text().trim(),
|
||||
find('[data-test-error="redirects"]').textContent.trim(),
|
||||
'upload error text'
|
||||
).to.have.string('Test failure message');
|
||||
|
||||
// returned to normal button
|
||||
button = find('[data-test-button="upload-redirects"]');
|
||||
expect(button.length, 'no of post-failure buttons').to.equal(1);
|
||||
buttons = findAll('[data-test-button="upload-redirects"]');
|
||||
expect(buttons.length, 'no of post-failure buttons').to.equal(1);
|
||||
expect(
|
||||
button.hasClass('gh-btn-red'),
|
||||
buttons[0],
|
||||
'post-failure button doesn\'t have failure class'
|
||||
).to.be.false;
|
||||
).to.not.have.class('gh-btn-red');
|
||||
expect(
|
||||
button.text().trim(),
|
||||
buttons[0].textContent,
|
||||
'post-failure button text'
|
||||
).to.have.string('Upload redirects');
|
||||
|
||||
// successful upload clears error
|
||||
server.post('/redirects/json/', {}, 200);
|
||||
this.server.post('/redirects/json/', {}, 200);
|
||||
await fileUpload(
|
||||
'[data-test-file-input="redirects"]',
|
||||
'[data-test-file-input="redirects"] input',
|
||||
['test'],
|
||||
{name: 'redirects-bad.json', type: 'application/json'}
|
||||
);
|
||||
@ -192,18 +187,18 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
// can download redirects.json
|
||||
await click('[data-test-link="download-redirects"]');
|
||||
|
||||
let iframe = $('#iframeDownload');
|
||||
expect(iframe.attr('src')).to.have.string('/redirects/json/');
|
||||
let iframe = document.querySelector('#iframeDownload');
|
||||
expect(iframe.getAttribute('src')).to.have.string('/redirects/json/');
|
||||
});
|
||||
|
||||
it('can upload/download routes.yaml', async function () {
|
||||
await visit('/settings/labs');
|
||||
|
||||
// successful upload
|
||||
server.post('/settings/routes/yaml/', {}, 200);
|
||||
this.server.post('/settings/routes/yaml/', {}, 200);
|
||||
|
||||
await fileUpload(
|
||||
'[data-test-file-input="routes"]',
|
||||
'[data-test-file-input="routes"] input',
|
||||
['test'],
|
||||
{name: 'routes.yaml', type: 'application/x-yaml'}
|
||||
);
|
||||
@ -229,19 +224,19 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
// await wait();
|
||||
|
||||
// returned to normal button
|
||||
let button = find('[data-test-button="upload-routes"]');
|
||||
expect(button.length, 'no of post-success buttons').to.equal(1);
|
||||
let buttons = findAll('[data-test-button="upload-routes"]');
|
||||
expect(buttons.length, 'no of post-success buttons').to.equal(1);
|
||||
expect(
|
||||
button.hasClass('gh-btn-green'),
|
||||
buttons[0],
|
||||
'routes post-success button doesn\'t have success class'
|
||||
).to.be.false;
|
||||
).to.not.have.class('gh-btn-green');
|
||||
expect(
|
||||
button.text().trim(),
|
||||
buttons[0].textContent,
|
||||
'routes post-success button text'
|
||||
).to.have.string('Upload routes YAML');
|
||||
|
||||
// failed upload
|
||||
server.post('/settings/routes/yaml/', {
|
||||
this.server.post('/settings/routes/yaml/', {
|
||||
errors: [{
|
||||
errorType: 'BadRequestError',
|
||||
message: 'Test failure message'
|
||||
@ -249,7 +244,7 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
}, 400);
|
||||
|
||||
await fileUpload(
|
||||
'[data-test-file-input="routes"]',
|
||||
'[data-test-file-input="routes"] input',
|
||||
['test'],
|
||||
{name: 'routes-bad.yaml', type: 'application/x-yaml'}
|
||||
);
|
||||
@ -276,26 +271,26 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
|
||||
// shows error message
|
||||
expect(
|
||||
find('[data-test-error="routes"]').text().trim(),
|
||||
find('[data-test-error="routes"]').textContent,
|
||||
'routes upload error text'
|
||||
).to.have.string('Test failure message');
|
||||
|
||||
// returned to normal button
|
||||
button = find('[data-test-button="upload-routes"]');
|
||||
expect(button.length, 'no of post-failure buttons').to.equal(1);
|
||||
buttons = findAll('[data-test-button="upload-routes"]');
|
||||
expect(buttons.length, 'no of post-failure buttons').to.equal(1);
|
||||
expect(
|
||||
button.hasClass('gh-btn-red'),
|
||||
buttons[0],
|
||||
'routes post-failure button doesn\'t have failure class'
|
||||
).to.be.false;
|
||||
).to.not.have.class('gh-btn-red');
|
||||
expect(
|
||||
button.text().trim(),
|
||||
buttons[0].textContent,
|
||||
'routes post-failure button text'
|
||||
).to.have.string('Upload routes YAML');
|
||||
|
||||
// successful upload clears error
|
||||
server.post('/settings/routes/yaml/', {}, 200);
|
||||
this.server.post('/settings/routes/yaml/', {}, 200);
|
||||
await fileUpload(
|
||||
'[data-test-file-input="routes"]',
|
||||
'[data-test-file-input="routes"] input',
|
||||
['test'],
|
||||
{name: 'routes-good.yaml', type: 'application/x-yaml'}
|
||||
);
|
||||
@ -305,8 +300,8 @@ describe('Acceptance: Settings - Labs', function () {
|
||||
// can download redirects.json
|
||||
await click('[data-test-link="download-routes"]');
|
||||
|
||||
let iframe = $('#iframeDownload');
|
||||
expect(iframe.attr('src')).to.have.string('/settings/routes/yaml/');
|
||||
let iframe = document.querySelector('#iframeDownload');
|
||||
expect(iframe.getAttribute('src')).to.have.string('/settings/routes/yaml/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,65 +1,60 @@
|
||||
import Mirage from 'ember-cli-mirage';
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {blur, click, currentURL, fillIn, find, findAll, triggerEvent} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
describe('Acceptance: Settings - Integrations - Slack', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/integrations/slack');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/slack');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/slack');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/slack');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
});
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('it validates and saves a slack url properly', async function () {
|
||||
@ -71,7 +66,7 @@ describe('Acceptance: Settings - Integrations - Slack', function () {
|
||||
await fillIn('[data-test-slack-url-input]', 'notacorrecturl');
|
||||
await click('[data-test-save-button]');
|
||||
|
||||
expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
|
||||
expect(find('[data-test-error="slack-url"').textContent.trim(), 'inline validation response')
|
||||
.to.equal('The URL must be in a format like https://hooks.slack.com/services/<your personal key>');
|
||||
|
||||
// CMD-S shortcut works
|
||||
@ -82,22 +77,22 @@ describe('Acceptance: Settings - Integrations - Slack', function () {
|
||||
ctrlKey: ctrlOrCmd === 'ctrl'
|
||||
});
|
||||
|
||||
let [newRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [newRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
let params = JSON.parse(newRequest.requestBody);
|
||||
let [result] = JSON.parse(params.settings.findBy('key', 'slack').value);
|
||||
|
||||
expect(result.url).to.equal('https://hooks.slack.com/services/1275958430');
|
||||
expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
expect(find('[data-test-error="slack-url"'), 'inline validation response')
|
||||
.to.not.exist;
|
||||
|
||||
await fillIn('[data-test-slack-url-input]', 'https://hooks.slack.com/services/1275958430');
|
||||
await click('[data-test-send-notification-button]');
|
||||
|
||||
expect(find('.gh-notification').length, 'number of notifications').to.equal(1);
|
||||
expect(find('#slack-settings .error .response').text().trim(), 'inline validation response')
|
||||
.to.equal('');
|
||||
expect(findAll('.gh-notification').length, 'number of notifications').to.equal(1);
|
||||
expect(find('[data-test-error="slack-url"'), 'inline validation response')
|
||||
.to.not.exist;
|
||||
|
||||
server.put('/settings/', function () {
|
||||
this.server.put('/settings/', function () {
|
||||
return new Mirage.Response(422, {}, {
|
||||
errors: [
|
||||
{
|
||||
@ -112,9 +107,9 @@ describe('Acceptance: Settings - Integrations - Slack', function () {
|
||||
await click('[data-test-send-notification-button]');
|
||||
|
||||
// we shouldn't try to send the test request if the save fails
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.url).to.not.match(/\/slack\/test/);
|
||||
expect(find('.gh-notification').length, 'check slack notification after api validation error').to.equal(0);
|
||||
expect(findAll('.gh-notification').length, 'check slack notification after api validation error').to.equal(0);
|
||||
});
|
||||
|
||||
it('warns when leaving without saving', async function () {
|
||||
@ -124,14 +119,14 @@ describe('Acceptance: Settings - Integrations - Slack', function () {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/slack');
|
||||
|
||||
await fillIn('[data-test-slack-url-input]', 'https://hooks.slack.com/services/1275958430');
|
||||
await triggerEvent('[data-test-slack-url-input]', 'blur');
|
||||
await blur('[data-test-slack-url-input]');
|
||||
|
||||
await visit('/settings/design');
|
||||
|
||||
expect(find('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
expect(findAll('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
|
||||
// Leave without saving
|
||||
await (click('.fullscreen-modal [data-test-leave-button]'), 'leave without saving');
|
||||
await click('.fullscreen-modal [data-test-leave-button]');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/design');
|
||||
|
||||
@ -141,7 +136,7 @@ describe('Acceptance: Settings - Integrations - Slack', function () {
|
||||
|
||||
// settings were not saved
|
||||
expect(
|
||||
find('[data-test-slack-url-input]').text().trim(),
|
||||
find('[data-test-slack-url-input]').textContent.trim(),
|
||||
'Slack Webhook URL'
|
||||
).to.equal('');
|
||||
});
|
||||
|
@ -1,16 +1,16 @@
|
||||
/* eslint-disable camelcase */
|
||||
import $ from 'jquery';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import windowProxy from 'ghost-admin/utils/window-proxy';
|
||||
import {Response} from 'ember-cli-mirage';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {blur, click, currentRouteName, currentURL, fillIn, find, findAll} from '@ember/test-helpers';
|
||||
import {errorOverride, errorReset} from 'ghost-admin/tests/helpers/adapter-error';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {timeout} from 'ember-concurrency';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
// Grabbed from keymaster's testing code because Ember's `keyEvent` helper
|
||||
// is for some reason not triggering the events in a way that keymaster detects:
|
||||
@ -40,38 +40,31 @@ let keyup = function (code, el) {
|
||||
};
|
||||
|
||||
describe('Acceptance: Settings - Tags', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/tags');
|
||||
|
||||
expect(currentURL()).to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/design');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/design');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
@ -80,9 +73,9 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
describe('when logged in', function () {
|
||||
let newLocation, originalReplaceState;
|
||||
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
originalReplaceState = windowProxy.replaceState;
|
||||
windowProxy.replaceState = function (params, title, url) {
|
||||
@ -90,7 +83,7 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
};
|
||||
newLocation = undefined;
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
@ -98,8 +91,8 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
});
|
||||
|
||||
it('it renders, can be navigated, can edit, create & delete tags', async function () {
|
||||
let tag1 = server.create('tag');
|
||||
let tag2 = server.create('tag');
|
||||
let tag1 = this.server.create('tag');
|
||||
let tag2 = this.server.create('tag');
|
||||
|
||||
await visit('/settings/tags');
|
||||
|
||||
@ -113,37 +106,39 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
expect(document.title, 'page title').to.equal('Settings - Tags - Test Blog');
|
||||
|
||||
// it highlights nav menu
|
||||
expect($('[data-test-nav="tags"]').hasClass('active'), 'highlights nav menu item')
|
||||
.to.be.true;
|
||||
expect(find('[data-test-nav="tags"]'), 'highlights nav menu item')
|
||||
.to.have.class('active');
|
||||
|
||||
// it lists all tags
|
||||
expect(find('.settings-tags .settings-tag').length, 'tag list count')
|
||||
expect(findAll('.settings-tags .settings-tag').length, 'tag list count')
|
||||
.to.equal(2);
|
||||
expect(find('.settings-tags .settings-tag:first .tag-title').text(), 'tag list item title')
|
||||
let tag = find('.settings-tags .settings-tag');
|
||||
expect(tag.querySelector('.tag-title').textContent, 'tag list item title')
|
||||
.to.equal(tag1.name);
|
||||
|
||||
// it highlights selected tag
|
||||
expect(find(`a[href="/ghost/settings/tags/${tag1.slug}"]`).hasClass('active'), 'highlights selected tag')
|
||||
.to.be.true;
|
||||
expect(find(`a[href="/ghost/settings/tags/${tag1.slug}"]`), 'highlights selected tag')
|
||||
.to.have.class('active');
|
||||
|
||||
// it shows selected tag form
|
||||
expect(find('.tag-settings-pane h4').text(), 'settings pane title')
|
||||
expect(find('.tag-settings-pane h4').textContent, 'settings pane title')
|
||||
.to.equal('Tag Settings');
|
||||
expect(find('.tag-settings-pane input[name="name"]').val(), 'loads correct tag into form')
|
||||
expect(find('.tag-settings-pane input[name="name"]').value, 'loads correct tag into form')
|
||||
.to.equal(tag1.name);
|
||||
|
||||
// click the second tag in the list
|
||||
await click('.tag-edit-button:last');
|
||||
let tagEditButtons = findAll('.tag-edit-button');
|
||||
await click(tagEditButtons[tagEditButtons.length - 1]);
|
||||
|
||||
// it navigates to selected tag
|
||||
expect(currentURL(), 'url after clicking tag').to.equal(`/settings/tags/${tag2.slug}`);
|
||||
|
||||
// it highlights selected tag
|
||||
expect(find(`a[href="/ghost/settings/tags/${tag2.slug}"]`).hasClass('active'), 'highlights selected tag')
|
||||
.to.be.true;
|
||||
expect(find(`a[href="/ghost/settings/tags/${tag2.slug}"]`), 'highlights selected tag')
|
||||
.to.have.class('active');
|
||||
|
||||
// it shows selected tag form
|
||||
expect(find('.tag-settings-pane input[name="name"]').val(), 'loads correct tag into form')
|
||||
expect(find('.tag-settings-pane input[name="name"]').value, 'loads correct tag into form')
|
||||
.to.equal(tag2.name);
|
||||
|
||||
// simulate up arrow press
|
||||
@ -158,8 +153,8 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
expect(currentURL(), 'url after keyboard up arrow').to.equal(`/settings/tags/${tag1.slug}`);
|
||||
|
||||
// it highlights selected tag
|
||||
expect(find(`a[href="/ghost/settings/tags/${tag1.slug}"]`).hasClass('active'), 'selects previous tag')
|
||||
.to.be.true;
|
||||
expect(find(`a[href="/ghost/settings/tags/${tag1.slug}"]`), 'selects previous tag')
|
||||
.to.have.class('active');
|
||||
|
||||
// simulate down arrow press
|
||||
run(() => {
|
||||
@ -173,21 +168,23 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
expect(currentURL(), 'url after keyboard down arrow').to.equal(`/settings/tags/${tag2.slug}`);
|
||||
|
||||
// it highlights selected tag
|
||||
expect(find(`a[href="/ghost/settings/tags/${tag2.slug}"]`).hasClass('active'), 'selects next tag')
|
||||
.to.be.true;
|
||||
expect(find(`a[href="/ghost/settings/tags/${tag2.slug}"]`), 'selects next tag')
|
||||
.to.have.class('active');
|
||||
|
||||
// trigger save
|
||||
await fillIn('.tag-settings-pane input[name="name"]', 'New Name');
|
||||
await triggerEvent('.tag-settings-pane input[name="name"]', 'blur');
|
||||
await blur('.tag-settings-pane input[name="name"]');
|
||||
|
||||
// extra timeout needed for Travis - sometimes it doesn't update
|
||||
// quick enough and an extra wait() call doesn't help
|
||||
await timeout(100);
|
||||
|
||||
// check we update with the data returned from the server
|
||||
expect(find('.settings-tag')[0].querySelector('.tag-title').textContent.trim(), 'tag list updates on save')
|
||||
let tags = findAll('.settings-tags .settings-tag');
|
||||
tag = tags[0];
|
||||
expect(tag.querySelector('.tag-title').textContent, 'tag list updates on save')
|
||||
.to.equal('New Name');
|
||||
expect(find('.tag-settings-pane input[name="name"]').val(), 'settings form updates on save')
|
||||
expect(find('.tag-settings-pane input[name="name"]').value, 'settings form updates on save')
|
||||
.to.equal('New Name');
|
||||
|
||||
// start new tag
|
||||
@ -197,18 +194,18 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
expect(currentURL(), 'new tag URL').to.equal('/settings/tags/new');
|
||||
|
||||
// it displays the new tag form
|
||||
expect(find('.tag-settings-pane h4').text(), 'settings pane title')
|
||||
expect(find('.tag-settings-pane h4').textContent, 'settings pane title')
|
||||
.to.equal('New Tag');
|
||||
|
||||
// all fields start blank
|
||||
find('.tag-settings-pane input, .tag-settings-pane textarea').each(function () {
|
||||
expect($(this).val(), `input field for ${$(this).attr('name')}`)
|
||||
findAll('.tag-settings-pane input, .tag-settings-pane textarea').forEach(function (elem) {
|
||||
expect(elem.value, `input field for ${elem.getAttribute('name')}`)
|
||||
.to.be.empty;
|
||||
});
|
||||
|
||||
// save new tag
|
||||
await fillIn('.tag-settings-pane input[name="name"]', 'New Tag');
|
||||
await triggerEvent('.tag-settings-pane input[name="name"]', 'blur');
|
||||
await blur('.tag-settings-pane input[name="name"]');
|
||||
|
||||
// extra timeout needed for FF on Linux - sometimes it doesn't update
|
||||
// quick enough, especially on Travis, and an extra wait() call
|
||||
@ -219,13 +216,17 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
expect(currentURL(), 'URL after tag creation').to.equal('/settings/tags/new-tag');
|
||||
|
||||
// it adds the tag to the list and selects
|
||||
expect(find('.settings-tags .settings-tag').length, 'tag list count after creation')
|
||||
tags = findAll('.settings-tags .settings-tag');
|
||||
tag = tags[1]; // second tag in list due to alphabetical ordering
|
||||
expect(tags.length, 'tag list count after creation')
|
||||
.to.equal(3);
|
||||
|
||||
// new tag will be second in the list due to alphabetical sorting
|
||||
expect(find('.settings-tags .settings-tag')[1].querySelector('.tag-title').textContent.trim(), 'new tag list item title')
|
||||
expect(findAll('.settings-tags .settings-tag')[1].querySelector('.tag-title').textContent.trim(), 'new tag list item title');
|
||||
expect(tag.querySelector('.tag-title').textContent, 'new tag list item title')
|
||||
.to.equal('New Tag');
|
||||
expect(find('a[href="/ghost/settings/tags/new-tag"]').hasClass('active'), 'highlights new tag')
|
||||
.to.be.true;
|
||||
expect(find('a[href="/ghost/settings/tags/new-tag"]'), 'highlights new tag')
|
||||
.to.have.class('active');
|
||||
|
||||
// delete tag
|
||||
await click('.settings-menu-delete-button');
|
||||
@ -235,7 +236,7 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
expect(currentURL(), 'URL after tag deletion').to.equal(`/settings/tags/${tag1.slug}`);
|
||||
|
||||
// it removes the tag from the list
|
||||
expect(find('.settings-tags .settings-tag').length, 'tag list count after deletion')
|
||||
expect(findAll('.settings-tags .settings-tag').length, 'tag list count after deletion')
|
||||
.to.equal(2);
|
||||
});
|
||||
|
||||
@ -243,7 +244,7 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
// skipped because it was failing most of the time on Travis
|
||||
// see https://github.com/TryGhost/Ghost/issues/8805
|
||||
it.skip('loads tag via slug when accessed directly', async function () {
|
||||
server.createList('tag', 2);
|
||||
this.server.createList('tag', 2);
|
||||
|
||||
await visit('/settings/tags/tag-1');
|
||||
|
||||
@ -253,20 +254,20 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
expect(currentURL(), 'URL after direct load').to.equal('/settings/tags/tag-1');
|
||||
|
||||
// it loads all other tags
|
||||
expect(find('.settings-tags .settings-tag').length, 'tag list count after direct load')
|
||||
expect(findAll('.settings-tags .settings-tag').length, 'tag list count after direct load')
|
||||
.to.equal(2);
|
||||
|
||||
// selects tag in list
|
||||
expect(find('a[href="/ghost/settings/tags/tag-1"]').hasClass('active'), 'highlights requested tag')
|
||||
expect(find('a[href="/ghost/settings/tags/tag-1"]').classList.contains('active'), 'highlights requested tag')
|
||||
.to.be.true;
|
||||
|
||||
// shows requested tag in settings pane
|
||||
expect(find('.tag-settings-pane input[name="name"]').val(), 'loads correct tag into form')
|
||||
expect(find('.tag-settings-pane input[name="name"]').value, 'loads correct tag into form')
|
||||
.to.equal('Tag 1');
|
||||
});
|
||||
|
||||
it('shows the internal tag label', async function () {
|
||||
server.create('tag', {name: '#internal-tag', slug: 'hash-internal-tag', visibility: 'internal'});
|
||||
this.server.create('tag', {name: '#internal-tag', slug: 'hash-internal-tag', visibility: 'internal'});
|
||||
|
||||
await visit('settings/tags/');
|
||||
|
||||
@ -275,18 +276,20 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
|
||||
expect(currentURL()).to.equal('/settings/tags/hash-internal-tag');
|
||||
|
||||
expect(find('.settings-tags .settings-tag').length, 'tag list count')
|
||||
expect(findAll('.settings-tags .settings-tag').length, 'tag list count')
|
||||
.to.equal(1);
|
||||
|
||||
expect(find('.settings-tags .settings-tag:first .label.label-blue').length, 'internal tag label')
|
||||
let tag = find('.settings-tags .settings-tag');
|
||||
|
||||
expect(tag.querySelectorAll('.label.label-blue').length, 'internal tag label')
|
||||
.to.equal(1);
|
||||
|
||||
expect(find('.settings-tags .settings-tag:first .label.label-blue').text().trim(), 'internal tag label text')
|
||||
expect(tag.querySelector('.label.label-blue').textContent.trim(), 'internal tag label text')
|
||||
.to.equal('internal');
|
||||
});
|
||||
|
||||
it('updates the URL when slug changes', async function () {
|
||||
server.createList('tag', 2);
|
||||
this.server.createList('tag', 2);
|
||||
|
||||
await visit('/settings/tags/tag-1');
|
||||
|
||||
@ -297,7 +300,7 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
|
||||
// update the slug
|
||||
await fillIn('.tag-settings-pane input[name="slug"]', 'test');
|
||||
await triggerEvent('.tag-settings-pane input[name="slug"]', 'blur');
|
||||
await blur('.tag-settings-pane input[name="slug"]');
|
||||
|
||||
// tests don't have a location.hash so we can only check that the
|
||||
// slug portion is updated correctly
|
||||
@ -305,7 +308,7 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
});
|
||||
|
||||
it('redirects to 404 when tag does not exist', async function () {
|
||||
server.get('/tags/slug/unknown/', function () {
|
||||
this.server.get('/tags/slug/unknown/', function () {
|
||||
return new Response(404, {'Content-Type': 'application/json'}, {errors: [{message: 'Tag not found.', errorType: 'NotFoundError'}]});
|
||||
});
|
||||
|
||||
@ -314,25 +317,27 @@ describe('Acceptance: Settings - Tags', function () {
|
||||
await visit('settings/tags/unknown');
|
||||
|
||||
errorReset();
|
||||
expect(currentPath()).to.equal('error404');
|
||||
expect(currentRouteName()).to.equal('error404');
|
||||
expect(currentURL()).to.equal('/settings/tags/unknown');
|
||||
});
|
||||
|
||||
it('sorts tags correctly', async function () {
|
||||
server.create('tag', {name: 'B - Second', slug: 'second'});
|
||||
server.create('tag', {name: 'Z - Last', slug: 'last'});
|
||||
server.create('tag', {name: 'A - First', slug: 'first'});
|
||||
this.server.create('tag', {name: 'B - Third', slug: 'third'});
|
||||
this.server.create('tag', {name: 'Z - Last', slug: 'last'});
|
||||
this.server.create('tag', {name: '#A - Second', slug: 'second'});
|
||||
this.server.create('tag', {name: 'A - First', slug: 'first'});
|
||||
|
||||
await visit('settings/tags');
|
||||
|
||||
// second wait is needed for the vertical-collection to settle
|
||||
await wait();
|
||||
|
||||
let tags = find('[data-test-tag]');
|
||||
let tags = findAll('[data-test-tag]');
|
||||
|
||||
expect(tags[0].querySelector('[data-test-name]').textContent.trim()).to.equal('A - First');
|
||||
expect(tags[1].querySelector('[data-test-name]').textContent.trim()).to.equal('B - Second');
|
||||
expect(tags[2].querySelector('[data-test-name]').textContent.trim()).to.equal('Z - Last');
|
||||
expect(tags[1].querySelector('[data-test-name]').textContent.trim()).to.equal('#A - Second');
|
||||
expect(tags[2].querySelector('[data-test-name]').textContent.trim()).to.equal('B - Third');
|
||||
expect(tags[3].querySelector('[data-test-name]').textContent.trim()).to.equal('Z - Last');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,64 +1,63 @@
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
beforeEach,
|
||||
describe,
|
||||
it
|
||||
} from 'mocha';
|
||||
import {click, currentURL, find, findAll, triggerEvent} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
describe('Acceptance: Settings - Integrations - Unsplash', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/integrations/unsplash');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/unsplash');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/unsplash');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/unsplash');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
});
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('it can activate/deactivate', async function () {
|
||||
@ -69,13 +68,13 @@ describe('Acceptance: Settings - Integrations - Unsplash', function () {
|
||||
|
||||
// verify we don't have an unsplash setting fixture loaded
|
||||
expect(
|
||||
server.db.settings.where({key: 'unsplash'}),
|
||||
this.server.db.settings.where({key: 'unsplash'}),
|
||||
'initial server settings'
|
||||
).to.be.empty;
|
||||
|
||||
// it's enabled by default when settings is empty
|
||||
expect(
|
||||
find('[data-test-checkbox="unsplash"]').prop('checked'),
|
||||
find('[data-test-checkbox="unsplash"]').checked,
|
||||
'checked by default'
|
||||
).to.be.true;
|
||||
|
||||
@ -83,12 +82,12 @@ describe('Acceptance: Settings - Integrations - Unsplash', function () {
|
||||
await click('[data-test-save-button]');
|
||||
|
||||
// server should now have an unsplash setting
|
||||
let [setting] = server.db.settings.where({key: 'unsplash'});
|
||||
let [setting] = this.server.db.settings.where({key: 'unsplash'});
|
||||
expect(setting, 'unsplash setting after save').to.exist;
|
||||
expect(setting.value).to.equal('{"isActive":true}');
|
||||
|
||||
// disable
|
||||
await click(find('[data-test-checkbox="unsplash"]'));
|
||||
await click('[data-test-checkbox="unsplash"]');
|
||||
|
||||
// save via CMD-S shortcut
|
||||
await triggerEvent('.gh-app', 'keydown', {
|
||||
@ -98,7 +97,7 @@ describe('Acceptance: Settings - Integrations - Unsplash', function () {
|
||||
});
|
||||
|
||||
// server should have an updated setting
|
||||
[setting] = server.db.settings.where({key: 'unsplash'});
|
||||
[setting] = this.server.db.settings.where({key: 'unsplash'});
|
||||
expect(setting.value).to.equal('{"isActive":false}');
|
||||
});
|
||||
|
||||
@ -109,20 +108,20 @@ describe('Acceptance: Settings - Integrations - Unsplash', function () {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/unsplash');
|
||||
|
||||
expect(
|
||||
find('[data-test-checkbox="unsplash"]').prop('checked'),
|
||||
find('[data-test-checkbox="unsplash"]').checked,
|
||||
'checked by default'
|
||||
).to.be.true;
|
||||
|
||||
await click('[data-test-checkbox="unsplash"]');
|
||||
|
||||
expect(find('[data-test-checkbox="unsplash"]').prop('checked'), 'Unsplash checkbox').to.be.false;
|
||||
expect(find('[data-test-checkbox="unsplash"]').checked, 'Unsplash checkbox').to.be.false;
|
||||
|
||||
await visit('/settings/labs');
|
||||
|
||||
expect(find('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
expect(findAll('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
|
||||
// Leave without saving
|
||||
await (click('.fullscreen-modal [data-test-leave-button]'), 'leave without saving');
|
||||
await click('.fullscreen-modal [data-test-leave-button]');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/labs');
|
||||
|
||||
@ -131,7 +130,7 @@ describe('Acceptance: Settings - Integrations - Unsplash', function () {
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/unsplash');
|
||||
|
||||
// settings were not saved
|
||||
expect(find('[data-test-checkbox="unsplash"]').prop('checked'), 'Unsplash checkbox').to.be.true;
|
||||
expect(find('[data-test-checkbox="unsplash"]').checked, 'Unsplash checkbox').to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,68 +1,62 @@
|
||||
import destroyApp from '../../helpers/destroy-app';
|
||||
import startApp from '../../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
it
|
||||
} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import {currentURL} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../../helpers/visit';
|
||||
|
||||
describe('Acceptance: Settings - Integrations - Zapier', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/settings/integrations/zapier');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/zapier');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/zapier');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects to team page when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/settings/integrations/zapier');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
});
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('it loads', async function () {
|
||||
|
@ -1,27 +1,22 @@
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import moment from 'moment';
|
||||
import startApp from '../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {Response} from 'ember-cli-mirage';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from '../helpers/ember-simple-auth';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {blur, click, currentURL, fillIn, find, findAll} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../helpers/visit';
|
||||
|
||||
describe('Acceptance: Setup', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects if already authenticated', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
await authenticateSession(application);
|
||||
await authenticateSession();
|
||||
|
||||
await visit('/setup/one');
|
||||
expect(currentURL()).to.equal('/');
|
||||
@ -35,7 +30,7 @@ describe('Acceptance: Setup', function () {
|
||||
|
||||
it('redirects to signin if already set up', async function () {
|
||||
// mimick an already setup blog
|
||||
server.get('/authentication/setup/', function () {
|
||||
this.server.get('/authentication/setup/', function () {
|
||||
return {
|
||||
setup: [
|
||||
{status: true}
|
||||
@ -43,7 +38,7 @@ describe('Acceptance: Setup', function () {
|
||||
};
|
||||
});
|
||||
|
||||
await invalidateSession(application);
|
||||
await invalidateSession();
|
||||
|
||||
await visit('/setup');
|
||||
expect(currentURL()).to.equal('/signin');
|
||||
@ -52,7 +47,7 @@ describe('Acceptance: Setup', function () {
|
||||
describe('with a new blog', function () {
|
||||
beforeEach(function () {
|
||||
// mimick a new blog
|
||||
server.get('/authentication/setup/', function () {
|
||||
this.server.get('/authentication/setup/', function () {
|
||||
return {
|
||||
setup: [
|
||||
{status: false}
|
||||
@ -62,8 +57,8 @@ describe('Acceptance: Setup', function () {
|
||||
});
|
||||
|
||||
it('has a successful happy path', async function () {
|
||||
invalidateSession(application);
|
||||
server.loadFixtures('roles');
|
||||
await invalidateSession();
|
||||
this.server.loadFixtures('roles');
|
||||
|
||||
await visit('/setup');
|
||||
|
||||
@ -72,16 +67,15 @@ describe('Acceptance: Setup', function () {
|
||||
.to.equal('/setup/one');
|
||||
|
||||
// it highlights first step
|
||||
expect(find('.gh-flow-nav .step:first-of-type').hasClass('active'))
|
||||
.to.be.true;
|
||||
expect(find('.gh-flow-nav .step:nth-of-type(2)').hasClass('active'))
|
||||
.to.be.false;
|
||||
expect(find('.gh-flow-nav .step:nth-of-type(3)').hasClass('active'))
|
||||
.to.be.false;
|
||||
let stepIcons = findAll('.gh-flow-nav .step');
|
||||
expect(stepIcons.length, 'sanity check: three steps').to.equal(3);
|
||||
expect(stepIcons[0], 'first step').to.have.class('active');
|
||||
expect(stepIcons[1], 'second step').to.not.have.class('active');
|
||||
expect(stepIcons[2], 'third step').to.not.have.class('active');
|
||||
|
||||
// it displays download count (count increments for each ajax call
|
||||
// and polling is disabled in testing so our count should be "1"
|
||||
expect(find('.gh-flow-content em').text().trim()).to.equal('1');
|
||||
expect(find('.gh-flow-content em').textContent.trim()).to.equal('1');
|
||||
|
||||
await click('.gh-btn-green');
|
||||
|
||||
@ -92,21 +86,21 @@ describe('Acceptance: Setup', function () {
|
||||
// email field is focused by default
|
||||
// NOTE: $('x').is(':focus') doesn't work in phantomjs CLI runner
|
||||
// https://github.com/ariya/phantomjs/issues/10427
|
||||
expect(find('[data-test-blog-title-input]').get(0) === document.activeElement, 'blog title has focus')
|
||||
expect(findAll('[data-test-blog-title-input]')[0] === document.activeElement, 'blog title has focus')
|
||||
.to.be.true;
|
||||
|
||||
await click('.gh-btn-green');
|
||||
|
||||
// it marks fields as invalid
|
||||
expect(find('.form-group.error').length, 'number of invalid fields')
|
||||
expect(findAll('.form-group.error').length, 'number of invalid fields')
|
||||
.to.equal(4);
|
||||
|
||||
// it displays error messages
|
||||
expect(find('.error .response').length, 'number of in-line validation messages')
|
||||
expect(findAll('.error .response').length, 'number of in-line validation messages')
|
||||
.to.equal(4);
|
||||
|
||||
// it displays main error
|
||||
expect(find('.main-error').length, 'main error is displayed')
|
||||
expect(findAll('.main-error').length, 'main error is displayed')
|
||||
.to.equal(1);
|
||||
|
||||
// enter valid details and submit
|
||||
@ -121,14 +115,14 @@ describe('Acceptance: Setup', function () {
|
||||
.to.equal('/setup/three');
|
||||
|
||||
// submit button is "disabled"
|
||||
expect(find('button[type="submit"]').hasClass('gh-btn-green'), 'invite button with no emails is white')
|
||||
expect(find('button[type="submit"]').classList.contains('gh-btn-green'), 'invite button with no emails is white')
|
||||
.to.be.false;
|
||||
|
||||
// fill in a valid email
|
||||
await fillIn('[name="users"]', 'new-user@example.com');
|
||||
|
||||
// submit button is "enabled"
|
||||
expect(find('button[type="submit"]').hasClass('gh-btn-green'), 'invite button is green with valid email address')
|
||||
expect(find('button[type="submit"]').classList.contains('gh-btn-green'), 'invite button is green with valid email address')
|
||||
.to.be.true;
|
||||
|
||||
// submit the invite form
|
||||
@ -139,17 +133,17 @@ describe('Acceptance: Setup', function () {
|
||||
.to.equal('/');
|
||||
|
||||
// it displays success alert
|
||||
expect(find('.gh-alert-green').length, 'number of success alerts')
|
||||
expect(findAll('.gh-alert-green').length, 'number of success alerts')
|
||||
.to.equal(1);
|
||||
});
|
||||
|
||||
it('handles validation errors in step 2', async function () {
|
||||
let postCount = 0;
|
||||
|
||||
invalidateSession(application);
|
||||
server.loadFixtures('roles');
|
||||
await invalidateSession();
|
||||
this.server.loadFixtures('roles');
|
||||
|
||||
server.post('/authentication/setup', function () {
|
||||
this.server.post('/authentication/setup', function () {
|
||||
postCount += 1;
|
||||
|
||||
// validation error
|
||||
@ -174,7 +168,7 @@ describe('Acceptance: Setup', function () {
|
||||
await click('.gh-btn-green');
|
||||
|
||||
// non-server validation
|
||||
expect(find('.main-error').text().trim(), 'error text')
|
||||
expect(find('.main-error').textContent.trim(), 'error text')
|
||||
.to.not.be.empty;
|
||||
|
||||
await fillIn('[data-test-email-input]', 'test@example.com');
|
||||
@ -185,22 +179,22 @@ describe('Acceptance: Setup', function () {
|
||||
// first post - simulated validation error
|
||||
await click('.gh-btn-green');
|
||||
|
||||
expect(find('.main-error').text().trim(), 'error text')
|
||||
expect(find('.main-error').textContent.trim(), 'error text')
|
||||
.to.equal('Server response message');
|
||||
|
||||
// second post - simulated server error
|
||||
await click('.gh-btn-green');
|
||||
|
||||
expect(find('.main-error').text().trim(), 'error text')
|
||||
expect(find('.main-error').textContent.trim(), 'error text')
|
||||
.to.be.empty;
|
||||
|
||||
expect(find('.gh-alert-red').length, 'number of alerts')
|
||||
expect(findAll('.gh-alert-red').length, 'number of alerts')
|
||||
.to.equal(1);
|
||||
});
|
||||
|
||||
it('handles invalid origin error on step 2', async function () {
|
||||
// mimick the API response for an invalid origin
|
||||
server.post('/session', function () {
|
||||
this.server.post('/session', function () {
|
||||
return new Response(401, {}, {
|
||||
errors: [
|
||||
{
|
||||
@ -211,8 +205,8 @@ describe('Acceptance: Setup', function () {
|
||||
});
|
||||
});
|
||||
|
||||
invalidateSession(application);
|
||||
server.loadFixtures('roles');
|
||||
await invalidateSession();
|
||||
this.server.loadFixtures('roles');
|
||||
|
||||
await visit('/setup/two');
|
||||
await fillIn('[data-test-email-input]', 'test@example.com');
|
||||
@ -222,10 +216,10 @@ describe('Acceptance: Setup', function () {
|
||||
await click('.gh-btn-green');
|
||||
|
||||
// button should not be spinning
|
||||
expect(find('.gh-btn-green .spinner').length, 'button has spinner')
|
||||
expect(findAll('.gh-btn-green .spinner').length, 'button has spinner')
|
||||
.to.equal(0);
|
||||
// we should show an error message
|
||||
expect(find('.main-error').text(), 'error text')
|
||||
expect(find('.main-error').textContent, 'error text')
|
||||
.to.have.string('Access Denied from url: unknown.com. Please use the url configured in config.js.');
|
||||
});
|
||||
|
||||
@ -234,10 +228,10 @@ describe('Acceptance: Setup', function () {
|
||||
let postCount = 0;
|
||||
let button, formGroup;
|
||||
|
||||
invalidateSession(application);
|
||||
server.loadFixtures('roles');
|
||||
await invalidateSession();
|
||||
this.server.loadFixtures('roles');
|
||||
|
||||
server.post('/invites/', function ({invites}) {
|
||||
this.server.post('/invites/', function ({invites}) {
|
||||
let attrs = this.normalizedRequestAttrs();
|
||||
|
||||
postCount += 1;
|
||||
@ -278,66 +272,66 @@ describe('Acceptance: Setup', function () {
|
||||
formGroup = find('.gh-flow-invite .form-group');
|
||||
button = find('.gh-flow-invite button[type="submit"]');
|
||||
|
||||
expect(formGroup.hasClass('error'), 'default field has error class')
|
||||
.to.be.false;
|
||||
expect(formGroup, 'default field has error class')
|
||||
.to.not.have.class('error');
|
||||
|
||||
expect(button.text().trim(), 'default button text')
|
||||
.to.equal('Invite some users');
|
||||
expect(button.textContent, 'default button text')
|
||||
.to.have.string('Invite some users');
|
||||
|
||||
expect(button.hasClass('gh-btn-minor'), 'default button is disabled')
|
||||
.to.be.true;
|
||||
expect(button, 'default button is disabled')
|
||||
.to.have.class('gh-btn-minor');
|
||||
|
||||
// no users submitted state
|
||||
await click('.gh-flow-invite button[type="submit"]');
|
||||
|
||||
expect(formGroup.hasClass('error'), 'no users submitted field has error class')
|
||||
.to.be.true;
|
||||
expect(formGroup, 'no users submitted field has error class')
|
||||
.to.have.class('error');
|
||||
|
||||
expect(button.text().trim(), 'no users submitted button text')
|
||||
.to.equal('No users to invite');
|
||||
expect(button.textContent, 'no users submitted button text')
|
||||
.to.have.string('No users to invite');
|
||||
|
||||
expect(button.hasClass('gh-btn-minor'), 'no users submitted button is disabled')
|
||||
.to.be.true;
|
||||
expect(button, 'no users submitted button is disabled')
|
||||
.to.have.class('gh-btn-minor');
|
||||
|
||||
// single invalid email
|
||||
await fillIn(input, 'invalid email');
|
||||
await triggerEvent(input, 'blur');
|
||||
await blur(input);
|
||||
|
||||
expect(formGroup.hasClass('error'), 'invalid field has error class')
|
||||
.to.be.true;
|
||||
expect(formGroup, 'invalid field has error class')
|
||||
.to.have.class('error');
|
||||
|
||||
expect(button.text().trim(), 'single invalid button text')
|
||||
.to.equal('1 invalid email address');
|
||||
expect(button.textContent, 'single invalid button text')
|
||||
.to.have.string('1 invalid email address');
|
||||
|
||||
expect(button.hasClass('gh-btn-minor'), 'invalid email button is disabled')
|
||||
.to.be.true;
|
||||
expect(button, 'invalid email button is disabled')
|
||||
.to.have.class('gh-btn-minor');
|
||||
|
||||
// multiple invalid emails
|
||||
await fillIn(input, 'invalid email\nanother invalid address');
|
||||
await triggerEvent(input, 'blur');
|
||||
await blur(input);
|
||||
|
||||
expect(button.text().trim(), 'multiple invalid button text')
|
||||
.to.equal('2 invalid email addresses');
|
||||
expect(button.textContent, 'multiple invalid button text')
|
||||
.to.have.string('2 invalid email addresses');
|
||||
|
||||
// single valid email
|
||||
await fillIn(input, 'invited@example.com');
|
||||
await triggerEvent(input, 'blur');
|
||||
await blur(input);
|
||||
|
||||
expect(formGroup.hasClass('error'), 'valid field has error class')
|
||||
.to.be.false;
|
||||
expect(formGroup, 'valid field has error class')
|
||||
.to.not.have.class('error');
|
||||
|
||||
expect(button.text().trim(), 'single valid button text')
|
||||
.to.equal('Invite 1 user');
|
||||
expect(button.textContent, 'single valid button text')
|
||||
.to.have.string('Invite 1 user');
|
||||
|
||||
expect(button.hasClass('gh-btn-green'), 'valid email button is enabled')
|
||||
.to.be.true;
|
||||
expect(button, 'valid email button is enabled')
|
||||
.to.have.class('gh-btn-green');
|
||||
|
||||
// multiple valid emails
|
||||
await fillIn(input, 'invited1@example.com\ninvited2@example.com');
|
||||
await triggerEvent(input, 'blur');
|
||||
await blur(input);
|
||||
|
||||
expect(button.text().trim(), 'multiple valid button text')
|
||||
.to.equal('Invite 2 users');
|
||||
expect(button.textContent, 'multiple valid button text')
|
||||
.to.have.string('Invite 2 users');
|
||||
|
||||
// submit invitations with simulated failure on 1 invite
|
||||
await click('.gh-btn-green');
|
||||
@ -347,11 +341,11 @@ describe('Acceptance: Setup', function () {
|
||||
.to.equal('/');
|
||||
|
||||
// it displays success alert
|
||||
expect(find('.gh-alert-green').length, 'number of success alerts')
|
||||
expect(findAll('.gh-alert-green').length, 'number of success alerts')
|
||||
.to.equal(1);
|
||||
|
||||
// it displays failure alert
|
||||
expect(find('.gh-alert-red').length, 'number of failure alerts')
|
||||
expect(findAll('.gh-alert-red').length, 'number of failure alerts')
|
||||
.to.equal(1);
|
||||
});
|
||||
});
|
||||
|
@ -1,31 +1,25 @@
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import startApp from '../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {Response} from 'ember-cli-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
it
|
||||
} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from '../helpers/ember-simple-auth';
|
||||
import {click, currentURL, fillIn, find, findAll} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../helpers/visit';
|
||||
|
||||
describe('Acceptance: Signin', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects if already authenticated', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
await authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/signin');
|
||||
|
||||
expect(currentURL(), 'current url').to.equal('/');
|
||||
@ -33,10 +27,10 @@ describe('Acceptance: Signin', function () {
|
||||
|
||||
describe('when attempting to signin', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
server.post('/session', function (schema, {requestBody}) {
|
||||
this.server.post('/session', function (schema, {requestBody}) {
|
||||
let {
|
||||
username,
|
||||
password
|
||||
@ -58,22 +52,22 @@ describe('Acceptance: Signin', function () {
|
||||
});
|
||||
|
||||
it('errors correctly', async function () {
|
||||
await invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/signin');
|
||||
|
||||
expect(currentURL(), 'signin url').to.equal('/signin');
|
||||
|
||||
expect(find('input[name="identification"]').length, 'email input field')
|
||||
expect(findAll('input[name="identification"]').length, 'email input field')
|
||||
.to.equal(1);
|
||||
expect(find('input[name="password"]').length, 'password input field')
|
||||
expect(findAll('input[name="password"]').length, 'password input field')
|
||||
.to.equal(1);
|
||||
|
||||
await click('.gh-btn-blue');
|
||||
|
||||
expect(find('.form-group.error').length, 'number of invalid fields')
|
||||
expect(findAll('.form-group.error').length, 'number of invalid fields')
|
||||
.to.equal(2);
|
||||
|
||||
expect(find('.main-error').length, 'main error is displayed')
|
||||
expect(findAll('.main-error').length, 'main error is displayed')
|
||||
.to.equal(1);
|
||||
|
||||
await fillIn('[name="identification"]', 'test@example.com');
|
||||
@ -82,15 +76,15 @@ describe('Acceptance: Signin', function () {
|
||||
|
||||
expect(currentURL(), 'current url').to.equal('/signin');
|
||||
|
||||
expect(find('.main-error').length, 'main error is displayed')
|
||||
expect(findAll('.main-error').length, 'main error is displayed')
|
||||
.to.equal(1);
|
||||
|
||||
expect(find('.main-error').text().trim(), 'main error text')
|
||||
expect(find('.main-error').textContent.trim(), 'main error text')
|
||||
.to.equal('Invalid Password');
|
||||
});
|
||||
|
||||
it('submits successfully', async function () {
|
||||
invalidateSession(application);
|
||||
invalidateSession();
|
||||
|
||||
await visit('/signin');
|
||||
expect(currentURL(), 'current url').to.equal('/signin');
|
||||
|
@ -1,25 +1,18 @@
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import startApp from '../helpers/start-app';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
it
|
||||
} from 'mocha';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {blur, click, currentRouteName, fillIn, find} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../helpers/visit';
|
||||
|
||||
describe('Acceptance: Signup', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('can signup successfully', async function () {
|
||||
let server = this.server;
|
||||
|
||||
server.get('/authentication/invitation', function () {
|
||||
return {
|
||||
invitation: [{valid: true}]
|
||||
@ -48,118 +41,156 @@ describe('Acceptance: Signup', function () {
|
||||
// "1470346017929|kevin+test2@ghost.org|2cDnQc3g7fQTj9nNK4iGPSGfvomkLdXf68FuWgS66Ug="
|
||||
await visit('/signup/MTQ3MDM0NjAxNzkyOXxrZXZpbit0ZXN0MkBnaG9zdC5vcmd8MmNEblFjM2c3ZlFUajluTks0aUdQU0dmdm9ta0xkWGY2OEZ1V2dTNjZVZz0');
|
||||
|
||||
expect(currentPath()).to.equal('signup');
|
||||
expect(currentRouteName()).to.equal('signup');
|
||||
|
||||
// email address should be pre-filled and disabled
|
||||
expect(
|
||||
find('input[name="email"]').val(),
|
||||
find('input[name="email"]').value,
|
||||
'email field value'
|
||||
).to.equal('kevin+test2@ghost.org');
|
||||
|
||||
expect(
|
||||
find('input[name="email"]').is(':disabled'),
|
||||
find('input[name="email"]').matches(':disabled'),
|
||||
'email field is disabled'
|
||||
).to.be.true;
|
||||
|
||||
// focus out in Name field triggers inline error
|
||||
await triggerEvent('input[name="name"]', 'blur');
|
||||
await blur('input[name="name"]');
|
||||
|
||||
expect(
|
||||
find('input[name="name"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="name"]').closest('.form-group'),
|
||||
'name field group has error class when empty'
|
||||
).to.be.true;
|
||||
).to.have.class('error');
|
||||
|
||||
expect(
|
||||
find('input[name="name"]').closest('.form-group').find('.response').text().trim(),
|
||||
find('input[name="name"]').closest('.form-group').querySelector('.response').textContent,
|
||||
'name inline-error text'
|
||||
).to.match(/Please enter a name/);
|
||||
).to.have.string('Please enter a name');
|
||||
|
||||
// entering text in Name field clears error
|
||||
await fillIn('input[name="name"]', 'Test User');
|
||||
await triggerEvent('input[name="name"]', 'blur');
|
||||
await blur('input[name="name"]');
|
||||
|
||||
expect(
|
||||
find('input[name="name"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="name"]').closest('.form-group'),
|
||||
'name field loses error class after text input'
|
||||
).to.be.false;
|
||||
).to.not.have.class('error');
|
||||
|
||||
expect(
|
||||
find('input[name="name"]').closest('.form-group').find('.response').text().trim(),
|
||||
find('input[name="name"]').closest('.form-group').querySelector('.response').textContent.trim(),
|
||||
'name field error is removed after text input'
|
||||
).to.equal('');
|
||||
).to.be.empty;
|
||||
|
||||
// check password validation
|
||||
// focus out in password field triggers inline error
|
||||
// no password
|
||||
await triggerEvent('input[name="password"]', 'blur');
|
||||
await click('input[name="password"]');
|
||||
await blur();
|
||||
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="password"]').closest('.form-group'),
|
||||
'password field group has error class when empty'
|
||||
).to.be.true;
|
||||
).to.have.class('error');
|
||||
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').find('.response').text().trim(),
|
||||
find('input[name="password"]').closest('.form-group').querySelector('.response').textContent,
|
||||
'password field error text'
|
||||
).to.match(/must be at least 10 characters/);
|
||||
).to.have.string('must be at least 10 characters');
|
||||
|
||||
// password too short
|
||||
await fillIn('input[name="password"]', 'short');
|
||||
await triggerEvent('input[name="password"]', 'blur');
|
||||
await blur('input[name="password"]');
|
||||
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').find('.response').text().trim(),
|
||||
find('input[name="password"]').closest('.form-group').querySelector('.response').textContent,
|
||||
'password field error text'
|
||||
).to.match(/must be at least 10 characters/);
|
||||
).to.have.string('must be at least 10 characters');
|
||||
|
||||
// password must not be a bad password
|
||||
await fillIn('input[name="password"]', '1234567890');
|
||||
await triggerEvent('input[name="password"]', 'blur');
|
||||
await blur('input[name="password"]');
|
||||
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').find('.response').text().trim(),
|
||||
find('input[name="password"]').closest('.form-group').querySelector('.response').textContent,
|
||||
'password field error text'
|
||||
).to.match(/you cannot use an insecure password/);
|
||||
).to.have.string('you cannot use an insecure password');
|
||||
|
||||
// password must not be a disallowed password
|
||||
await fillIn('input[name="password"]', 'password99');
|
||||
await triggerEvent('input[name="password"]', 'blur');
|
||||
await blur('input[name="password"]');
|
||||
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').find('.response').text().trim(),
|
||||
find('input[name="password"]').closest('.form-group').querySelector('.response').textContent,
|
||||
'password field error text'
|
||||
).to.match(/you cannot use an insecure password/);
|
||||
).to.have.string('you cannot use an insecure password');
|
||||
|
||||
// password must not have repeating characters
|
||||
await fillIn('input[name="password"]', '2222222222');
|
||||
await triggerEvent('input[name="password"]', 'blur');
|
||||
await blur('input[name="password"]');
|
||||
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').find('.response').text().trim(),
|
||||
find('input[name="password"]').closest('.form-group').querySelector('.response').textContent,
|
||||
'password field error text'
|
||||
).to.match(/you cannot use an insecure password/);
|
||||
).to.have.string('you cannot use an insecure password');
|
||||
|
||||
// entering valid text in Password field clears error
|
||||
await fillIn('input[name="password"]', 'thisissupersafe');
|
||||
await triggerEvent('input[name="password"]', 'blur');
|
||||
await blur('input[name="password"]');
|
||||
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').hasClass('error'),
|
||||
find('input[name="password"]').closest('.form-group'),
|
||||
'password field loses error class after text input'
|
||||
).to.be.false;
|
||||
).to.not.have.class('error');
|
||||
|
||||
expect(
|
||||
find('input[name="password"]').closest('.form-group').find('.response').text().trim(),
|
||||
find('input[name="password"]').closest('.form-group').querySelector('.response').textContent.trim(),
|
||||
'password field error is removed after text input'
|
||||
).to.equal('');
|
||||
|
||||
// submitting sends correct details and redirects to content screen
|
||||
await click('.gh-btn-green');
|
||||
|
||||
expect(currentPath()).to.equal('posts.index');
|
||||
expect(currentRouteName()).to.equal('posts.index');
|
||||
});
|
||||
|
||||
it('redirects if already logged in');
|
||||
it('redirects with alert on invalid token');
|
||||
it('redirects with alert on non-existant or expired token');
|
||||
it('redirects if already logged in', async function () {
|
||||
this.server.get('/authentication/invitation', function () {
|
||||
return {
|
||||
invitation: [{valid: true}]
|
||||
};
|
||||
});
|
||||
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
await authenticateSession();
|
||||
// token details:
|
||||
// "1470346017929|kevin+test2@ghost.org|2cDnQc3g7fQTj9nNK4iGPSGfvomkLdXf68FuWgS66Ug="
|
||||
await visit('/signup/MTQ3MDM0NjAxNzkyOXxrZXZpbit0ZXN0MkBnaG9zdC5vcmd8MmNEblFjM2c3ZlFUajluTks0aUdQU0dmdm9ta0xkWGY2OEZ1V2dTNjZVZz0');
|
||||
|
||||
expect(currentRouteName()).to.equal('posts.index');
|
||||
expect(find('.gh-alert-content').textContent).to.have.string('sign out to register');
|
||||
});
|
||||
|
||||
it('redirects with alert on invalid token', async function () {
|
||||
await invalidateSession();
|
||||
await visit('/signup/---invalid---');
|
||||
|
||||
expect(currentRouteName()).to.equal('signin');
|
||||
expect(find('.gh-alert-content').textContent).to.have.string('Invalid token');
|
||||
});
|
||||
|
||||
it('redirects with alert on non-existant or expired token', async function () {
|
||||
this.server.get('/authentication/invitation', function () {
|
||||
return {
|
||||
invitation: [{valid: false}]
|
||||
};
|
||||
});
|
||||
|
||||
await invalidateSession();
|
||||
await visit('/signup/expired');
|
||||
|
||||
expect(currentRouteName()).to.equal('signin');
|
||||
expect(find('.gh-alert-content').textContent).to.have.string('not exist');
|
||||
});
|
||||
});
|
||||
|
@ -1,79 +1,76 @@
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import startApp from '../helpers/start-app';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from 'ghost-admin/tests/helpers/ember-simple-auth';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {beforeEach, describe, it} from 'mocha';
|
||||
import {click, currentRouteName, currentURL, fillIn, find, findAll} from '@ember/test-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {fileUpload} from '../helpers/file-upload';
|
||||
import {findAllWithText, findWithText} from '../helpers/find';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../helpers/visit';
|
||||
|
||||
describe('Acceptance: Subscribers', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/subscribers');
|
||||
|
||||
expect(currentURL()).to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects editors to posts', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role]});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/subscribers');
|
||||
|
||||
expect(currentURL()).to.equal('/');
|
||||
expect(find('[data-test-nav="subscribers"]').length, 'sidebar link is visible')
|
||||
.to.equal(0);
|
||||
expect(find('[data-test-nav="subscribers"]'), 'sidebar link')
|
||||
.to.not.exist;
|
||||
});
|
||||
|
||||
it('redirects authors to posts', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role]});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/subscribers');
|
||||
|
||||
expect(currentURL()).to.equal('/');
|
||||
expect(find('[data-test-nav="subscribers"]').length, 'sidebar link is visible')
|
||||
.to.equal(0);
|
||||
expect(find('[data-test-nav="subscribers"]'), 'sidebar link')
|
||||
.to.not.exist;
|
||||
});
|
||||
|
||||
it('redirects contributors to posts', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role]});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/subscribers');
|
||||
|
||||
expect(currentURL()).to.equal('/');
|
||||
expect(find('[data-test-nav="subscribers"]').length, 'sidebar link is visible')
|
||||
.to.equal(0);
|
||||
expect(find('[data-test-nav="subscribers"]'), 'sidebar link')
|
||||
.to.not.exist;
|
||||
});
|
||||
|
||||
describe('an admin', function () {
|
||||
beforeEach(function () {
|
||||
let role = server.create('role', {name: 'Administrator'});
|
||||
server.create('user', {roles: [role]});
|
||||
beforeEach(async function () {
|
||||
let role = this.server.create('role', {name: 'Administrator'});
|
||||
this.server.create('user', {roles: [role]});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('can manage subscribers', async function () {
|
||||
server.createList('subscriber', 40);
|
||||
this.server.createList('subscriber', 40);
|
||||
|
||||
await visit('/');
|
||||
await click('[data-test-nav="subscribers"]');
|
||||
|
||||
// it navigates to the correct page
|
||||
expect(currentPath()).to.equal('subscribers.index');
|
||||
expect(currentRouteName()).to.equal('subscribers.index');
|
||||
|
||||
// it has correct page title
|
||||
expect(document.title, 'page title')
|
||||
@ -83,33 +80,33 @@ describe('Acceptance: Subscribers', function () {
|
||||
// TODO: latest ember-in-viewport causes infinite scroll issues with
|
||||
// FF here where it loads two pages straight away so we need to check
|
||||
// if rows are greater than or equal to a single page
|
||||
expect(find('.subscribers-table .lt-body .lt-row').length, 'number of subscriber rows')
|
||||
expect(findAll('.subscribers-table .lt-body .lt-row').length, 'number of subscriber rows')
|
||||
.to.be.at.least(30);
|
||||
|
||||
// it shows the total number of subscribers
|
||||
expect(find('[data-test-total-subscribers]').text().trim(), 'displayed subscribers total')
|
||||
expect(find('[data-test-total-subscribers]').textContent.trim(), 'displayed subscribers total')
|
||||
.to.equal('(40)');
|
||||
|
||||
// it defaults to sorting by created_at desc
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.order).to.equal('created_at desc');
|
||||
|
||||
let createdAtHeader = find('.subscribers-table th:contains("Subscription Date")');
|
||||
expect(createdAtHeader.hasClass('is-sorted'), 'createdAt column is sorted')
|
||||
.to.be.true;
|
||||
expect(createdAtHeader.find('.gh-icon-descending').length, 'createdAt column has descending icon')
|
||||
.to.equal(1);
|
||||
let createdAtHeader = findWithText('.subscribers-table th', 'Subscription Date');
|
||||
expect(createdAtHeader, 'createdAt column is sorted')
|
||||
.to.have.class('is-sorted');
|
||||
expect(createdAtHeader.querySelectorAll('.gh-icon-descending'), 'createdAt column has descending icon')
|
||||
.to.exist;
|
||||
|
||||
// click the column to re-order
|
||||
await click('th:contains("Subscription Date")');
|
||||
await click(findWithText('th', 'Subscription Date'));
|
||||
|
||||
// it flips the directions and re-fetches
|
||||
[lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
expect(lastRequest.queryParams.order).to.equal('created_at asc');
|
||||
|
||||
createdAtHeader = find('.subscribers-table th:contains("Subscription Date")');
|
||||
expect(createdAtHeader.find('.gh-icon-ascending').length, 'createdAt column has ascending icon')
|
||||
.to.equal(1);
|
||||
createdAtHeader = findWithText('.subscribers-table th', 'Subscription Date');
|
||||
expect(createdAtHeader.querySelector('.gh-icon-ascending'), 'createdAt column has ascending icon')
|
||||
.to.exist;
|
||||
|
||||
// TODO: scroll test disabled as ember-light-table doesn't calculate
|
||||
// the scroll trigger element's positioning against the scroll
|
||||
@ -126,31 +123,31 @@ describe('Acceptance: Subscribers', function () {
|
||||
// .to.equal(40);
|
||||
|
||||
// click the add subscriber button
|
||||
await click('.gh-btn:contains("Add Subscriber")');
|
||||
await click('[data-test-link="add-subscriber"]');
|
||||
|
||||
// it displays the add subscriber modal
|
||||
expect(find('.fullscreen-modal').length, 'add subscriber modal displayed')
|
||||
.to.equal(1);
|
||||
expect(find('[data-test-modal="new-subscriber"]'), 'add subscriber modal displayed')
|
||||
.to.exist;
|
||||
|
||||
// cancel the modal
|
||||
await click('.fullscreen-modal .gh-btn:contains("Cancel")');
|
||||
await click('[data-test-button="cancel-new-subscriber"]');
|
||||
|
||||
// it closes the add subscriber modal
|
||||
expect(find('.fullscreen-modal').length, 'add subscriber modal displayed after cancel')
|
||||
.to.equal(0);
|
||||
expect(find('[data-test-modal]'), 'add subscriber modal displayed after cancel')
|
||||
.to.not.exist;
|
||||
|
||||
// save a new subscriber
|
||||
await click('.gh-btn:contains("Add Subscriber")');
|
||||
await fillIn('.fullscreen-modal input[name="email"]', 'test@example.com');
|
||||
await click('.fullscreen-modal .gh-btn:contains("Add")');
|
||||
await click('[data-test-link="add-subscriber"]');
|
||||
await fillIn('[data-test-input="new-subscriber-email"]', 'test@example.com');
|
||||
await click('[data-test-button="create-subscriber"]');
|
||||
|
||||
// the add subscriber modal is closed
|
||||
expect(find('.fullscreen-modal').length, 'add subscriber modal displayed after save')
|
||||
.to.equal(0);
|
||||
expect(find('[data-test-modal]'), 'add subscriber modal displayed after save')
|
||||
.to.not.exist;
|
||||
|
||||
// the subscriber is added to the table
|
||||
expect(find('.subscribers-table .lt-body .lt-row:first-of-type .lt-cell:first-of-type').text().trim(), 'first email in list after addition')
|
||||
.to.equal('test@example.com');
|
||||
expect(find('.subscribers-table .lt-body .lt-row:first-of-type .lt-cell:first-of-type'), 'first email in list after addition')
|
||||
.to.contain.text('test@example.com');
|
||||
|
||||
// the table is scrolled to the top
|
||||
// TODO: implement scroll to new record after addition
|
||||
@ -158,90 +155,90 @@ describe('Acceptance: Subscribers', function () {
|
||||
// .to.equal(0);
|
||||
|
||||
// the subscriber total is updated
|
||||
expect(find('[data-test-total-subscribers]').text().trim(), 'subscribers total after addition')
|
||||
.to.equal('(41)');
|
||||
expect(find('[data-test-total-subscribers]'), 'subscribers total after addition')
|
||||
.to.have.trimmed.text('(41)');
|
||||
|
||||
// saving a duplicate subscriber
|
||||
await click('.gh-btn:contains("Add Subscriber")');
|
||||
await fillIn('.fullscreen-modal input[name="email"]', 'test@example.com');
|
||||
await click('.fullscreen-modal .gh-btn:contains("Add")');
|
||||
await click('[data-test-link="add-subscriber"]');
|
||||
await fillIn('[data-test-input="new-subscriber-email"]', 'test@example.com');
|
||||
await click('[data-test-button="create-subscriber"]');
|
||||
|
||||
// the validation error is displayed
|
||||
expect(find('.fullscreen-modal .error .response').text().trim(), 'duplicate email validation')
|
||||
.to.equal('Email already exists.');
|
||||
expect(find('[data-test-error="new-subscriber-email"]'), 'duplicate email validation')
|
||||
.to.have.trimmed.text('Email already exists.');
|
||||
|
||||
// the subscriber is not added to the table
|
||||
expect(find('.lt-cell:contains(test@example.com)').length, 'number of "test@example.com rows"')
|
||||
expect(findAllWithText('.lt-cell', 'test@example.com').length, 'number of "test@example.com rows"')
|
||||
.to.equal(1);
|
||||
|
||||
// the subscriber total is unchanged
|
||||
expect(find('[data-test-total-subscribers]').text().trim(), 'subscribers total after failed add')
|
||||
.to.equal('(41)');
|
||||
expect(find('[data-test-total-subscribers]'), 'subscribers total after failed add')
|
||||
.to.have.trimmed.text('(41)');
|
||||
|
||||
// deleting a subscriber
|
||||
await click('.fullscreen-modal .gh-btn:contains("Cancel")');
|
||||
await click('[data-test-button="cancel-new-subscriber"]');
|
||||
await click('.subscribers-table tbody tr:first-of-type button:last-of-type');
|
||||
|
||||
// it displays the delete subscriber modal
|
||||
expect(find('.fullscreen-modal').length, 'delete subscriber modal displayed')
|
||||
.to.equal(1);
|
||||
expect(find('[data-test-modal="delete-subscriber"]'), 'delete subscriber modal displayed')
|
||||
.to.exist;
|
||||
|
||||
// cancel the modal
|
||||
await click('.fullscreen-modal .gh-btn:contains("Cancel")');
|
||||
await click('[data-test-button="cancel-delete-subscriber"]');
|
||||
|
||||
// it closes the add subscriber modal
|
||||
expect(find('.fullscreen-modal').length, 'delete subscriber modal displayed after cancel')
|
||||
.to.equal(0);
|
||||
expect(find('[data-test-modal]'), 'delete subscriber modal displayed after cancel')
|
||||
.to.not.exist;
|
||||
|
||||
await click('.subscribers-table tbody tr:first-of-type button:last-of-type');
|
||||
await click('.fullscreen-modal .gh-btn:contains("Delete")');
|
||||
await click('[data-test-button="confirm-delete-subscriber"]');
|
||||
|
||||
// the add subscriber modal is closed
|
||||
expect(find('.fullscreen-modal').length, 'delete subscriber modal displayed after confirm')
|
||||
.to.equal(0);
|
||||
expect(find('[data-test-modal]'), 'delete subscriber modal displayed after confirm')
|
||||
.to.not.exist;
|
||||
|
||||
// the subscriber is removed from the table
|
||||
expect(find('.subscribers-table .lt-body .lt-row:first-of-type .lt-cell:first-of-type').text().trim(), 'first email in list after addition')
|
||||
.to.not.equal('test@example.com');
|
||||
expect(find('.subscribers-table .lt-body .lt-row:first-of-type .lt-cell:first-of-type'), 'first email in list after addition')
|
||||
.to.not.have.trimmed.text('test@example.com');
|
||||
|
||||
// the subscriber total is updated
|
||||
expect(find('[data-test-total-subscribers]').text().trim(), 'subscribers total after addition')
|
||||
.to.equal('(40)');
|
||||
expect(find('[data-test-total-subscribers]'), 'subscribers total after addition')
|
||||
.to.have.trimmed.text('(40)');
|
||||
|
||||
// click the import subscribers button
|
||||
await click('[data-test-link="import-csv"]');
|
||||
|
||||
// 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);
|
||||
expect(find('[data-test-modal="import-subscribers"]'), 'import subscribers modal displayed')
|
||||
.to.exist;
|
||||
expect(find('.fullscreen-modal input[type="file"]'), 'import modal contains file input')
|
||||
.to.exist;
|
||||
|
||||
// cancel the modal
|
||||
await click('.fullscreen-modal .gh-btn:contains("Cancel")');
|
||||
await click('[data-test-button="close-import-subscribers"]');
|
||||
|
||||
// it closes the import subscribers modal
|
||||
expect(find('.fullscreen-modal').length, 'import subscribers modal displayed after cancel')
|
||||
.to.equal(0);
|
||||
expect(find('[data-test-modal]'), 'import subscribers modal displayed after cancel')
|
||||
.to.not.exist;
|
||||
|
||||
await click('[data-test-link="import-csv"]');
|
||||
await fileUpload('.fullscreen-modal input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
// modal title changes
|
||||
expect(find('.fullscreen-modal h1').text().trim(), 'import modal title after import')
|
||||
.to.equal('Import Successful');
|
||||
expect(find('[data-test-modal="import-subscribers"] h1'), 'import modal title after import')
|
||||
.to.have.trimmed.text('Import Successful');
|
||||
|
||||
// modal button changes
|
||||
expect(find('.fullscreen-modal .modal-footer button').text().trim(), 'import modal button text after import')
|
||||
.to.equal('Close');
|
||||
expect(find('[data-test-button="close-import-subscribers"]'), 'import modal button text after import')
|
||||
.to.have.trimmed.text('Close');
|
||||
|
||||
// subscriber total is updated
|
||||
expect(find('[data-test-total-subscribers]').text().trim(), 'subscribers total after import')
|
||||
.to.equal('(90)');
|
||||
expect(find('[data-test-total-subscribers]'), 'subscribers total after import')
|
||||
.to.have.trimmed.text('(90)');
|
||||
|
||||
// TODO: re-enable once bug in ember-light-table that triggers second page load is fixed
|
||||
// table is reset
|
||||
// [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
// [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
// expect(lastRequest.url, 'endpoint requested after import')
|
||||
// .to.match(/\/subscribers\/\?/);
|
||||
// expect(lastRequest.queryParams.page, 'page requested after import')
|
||||
|
@ -1,63 +1,69 @@
|
||||
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
|
||||
import destroyApp from '../helpers/destroy-app';
|
||||
import moment from 'moment';
|
||||
import startApp from '../helpers/start-app';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import windowProxy from 'ghost-admin/utils/window-proxy';
|
||||
import {Response} from 'ember-cli-mirage';
|
||||
import {afterEach, beforeEach, describe, it} from 'mocha';
|
||||
import {authenticateSession, invalidateSession} from '../helpers/ember-simple-auth';
|
||||
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
blur,
|
||||
click,
|
||||
currentRouteName,
|
||||
currentURL,
|
||||
fillIn,
|
||||
find,
|
||||
findAll,
|
||||
focus,
|
||||
triggerEvent,
|
||||
triggerKeyEvent
|
||||
} from '@ember/test-helpers';
|
||||
import {errorOverride, errorReset} from '../helpers/adapter-error';
|
||||
import {expect} from 'chai';
|
||||
import {setupApplicationTest} from 'ember-mocha';
|
||||
import {visit} from '../helpers/visit';
|
||||
|
||||
describe('Acceptance: Team', function () {
|
||||
let application;
|
||||
|
||||
beforeEach(function () {
|
||||
application = startApp();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
destroyApp(application);
|
||||
});
|
||||
let hooks = setupApplicationTest();
|
||||
setupMirage(hooks);
|
||||
|
||||
it('redirects to signin when not authenticated', async function () {
|
||||
invalidateSession(application);
|
||||
await invalidateSession();
|
||||
await visit('/team');
|
||||
|
||||
expect(currentURL()).to.equal('/signin');
|
||||
});
|
||||
|
||||
it('redirects correctly when authenticated as contributor', async function () {
|
||||
let role = server.create('role', {name: 'Contributor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Contributor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
server.create('user', {slug: 'no-access'});
|
||||
this.server.create('user', {slug: 'no-access'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/team/no-access');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects correctly when authenticated as author', async function () {
|
||||
let role = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
server.create('user', {slug: 'no-access'});
|
||||
this.server.create('user', {slug: 'no-access'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/team/no-access');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-user');
|
||||
});
|
||||
|
||||
it('redirects correctly when authenticated as editor', async function () {
|
||||
let role = server.create('role', {name: 'Editor'});
|
||||
server.create('user', {roles: [role], slug: 'test-user'});
|
||||
let role = this.server.create('role', {name: 'Editor'});
|
||||
this.server.create('user', {roles: [role], slug: 'test-user'});
|
||||
|
||||
server.create('user', {slug: 'no-access'});
|
||||
this.server.create('user', {slug: 'no-access'});
|
||||
|
||||
authenticateSession(application);
|
||||
await authenticateSession();
|
||||
await visit('/team/no-access');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team');
|
||||
@ -66,24 +72,24 @@ describe('Acceptance: Team', function () {
|
||||
describe('when logged in as admin', function () {
|
||||
let admin, adminRole, suspendedUser;
|
||||
|
||||
beforeEach(function () {
|
||||
server.loadFixtures('roles');
|
||||
adminRole = server.schema.roles.find(1);
|
||||
beforeEach(async function () {
|
||||
this.server.loadFixtures('roles');
|
||||
adminRole = this.server.schema.roles.find(1);
|
||||
|
||||
admin = server.create('user', {email: 'admin@example.com', roles: [adminRole]});
|
||||
admin = this.server.create('user', {email: 'admin@example.com', roles: [adminRole]});
|
||||
|
||||
// add an expired invite
|
||||
server.create('invite', {expires: moment.utc().subtract(1, 'day').valueOf(), role: adminRole});
|
||||
this.server.create('invite', {expires: moment.utc().subtract(1, 'day').valueOf(), role: adminRole});
|
||||
|
||||
// add a suspended user
|
||||
suspendedUser = server.create('user', {email: 'suspended@example.com', roles: [adminRole], status: 'inactive'});
|
||||
suspendedUser = this.server.create('user', {email: 'suspended@example.com', roles: [adminRole], status: 'inactive'});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('it renders and navigates correctly', async function () {
|
||||
let user1 = server.create('user');
|
||||
let user2 = server.create('user');
|
||||
let user1 = this.server.create('user');
|
||||
let user2 = this.server.create('user');
|
||||
|
||||
await visit('/team');
|
||||
|
||||
@ -95,7 +101,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// it shows active users in active section
|
||||
expect(
|
||||
find('[data-test-active-users] [data-test-user-id]').length,
|
||||
findAll('[data-test-active-users] [data-test-user-id]').length,
|
||||
'number of active users'
|
||||
).to.equal(3);
|
||||
expect(
|
||||
@ -110,7 +116,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// it shows suspended users in suspended section
|
||||
expect(
|
||||
find('[data-test-suspended-users] [data-test-user-id]').length,
|
||||
findAll('[data-test-suspended-users] [data-test-user-id]').length,
|
||||
'number of suspended users'
|
||||
).to.equal(1);
|
||||
expect(
|
||||
@ -127,7 +133,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// view title should exist and be linkable and active
|
||||
expect(
|
||||
find('[data-test-screen-title] a[href="/ghost/team"]').hasClass('active'),
|
||||
find('[data-test-screen-title] a[href="/ghost/team"]').classList.contains('active'),
|
||||
'has linkable url back to team main page'
|
||||
).to.be.true;
|
||||
|
||||
@ -142,29 +148,29 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// invite user button exists
|
||||
expect(
|
||||
find('.view-actions .gh-btn-green').text().trim(),
|
||||
find('.view-actions .gh-btn-green').textContent.trim(),
|
||||
'invite people button text'
|
||||
).to.equal('Invite People');
|
||||
|
||||
// existing users are listed
|
||||
expect(
|
||||
find('[data-test-user-id]').length,
|
||||
findAll('[data-test-user-id]').length,
|
||||
'initial number of active users'
|
||||
).to.equal(2);
|
||||
|
||||
expect(
|
||||
find('[data-test-user-id="1"] [data-test-role-name]').text().trim(),
|
||||
find('[data-test-user-id="1"] [data-test-role-name]').textContent.trim(),
|
||||
'active user\'s role label'
|
||||
).to.equal('Administrator');
|
||||
|
||||
// existing invites are shown
|
||||
expect(
|
||||
find('[data-test-invite-id]').length,
|
||||
findAll('[data-test-invite-id]').length,
|
||||
'initial number of invited users'
|
||||
).to.equal(1);
|
||||
|
||||
expect(
|
||||
find('[data-test-invite-id="1"] [data-test-invite-description]').text(),
|
||||
find('[data-test-invite-id="1"] [data-test-invite-description]').textContent,
|
||||
'expired invite description'
|
||||
).to.match(/expired/);
|
||||
|
||||
@ -172,14 +178,14 @@ describe('Acceptance: Team', function () {
|
||||
await click('[data-test-invite-id="1"] [data-test-revoke-button]');
|
||||
|
||||
expect(
|
||||
find('[data-test-invite-id]').length,
|
||||
findAll('[data-test-invite-id]').length,
|
||||
'initial number of invited users'
|
||||
).to.equal(0);
|
||||
|
||||
// click the invite people button
|
||||
await click('.view-actions .gh-btn-green');
|
||||
|
||||
let roleOptions = find('.fullscreen-modal select[name="role"] option');
|
||||
let roleOptions = findAll('.fullscreen-modal select[name="role"] option');
|
||||
|
||||
function checkOwnerExists() {
|
||||
for (let i in roleOptions) {
|
||||
@ -201,13 +207,13 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// modal is displayed
|
||||
expect(
|
||||
find('.fullscreen-modal h1').text().trim(),
|
||||
find('.fullscreen-modal h1').textContent.trim(),
|
||||
'correct modal is displayed'
|
||||
).to.equal('Invite a New User');
|
||||
|
||||
// number of roles is correct
|
||||
expect(
|
||||
find('.fullscreen-modal select[name="role"] option').length,
|
||||
findAll('.fullscreen-modal select[name="role"] option').length,
|
||||
'number of selectable roles'
|
||||
).to.equal(3);
|
||||
|
||||
@ -220,34 +226,34 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// modal closes
|
||||
expect(
|
||||
find('.fullscreen-modal').length,
|
||||
findAll('[data-test-modal]').length,
|
||||
'number of modals after sending invite'
|
||||
).to.equal(0);
|
||||
|
||||
// invite is displayed, has correct e-mail + role
|
||||
expect(
|
||||
find('[data-test-invite-id]').length,
|
||||
findAll('[data-test-invite-id]').length,
|
||||
'number of invites after first invite'
|
||||
).to.equal(1);
|
||||
|
||||
expect(
|
||||
find('[data-test-invite-id="2"] [data-test-email]').text().trim(),
|
||||
find('[data-test-invite-id="2"] [data-test-email]').textContent.trim(),
|
||||
'displayed email of first invite'
|
||||
).to.equal('invite1@example.com');
|
||||
|
||||
expect(
|
||||
find('[data-test-invite-id="2"] [data-test-role-name]').text().trim(),
|
||||
find('[data-test-invite-id="2"] [data-test-role-name]').textContent.trim(),
|
||||
'displayed role of first invite'
|
||||
).to.equal('Author');
|
||||
|
||||
expect(
|
||||
find('[data-test-invite-id="2"] [data-test-invite-description]').text(),
|
||||
find('[data-test-invite-id="2"] [data-test-invite-description]').textContent,
|
||||
'new invite description'
|
||||
).to.match(/expires/);
|
||||
|
||||
// number of users is unchanged
|
||||
expect(
|
||||
find('[data-test-user-id]').length,
|
||||
findAll('[data-test-user-id]').length,
|
||||
'number of active users after first invite'
|
||||
).to.equal(2);
|
||||
|
||||
@ -259,18 +265,18 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// number of invites increases
|
||||
expect(
|
||||
find('[data-test-invite-id]').length,
|
||||
findAll('[data-test-invite-id]').length,
|
||||
'number of invites after second invite'
|
||||
).to.equal(2);
|
||||
|
||||
// invite has correct e-mail + role
|
||||
expect(
|
||||
find('[data-test-invite-id="3"] [data-test-email]').text().trim(),
|
||||
find('[data-test-invite-id="3"] [data-test-email]').textContent.trim(),
|
||||
'displayed email of second invite'
|
||||
).to.equal('invite2@example.com');
|
||||
|
||||
expect(
|
||||
find('[data-test-invite-id="3"] [data-test-role-name]').text().trim(),
|
||||
find('[data-test-invite-id="3"] [data-test-role-name]').textContent.trim(),
|
||||
'displayed role of second invite'
|
||||
).to.equal('Editor');
|
||||
|
||||
@ -281,7 +287,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// validation message is displayed
|
||||
expect(
|
||||
find('.fullscreen-modal .error .response').text().trim(),
|
||||
find('.fullscreen-modal .error .response').textContent.trim(),
|
||||
'inviting existing user error'
|
||||
).to.equal('A user with that email address already exists.');
|
||||
|
||||
@ -291,7 +297,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// validation message is displayed
|
||||
expect(
|
||||
find('.fullscreen-modal .error .response').text().trim(),
|
||||
find('.fullscreen-modal .error .response').textContent.trim(),
|
||||
'inviting invited user error'
|
||||
).to.equal('A user with that email address was already invited.');
|
||||
|
||||
@ -301,7 +307,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// validation message is displayed
|
||||
expect(
|
||||
find('.fullscreen-modal .error .response').text().trim(),
|
||||
find('.fullscreen-modal .error .response').textContent.trim(),
|
||||
'inviting invalid email error'
|
||||
).to.equal('Invalid Email.');
|
||||
|
||||
@ -311,19 +317,19 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// number of invites decreases
|
||||
expect(
|
||||
find('[data-test-invite-id]').length,
|
||||
findAll('[data-test-invite-id]').length,
|
||||
'number of invites after revoke'
|
||||
).to.equal(1);
|
||||
|
||||
// notification is displayed
|
||||
expect(
|
||||
find('.gh-notification').text().trim(),
|
||||
find('.gh-notification').textContent.trim(),
|
||||
'notifications contain revoke'
|
||||
).to.match(/Invitation revoked\. \(invite2@example\.com\)/);
|
||||
|
||||
// correct invite is removed
|
||||
expect(
|
||||
find('[data-test-invite-id] [data-test-email]').text().trim(),
|
||||
find('[data-test-invite-id] [data-test-email]').textContent.trim(),
|
||||
'displayed email of remaining invite'
|
||||
).to.equal('invite1@example.com');
|
||||
|
||||
@ -334,7 +340,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// new invite should be last in the list
|
||||
expect(
|
||||
find('[data-test-invite-id]:last [data-test-email]').text().trim(),
|
||||
find('[data-test-invite-id]:last-of-type [data-test-email]').textContent.trim(),
|
||||
'last invite email in list'
|
||||
).to.equal('invite3@example.com');
|
||||
|
||||
@ -343,13 +349,13 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// notification is displayed
|
||||
expect(
|
||||
find('.gh-notification').text().trim(),
|
||||
find('.gh-notification').textContent.trim(),
|
||||
'notifications contain resend'
|
||||
).to.match(/Invitation resent! \(invite1@example\.com\)/);
|
||||
|
||||
// first invite is still at the top
|
||||
expect(
|
||||
find('[data-test-invite-id]:first-of-type [data-test-email]').text().trim(),
|
||||
find('[data-test-invite-id]:first-of-type [data-test-email]').textContent.trim(),
|
||||
'first invite email in list'
|
||||
).to.equal('invite1@example.com');
|
||||
|
||||
@ -359,13 +365,13 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// number of invites decreases
|
||||
expect(
|
||||
find('[data-test-invite-id]').length,
|
||||
findAll('[data-test-invite-id]').length,
|
||||
'number of invites after resend/revoke'
|
||||
).to.equal(1);
|
||||
|
||||
// notification is displayed
|
||||
expect(
|
||||
find('.gh-notification').text().trim(),
|
||||
find('.gh-notification').textContent.trim(),
|
||||
'notifications contain revoke after resend/revoke'
|
||||
).to.match(/Invitation revoked\. \(invite1@example\.com\)/);
|
||||
});
|
||||
@ -395,7 +401,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// no suspended users
|
||||
expect(
|
||||
find('[data-test-suspended-users] [data-test-user-id]').length
|
||||
findAll('[data-test-suspended-users] [data-test-user-id]').length
|
||||
).to.equal(0);
|
||||
|
||||
await click(`[data-test-user-id="${suspendedUser.id}"]`);
|
||||
@ -407,9 +413,9 @@ describe('Acceptance: Team', function () {
|
||||
});
|
||||
|
||||
it('can delete users', async function () {
|
||||
let user1 = server.create('user');
|
||||
let user2 = server.create('user');
|
||||
let post = server.create('post', {authors: [user2]});
|
||||
let user1 = this.server.create('user');
|
||||
let user2 = this.server.create('user');
|
||||
let post = this.server.create('post', {authors: [user2]});
|
||||
|
||||
// we don't have a full many-to-many relationship in mirage so we
|
||||
// need to add the inverse manually
|
||||
@ -422,20 +428,20 @@ describe('Acceptance: Team', function () {
|
||||
// user deletion displays modal
|
||||
await click('button.delete');
|
||||
expect(
|
||||
find('.fullscreen-modal .modal-content:contains("delete this user")').length,
|
||||
findAll('[data-test-modal="delete-user"]').length,
|
||||
'user deletion modal displayed after button click'
|
||||
).to.equal(1);
|
||||
|
||||
// user has no posts so no warning about post deletion
|
||||
expect(
|
||||
find('.fullscreen-modal .modal-content:contains("is the author of")').length,
|
||||
findAll('[data-test-text="user-post-count"]').length,
|
||||
'deleting user with no posts has no post count'
|
||||
).to.equal(0);
|
||||
|
||||
// cancelling user deletion closes modal
|
||||
await click('.fullscreen-modal button:contains("Cancel")');
|
||||
await click('[data-test-button="cancel-delete-user"]');
|
||||
expect(
|
||||
find('.fullscreen-modal').length === 0,
|
||||
findAll('[data-test-modal]').length === 0,
|
||||
'delete user modal is closed when cancelling'
|
||||
).to.be.true;
|
||||
|
||||
@ -446,17 +452,17 @@ describe('Acceptance: Team', function () {
|
||||
await click('button.delete');
|
||||
// user has posts so should warn about post deletion
|
||||
expect(
|
||||
find('.fullscreen-modal .modal-content:contains("1 post created by this user")').length,
|
||||
find('[data-test-text="user-post-count"]').textContent,
|
||||
'deleting user with posts has post count'
|
||||
).to.equal(1);
|
||||
).to.have.string('1 post');
|
||||
|
||||
await click('.fullscreen-modal button:contains("Delete")');
|
||||
await click('[data-test-button="confirm-delete-user"]');
|
||||
// redirected to team page
|
||||
expect(currentURL()).to.equal('/team');
|
||||
|
||||
// deleted user is not in list
|
||||
expect(
|
||||
find(`[data-test-user-id="${user2.id}"]`).length,
|
||||
findAll(`[data-test-user-id="${user2.id}"]`).length,
|
||||
'deleted user is not in user list after deletion'
|
||||
).to.equal(0);
|
||||
});
|
||||
@ -465,7 +471,7 @@ describe('Acceptance: Team', function () {
|
||||
let user, newLocation, originalReplaceState;
|
||||
|
||||
beforeEach(function () {
|
||||
user = server.create('user', {
|
||||
user = this.server.create('user', {
|
||||
slug: 'test-1',
|
||||
name: 'Test User',
|
||||
facebook: 'test',
|
||||
@ -488,36 +494,36 @@ describe('Acceptance: Team', function () {
|
||||
await visit('/team/test-1');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-1');
|
||||
expect(find('[data-test-name-input]').val(), 'current user name').to.equal('Test User');
|
||||
expect(find('[data-test-name-input]').value, 'current user name').to.equal('Test User');
|
||||
|
||||
expect(find('[data-test-save-button]').text().trim(), 'save button text').to.equal('Save');
|
||||
expect(find('[data-test-save-button]').textContent.trim(), 'save button text').to.equal('Save');
|
||||
|
||||
// test empty user name
|
||||
await fillIn('[data-test-name-input]', '');
|
||||
await triggerEvent('[data-test-name-input]', 'blur');
|
||||
await blur('[data-test-name-input]');
|
||||
|
||||
expect(find('.user-details-bottom .first-form-group').hasClass('error'), 'username input is in error state with blank input').to.be.true;
|
||||
expect(find('.user-details-bottom .first-form-group').classList.contains('error'), 'username input is in error state with blank input').to.be.true;
|
||||
|
||||
// test too long user name
|
||||
await fillIn('[data-test-name-input]', new Array(195).join('a'));
|
||||
await triggerEvent('[data-test-name-input]', 'blur');
|
||||
await blur('[data-test-name-input]');
|
||||
|
||||
expect(find('.user-details-bottom .first-form-group').hasClass('error'), 'username input is in error state with too long input').to.be.true;
|
||||
expect(find('.user-details-bottom .first-form-group').classList.contains('error'), 'username input is in error state with too long input').to.be.true;
|
||||
|
||||
// reset name field
|
||||
await fillIn('[data-test-name-input]', 'Test User');
|
||||
|
||||
expect(find('[data-test-slug-input]').val(), 'slug value is default').to.equal('test-1');
|
||||
expect(find('[data-test-slug-input]').value, 'slug value is default').to.equal('test-1');
|
||||
|
||||
await fillIn('[data-test-slug-input]', '');
|
||||
await triggerEvent('[data-test-slug-input]', 'blur');
|
||||
await blur('[data-test-slug-input]');
|
||||
|
||||
expect(find('[data-test-slug-input]').val(), 'slug value is reset to original upon empty string').to.equal('test-1');
|
||||
expect(find('[data-test-slug-input]').value, 'slug value is reset to original upon empty string').to.equal('test-1');
|
||||
|
||||
// Save changes
|
||||
await click('[data-test-save-button]');
|
||||
|
||||
expect(find('[data-test-save-button]').text().trim(), 'save button text').to.equal('Saved');
|
||||
expect(find('[data-test-save-button]').textContent.trim(), 'save button text').to.equal('Saved');
|
||||
|
||||
// CMD-S shortcut works
|
||||
await fillIn('[data-test-slug-input]', 'Test User');
|
||||
@ -529,7 +535,7 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// we've already saved in this test so there's no on-screen indication
|
||||
// that we've had another save, check the request was fired instead
|
||||
let [lastRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
let params = JSON.parse(lastRequest.requestBody);
|
||||
|
||||
expect(params.users[0].name).to.equal('Test User');
|
||||
@ -538,43 +544,49 @@ describe('Acceptance: Team', function () {
|
||||
expect(newLocation).to.equal('Test User');
|
||||
|
||||
await fillIn('[data-test-slug-input]', 'white space');
|
||||
await triggerEvent('[data-test-slug-input]', 'blur');
|
||||
await blur('[data-test-slug-input]');
|
||||
|
||||
expect(find('[data-test-slug-input]').val(), 'slug value is correctly dasherized').to.equal('white-space');
|
||||
expect(find('[data-test-slug-input]').value, 'slug value is correctly dasherized').to.equal('white-space');
|
||||
|
||||
await fillIn('[data-test-email-input]', 'thisisnotanemail');
|
||||
await triggerEvent('[data-test-email-input]', 'blur');
|
||||
await blur('[data-test-email-input]');
|
||||
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(3)').hasClass('error'), 'email input should be in error state with invalid email').to.be.true;
|
||||
expect(find('.user-details-bottom .form-group:nth-of-type(3)').classList.contains('error'), 'email input should be in error state with invalid email').to.be.true;
|
||||
|
||||
await fillIn('[data-test-email-input]', 'test@example.com');
|
||||
await fillIn('[data-test-location-input]', new Array(160).join('a'));
|
||||
await triggerEvent('[data-test-location-input]', 'blur');
|
||||
await blur('[data-test-location-input]');
|
||||
|
||||
expect(find('[data-test-location-input]').closest('.form-group').hasClass('error'), 'location input should be in error state').to.be.true;
|
||||
expect(
|
||||
find('[data-test-location-input]').closest('.form-group'),
|
||||
'location input should be in error state'
|
||||
).to.have.class('error');
|
||||
|
||||
await fillIn('[data-test-location-input]', '');
|
||||
await fillIn('[data-test-website-input]', 'thisisntawebsite');
|
||||
await triggerEvent('[data-test-website-input]', 'blur');
|
||||
await blur('[data-test-website-input]');
|
||||
|
||||
expect(find('[data-test-website-input]').closest('.form-group').hasClass('error'), 'website input should be in error state').to.be.true;
|
||||
expect(
|
||||
find('[data-test-website-input]').closest('.form-group'),
|
||||
'website input should be in error state'
|
||||
).to.have.class('error');
|
||||
|
||||
let testSocialInput = async function (type, input, expectedValue, expectedError = '') {
|
||||
await fillIn(`[data-test-${type}-input]`, input);
|
||||
await triggerEvent(`[data-test-${type}-input]`, 'blur');
|
||||
await blur(`[data-test-${type}-input]`);
|
||||
|
||||
expect(
|
||||
find(`[data-test-${type}-input]`).val(),
|
||||
find(`[data-test-${type}-input]`).value,
|
||||
`${type} value for ${input}`
|
||||
).to.equal(expectedValue);
|
||||
|
||||
expect(
|
||||
find(`[data-test-${type}-error]`).text().trim(),
|
||||
find(`[data-test-error="user-${type}"]`).textContent.trim(),
|
||||
`${type} validation response for ${input}`
|
||||
).to.equal(expectedError);
|
||||
|
||||
expect(
|
||||
find(`[data-test-${type}-input]`).closest('.form-group').hasClass('error'),
|
||||
find(`[data-test-error="user-${type}"]`).closest('.form-group').classList.contains('error'),
|
||||
`${type} input should be in error state with '${input}'`
|
||||
).to.equal(!!expectedError);
|
||||
};
|
||||
@ -585,15 +597,15 @@ describe('Acceptance: Team', function () {
|
||||
// Testing Facebook input
|
||||
|
||||
// displays initial value
|
||||
expect(find('[data-test-facebook-input]').val(), 'initial facebook value')
|
||||
expect(find('[data-test-facebook-input]').value, 'initial facebook value')
|
||||
.to.equal('https://www.facebook.com/test');
|
||||
|
||||
await triggerEvent('[data-test-facebook-input]', 'focus');
|
||||
await triggerEvent('[data-test-facebook-input]', 'blur');
|
||||
await focus('[data-test-facebook-input]');
|
||||
await blur('[data-test-facebook-input]');
|
||||
|
||||
// regression test: we still have a value after the input is
|
||||
// focused and then blurred without any changes
|
||||
expect(find('[data-test-facebook-input]').val(), 'facebook value after blur with no change')
|
||||
expect(find('[data-test-facebook-input]').value, 'facebook value after blur with no change')
|
||||
.to.equal('https://www.facebook.com/test');
|
||||
|
||||
await testFacebookValidation(
|
||||
@ -637,15 +649,15 @@ describe('Acceptance: Team', function () {
|
||||
// Testing Twitter input
|
||||
|
||||
// loads fixtures and performs transform
|
||||
expect(find('[data-test-twitter-input]').val(), 'initial twitter value')
|
||||
expect(find('[data-test-twitter-input]').value, 'initial twitter value')
|
||||
.to.equal('https://twitter.com/test');
|
||||
|
||||
await triggerEvent('[data-test-twitter-input]', 'focus');
|
||||
await triggerEvent('[data-test-twitter-input]', 'blur');
|
||||
await focus('[data-test-twitter-input]');
|
||||
await blur('[data-test-twitter-input]');
|
||||
|
||||
// regression test: we still have a value after the input is
|
||||
// focused and then blurred without any changes
|
||||
expect(find('[data-test-twitter-input]').val(), 'twitter value after blur with no change')
|
||||
expect(find('[data-test-twitter-input]').value, 'twitter value after blur with no change')
|
||||
.to.equal('https://twitter.com/test');
|
||||
|
||||
await testTwitterValidation(
|
||||
@ -674,9 +686,12 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
await fillIn('[data-test-website-input]', '');
|
||||
await fillIn('[data-test-bio-input]', new Array(210).join('a'));
|
||||
await triggerEvent('[data-test-bio-input]', 'blur');
|
||||
await blur('[data-test-bio-input]');
|
||||
|
||||
expect(find('[data-test-bio-input]').closest('.form-group').hasClass('error'), 'bio input should be in error state').to.be.true;
|
||||
expect(
|
||||
find('[data-test-bio-input]').closest('.form-group'),
|
||||
'bio input should be in error state'
|
||||
).to.have.class('error');
|
||||
|
||||
// password reset ------
|
||||
|
||||
@ -684,47 +699,47 @@ describe('Acceptance: Team', function () {
|
||||
await click('[data-test-save-pw-button]');
|
||||
|
||||
expect(
|
||||
find('[data-test-new-pass-input]').closest('.form-group').hasClass('error'),
|
||||
find('[data-test-new-pass-input]').closest('.form-group'),
|
||||
'new password has error class when blank'
|
||||
).to.be.true;
|
||||
).to.have.class('error');
|
||||
|
||||
expect(
|
||||
find('[data-test-new-pass-input]').siblings('.response').text(),
|
||||
find('[data-test-error="user-new-pass"]').textContent,
|
||||
'new password error when blank'
|
||||
).to.match(/can't be blank/);
|
||||
).to.have.string('can\'t be blank');
|
||||
|
||||
// validates too short password (< 10 characters)
|
||||
await fillIn('[data-test-new-pass-input]', 'notlong');
|
||||
await fillIn('[data-test-ne2-pass-input]', 'notlong');
|
||||
|
||||
// enter key triggers action
|
||||
await keyEvent('[data-test-new-pass-input]', 'keyup', 13);
|
||||
await triggerKeyEvent('[data-test-new-pass-input]', 'keyup', 13);
|
||||
|
||||
expect(
|
||||
find('[data-test-new-pass-input]').closest('.form-group').hasClass('error'),
|
||||
find('[data-test-new-pass-input]').closest('.form-group'),
|
||||
'new password has error class when password too short'
|
||||
).to.be.true;
|
||||
).to.have.class('error');
|
||||
|
||||
expect(
|
||||
find('[data-test-new-pass-input]').siblings('.response').text(),
|
||||
'confirm password error when it\'s too short'
|
||||
).to.match(/at least 10 characters long/);
|
||||
find('[data-test-error="user-new-pass"]').textContent,
|
||||
'new password error when it\'s too short'
|
||||
).to.have.string('at least 10 characters long');
|
||||
|
||||
// validates unsafe password
|
||||
await fillIn('#user-password-new', 'ghostisawesome');
|
||||
await fillIn('#user-new-password-verification', 'ghostisawesome');
|
||||
await fillIn('[data-test-ne2-pass-input]', 'ghostisawesome');
|
||||
|
||||
// enter key triggers action
|
||||
await keyEvent('#user-password-new', 'keyup', 13);
|
||||
await triggerKeyEvent('#user-password-new', 'keyup', 13);
|
||||
|
||||
expect(
|
||||
find('#user-password-new').closest('.form-group').hasClass('error'),
|
||||
find('#user-password-new').closest('.form-group'),
|
||||
'new password has error class when password is insecure'
|
||||
).to.be.true;
|
||||
).to.have.class('error');
|
||||
|
||||
expect(
|
||||
find('#user-password-new').siblings('.response').text(),
|
||||
'confirm password error when it\'s insecure'
|
||||
find('[data-test-error="user-new-pass"]').textContent,
|
||||
'new password error when it\'s insecure'
|
||||
).to.match(/you cannot use an insecure password/);
|
||||
|
||||
// typing in inputs clears validation
|
||||
@ -732,29 +747,29 @@ describe('Acceptance: Team', function () {
|
||||
await triggerEvent('[data-test-new-pass-input]', 'input');
|
||||
|
||||
expect(
|
||||
find('[data-test-new-pass-input]').closest('.form-group').hasClass('error'),
|
||||
find('[data-test-new-pass-input]').closest('.form-group'),
|
||||
'password validation is visible after typing'
|
||||
).to.be.false;
|
||||
).to.not.have.class('error');
|
||||
|
||||
// enter key triggers action
|
||||
await keyEvent('[data-test-new-pass-input]', 'keyup', 13);
|
||||
await triggerKeyEvent('[data-test-new-pass-input]', 'keyup', 13);
|
||||
|
||||
expect(
|
||||
find('[data-test-ne2-pass-input]').closest('.form-group').hasClass('error'),
|
||||
find('[data-test-ne2-pass-input]').closest('.form-group'),
|
||||
'confirm password has error class when it doesn\'t match'
|
||||
).to.be.true;
|
||||
).to.have.class('error');
|
||||
|
||||
expect(
|
||||
find('[data-test-ne2-pass-input]').siblings('.response').text(),
|
||||
find('[data-test-error="user-ne2-pass"]').textContent,
|
||||
'confirm password error when it doesn\'t match'
|
||||
).to.match(/do not match/);
|
||||
).to.have.string('do not match');
|
||||
|
||||
// submits with correct details
|
||||
await fillIn('[data-test-ne2-pass-input]', 'thisissupersafe');
|
||||
await click('[data-test-save-pw-button]');
|
||||
|
||||
// hits the endpoint
|
||||
let [newRequest] = server.pretender.handledRequests.slice(-1);
|
||||
let [newRequest] = this.server.pretender.handledRequests.slice(-1);
|
||||
params = JSON.parse(newRequest.requestBody);
|
||||
|
||||
expect(newRequest.url, 'password request URL')
|
||||
@ -767,18 +782,18 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// clears the fields
|
||||
expect(
|
||||
find('[data-test-new-pass-input]').val(),
|
||||
find('[data-test-new-pass-input]').value,
|
||||
'password field after submit'
|
||||
).to.be.empty;
|
||||
|
||||
expect(
|
||||
find('[data-test-ne2-pass-input]').val(),
|
||||
find('[data-test-ne2-pass-input]').value,
|
||||
'password verification field after submit'
|
||||
).to.be.empty;
|
||||
|
||||
// displays a notification
|
||||
expect(
|
||||
find('.gh-notifications .gh-notification').length,
|
||||
findAll('.gh-notifications .gh-notification').length,
|
||||
'password saved notification is displayed'
|
||||
).to.equal(1);
|
||||
});
|
||||
@ -789,21 +804,21 @@ describe('Acceptance: Team', function () {
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-1');
|
||||
|
||||
await fillIn('[data-test-slug-input]', 'another slug');
|
||||
await triggerEvent('[data-test-slug-input]', 'blur');
|
||||
await blur('[data-test-slug-input]');
|
||||
|
||||
expect(find('[data-test-slug-input]').val()).to.be.equal('another-slug');
|
||||
expect(find('[data-test-slug-input]').value).to.be.equal('another-slug');
|
||||
|
||||
await fillIn('[data-test-facebook-input]', 'testuser');
|
||||
await triggerEvent('[data-test-facebook-input]', 'blur');
|
||||
await blur('[data-test-facebook-input]');
|
||||
|
||||
expect(find('[data-test-facebook-input]').val()).to.be.equal('https://www.facebook.com/testuser');
|
||||
expect(find('[data-test-facebook-input]').value).to.be.equal('https://www.facebook.com/testuser');
|
||||
|
||||
await visit('/settings/team');
|
||||
|
||||
expect(find('.fullscreen-modal').length, 'modal exists').to.equal(1);
|
||||
expect(findAll('[data-test-modal]').length, 'modal exists').to.equal(1);
|
||||
|
||||
// Leave without saving
|
||||
await (click('.fullscreen-modal [data-test-leave-button]'), 'leave without saving');
|
||||
await click('.fullscreen-modal [data-test-leave-button]');
|
||||
|
||||
expect(currentURL(), 'currentURL').to.equal('/settings/team');
|
||||
|
||||
@ -812,8 +827,8 @@ describe('Acceptance: Team', function () {
|
||||
expect(currentURL(), 'currentURL').to.equal('/team/test-1');
|
||||
|
||||
// settings were not saved
|
||||
expect(find('[data-test-slug-input]').val()).to.be.equal('test-1');
|
||||
expect(find('[data-test-facebook-input]').val()).to.be.equal('https://www.facebook.com/test');
|
||||
expect(find('[data-test-slug-input]').value).to.be.equal('test-1');
|
||||
expect(find('[data-test-facebook-input]').value).to.be.equal('https://www.facebook.com/test');
|
||||
});
|
||||
});
|
||||
|
||||
@ -826,39 +841,39 @@ describe('Acceptance: Team', function () {
|
||||
|
||||
// old password has error
|
||||
expect(
|
||||
find('[data-test-old-pass-input]').closest('.form-group').hasClass('error'),
|
||||
find('[data-test-old-pass-input]').closest('.form-group'),
|
||||
'old password has error class when blank'
|
||||
).to.be.true;
|
||||
).to.have.class('error');
|
||||
|
||||
expect(
|
||||
find('[data-test-old-pass-input]').siblings('.response').text(),
|
||||
find('[data-test-error="user-old-pass"]').textContent,
|
||||
'old password error when blank'
|
||||
).to.match(/is required/);
|
||||
).to.have.string('is required');
|
||||
|
||||
// new password has error
|
||||
expect(
|
||||
find('[data-test-new-pass-input]').closest('.form-group').hasClass('error'),
|
||||
find('[data-test-new-pass-input]').closest('.form-group'),
|
||||
'new password has error class when blank'
|
||||
).to.be.true;
|
||||
).to.have.class('error');
|
||||
|
||||
expect(
|
||||
find('[data-test-new-pass-input]').siblings('.response').text(),
|
||||
find('[data-test-error="user-new-pass"]').textContent,
|
||||
'new password error when blank'
|
||||
).to.match(/can't be blank/);
|
||||
).to.have.string('can\'t be blank');
|
||||
|
||||
// validation is cleared when typing
|
||||
await fillIn('[data-test-old-pass-input]', 'password');
|
||||
await triggerEvent('[data-test-old-pass-input]', 'input');
|
||||
|
||||
expect(
|
||||
find('[data-test-old-pass-input]').closest('.form-group').hasClass('error'),
|
||||
find('[data-test-old-pass-input]').closest('.form-group'),
|
||||
'old password validation is in error state after typing'
|
||||
).to.be.false;
|
||||
).to.not.have.class('error');
|
||||
});
|
||||
});
|
||||
|
||||
it('redirects to 404 when user does not exist', async function () {
|
||||
server.get('/users/slug/unknown/', function () {
|
||||
this.server.get('/users/slug/unknown/', function () {
|
||||
return new Response(404, {'Content-Type': 'application/json'}, {errors: [{message: 'User not found.', errorType: 'NotFoundError'}]});
|
||||
});
|
||||
|
||||
@ -867,7 +882,7 @@ describe('Acceptance: Team', function () {
|
||||
await visit('/team/unknown');
|
||||
|
||||
errorReset();
|
||||
expect(currentPath()).to.equal('error404');
|
||||
expect(currentRouteName()).to.equal('error404');
|
||||
expect(currentURL()).to.equal('/team/unknown');
|
||||
});
|
||||
});
|
||||
@ -875,12 +890,12 @@ describe('Acceptance: Team', function () {
|
||||
describe('when logged in as author', function () {
|
||||
let adminRole, authorRole;
|
||||
|
||||
beforeEach(function () {
|
||||
adminRole = server.create('role', {name: 'Administrator'});
|
||||
authorRole = server.create('role', {name: 'Author'});
|
||||
server.create('user', {roles: [authorRole]});
|
||||
beforeEach(async function () {
|
||||
adminRole = this.server.create('role', {name: 'Administrator'});
|
||||
authorRole = this.server.create('role', {name: 'Author'});
|
||||
this.server.create('user', {roles: [authorRole]});
|
||||
|
||||
server.get('/invites/', function () {
|
||||
this.server.get('/invites/', function () {
|
||||
return new Response(403, {}, {
|
||||
errors: [{
|
||||
errorType: 'NoPermissionError',
|
||||
@ -889,20 +904,20 @@ describe('Acceptance: Team', function () {
|
||||
});
|
||||
});
|
||||
|
||||
return authenticateSession(application);
|
||||
return await authenticateSession();
|
||||
});
|
||||
|
||||
it('can access the team page', async function () {
|
||||
server.create('user', {roles: [adminRole]});
|
||||
server.create('invite', {role: authorRole});
|
||||
this.server.create('user', {roles: [adminRole]});
|
||||
this.server.create('invite', {role: authorRole});
|
||||
|
||||
errorOverride();
|
||||
|
||||
await visit('/team');
|
||||
|
||||
errorReset();
|
||||
expect(currentPath()).to.equal('team.index');
|
||||
expect(find('.gh-alert').length).to.equal(0);
|
||||
expect(currentRouteName()).to.equal('team.index');
|
||||
expect(findAll('.gh-alert').length).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,15 +0,0 @@
|
||||
/* global key */
|
||||
import {run} from '@ember/runloop';
|
||||
|
||||
export default function destroyApp(application) {
|
||||
// this is required to fix "second Pretender instance" warnings
|
||||
if (server) {
|
||||
server.shutdown();
|
||||
}
|
||||
|
||||
// extra check to ensure we don't have references hanging around via key
|
||||
// bindings on supposedly destroyed objects
|
||||
key.deleteScope('default');
|
||||
|
||||
run(application, 'destroy');
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
/* global Blob */
|
||||
import $ from 'jquery';
|
||||
import {registerAsyncHelper} from '@ember/test';
|
||||
import {triggerEvent} from '@ember/test-helpers';
|
||||
|
||||
export function createFile(content = ['test'], options = {}) {
|
||||
let {
|
||||
@ -14,22 +12,12 @@ export function createFile(content = ['test'], options = {}) {
|
||||
return file;
|
||||
}
|
||||
|
||||
export function fileUpload($element, content, options) {
|
||||
export function fileUpload(target, content, options) {
|
||||
let file = createFile(content, options);
|
||||
// eslint-disable-next-line new-cap
|
||||
let event = $.Event('change', {
|
||||
testingFiles: [file]
|
||||
});
|
||||
|
||||
$element.trigger(event);
|
||||
}
|
||||
|
||||
export default registerAsyncHelper('fileUpload', function (app, selector, content, options) {
|
||||
let file = createFile(content, options);
|
||||
|
||||
// TODO: replace `[file]` with `{files: [file]}` after upgrading ember-test-helpers
|
||||
return triggerEvent(
|
||||
selector,
|
||||
target,
|
||||
'change',
|
||||
{testingFiles: [file]}
|
||||
[file]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
13
ghost/admin/tests/helpers/find.js
Normal file
13
ghost/admin/tests/helpers/find.js
Normal file
@ -0,0 +1,13 @@
|
||||
import {findAll} from '@ember/test-helpers';
|
||||
|
||||
export function elementHasText(element, text) {
|
||||
return RegExp(text).test(element.textContent);
|
||||
}
|
||||
|
||||
export function findWithText(selector, text) {
|
||||
return Array.from(findAll(selector)).find(element => elementHasText(element, text));
|
||||
}
|
||||
|
||||
export function findAllWithText(selector, text) {
|
||||
return Array.from(findAll(selector)).filter(element => elementHasText(element, text));
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import Application from '../../app';
|
||||
import config from '../../config/environment';
|
||||
import fileUpload from './file-upload'; // eslint-disable-line
|
||||
import registerPowerDatepickerHelpers from '../../tests/helpers/ember-power-datepicker';
|
||||
import registerPowerSelectHelpers from 'ember-power-select/test-support/helpers';
|
||||
import {assign} from '@ember/polyfills';
|
||||
import {run} from '@ember/runloop';
|
||||
|
||||
registerPowerSelectHelpers();
|
||||
registerPowerDatepickerHelpers();
|
||||
|
||||
export default function startApp(attrs) {
|
||||
let attributes = assign({}, config.APP);
|
||||
attributes = assign(attributes, attrs); // use defaults, but you can override;
|
||||
|
||||
return run(() => {
|
||||
let application = Application.create(attributes);
|
||||
application.setupForTesting();
|
||||
application.injectTestHelpers();
|
||||
return application;
|
||||
});
|
||||
}
|
16
ghost/admin/tests/helpers/visit.js
Normal file
16
ghost/admin/tests/helpers/visit.js
Normal file
@ -0,0 +1,16 @@
|
||||
// TODO: remove once bug is fixed in Ember
|
||||
// see https://github.com/emberjs/ember-test-helpers/issues/332
|
||||
|
||||
import {visit as _visit, settled} from '@ember/test-helpers';
|
||||
|
||||
export async function visit(url) {
|
||||
try {
|
||||
await _visit(url);
|
||||
} catch (e) {
|
||||
if (e.message !== 'TransitionAborted') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
await settled();
|
||||
}
|
@ -1,40 +1,38 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-alert', function () {
|
||||
setupComponentTest('gh-alert', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
this.set('message', {message: 'Test message', type: 'success'});
|
||||
|
||||
this.render(hbs`{{gh-alert message=message}}`);
|
||||
await render(hbs`{{gh-alert message=message}}`);
|
||||
|
||||
expect(this.$('article.gh-alert')).to.have.length(1);
|
||||
let $alert = this.$('.gh-alert');
|
||||
|
||||
expect($alert.text()).to.match(/Test message/);
|
||||
let alert = this.element.querySelector('article.gh-alert');
|
||||
expect(alert).to.exist;
|
||||
expect(alert).to.contain.text('Test message');
|
||||
});
|
||||
|
||||
it('maps message types to CSS classes', function () {
|
||||
it('maps message types to CSS classes', async function () {
|
||||
this.set('message', {message: 'Test message', type: 'success'});
|
||||
|
||||
this.render(hbs`{{gh-alert message=message}}`);
|
||||
let $alert = this.$('.gh-alert');
|
||||
await render(hbs`{{gh-alert message=message}}`);
|
||||
let alert = this.element.querySelector('article.gh-alert');
|
||||
|
||||
this.set('message.type', 'success');
|
||||
expect($alert.hasClass('gh-alert-green'), 'success class isn\'t green').to.be.true;
|
||||
expect(alert, 'success class is green').to.have.class('gh-alert-green');
|
||||
|
||||
this.set('message.type', 'error');
|
||||
expect($alert.hasClass('gh-alert-red'), 'success class isn\'t red').to.be.true;
|
||||
expect(alert, 'error class is red').to.have.class('gh-alert-red');
|
||||
|
||||
this.set('message.type', 'warn');
|
||||
expect($alert.hasClass('gh-alert-blue'), 'success class isn\'t yellow').to.be.true;
|
||||
expect(alert, 'warn class is yellow').to.have.class('gh-alert-blue');
|
||||
|
||||
this.set('message.type', 'info');
|
||||
expect($alert.hasClass('gh-alert-blue'), 'success class isn\'t blue').to.be.true;
|
||||
expect(alert, 'info class is blue').to.have.class('gh-alert-blue');
|
||||
});
|
||||
});
|
||||
|
@ -3,48 +3,53 @@ import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {A as emberA} from '@ember/array';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {find, findAll, render, settled} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
let notificationsStub = Service.extend({
|
||||
alerts: emberA()
|
||||
});
|
||||
|
||||
describe('Integration: Component: gh-alerts', function () {
|
||||
setupComponentTest('gh-alerts', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
this.register('service:notifications', notificationsStub);
|
||||
this.inject.service('notifications', {as: 'notifications'});
|
||||
this.owner.register('service:notifications', notificationsStub);
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
|
||||
this.set('notifications.alerts', [
|
||||
notifications.set('alerts', [
|
||||
{message: 'First', type: 'error'},
|
||||
{message: 'Second', type: 'warn'}
|
||||
]);
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.render(hbs`{{gh-alerts}}`);
|
||||
expect(this.$('.gh-alerts').length).to.equal(1);
|
||||
expect(this.$('.gh-alerts').children().length).to.equal(2);
|
||||
it('renders', async function () {
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
|
||||
this.set('notifications.alerts', emberA());
|
||||
expect(this.$('.gh-alerts').children().length).to.equal(0);
|
||||
await render(hbs`{{gh-alerts}}`);
|
||||
expect(findAll('.gh-alerts').length).to.equal(1);
|
||||
expect(find('.gh-alerts').children.length).to.equal(2);
|
||||
|
||||
notifications.set('alerts', emberA());
|
||||
await settled();
|
||||
expect(find('.gh-alerts').children.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('triggers "notify" action when message count changes', function () {
|
||||
it('triggers "notify" action when message count changes', async function () {
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
let expectedCount = 0;
|
||||
|
||||
// test double for notify action
|
||||
this.set('notify', count => expect(count).to.equal(expectedCount));
|
||||
|
||||
this.render(hbs`{{gh-alerts notify=(action notify)}}`);
|
||||
await render(hbs`{{gh-alerts notify=(action notify)}}`);
|
||||
|
||||
expectedCount = 3;
|
||||
this.get('notifications.alerts').pushObject({message: 'Third', type: 'success'});
|
||||
notifications.alerts.pushObject({message: 'Third', type: 'success'});
|
||||
await settled();
|
||||
|
||||
expectedCount = 0;
|
||||
this.set('notifications.alerts', emberA());
|
||||
notifications.set('alerts', emberA());
|
||||
await settled();
|
||||
});
|
||||
});
|
||||
|
@ -1,21 +1,17 @@
|
||||
import $ from 'jquery';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {clickTrigger} from '../../helpers/ember-basic-dropdown';
|
||||
import {clickTrigger} from 'ember-basic-dropdown/test-support/helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {find} from 'ember-native-dom-helpers';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {find, render, settled} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-basic-dropdown', function () {
|
||||
setupComponentTest('gh-basic-dropdown', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('closes when dropdown service fires close event', function () {
|
||||
let dropdownService = this.container.lookup('service:dropdown');
|
||||
it('closes when dropdown service fires close event', async function () {
|
||||
let dropdownService = this.owner.lookup('service:dropdown');
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-basic-dropdown as |dropdown|}}
|
||||
<button class="ember-basic-dropdown-trigger" onclick={{dropdown.actions.toggle}}></button>
|
||||
{{#if dropdown.isOpen}}
|
||||
@ -24,13 +20,12 @@ describe('Integration: Component: gh-basic-dropdown', function () {
|
||||
{{/gh-basic-dropdown}}
|
||||
`);
|
||||
|
||||
clickTrigger();
|
||||
expect($(find('#dropdown-is-opened'))).to.exist;
|
||||
await clickTrigger();
|
||||
expect(find('#dropdown-is-opened')).to.exist;
|
||||
|
||||
run(() => {
|
||||
dropdownService.closeDropdowns();
|
||||
});
|
||||
dropdownService.closeDropdowns();
|
||||
await settled();
|
||||
|
||||
expect($(find('#dropdown-is-opened'))).to.not.exist;
|
||||
expect(find('#dropdown-is-opened')).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
@ -1,91 +1,81 @@
|
||||
import $ from 'jquery';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {click, find, triggerEvent} from 'ember-native-dom-helpers';
|
||||
import {click, find, render, settled, triggerEvent} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
// NOTE: If the browser window is not focused/visible CodeMirror (or Chrome?) will
|
||||
// take longer to respond to/fire events so it's possible that some of these tests
|
||||
// will take 1-3 seconds
|
||||
|
||||
describe('Integration: Component: gh-cm-editor', function () {
|
||||
setupComponentTest('gh-cm-editor', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('handles change event', function () {
|
||||
it('handles change event', async function () {
|
||||
this.set('text', '');
|
||||
|
||||
this.render(hbs`{{gh-cm-editor text class="gh-input" update=(action (mut text))}}`);
|
||||
await render(hbs`{{gh-cm-editor text class="gh-input" update=(action (mut text))}}`);
|
||||
// access CodeMirror directly as it doesn't pick up changes to the textarea
|
||||
let cm = find('.gh-input .CodeMirror').CodeMirror;
|
||||
cm.setValue('Testing');
|
||||
|
||||
return wait().then(() => {
|
||||
expect(this.get('text'), 'text value after CM editor change')
|
||||
.to.equal('Testing');
|
||||
});
|
||||
await settled();
|
||||
|
||||
expect(this.get('text'), 'text value after CM editor change')
|
||||
.to.equal('Testing');
|
||||
});
|
||||
|
||||
it('handles focus event', function (done) {
|
||||
it('handles focus event', async function () {
|
||||
// CodeMirror's events are triggered outside of anything we can watch for
|
||||
// in the tests so let's run the class check when we know the event has
|
||||
// been fired and timeout if it's not fired as we expect
|
||||
let onFocus = () => {
|
||||
let onFocus = async () => {
|
||||
// wait for runloop to finish so that the new class has been rendered
|
||||
wait().then(() => {
|
||||
expect($(find('.gh-input')).hasClass('focus'), 'has focused class on first render with autofocus')
|
||||
.to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
await settled();
|
||||
expect(find('.gh-input'), 'has focused class on first render with autofocus')
|
||||
.to.have.class('focus');
|
||||
};
|
||||
|
||||
this.set('onFocus', onFocus);
|
||||
this.set('text', '');
|
||||
|
||||
this.render(hbs`{{gh-cm-editor text class="gh-input" update=(action (mut text)) focus-in=(action onFocus)}}`);
|
||||
await render(hbs`{{gh-cm-editor text class="gh-input" update=(action (mut text)) focus-in=(action onFocus)}}`);
|
||||
|
||||
// CodeMirror polls the input for changes every 100ms
|
||||
click('textarea');
|
||||
triggerEvent('textarea', 'focus');
|
||||
await click('textarea');
|
||||
await triggerEvent('textarea', 'focus');
|
||||
});
|
||||
|
||||
it('handles blur event', async function () {
|
||||
this.set('text', '');
|
||||
this.render(hbs`{{gh-cm-editor text class="gh-input" update=(action (mut text))}}`);
|
||||
await render(hbs`{{gh-cm-editor text class="gh-input" update=(action (mut text))}}`);
|
||||
|
||||
expect($(find('.gh-input')).hasClass('focus')).to.be.false;
|
||||
expect(find('.gh-input')).to.not.have.class('focus');
|
||||
|
||||
await click('textarea');
|
||||
await triggerEvent('textarea', 'focus');
|
||||
|
||||
expect($(find('.gh-input')).hasClass('focus')).to.be.true;
|
||||
expect(find('.gh-input')).to.have.class('focus');
|
||||
|
||||
await triggerEvent('textarea', 'blur');
|
||||
|
||||
expect($(find('.gh-input')).hasClass('focus')).to.be.false;
|
||||
expect(find('.gh-input')).to.not.have.class('focus');
|
||||
});
|
||||
|
||||
it('can autofocus', function (done) {
|
||||
it('can autofocus', async function () {
|
||||
// CodeMirror's events are triggered outside of anything we can watch for
|
||||
// in the tests so let's run the class check when we know the event has
|
||||
// been fired and timeout if it's not fired as we expect
|
||||
let onFocus = () => {
|
||||
let onFocus = async () => {
|
||||
// wait for runloop to finish so that the new class has been rendered
|
||||
wait().then(() => {
|
||||
expect(this.$('.gh-input').hasClass('focus'), 'has focused class on first render with autofocus')
|
||||
.to.be.true;
|
||||
|
||||
done();
|
||||
});
|
||||
await settled();
|
||||
expect(find('.gh-input').classList.contains('focus'), 'has focused class on first render with autofocus')
|
||||
.to.be.true;
|
||||
};
|
||||
|
||||
this.set('onFocus', onFocus);
|
||||
this.set('text', '');
|
||||
|
||||
this.render(hbs`{{gh-cm-editor text class="gh-input" update=(action (mut text)) autofocus=true focus-in=(action onFocus)}}`);
|
||||
await render(hbs`{{gh-cm-editor text class="gh-input" update=(action (mut text)) autofocus=true focus-in=(action onFocus)}}`);
|
||||
});
|
||||
});
|
||||
|
@ -1,24 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-date-time-picker', function () {
|
||||
setupComponentTest('gh-date-time-picker', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it.skip('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-date-time-picker}}
|
||||
// template content
|
||||
// {{/gh-date-time-picker}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-date-time-picker}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,14 +1,12 @@
|
||||
import Pretender from 'pretender';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-download-count', function () {
|
||||
setupComponentTest('gh-download-count', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
let server;
|
||||
|
||||
@ -23,23 +21,19 @@ describe('Integration: Component: gh-download-count', function () {
|
||||
server.shutdown();
|
||||
});
|
||||
|
||||
it('hits count endpoint and renders', function () {
|
||||
this.render(hbs`{{gh-download-count}}`);
|
||||
it('hits count endpoint and renders', async function () {
|
||||
await render(hbs`{{gh-download-count}}`);
|
||||
|
||||
return wait().then(() => {
|
||||
expect(this.$().text().trim()).to.equal('42');
|
||||
});
|
||||
expect(this.element).to.have.trimmed.text('42');
|
||||
});
|
||||
|
||||
it('renders with a block', function () {
|
||||
this.render(hbs`
|
||||
it('renders with a block', async function () {
|
||||
await render(hbs`
|
||||
{{#gh-download-count as |count|}}
|
||||
{{count}} downloads
|
||||
{{/gh-download-count}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
expect(this.$().text().trim()).to.equal('42 downloads');
|
||||
});
|
||||
expect(this.element).to.have.trimmed.text('42 downloads');
|
||||
});
|
||||
});
|
||||
|
@ -1,24 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-editor-post-status', function () {
|
||||
setupComponentTest('gh-editor-post-status', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-editor-post-status}}
|
||||
// template content
|
||||
// {{/gh-editor-post-status}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-editor-post-status}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,52 +1,44 @@
|
||||
import Service from '@ember/service';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {click, find, render} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
const featureStub = Service.extend({
|
||||
testFlag: true
|
||||
});
|
||||
|
||||
describe('Integration: Component: gh-feature-flag', function () {
|
||||
setupComponentTest('gh-feature-flag', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
this.register('service:feature', featureStub);
|
||||
this.inject.service('feature', {as: 'feature'});
|
||||
this.owner.register('service:feature', featureStub);
|
||||
});
|
||||
|
||||
it('renders properties correctly', function () {
|
||||
this.render(hbs`{{gh-feature-flag "testFlag"}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
expect(this.$('label').attr('for')).to.equal(this.$('input[type="checkbox"]').attr('id'));
|
||||
it('renders properties correctly', async function () {
|
||||
await render(hbs`{{gh-feature-flag "testFlag"}}`);
|
||||
expect(find('label').getAttribute('for')).to.equal(find('input[type="checkbox"]').id);
|
||||
});
|
||||
|
||||
it('renders correctly when flag is set to true', function () {
|
||||
this.render(hbs`{{gh-feature-flag "testFlag"}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
expect(this.$('label input[type="checkbox"]').prop('checked')).to.be.true;
|
||||
it('renders correctly when flag is set to true', async function () {
|
||||
await render(hbs`{{gh-feature-flag "testFlag"}}`);
|
||||
expect(find('label input[type="checkbox"]').checked).to.be.true;
|
||||
});
|
||||
|
||||
it('renders correctly when flag is set to false', function () {
|
||||
this.set('feature.testFlag', false);
|
||||
it('renders correctly when flag is set to false', async function () {
|
||||
let feature = this.owner.lookup('service:feature');
|
||||
feature.set('testFlag', false);
|
||||
|
||||
this.render(hbs`{{gh-feature-flag "testFlag"}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
|
||||
expect(this.$('label input[type="checkbox"]').prop('checked')).to.be.false;
|
||||
await render(hbs`{{gh-feature-flag "testFlag"}}`);
|
||||
expect(find('label input[type="checkbox"]').checked).to.be.false;
|
||||
});
|
||||
|
||||
it('updates to reflect changes in flag property', function () {
|
||||
this.render(hbs`{{gh-feature-flag "testFlag"}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
it('updates to reflect changes in flag property', async function () {
|
||||
await render(hbs`{{gh-feature-flag "testFlag"}}`);
|
||||
expect(find('label input[type="checkbox"]').checked).to.be.true;
|
||||
|
||||
expect(this.$('label input[type="checkbox"]').prop('checked')).to.be.true;
|
||||
|
||||
this.$('label').click();
|
||||
|
||||
expect(this.$('label input[type="checkbox"]').prop('checked')).to.be.false;
|
||||
await click('label');
|
||||
expect(find('label input[type="checkbox"]').checked).to.be.false;
|
||||
});
|
||||
});
|
||||
|
@ -3,13 +3,13 @@ import Pretender from 'pretender';
|
||||
import Service from '@ember/service';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import sinon from 'sinon';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {UnsupportedMediaTypeError} from 'ghost-admin/services/ajax';
|
||||
import {click, find, findAll, render, settled, triggerEvent} from '@ember/test-helpers';
|
||||
import {createFile, fileUpload} from '../../helpers/file-upload';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
const notificationsStub = Service.extend({
|
||||
showAPIError() {
|
||||
@ -35,9 +35,7 @@ const stubFailedUpload = function (server, code, error, delay = 0) {
|
||||
};
|
||||
|
||||
describe('Integration: Component: gh-file-uploader', function () {
|
||||
setupComponentTest('gh-file-uploader', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
let server;
|
||||
|
||||
@ -45,281 +43,234 @@ describe('Integration: Component: gh-file-uploader', function () {
|
||||
server = new Pretender();
|
||||
this.set('uploadUrl', '/ghost/api/v2/admin/uploads/');
|
||||
|
||||
this.register('service:notifications', notificationsStub);
|
||||
this.inject.service('notifications', {as: 'notifications'});
|
||||
this.owner.register('service:notifications', notificationsStub);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
server.shutdown();
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.render(hbs`{{gh-file-uploader}}`);
|
||||
it('renders', async function () {
|
||||
await render(hbs`{{gh-file-uploader}}`);
|
||||
|
||||
expect(this.$('label').text().trim(), 'default label')
|
||||
expect(find('label').textContent.trim(), 'default label')
|
||||
.to.equal('Select or drag-and-drop a file');
|
||||
});
|
||||
|
||||
it('allows file input "accept" attribute to be changed', function () {
|
||||
this.render(hbs`{{gh-file-uploader}}`);
|
||||
it('allows file input "accept" attribute to be changed', async function () {
|
||||
await render(hbs`{{gh-file-uploader}}`);
|
||||
expect(
|
||||
this.$('input[type="file"]').attr('accept'),
|
||||
find('input[type="file"]').getAttribute('accept'),
|
||||
'default "accept" attribute'
|
||||
).to.equal('text/csv');
|
||||
|
||||
this.render(hbs`{{gh-file-uploader accept="application/zip"}}`);
|
||||
await render(hbs`{{gh-file-uploader accept="application/zip"}}`);
|
||||
expect(
|
||||
this.$('input[type="file"]').attr('accept'),
|
||||
find('input[type="file"]').getAttribute('accept'),
|
||||
'specified "accept" attribute'
|
||||
).to.equal('application/zip');
|
||||
});
|
||||
|
||||
it('renders form with supplied label text', function () {
|
||||
it('renders form with supplied label text', async function () {
|
||||
this.set('labelText', 'My label');
|
||||
this.render(hbs`{{gh-file-uploader labelText=labelText}}`);
|
||||
await render(hbs`{{gh-file-uploader labelText=labelText}}`);
|
||||
|
||||
expect(this.$('label').text().trim(), 'label')
|
||||
expect(find('label').textContent.trim(), 'label')
|
||||
.to.equal('My label');
|
||||
});
|
||||
|
||||
it('generates request to supplied endpoint', function (done) {
|
||||
it('generates request to supplied endpoint', async function () {
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(server.handledRequests.length).to.equal(1);
|
||||
expect(server.handledRequests[0].url).to.equal('/ghost/api/v2/admin/uploads/');
|
||||
done();
|
||||
});
|
||||
expect(server.handledRequests.length).to.equal(1);
|
||||
expect(server.handledRequests[0].url).to.equal('/ghost/api/v2/admin/uploads/');
|
||||
});
|
||||
|
||||
it('fires uploadSuccess action on successful upload', function (done) {
|
||||
it('fires uploadSuccess action on successful upload', async function () {
|
||||
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"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl uploadSuccess=(action uploadSuccess)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
|
||||
done();
|
||||
});
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
|
||||
});
|
||||
|
||||
it('doesn\'t fire uploadSuccess action on failed upload', function (done) {
|
||||
it('doesn\'t fire uploadSuccess action on failed upload', async function () {
|
||||
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"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl uploadSuccess=(action uploadSuccess)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadSuccess.calledOnce).to.be.false;
|
||||
done();
|
||||
});
|
||||
await settled();
|
||||
expect(uploadSuccess.calledOnce).to.be.false;
|
||||
});
|
||||
|
||||
it('fires fileSelected action on file selection', function (done) {
|
||||
it('fires fileSelected action on file selection', async function () {
|
||||
let fileSelected = sinon.spy();
|
||||
this.set('fileSelected', fileSelected);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl fileSelected=(action fileSelected)}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl fileSelected=(action fileSelected)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(fileSelected.calledOnce).to.be.true;
|
||||
expect(fileSelected.args[0]).to.not.be.empty;
|
||||
done();
|
||||
});
|
||||
expect(fileSelected.calledOnce).to.be.true;
|
||||
expect(fileSelected.args[0]).to.not.be.empty;
|
||||
});
|
||||
|
||||
it('fires uploadStarted action on upload start', function (done) {
|
||||
it('fires uploadStarted action on upload start', async function () {
|
||||
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"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl uploadStarted=(action uploadStarted)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadStarted.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(uploadStarted.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('fires uploadFinished action on successful upload', function (done) {
|
||||
it('fires uploadFinished action on successful upload', async function () {
|
||||
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"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl uploadFinished=(action uploadFinished)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('fires uploadFinished action on failed upload', function (done) {
|
||||
it('fires uploadFinished action on failed upload', async function () {
|
||||
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"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl uploadFinished=(action uploadFinished)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('displays invalid file type error', function (done) {
|
||||
it('displays invalid file type error', async function () {
|
||||
stubFailedUpload(server, 415, 'UnsupportedMediaTypeError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
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.$('.gh-btn-green').length, 'reset button is displayed').to.equal(1);
|
||||
expect(this.$('.gh-btn-green').text()).to.equal('Try Again');
|
||||
done();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The file type you uploaded is not supported/);
|
||||
expect(findAll('.gh-btn-green').length, 'reset button is displayed').to.equal(1);
|
||||
expect(find('.gh-btn-green').textContent).to.equal('Try Again');
|
||||
});
|
||||
|
||||
it('displays file too large for server error', function (done) {
|
||||
it('displays file too large for server error', async function () {
|
||||
stubFailedUpload(server, 413, 'RequestEntityTooLargeError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
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();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The file you uploaded was larger/);
|
||||
});
|
||||
|
||||
it('handles file too large error directly from the web server', function (done) {
|
||||
it('handles file too large error directly from the web server', async function () {
|
||||
server.post('/ghost/api/v2/admin/uploads/', function () {
|
||||
return [413, {}, ''];
|
||||
});
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
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();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The file you uploaded was larger/);
|
||||
});
|
||||
|
||||
it('displays other server-side error with message', function (done) {
|
||||
it('displays other server-side error with message', async function () {
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(this.$('.failed').text()).to.match(/Error: UnknownError/);
|
||||
done();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/Error: UnknownError/);
|
||||
});
|
||||
|
||||
it('handles unknown failure', function (done) {
|
||||
it('handles unknown failure', async function () {
|
||||
server.post('/ghost/api/v2/admin/uploads/', function () {
|
||||
return [500, {'Content-Type': 'application/json'}, ''];
|
||||
});
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(this.$('.failed').text()).to.match(/Something went wrong/);
|
||||
done();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/Something went wrong/);
|
||||
});
|
||||
|
||||
it('triggers notifications.showAPIError for VersionMismatchError', function (done) {
|
||||
it('triggers notifications.showAPIError for VersionMismatchError', async function () {
|
||||
let showAPIError = sinon.spy();
|
||||
this.set('notifications.showAPIError', showAPIError);
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
notifications.set('showAPIError', showAPIError);
|
||||
|
||||
stubFailedUpload(server, 400, 'VersionMismatchError');
|
||||
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(showAPIError.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(showAPIError.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('doesn\'t trigger notifications.showAPIError for other errors', function (done) {
|
||||
it('doesn\'t trigger notifications.showAPIError for other errors', async function () {
|
||||
let showAPIError = sinon.spy();
|
||||
this.set('notifications.showAPIError', showAPIError);
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
notifications.set('showAPIError', showAPIError);
|
||||
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(showAPIError.called).to.be.false;
|
||||
done();
|
||||
});
|
||||
expect(showAPIError.called).to.be.false;
|
||||
});
|
||||
|
||||
it('can be reset after a failed upload', function (done) {
|
||||
it('can be reset after a failed upload', async function () {
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
await click('.gh-btn-green');
|
||||
|
||||
wait().then(() => {
|
||||
run(() => {
|
||||
this.$('.gh-btn-green').click();
|
||||
});
|
||||
});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('input[type="file"]').length).to.equal(1);
|
||||
done();
|
||||
});
|
||||
expect(findAll('input[type="file"]').length).to.equal(1);
|
||||
});
|
||||
|
||||
it('displays upload progress', function (done) {
|
||||
this.set('done', done);
|
||||
|
||||
it('displays upload progress', async function () {
|
||||
// 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"]'), ['test'], {name: 'test.csv'});
|
||||
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
|
||||
fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
// TODO: replace with waitFor/waitUntil helpers
|
||||
// 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(findAll('.progress .bar').length).to.equal(1);
|
||||
let [, percentageWidth] = find('.progress .bar').getAttribute('style').match(/width: (\d+)%?/);
|
||||
percentageWidth = Number.parseInt(percentageWidth);
|
||||
expect(percentageWidth).to.be.above(0);
|
||||
expect(percentageWidth).to.be.below(100);
|
||||
}, 75);
|
||||
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('handles drag over/leave', function () {
|
||||
this.render(hbs`{{gh-file-uploader}}`);
|
||||
it('handles drag over/leave', async function () {
|
||||
await render(hbs`{{gh-file-uploader}}`);
|
||||
|
||||
run(() => {
|
||||
// eslint-disable-next-line new-cap
|
||||
@ -331,16 +282,16 @@ describe('Integration: Component: gh-file-uploader', function () {
|
||||
this.$('.gh-image-uploader').trigger(dragover);
|
||||
});
|
||||
|
||||
expect(this.$('.gh-image-uploader').hasClass('-drag-over'), 'has drag-over class').to.be.true;
|
||||
await settled();
|
||||
|
||||
run(() => {
|
||||
this.$('.gh-image-uploader').trigger('dragleave');
|
||||
});
|
||||
expect(find('.gh-image-uploader').classList.contains('-drag-over'), 'has drag-over class').to.be.true;
|
||||
|
||||
expect(this.$('.gh-image-uploader').hasClass('-drag-over'), 'has drag-over class').to.be.false;
|
||||
await triggerEvent('.gh-image-uploader', 'dragleave');
|
||||
|
||||
expect(find('.gh-image-uploader').classList.contains('-drag-over'), 'has drag-over class').to.be.false;
|
||||
});
|
||||
|
||||
it('triggers file upload on file drop', function (done) {
|
||||
it('triggers file upload on file drop', async function () {
|
||||
let uploadSuccess = sinon.spy();
|
||||
// eslint-disable-next-line new-cap
|
||||
let drop = $.Event('drop', {
|
||||
@ -352,20 +303,19 @@ describe('Integration: Component: gh-file-uploader', function () {
|
||||
this.set('uploadSuccess', uploadSuccess);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
this.render(hbs`{{gh-file-uploader url=uploadUrl uploadSuccess=(action uploadSuccess)}}`);
|
||||
await 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();
|
||||
});
|
||||
await settled();
|
||||
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
|
||||
});
|
||||
|
||||
it('validates extension by default', function (done) {
|
||||
it('validates extension by default', async function () {
|
||||
let uploadSuccess = sinon.spy();
|
||||
let uploadFailed = sinon.spy();
|
||||
|
||||
@ -374,23 +324,20 @@ describe('Integration: Component: gh-file-uploader', function () {
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader
|
||||
await render(hbs`{{gh-file-uploader
|
||||
url=uploadUrl
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
uploadFailed=(action uploadFailed)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.txt'});
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.txt'});
|
||||
|
||||
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 file type you uploaded is not supported/);
|
||||
done();
|
||||
});
|
||||
expect(uploadSuccess.called).to.be.false;
|
||||
expect(uploadFailed.calledOnce).to.be.true;
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The file type you uploaded is not supported/);
|
||||
});
|
||||
|
||||
it('uploads if validate action supplied and returns true', function (done) {
|
||||
it('uploads if validate action supplied and returns true', async function () {
|
||||
let validate = sinon.stub().returns(true);
|
||||
let uploadSuccess = sinon.spy();
|
||||
|
||||
@ -399,21 +346,20 @@ describe('Integration: Component: gh-file-uploader', function () {
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader
|
||||
await render(hbs`{{gh-file-uploader
|
||||
url=uploadUrl
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
validate=(action validate)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
await settled();
|
||||
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('skips upload and displays error if validate action supplied and doesn\'t return true', function (done) {
|
||||
it('skips upload and displays error if validate action supplied and doesn\'t return true', async function () {
|
||||
let validate = sinon.stub().returns(new UnsupportedMediaTypeError());
|
||||
let uploadSuccess = sinon.spy();
|
||||
let uploadFailed = sinon.spy();
|
||||
@ -424,21 +370,18 @@ describe('Integration: Component: gh-file-uploader', function () {
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-file-uploader
|
||||
await render(hbs`{{gh-file-uploader
|
||||
url=uploadUrl
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
uploadFailed=(action uploadFailed)
|
||||
validate=(action validate)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.csv'});
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
|
||||
|
||||
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 file type you uploaded is not supported/);
|
||||
done();
|
||||
});
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.called).to.be.false;
|
||||
expect(uploadFailed.calledOnce).to.be.true;
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The file type you uploaded is not supported/);
|
||||
});
|
||||
});
|
||||
|
@ -3,13 +3,13 @@ import Pretender from 'pretender';
|
||||
import Service from '@ember/service';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import sinon from 'sinon';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {UnsupportedMediaTypeError} from 'ghost-admin/services/ajax';
|
||||
import {click, find, findAll, render, settled, triggerEvent} from '@ember/test-helpers';
|
||||
import {createFile, fileUpload} from '../../helpers/file-upload';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
const notificationsStub = Service.extend({
|
||||
showAPIError(/* error, options */) {
|
||||
@ -46,17 +46,13 @@ const stubFailedUpload = function (server, code, error, delay = 0) {
|
||||
};
|
||||
|
||||
describe('Integration: Component: gh-image-uploader', function () {
|
||||
setupComponentTest('gh-image-upload', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
let server;
|
||||
|
||||
beforeEach(function () {
|
||||
this.register('service:session', sessionStub);
|
||||
this.register('service:notifications', notificationsStub);
|
||||
this.inject.service('session', {as: 'sessionService'});
|
||||
this.inject.service('notifications', {as: 'notifications'});
|
||||
this.owner.register('service:session', sessionStub);
|
||||
this.owner.register('service:notifications', notificationsStub);
|
||||
this.set('update', function () {});
|
||||
server = new Pretender();
|
||||
});
|
||||
@ -65,263 +61,215 @@ describe('Integration: Component: gh-image-uploader', function () {
|
||||
server.shutdown();
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
this.set('image', 'http://example.com/test.png');
|
||||
this.render(hbs`{{gh-image-uploader image=image}}`);
|
||||
await render(hbs`{{gh-image-uploader image=image}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
|
||||
it('renders form with supplied alt text', function () {
|
||||
this.render(hbs`{{gh-image-uploader image=image altText="text test"}}`);
|
||||
expect(this.$('[data-test-file-input-description]').text().trim()).to.equal('Upload image of "text test"');
|
||||
it('renders form with supplied alt text', async function () {
|
||||
await render(hbs`{{gh-image-uploader image=image altText="text test"}}`);
|
||||
expect(find('[data-test-file-input-description]')).to.have.trimmed.text('Upload image of "text test"');
|
||||
});
|
||||
|
||||
it('renders form with supplied text', function () {
|
||||
this.render(hbs`{{gh-image-uploader image=image text="text test"}}`);
|
||||
expect(this.$('[data-test-file-input-description]').text().trim()).to.equal('text test');
|
||||
it('renders form with supplied text', async function () {
|
||||
await render(hbs`{{gh-image-uploader image=image text="text test"}}`);
|
||||
expect(find('[data-test-file-input-description]')).to.have.trimmed.text('text test');
|
||||
});
|
||||
|
||||
it('generates request to correct endpoint', function (done) {
|
||||
it('generates request to correct endpoint', async function () {
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(server.handledRequests.length).to.equal(1);
|
||||
expect(server.handledRequests[0].url).to.equal('/ghost/api/v2/admin/uploads/');
|
||||
expect(server.handledRequests[0].requestHeaders.Authorization).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
expect(server.handledRequests.length).to.equal(1);
|
||||
expect(server.handledRequests[0].url).to.equal('/ghost/api/v2/admin/uploads/');
|
||||
expect(server.handledRequests[0].requestHeaders.Authorization).to.be.undefined;
|
||||
});
|
||||
|
||||
it('fires update action on successful upload', function (done) {
|
||||
it('fires update action on successful upload', async function () {
|
||||
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'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('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();
|
||||
});
|
||||
expect(update.calledOnce).to.be.true;
|
||||
expect(update.firstCall.args[0]).to.equal('/content/images/test.png');
|
||||
});
|
||||
|
||||
it('doesn\'t fire update action on failed upload', function (done) {
|
||||
it('doesn\'t fire update action on failed upload', async function () {
|
||||
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'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(update.calledOnce).to.be.false;
|
||||
done();
|
||||
});
|
||||
expect(update.calledOnce).to.be.false;
|
||||
});
|
||||
|
||||
it('fires fileSelected action on file selection', function (done) {
|
||||
it('fires fileSelected action on file selection', async function () {
|
||||
let fileSelected = sinon.spy();
|
||||
this.set('fileSelected', fileSelected);
|
||||
|
||||
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'});
|
||||
await render(hbs`{{gh-image-uploader image=image fileSelected=(action fileSelected) update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(fileSelected.calledOnce).to.be.true;
|
||||
expect(fileSelected.args[0]).to.not.be.empty;
|
||||
done();
|
||||
});
|
||||
expect(fileSelected.calledOnce).to.be.true;
|
||||
expect(fileSelected.args[0]).to.not.be.empty;
|
||||
});
|
||||
|
||||
it('fires uploadStarted action on upload start', function (done) {
|
||||
it('fires uploadStarted action on upload start', async function () {
|
||||
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"]'), ['test'], {name: 'test.png'});
|
||||
await render(hbs`{{gh-image-uploader image=image uploadStarted=(action uploadStarted) update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadStarted.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(uploadStarted.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('fires uploadFinished action on successful upload', function (done) {
|
||||
it('fires uploadFinished action on successful upload', async function () {
|
||||
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'});
|
||||
await render(hbs`{{gh-image-uploader image=image uploadFinished=(action uploadFinished) update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('fires uploadFinished action on failed upload', function (done) {
|
||||
it('fires uploadFinished action on failed upload', async function () {
|
||||
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'});
|
||||
await render(hbs`{{gh-image-uploader image=image uploadFinished=(action uploadFinished) update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(uploadFinished.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('displays invalid file type error', function (done) {
|
||||
it('displays invalid file type error', async function () {
|
||||
stubFailedUpload(server, 415, 'UnsupportedMediaTypeError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('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();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The image type you uploaded is not supported/);
|
||||
expect(findAll('.gh-btn-green').length, 'reset button is displayed').to.equal(1);
|
||||
expect(find('.gh-btn-green').textContent).to.equal('Try Again');
|
||||
});
|
||||
|
||||
it('displays file too large for server error', function (done) {
|
||||
it('displays file too large for server error', async function () {
|
||||
stubFailedUpload(server, 413, 'RequestEntityTooLargeError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('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();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The image you uploaded was larger/);
|
||||
});
|
||||
|
||||
it('handles file too large error directly from the web server', function (done) {
|
||||
it('handles file too large error directly from the web server', async function () {
|
||||
server.post('/ghost/api/v2/admin/uploads/', function () {
|
||||
return [413, {}, ''];
|
||||
});
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('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();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The image you uploaded was larger/);
|
||||
});
|
||||
|
||||
it('displays other server-side error with message', function (done) {
|
||||
it('displays other server-side error with message', async function () {
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('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(/Error: UnknownError/);
|
||||
done();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/Error: UnknownError/);
|
||||
});
|
||||
|
||||
it('handles unknown failure', function (done) {
|
||||
it('handles unknown failure', async function () {
|
||||
server.post('/ghost/api/v2/admin/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'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('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(/Something went wrong/);
|
||||
done();
|
||||
});
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/Something went wrong/);
|
||||
});
|
||||
|
||||
it('triggers notifications.showAPIError for VersionMismatchError', function (done) {
|
||||
it('triggers notifications.showAPIError for VersionMismatchError', async function () {
|
||||
let showAPIError = sinon.spy();
|
||||
this.set('notifications.showAPIError', showAPIError);
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
notifications.set('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'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(showAPIError.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(showAPIError.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('doesn\'t trigger notifications.showAPIError for other errors', function (done) {
|
||||
it('doesn\'t trigger notifications.showAPIError for other errors', async function () {
|
||||
let showAPIError = sinon.spy();
|
||||
this.set('notifications.showAPIError', showAPIError);
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
notifications.set('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'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(showAPIError.called).to.be.false;
|
||||
done();
|
||||
});
|
||||
expect(showAPIError.called).to.be.false;
|
||||
});
|
||||
|
||||
it('can be reset after a failed upload', function (done) {
|
||||
it('can be reset after a failed upload', async function () {
|
||||
stubFailedUpload(server, 400, 'UnknownError');
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {type: 'test.png'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await fileUpload('input[type="file"]', ['test'], {type: 'test.png'});
|
||||
await click('.gh-btn-green');
|
||||
|
||||
wait().then(() => {
|
||||
run(() => {
|
||||
this.$('.gh-btn-green').click();
|
||||
});
|
||||
});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('input[type="file"]').length).to.equal(1);
|
||||
done();
|
||||
});
|
||||
expect(findAll('input[type="file"]').length).to.equal(1);
|
||||
});
|
||||
|
||||
it('displays upload progress', function (done) {
|
||||
this.set('done', done);
|
||||
|
||||
it('displays upload progress', async function () {
|
||||
// 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'});
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
fileUpload('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(findAll('.progress .bar').length).to.equal(1);
|
||||
let [, percentageWidth] = find('.progress .bar').getAttribute('style').match(/width: (\d+)%?/);
|
||||
percentageWidth = Number.parseInt(percentageWidth);
|
||||
expect(percentageWidth).to.be.above(0);
|
||||
expect(percentageWidth).to.be.below(100);
|
||||
}, 75);
|
||||
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('handles drag over/leave', function () {
|
||||
it('handles drag over/leave', async function () {
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
|
||||
|
||||
run(() => {
|
||||
// eslint-disable-next-line new-cap
|
||||
@ -332,17 +280,16 @@ describe('Integration: Component: gh-image-uploader', function () {
|
||||
});
|
||||
this.$('.gh-image-uploader').trigger(dragover);
|
||||
});
|
||||
await settled();
|
||||
|
||||
expect(this.$('.gh-image-uploader').hasClass('-drag-over'), 'has drag-over class').to.be.true;
|
||||
expect(find('.gh-image-uploader').classList.contains('-drag-over'), 'has drag-over class').to.be.true;
|
||||
|
||||
run(() => {
|
||||
this.$('.gh-image-uploader').trigger('dragleave');
|
||||
});
|
||||
await triggerEvent('.gh-image-uploader', 'dragleave');
|
||||
|
||||
expect(this.$('.gh-image-uploader').hasClass('-drag-over'), 'has drag-over class').to.be.false;
|
||||
expect(find('.gh-image-uploader').classList.contains('-drag-over'), 'has drag-over class').to.be.false;
|
||||
});
|
||||
|
||||
it('triggers file upload on file drop', function (done) {
|
||||
it('triggers file upload on file drop', async function () {
|
||||
let uploadSuccess = sinon.spy();
|
||||
// eslint-disable-next-line new-cap
|
||||
let drop = $.Event('drop', {
|
||||
@ -354,20 +301,18 @@ describe('Integration: Component: gh-image-uploader', function () {
|
||||
this.set('uploadSuccess', uploadSuccess);
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
this.render(hbs`{{gh-image-uploader uploadSuccess=(action uploadSuccess)}}`);
|
||||
await render(hbs`{{gh-image-uploader uploadSuccess=(action uploadSuccess)}}`);
|
||||
|
||||
run(() => {
|
||||
this.$('.gh-image-uploader').trigger(drop);
|
||||
});
|
||||
await settled();
|
||||
|
||||
wait().then(() => {
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
|
||||
done();
|
||||
});
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.firstCall.args[0]).to.equal('/content/images/test.png');
|
||||
});
|
||||
|
||||
it('validates extension by default', function (done) {
|
||||
it('validates extension by default', async function () {
|
||||
let uploadSuccess = sinon.spy();
|
||||
let uploadFailed = sinon.spy();
|
||||
|
||||
@ -376,22 +321,19 @@ describe('Integration: Component: gh-image-uploader', function () {
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader
|
||||
await render(hbs`{{gh-image-uploader
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
uploadFailed=(action uploadFailed)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.json'});
|
||||
await fileUpload('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();
|
||||
});
|
||||
expect(uploadSuccess.called).to.be.false;
|
||||
expect(uploadFailed.calledOnce).to.be.true;
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The image type you uploaded is not supported/);
|
||||
});
|
||||
|
||||
it('uploads if validate action supplied and returns true', function (done) {
|
||||
it('uploads if validate action supplied and returns true', async function () {
|
||||
let validate = sinon.stub().returns(true);
|
||||
let uploadSuccess = sinon.spy();
|
||||
|
||||
@ -400,20 +342,17 @@ describe('Integration: Component: gh-image-uploader', function () {
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader
|
||||
await render(hbs`{{gh-image-uploader
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
validate=(action validate)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.txt'});
|
||||
await fileUpload('input[type="file"]', ['test'], {name: 'test.txt'});
|
||||
|
||||
wait().then(() => {
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
done();
|
||||
});
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('skips upload and displays error if validate action supplied and doesn\'t return true', function (done) {
|
||||
it('skips upload and displays error if validate action supplied and doesn\'t return true', async function () {
|
||||
let validate = sinon.stub().returns(new UnsupportedMediaTypeError());
|
||||
let uploadSuccess = sinon.spy();
|
||||
let uploadFailed = sinon.spy();
|
||||
@ -424,21 +363,18 @@ describe('Integration: Component: gh-image-uploader', function () {
|
||||
|
||||
stubSuccessfulUpload(server);
|
||||
|
||||
this.render(hbs`{{gh-image-uploader
|
||||
await render(hbs`{{gh-image-uploader
|
||||
uploadSuccess=(action uploadSuccess)
|
||||
uploadFailed=(action uploadFailed)
|
||||
validate=(action validate)}}`);
|
||||
|
||||
fileUpload(this.$('input[type="file"]'), ['test'], {name: 'test.png'});
|
||||
await fileUpload('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();
|
||||
});
|
||||
expect(validate.calledOnce).to.be.true;
|
||||
expect(uploadSuccess.called).to.be.false;
|
||||
expect(uploadFailed.calledOnce).to.be.true;
|
||||
expect(findAll('.failed').length, 'error message is displayed').to.equal(1);
|
||||
expect(find('.failed').textContent).to.match(/The image type you uploaded is not supported/);
|
||||
});
|
||||
|
||||
describe('unsplash', function () {
|
||||
|
@ -1,39 +1,35 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import sinon from 'sinon';
|
||||
import {click, find, findAll, render} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-image-uploader-with-preview', function () {
|
||||
setupComponentTest('gh-image-uploader-with-preview', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('renders image if provided', function () {
|
||||
it('renders image if provided', async function () {
|
||||
this.set('image', 'http://example.com/test.png');
|
||||
|
||||
this.render(hbs`{{gh-image-uploader-with-preview image=image}}`);
|
||||
await render(hbs`{{gh-image-uploader-with-preview image=image}}`);
|
||||
|
||||
expect(this.$('.gh-image-uploader.-with-image').length).to.equal(1);
|
||||
expect(this.$('img').attr('src')).to.equal('http://example.com/test.png');
|
||||
expect(findAll('.gh-image-uploader.-with-image').length).to.equal(1);
|
||||
expect(find('img').getAttribute('src')).to.equal('http://example.com/test.png');
|
||||
});
|
||||
|
||||
it('renders upload form when no image provided', function () {
|
||||
this.render(hbs`{{gh-image-uploader-with-preview image=image}}`);
|
||||
it('renders upload form when no image provided', async function () {
|
||||
await render(hbs`{{gh-image-uploader-with-preview image=image}}`);
|
||||
|
||||
expect(this.$('input[type="file"]').length).to.equal(1);
|
||||
expect(findAll('input[type="file"]').length).to.equal(1);
|
||||
});
|
||||
|
||||
it('triggers remove action when delete icon is clicked', function () {
|
||||
it('triggers remove action when delete icon is clicked', async function () {
|
||||
let remove = sinon.spy();
|
||||
this.set('remove', remove);
|
||||
this.set('image', 'http://example.com/test.png');
|
||||
|
||||
this.render(hbs`{{gh-image-uploader-with-preview image=image remove=(action remove)}}`);
|
||||
run(() => {
|
||||
this.$('.image-cancel').click();
|
||||
});
|
||||
await render(hbs`{{gh-image-uploader-with-preview image=image remove=(action remove)}}`);
|
||||
await click('.image-cancel');
|
||||
|
||||
expect(remove.calledOnce).to.be.true;
|
||||
});
|
||||
|
@ -1,24 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-koenig-editor', function () {
|
||||
setupComponentTest('gh-koenig-editor', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-koenig-editor}}
|
||||
// template content
|
||||
// {{/gh-koenig-editor}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-koenig-editor}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,33 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-markdown-editor', function () {
|
||||
setupComponentTest('gh-markdown-editor', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-markdown-editor}}
|
||||
// template content
|
||||
// {{/gh-markdown-editor}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-markdown-editor}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
|
||||
describe('unsplash', function () {
|
||||
it('has unsplash icon in toolbar if unsplash is active');
|
||||
it('opens unsplash modal when clicked');
|
||||
it('closes unsplash modal when close triggered');
|
||||
it('inserts unsplash image & credit when selected');
|
||||
it('inserts at cursor when editor has focus');
|
||||
it('inserts at end when editor is blurred');
|
||||
});
|
||||
});
|
@ -1,23 +1,21 @@
|
||||
import NavItem from 'ghost-admin/models/navigation-item';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {click, render, triggerEvent} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-navitem', function () {
|
||||
setupComponentTest('gh-navitem', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
this.set('baseUrl', 'http://localhost:2368');
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
this.set('navItem', NavItem.create({label: 'Test', url: '/url'}));
|
||||
|
||||
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
|
||||
await render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
|
||||
let $item = this.$('.gh-blognav-item');
|
||||
|
||||
expect($item.find('.gh-blognav-grab').length).to.equal(1);
|
||||
@ -31,95 +29,95 @@ describe('Integration: Component: gh-navitem', function () {
|
||||
expect($item.find('.response:visible').length).to.equal(0);
|
||||
});
|
||||
|
||||
it('doesn\'t show drag handle for new items', function () {
|
||||
it('doesn\'t show drag handle for new items', async function () {
|
||||
this.set('navItem', NavItem.create({label: 'Test', url: '/url', isNew: true}));
|
||||
|
||||
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
|
||||
await render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
|
||||
let $item = this.$('.gh-blognav-item');
|
||||
|
||||
expect($item.find('.gh-blognav-grab').length).to.equal(0);
|
||||
});
|
||||
|
||||
it('shows add button for new items', function () {
|
||||
it('shows add button for new items', async function () {
|
||||
this.set('navItem', NavItem.create({label: 'Test', url: '/url', isNew: true}));
|
||||
|
||||
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
|
||||
await render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
|
||||
let $item = this.$('.gh-blognav-item');
|
||||
|
||||
expect($item.find('.gh-blognav-add').length).to.equal(1);
|
||||
expect($item.find('.gh-blognav-delete').length).to.equal(0);
|
||||
});
|
||||
|
||||
it('triggers delete action', function () {
|
||||
it('triggers delete action', async function () {
|
||||
this.set('navItem', NavItem.create({label: 'Test', url: '/url'}));
|
||||
|
||||
let deleteActionCallCount = 0;
|
||||
this.on('deleteItem', (navItem) => {
|
||||
this.set('deleteItem', (navItem) => {
|
||||
expect(navItem).to.equal(this.get('navItem'));
|
||||
deleteActionCallCount += 1;
|
||||
});
|
||||
|
||||
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl deleteItem=(action "deleteItem")}}`);
|
||||
this.$('.gh-blognav-delete').trigger('click');
|
||||
await render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl deleteItem=(action deleteItem)}}`);
|
||||
await click('.gh-blognav-delete');
|
||||
|
||||
expect(deleteActionCallCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('triggers add action', function () {
|
||||
it('triggers add action', async function () {
|
||||
this.set('navItem', NavItem.create({label: 'Test', url: '/url', isNew: true}));
|
||||
|
||||
let addActionCallCount = 0;
|
||||
this.on('add', () => {
|
||||
this.set('add', () => {
|
||||
addActionCallCount += 1;
|
||||
});
|
||||
|
||||
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl addItem=(action "add")}}`);
|
||||
this.$('.gh-blognav-add').trigger('click');
|
||||
await render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl addItem=(action add)}}`);
|
||||
await click('.gh-blognav-add');
|
||||
|
||||
expect(addActionCallCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('triggers update url action', function () {
|
||||
it('triggers update url action', async function () {
|
||||
this.set('navItem', NavItem.create({label: 'Test', url: '/url'}));
|
||||
|
||||
let updateActionCallCount = 0;
|
||||
this.on('update', () => {
|
||||
this.set('update', (value) => {
|
||||
updateActionCallCount += 1;
|
||||
return value;
|
||||
});
|
||||
|
||||
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl updateUrl=(action "update")}}`);
|
||||
this.$('.gh-blognav-url input').trigger('blur');
|
||||
await render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl updateUrl=(action update)}}`);
|
||||
await triggerEvent('.gh-blognav-url input', 'blur');
|
||||
|
||||
expect(updateActionCallCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('triggers update label action', function () {
|
||||
it('triggers update label action', async function () {
|
||||
this.set('navItem', NavItem.create({label: 'Test', url: '/url'}));
|
||||
|
||||
let updateActionCallCount = 0;
|
||||
this.on('update', () => {
|
||||
this.set('update', (value) => {
|
||||
updateActionCallCount += 1;
|
||||
return value;
|
||||
});
|
||||
|
||||
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl updateLabel=(action "update")}}`);
|
||||
this.$('.gh-blognav-label input').trigger('blur');
|
||||
await render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl updateLabel=(action update)}}`);
|
||||
await triggerEvent('.gh-blognav-label input', 'blur');
|
||||
|
||||
expect(updateActionCallCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('displays inline errors', function () {
|
||||
it('displays inline errors', async function () {
|
||||
this.set('navItem', NavItem.create({label: '', url: ''}));
|
||||
this.get('navItem').validate();
|
||||
|
||||
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
|
||||
await render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
|
||||
let $item = this.$('.gh-blognav-item');
|
||||
|
||||
return wait().then(() => {
|
||||
expect($item.hasClass('gh-blognav-item--error')).to.be.true;
|
||||
expect($item.find('.gh-blognav-label').hasClass('error')).to.be.true;
|
||||
expect($item.find('.gh-blognav-label .response').text().trim()).to.equal('You must specify a label');
|
||||
expect($item.find('.gh-blognav-url').hasClass('error')).to.be.true;
|
||||
expect($item.find('.gh-blognav-url .response').text().trim()).to.equal('You must specify a URL or relative path');
|
||||
});
|
||||
expect($item.hasClass('gh-blognav-item--error')).to.be.true;
|
||||
expect($item.find('.gh-blognav-label').hasClass('error')).to.be.true;
|
||||
expect($item.find('.gh-blognav-label .response').text().trim()).to.equal('You must specify a label');
|
||||
expect($item.find('.gh-blognav-url').hasClass('error')).to.be.true;
|
||||
expect($item.find('.gh-blognav-url .response').text().trim()).to.equal('You must specify a URL or relative path');
|
||||
});
|
||||
});
|
||||
|
@ -1,423 +1,350 @@
|
||||
import $ from 'jquery';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {blur, click, fillIn, find, findAll, render, triggerKeyEvent} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
// we want baseUrl to match the running domain so relative URLs are
|
||||
// handled as expected (browser auto-sets the domain when using a.href)
|
||||
let currentUrl = `${window.location.protocol}//${window.location.host}/`;
|
||||
|
||||
describe('Integration: Component: gh-navitem-url-input', function () {
|
||||
setupComponentTest('gh-navitem-url-input', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
// set defaults
|
||||
this.set('baseUrl', currentUrl);
|
||||
this.set('url', '');
|
||||
this.set('isNew', false);
|
||||
this.on('clearErrors', function () {
|
||||
this.set('clearErrors', function () {
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly with blank url', function () {
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action "clearErrors")}}
|
||||
it('renders correctly with blank url', async function () {
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
expect($input).to.have.length(1);
|
||||
expect($input.hasClass('gh-input')).to.be.true;
|
||||
expect($input.val()).to.equal(currentUrl);
|
||||
expect(findAll('input')).to.have.length(1);
|
||||
expect(find('input')).to.have.class('gh-input');
|
||||
expect(find('input')).to.have.value(currentUrl);
|
||||
});
|
||||
|
||||
it('renders correctly with relative urls', function () {
|
||||
it('renders correctly with relative urls', async function () {
|
||||
this.set('url', '/about');
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action "clearErrors")}}
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
expect($input.val()).to.equal(`${currentUrl}about`);
|
||||
expect(find('input')).to.have.value(`${currentUrl}about`);
|
||||
|
||||
this.set('url', '/about#contact');
|
||||
expect($input.val()).to.equal(`${currentUrl}about#contact`);
|
||||
expect(find('input')).to.have.value(`${currentUrl}about#contact`);
|
||||
});
|
||||
|
||||
it('renders correctly with absolute urls', function () {
|
||||
it('renders correctly with absolute urls', async function () {
|
||||
this.set('url', 'https://example.com:2368/#test');
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action "clearErrors")}}
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
expect($input.val()).to.equal('https://example.com:2368/#test');
|
||||
expect(find('input')).to.have.value('https://example.com:2368/#test');
|
||||
|
||||
this.set('url', 'mailto:test@example.com');
|
||||
expect($input.val()).to.equal('mailto:test@example.com');
|
||||
expect(find('input')).to.have.value('mailto:test@example.com');
|
||||
|
||||
this.set('url', 'tel:01234-5678-90');
|
||||
expect($input.val()).to.equal('tel:01234-5678-90');
|
||||
expect(find('input')).to.have.value('tel:01234-5678-90');
|
||||
|
||||
this.set('url', '//protocol-less-url.com');
|
||||
expect($input.val()).to.equal('//protocol-less-url.com');
|
||||
expect(find('input')).to.have.value('//protocol-less-url.com');
|
||||
|
||||
this.set('url', '#anchor');
|
||||
expect($input.val()).to.equal('#anchor');
|
||||
expect(find('input')).to.have.value('#anchor');
|
||||
});
|
||||
|
||||
it('deletes base URL on backspace', function () {
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action "clearErrors")}}
|
||||
it('deletes base URL on backspace', async function () {
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
expect($input.val()).to.equal(currentUrl);
|
||||
run(() => {
|
||||
// TODO: why is ember's keyEvent helper not available here?
|
||||
// eslint-disable-next-line new-cap
|
||||
let e = $.Event('keydown');
|
||||
e.keyCode = 8;
|
||||
$input.trigger(e);
|
||||
});
|
||||
expect($input.val()).to.equal('');
|
||||
expect(find('input')).to.have.value(currentUrl);
|
||||
await triggerKeyEvent('input', 'keydown', 8);
|
||||
expect(find('input')).to.have.value('');
|
||||
});
|
||||
|
||||
it('deletes base URL on delete', function () {
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action "clearErrors")}}
|
||||
it('deletes base URL on delete', async function () {
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
expect($input.val()).to.equal(currentUrl);
|
||||
run(() => {
|
||||
// TODO: why is ember's keyEvent helper not available here?
|
||||
// eslint-disable-next-line new-cap
|
||||
let e = $.Event('keydown');
|
||||
e.keyCode = 46;
|
||||
$input.trigger(e);
|
||||
});
|
||||
expect($input.val()).to.equal('');
|
||||
expect(find('input')).to.have.value(currentUrl);
|
||||
await triggerKeyEvent('input', 'keydown', 46);
|
||||
expect(find('input')).to.have.value('');
|
||||
});
|
||||
|
||||
it('adds base url to relative urls on blur', function () {
|
||||
this.on('updateUrl', () => null);
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
it('adds base url to relative urls on blur', async function () {
|
||||
this.set('updateUrl', val => val);
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
run(() => {
|
||||
$input.val('/about').trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
await fillIn('input', '/about');
|
||||
await blur('input');
|
||||
|
||||
expect($input.val()).to.equal(`${currentUrl}about`);
|
||||
expect(find('input')).to.have.value(`${currentUrl}about/`);
|
||||
});
|
||||
|
||||
it('adds "mailto:" to email addresses on blur', function () {
|
||||
this.on('updateUrl', () => null);
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
it('adds "mailto:" to email addresses on blur', async function () {
|
||||
this.set('updateUrl', val => val);
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
run(() => {
|
||||
$input.val('test@example.com').trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
await fillIn('input', 'test@example.com');
|
||||
await blur('input');
|
||||
|
||||
expect($input.val()).to.equal('mailto:test@example.com');
|
||||
expect(find('input')).to.have.value('mailto:test@example.com');
|
||||
|
||||
// ensure we don't double-up on the mailto:
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
expect($input.val()).to.equal('mailto:test@example.com');
|
||||
await blur('input');
|
||||
expect(find('input')).to.have.value('mailto:test@example.com');
|
||||
});
|
||||
|
||||
it('doesn\'t add base url to invalid urls on blur', function () {
|
||||
this.on('updateUrl', () => null);
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
it('doesn\'t add base url to invalid urls on blur', async function () {
|
||||
this.set('updateUrl', val => val);
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
let changeValue = function (value) {
|
||||
run(() => {
|
||||
$input.val(value).trigger('input').trigger('blur');
|
||||
});
|
||||
let changeValue = async (value) => {
|
||||
await fillIn('input', value);
|
||||
await blur('input');
|
||||
};
|
||||
|
||||
changeValue('with spaces');
|
||||
expect($input.val()).to.equal('with spaces');
|
||||
await changeValue('with spaces');
|
||||
expect(find('input')).to.have.value('with spaces');
|
||||
|
||||
changeValue('/with spaces');
|
||||
expect($input.val()).to.equal('/with spaces');
|
||||
await changeValue('/with spaces');
|
||||
expect(find('input')).to.have.value('/with spaces');
|
||||
});
|
||||
|
||||
it('doesn\'t mangle invalid urls on blur', function () {
|
||||
this.on('updateUrl', () => null);
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
it('doesn\'t mangle invalid urls on blur', async function () {
|
||||
this.set('updateUrl', val => val);
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
run(() => {
|
||||
$input.val(`${currentUrl} /test`).trigger('input').trigger('blur');
|
||||
});
|
||||
await fillIn('input', `${currentUrl} /test`);
|
||||
await blur('input');
|
||||
|
||||
expect($input.val()).to.equal(`${currentUrl} /test`);
|
||||
expect(find('input')).to.have.value(`${currentUrl} /test`);
|
||||
});
|
||||
|
||||
// https://github.com/TryGhost/Ghost/issues/9373
|
||||
it('doesn\'t mangle urls when baseUrl has unicode characters', function () {
|
||||
this.on('updateUrl', () => null);
|
||||
it('doesn\'t mangle urls when baseUrl has unicode characters', async function () {
|
||||
this.set('updateUrl', val => val);
|
||||
|
||||
this.set('baseUrl', 'http://exämple.com');
|
||||
|
||||
this.render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs`
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
await fillIn('input', `${currentUrl}/test`);
|
||||
await blur('input');
|
||||
|
||||
run(() => {
|
||||
$input.val(`${currentUrl}/test`).trigger('input').trigger('blur');
|
||||
});
|
||||
|
||||
expect($input.val()).to.equal(`${currentUrl}/test`);
|
||||
expect(find('input')).to.have.value(`${currentUrl}/test`);
|
||||
});
|
||||
|
||||
it('triggers "update" action on blur', function () {
|
||||
it('triggers "update" action on blur', async function () {
|
||||
let changeActionCallCount = 0;
|
||||
this.on('updateUrl', () => {
|
||||
this.set('updateUrl', (val) => {
|
||||
changeActionCallCount += 1;
|
||||
return val;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
$input.trigger('blur');
|
||||
await click('input');
|
||||
await blur('input');
|
||||
|
||||
expect(changeActionCallCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('triggers "update" action on enter', function () {
|
||||
it('triggers "update" action on enter', async function () {
|
||||
let changeActionCallCount = 0;
|
||||
this.on('updateUrl', () => {
|
||||
this.set('updateUrl', (val) => {
|
||||
changeActionCallCount += 1;
|
||||
return val;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
await triggerKeyEvent('input', 'keypress', 13);
|
||||
|
||||
run(() => {
|
||||
// TODO: why is ember's keyEvent helper not available here?
|
||||
// eslint-disable-next-line new-cap
|
||||
let e = $.Event('keypress');
|
||||
e.keyCode = 13;
|
||||
$input.trigger(e);
|
||||
expect(changeActionCallCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('triggers "update" action on CMD-S', async function () {
|
||||
let changeActionCallCount = 0;
|
||||
this.set('updateUrl', (val) => {
|
||||
changeActionCallCount += 1;
|
||||
return val;
|
||||
});
|
||||
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
await triggerKeyEvent('input', 'keydown', 83, {
|
||||
metaKey: true
|
||||
});
|
||||
|
||||
expect(changeActionCallCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('triggers "update" action on CMD-S', function () {
|
||||
let changeActionCallCount = 0;
|
||||
this.on('updateUrl', () => {
|
||||
changeActionCallCount += 1;
|
||||
it('sends absolute urls straight through to update action', async function () {
|
||||
let lastSeenUrl = '';
|
||||
|
||||
this.set('updateUrl', (url) => {
|
||||
lastSeenUrl = url;
|
||||
return url;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
run(() => {
|
||||
// TODO: why is ember's keyEvent helper not available here?
|
||||
// eslint-disable-next-line new-cap
|
||||
let e = $.Event('keydown');
|
||||
e.keyCode = 83;
|
||||
e.metaKey = true;
|
||||
$input.trigger(e);
|
||||
});
|
||||
|
||||
expect(changeActionCallCount).to.equal(1);
|
||||
});
|
||||
|
||||
it('sends absolute urls straight through to change action', function () {
|
||||
let expectedUrl = '';
|
||||
|
||||
this.on('updateUrl', (url) => {
|
||||
expect(url).to.equal(expectedUrl);
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
let testUrl = (url) => {
|
||||
expectedUrl = url;
|
||||
run(() => {
|
||||
$input.val(url).trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
let testUrl = async (url) => {
|
||||
await fillIn('input', url);
|
||||
await blur('input');
|
||||
expect(lastSeenUrl).to.equal(url);
|
||||
};
|
||||
|
||||
testUrl('http://example.com');
|
||||
testUrl('http://example.com/');
|
||||
testUrl('https://example.com');
|
||||
testUrl('//example.com');
|
||||
testUrl('//localhost:1234');
|
||||
testUrl('#anchor');
|
||||
testUrl('mailto:test@example.com');
|
||||
testUrl('tel:12345-567890');
|
||||
testUrl('javascript:alert("testing");');
|
||||
await testUrl('http://example.com');
|
||||
await testUrl('http://example.com/');
|
||||
await testUrl('https://example.com');
|
||||
await testUrl('//example.com');
|
||||
await testUrl('//localhost:1234');
|
||||
await testUrl('#anchor');
|
||||
await testUrl('mailto:test@example.com');
|
||||
await testUrl('tel:12345-567890');
|
||||
await testUrl('javascript:alert("testing");');
|
||||
});
|
||||
|
||||
it('strips base url from relative urls before sending to change action', function () {
|
||||
let expectedUrl = '';
|
||||
it('strips base url from relative urls before sending to update action', async function () {
|
||||
let lastSeenUrl = '';
|
||||
|
||||
this.on('updateUrl', (url) => {
|
||||
expect(url).to.equal(expectedUrl);
|
||||
this.set('updateUrl', (url) => {
|
||||
lastSeenUrl = url;
|
||||
return url;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
let testUrl = (url) => {
|
||||
expectedUrl = `/${url}`;
|
||||
run(() => {
|
||||
$input.val(`${currentUrl}${url}`).trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
let testUrl = async (url) => {
|
||||
await fillIn('input', `${currentUrl}${url}`);
|
||||
await blur('input');
|
||||
expect(lastSeenUrl).to.equal(`/${url}`);
|
||||
};
|
||||
|
||||
testUrl('about/');
|
||||
testUrl('about#contact');
|
||||
testUrl('test/nested/');
|
||||
await testUrl('about/');
|
||||
await testUrl('about#contact');
|
||||
await testUrl('test/nested/');
|
||||
});
|
||||
|
||||
it('handles links to subdomains of blog domain', function () {
|
||||
it('handles links to subdomains of blog domain', async function () {
|
||||
let expectedUrl = '';
|
||||
|
||||
this.set('baseUrl', 'http://example.com/');
|
||||
|
||||
this.on('updateUrl', (url) => {
|
||||
this.set('updateUrl', (url) => {
|
||||
expect(url).to.equal(expectedUrl);
|
||||
return url;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
expectedUrl = 'http://test.example.com/';
|
||||
run(() => {
|
||||
$input.val(expectedUrl).trigger('input').trigger('blur');
|
||||
});
|
||||
expect($input.val()).to.equal(expectedUrl);
|
||||
await fillIn('input', expectedUrl);
|
||||
await blur('input');
|
||||
expect(find('input')).to.have.value(expectedUrl);
|
||||
});
|
||||
|
||||
it('adds trailing slash to relative URL', function () {
|
||||
let expectedUrl = '';
|
||||
it('adds trailing slash to relative URL', async function () {
|
||||
let lastSeenUrl = '';
|
||||
|
||||
this.on('updateUrl', (url) => {
|
||||
expect(url).to.equal(expectedUrl);
|
||||
this.set('updateUrl', (url) => {
|
||||
lastSeenUrl = url;
|
||||
return url;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
let testUrl = (url) => {
|
||||
expectedUrl = `/${url}/`;
|
||||
run(() => {
|
||||
$input.val(`${currentUrl}${url}`).trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
let testUrl = async (url) => {
|
||||
await fillIn('input', `${currentUrl}${url}`);
|
||||
await blur('input');
|
||||
expect(lastSeenUrl).to.equal(`/${url}/`);
|
||||
};
|
||||
|
||||
testUrl('about');
|
||||
testUrl('test/nested');
|
||||
await testUrl('about');
|
||||
await testUrl('test/nested');
|
||||
});
|
||||
|
||||
it('does not add trailing slash on relative URL with [.?#]', function () {
|
||||
let expectedUrl = '';
|
||||
it('does not add trailing slash on relative URL with [.?#]', async function () {
|
||||
let lastSeenUrl = '';
|
||||
|
||||
this.on('updateUrl', (url) => {
|
||||
expect(url).to.equal(expectedUrl);
|
||||
this.set('updateUrl', (url) => {
|
||||
lastSeenUrl = url;
|
||||
return url;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
let testUrl = (url) => {
|
||||
expectedUrl = `/${url}`;
|
||||
run(() => {
|
||||
$input.val(`${currentUrl}${url}`).trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
let testUrl = async (url) => {
|
||||
await fillIn('input', `${currentUrl}${url}`);
|
||||
await blur('input');
|
||||
expect(lastSeenUrl).to.equal(`/${url}`);
|
||||
};
|
||||
|
||||
testUrl('about#contact');
|
||||
testUrl('test/nested.svg');
|
||||
testUrl('test?gho=sties');
|
||||
testUrl('test/nested?sli=mer');
|
||||
await testUrl('about#contact');
|
||||
await testUrl('test/nested.svg');
|
||||
await testUrl('test?gho=sties');
|
||||
await testUrl('test/nested?sli=mer');
|
||||
});
|
||||
|
||||
it('does not add trailing slash on non-relative URLs', function () {
|
||||
let expectedUrl = '';
|
||||
it('does not add trailing slash on non-relative URLs', async function () {
|
||||
let lastSeenUrl = '';
|
||||
|
||||
this.on('updateUrl', (url) => {
|
||||
expect(url).to.equal(expectedUrl);
|
||||
this.set('updateUrl', (url) => {
|
||||
lastSeenUrl = url;
|
||||
return url;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
let testUrl = (url) => {
|
||||
expectedUrl = `/${url}`;
|
||||
run(() => {
|
||||
$input.val(`${currentUrl}${url}`).trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
let testUrl = async (url) => {
|
||||
await fillIn('input', url);
|
||||
await blur('input');
|
||||
expect(lastSeenUrl).to.equal(url);
|
||||
};
|
||||
|
||||
testUrl('http://woo.ff/test');
|
||||
testUrl('http://me.ow:2342/nested/test');
|
||||
testUrl('https://wro.om/car#race');
|
||||
testUrl('https://kabo.om/explosion?really=now');
|
||||
await testUrl('http://woo.ff/test');
|
||||
await testUrl('http://me.ow:2342/nested/test');
|
||||
await testUrl('https://wro.om/car#race');
|
||||
await testUrl('https://kabo.om/explosion?really=now');
|
||||
});
|
||||
|
||||
describe('with sub-folder baseUrl', function () {
|
||||
@ -425,65 +352,57 @@ describe('Integration: Component: gh-navitem-url-input', function () {
|
||||
this.set('baseUrl', `${currentUrl}blog/`);
|
||||
});
|
||||
|
||||
it('handles URLs relative to base url', function () {
|
||||
let expectedUrl = '';
|
||||
it('handles URLs relative to base url', async function () {
|
||||
let lastSeenUrl = '';
|
||||
|
||||
this.on('updateUrl', (url) => {
|
||||
expect(url).to.equal(expectedUrl);
|
||||
this.set('updateUrl', (url) => {
|
||||
lastSeenUrl = url;
|
||||
return url;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
let testUrl = (url) => {
|
||||
expectedUrl = url;
|
||||
run(() => {
|
||||
$input.val(`${currentUrl}blog${url}`).trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
let testUrl = async (url) => {
|
||||
await fillIn('input', `${currentUrl}blog${url}`);
|
||||
await blur('input');
|
||||
expect(lastSeenUrl).to.equal(url);
|
||||
};
|
||||
|
||||
testUrl('/about/');
|
||||
testUrl('/about#contact');
|
||||
testUrl('/test/nested/');
|
||||
await testUrl('/about/');
|
||||
await testUrl('/about#contact');
|
||||
await testUrl('/test/nested/');
|
||||
});
|
||||
|
||||
it('handles URLs relative to base host', function () {
|
||||
let expectedUrl = '';
|
||||
it('handles URLs relative to base host', async function () {
|
||||
let lastSeenUrl = '';
|
||||
|
||||
this.on('updateUrl', (url) => {
|
||||
expect(url).to.equal(expectedUrl);
|
||||
this.set('updateUrl', (url) => {
|
||||
lastSeenUrl = url;
|
||||
return url;
|
||||
});
|
||||
|
||||
this.render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action "updateUrl") clearErrors=(action "clearErrors")}}
|
||||
await render(hbs `
|
||||
{{gh-navitem-url-input baseUrl=baseUrl url=url isNew=isNew update=(action updateUrl) clearErrors=(action clearErrors)}}
|
||||
`);
|
||||
let $input = this.$('input');
|
||||
|
||||
let testUrl = (url) => {
|
||||
expectedUrl = url;
|
||||
run(() => {
|
||||
$input.val(url).trigger('input');
|
||||
});
|
||||
run(() => {
|
||||
$input.trigger('blur');
|
||||
});
|
||||
let testUrl = async (url) => {
|
||||
await fillIn('input', url);
|
||||
await blur('input');
|
||||
expect(lastSeenUrl).to.equal(url);
|
||||
};
|
||||
|
||||
testUrl(`http://${window.location.host}`);
|
||||
testUrl(`https://${window.location.host}`);
|
||||
testUrl(`http://${window.location.host}/`);
|
||||
testUrl(`https://${window.location.host}/`);
|
||||
testUrl(`http://${window.location.host}/test`);
|
||||
testUrl(`https://${window.location.host}/test`);
|
||||
testUrl(`http://${window.location.host}/#test`);
|
||||
testUrl(`https://${window.location.host}/#test`);
|
||||
testUrl(`http://${window.location.host}/another/folder`);
|
||||
testUrl(`https://${window.location.host}/another/folder`);
|
||||
await testUrl(`http://${window.location.host}`);
|
||||
await testUrl(`https://${window.location.host}`);
|
||||
await testUrl(`http://${window.location.host}/`);
|
||||
await testUrl(`https://${window.location.host}/`);
|
||||
await testUrl(`http://${window.location.host}/test`);
|
||||
await testUrl(`https://${window.location.host}/test`);
|
||||
await testUrl(`http://${window.location.host}/#test`);
|
||||
await testUrl(`https://${window.location.host}/#test`);
|
||||
await testUrl(`http://${window.location.host}/another/folder`);
|
||||
await testUrl(`https://${window.location.host}/another/folder`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,38 +1,40 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {find, render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-notification', function () {
|
||||
setupComponentTest('gh-notification', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
this.set('message', {message: 'Test message', type: 'success'});
|
||||
|
||||
this.render(hbs`{{gh-notification message=message}}`);
|
||||
await render(hbs`{{gh-notification message=message}}`);
|
||||
|
||||
expect(this.$('article.gh-notification')).to.have.length(1);
|
||||
let $notification = this.$('.gh-notification');
|
||||
expect(find('article.gh-notification')).to.exist;
|
||||
|
||||
expect($notification.hasClass('gh-notification-passive')).to.be.true;
|
||||
expect($notification.text()).to.match(/Test message/);
|
||||
let notification = find('.gh-notification');
|
||||
expect(notification).to.have.class('gh-notification-passive');
|
||||
expect(notification).to.contain.text('Test message');
|
||||
});
|
||||
|
||||
it('maps message types to CSS classes', function () {
|
||||
it('maps message types to CSS classes', async function () {
|
||||
this.set('message', {message: 'Test message', type: 'success'});
|
||||
|
||||
this.render(hbs`{{gh-notification message=message}}`);
|
||||
let $notification = this.$('.gh-notification');
|
||||
await render(hbs`{{gh-notification message=message}}`);
|
||||
let notification = find('.gh-notification');
|
||||
|
||||
this.set('message.type', 'success');
|
||||
expect($notification.hasClass('gh-notification-green'), 'success class isn\'t green').to.be.true;
|
||||
expect(notification, 'success class is green')
|
||||
.to.have.class('gh-notification-green');
|
||||
|
||||
this.set('message.type', 'error');
|
||||
expect($notification.hasClass('gh-notification-red'), 'success class isn\'t red').to.be.true;
|
||||
expect(notification, 'success class is red')
|
||||
.to.have.class('gh-notification-red');
|
||||
|
||||
this.set('message.type', 'warn');
|
||||
expect($notification.hasClass('gh-notification-yellow'), 'success class isn\'t yellow').to.be.true;
|
||||
expect(notification, 'success class is yellow')
|
||||
.to.have.class('gh-notification-yellow');
|
||||
});
|
||||
});
|
||||
|
@ -3,34 +3,35 @@ import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {A as emberA} from '@ember/array';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {find, render, settled} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
let notificationsStub = Service.extend({
|
||||
notifications: emberA()
|
||||
});
|
||||
|
||||
describe('Integration: Component: gh-notifications', function () {
|
||||
setupComponentTest('gh-notifications', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
this.register('service:notifications', notificationsStub);
|
||||
this.inject.service('notifications', {as: 'notifications'});
|
||||
this.owner.register('service:notifications', notificationsStub);
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
|
||||
this.set('notifications.notifications', [
|
||||
notifications.set('notifications', [
|
||||
{message: 'First', type: 'error'},
|
||||
{message: 'Second', type: 'warn'}
|
||||
]);
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.render(hbs`{{gh-notifications}}`);
|
||||
expect(this.$('.gh-notifications').length).to.equal(1);
|
||||
it('renders', async function () {
|
||||
await render(hbs`{{gh-notifications}}`);
|
||||
expect(find('.gh-notifications')).to.exist;
|
||||
|
||||
expect(this.$('.gh-notifications').children().length).to.equal(2);
|
||||
expect(find('.gh-notifications').children.length).to.equal(2);
|
||||
|
||||
this.set('notifications.notifications', emberA());
|
||||
expect(this.$('.gh-notifications').children().length).to.equal(0);
|
||||
let notifications = this.owner.lookup('service:notifications');
|
||||
notifications.set('notifications', emberA());
|
||||
await settled();
|
||||
expect(find('.gh-notifications').children.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
@ -2,11 +2,10 @@ import Pretender from 'pretender';
|
||||
import Service from '@ember/service';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import md5 from 'npm:blueimp-md5';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {find, render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
import {timeout} from 'ember-concurrency';
|
||||
|
||||
let pathsStub = Service.extend({
|
||||
@ -51,17 +50,13 @@ let configStubuseGravatar = Service.extend({
|
||||
});
|
||||
|
||||
describe('Integration: Component: gh-profile-image', function () {
|
||||
setupComponentTest('gh-profile-image', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
let server;
|
||||
|
||||
beforeEach(function () {
|
||||
this.register('service:ghost-paths', pathsStub);
|
||||
this.inject.service('ghost-paths', {as: 'ghost-paths'});
|
||||
this.register('service:config', configStubuseGravatar);
|
||||
this.inject.service('config', {as: 'config'});
|
||||
this.owner.register('service:ghost-paths', pathsStub);
|
||||
this.owner.register('service:config', configStubuseGravatar);
|
||||
|
||||
server = new Pretender();
|
||||
stubKnownGravatar(server);
|
||||
@ -71,25 +66,29 @@ describe('Integration: Component: gh-profile-image', function () {
|
||||
server.shutdown();
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
this.set('email', '');
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{gh-profile-image email=email}}
|
||||
`);
|
||||
|
||||
expect(this.$()).to.have.length(1);
|
||||
expect(find('.account-image')).to.exist;
|
||||
expect(find('.placeholder-img')).to.exist;
|
||||
expect(find('input[type="file"]')).to.exist;
|
||||
});
|
||||
|
||||
it('renders default image if no email supplied', function () {
|
||||
it('renders default image if no email supplied', async function () {
|
||||
this.set('email', null);
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{gh-profile-image email=email size=100 debounce=50}}
|
||||
`);
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.equal('display: none');
|
||||
expect(
|
||||
find('.gravatar-img'),
|
||||
'gravatar image style'
|
||||
).to.have.attribute('style', 'display: none');
|
||||
});
|
||||
|
||||
it('renders the gravatar if valid email supplied and privacy.useGravatar allows it', async function () {
|
||||
@ -98,44 +97,44 @@ describe('Integration: Component: gh-profile-image', function () {
|
||||
|
||||
this.set('email', email);
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{gh-profile-image email=email size=100 debounce=50}}
|
||||
`);
|
||||
|
||||
// wait for the ajax request to complete
|
||||
await wait();
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.equal(`background-image: url(${expectedUrl}); display: block`);
|
||||
expect(
|
||||
find('.gravatar-img'),
|
||||
'gravatar image style'
|
||||
).to.have.attribute('style', `background-image: url(${expectedUrl}); display: block`);
|
||||
});
|
||||
|
||||
it('doesn\'t render the gravatar if valid email supplied but privacy.useGravatar forbids it', async function () {
|
||||
let config = this.owner.lookup('service:config');
|
||||
let email = 'test@example.com';
|
||||
|
||||
this.set('email', email);
|
||||
this.set('config.useGravatar', false);
|
||||
config.set('useGravatar', false);
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{gh-profile-image email=email size=100 debounce=50}}
|
||||
`);
|
||||
|
||||
await wait();
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.equal('display: none');
|
||||
expect(
|
||||
find('.gravatar-img'),
|
||||
'gravatar image style'
|
||||
).to.have.attribute('style', 'display: none');
|
||||
});
|
||||
|
||||
it('doesn\'t add background url if gravatar image doesn\'t exist', async function () {
|
||||
stubUnknownGravatar(server);
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{gh-profile-image email="test@example.com" size=100 debounce=50}}
|
||||
`);
|
||||
|
||||
await wait();
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), 'gravatar image style')
|
||||
.to.equal('background-image: url(); display: none');
|
||||
expect(
|
||||
find('.gravatar-img'),
|
||||
'gravatar image style'
|
||||
).to.have.attribute('style', 'background-image: url(); display: none');
|
||||
});
|
||||
|
||||
it('throttles gravatar loading as email is changed', async function () {
|
||||
@ -144,25 +143,31 @@ describe('Integration: Component: gh-profile-image', function () {
|
||||
|
||||
this.set('email', 'test');
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{gh-profile-image email=email size=100 debounce=300}}
|
||||
`);
|
||||
|
||||
run(() => {
|
||||
this.set('email', email);
|
||||
});
|
||||
this.set('email', email);
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img background not immediately changed on email change')
|
||||
.to.equal('display: none');
|
||||
await timeout(50);
|
||||
|
||||
expect(
|
||||
find('.gravatar-img'),
|
||||
'.gravatar-img background not immediately changed on email change'
|
||||
).to.have.attribute('style', 'display: none');
|
||||
|
||||
await timeout(250);
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img background still not changed before debounce timeout')
|
||||
.to.equal('display: none');
|
||||
expect(
|
||||
find('.gravatar-img'),
|
||||
'.gravatar-img background still not changed before debounce timeout'
|
||||
).to.have.attribute('style', 'display: none');
|
||||
|
||||
await timeout(100);
|
||||
|
||||
expect(this.$('.gravatar-img').attr('style'), '.gravatar-img background changed after debounce timeout')
|
||||
.to.equal(`background-image: url(${expectedUrl}); display: block`);
|
||||
expect(
|
||||
find('.gravatar-img'),
|
||||
'.gravatar-img background changed after debounce timeout'
|
||||
).to.have.attribute('style', `background-image: url(${expectedUrl}); display: block`);
|
||||
});
|
||||
});
|
||||
|
@ -1,24 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-progress-bar', function () {
|
||||
setupComponentTest('gh-progress-bar', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-progress-bar}}
|
||||
// template content
|
||||
// {{/gh-progress-bar}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-progress-bar}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,37 +1,31 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import mockPosts from '../../../mirage/config/posts';
|
||||
import mockTags from '../../../mirage/config/themes';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {click, findAll} from 'ember-native-dom-helpers';
|
||||
import {click, findAll, render, settled} from '@ember/test-helpers';
|
||||
import {clickTrigger, selectChoose, typeInSearch} from 'ember-power-select/test-support/helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
import {startMirage} from 'ghost-admin/initializers/ember-cli-mirage';
|
||||
import {timeout} from 'ember-concurrency';
|
||||
|
||||
// NOTE: although Mirage has posts<->tags relationship and can respond
|
||||
// to :post-id/?include=tags all ordering information is lost so we
|
||||
// need to build the tags array manually
|
||||
const assignPostWithTags = function postWithTags(context, ...slugs) {
|
||||
context.get('store').findRecord('post', 1).then((post) => {
|
||||
context.get('store').findAll('tag').then((tags) => {
|
||||
slugs.forEach((slug) => {
|
||||
post.get('tags').pushObject(tags.findBy('slug', slug));
|
||||
});
|
||||
const assignPostWithTags = async function postWithTags(context, ...slugs) {
|
||||
let post = await context.store.findRecord('post', 1);
|
||||
let tags = await context.store.findAll('tag');
|
||||
|
||||
context.set('post', post);
|
||||
});
|
||||
slugs.forEach((slug) => {
|
||||
post.get('tags').pushObject(tags.findBy('slug', slug));
|
||||
});
|
||||
|
||||
context.set('post', post);
|
||||
await settled();
|
||||
};
|
||||
|
||||
// TODO: Unskip and fix
|
||||
// skipped because it was failing most of the time on Travis
|
||||
// see https://github.com/TryGhost/Ghost/issues/8805
|
||||
describe.skip('Integration: Component: gh-psm-tags-input', function () {
|
||||
setupComponentTest('gh-psm-tags-input', {
|
||||
integration: true
|
||||
});
|
||||
describe('Integration: Component: gh-psm-tags-input', function () {
|
||||
setupRenderingTest();
|
||||
|
||||
let server;
|
||||
|
||||
@ -42,13 +36,13 @@ describe.skip('Integration: Component: gh-psm-tags-input', function () {
|
||||
mockPosts(server);
|
||||
mockTags(server);
|
||||
|
||||
server.create('post', {author});
|
||||
server.create('tag', {name: 'Tag One', slug: 'one'});
|
||||
server.create('tag', {name: 'Tag Two', slug: 'two'});
|
||||
server.create('tag', {name: 'Tag Three', slug: 'three'});
|
||||
server.create('tag', {name: '#Internal Tag', visibility: 'internal', slug: 'internal'});
|
||||
server.create('post', {authors: [author]});
|
||||
server.create('tag', {name: 'Tag 1', slug: 'one'});
|
||||
server.create('tag', {name: '#Tag 2', visibility: 'internal', slug: 'two'});
|
||||
server.create('tag', {name: 'Tag 3', slug: 'three'});
|
||||
server.create('tag', {name: 'Tag 4', slug: 'four'});
|
||||
|
||||
this.inject.service('store');
|
||||
this.set('store', this.owner.lookup('service:store'));
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
@ -56,153 +50,123 @@ describe.skip('Integration: Component: gh-psm-tags-input', function () {
|
||||
});
|
||||
|
||||
it('shows selected tags on render', async function () {
|
||||
run(() => {
|
||||
assignPostWithTags(this, 'one', 'three');
|
||||
});
|
||||
await wait();
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await assignPostWithTags(this, 'one', 'three');
|
||||
await render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
|
||||
let selected = findAll('.tag-token');
|
||||
expect(selected.length).to.equal(2);
|
||||
expect(selected[0].textContent).to.have.string('Tag One');
|
||||
expect(selected[1].textContent).to.have.string('Tag Three');
|
||||
expect(selected[0]).to.contain.text('Tag 1');
|
||||
expect(selected[1]).to.contain.text('Tag 3');
|
||||
});
|
||||
|
||||
it('exposes all tags as options sorted alphabetically', async function () {
|
||||
run(() => {
|
||||
this.set('post', this.get('store').findRecord('post', 1));
|
||||
});
|
||||
await wait();
|
||||
this.set('post', this.store.findRecord('post', 1));
|
||||
await settled();
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await clickTrigger();
|
||||
await settled();
|
||||
// unsure why settled() is sometimes not catching the update
|
||||
await timeout(100);
|
||||
|
||||
let options = findAll('.ember-power-select-option');
|
||||
expect(options.length).to.equal(4);
|
||||
expect(options[0].textContent).to.have.string('Tag One');
|
||||
expect(options[1].textContent).to.have.string('Tag Three');
|
||||
expect(options[2].textContent).to.have.string('Tag Two');
|
||||
expect(options[3].textContent).to.have.string('#Internal Tag');
|
||||
expect(options[0]).to.contain.text('Tag 1');
|
||||
expect(options[1]).to.contain.text('#Tag 2');
|
||||
expect(options[2]).to.contain.text('Tag 3');
|
||||
expect(options[3]).to.contain.text('Tag 4');
|
||||
});
|
||||
|
||||
it('matches options on lowercase tag names', async function () {
|
||||
run(() => {
|
||||
this.set('post', this.get('store').findRecord('post', 1));
|
||||
});
|
||||
await wait();
|
||||
this.set('post', this.store.findRecord('post', 1));
|
||||
await settled();
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await clickTrigger();
|
||||
await typeInSearch('two');
|
||||
await typeInSearch('2');
|
||||
await settled();
|
||||
// unsure why settled() is sometimes not catching the update
|
||||
await timeout(100);
|
||||
|
||||
let options = findAll('.ember-power-select-option');
|
||||
expect(options.length).to.equal(2);
|
||||
expect(options[0].textContent).to.have.string('Add "two"...');
|
||||
expect(options[1].textContent).to.have.string('Tag Two');
|
||||
expect(options[0]).to.contain.text('Add "2"...');
|
||||
expect(options[1]).to.contain.text('Tag 2');
|
||||
});
|
||||
|
||||
it('hides create option on exact matches', async function () {
|
||||
run(() => {
|
||||
this.set('post', this.get('store').findRecord('post', 1));
|
||||
});
|
||||
await wait();
|
||||
this.set('post', this.store.findRecord('post', 1));
|
||||
await settled();
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await clickTrigger();
|
||||
await typeInSearch('Tag Two');
|
||||
await typeInSearch('#Tag 2');
|
||||
await settled();
|
||||
// unsure why settled() is sometimes not catching the update
|
||||
await timeout(100);
|
||||
|
||||
let options = findAll('.ember-power-select-option');
|
||||
expect(options.length).to.equal(1);
|
||||
expect(options[0].textContent).to.have.string('Tag Two');
|
||||
expect(options[0]).to.contain.text('#Tag 2');
|
||||
});
|
||||
|
||||
describe('primary tags', function () {
|
||||
it('adds primary tag class to first tag', async function () {
|
||||
run(() => {
|
||||
assignPostWithTags(this, 'one', 'three');
|
||||
});
|
||||
await wait();
|
||||
it('highlights internal tags', async function () {
|
||||
await assignPostWithTags(this, 'two', 'three');
|
||||
await render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
|
||||
let selected = findAll('.tag-token');
|
||||
expect(selected.length).to.equal(2);
|
||||
expect(selected[0].classList.contains('tag-token--primary')).to.be.true;
|
||||
expect(selected[1].classList.contains('tag-token--primary')).to.be.false;
|
||||
});
|
||||
|
||||
it('doesn\'t add primary tag class if first tag is internal', async function () {
|
||||
run(() => {
|
||||
assignPostWithTags(this, 'internal', 'two');
|
||||
});
|
||||
await wait();
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
|
||||
let selected = findAll('.tag-token');
|
||||
expect(selected.length).to.equal(2);
|
||||
expect(selected[0].classList.contains('tag-token--primary')).to.be.false;
|
||||
expect(selected[1].classList.contains('tag-token--primary')).to.be.false;
|
||||
});
|
||||
let selected = findAll('.tag-token');
|
||||
expect(selected.length).to.equal(2);
|
||||
expect(selected[0]).to.have.class('tag-token--internal');
|
||||
expect(selected[1]).to.not.have.class('tag-token--internal');
|
||||
});
|
||||
|
||||
describe('updateTags', function () {
|
||||
it('modifies post.tags', async function () {
|
||||
run(() => {
|
||||
assignPostWithTags(this, 'internal', 'two');
|
||||
});
|
||||
await wait();
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await selectChoose('.ember-power-select-trigger', 'Tag One');
|
||||
await assignPostWithTags(this, 'two', 'three');
|
||||
await render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await selectChoose('.ember-power-select-trigger', 'Tag 1');
|
||||
|
||||
expect(
|
||||
this.get('post.tags').mapBy('name').join(',')
|
||||
).to.equal('#Internal Tag,Tag Two,Tag One');
|
||||
this.post.tags.mapBy('name').join(',')
|
||||
).to.equal('#Tag 2,Tag 3,Tag 1');
|
||||
});
|
||||
|
||||
it('destroys new tag records when not selected', async function () {
|
||||
run(() => {
|
||||
assignPostWithTags(this, 'internal', 'two');
|
||||
});
|
||||
await wait();
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await assignPostWithTags(this, 'two', 'three');
|
||||
await render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await clickTrigger();
|
||||
await typeInSearch('New');
|
||||
await settled();
|
||||
await selectChoose('.ember-power-select-trigger', 'Add "New"...');
|
||||
|
||||
let tags = await this.get('store').peekAll('tag');
|
||||
expect(tags.get('length')).to.equal(5);
|
||||
let tags = await this.store.peekAll('tag');
|
||||
expect(tags.length).to.equal(5);
|
||||
|
||||
let removeBtns = findAll('.ember-power-select-multiple-remove-btn');
|
||||
await click(removeBtns[removeBtns.length - 1]);
|
||||
|
||||
tags = await this.get('store').peekAll('tag');
|
||||
expect(tags.get('length')).to.equal(4);
|
||||
tags = await this.store.peekAll('tag');
|
||||
expect(tags.length).to.equal(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTag', function () {
|
||||
it('creates new records', async function () {
|
||||
run(() => {
|
||||
assignPostWithTags(this, 'internal', 'two');
|
||||
});
|
||||
await wait();
|
||||
|
||||
await this.render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await assignPostWithTags(this, 'two', 'three');
|
||||
await render(hbs`{{gh-psm-tags-input post=post}}`);
|
||||
await clickTrigger();
|
||||
await typeInSearch('New One');
|
||||
await selectChoose('.ember-power-select-trigger', 'Add "New One"...');
|
||||
await settled();
|
||||
await selectChoose('.ember-power-select-trigger', '.ember-power-select-option', 0);
|
||||
await typeInSearch('New Two');
|
||||
await selectChoose('.ember-power-select-trigger', 'Add "New Two"...');
|
||||
await settled();
|
||||
await selectChoose('.ember-power-select-trigger', '.ember-power-select-option', 0);
|
||||
|
||||
let tags = await this.get('store').peekAll('tag');
|
||||
expect(tags.get('length')).to.equal(6);
|
||||
let tags = await this.store.peekAll('tag');
|
||||
expect(tags.length).to.equal(6);
|
||||
|
||||
expect(tags.findBy('name', 'New One').get('isNew')).to.be.true;
|
||||
expect(tags.findBy('name', 'New Two').get('isNew')).to.be.true;
|
||||
expect(tags.findBy('name', 'New One').isNew).to.be.true;
|
||||
expect(tags.findBy('name', 'New Two').isNew).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -3,14 +3,12 @@ import mockThemes from '../../../mirage/config/themes';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {find} from 'ember-native-dom-helpers';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {find, render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
import {startMirage} from 'ghost-admin/initializers/ember-cli-mirage';
|
||||
|
||||
describe('Integration: Component: gh-psm-template-select', function () {
|
||||
setupComponentTest('gh-psm-template-select', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
let server;
|
||||
|
||||
@ -65,11 +63,11 @@ describe('Integration: Component: gh-psm-template-select', function () {
|
||||
page: false
|
||||
});
|
||||
|
||||
this.render(hbs`{{gh-psm-template-select post=post}}`);
|
||||
await render(hbs`{{gh-psm-template-select post=post}}`);
|
||||
await wait();
|
||||
|
||||
expect(find('select').disabled, 'select is disabled').to.be.true;
|
||||
expect(find('p').textContent).to.have.string('post-one.hbs');
|
||||
expect(find('p')).to.contain.text('post-one.hbs');
|
||||
});
|
||||
|
||||
it('disables template selector if slug matches page template', async function () {
|
||||
@ -78,10 +76,10 @@ describe('Integration: Component: gh-psm-template-select', function () {
|
||||
page: true
|
||||
});
|
||||
|
||||
this.render(hbs`{{gh-psm-template-select post=post}}`);
|
||||
await render(hbs`{{gh-psm-template-select post=post}}`);
|
||||
await wait();
|
||||
|
||||
expect(find('select').disabled, 'select is disabled').to.be.true;
|
||||
expect(find('p').textContent).to.have.string('page-about.hbs');
|
||||
expect(find('p')).to.contain.text('page-about.hbs');
|
||||
});
|
||||
});
|
||||
|
@ -1,24 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-publishmenu-draft', function () {
|
||||
setupComponentTest('gh-publishmenu-draft', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it.skip('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-publishmenu-draft}}
|
||||
// template content
|
||||
// {{/gh-publishmenu-draft}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-publishmenu-draft}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,24 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-publishmenu-published', function () {
|
||||
setupComponentTest('gh-publishmenu-published', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it.skip('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-publishmenu-published}}
|
||||
// template content
|
||||
// {{/gh-publishmenu-published}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-publishmenu-published}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,24 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-publishmenu-scheduled', function () {
|
||||
setupComponentTest('gh-publishmenu-scheduled', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it.skip('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-publishmenu-scheduled}}
|
||||
// template content
|
||||
// {{/gh-publishmenu-scheduled}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-publishmenu-scheduled}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,30 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {startMirage} from 'ghost-admin/initializers/ember-cli-mirage';
|
||||
|
||||
describe('Integration: Component: gh-publishmenu', function () {
|
||||
setupComponentTest('gh-publishmenu', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
let server;
|
||||
|
||||
beforeEach(function () {
|
||||
server = startMirage();
|
||||
server.loadFixtures();
|
||||
|
||||
server.create('user');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
server.shutdown();
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.post = server.create('post');
|
||||
this.render(hbs`{{gh-publishmenu post=post}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,15 +1,12 @@
|
||||
import Pretender from 'pretender';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {fillIn, findAll, render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-search-input', function () {
|
||||
setupComponentTest('gh-search-input', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
let server;
|
||||
|
||||
@ -21,24 +18,17 @@ describe('Integration: Component: gh-search-input', function () {
|
||||
server.shutdown();
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
// renders the component on the page
|
||||
this.render(hbs`{{gh-search-input}}`);
|
||||
await render(hbs`{{gh-search-input}}`);
|
||||
|
||||
expect(this.$('.ember-power-select-search input')).to.have.length(1);
|
||||
});
|
||||
|
||||
it('opens the dropdown on text entry', function (done) {
|
||||
this.render(hbs`{{gh-search-input}}`);
|
||||
it('opens the dropdown on text entry', async function () {
|
||||
await render(hbs`{{gh-search-input}}`);
|
||||
await fillIn('input[type="search"]', 'test');
|
||||
|
||||
// enter text to trigger search
|
||||
run(() => {
|
||||
this.$('input[type="search"]').val('test').trigger('input');
|
||||
});
|
||||
|
||||
wait().then(() => {
|
||||
expect(this.$('.ember-basic-dropdown-content').length).to.equal(1);
|
||||
done();
|
||||
});
|
||||
expect(findAll('.ember-basic-dropdown-content').length).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
@ -1,24 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-simplemde', function () {
|
||||
setupComponentTest('gh-simplemde', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#gh-simplemde}}
|
||||
// template content
|
||||
// {{/gh-simplemde}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-simplemde}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,20 +0,0 @@
|
||||
import Table from 'ember-light-table';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-subscribers-table', function () {
|
||||
setupComponentTest('gh-subscribers-table', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.set('table', new Table([], []));
|
||||
this.set('sortByColumn', function () {});
|
||||
this.set('delete', function () {});
|
||||
|
||||
this.render(hbs`{{gh-subscribers-table table=table sortByColumn=(action sortByColumn) delete=(action delete)}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -2,11 +2,10 @@ import DS from 'ember-data';
|
||||
import EmberObject from '@ember/object';
|
||||
import Service from '@ember/service';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {blur, click, fillIn, find, findAll, render} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
const {Errors} = DS;
|
||||
|
||||
@ -19,9 +18,7 @@ let mediaQueriesStub = Service.extend({
|
||||
});
|
||||
|
||||
describe('Integration: Component: gh-tag-settings-form', function () {
|
||||
setupComponentTest('gh-tag-settings-form', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
/* eslint-disable camelcase */
|
||||
@ -38,88 +35,79 @@ describe('Integration: Component: gh-tag-settings-form', function () {
|
||||
/* eslint-enable camelcase */
|
||||
|
||||
this.set('tag', tag);
|
||||
this.set('actions.setProperty', function (property, value) {
|
||||
this.set('setProperty', function (property, value) {
|
||||
// this should be overridden if a call is expected
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`setProperty called '${property}: ${value}'`);
|
||||
});
|
||||
|
||||
this.register('service:config', configStub);
|
||||
this.inject.service('config', {as: 'config'});
|
||||
|
||||
this.register('service:media-queries', mediaQueriesStub);
|
||||
this.inject.service('media-queries', {as: 'mediaQueries'});
|
||||
this.owner.register('service:config', configStub);
|
||||
this.owner.register('service:media-queries', mediaQueriesStub);
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('renders', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
|
||||
it('has the correct title', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('has the correct title', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
expect(this.$('.tag-settings-pane h4').text(), 'existing tag title').to.equal('Tag Settings');
|
||||
expect(find('.tag-settings-pane h4').textContent, 'existing tag title').to.equal('Tag Settings');
|
||||
|
||||
this.set('tag.isNew', true);
|
||||
expect(this.$('.tag-settings-pane h4').text(), 'new tag title').to.equal('New Tag');
|
||||
expect(find('.tag-settings-pane h4').textContent, 'new tag title').to.equal('New Tag');
|
||||
});
|
||||
|
||||
it('renders main settings', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('renders main settings', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
|
||||
expect(this.$('.gh-image-uploader').length, 'displays image uploader').to.equal(1);
|
||||
expect(this.$('input[name="name"]').val(), 'name field value').to.equal('Test');
|
||||
expect(this.$('input[name="slug"]').val(), 'slug field value').to.equal('test');
|
||||
expect(this.$('textarea[name="description"]').val(), 'description field value').to.equal('Description.');
|
||||
expect(this.$('input[name="metaTitle"]').val(), 'metaTitle field value').to.equal('Meta Title');
|
||||
expect(this.$('textarea[name="metaDescription"]').val(), 'metaDescription field value').to.equal('Meta description');
|
||||
expect(findAll('.gh-image-uploader').length, 'displays image uploader').to.equal(1);
|
||||
expect(find('input[name="name"]').value, 'name field value').to.equal('Test');
|
||||
expect(find('input[name="slug"]').value, 'slug field value').to.equal('test');
|
||||
expect(find('textarea[name="description"]').value, 'description field value').to.equal('Description.');
|
||||
expect(find('input[name="metaTitle"]').value, 'metaTitle field value').to.equal('Meta Title');
|
||||
expect(find('textarea[name="metaDescription"]').value, 'metaDescription field value').to.equal('Meta description');
|
||||
});
|
||||
|
||||
it('can switch between main/meta settings', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('can switch between main/meta settings', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
|
||||
expect(this.$('.tag-settings-pane').hasClass('settings-menu-pane-in'), 'main settings are displayed by default').to.be.true;
|
||||
expect(this.$('.tag-meta-settings-pane').hasClass('settings-menu-pane-out-right'), 'meta settings are hidden by default').to.be.true;
|
||||
expect(find('.tag-settings-pane').classList.contains('settings-menu-pane-in'), 'main settings are displayed by default').to.be.true;
|
||||
expect(find('.tag-meta-settings-pane').classList.contains('settings-menu-pane-out-right'), 'meta settings are hidden by default').to.be.true;
|
||||
|
||||
run(() => {
|
||||
this.$('.meta-data-button').click();
|
||||
});
|
||||
await click('.meta-data-button');
|
||||
|
||||
expect(this.$('.tag-settings-pane').hasClass('settings-menu-pane-out-left'), 'main settings are hidden after clicking Meta Data button').to.be.true;
|
||||
expect(this.$('.tag-meta-settings-pane').hasClass('settings-menu-pane-in'), 'meta settings are displayed after clicking Meta Data button').to.be.true;
|
||||
expect(find('.tag-settings-pane').classList.contains('settings-menu-pane-out-left'), 'main settings are hidden after clicking Meta Data button').to.be.true;
|
||||
expect(find('.tag-meta-settings-pane').classList.contains('settings-menu-pane-in'), 'meta settings are displayed after clicking Meta Data button').to.be.true;
|
||||
|
||||
run(() => {
|
||||
this.$('.back').click();
|
||||
});
|
||||
await click('.back');
|
||||
|
||||
expect(this.$('.tag-settings-pane').hasClass('settings-menu-pane-in'), 'main settings are displayed after clicking "back"').to.be.true;
|
||||
expect(this.$('.tag-meta-settings-pane').hasClass('settings-menu-pane-out-right'), 'meta settings are hidden after clicking "back"').to.be.true;
|
||||
expect(find('.tag-settings-pane').classList.contains('settings-menu-pane-in'), 'main settings are displayed after clicking "back"').to.be.true;
|
||||
expect(find('.tag-meta-settings-pane').classList.contains('settings-menu-pane-out-right'), 'meta settings are hidden after clicking "back"').to.be.true;
|
||||
});
|
||||
|
||||
it('has one-way binding for properties', function () {
|
||||
this.set('actions.setProperty', function () {
|
||||
it('has one-way binding for properties', async function () {
|
||||
this.set('setProperty', function () {
|
||||
// noop
|
||||
});
|
||||
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
|
||||
run(() => {
|
||||
this.$('input[name="name"]').val('New name');
|
||||
this.$('input[name="slug"]').val('new-slug');
|
||||
this.$('textarea[name="description"]').val('New description');
|
||||
this.$('input[name="metaTitle"]').val('New metaTitle');
|
||||
this.$('textarea[name="metaDescription"]').val('New metaDescription');
|
||||
});
|
||||
await fillIn('input[name="name"]', 'New name');
|
||||
await fillIn('input[name="slug"]', 'new-slug');
|
||||
await fillIn('textarea[name="description"]', 'New description');
|
||||
await fillIn('input[name="metaTitle"]', 'New metaTitle');
|
||||
await fillIn('textarea[name="metaDescription"]', 'New metaDescription');
|
||||
|
||||
expect(this.get('tag.name'), 'tag name').to.equal('Test');
|
||||
expect(this.get('tag.slug'), 'tag slug').to.equal('test');
|
||||
@ -128,51 +116,35 @@ describe('Integration: Component: gh-tag-settings-form', function () {
|
||||
expect(this.get('tag.metaDescription'), 'tag metaDescription').to.equal('Meta description');
|
||||
});
|
||||
|
||||
it('triggers setProperty action on blur of all fields', function () {
|
||||
let expectedProperty = '';
|
||||
let expectedValue = '';
|
||||
it('triggers setProperty action on blur of all fields', async function () {
|
||||
let lastSeenProperty = '';
|
||||
let lastSeenValue = '';
|
||||
|
||||
this.set('actions.setProperty', function (property, value) {
|
||||
expect(property, 'property').to.equal(expectedProperty);
|
||||
expect(value, 'value').to.equal(expectedValue);
|
||||
this.set('setProperty', function (property, value) {
|
||||
lastSeenProperty = property;
|
||||
lastSeenValue = value;
|
||||
});
|
||||
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
let testSetProperty = async (selector, expectedProperty, expectedValue) => {
|
||||
await click(selector);
|
||||
await fillIn(selector, expectedValue);
|
||||
await blur(selector);
|
||||
expect(lastSeenProperty, 'property').to.equal(expectedProperty);
|
||||
expect(lastSeenValue, 'value').to.equal(expectedValue);
|
||||
};
|
||||
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
|
||||
expectedProperty = 'name';
|
||||
expectedValue = 'new-slug';
|
||||
run(() => {
|
||||
this.$('input[name="name"]').val('New name');
|
||||
});
|
||||
|
||||
expectedProperty = 'url';
|
||||
expectedValue = 'new-slug';
|
||||
run(() => {
|
||||
this.$('input[name="slug"]').val('new-slug');
|
||||
});
|
||||
|
||||
expectedProperty = 'description';
|
||||
expectedValue = 'New description';
|
||||
run(() => {
|
||||
this.$('textarea[name="description"]').val('New description');
|
||||
});
|
||||
|
||||
expectedProperty = 'metaTitle';
|
||||
expectedValue = 'New metaTitle';
|
||||
run(() => {
|
||||
this.$('input[name="metaTitle"]').val('New metaTitle');
|
||||
});
|
||||
|
||||
expectedProperty = 'metaDescription';
|
||||
expectedValue = 'New metaDescription';
|
||||
run(() => {
|
||||
this.$('textarea[name="metaDescription"]').val('New metaDescription');
|
||||
});
|
||||
await testSetProperty('input[name="name"]', 'name', 'New name');
|
||||
await testSetProperty('input[name="slug"]', 'slug', 'new-slug');
|
||||
await testSetProperty('textarea[name="description"]', 'description', 'New description');
|
||||
await testSetProperty('input[name="metaTitle"]', 'metaTitle', 'New metaTitle');
|
||||
await testSetProperty('textarea[name="metaDescription"]', 'metaDescription', 'New metaDescription');
|
||||
});
|
||||
|
||||
it('displays error messages for validated fields', function () {
|
||||
it('displays error messages for validated fields', async function () {
|
||||
let errors = this.get('tag.errors');
|
||||
let hasValidated = this.get('tag.hasValidated');
|
||||
|
||||
@ -191,35 +163,33 @@ describe('Integration: Component: gh-tag-settings-form', function () {
|
||||
errors.add('metaDescription', 'is too long');
|
||||
hasValidated.push('metaDescription');
|
||||
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
let nameFormGroup = this.$('input[name="name"]').closest('.form-group');
|
||||
expect(nameFormGroup.hasClass('error'), 'name form group has error state').to.be.true;
|
||||
expect(nameFormGroup.find('.response').length, 'name form group has error message').to.equal(1);
|
||||
let nameFormGroup = this.$('input[name="name"]').closest('.form-group');
|
||||
expect(nameFormGroup.hasClass('error'), 'name form group has error state').to.be.true;
|
||||
expect(nameFormGroup.find('.response').length, 'name form group has error message').to.equal(1);
|
||||
|
||||
let slugFormGroup = this.$('input[name="slug"]').closest('.form-group');
|
||||
expect(slugFormGroup.hasClass('error'), 'slug form group has error state').to.be.true;
|
||||
expect(slugFormGroup.find('.response').length, 'slug form group has error message').to.equal(1);
|
||||
let slugFormGroup = this.$('input[name="slug"]').closest('.form-group');
|
||||
expect(slugFormGroup.hasClass('error'), 'slug form group has error state').to.be.true;
|
||||
expect(slugFormGroup.find('.response').length, 'slug form group has error message').to.equal(1);
|
||||
|
||||
let descriptionFormGroup = this.$('textarea[name="description"]').closest('.form-group');
|
||||
expect(descriptionFormGroup.hasClass('error'), 'description form group has error state').to.be.true;
|
||||
let descriptionFormGroup = this.$('textarea[name="description"]').closest('.form-group');
|
||||
expect(descriptionFormGroup.hasClass('error'), 'description form group has error state').to.be.true;
|
||||
|
||||
let metaTitleFormGroup = this.$('input[name="metaTitle"]').closest('.form-group');
|
||||
expect(metaTitleFormGroup.hasClass('error'), 'metaTitle form group has error state').to.be.true;
|
||||
expect(metaTitleFormGroup.find('.response').length, 'metaTitle form group has error message').to.equal(1);
|
||||
let metaTitleFormGroup = this.$('input[name="metaTitle"]').closest('.form-group');
|
||||
expect(metaTitleFormGroup.hasClass('error'), 'metaTitle form group has error state').to.be.true;
|
||||
expect(metaTitleFormGroup.find('.response').length, 'metaTitle form group has error message').to.equal(1);
|
||||
|
||||
let metaDescriptionFormGroup = this.$('textarea[name="metaDescription"]').closest('.form-group');
|
||||
expect(metaDescriptionFormGroup.hasClass('error'), 'metaDescription form group has error state').to.be.true;
|
||||
expect(metaDescriptionFormGroup.find('.response').length, 'metaDescription form group has error message').to.equal(1);
|
||||
});
|
||||
let metaDescriptionFormGroup = this.$('textarea[name="metaDescription"]').closest('.form-group');
|
||||
expect(metaDescriptionFormGroup.hasClass('error'), 'metaDescription form group has error state').to.be.true;
|
||||
expect(metaDescriptionFormGroup.find('.response').length, 'metaDescription form group has error message').to.equal(1);
|
||||
});
|
||||
|
||||
it('displays char count for text fields', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('displays char count for text fields', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
|
||||
let descriptionFormGroup = this.$('textarea[name="description"]').closest('.form-group');
|
||||
@ -229,93 +199,79 @@ describe('Integration: Component: gh-tag-settings-form', function () {
|
||||
expect(metaDescriptionFormGroup.find('.word-count').text(), 'description char count').to.equal('16');
|
||||
});
|
||||
|
||||
it('renders SEO title preview', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('renders SEO title preview', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
expect(this.$('.seo-preview-title').text(), 'displays meta title if present').to.equal('Meta Title');
|
||||
expect(find('.seo-preview-title').textContent, 'displays meta title if present').to.equal('Meta Title');
|
||||
|
||||
run(() => {
|
||||
this.set('tag.metaTitle', '');
|
||||
});
|
||||
expect(this.$('.seo-preview-title').text(), 'falls back to tag name without metaTitle').to.equal('Test');
|
||||
this.set('tag.metaTitle', '');
|
||||
expect(find('.seo-preview-title').textContent, 'falls back to tag name without metaTitle').to.equal('Test');
|
||||
|
||||
run(() => {
|
||||
this.set('tag.name', (new Array(151).join('x')));
|
||||
});
|
||||
this.set('tag.name', (new Array(151).join('x')));
|
||||
let expectedLength = 70 + '…'.length;
|
||||
expect(this.$('.seo-preview-title').text().length, 'cuts title to max 70 chars').to.equal(expectedLength);
|
||||
expect(find('.seo-preview-title').textContent.length, 'cuts title to max 70 chars').to.equal(expectedLength);
|
||||
});
|
||||
|
||||
it('renders SEO URL preview', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('renders SEO URL preview', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
expect(this.$('.seo-preview-link').text(), 'adds url and tag prefix').to.equal('http://localhost:2368/tag/test/');
|
||||
expect(find('.seo-preview-link').textContent, 'adds url and tag prefix').to.equal('http://localhost:2368/tag/test/');
|
||||
|
||||
run(() => {
|
||||
this.set('tag.slug', (new Array(151).join('x')));
|
||||
});
|
||||
this.set('tag.slug', (new Array(151).join('x')));
|
||||
let expectedLength = 70 + '…'.length;
|
||||
expect(this.$('.seo-preview-link').text().length, 'cuts slug to max 70 chars').to.equal(expectedLength);
|
||||
expect(find('.seo-preview-link').textContent.length, 'cuts slug to max 70 chars').to.equal(expectedLength);
|
||||
});
|
||||
|
||||
it('renders SEO description preview', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('renders SEO description preview', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
expect(this.$('.seo-preview-description').text(), 'displays meta description if present').to.equal('Meta description');
|
||||
expect(find('.seo-preview-description').textContent, 'displays meta description if present').to.equal('Meta description');
|
||||
|
||||
run(() => {
|
||||
this.set('tag.metaDescription', '');
|
||||
});
|
||||
expect(this.$('.seo-preview-description').text(), 'falls back to tag description without metaDescription').to.equal('Description.');
|
||||
this.set('tag.metaDescription', '');
|
||||
expect(find('.seo-preview-description').textContent, 'falls back to tag description without metaDescription').to.equal('Description.');
|
||||
|
||||
run(() => {
|
||||
this.set('tag.description', (new Array(500).join('x')));
|
||||
});
|
||||
this.set('tag.description', (new Array(500).join('x')));
|
||||
let expectedLength = 156 + '…'.length;
|
||||
expect(this.$('.seo-preview-description').text().length, 'cuts description to max 156 chars').to.equal(expectedLength);
|
||||
expect(find('.seo-preview-description').textContent.length, 'cuts description to max 156 chars').to.equal(expectedLength);
|
||||
});
|
||||
|
||||
it('resets if a new tag is received', function () {
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
it('resets if a new tag is received', async function () {
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
run(() => {
|
||||
this.$('.meta-data-button').click();
|
||||
});
|
||||
expect(this.$('.tag-meta-settings-pane').hasClass('settings-menu-pane-in'), 'meta data pane is shown').to.be.true;
|
||||
await click('.meta-data-button');
|
||||
expect(find('.tag-meta-settings-pane').classList.contains('settings-menu-pane-in'), 'meta data pane is shown').to.be.true;
|
||||
|
||||
run(() => {
|
||||
this.set('tag', EmberObject.create({id: '2'}));
|
||||
});
|
||||
expect(this.$('.tag-settings-pane').hasClass('settings-menu-pane-in'), 'resets to main settings').to.be.true;
|
||||
this.set('tag', EmberObject.create({id: '2'}));
|
||||
expect(find('.tag-settings-pane').classList.contains('settings-menu-pane-in'), 'resets to main settings').to.be.true;
|
||||
});
|
||||
|
||||
it('triggers delete tag modal on delete click', function (done) {
|
||||
// TODO: will time out if this isn't hit, there's probably a better
|
||||
// way of testing this
|
||||
this.set('actions.openModal', () => {
|
||||
done();
|
||||
it('triggers delete tag modal on delete click', async function () {
|
||||
let openModalFired = false;
|
||||
|
||||
this.set('openModal', () => {
|
||||
openModalFired = true;
|
||||
});
|
||||
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty') showDeleteTagModal=(action 'openModal')}}
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty) showDeleteTagModal=(action openModal)}}
|
||||
`);
|
||||
await click('.settings-menu-delete-button');
|
||||
|
||||
run(() => {
|
||||
this.$('.settings-menu-delete-button').click();
|
||||
});
|
||||
expect(openModalFired).to.be.true;
|
||||
});
|
||||
|
||||
it('shows settings.tags arrow link on mobile', function () {
|
||||
this.set('mediaQueries.maxWidth600', true);
|
||||
it('shows settings.tags arrow link on mobile', async function () {
|
||||
let mediaQueries = this.owner.lookup('service:media-queries');
|
||||
mediaQueries.set('maxWidth600', true);
|
||||
|
||||
this.render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
|
||||
await render(hbs`
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action setProperty)}}
|
||||
`);
|
||||
|
||||
expect(this.$('.tag-settings-pane .settings-menu-header .settings-menu-header-action').length, 'settings.tags link is shown').to.equal(1);
|
||||
expect(findAll('.tag-settings-pane .settings-menu-header .settings-menu-header-action').length, 'settings.tags link is shown').to.equal(1);
|
||||
});
|
||||
});
|
||||
|
@ -1,26 +0,0 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-tags-management-container', function () {
|
||||
setupComponentTest('gh-tags-management-container', {
|
||||
integration: true
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.set('tags', []);
|
||||
this.set('selectedTag', null);
|
||||
this.on('enteredMobile', function () {
|
||||
// noop
|
||||
});
|
||||
this.on('leftMobile', function () {
|
||||
// noop
|
||||
});
|
||||
|
||||
this.render(hbs`
|
||||
{{#gh-tags-management-container tags=tags selectedTag=selectedTag enteredMobile="enteredMobile" leftMobile=(action "leftMobile")}}{{/gh-tags-management-container}}
|
||||
`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
});
|
@ -1,135 +1,133 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {click, find, render, settled} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
import {task, timeout} from 'ember-concurrency';
|
||||
|
||||
describe('Integration: Component: gh-task-button', function () {
|
||||
setupComponentTest('gh-task-button', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
// sets button text using positional param
|
||||
this.render(hbs`{{gh-task-button "Test"}}`);
|
||||
expect(this.$('button')).to.exist;
|
||||
expect(this.$('button')).to.contain('Test');
|
||||
expect(this.$('button')).to.have.prop('disabled', false);
|
||||
await render(hbs`{{gh-task-button "Test"}}`);
|
||||
expect(find('button')).to.exist;
|
||||
expect(find('button')).to.contain.text('Test');
|
||||
expect(find('button').disabled).to.be.false;
|
||||
|
||||
this.render(hbs`{{gh-task-button class="testing"}}`);
|
||||
expect(this.$('button')).to.have.class('testing');
|
||||
await render(hbs`{{gh-task-button class="testing"}}`);
|
||||
expect(find('button')).to.have.class('testing');
|
||||
// default button text is "Save"
|
||||
expect(this.$('button')).to.contain('Save');
|
||||
expect(find('button')).to.contain.text('Save');
|
||||
|
||||
// passes disabled attr
|
||||
this.render(hbs`{{gh-task-button disabled=true buttonText="Test"}}`);
|
||||
expect(this.$('button')).to.have.prop('disabled', true);
|
||||
await render(hbs`{{gh-task-button disabled=true buttonText="Test"}}`);
|
||||
expect(find('button').disabled).to.be.true;
|
||||
// allows button text to be set via hash param
|
||||
expect(this.$('button')).to.contain('Test');
|
||||
expect(find('button')).to.contain.text('Test');
|
||||
|
||||
// passes type attr
|
||||
this.render(hbs`{{gh-task-button type="submit"}}`);
|
||||
expect(this.$('button')).to.have.attr('type', 'submit');
|
||||
await render(hbs`{{gh-task-button type="submit"}}`);
|
||||
expect(find('button')).to.have.attr('type', 'submit');
|
||||
|
||||
// passes tabindex attr
|
||||
this.render(hbs`{{gh-task-button tabindex="-1"}}`);
|
||||
expect(this.$('button')).to.have.attr('tabindex', '-1');
|
||||
await render(hbs`{{gh-task-button tabindex="-1"}}`);
|
||||
expect(find('button')).to.have.attr('tabindex', '-1');
|
||||
});
|
||||
|
||||
it('shows spinner whilst running', function () {
|
||||
it('shows spinner whilst running', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
yield timeout(50);
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask}}`);
|
||||
await render(hbs`{{gh-task-button task=myTask}}`);
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button')).to.have.descendants('svg');
|
||||
expect(find('button')).to.have.descendants('svg');
|
||||
}, 20);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('shows running text when passed whilst running', function () {
|
||||
it('shows running text when passed whilst running', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
yield timeout(50);
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask runningText="Running"}}`);
|
||||
await render(hbs`{{gh-task-button task=myTask runningText="Running"}}`);
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button')).to.have.descendants('svg');
|
||||
expect(this.$('button')).to.contain('Running');
|
||||
expect(find('button')).to.have.descendants('svg');
|
||||
expect(find('button')).to.contain.text('Running');
|
||||
}, 20);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('appears disabled whilst running', function () {
|
||||
it('appears disabled whilst running', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
yield timeout(50);
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask}}`);
|
||||
expect(this.$('button'), 'initial class').to.not.have.class('appear-disabled');
|
||||
await render(hbs`{{gh-task-button task=myTask}}`);
|
||||
expect(find('button'), 'initial class').to.not.have.class('appear-disabled');
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button'), 'running class').to.have.class('appear-disabled');
|
||||
expect(find('button'), 'running class').to.have.class('appear-disabled');
|
||||
}, 20);
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button'), 'ended class').to.not.have.class('appear-disabled');
|
||||
expect(find('button'), 'ended class').to.not.have.class('appear-disabled');
|
||||
}, 100);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('shows success on success', function () {
|
||||
it('shows success on success', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
yield timeout(50);
|
||||
return true;
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask}}`);
|
||||
await render(hbs`{{gh-task-button task=myTask}}`);
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button')).to.have.class('gh-btn-green');
|
||||
expect(this.$('button')).to.contain('Saved');
|
||||
expect(find('button')).to.have.class('gh-btn-green');
|
||||
expect(find('button')).to.contain.text('Saved');
|
||||
}, 100);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('assigns specified success class on success', function () {
|
||||
it('assigns specified success class on success', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
yield timeout(50);
|
||||
return true;
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask successClass="im-a-success"}}`);
|
||||
await render(hbs`{{gh-task-button task=myTask successClass="im-a-success"}}`);
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button')).to.not.have.class('gh-btn-green');
|
||||
expect(this.$('button')).to.have.class('im-a-success');
|
||||
expect(this.$('button')).to.contain('Saved');
|
||||
expect(find('button')).to.not.have.class('gh-btn-green');
|
||||
expect(find('button')).to.have.class('im-a-success');
|
||||
expect(find('button')).to.contain.text('Saved');
|
||||
}, 100);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('shows failure when task errors', function () {
|
||||
it('shows failure when task errors', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
try {
|
||||
yield timeout(50);
|
||||
@ -139,56 +137,56 @@ describe('Integration: Component: gh-task-button', function () {
|
||||
}
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask}}`);
|
||||
await render(hbs`{{gh-task-button task=myTask}}`);
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button')).to.have.class('gh-btn-red');
|
||||
expect(this.$('button')).to.contain('Retry');
|
||||
expect(find('button')).to.have.class('gh-btn-red');
|
||||
expect(find('button')).to.contain.text('Retry');
|
||||
}, 100);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('shows failure on falsy response', function () {
|
||||
it('shows failure on falsy response', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
yield timeout(50);
|
||||
return false;
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask}}`);
|
||||
await render(hbs`{{gh-task-button task=myTask}}`);
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button')).to.have.class('gh-btn-red');
|
||||
expect(this.$('button')).to.contain('Retry');
|
||||
expect(find('button')).to.have.class('gh-btn-red');
|
||||
expect(find('button')).to.contain.text('Retry');
|
||||
}, 100);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('assigns specified failure class on failure', function () {
|
||||
it('assigns specified failure class on failure', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
yield timeout(50);
|
||||
return false;
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask failureClass="im-a-failure"}}`);
|
||||
await render(hbs`{{gh-task-button task=myTask failureClass="im-a-failure"}}`);
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
expect(this.$('button')).to.not.have.class('gh-btn-red');
|
||||
expect(this.$('button')).to.have.class('im-a-failure');
|
||||
expect(this.$('button')).to.contain('Retry');
|
||||
expect(find('button')).to.not.have.class('gh-btn-red');
|
||||
expect(find('button')).to.have.class('im-a-failure');
|
||||
expect(find('button')).to.contain.text('Retry');
|
||||
}, 100);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
|
||||
it('performs task on click', function () {
|
||||
it('performs task on click', async function () {
|
||||
let taskCount = 0;
|
||||
|
||||
this.set('myTask', task(function* () {
|
||||
@ -196,43 +194,41 @@ describe('Integration: Component: gh-task-button', function () {
|
||||
taskCount = taskCount + 1;
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask}}`);
|
||||
this.$('button').click();
|
||||
await render(hbs`{{gh-task-button task=myTask}}`);
|
||||
await click('button');
|
||||
|
||||
return wait().then(() => {
|
||||
await settled().then(() => {
|
||||
expect(taskCount, 'taskCount').to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('keeps button size when showing spinner', function () {
|
||||
it.skip('keeps button size when showing spinner', async function () {
|
||||
this.set('myTask', task(function* () {
|
||||
yield timeout(50);
|
||||
}));
|
||||
|
||||
this.render(hbs`{{gh-task-button task=myTask}}`);
|
||||
let width = this.$('button').width();
|
||||
let height = this.$('button').height();
|
||||
expect(this.$('button')).to.not.have.attr('style');
|
||||
await render(hbs`{{gh-task-button task=myTask}}`);
|
||||
let width = find('button').clientWidth;
|
||||
let height = find('button').clientHeight;
|
||||
expect(find('button')).to.not.have.attr('style');
|
||||
|
||||
this.get('myTask').perform();
|
||||
|
||||
run.later(this, function () {
|
||||
// we can't test exact width/height because Chrome/Firefox use different rounding methods
|
||||
// expect(this.$('button')).to.have.attr('style', `width: ${width}px; height: ${height}px;`);
|
||||
// expect(find('button')).to.have.attr('style', `width: ${width}px; height: ${height}px;`);
|
||||
|
||||
let [widthInt] = width.toString().split('.');
|
||||
let [heightInt] = height.toString().split('.');
|
||||
|
||||
expect(this.$('button').attr('style')).to.have.string(`width: ${widthInt}`);
|
||||
expect(this.$('button').attr('style')).to.have.string(`height: ${heightInt}`);
|
||||
expect(find('button')).to.have.attr('style', `width: ${widthInt}`);
|
||||
expect(find('button')).to.have.attr('style', `height: ${heightInt}`);
|
||||
}, 20);
|
||||
|
||||
run.later(this, function () {
|
||||
// chai-jquery test doesn't work because Firefox outputs blank string
|
||||
// expect(this.$('button')).to.not.have.attr('style');
|
||||
expect(this.$('button').attr('style')).to.be.empty;
|
||||
expect(find('button').getAttribute('style')).to.be.empty;
|
||||
}, 100);
|
||||
|
||||
return wait();
|
||||
await settled();
|
||||
});
|
||||
});
|
||||
|
@ -1,17 +1,14 @@
|
||||
import $ from 'jquery';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import sinon from 'sinon';
|
||||
import {click, find, findAll, render} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-theme-table', function () {
|
||||
setupComponentTest('gh-theme-table', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
this.set('themes', [
|
||||
{name: 'Daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
|
||||
{name: 'casper', package: {name: 'Casper', version: '1.3.1'}},
|
||||
@ -20,17 +17,17 @@ describe('Integration: Component: gh-theme-table', function () {
|
||||
]);
|
||||
this.set('actionHandler', sinon.spy());
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
await render(hbs`{{gh-theme-table
|
||||
themes=themes
|
||||
activateTheme=(action actionHandler)
|
||||
downloadTheme=(action actionHandler)
|
||||
deleteTheme=(action actionHandler)
|
||||
}}`);
|
||||
|
||||
expect(this.$('[data-test-themes-list]').length, 'themes list is present').to.equal(1);
|
||||
expect(this.$('[data-test-theme-id]').length, 'number of rows').to.equal(4);
|
||||
expect(findAll('[data-test-themes-list]').length, 'themes list is present').to.equal(1);
|
||||
expect(findAll('[data-test-theme-id]').length, 'number of rows').to.equal(4);
|
||||
|
||||
let packageNames = this.$('[data-test-theme-title]').map((i, name) => $(name).text().trim()).toArray();
|
||||
let packageNames = findAll('[data-test-theme-title]').map(name => name.textContent.trim());
|
||||
|
||||
expect(
|
||||
packageNames,
|
||||
@ -43,42 +40,42 @@ describe('Integration: Component: gh-theme-table', function () {
|
||||
]);
|
||||
|
||||
expect(
|
||||
this.$('[data-test-theme-active="true"]').find('[data-test-theme-title]').text().trim(),
|
||||
find('[data-test-theme-active="true"]').querySelector('[data-test-theme-title]'),
|
||||
'active theme is highlighted'
|
||||
).to.equal('Daring');
|
||||
).to.have.trimmed.text('Daring');
|
||||
|
||||
expect(
|
||||
this.$('[data-test-theme-activate-button]').length === 3,
|
||||
findAll('[data-test-theme-activate-button]').length,
|
||||
'non-active themes have an activate link'
|
||||
).to.be.true;
|
||||
).to.equal(3);
|
||||
|
||||
expect(
|
||||
this.$('[data-test-theme-active="true"]').find('[data-test-theme-activate-button]').length === 0,
|
||||
find('[data-test-theme-active="true"]').querySelector('[data-test-theme-activate-button]'),
|
||||
'active theme doesn\'t have an activate link'
|
||||
).to.be.true;
|
||||
).to.not.exist;
|
||||
|
||||
expect(
|
||||
this.$('[data-test-theme-download-button]').length,
|
||||
findAll('[data-test-theme-download-button]').length,
|
||||
'all themes have a download link'
|
||||
).to.equal(4);
|
||||
|
||||
expect(
|
||||
this.$('[data-test-theme-id="foo"]').find('[data-test-theme-delete-button]').length === 1,
|
||||
find('[data-test-theme-id="foo"]').querySelector('[data-test-theme-delete-button]'),
|
||||
'non-active, non-casper theme has delete link'
|
||||
).to.be.true;
|
||||
).to.exist;
|
||||
|
||||
expect(
|
||||
this.$('[data-test-theme-id="casper"]').find('[data-test-theme-delete-button]').length === 0,
|
||||
find('[data-test-theme-id="casper"]').querySelector('[data-test-theme-delete-button]'),
|
||||
'casper doesn\'t have delete link'
|
||||
).to.be.true;
|
||||
).to.not.exist;
|
||||
|
||||
expect(
|
||||
this.$('[data-test-theme-active="true"]').find('[data-test-theme-delete-button]').length === 0,
|
||||
find('[data-test-theme-active="true"]').querySelector('[data-test-theme-delete-button]'),
|
||||
'active theme doesn\'t have delete link'
|
||||
).to.be.true;
|
||||
).to.not.exist;
|
||||
});
|
||||
|
||||
it('delete link triggers passed in action', function () {
|
||||
it('delete link triggers passed in action', async function () {
|
||||
let deleteAction = sinon.spy();
|
||||
let actionHandler = sinon.spy();
|
||||
|
||||
@ -89,22 +86,20 @@ describe('Integration: Component: gh-theme-table', function () {
|
||||
this.set('deleteAction', deleteAction);
|
||||
this.set('actionHandler', actionHandler);
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
await render(hbs`{{gh-theme-table
|
||||
themes=themes
|
||||
activateTheme=(action actionHandler)
|
||||
downloadTheme=(action actionHandler)
|
||||
deleteTheme=(action deleteAction)
|
||||
}}`);
|
||||
|
||||
run(() => {
|
||||
this.$('[data-test-theme-id="Bar"] [data-test-theme-delete-button]').click();
|
||||
});
|
||||
await click('[data-test-theme-id="Bar"] [data-test-theme-delete-button]');
|
||||
|
||||
expect(deleteAction.calledOnce).to.be.true;
|
||||
expect(deleteAction.firstCall.args[0].name).to.equal('Bar');
|
||||
});
|
||||
|
||||
it('download link triggers passed in action', function () {
|
||||
it('download link triggers passed in action', async function () {
|
||||
let downloadAction = sinon.spy();
|
||||
let actionHandler = sinon.spy();
|
||||
|
||||
@ -115,22 +110,20 @@ describe('Integration: Component: gh-theme-table', function () {
|
||||
this.set('downloadAction', downloadAction);
|
||||
this.set('actionHandler', actionHandler);
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
await render(hbs`{{gh-theme-table
|
||||
themes=themes
|
||||
activateTheme=(action actionHandler)
|
||||
downloadTheme=(action downloadAction)
|
||||
deleteTheme=(action actionHandler)
|
||||
}}`);
|
||||
|
||||
run(() => {
|
||||
this.$('[data-test-theme-id="Foo"] [data-test-theme-download-button]').click();
|
||||
});
|
||||
await click('[data-test-theme-id="Foo"] [data-test-theme-download-button]');
|
||||
|
||||
expect(downloadAction.calledOnce).to.be.true;
|
||||
expect(downloadAction.firstCall.args[0].name).to.equal('Foo');
|
||||
});
|
||||
|
||||
it('activate link triggers passed in action', function () {
|
||||
it('activate link triggers passed in action', async function () {
|
||||
let activateAction = sinon.spy();
|
||||
let actionHandler = sinon.spy();
|
||||
|
||||
@ -141,22 +134,20 @@ describe('Integration: Component: gh-theme-table', function () {
|
||||
this.set('activateAction', activateAction);
|
||||
this.set('actionHandler', actionHandler);
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
await render(hbs`{{gh-theme-table
|
||||
themes=themes
|
||||
activateTheme=(action activateAction)
|
||||
downloadTheme=(action actionHandler)
|
||||
deleteTheme=(action actionHandler)
|
||||
}}`);
|
||||
|
||||
run(() => {
|
||||
this.$('[data-test-theme-id="Bar"] [data-test-theme-activate-button]').click();
|
||||
});
|
||||
await click('[data-test-theme-id="Bar"] [data-test-theme-activate-button]');
|
||||
|
||||
expect(activateAction.calledOnce).to.be.true;
|
||||
expect(activateAction.firstCall.args[0].name).to.equal('Bar');
|
||||
});
|
||||
|
||||
it('displays folder names if there are duplicate package names', function () {
|
||||
it('displays folder names if there are duplicate package names', async function () {
|
||||
this.set('themes', [
|
||||
{name: 'daring', package: {name: 'Daring', version: '0.1.4'}, active: true},
|
||||
{name: 'daring-0.1.5', package: {name: 'Daring', version: '0.1.4'}},
|
||||
@ -167,14 +158,14 @@ describe('Integration: Component: gh-theme-table', function () {
|
||||
]);
|
||||
this.set('actionHandler', sinon.spy());
|
||||
|
||||
this.render(hbs`{{gh-theme-table
|
||||
await render(hbs`{{gh-theme-table
|
||||
themes=themes
|
||||
activateTheme=(action actionHandler)
|
||||
downloadTheme=(action actionHandler)
|
||||
deleteTheme=(action actionHandler)
|
||||
}}`);
|
||||
|
||||
let packageNames = this.$('[data-test-theme-title]').map((i, name) => $(name).text().trim()).toArray();
|
||||
let packageNames = findAll('[data-test-theme-title]').map(name => name.textContent.trim());
|
||||
|
||||
expect(
|
||||
packageNames,
|
||||
|
@ -1,15 +1,12 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import sinon from 'sinon';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {blur, fillIn, find, findAll, render} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-timezone-select', function () {
|
||||
setupComponentTest('gh-timezone-select', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
this.set('availableTimezones', [
|
||||
@ -20,50 +17,46 @@ describe('Integration: Component: gh-timezone-select', function () {
|
||||
this.set('activeTimezone', 'Etc/UTC');
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.render(hbs`{{gh-timezone-select
|
||||
it('renders', async function () {
|
||||
await render(hbs`{{gh-timezone-select
|
||||
availableTimezones=availableTimezones
|
||||
activeTimezone=activeTimezone}}`);
|
||||
|
||||
expect(this.$(), 'top-level elements').to.have.length(1);
|
||||
expect(this.$('option'), 'number of options').to.have.length(3);
|
||||
expect(this.$('select').val(), 'selected option value').to.equal('Etc/UTC');
|
||||
expect(this.element, 'top-level elements').to.exist;
|
||||
expect(findAll('option'), 'number of options').to.have.length(3);
|
||||
expect(find('select').value, 'selected option value').to.equal('Etc/UTC');
|
||||
});
|
||||
|
||||
it('handles an unknown timezone', function () {
|
||||
it('handles an unknown timezone', async function () {
|
||||
this.set('activeTimezone', 'Europe/London');
|
||||
|
||||
this.render(hbs`{{gh-timezone-select
|
||||
await render(hbs`{{gh-timezone-select
|
||||
availableTimezones=availableTimezones
|
||||
activeTimezone=activeTimezone}}`);
|
||||
|
||||
// we have an additional blank option at the top
|
||||
expect(this.$('option'), 'number of options').to.have.length(4);
|
||||
expect(findAll('option'), 'number of options').to.have.length(4);
|
||||
// blank option is selected
|
||||
expect(this.$('select').val(), 'selected option value').to.equal('');
|
||||
expect(find('select').value, 'selected option value').to.equal('');
|
||||
// we indicate the manual override
|
||||
expect(this.$('p').text()).to.match(/Your timezone has been automatically set to Europe\/London/);
|
||||
expect(find('p').textContent).to.match(/Your timezone has been automatically set to Europe\/London/);
|
||||
});
|
||||
|
||||
it('triggers update action on change', function (done) {
|
||||
it('triggers update action on change', async function () {
|
||||
let update = sinon.spy();
|
||||
this.set('update', update);
|
||||
|
||||
this.render(hbs`{{gh-timezone-select
|
||||
await render(hbs`{{gh-timezone-select
|
||||
availableTimezones=availableTimezones
|
||||
activeTimezone=activeTimezone
|
||||
update=(action update)}}`);
|
||||
|
||||
run(() => {
|
||||
this.$('select').val('Pacific/Pago_Pago').change();
|
||||
});
|
||||
await fillIn('select', 'Pacific/Pago_Pago');
|
||||
await blur('select');
|
||||
|
||||
wait().then(() => {
|
||||
expect(update.calledOnce, 'update was called once').to.be.true;
|
||||
expect(update.firstCall.args[0].name, 'update was passed new timezone')
|
||||
.to.equal('Pacific/Pago_Pago');
|
||||
done();
|
||||
});
|
||||
expect(update.calledOnce, 'update was called once').to.be.true;
|
||||
expect(update.firstCall.args[0].name, 'update was passed new timezone')
|
||||
.to.equal('Pacific/Pago_Pago');
|
||||
});
|
||||
|
||||
// TODO: mock clock service, fake the time, test we have the correct
|
||||
|
@ -1,66 +1,60 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {blur, find, render} from '@ember/test-helpers';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-trim-focus-input', function () {
|
||||
setupComponentTest('gh-trim-focus-input', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('trims value on focusOut', function () {
|
||||
it('trims value on focusOut', async function () {
|
||||
this.set('text', 'some random stuff ');
|
||||
this.render(hbs`{{gh-trim-focus-input value=(readonly text) input=(action (mut text) value="target.value")}}`);
|
||||
await render(hbs`{{gh-trim-focus-input value=(readonly text) input=(action (mut text) value="target.value")}}`);
|
||||
|
||||
run(() => {
|
||||
this.$('.gh-input').trigger('focusout');
|
||||
});
|
||||
await blur('input');
|
||||
|
||||
expect(this.get('text')).to.equal('some random stuff');
|
||||
});
|
||||
|
||||
it('trims value on focusOut before calling custom focus-out', function () {
|
||||
it('trims value on focusOut before calling custom focus-out', async function () {
|
||||
this.set('text', 'some random stuff ');
|
||||
this.set('customFocusOut', function (value) {
|
||||
expect(this.$('.gh-input').val(), 'input value').to.equal('some random stuff');
|
||||
expect(find('.gh-input').value, 'input value').to.equal('some random stuff');
|
||||
expect(value, 'value').to.equal('some random stuff');
|
||||
});
|
||||
|
||||
this.render(hbs`{{gh-trim-focus-input
|
||||
await render(hbs`{{gh-trim-focus-input
|
||||
value=(readonly text)
|
||||
input=(action (mut text) value="target.value")
|
||||
focus-out=(action customFocusOut)
|
||||
}}`);
|
||||
|
||||
run(() => {
|
||||
this.$('.gh-input').trigger('focusout');
|
||||
});
|
||||
await blur('input');
|
||||
|
||||
expect(this.get('text')).to.equal('some random stuff');
|
||||
});
|
||||
|
||||
it('does not have the autofocus attribute if not set to focus', function () {
|
||||
it('does not have the autofocus attribute if not set to focus', async function () {
|
||||
this.set('text', 'some text');
|
||||
this.render(hbs`{{gh-trim-focus-input value=(readonly text) shouldFocus=false}}`);
|
||||
expect(this.$('.gh-input').attr('autofocus')).to.not.be.ok;
|
||||
await render(hbs`{{gh-trim-focus-input value=(readonly text) shouldFocus=false}}`);
|
||||
expect(find('input').autofocus).to.not.be.ok;
|
||||
});
|
||||
|
||||
it('has the autofocus attribute if set to focus', function () {
|
||||
it('has the autofocus attribute if set to focus', async function () {
|
||||
this.set('text', 'some text');
|
||||
this.render(hbs`{{gh-trim-focus-input value=(readonly text) shouldFocus=true}}`);
|
||||
expect(this.$('.gh-input').attr('autofocus')).to.be.ok;
|
||||
await render(hbs`{{gh-trim-focus-input value=(readonly text) shouldFocus=true}}`);
|
||||
expect(find('input').autofocus).to.be.ok;
|
||||
});
|
||||
|
||||
it('handles undefined values', function () {
|
||||
it('handles undefined values', async function () {
|
||||
this.set('text', undefined);
|
||||
this.render(hbs`{{gh-trim-focus-input value=(readonly text) shouldFocus=true}}`);
|
||||
expect(this.$('.gh-input').attr('autofocus')).to.be.ok;
|
||||
await render(hbs`{{gh-trim-focus-input value=(readonly text) shouldFocus=true}}`);
|
||||
expect(find('input').autofocus).to.be.ok;
|
||||
});
|
||||
|
||||
it('handles non-string values', function () {
|
||||
it('handles non-string values', async function () {
|
||||
this.set('text', 10);
|
||||
this.render(hbs`{{gh-trim-focus-input value=(readonly text) shouldFocus=true}}`);
|
||||
expect(this.$('.gh-input').val()).to.equal('10');
|
||||
await render(hbs`{{gh-trim-focus-input value=(readonly text) shouldFocus=true}}`);
|
||||
expect(find('input').value).to.equal('10');
|
||||
});
|
||||
});
|
||||
|
@ -1,13 +1,11 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {find} from 'ember-native-dom-helpers';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {find, render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-unsplash-photo', function () {
|
||||
setupComponentTest('gh-unsplash-photo', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
// NOTE: images.unsplash.com replaced with example.com to ensure we aren't
|
||||
@ -70,16 +68,16 @@ describe('Integration: Component: gh-unsplash-photo', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('sets background-color style', function () {
|
||||
this.render(hbs`{{gh-unsplash-photo photo=photo}}`);
|
||||
it('sets background-color style', async function () {
|
||||
await render(hbs`{{gh-unsplash-photo photo=photo}}`);
|
||||
|
||||
expect(
|
||||
find('[data-test-unsplash-photo-container]').attributes.style.value
|
||||
).to.have.string('background-color: #A8A99B');
|
||||
});
|
||||
|
||||
it('sets padding-bottom style', function () {
|
||||
this.render(hbs`{{gh-unsplash-photo photo=photo}}`);
|
||||
it('sets padding-bottom style', async function () {
|
||||
await render(hbs`{{gh-unsplash-photo photo=photo}}`);
|
||||
|
||||
// don't check full padding-bottom value as it will likely vary across
|
||||
// browsers
|
||||
@ -88,16 +86,16 @@ describe('Integration: Component: gh-unsplash-photo', function () {
|
||||
).to.have.string('padding-bottom: 66.66');
|
||||
});
|
||||
|
||||
it('uses correct image size url', function () {
|
||||
this.render(hbs`{{gh-unsplash-photo photo=photo}}`);
|
||||
it('uses correct image size url', async function () {
|
||||
await render(hbs`{{gh-unsplash-photo photo=photo}}`);
|
||||
|
||||
expect(
|
||||
find('[data-test-unsplash-photo-image]').attributes.src.value
|
||||
).to.have.string('&w=1200');
|
||||
});
|
||||
|
||||
it('calculates image width/height', function () {
|
||||
this.render(hbs`{{gh-unsplash-photo photo=photo}}`);
|
||||
it('calculates image width/height', async function () {
|
||||
await render(hbs`{{gh-unsplash-photo photo=photo}}`);
|
||||
|
||||
expect(
|
||||
find('[data-test-unsplash-photo-image]').attributes.width.value
|
||||
|
@ -1,24 +1,23 @@
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
describe('Integration: Component: gh-unsplash', function () {
|
||||
setupComponentTest('gh-unsplash', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
it('renders', function () {
|
||||
it('renders', async function () {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
// Template block usage:
|
||||
// this.render(hbs`
|
||||
// await render(hbs`
|
||||
// {{#gh-unsplash}}
|
||||
// template content
|
||||
// {{/gh-unsplash}}
|
||||
// `);
|
||||
|
||||
this.render(hbs`{{gh-unsplash}}`);
|
||||
await render(hbs`{{gh-unsplash}}`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import Pretender from 'pretender';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import sinon from 'sinon';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {click, find, findAll} from 'ember-native-dom-helpers';
|
||||
import {click, find, findAll, render, settled} from '@ember/test-helpers';
|
||||
import {createFile} from '../../helpers/file-upload';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {run} from '@ember/runloop';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
const stubSuccessfulUpload = function (server, delay = 0) {
|
||||
server.post('/ghost/api/v2/admin/uploads/', function () {
|
||||
@ -27,9 +26,7 @@ const stubFailedUpload = function (server, code, error, delay = 0) {
|
||||
};
|
||||
|
||||
describe('Integration: Component: gh-uploader', function () {
|
||||
setupComponentTest('gh-uploader', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
let server;
|
||||
|
||||
@ -47,10 +44,10 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
});
|
||||
|
||||
it('triggers uploads when `files` is set', async function () {
|
||||
this.render(hbs`{{#gh-uploader files=files}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files}}{{/gh-uploader}}`);
|
||||
|
||||
this.set('files', [createFile()]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
let [lastRequest] = server.handledRequests;
|
||||
expect(server.handledRequests.length).to.equal(1);
|
||||
@ -62,10 +59,10 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
});
|
||||
|
||||
it('triggers multiple uploads', async function () {
|
||||
this.render(hbs`{{#gh-uploader files=files}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files}}{{/gh-uploader}}`);
|
||||
|
||||
this.set('files', [createFile(), createFile()]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(server.handledRequests.length).to.equal(2);
|
||||
});
|
||||
@ -73,9 +70,9 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
it('triggers onStart when upload starts', async function () {
|
||||
this.set('uploadStarted', sinon.spy());
|
||||
|
||||
this.render(hbs`{{#gh-uploader files=files onStart=(action uploadStarted)}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files onStart=(action uploadStarted)}}{{/gh-uploader}}`);
|
||||
this.set('files', [createFile(), createFile()]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(this.get('uploadStarted').calledOnce).to.be.true;
|
||||
});
|
||||
@ -83,9 +80,9 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
it('triggers onUploadSuccess when a file uploads', async function () {
|
||||
this.set('fileUploaded', sinon.spy());
|
||||
|
||||
this.render(hbs`{{#gh-uploader files=files onUploadSuccess=(action fileUploaded)}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files onUploadSuccess=(action fileUploaded)}}{{/gh-uploader}}`);
|
||||
this.set('files', [createFile(['test'], {name: 'file1.png'}), createFile()]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
// triggered for each file
|
||||
expect(this.get('fileUploaded').calledTwice).to.be.true;
|
||||
@ -99,12 +96,12 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
it('triggers onComplete when all files uploaded', async function () {
|
||||
this.set('uploadsFinished', sinon.spy());
|
||||
|
||||
this.render(hbs`{{#gh-uploader files=files onComplete=(action uploadsFinished)}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files onComplete=(action uploadsFinished)}}{{/gh-uploader}}`);
|
||||
this.set('files', [
|
||||
createFile(['test'], {name: 'file1.png'}),
|
||||
createFile(['test'], {name: 'file2.png'})
|
||||
]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(this.get('uploadsFinished').calledOnce).to.be.true;
|
||||
|
||||
@ -120,17 +117,17 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
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}}`);
|
||||
await render(hbs`{{#gh-uploader files=files onComplete=(action uploadsFinished)}}{{/gh-uploader}}`);
|
||||
this.set('files', [
|
||||
createFile(['test'], {name: 'file1.png'})
|
||||
]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
this.set('files', [
|
||||
createFile(['test'], {name: 'file2.png'})
|
||||
]);
|
||||
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
let [results] = this.get('uploadsFinished').getCall(1).args;
|
||||
expect(results.length).to.equal(1);
|
||||
@ -148,12 +145,12 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
|
||||
this.set('uploadsFinished', sinon.spy());
|
||||
|
||||
this.render(hbs`{{#gh-uploader files=files onComplete=(action uploadsFinished)}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files onComplete=(action uploadsFinished)}}{{/gh-uploader}}`);
|
||||
this.set('files', [
|
||||
createFile(['test'], {name: 'file1.png'}), // large - finishes last
|
||||
createFile(['test'], {name: 'file2.png'}) // small - finishes first
|
||||
]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
let [results] = this.get('uploadsFinished').getCall(0).args;
|
||||
expect(results.length).to.equal(2);
|
||||
@ -164,7 +161,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
let errorSpy = sinon.spy(console, 'error');
|
||||
stubSuccessfulUpload(server, 100);
|
||||
|
||||
this.render(hbs`{{#gh-uploader files=files}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files}}{{/gh-uploader}}`);
|
||||
this.set('files', [createFile()]);
|
||||
|
||||
// logs error because upload is in progress
|
||||
@ -177,7 +174,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
this.set('files', [createFile()]);
|
||||
}, 200);
|
||||
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(server.handledRequests.length).to.equal(2);
|
||||
expect(errorSpy.calledOnce).to.be.true;
|
||||
@ -187,7 +184,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
it('yields isUploading whilst upload is in progress', async function () {
|
||||
stubSuccessfulUpload(server, 200);
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader files=files as |uploader|}}
|
||||
{{#if uploader.isUploading}}
|
||||
<div class="is-uploading-test"></div>
|
||||
@ -200,7 +197,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
expect(find('.is-uploading-test')).to.exist;
|
||||
}, 100);
|
||||
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(find('.is-uploading-test')).to.not.exist;
|
||||
});
|
||||
@ -208,7 +205,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
it('yields progressBar component with total upload progress', async function () {
|
||||
stubSuccessfulUpload(server, 200);
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader files=files as |uploader|}}
|
||||
{{uploader.progressBar}}
|
||||
{{/gh-uploader}}`);
|
||||
@ -222,14 +219,14 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
expect(progressWidth).to.be.below(100);
|
||||
}, 100);
|
||||
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
let progressWidth = parseInt(find('[data-test-progress-bar]').style.width);
|
||||
expect(progressWidth).to.equal(100);
|
||||
});
|
||||
|
||||
it('yields files property', function () {
|
||||
this.render(hbs`
|
||||
it('yields files property', async function () {
|
||||
await render(hbs`
|
||||
{{#gh-uploader files=files as |uploader|}}
|
||||
{{#each uploader.files as |file|}}
|
||||
<div class="file">{{file.name}}</div>
|
||||
@ -250,7 +247,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
this.set('cancelled', sinon.spy());
|
||||
this.set('complete', sinon.spy());
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader files=files onCancel=(action cancelled) as |uploader|}}
|
||||
<button class="cancel-button" {{action uploader.cancel}}>Cancel</button>
|
||||
{{/gh-uploader}}`);
|
||||
@ -261,7 +258,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
click('.cancel-button');
|
||||
}, 50);
|
||||
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(this.get('cancelled').calledOnce, 'onCancel triggered').to.be.true;
|
||||
expect(this.get('complete').notCalled, 'onComplete triggered').to.be.true;
|
||||
@ -272,18 +269,18 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
return [200, {'Content-Type': 'application/json'}, '"/content/images/test.png"'];
|
||||
});
|
||||
|
||||
this.render(hbs`{{#gh-uploader files=files uploadUrl="/images/"}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files uploadUrl="/images/"}}{{/gh-uploader}}`);
|
||||
this.set('files', [createFile()]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
let [lastRequest] = server.handledRequests;
|
||||
expect(lastRequest.url).to.equal('/ghost/api/v2/admin/images/');
|
||||
});
|
||||
|
||||
it('passes supplied paramName in request', async function () {
|
||||
this.render(hbs`{{#gh-uploader files=files paramName="testupload"}}{{/gh-uploader}}`);
|
||||
await render(hbs`{{#gh-uploader files=files paramName="testupload"}}{{/gh-uploader}}`);
|
||||
this.set('files', [createFile()]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
let [lastRequest] = server.handledRequests;
|
||||
// requestBody is a FormData object
|
||||
@ -297,11 +294,11 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
it('validates file extensions by default', async function () {
|
||||
this.set('onFailed', sinon.spy());
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader files=files extensions="jpg,jpeg" onFailed=(action onFailed)}}{{/gh-uploader}}
|
||||
`);
|
||||
this.set('files', [createFile(['test'], {name: 'test.png'})]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
let [onFailedResult] = this.get('onFailed').firstCall.args;
|
||||
expect(onFailedResult.length).to.equal(1);
|
||||
@ -315,11 +312,11 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
});
|
||||
this.set('onFailed', sinon.spy());
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader files=files validate=(action validate) onFailed=(action onFailed)}}{{/gh-uploader}}
|
||||
`);
|
||||
this.set('files', [createFile(['test'], {name: 'test.png'})]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
let [onFailedResult] = this.get('onFailed').firstCall.args;
|
||||
expect(onFailedResult.length).to.equal(1);
|
||||
@ -328,7 +325,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
});
|
||||
|
||||
it('yields errors when validation fails', async function () {
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader files=files extensions="jpg,jpeg" as |uploader|}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="error-fileName">{{error.fileName}}</div>
|
||||
@ -337,7 +334,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
{{/gh-uploader}}
|
||||
`);
|
||||
this.set('files', [createFile(['test'], {name: 'test.png'})]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(find('.error-fileName').textContent).to.equal('test.png');
|
||||
expect(find('.error-message').textContent).to.match(/not supported/);
|
||||
@ -353,7 +350,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
this.set('uploadFailed', sinon.spy());
|
||||
this.set('uploadComplete', sinon.spy());
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader
|
||||
files=files
|
||||
onFailed=(action uploadFailed)
|
||||
@ -364,7 +361,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
createFile(['test'], {name: 'file1.png'}),
|
||||
createFile(['test'], {name: 'file2.png'})
|
||||
]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(this.get('uploadFailed').calledOnce).to.be.true;
|
||||
expect(this.get('uploadComplete').calledOnce).to.be.true;
|
||||
@ -378,7 +375,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
it('triggers onUploadFailure when each upload fails', async function () {
|
||||
this.set('uploadFail', sinon.spy());
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader
|
||||
files=files
|
||||
onUploadFailure=(action uploadFail)}}
|
||||
@ -388,7 +385,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
createFile(['test'], {name: 'file1.png'}),
|
||||
createFile(['test'], {name: 'file2.png'})
|
||||
]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(this.get('uploadFail').calledTwice).to.be.true;
|
||||
|
||||
@ -402,7 +399,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
});
|
||||
|
||||
it('yields errors when uploads fail', async function () {
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-uploader files=files as |uploader|}}
|
||||
{{#each uploader.errors as |error|}}
|
||||
<div class="error-fileName">{{error.fileName}}</div>
|
||||
@ -411,7 +408,7 @@ describe('Integration: Component: gh-uploader', function () {
|
||||
{{/gh-uploader}}
|
||||
`);
|
||||
this.set('files', [createFile(['test'], {name: 'test.png'})]);
|
||||
await wait();
|
||||
await settled();
|
||||
|
||||
expect(find('.error-fileName').textContent).to.equal('test.png');
|
||||
expect(find('.error-message').textContent).to.equal('Error: No upload for you');
|
||||
|
@ -1,17 +1,15 @@
|
||||
import DS from 'ember-data';
|
||||
import EmberObject from '@ember/object';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import {describe, it} from 'mocha';
|
||||
import {expect} from 'chai';
|
||||
import {setupComponentTest} from 'ember-mocha';
|
||||
import {find, render} from '@ember/test-helpers';
|
||||
import {setupRenderingTest} from 'ember-mocha';
|
||||
|
||||
const {Errors} = DS;
|
||||
|
||||
describe('Integration: Component: gh-validation-status-container', function () {
|
||||
setupComponentTest('gh-validation-status-container', {
|
||||
integration: true
|
||||
});
|
||||
setupRenderingTest();
|
||||
|
||||
beforeEach(function () {
|
||||
let testObject = EmberObject.create();
|
||||
@ -22,60 +20,52 @@ describe('Integration: Component: gh-validation-status-container', function () {
|
||||
this.set('testObject', testObject);
|
||||
});
|
||||
|
||||
it('has no success/error class by default', function () {
|
||||
this.render(hbs`
|
||||
it('has no success/error class by default', async function () {
|
||||
await render(hbs`
|
||||
{{#gh-validation-status-container class="gh-test" property="name" errors=testObject.errors hasValidated=testObject.hasValidated}}
|
||||
{{/gh-validation-status-container}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
expect(this.$('.gh-test')).to.have.length(1);
|
||||
expect(this.$('.gh-test').hasClass('success')).to.be.false;
|
||||
expect(this.$('.gh-test').hasClass('error')).to.be.false;
|
||||
});
|
||||
expect(find('.gh-test')).to.exist;
|
||||
expect(find('.gh-test')).to.not.have.class('success');
|
||||
expect(find('.gh-test')).to.not.have.class('error');
|
||||
});
|
||||
|
||||
it('has success class when valid', function () {
|
||||
it('has success class when valid', async function () {
|
||||
this.get('testObject.hasValidated').push('name');
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-validation-status-container class="gh-test" property="name" errors=testObject.errors hasValidated=testObject.hasValidated}}
|
||||
{{/gh-validation-status-container}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
expect(this.$('.gh-test')).to.have.length(1);
|
||||
expect(this.$('.gh-test').hasClass('success')).to.be.true;
|
||||
expect(this.$('.gh-test').hasClass('error')).to.be.false;
|
||||
});
|
||||
expect(find('.gh-test')).to.exist;
|
||||
expect(find('.gh-test')).to.have.class('success');
|
||||
expect(find('.gh-test')).to.not.have.class('error');
|
||||
});
|
||||
|
||||
it('has error class when invalid', function () {
|
||||
it('has error class when invalid', async function () {
|
||||
this.get('testObject.hasValidated').push('name');
|
||||
this.get('testObject.errors').add('name', 'has error');
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-validation-status-container class="gh-test" property="name" errors=testObject.errors hasValidated=testObject.hasValidated}}
|
||||
{{/gh-validation-status-container}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
expect(this.$('.gh-test')).to.have.length(1);
|
||||
expect(this.$('.gh-test').hasClass('success')).to.be.false;
|
||||
expect(this.$('.gh-test').hasClass('error')).to.be.true;
|
||||
});
|
||||
expect(find('.gh-test')).to.exist;
|
||||
expect(find('.gh-test')).to.not.have.class('success');
|
||||
expect(find('.gh-test')).to.have.class('error');
|
||||
});
|
||||
|
||||
it('still renders if hasValidated is undefined', function () {
|
||||
it('still renders if hasValidated is undefined', async function () {
|
||||
this.set('testObject.hasValidated', undefined);
|
||||
|
||||
this.render(hbs`
|
||||
await render(hbs`
|
||||
{{#gh-validation-status-container class="gh-test" property="name" errors=testObject.errors hasValidated=testObject.hasValidated}}
|
||||
{{/gh-validation-status-container}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
expect(this.$('.gh-test')).to.have.length(1);
|
||||
});
|
||||
expect(find('.gh-test')).to.exist;
|
||||
});
|
||||
});
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user