0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-01 02:41:39 -05:00

Added initial tests for new publish flow

refs https://github.com/TryGhost/Team/issues/1586

- added test helper for enabling/disabling members and helpers for disabling mailgun and newsletters
- added `loginAsRole` helper that alleviates duplication of user+role creation and also handles log-out before log-in so it's easier to mix different role tests within a block that has a default role setup in `beforeEach()`
- cleaned up editor tests that were skipped due to using the old publish flow
- added `Publish flow` acceptance test suite with an initial batch of tests
This commit is contained in:
Kevin Ansfield 2022-05-30 19:08:07 +01:00
parent b123b297b6
commit 3f0e46c7b0
12 changed files with 398 additions and 427 deletions

View file

@ -1,6 +1,6 @@
<div class="flex flex-column h-100 items-center overflow-auto">
<header class="gh-publish-header" data-test-modal="publish-flow">
<button class="gh-btn-editor gh-publish-back-button" title="Close" type="button" {{on "click" @close}}>
<div class="flex flex-column h-100 items-center overflow-auto" data-test-modal="publish-flow">
<header class="gh-publish-header">
<button class="gh-btn-editor gh-publish-back-button" title="Close" type="button" {{on "click" @close}} data-test-button="close-publish-flow">
<span>{{svg-jar "arrow-left"}} Editor</span>
</button>
@ -10,10 +10,17 @@
type="button"
class="gh-btn gh-btn-editor gh-editor-preview-trigger"
{{on "click" @data.togglePreviewPublish}}
data-test-button="publish-flow-preview"
>
<span>Preview</span>
</button>
<button class="gh-btn gh-btn-editor darkgrey gh-publish-trigger active" title="Close" type="button" {{on "click" @close}}>
<button
type="button"
class="gh-btn gh-btn-editor darkgrey gh-publish-trigger active"
title="Close"
{{on "click" @close}}
data-test-button="publish-flow-publish"
>
<span>Publish</span>
</button>
<div class="settings-menu-toggle-spacer"></div>

View file

