Reduced unnecessary waiting in tests

no issue

- fixed `<GhTaskButton>` not resetting after an externally triggered task run such as when pressing Cmd+S
- cleaned up manual timeouts/resets where button reset is now fully handled by `<GhTaskButton>` (these were causing 2.5s waits each time a save occurred in acceptance tests)
- where manual timeouts were required, reduce testing time from >2.5s to 50ms
This commit is contained in:
Kevin Ansfield 2020-05-11 11:37:35 +01:00
parent 8d96b2f610
commit 07f24e69f6
20 changed files with 62 additions and 132 deletions

View File

@ -1,6 +1,6 @@
import Component from '@ember/component';
import config from 'ghost-admin/config/environment';
import {computed} from '@ember/object';
import {action, computed} from '@ember/object';
import {isBlank} from '@ember/utils';
import {reads} from '@ember/object/computed';
import {task, timeout} from 'ember-concurrency';
@ -151,6 +151,13 @@ const GhTaskButton = Component.extend({
}
},
handleReset: action(function () {
const isTaskSuccess = this.get('task.last.isSuccessful') && this.get('task.last.value');
if (this.autoReset && this.showSuccess && isTaskSuccess) {
this._resetButtonState.perform();
}
}),
// when local validation fails there's no transition from failed->running
// so we want to restart the retry spinner animation to show something
// has happened when the button is clicked

View File

