Increased publish flow acceptance tests

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

- updated `?newsletter_id=id` handling in posts API mock to match the real `?newsletter=slug` behaviour
- added additional publish flow acceptance tests for single/multiple newsletter behaviour, scheduling, and sending
This commit is contained in:
Kevin Ansfield 2022-05-31 22:59:12 +01:00
parent 4d89ddf5bf
commit ed235b374d
9 changed files with 236 additions and 34 deletions

View File

@ -35,7 +35,7 @@
</div> </div>
{{#if post.emailOnly}} {{#if post.emailOnly}}
<div class="gh-publish-confirmation" data-test-complete-> <div class="gh-publish-confirmation" data-test-complete-details>
<p> <p>
Your post Your post
{{if post.isScheduled "will be" "was"}} {{if post.isScheduled "will be" "was"}}

View File

@ -23,7 +23,7 @@ export default class PublishFlowOptions extends Component {
buttonTextMap = { buttonTextMap = {
'publish+send': { 'publish+send': {
idle: 'Publish & Send', idle: 'Publish & send',
running: 'Publishing & sending', running: 'Publishing & sending',
success: 'Published & sent' success: 'Published & sent'
}, },

View File

@ -109,7 +109,7 @@
</div> </div>
{{/if}} {{/if}}
<div class="gh-publish-setting last" data-test-setting="publish-time"> <div class="gh-publish-setting last" data-test-setting="publish-at">
<button type="button" class="gh-publish-setting-title" {{on "click" (fn this.toggleSection "publishAt")}} data-test-setting-title> <button type="button" class="gh-publish-setting-title" {{on "click" (fn this.toggleSection "publishAt")}} data-test-setting-title>
{{svg-jar "clock"}} {{svg-jar "clock"}}
<div class="gh-publish-setting-trigger"> <div class="gh-publish-setting-trigger">

View File

@ -45,7 +45,7 @@ export default class PublishManagement extends Component {
const isValid = await this._validatePost(); const isValid = await this._validatePost();
if (isValid && !this.publishFlowModal || this.publishFlowModal.isClosing) { if (isValid && !this.publishFlowModal || this.publishFlowModal?.isClosing) {
this.publishOptions.resetPastScheduledAt(); this.publishOptions.resetPastScheduledAt();
this.publishFlowModal = this.modals.open(PublishFlowModal, { this.publishFlowModal = this.modals.open(PublishFlowModal, {

View File

@ -8,7 +8,7 @@
/> />
{{#if (gt @publishOptions.newsletters.length 1)}} {{#if (gt @publishOptions.newsletters.length 1)}}
<div class="mt4"> <div class="mt4" data-test-select="newsletter">
<label class="gh-main-section-header small bn">Newsletter</label> <label class="gh-main-section-header small bn">Newsletter</label>
<PowerSelect <PowerSelect
@selected={{@publishOptions.newsletter}} @selected={{@publishOptions.newsletter}}
@ -19,7 +19,7 @@
@dropdownClass="gh-publish-newsletter-dropdown" @dropdownClass="gh-publish-newsletter-dropdown"
as |newsletter| as |newsletter|
> >
<span>{{newsletter.name}}</span> <span data-test-select-option={{newsletter.name}}>{{newsletter.name}}</span>
{{!-- TODO: remove conditional when author/editor can fetch member counts --}} {{!-- TODO: remove conditional when author/editor can fetch member counts --}}
{{#if @publishOptions.user.isAdmin}} {{#if @publishOptions.user.isAdmin}}
<span class="gh-newsletter-count">{{format-number newsletter.count.members}}</span> <span class="gh-newsletter-count">{{format-number newsletter.count.members}}</span>

View File

@ -2,11 +2,11 @@
<fieldset> <fieldset>
<div class="gh-publish-schedule"> <div class="gh-publish-schedule">
<div class="gh-radio {{unless @publishOptions.isScheduled "active"}}" {{on "click" (fn @publishOptions.toggleScheduled false)}}> <div class="gh-radio {{unless @publishOptions.isScheduled "active"}}" {{on "click" (fn @publishOptions.toggleScheduled false)}}>
<div class="gh-radio-button" data-test-publishmenu-published-option></div> <div class="gh-radio-button" data-test-radio="publish-now"></div>
<label>Set it live now</label> <label>Set it live now</label>
</div> </div>
<div class="gh-radio {{if @publishOptions.isScheduled "active"}}" {{on "click" (fn @publishOptions.toggleScheduled true)}}> <div class="gh-radio {{if @publishOptions.isScheduled "active"}}" {{on "click" (fn @publishOptions.toggleScheduled true)}}>
<div class="gh-radio-button" data-test-publishmenu-scheduled-option></div> <div class="gh-radio-button" data-test-radio="schedule"></div>
<label>Schedule for later</label> <label>Schedule for later</label>
</div> </div>
</div> </div>

View File

@ -71,7 +71,7 @@ export default function mockPosts(server) {
}); });
}); });
server.put('/posts/:id/', function ({newsletters, posts, users, tags}, {params}) { server.put('/posts/:id/', function ({newsletters, posts, users, tags}, {params, queryParams}) {
const attrs = this.normalizedRequestAttrs(); const attrs = this.normalizedRequestAttrs();
const post = posts.find(params.id); const post = posts.find(params.id);
@ -80,8 +80,8 @@ export default function mockPosts(server) {
attrs.updatedAt = moment.utc().toDate(); attrs.updatedAt = moment.utc().toDate();
if (params.newsletter_id) { if (queryParams.newsletter) {
const newsletter = newsletters.find(params.newsletter_id); const newsletter = newsletters.findBy({slug: queryParams.newsletter});
post.newsletter = newsletter; post.newsletter = newsletter;
post.save(); post.save();
} }

View File

@ -1,11 +1,12 @@
import loginAsRole from '../../helpers/login-as-role'; import loginAsRole from '../../helpers/login-as-role';
import moment from 'moment'; import moment from 'moment';
import {blur, click, fillIn, find} from '@ember/test-helpers'; import {blur, click, fillIn, find, findAll} from '@ember/test-helpers';
import {clickTrigger, selectChoose} from 'ember-power-select/test-support/helpers';
import {disableMailgun, enableMailgun} from '../../helpers/mailgun'; import {disableMailgun, enableMailgun} from '../../helpers/mailgun';
import {disableMembers, enableMembers} from '../../helpers/members'; import {disableMembers, enableMembers} from '../../helpers/members';
import {disableNewsletters, enableNewsletters} from '../../helpers/newsletters'; import {disableNewsletters, enableNewsletters} from '../../helpers/newsletters';
import {enableStripe} from '../../helpers/stripe';
import {expect} from 'chai'; import {expect} from 'chai';
import {selectChoose} from 'ember-power-select/test-support/helpers';
import {setupApplicationTest} from 'ember-mocha'; import {setupApplicationTest} from 'ember-mocha';
import {setupMirage} from 'ember-cli-mirage/test-support'; import {setupMirage} from 'ember-cli-mirage/test-support';
import {visit} from '../../helpers/visit'; import {visit} from '../../helpers/visit';
@ -81,11 +82,11 @@ describe('Acceptance: Publish flow', function () {
expect(find('[data-test-setting="email-recipients"]')).to.not.exist; 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-at"]'), 'publish time setting').to.exist;
expect( expect(
find('[data-test-setting="publish-time"] [data-test-setting-title]'), 'publish time title' find('[data-test-setting="publish-at"] [data-test-setting-title]'), 'publish time title'
).to.contain.trimmed.text('Right now'); ).to.contain.trimmed.text('Right now');
expect(find('[data-test-setting="publish-time"] [data-test-setting-title]')).to.match('button'); expect(find('[data-test-setting="publish-at"] [data-test-setting-title]')).to.match('button');
await click('[data-test-button="continue"]'); await click('[data-test-button="continue"]');
@ -151,20 +152,217 @@ describe('Acceptance: Publish flow', function () {
enableMembers(this.server); enableMembers(this.server);
enableMailgun(this.server); enableMailgun(this.server);
enableNewsletters(this.server); enableNewsletters(this.server);
enableStripe(this.server);
// at least one member is required for publish+send to be available // at least one member is required for publish+send to be available
this.server.create('member'); this.server.createList('member', 3, {status: 'free'});
this.server.createList('member', 4, {status: 'paid'});
await loginAsRole('Administrator', this.server); await loginAsRole('Administrator', this.server);
}); });
it('can publish+send with single newsletter'); it('can publish+send with single newsletter', async function () {
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
await click('[data-test-button="publish-flow"]');
it('can publish+send with multiple newsletters'); expect(
find('[data-test-setting="publish-type"] [data-test-setting-title]'), 'publish type title'
).to.have.trimmed.rendered.text('Publish and email');
// newsletter is not mentioned in the recipients title
expect(
find('[data-test-setting="email-recipients"] [data-test-setting-title]'), 'publish type title'
).to.have.trimmed.rendered.text('All 7 subscribers');
// newsletter select shouldn't exist
await click('[data-test-setting="email-recipients"] [data-test-setting-title]');
expect(find('[data-test-select="newsletter"]'), 'newsletter select').to.not.exist;
await click('[data-test-button="continue"]');
// confirm text is correct
expect(find('[data-test-text="confirm-details"]')).to.contain.rendered
.text('will be published on your site, and delivered to all 7 subscribers.');
expect(find('[data-test-button="confirm-publish"]')).to.have.rendered
.text('Publish & send, right now');
await click('[data-test-button="confirm-publish"]');
// complete text has right count
expect(find('[data-test-complete-title]')).to.contain.rendered
.text('Thats 1 post published');
});
it('can publish+send with multiple newsletters', async function () {
const newsletter = this.server.create('newsletter', {
name: 'Second newsletter',
slug: 'second-newsletter',
status: 'active',
subscribeOnSignup: true
});
this.server.create('newsletter', {
name: 'Archived newsletter',
slug: 'archived-newsletter',
status: 'archived',
subscribeOnSignup: true
});
this.server.create('member', {newsletters: [newsletter], status: 'free'});
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
await click('[data-test-button="publish-flow"]');
// newsletter is mentioned in the recipients title
expect(
find('[data-test-setting="email-recipients"] [data-test-setting-title]'), 'publish type title'
).to.have.trimmed.rendered.text('All 8 subscribers of Default newsletter');
// newsletter select should exist with all active newsletters listed
await click('[data-test-setting="email-recipients"] [data-test-setting-title]');
expect(find('[data-test-select="newsletter"]'), 'newsletter select').to.exist;
await clickTrigger('[data-test-select="newsletter"]');
expect(findAll('.ember-power-select-dropdown [data-test-select-option]').length).to.equal(2);
// selecting a different newsletter updates recipient count
await selectChoose('[data-test-select="newsletter"]', 'Second newsletter');
expect(
find('[data-test-setting="email-recipients"] [data-test-setting-title]'), 'publish type title'
).to.have.trimmed.rendered.text('1 subscriber of Second newsletter');
await click('[data-test-button="continue"]');
// confirm text is correct
expect(find('[data-test-text="confirm-details"]')).to.contain.rendered
.text('will be published on your site, and delivered to all 1 subscriber of Second newsletter.');
expect(find('[data-test-button="confirm-publish"]')).to.have.rendered
.text('Publish & send, right now');
await click('[data-test-button="confirm-publish"]');
// saved with correct newsletter id
expect(post.newsletterId).to.equal(newsletter.id);
});
it('can schedule publish+send', async function () {
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-at"] [data-test-setting-title]')).to.have.rendered
.text('Right now');
const siteTz = this.server.db.settings.findBy({key: 'timezone'}).value;
const plus5 = moment().tz(siteTz).add(5, 'minutes').set({});
await click('[data-test-setting="publish-at"] [data-test-setting-title]');
await click('[data-test-radio="schedule"]');
// date + time inputs are shown, defaults to now+5 mins
expect(find('[data-test-setting="publish-at"] [data-test-date-time-picker-datepicker]'), 'datepicker').to.exist;
expect(find('[data-test-setting="publish-at"] [data-test-date-time-picker-date-input]'), 'initial datepicker value')
.to.have.value(plus5.format('YYYY-MM-DD'));
expect(find('[data-test-setting="publish-at"] [data-test-date-time-picker-time-input]'), 'time input').to.exist;
expect(find('[data-test-setting="publish-at"] [data-test-date-time-picker-time-input]'), 'initial time input value')
.to.have.value(plus5.format('HH:mm'));
// can set a new date and time
const newDate = moment().tz(siteTz).add(4, 'days').add(5, 'hours').set('second', 0);
await fillIn('[data-test-setting="publish-at"] [data-test-date-time-picker-date-input]', newDate.format('YYYY-MM-DD'));
await blur('[data-test-setting="publish-at"] [data-test-date-time-picker-date-input]');
await fillIn('[data-test-setting="publish-at"] [data-test-date-time-picker-time-input]', newDate.format('HH:mm'));
await blur('[data-test-setting="publish-at"] [data-test-date-time-picker-time-input]');
expect(find('[data-test-setting="publish-at"] [data-test-date-time-picker-time-input]')).to.have.value(newDate.format('HH:mm'));
expect(find('[data-test-setting="publish-at"] [data-test-setting-title]'), 'publish-at title after change').to.have.rendered
.text('In 4 days');
await click('[data-test-button="continue"]');
// has correct confirm text
expect(find('[data-test-text="confirm-details"]')).to.have.rendered
.text(`On ${newDate.format('D MMM YYYY')} at ${newDate.format('HH:mm')} your post will be published on your site, and delivered to all 7 subscribers.`);
expect(find('[data-test-button="confirm-publish"]')).to.have.rendered
.text(`Publish & send, on ${newDate.format('MMMM Do')}`);
await click('[data-test-button="confirm-publish"]');
// saved with correct details
expect(post.status).to.equal('scheduled');
expect(moment.utc(post.publishedAt).format('YYYY-MM-DD HH:mm')).to.equal(moment(newDate).utc().format('YYYY-MM-DD HH:mm'));
expect(post.newsletterId).to.equal('1');
});
it('can send', async function () {
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
await click('[data-test-button="publish-flow"]');
await click('[data-test-setting="publish-type"] [data-test-setting-title]');
await click('[data-test-publish-type="send"]');
expect(find('[data-test-setting="publish-type"] [data-test-setting-title]')).to.have.rendered
.text('Email');
await click('[data-test-button="continue"]');
// has correct confirm text
expect(find('[data-test-text="confirm-details"]')).to.have.rendered
.text(`Your post will be delivered to all 7 subscribers, and will not be published on your site.`);
expect(find('[data-test-button="confirm-publish"]')).to.have.rendered
.text(`Send email, right now`);
await click('[data-test-button="confirm-publish"]');
expect(post.attrs.emailOnly).to.be.true;
});
it('can schedule send', async function () {
const post = this.server.create('post', {status: 'draft'});
await visit(`/editor/post/${post.id}`);
await click('[data-test-button="publish-flow"]');
await click('[data-test-setting="publish-type"] [data-test-setting-title]');
await click('[data-test-publish-type="send"]');
expect(find('[data-test-setting="publish-type"] [data-test-setting-title]')).to.have.rendered
.text('Email');
await click('[data-test-setting="publish-at"] [data-test-setting-title]');
await click('[data-test-setting="publish-at"] [data-test-radio="schedule"]');
const siteTz = this.server.db.settings.findBy({key: 'timezone'}).value;
const newDate = moment().tz(siteTz).add(4, 'days').add(5, 'hours').set('second', 0);
await fillIn('[data-test-setting="publish-at"] [data-test-date-time-picker-date-input]', newDate.format('YYYY-MM-DD'));
await blur('[data-test-setting="publish-at"] [data-test-date-time-picker-date-input]');
await fillIn('[data-test-setting="publish-at"] [data-test-date-time-picker-time-input]', newDate.format('HH:mm'));
await blur('[data-test-setting="publish-at"] [data-test-date-time-picker-time-input]');
expect(find('[data-test-setting="publish-at"] [data-test-date-time-picker-time-input]')).to.have.value(newDate.format('HH:mm'));
expect(find('[data-test-setting="publish-at"] [data-test-setting-title]'), 'publish-at title after change').to.have.rendered
.text('In 4 days');
await click('[data-test-button="continue"]');
// has correct confirm text
expect(find('[data-test-text="confirm-details"]')).to.have.rendered
.text(`On ${newDate.format('D MMM YYYY')} at ${newDate.format('HH:mm')} your post will be delivered to all 7 subscribers, and will not be published on your site.`);
expect(find('[data-test-button="confirm-publish"]')).to.have.rendered
.text(`Send email, on ${newDate.format('MMMM Do')}`);
await click('[data-test-button="confirm-publish"]');
expect(post.attrs.emailOnly).to.be.true;
});
it('can schedule publish+send');
it('can send');
it('can schedule send');
it('can publish'); it('can publish');
it('can schedule publish'); it('can schedule publish');
@ -185,7 +383,7 @@ describe('Acceptance: Publish flow', function () {
).to.have.trimmed.text('Publish'); ).to.have.trimmed.text('Publish');
expect( expect(
find('[data-test-setting="email-recipients"] [data-test-setting-title]'), 'publish type title' find('[data-test-setting="email-recipients"] [data-test-setting-title]'), 'recipients title'
).to.have.trimmed.text('Not sent as newsletter'); ).to.have.trimmed.text('Not sent as newsletter');
await click('[data-test-setting="publish-type"] [data-test-setting-title]'); await click('[data-test-setting="publish-type"] [data-test-setting-title]');
@ -197,8 +395,8 @@ describe('Acceptance: Publish flow', function () {
await click('[data-test-publish-type="publish+send"]'); await click('[data-test-publish-type="publish+send"]');
expect( expect(
find('[data-test-setting="email-recipients"] [data-test-setting-title]'), 'publish type title' find('[data-test-setting="email-recipients"] [data-test-setting-title]'), 'recipients title'
).to.have.trimmed.rendered.text('1 subscriber'); ).to.have.trimmed.rendered.text('All 7 subscribers');
}); });
it('handles Mailgun not being set up', async function () { it('handles Mailgun not being set up', async function () {

View File

@ -1,16 +1,20 @@
export function enableStripe(server) { export function enableStripe(server, enabled = true) {
server.db.settings.find({key: 'stripe_connect_account_id'}) server.db.settings.find({key: 'stripe_connect_account_id'})
? server.db.settings.update({key: 'stripe_connect_account_id'}, {value: 'stripe_account_id'}) ? server.db.settings.update({key: 'stripe_connect_account_id'}, {value: (enabled ? 'stripe_account_id' : null)})
: server.create('setting', {key: 'stripe_connect_account_id', value: 'stripe_account_id', group: 'members'}); : server.create('setting', {key: 'stripe_connect_account_id', value: (enabled ? 'stripe_account_id' : null), group: 'members'});
// needed for membersUtils.isStripeEnabled // needed for membersUtils.isStripeEnabled
server.db.settings.find({key: 'stripe_connect_secret_key'}) server.db.settings.find({key: 'stripe_connect_secret_key'})
? server.db.settings.update({key: 'stripe_connect_secret_key'}, {value: 'stripe_secret_key'}) ? server.db.settings.update({key: 'stripe_connect_secret_key'}, {value: (enabled ? 'stripe_secret_key' : null)})
: server.create('setting', {key: 'stripe_connect_secret_key', value: 'stripe_secret_key', group: 'members'}); : server.create('setting', {key: 'stripe_connect_secret_key', value: (enabled ? 'stripe_secret_key' : null), group: 'members'});
server.db.settings.find({key: 'stripe_connect_publishable_key'}) server.db.settings.find({key: 'stripe_connect_publishable_key'})
? server.db.settings.update({key: 'stripe_connect_publishable_key'}, {value: 'stripe_secret_key'}) ? server.db.settings.update({key: 'stripe_connect_publishable_key'}, {value: (enabled ? 'stripe_secret_key' : null)})
: server.create('setting', {key: 'stripe_connect_publishable_key', value: 'stripe_secret_key', group: 'members'}); : server.create('setting', {key: 'stripe_connect_publishable_key', value: (enabled ? 'stripe_secret_key' : null), group: 'members'});
server.db.settings.find({key: 'paid_members_enabled'}) server.db.settings.find({key: 'paid_members_enabled'})
? server.db.settings.update({key: 'paid_members_enabled'}, {value: true}) ? server.db.settings.update({key: 'paid_members_enabled'}, {value: enabled})
: server.create('setting', {key: 'paid_members_enabled', value: true, group: 'members'}); : server.create('setting', {key: 'paid_members_enabled', value: enabled, group: 'members'});
}
export function disableStripe(server) {
enableStripe(server, false);
} }