@ -1,5 +1,5 @@
{{#let @publishOptions.post as |post|}}
<div class="gh-publish-title">
<div class="gh-publish-title" data-test-publish-flow="complete" data-test-complete-title>
{{#if post.isScheduled}}
<span class="green">All set!</span>
@ -27,7 +27,7 @@
{{#if post.emailOnly}}
Your email has been sent.
{{else if (and post.isPost @postCount)}}
Thats {{@postCount}} posts published, keep going!
Thats {{gh-pluralize @postCount "post"}} published, keep going!
{{else}}
Your {{post.displayName}} has been published.
{{/if}}
@ -35,7 +35,7 @@
</div>
{{#if post.emailOnly}}
<div class="gh-publish-confirmation">
<div class="gh-publish-confirmation" data-test-complete->
<p>
Your post
{{if post.isScheduled "will be" "was"}}
@ -79,7 +79,7 @@
{{/if}}
</div>
{{else}}
<a href={{post.url}} class="gh-post-bookmark-wrapper" target="_blank" rel="noopener noreferrer">
<a href={{post.url}} class="gh-post-bookmark-wrapper" target="_blank" rel="noopener noreferrer" data-test-complete-bookmark>
<GhPostBookmark @post={{post}} />
</a>
@ -90,6 +90,7 @@
type="button"
class="gh-revert-to-draft"
{{on "click" (fn @close (hash afterTask="revertToDraftTask"))}}
data-test-button="revert-to-draft"
>
<span>Unschedule and revert to draft &rarr;</span>
</button>
@ -100,6 +101,7 @@
type="button"
class="gh-back-to-editor"
{{on "click" @close}}
data-test-button="back-to-editor"
>
<span>Back to editor</span>
</button>

View file

@ -1,8 +1,8 @@
<div class="gh-publish-title">
<div class="gh-publish-title" data-test-publish-flow="confirm">
<div class="green">Ready, set, publish.</div>
<div>Share it with the world.</div>
</div>
<p class="gh-publish-confirmation">
<p class="gh-publish-confirmation" data-test-text="confirm-details">
{{#if @publishOptions.isScheduled}}
{{#let (moment-site-tz @publishOptions.scheduledAtUTC) as |scheduledAt|}}
On
@ -53,7 +53,7 @@
</p>
{{#if this.errorMessage}}
<p class="error gh-box gh-box-error mt3 mb3">
<p class="error gh-box gh-box-error mt3 mb3" data-test-confirm-error>
{{this.errorMessage}}
</p>
{{/if}}
@ -67,6 +67,7 @@
@class="gh-btn gh-btn-large"
@idleClass="gh-btn-pulse"
@runningClass="gh-btn-green gh-btn-icon"
data-test-button="confirm-publish"
/>
<button type="button" class="gh-btn gh-btn-link gh-btn-large gh-publish-cta-secondary" {{on "click" @cancel}}>Back to settings</button>
<button type="button" class="gh-btn gh-btn-link gh-btn-large gh-publish-cta-secondary" {{on "click" @cancel}} data-test-button="back-to-options">Back to settings</button>
</div>

View file

@ -1,16 +1,16 @@
<div class="gh-publish-title">
<div class="gh-publish-title" data-test-publish-flow="options">
<div class="green">Ready, set, publish.</div>
<div>Share it with the world.</div>
</div>
<div class="gh-publish-settings">
<div class="gh-publish-setting">
<div class="gh-publish-setting" data-test-setting="publish-type">
{{#if @publishOptions.emailUnavailable}}
<div class="gh-publish-setting-title">
<div class="gh-publish-setting-title" data-test-setting-title>
{{svg-jar "send-email"}}
<div class="gh-publish-setting-trigger">Publish on site</div>
</div>
{{else}}
<button type="button" class="gh-publish-setting-title" {{on "click" (fn this.toggleSection "publishType")}}>
<button type="button" class="gh-publish-setting-title" {{on "click" (fn this.toggleSection "publishType")}} data-test-setting-title>
{{svg-jar "send-email"}}
<div class="gh-publish-setting-trigger">
<span>{{@publishOptions.selectedPublishTypeOption.display}}</span>
@ -30,12 +30,13 @@
</div>
{{#unless @publishOptions.emailUnavailable}}
<div class="gh-publish-setting">
<div class="gh-publish-setting" data-test-setting="email-recipients">
{{#if (not-eq @publishOptions.publishType "publish")}}
<button
type="button"
class="gh-publish-setting-title"
{{on "click" (fn this.toggleSection "emailRecipients")}}
data-test-setting-title
>
{{svg-jar "member"}}
<div class="gh-publish-setting-trigger">
@ -70,6 +71,7 @@
<button
type="button"
class="gh-publish-setting-title disabled"
data-test-setting-title
>
{{svg-jar "member"}}
<div class="gh-publish-setting-trigger">
@ -91,8 +93,8 @@
{{/unless}}
{{#if (and @publishOptions.post.email (not @publishOptions.emailDisabledInSettings))}}
<div class="gh-publish-setting">
<div class="gh-publish-setting-title disabled">
<div class="gh-publish-setting" data-test-setting="email-recipients">
<div class="gh-publish-setting-title disabled" data-test-setting-title>
{{svg-jar "member"}}
<div class="gh-publish-setting-trigger">
Already sent to
@ -107,8 +109,8 @@
</div>
{{/if}}
<div class="gh-publish-setting last">
<button type="button" class="gh-publish-setting-title" {{on "click" (fn this.toggleSection "publishAt")}}>
<div class="gh-publish-setting last" data-test-setting="publish-time">
<button type="button" class="gh-publish-setting-title" {{on "click" (fn this.toggleSection "publishAt")}} data-test-setting-title>
{{svg-jar "clock"}}
<div class="gh-publish-setting-trigger">
<span>
@ -135,5 +137,7 @@
</div>
<div class="gh-publish-cta">
<button type="button" class="gh-btn gh-btn-black gh-btn-large" {{on "click" @confirm}}><span>Continue, final review &rarr;</span></button>
<button type="button" class="gh-btn gh-btn-black gh-btn-large" {{on "click" @confirm}} data-test-button="continue">
<span>Continue, final review &rarr;</span>
</button>
</div>

View file

@ -1,5 +1,5 @@
<div class="flex flex-column h-100 items-center overflow-auto">
<header class="gh-publish-header" data-test-modal="update-flow">
<div class="flex flex-column h-100 items-center overflow-auto" data-test-modal="update-flow">
<header class="gh-publish-header">
<button class="gh-btn-editor gh-publish-back-button" title="Close" type="button" {{on "click" @close}}>
<span>{{svg-jar "arrow-left"}} Editor</span>
</button>
@ -13,7 +13,7 @@
{{#let @data.publishOptions.post as |post|}}
<div class="gh-publish-settings-container gh-update-flow fade-in">
<div class="gh-publish-title">
<div class="gh-publish-title" data-test-update-flow-title>
{{#if (and post.isSent (not post.isPublished))}}
This {{post.displayName}} was
<span class="green">{{post.status}} by email</span>
@ -23,7 +23,7 @@
{{/if}}
</div>
<div class="gh-publish-confirmation">
<div class="gh-publish-confirmation" data-test-update-flow-confirmation>
<p>
Your
{{post.displayName}}
@ -86,6 +86,7 @@
type="button"
class="gh-revert-to-draft"
{{on "click" (fn @close (hash afterTask="revertToDraftTask"))}}
data-test-button="revert-to-draft"
>
<span>Unschedule and revert to draft &rarr;</span>
</button>
@ -95,6 +96,7 @@
type="button"
class="gh-revert-to-draft"
{{on "click" (fn @close (hash afterTask="revertToDraftTask"))}}
data-test-button="revert-to-draft"
>
<span>Unpublish and revert to private draft &rarr;</span>
</button>

View file

@ -10,6 +10,7 @@
checked={{eq option.value @publishOptions.selectedPublishTypeOption.value}}
disabled={{option.disabled}}
{{on "change" this.onChange}}
data-test-publish-type={{option.value}}
>
<label for="publish-type-{{option.value}}">{{option.label}}</label>
</span>
@ -17,16 +18,16 @@
</fieldset>
{{#if @publishOptions.emailDisabledError}}
<p class="gh-box gh-content-box">
<p class="gh-box gh-content-box" data-test-publish-type-error="email-disabled">
{{@publishOptions.emailDisabledError}}
</p>
{{else if (eq @publishOptions.totalMemberCount 0)}}
<p class="gh-box gh-content-box">
<p class="gh-box gh-content-box" data-test-publish-type-error="no-members">
<LinkTo @route="members">Add members</LinkTo>
to start sending newsletters!
</p>
{{else if (not @publishOptions.mailgunIsConfigured)}}
<p class="gh-box gh-content-box">
<p class="gh-box gh-content-box" data-test-publish-type-error="no-mailgun">
Set up <a href="https://ghost.org/docs/newsletters/#bulk-email-configuration" target="_blank" rel="noreferrer noopener">Mailgun</a> to start sending newsletters!
</p>
{{/if}}

View file

@ -6,9 +6,6 @@ import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-sup
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 {enableMailgun} from '../helpers/mailgun';
import {enableNewsletters} from '../helpers/newsletters';
import {enableStripe} from '../helpers/stripe';
import {expect} from 'chai';
import {selectChoose} from 'ember-power-select/test-support';
import {setupApplicationTest} from 'ember-mocha';
@ -120,319 +117,101 @@ describe('Acceptance: Editor', function () {
return await authenticateSession();
});
it.skip('renders the editor correctly, PSM Publish Date and Save Button', async function () {
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
await visit('/editor/post/1');
expect(currentURL(), 'currentURL')
.to.equal('/editor/post/1');
// open post settings menu
await click('[data-test-psm-trigger]');
// should error, if the publish time is in the wrong format
await fillIn('[data-test-date-time-picker-time-input]', 'foo');
await blur('[data-test-date-time-picker-time-input]');
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 fillIn('[data-test-date-time-picker-datepicker] input', moment.tz('Etc/UTC').add(1, 'day').format('YYYY-MM-DD'));
await blur('[data-test-date-time-picker-datepicker] input');
await fillIn('[data-test-date-time-picker-time-input]', futureTime.format('HH:mm'));
await blur('[data-test-date-time-picker-time-input]');
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
await click('[data-test-psm-trigger]');
await click('[data-test-psm-trigger]');
expect(
find('[data-test-date-time-picker-error]'),
'date picker error after closing PSM'
).to.not.exist;
expect(
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('YYYY-MM-DD'));
expect(
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 blur('[data-test-date-time-picker-time-input]');
await datepickerSelect('[data-test-date-time-picker-datepicker]', validTime.toDate());
// hide psm
await click('[data-test-psm-trigger]');
// checking the flow of the saving button for a draft
expect(
find('[data-test-publishmenu-trigger]').textContent.trim(),
'draft publish button text'
).to.equal('Publish');
expect(
find('[data-test-editor-post-status]').textContent.trim(),
'draft status text'
).to.match(/Draft\s+- Saved/);
// click on publish now
await click('[data-test-publishmenu-trigger]');
expect(
find('[data-test-publishmenu-draft]'),
'draft publish menu is shown'
).to.exist;
await click('[data-test-publishmenu-scheduled-option]');
expect(
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]').textContent.trim(),
'draft post publish button text'
).to.equal('Publish');
// Publish the post and re-open publish menu
await click('[data-test-publishmenu-save]');
expect(
find('[data-test-publishmenu-save]').textContent.trim(),
'publish menu save button updated after draft is published'
).to.equal('Update');
expect(
find('[data-test-publishmenu-published]'),
'publish menu is shown after draft published'
).to.exist;
expect(
find('[data-test-editor-post-status]').textContent.trim(),
'post status updated after draft published'
).to.equal('Published');
await click('[data-test-publishmenu-cancel]');
await click('[data-test-publishmenu-trigger]');
await click('[data-test-publishmenu-unpublished-option]');
expect(
find('[data-test-publishmenu-save]').textContent.trim(),
'published post unpublish button text'
).to.equal('Unpublish');
// post id 2 is a published post, checking for published post behaviour now
await visit('/editor/post/2');
expect(currentURL(), 'currentURL').to.equal('/editor/post/2');
await click('[data-test-psm-trigger]');
expect(find('[data-test-date-time-picker-date-input]').value).to.equal('2015-12-19');
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').toDate());
await fillIn('[data-test-date-time-picker-time-input]', '10:00');
await blur('[data-test-date-time-picker-time-input]');
await click('[data-test-psm-trigger]');
// saving
await click('[data-test-publishmenu-trigger]');
expect(
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]').textContent.trim(),
'publish menu save button updated after published post is updated'
).to.equal('Update');
// go to settings to change the timezone
await visit('/settings/general');
await click('[data-test-toggle-timezone]');
expect(currentURL(), 'currentURL for settings')
.to.equal('/settings/general');
expect(find('#timezone option:checked').textContent.trim(), 'default timezone')
.to.equal('(GMT) UTC');
// select a new timezone
find('#timezone option[value="Pacific/Kwajalein"]').selected = true;
await triggerEvent('#timezone', 'change');
// save the settings
await click('[data-test-button="save"]');
expect(find('#timezone option:checked').textContent.trim(), 'new timezone after saving')
.to.equal('(GMT +12:00) International Date Line West');
// and now go back to the editor
await visit('/editor/post/2');
expect(currentURL(), 'currentURL in editor')
.to.equal('/editor/post/2');
await click('[data-test-psm-trigger]');
expect(
find('[data-test-date-time-picker-date-input]').value,
'date after timezone change'
).to.equal('2016-05-10');
expect(
find('[data-test-date-time-picker-time-input]').value,
'time after timezone change'
).to.equal('22:00');
// unpublish
await click('[data-test-publishmenu-trigger]');
await click('[data-test-publishmenu-unpublished-option]');
expect(
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]').textContent.trim(),
'publish menu save button updated after published post is unpublished'
).to.equal('Publish');
expect(
find('[data-test-publishmenu-draft]'),
'draft menu is shown after unpublished'
).to.exist;
expect(
find('[data-test-editor-post-status]').textContent.trim(),
'post status updated after unpublished'
).to.match(/Draft\s+- Saved/);
// schedule post
await click('[data-test-publishmenu-cancel]');
await click('[data-test-publishmenu-trigger]');
await click('[data-test-publishmenu-scheduled-option]');
expect(
find('[data-test-publishmenu-save]').textContent.trim(),
'draft post, schedule button text'
).to.equal('Schedule');
// get time in current timezone and select the current date
// will result in the default +5mins schedule time
let newFutureTime = moment.tz('Pacific/Kwajalein');
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]').textContent.trim(),
'publish menu save button updated after draft is scheduled'
).to.equal('Reschedule');
await click('[data-test-publishmenu-cancel]');
expect(
find('[data-test-publishmenu-scheduled]'),
'publish menu is not shown after closed'
).to.not.exist;
// expect countdown to show warning that post is scheduled to be published
await triggerEvent('[data-test-editor-post-status]', 'mouseover');
expect(find('[data-test-schedule-countdown]').textContent.trim(), 'notification countdown')
.to.match(/to be published\s+in (4|5) minutes/);
expect(
find('[data-test-publishmenu-trigger]').textContent.trim(),
'scheduled publish button text'
).to.equal('Scheduled');
expect(
find('[data-test-editor-post-status]').textContent.trim(),
'scheduled post status'
).to.match(/to be published\s+in (4|5) minutes/);
// Re-schedule
await click('[data-test-publishmenu-trigger]');
await click('[data-test-publishmenu-scheduled-option]');
expect(
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]').textContent.trim(),
'publish menu save button text for a rescheduled post'
).to.equal('Reschedule');
await click('[data-test-publishmenu-cancel]');
expect(
find('[data-test-publishmenu-scheduled]'),
'publish menu is not shown after closed'
).to.not.exist;
expect(
find('[data-test-editor-post-status]').textContent.trim(),
'scheduled status text'
).to.match(/to be published\s+in (4|5) minutes/);
// unschedule
await click('[data-test-publishmenu-trigger]');
await click('[data-test-publishmenu-draft-option]');
expect(
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]').textContent.trim(),
'publish menu save button updated after scheduled post is unscheduled'
).to.equal('Publish');
await click('[data-test-publishmenu-cancel]');
expect(
find('[data-test-publishmenu-trigger]').textContent.trim(),
'publish button text after unschedule'
).to.equal('Publish');
expect(
find('[data-test-editor-post-status]').textContent.trim(),
'status text after unschedule'
).to.match(/Draft\s+- Saved/);
expect(
find('[data-test-schedule-countdown]'),
'scheduled countdown after unschedule'
).to.not.exist;
describe('post settings menu', function () {
it('can set publish date', async function () {
let [post1] = this.server.createList('post', 2, {authors: [author]});
let futureTime = moment().tz('Etc/UTC').add(10, 'minutes');
// sanity check
expect(
moment(post1.publishedAt).tz('Etc/UTC').format('YYYY-MM-DD HH:mm:ss'),
'initial publishedAt sanity check')
.to.equal('2015-12-19 16:25:07');
// post id 1 is a draft, checking for draft behaviour now
await visit('/editor/post/1');
// open post settings menu
await click('[data-test-psm-trigger]');
// should error, if the publish time is in the wrong format
await fillIn('[data-test-date-time-picker-time-input]', 'foo');
await blur('[data-test-date-time-picker-time-input]');
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 fillIn('[data-test-date-time-picker-datepicker] input', moment.tz('Etc/UTC').add(1, 'day').format('YYYY-MM-DD'));
await blur('[data-test-date-time-picker-datepicker] input');
await fillIn('[data-test-date-time-picker-time-input]', futureTime.format('HH:mm'));
await blur('[data-test-date-time-picker-time-input]');
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
await click('[data-test-psm-trigger]');
await click('[data-test-psm-trigger]');
expect(
find('[data-test-date-time-picker-error]'),
'date picker error after closing PSM'
).to.not.exist;
expect(
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('YYYY-MM-DD'));
expect(
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');
await fillIn('[data-test-date-time-picker-time-input]', validTime.format('HH:mm'));
await blur('[data-test-date-time-picker-time-input]');
await datepickerSelect('[data-test-date-time-picker-datepicker]', validTime.toDate());
expect(moment(post1.publishedAt).tz('Etc/UTC').format('YYYY-MM-DD HH:mm:ss')).to.equal('2017-04-09 12:00:00');
// go to settings to change the timezone
await visit('/settings/general');
await click('[data-test-toggle-timezone]');
expect(currentURL(), 'currentURL for settings')
.to.equal('/settings/general');
expect(find('#timezone option:checked').textContent.trim(), 'default timezone')
.to.equal('(GMT) UTC');
// select a new timezone
find('#timezone option[value="Pacific/Kwajalein"]').selected = true;
await triggerEvent('#timezone', 'change');
// save the settings
await click('[data-test-button="save"]');
expect(find('#timezone option:checked').textContent.trim(), 'new timezone after saving')
.to.equal('(GMT +12:00) International Date Line West');
// and now go back to the editor
await visit('/editor/post/1');
await click('[data-test-psm-trigger]');
expect(
find('[data-test-date-time-picker-date-input]').value,
'date after timezone change'
).to.equal('2017-04-10');
expect(
find('[data-test-date-time-picker-time-input]').value,
'time after timezone change'
).to.equal('00:00');
});
});
it.skip('handles validation errors when scheduling', async function () {
@ -836,76 +615,4 @@ describe('Acceptance: Editor', function () {
expect(body.posts[0].title).to.equal('CMD-S Test');
});
});
describe('regressions', function () {
let user;
beforeEach(async function () {
const role = this.server.create('role', {name: 'Administrator'});
user = this.server.create('user', {roles: [role]});
this.server.loadFixtures('settings');
return await authenticateSession();
});
// BUG: opening the publish menu and selecting a scheduled time then
// closing prevents scheduling with a "Must be in the past" error
// when re-opening the menu
// https://github.com/TryGhost/Team/issues/1399
it.skip('can close publish menu after selecting schedule then re-open, schedule, and publish without error', async function () {
const post = this.server.create('post', {status: 'draft', authors: [user]});
await visit(`/editor/post/${post.id}`);
await click('[data-test-publishmenu-trigger]');
await click('[data-test-publishmenu-scheduled-option]');
await click('[data-test-publishmenu-cancel]');
await click('[data-test-publishmenu-trigger]');
await click('[data-test-publishmenu-scheduled-option]');
await click('[data-test-publishmenu-save]');
expect(post.attrs.status).to.equal('scheduled');
});
// BUG: re-scheduling a send-only post unexpectedly switched to publish+send
// https://github.com/TryGhost/Ghost/issues/14354
it.skip('can re-schedule an email-only post', async function () {
// Enable newsletters (extra confirmation step)
enableMailgun(this.server);
enableNewsletters(this.server, true);
// Enable stripe to also show paid members breakdown
enableStripe(this.server);
const newsletter = this.server.create('newsletter', {status: 'active', name: 'test newsletter', slug: 'test-newsletter'});
this.server.createList('member', 4, {status: 'free', newsletters: [newsletter]});
this.server.createList('member', 2, {status: 'paid', newsletters: [newsletter]});
const post = this.server.create('post', {status: 'draft', authors: [user]});
const scheduledTime = moment().add(2, 'hours');
await visit(`/editor/post/${post.id}`);
await click('[data-test-publishmenu-trigger]');
await selectChoose('[data-test-distribution-action-select]', 'send');
await click('[data-test-publishmenu-scheduled-option]');
await datepickerSelect('[data-test-publishmenu-draft] [data-test-date-time-picker-datepicker]', new Date(scheduledTime.format().replace(/\+.*$/, '')));
// Expect 4 free and 2 paid recipients here
expect(find('[data-test-email-count="free-members"]')).to.contain.text('4');
expect(find('[data-test-email-count="paid-members"]')).to.contain.text('2');
await click('[data-test-publishmenu-save]');
await click('[data-test-button="confirm-schedule"]');
expect(post.attrs.emailOnly).to.be.true;
await click('[data-test-publishmenu-trigger]');
expect(find('[data-test-publishmenu-header]')).to.contain.text('Will be sent');
await datepickerSelect('[data-test-publishmenu-scheduled] [data-test-date-time-picker-datepicker]', new Date(scheduledTime.add(1, 'day').format().replace(/\+.*$/, '')));
await click('[data-test-publishmenu-save]');
expect(post.attrs.emailOnly).to.be.true;
});
});
});

View file

@ -0,0 +1,218 @@
import loginAsRole from '../../helpers/login-as-role';
import moment from 'moment';
import {blur, click, fillIn, find} from '@ember/test-helpers';
import {disableMailgun, enableMailgun} from '../../helpers/mailgun';
import {disableMembers, enableMembers} from '../../helpers/members';
import {disableNewsletters, enableNewsletters} from '../../helpers/newsletters';
import {expect} from 'chai';
import {setupApplicationTest} from 'ember-mocha';
import {setupMirage} from 'ember-cli-mirage/test-support';
import {visit} from '../../helpers/visit';
describe('Acceptance: Publish flow', function () {
let hooks = setupApplicationTest();
setupMirage(hooks);
beforeEach(function () {
this.server.loadFixtures();
});
it('has minimal features for contributors', async function () {
await loginAsRole('Contributor', this.server);
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
expect(find('[data-test-button="publish-flow"]'), 'publish button').to.not.exist;
expect(find('[data-test-button="contributor-preview"]'), 'contributor preview button').to.exist;
expect(find('[data-test-button="contributor-save"]'), 'contributor save button').to.exist;
await fillIn('[data-test-editor-title-input]', 'Contributor save test');
await click('[data-test-button="contributor-save"]');
expect(post.title, 'post title after save').to.equal('Contributor save test');
});
it('triggers post validation before opening', async function () {
await loginAsRole('Administrator', this.server);
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
await fillIn('[data-test-editor-title-input]', Array(260).join('a'));
await blur('[data-test-editor-title-input]');
await click('[data-test-button="publish-flow"]');
expect(find('.gh-alert'), 'validation shown in alert').to.exist;
expect(find('[data-test-modal="publish-flow"]'), 'publish flow modal').to.not.exist;
});
it('handles timezones correctly when scheduling');
// email unavailable state occurs when
// 1. members signup access is set to "none"
// 2. default newsletter recipients is set to "disabled"
async function testEmailUnavailableFlow() {
await loginAsRole('Administrator', this.server);
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
await fillIn('[data-test-editor-title-input]', 'Members disabled publish test');
await blur('[data-test-editor-title-input]');
await click('[data-test-button="publish-flow"]');
expect(find('[data-test-modal="publish-flow"]'), 'publish flow modal').to.exist;
expect(find('[data-test-publish-flow="options"]'), 'options step').to.exist;
// members/newsletters disabled =
// - fixed "Publish on site" publish type
// - no email options shown
// - standard publish time options
expect(find('[data-test-setting="publish-type"]'), 'publish type setting').to.exist;
expect(
find('[data-test-setting="publish-type"] [data-test-setting-title]'), 'publish type title'
).to.contain.trimmed.text('Publish on site');
expect(find('[data-test-setting="publish-type"] [data-test-setting-title]')).to.not.match('button');
expect(find('[data-test-setting="email-recipients"]')).to.not.exist;
expect(find('[data-test-setting="publish-time"]'), 'publish time setting').to.exist;
expect(
find('[data-test-setting="publish-time"] [data-test-setting-title]'), 'publish time title'
).to.contain.trimmed.text('Right now');
expect(find('[data-test-setting="publish-time"] [data-test-setting-title]')).to.match('button');
await click('[data-test-button="continue"]');
expect(find('[data-test-publish-flow="confirm"]'), 'confirm step').to.exist;
expect(find('[data-test-text="confirm-details"]'), 'confirmation text')
.to.have.rendered.text('Your post will be published on your site.');
expect(find('[data-test-button="confirm-publish"]'), 'publish button text')
.to.have.rendered.text('Publish post, right now');
await click('[data-test-button="confirm-publish"]');
expect(post.status, 'post status after publish').to.equal('published');
expect(find('[data-test-publish-flow="complete"]'), 'complete step').to.exist;
expect(find('[data-test-complete-title]'), 'complete title').to.have.rendered.text('Boom. Its out there. Thats 1 post published, keep going!');
expect(find('[data-test-complete-bookmark]'), 'bookmark card').to.exist;
// "revert to draft" only shown for scheduled posts
expect(find('[data-test-button="revert-to-draft"]'), 'revert-to-draft button').to.not.exist;
// publish/preview buttons are hidden on complete step
expect(find('[data-test-button="publish-flow-preview"]'), 'preview button on complete step').to.not.exist;
expect(find('[data-test-button="publish-flow-publish"]'), 'publish button on complete step').to.not.exist;
await click('[data-test-button="back-to-editor"]');
expect(find('[data-test-button="publish-flow"]'), 'publish button after publishing').to.not.exist;
expect(find('[data-test-button="update-flow"]'), 'update button after publishing').to.exist;
await click('[data-test-button="update-flow"]');
expect(find('[data-test-modal="update-flow"]'), 'update flow modal').to.exist;
expect(find('[data-test-update-flow-title]')).to.have.rendered.text('This post has been published');
expect(find('[data-test-update-flow-confirmation]')).to.contain.rendered.text('Your post was published on your site');
const savedPublishAt = moment(post.publishedAt).utc();
expect(find('[data-test-update-flow-confirmation]')).to.contain.rendered.text(`on ${savedPublishAt.format('D MMM YYYY')} at ${savedPublishAt.format('HH:mm')}`);
expect(find('[data-test-button="revert-to-draft"]')).to.exist;
expect(find('[data-test-button="revert-to-draft"]')).to.contain.rendered.text('Unpublish and revert to private draft');
await click('[data-test-button="revert-to-draft"]');
expect(post.status).to.equal('draft');
expect(find('[data-test-modal="update-flow"]')).to.not.exist;
expect(find('[data-test-button="publish-flow"]')).to.exist;
}
it('can publish with members disabled', async function () {
await disableMembers(this.server);
await testEmailUnavailableFlow.apply(this);
});
it('can publish with newsletters disabled', async function () {
await enableMembers(this.server);
await disableNewsletters(this.server);
await testEmailUnavailableFlow.apply(this);
});
describe('members enabled', function () {
beforeEach(async function () {
enableMembers(this.server);
enableMailgun(this.server);
enableNewsletters(this.server);
// at least one member is required for publish+send to be available
this.server.create('member');
await loginAsRole('Administrator', this.server);
});
it('can publish+send with single newsletter');
it('can publish+send with multiple newsletters');
it('can schedule publish+send');
it('can send');
it('can schedule send');
it('can publish');
it('can schedule publish');
it('handles Mailgun not being set up', async function () {
disableMailgun(this.server);
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
await click('[data-test-button="publish-flow"]');
expect(
find('[data-test-setting="publish-type"] [data-test-setting-title]'), 'publish type title'
).to.have.trimmed.text('Publish');
await click('[data-test-setting="publish-type"] [data-test-setting-title]');
// mailgun not set up notice is shown
expect(find('[data-test-publish-type-error]'), 'publish type error').to.exist;
expect(find('[data-test-publish-type-error="no-mailgun"]'), 'publish type error text').to.exist;
// email-related options are disabled
expect(find('[data-test-publish-type="publish+send"]')).to.have.attribute('disabled');
expect(find('[data-test-publish-type="send"]')).to.have.attribute('disabled');
});
it('handles no members present', async function () {
this.server.db.members.remove();
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
await click('[data-test-button="publish-flow"]');
expect(
find('[data-test-setting="publish-type"] [data-test-setting-title]'), 'publish type title'
).to.have.trimmed.text('Publish');
await click('[data-test-setting="publish-type"] [data-test-setting-title]');
// no-members notice is shown
expect(find('[data-test-publish-type-error]'), 'publish type error').to.exist;
expect(find('[data-test-publish-type-error="no-members"]'), 'publish type error text').to.exist;
// email-related options are disabled
expect(find('[data-test-publish-type="publish+send"]')).to.have.attribute('disabled');
expect(find('[data-test-publish-type="send"]')).to.have.attribute('disabled');
});
it('handles member limits');
it('handles server error when confirming');
it('handles email sending error');
});
});

View file

@ -0,0 +1,10 @@
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
export default async function loginRole(roleName, server) {
const role = server.create('role', {name: roleName});
const user = server.create('user', {roles: [role], slug: 'test-user'});
await invalidateSession();
await authenticateSession();
return user;
}

View file

@ -1,13 +1,17 @@
export function enableMailgun(server) {
export function enableMailgun(server, enabled = true) {
server.db.settings.find({key: 'mailgun_api_key'})
? server.db.settings.update({key: 'mailgun_api_key'}, {value: 'MAILGUN_API_KEY'})
: server.create('setting', {key: 'mailgun_api_key', value: 'MAILGUN_API_KEY', group: 'email'});
? server.db.settings.update({key: 'mailgun_api_key'}, {value: (enabled ? 'MAILGUN_API_KEY' : null)})
: server.create('setting', {key: 'mailgun_api_key', value: (enabled ? 'MAILGUN_API_KEY' : null), group: 'email'});
server.db.settings.find({key: 'mailgun_domain'})
? server.db.settings.update({key: 'mailgun_domain'}, {value: 'MAILGUN_DOMAIN'})
: server.create('setting', {key: 'mailgun_domain', value: 'MAILGUN_DOMAIN', group: 'email'});
? server.db.settings.update({key: 'mailgun_domain'}, {value: (enabled ? 'MAILGUN_DOMAIN' : null)})
: server.create('setting', {key: 'mailgun_domain', value: (enabled ? 'MAILGUN_DOMAIN' : null), group: 'email'});
server.db.settings.find({key: 'mailgun_base_url'})
? server.db.settings.update({key: 'mailgun_base_url'}, {value: 'MAILGUN_BASE_URL'})
: server.create('setting', {key: 'mailgun_base_url', value: 'MAILGUN_BASE_URL', group: 'email'});
? server.db.settings.update({key: 'mailgun_base_url'}, {value: (enabled ? 'MAILGUN_BASE_URL' : null)})
: server.create('setting', {key: 'mailgun_base_url', value: (enabled ? 'MAILGUN_BASE_URL' : null), group: 'email'});
}
export function disableMailgun(server) {
enableMailgun(server, false);
}

View file

@ -0,0 +1,11 @@
export function enableMembers(server) {
server.db.settings.find({key: 'members_signup_access'})
? server.db.settings.update({key: 'members_signup_access'}, {value: 'all'})
: server.create('setting', {key: 'members_signup_access', value: 'all', group: 'members'});
}
export function disableMembers(server) {
server.db.settings.find({key: 'members_signup_access'})
? server.db.settings.update({key: 'members_signup_access'}, {value: 'none'})
: server.create('setting', {key: 'members_signup_access', value: 'none', group: 'members'});
}

View file

@ -1,5 +1,9 @@
export function enableNewsletters(server, enabled) {
export function enableNewsletters(server, enabled = true) {
server.db.settings.find({key: 'editor_default_email_recipients'})
? server.db.settings.update({key: 'editor_default_email_recipients'}, {value: enabled ? 'visibility' : 'disabled'})
: server.create('setting', {key: 'editor_default_email_recipients', value: enabled ? 'visibility' : 'disabled', group: 'editor'});
? server.db.settings.update({key: 'editor_default_email_recipients'}, {value: (enabled ? 'visibility' : 'disabled')})
: server.create('setting', {key: 'editor_default_email_recipients', value: (enabled ? 'visibility' : 'disabled'), group: 'editor'});
}
export function disableNewsletters(server) {
enableNewsletters(server, false);
}