@ -6,7 +6,7 @@ import {alias} from '@ember/object/computed';
import {computed, defineProperty} from '@ember/object';
import {inject as controller} from '@ember/controller';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
import {task} from 'ember-concurrency';
const SCRATCH_PROPS = ['name', 'email', 'note'];
@ -89,7 +89,7 @@ export default Controller.extend({
}
},
saveMember: task(function* () {
save: task(function* () {
let {member, scratchMember} = this;
// if Cmd+S is pressed before the field loses focus make sure we're
@ -111,15 +111,6 @@ export default Controller.extend({
}
}).drop(),
save: task(function* () {
yield this.saveMember.perform();
yield timeout(2500);
if (this.get('saveMember.last.isSuccessful') && this.get('saveMember.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveMember.last', null);
}
}).drop(),
fetchMember: task(function* (memberId) {
this.set('isLoading', true);

View File

@ -1,7 +1,7 @@
/* eslint-disable ghost/ember/alias-model-in-controller */
import Controller from '@ember/controller';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
import {task} from 'ember-concurrency';
export default Controller.extend({
notifications: service(),
@ -9,7 +9,7 @@ export default Controller.extend({
actions: {
save() {
this.save.perform();
this.saveTask.perform();
},
toggleLeaveSettingsModal(transition) {
@ -25,8 +25,8 @@ export default Controller.extend({
this.set('leaveSettingsTransition', transition);
// if a save is running, wait for it to finish then transition
if (this.get('save.isRunning')) {
return this.get('save.last').then(() => {
if (this.save.isRunning) {
return this.save.last.then(() => {
transition.retry();
});
}
@ -62,14 +62,5 @@ export default Controller.extend({
notifications.showAPIError(error, {key: 'code-injection.save'});
throw error;
}
}),
save: task(function* () {
yield this.saveTask.perform();
yield timeout(2500);
if (this.get('saveTask.last.isSuccessful') && this.get('saveTask.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveTask.last', null);
}
})
});

View File

@ -8,7 +8,7 @@ import {isEmpty} from '@ember/utils';
import {isThemeValidationError} from 'ghost-admin/services/ajax';
import {notEmpty} from '@ember/object/computed';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
import {task} from 'ember-concurrency';
export default Controller.extend({
config: service(),
@ -97,8 +97,8 @@ export default Controller.extend({
this.set('leaveSettingsTransition', transition);
// if a save is running, wait for it to finish then transition
if (this.get('save.isRunning')) {
return this.get('save.last').then(() => {
if (this.save.isRunning) {
return this.save.last.then(() => {
transition.retry();
});
}
@ -200,7 +200,7 @@ export default Controller.extend({
}
},
saveTask: task(function* () {
save: task(function* () {
let navItems = this.get('settings.navigation');
let secondaryNavItems = this.get('settings.secondaryNavigation');
@ -235,15 +235,6 @@ export default Controller.extend({
}
}),
save: task(function* () {
yield this.saveTask.perform();
yield timeout(2500);
if (this.get('saveTask.last.isSuccessful') && this.get('saveTask.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveTask.last', null);
}
}),
addNewNavItem(item) {
let navItems = item.isSecondary ? this.get('settings.secondaryNavigation') : this.get('settings.navigation');

View File

@ -11,7 +11,7 @@ import {computed} from '@ember/object';
import {htmlSafe} from '@ember/string';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
import {task} from 'ember-concurrency';
const ICON_EXTENSIONS = ['ico', 'png'];
@ -135,8 +135,8 @@ export default Controller.extend({
this.set('leaveSettingsTransition', transition);
// if a save is running, wait for it to finish then transition
if (this.get('save.isRunning')) {
return this.get('save.last').then(() => {
if (this.save.isRunning) {
return this.save.last.then(() => {
transition.retry();
});
}
@ -321,7 +321,7 @@ export default Controller.extend({
});
},
saveSettings: task(function* () {
save: task(function* () {
let notifications = this.notifications;
let config = this.config;
@ -339,14 +339,5 @@ export default Controller.extend({
}
throw error;
}
}),
save: task(function* () {
yield this.saveSettings.perform();
yield timeout(2500);
if (this.get('saveSettings.last.isSuccessful') && this.get('saveSettings.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveSettings.last', null);
}
})
});

View File

@ -1,4 +1,5 @@
import Controller from '@ember/controller';
import config from 'ghost-admin/config/environment';
import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard';
import {
IMAGE_EXTENSIONS,
@ -20,6 +21,13 @@ export default Controller.extend({
selectedApiKey: null,
isApiKeyRegenerated: false,
init() {
this._super(...arguments);
if (this.isTesting === undefined) {
this.isTesting = config.environment === 'test';
}
},
integration: alias('model'),
apiUrl: computed(function () {
@ -157,31 +165,22 @@ export default Controller.extend({
}
},
saveIntegration: task(function* () {
return yield this.integration.save();
}),
save: task(function* () {
yield this.saveIntegration.perform();
yield timeout(2500);
if (this.get('saveIntegration.last.isSuccessful') && this.get('saveIntegration.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveIntegration.last', null);
}
return yield this.integration.save();
}),
copyContentKey: task(function* () {
copyTextToClipboard(this.integration.contentKey.secret);
yield timeout(3000);
yield timeout(this.isTesting ? 50 : 3000);
}),
copyAdminKey: task(function* () {
copyTextToClipboard(this.integration.adminKey.secret);
yield timeout(3000);
yield timeout(this.isTesting ? 50 : 3000);
}),
copyApiUrl: task(function* () {
copyTextToClipboard(this.apiUrl);
yield timeout(3000);
yield timeout(this.isTesting ? 50 : 3000);
})
});

View File

@ -2,7 +2,7 @@
import Controller from '@ember/controller';
import {alias} from '@ember/object/computed';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
import {task} from 'ember-concurrency';
export default Controller.extend({
notifications: service(),
@ -34,8 +34,8 @@ export default Controller.extend({
this.set('leaveSettingsTransition', transition);
// if a save is running, wait for it to finish then transition
if (this.get('save.isRunning')) {
return this.get('save.last').then(() => {
if (this.save.isRunning) {
return this.save.last.then(() => {
transition.retry();
});
}
@ -61,7 +61,7 @@ export default Controller.extend({
}
},
saveTask: task(function* () {
save: task(function* () {
let amp = this.ampSettings;
let settings = this.settings;
@ -73,15 +73,5 @@ export default Controller.extend({
this.notifications.showAPIError(error);
throw error;
}
}).drop(),
save: task(function* () {
yield this.saveTask.perform();
yield timeout(2500);
if (this.get('saveTask.last.isSuccessful') && this.get('saveTask.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveTask.last', null);
}
}).drop()
});

View File

@ -4,7 +4,7 @@ import boundOneWay from 'ghost-admin/utils/bound-one-way';
import {empty} from '@ember/object/computed';
import {isInvalidError} from 'ember-ajax/errors';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
import {task} from 'ember-concurrency';
export default Controller.extend({
ghostPaths: service(),
@ -65,8 +65,8 @@ export default Controller.extend({
this.set('leaveSettingsTransition', transition);
// if a save is running, wait for it to finish then transition
if (this.get('save.isRunning')) {
return this.get('save.last').then(() => {
if (this.save.isRunning) {
return this.save.last.then(() => {
transition.retry();
});
}
@ -94,7 +94,7 @@ export default Controller.extend({
}
},
saveTask: task(function* () {
save: task(function* () {
let slack = this.slackSettings;
let settings = this.settings;
let slackArray = this.slackArray;
@ -113,15 +113,6 @@ export default Controller.extend({
}
}).drop(),
save: task(function* () {
yield this.saveTask.perform();
yield timeout(2500);
if (this.get('saveTask.last.isSuccessful') && this.get('saveTask.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveTask.last', null);
}
}).drop(),
sendTestNotification: task(function* () {
let notifications = this.notifications;
let slackApi = this.get('ghostPaths.url').api('slack', 'test');

View File

@ -2,7 +2,7 @@
import Controller from '@ember/controller';
import {alias} from '@ember/object/computed';
import {inject as service} from '@ember/service';
import {task, timeout} from 'ember-concurrency';
import {task} from 'ember-concurrency';
export default Controller.extend({
notifications: service(),
@ -40,8 +40,8 @@ export default Controller.extend({
this.set('leaveSettingsTransition', transition);
// if a save is running, wait for it to finish then transition
if (this.get('save.isRunning')) {
return this.get('save.last').then(() => {
if (this.save.isRunning) {
return this.save.last.then(() => {
transition.retry();
});
}
@ -68,7 +68,7 @@ export default Controller.extend({
}
},
saveTask: task(function* () {
save: task(function* () {
let unsplash = this.unsplashSettings;
let settings = this.settings;
@ -83,14 +83,5 @@ export default Controller.extend({
throw error;
}
}
}).drop(),
save: task(function* () {
yield this.saveTask.perform();
yield timeout(2500);
if (this.get('saveTask.last.isSuccessful') && this.get('saveTask.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveTask.last', null);
}
}).drop()
});

View File

@ -10,7 +10,6 @@ import {task, timeout} from 'ember-concurrency';
export default Controller.extend({
ghostPaths: service(),
isTesting: undefined,
selectedApiKey: null,
isApiKeyRegenerated: false,
@ -56,11 +55,11 @@ export default Controller.extend({
copyAdminKey: task(function* () {
copyTextToClipboard(this.integration.adminKey.secret);
yield timeout(3000);
yield timeout(this.isTesting ? 50 : 3000);
}),
copyApiUrl: task(function* () {
copyTextToClipboard(this.apiUrl);
yield timeout(3000);
yield timeout(this.isTesting ? 50 : 3000);
})
});

View File

@ -5,7 +5,7 @@ import {alias} from '@ember/object/computed';
import {computed, defineProperty} from '@ember/object';
import {inject as service} from '@ember/service';
import {slugify} from '@tryghost/string';
import {task, timeout} from 'ember-concurrency';
import {task} from 'ember-concurrency';
const SCRATCH_PROPS = ['name', 'slug', 'description', 'metaTitle', 'metaDescription'];
@ -74,7 +74,7 @@ export default Controller.extend({
}
},
saveTask: task(function* () {
save: task(function* () {
let {tag, scratchTag} = this;
// if Cmd+S is pressed before the field loses focus make sure we're
@ -94,15 +94,6 @@ export default Controller.extend({
this.notifications.showAPIError(error, {key: 'tag.save'});
}
}
}),
save: task(function* () {
yield this.saveTask.perform();
yield timeout(2500);
if (this.get('saveTask.last.isSuccessful') && this.get('saveTask.last.value')) {
// Reset last task to bring button back to idle state
yield this.set('saveTask.last', null);
}
}).drop(),
fetchTag: task(function* (slug) {

View File

@ -6,10 +6,8 @@
isFailure=this.isFailure
)}}
{{else}}
<span>
{{#if this.isRunning}}{{svg-jar "spinner" class="gh-icon-spinner"}}{{this.runningText}}{{/if}}
{{#if this.isIdle}}{{this.buttonText}}{{/if}}
{{#if this.isSuccess}}{{svg-jar "check-circle"}} {{this.successText}}{{/if}}
{{#if this.isFailure}}{{svg-jar "retry"}} {{this.failureText}}{{/if}}
</span>
{{#if this.isRunning}}<span>{{svg-jar "spinner" class="gh-icon-spinner"}}{{this.runningText}}</span>{{/if}}
{{#if this.isIdle}}<span>{{this.buttonText}}</span>{{/if}}
{{#if this.isSuccess}}<span {{did-insert this.handleReset}}>{{svg-jar "check-circle"}} {{this.successText}}</span>{{/if}}
{{#if this.isFailure}}<span>{{svg-jar "retry"}} {{this.failureText}}</span>{{/if}}
{{/if}}

View File

@ -21,7 +21,7 @@
{{/unless}}
{{/if}}
<GhTaskButton @class="gh-btn gh-btn-blue gh-btn-icon" @type="button" @task={{this.saveMember}} @autoReset={{true}} @data-test-button="save" />
<GhTaskButton @class="gh-btn gh-btn-blue gh-btn-icon" @type="button" @task={{this.save}} @autoReset={{true}} @data-test-button="save" />
</section>
</GhCanvasHeader>

View File

@ -4,7 +4,7 @@
Design
</h2>
<section class="view-actions">
<GhTaskButton @task={{this.saveTask}} @autoReset={{true}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button={{true}} />
<GhTaskButton @task={{this.save}} @autoReset={{true}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button={{true}} />
</section>
</GhCanvasHeader>

View File

@ -5,7 +5,7 @@
General settings
</h2>
<section class="view-actions">
<GhTaskButton @buttonText="Save settings" @task={{this.saveSettings}} @autoReset={{true}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button="true" />
<GhTaskButton @buttonText="Save settings" @task={{this.save}} @autoReset={{true}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button="true" />
</section>
</GhCanvasHeader>

View File

@ -7,7 +7,7 @@
{{this.integration.name}}
</h2>
<section class="view-actions">
<GhTaskButton @task={{this.saveIntegration}} @class="gh-btn gh-btn-blue gh-btn-icon" @autoReset={{true}} data-test-button="save" />
<GhTaskButton @task={{this.save}} @class="gh-btn gh-btn-blue gh-btn-icon" @autoReset={{true}} data-test-button="save" />
</section>
</GhCanvasHeader>

View File

@ -6,7 +6,7 @@
AMP
</h2>
<section class="view-actions">
<GhTaskButton @task={{this.saveTask}} @class="gh-btn gh-btn-blue gh-btn-icon" @autoReset={{true}} data-test-save-button={{true}} />
<GhTaskButton @task={{this.save}} @class="gh-btn gh-btn-blue gh-btn-icon" @autoReset={{true}} data-test-save-button={{true}} />
</section>
</GhCanvasHeader>

View File

@ -6,7 +6,7 @@
Slack
</h2>
<section class="view-actions">
<GhTaskButton @task={{this.saveTask}} @autoReset={{true}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button={{true}} />
<GhTaskButton @task={{this.save}} @autoReset={{true}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button={{true}} />
</section>
</GhCanvasHeader>

View File

@ -6,7 +6,7 @@
Unsplash
</h2>
<section class="view-actions">
<GhTaskButton @task={{this.saveTask}} @autoReset={{true}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button={{true}} />
<GhTaskButton @task={{this.save}} @autoReset={{true}} @class="gh-btn gh-btn-blue gh-btn-icon" data-test-save-button={{true}} />
</section>
</GhCanvasHeader>

View File

@ -7,7 +7,7 @@
{{if this.tag.isNew "New tag" this.tag.name}}
</h2>
<section class="view-actions">
<GhTaskButton @task={{this.saveTask}} @type="button" @class="gh-btn gh-btn-blue gh-btn-icon" @autoReset={{true}} @data-test-button="save" />
<GhTaskButton @task={{this.save}} @type="button" @class="gh-btn gh-btn-blue gh-btn-icon" @autoReset={{true}} @data-test-button="save" />
</section>
</GhCanvasHeader>