f64820b1be
fixes https://linear.app/tryghost/issue/ONC-206 ref https://app.incident.io/ghost/incidents/90 - when multiple member filters are used in combination, NQL sometimes hit a limitation that results in the wrong members being returned - while we work on the NQL limitation, we are temporarily disabling bulk member deletion when more than one member filter has been applied
309 lines
13 KiB
JavaScript
309 lines
13 KiB
JavaScript
import moment from 'moment-timezone';
|
|
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 {setupMirage} from 'ember-cli-mirage/test-support';
|
|
import {visit} from '../helpers/visit';
|
|
|
|
describe('Acceptance: Members', function () {
|
|
let hooks = setupApplicationTest();
|
|
setupMirage(hooks);
|
|
|
|
it('redirects to signin when not authenticated', async function () {
|
|
await invalidateSession();
|
|
await visit('/members');
|
|
|
|
expect(currentURL()).to.equal('/signin');
|
|
});
|
|
|
|
it('redirects non-admins to site', async function () {
|
|
let role = this.server.create('role', {name: 'Editor'});
|
|
this.server.create('user', {roles: [role]});
|
|
|
|
await authenticateSession();
|
|
await visit('/members');
|
|
|
|
expect(currentURL()).to.equal('/site');
|
|
expect(find('[data-test-nav="members"]'), 'sidebar link')
|
|
.to.not.exist;
|
|
});
|
|
|
|
describe('as owner', function () {
|
|
beforeEach(async function () {
|
|
this.server.loadFixtures('configs');
|
|
|
|
let role = this.server.create('role', {name: 'Owner'});
|
|
this.server.create('user', {roles: [role]});
|
|
|
|
return await authenticateSession();
|
|
});
|
|
|
|
it('it renders, can be navigated, can edit member', async function () {
|
|
let member1 = this.server.create('member', {createdAt: moment.utc().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss')});
|
|
this.server.create('member', {createdAt: moment.utc().subtract(2, 'day').format('YYYY-MM-DD HH:mm:ss')});
|
|
|
|
await visit('/members');
|
|
|
|
// lands on correct page
|
|
expect(currentURL(), 'currentURL').to.equal('/members');
|
|
|
|
// it lists all members
|
|
expect(findAll('[data-test-list="members-list-item"]').length, 'members list count')
|
|
.to.equal(2);
|
|
|
|
// it highlights active state in nav menu
|
|
expect(
|
|
find('[data-test-nav="members"]'),
|
|
'highlights nav menu item'
|
|
).to.have.class('active');
|
|
|
|
let member = find('[data-test-list="members-list-item"]');
|
|
expect(member.querySelector('.gh-members-list-name').textContent, 'member list item title')
|
|
.to.equal(member1.name);
|
|
|
|
// it does not add ?include=email_recipients
|
|
const membersRequests = this.server.pretender.handledRequests.filter(r => r.url.match(/\/members\/(\?|$)/));
|
|
expect(membersRequests[0].url).to.not.have.string('email_recipients');
|
|
|
|
await visit(`/members/${member1.id}`);
|
|
|
|
// it shows selected member form
|
|
expect(find('[data-test-input="member-name"]').value, 'loads correct member into form')
|
|
.to.equal(member1.name);
|
|
|
|
expect(find('[data-test-input="member-email"]').value, 'loads correct email into form')
|
|
.to.equal(member1.email);
|
|
|
|
// it maintains active state in nav menu
|
|
expect(
|
|
find('[data-test-nav="members"]'),
|
|
'highlights nav menu item'
|
|
).to.have.class('active');
|
|
|
|
// trigger save
|
|
await fillIn('[data-test-input="member-name"]', 'New Name');
|
|
await blur('[data-test-input="member-name"]');
|
|
|
|
await click('[data-test-button="save"]');
|
|
|
|
await click('[data-test-link="members-back"]');
|
|
|
|
// lands on correct page
|
|
expect(currentURL(), 'currentURL').to.equal('/members');
|
|
});
|
|
|
|
it('can create a new member', async function () {
|
|
this.server.create('member', {createdAt: moment.utc().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss')});
|
|
|
|
await visit('/members');
|
|
|
|
// lands on correct page
|
|
expect(currentURL(), 'currentURL').to.equal('/members');
|
|
|
|
// it lists all members
|
|
expect(findAll('[data-test-list="members-list-item"]').length, 'members list count')
|
|
.to.equal(1);
|
|
|
|
// start new member
|
|
await click('[data-test-new-member-button="true"]');
|
|
|
|
// it navigates to the new member route
|
|
expect(currentURL(), 'new member URL').to.equal('/members/new');
|
|
// it displays the new member form
|
|
expect(find('.gh-canvas-header h2').textContent, 'settings pane title')
|
|
.to.contain('New');
|
|
|
|
// it highlights active state in nav menu
|
|
expect(
|
|
find('[data-test-nav="members"]'),
|
|
'highlights nav menu item'
|
|
).to.have.class('active');
|
|
|
|
// all fields start blank
|
|
findAll('.gh-member-settings-primary .gh-input').forEach(function (elem) {
|
|
expect(elem.value, `input field for ${elem.getAttribute('name')}`)
|
|
.to.be.empty;
|
|
});
|
|
|
|
// save new member
|
|
await fillIn('[data-test-input="member-name"]', 'New Name');
|
|
await blur('[data-test-input="member-name"]');
|
|
|
|
await fillIn('[data-test-input="member-email"]', 'example@domain.com');
|
|
await blur('[data-test-input="member-email"]');
|
|
|
|
await click('[data-test-button="save"]');
|
|
|
|
expect(find('[data-test-input="member-name"]').value, 'name has been preserved')
|
|
.to.equal('New Name');
|
|
|
|
expect(find('[data-test-input="member-email"]').value, 'email has been preserved')
|
|
.to.equal('example@domain.com');
|
|
});
|
|
|
|
/* NOTE: Bulk deletion is disabled temporarily when multiple filters are applied, due to a NQL limitation.
|
|
* Delete this test once we have fixed the root NQL limitation.
|
|
* See https://linear.app/tryghost/issue/ONC-203
|
|
*/
|
|
it('cannot bulk delete members if more than 1 filter is selected', async function () {
|
|
// Members with label
|
|
const labelOne = this.server.create('label');
|
|
const labelTwo = this.server.create('label');
|
|
this.server.createList('member', 2, {labels: [labelOne]});
|
|
this.server.createList('member', 2, {labels: [labelOne, labelTwo]});
|
|
|
|
await visit('/members');
|
|
expect(findAll('[data-test-member]').length).to.equal(4);
|
|
|
|
// The delete button should not be visible by default
|
|
await click('[data-test-button="members-actions"]');
|
|
expect(find('[data-test-button="delete-selected"]')).to.not.exist;
|
|
|
|
// Apply a single filter
|
|
await click('[data-test-button="members-filter-actions"]');
|
|
await fillIn('[data-test-members-filter="0"] [data-test-select="members-filter"]', 'label');
|
|
await click('.gh-member-label-input input');
|
|
await click(`[data-test-label-filter="${labelOne.name}"]`);
|
|
await click(`[data-test-button="members-apply-filter"]`);
|
|
|
|
expect(findAll('[data-test-member]').length).to.equal(4);
|
|
expect(currentURL()).to.equal(`/members?filter=label%3A%5B${labelOne.slug}%5D`);
|
|
|
|
await click('[data-test-button="members-actions"]');
|
|
expect(find('[data-test-button="delete-selected"]')).to.exist;
|
|
|
|
// Apply a second filter
|
|
await click('[data-test-button="members-filter-actions"]');
|
|
await click('[data-test-button="add-members-filter"]');
|
|
|
|
await fillIn('[data-test-members-filter="1"] [data-test-select="members-filter"]', 'label');
|
|
await click('[data-test-members-filter="1"] .gh-member-label-input input');
|
|
await click(`[data-test-members-filter="1"] [data-test-label-filter="${labelTwo.name}"]`);
|
|
await click(`[data-test-button="members-apply-filter"]`);
|
|
|
|
expect(findAll('[data-test-member]').length).to.equal(2);
|
|
expect(currentURL()).to.equal(`/members?filter=label%3A%5B${labelOne.slug}%5D%2Blabel%3A%5B${labelTwo.slug}%5D`);
|
|
|
|
await click('[data-test-button="members-actions"]');
|
|
expect(find('[data-test-button="delete-selected"]')).to.not.exist;
|
|
});
|
|
|
|
it('can bulk delete members', async function () {
|
|
// members to be kept
|
|
this.server.createList('member', 6);
|
|
|
|
// imported members to be deleted
|
|
const label = this.server.create('label');
|
|
this.server.createList('member', 5, {labels: [label]});
|
|
|
|
await visit('/members');
|
|
|
|
expect(findAll('[data-test-member]').length).to.equal(11);
|
|
|
|
await click('[data-test-button="members-actions"]');
|
|
|
|
expect(find('[data-test-button="delete-selected"]')).to.not.exist;
|
|
|
|
// a filter is needed for the delete-selected button to show
|
|
await click('[data-test-button="members-filter-actions"]');
|
|
await fillIn('[data-test-members-filter="0"] [data-test-select="members-filter"]', 'label');
|
|
await click('.gh-member-label-input input');
|
|
await click(`[data-test-label-filter="${label.name}"]`);
|
|
await click(`[data-test-button="members-apply-filter"]`);
|
|
|
|
expect(findAll('[data-test-member]').length).to.equal(5);
|
|
expect(currentURL()).to.equal(`/members?filter=label%3A%5B${label.slug}%5D`);
|
|
|
|
await click('[data-test-button="members-actions"]');
|
|
|
|
expect(find('[data-test-button="delete-selected"]')).to.exist;
|
|
|
|
await click('[data-test-button="delete-selected"]');
|
|
|
|
expect(find('[data-test-modal="delete-members"]')).to.exist;
|
|
expect(find('[data-test-text="delete-count"]')).to.have.text('5 members');
|
|
|
|
// ensure export endpoint gets hit with correct query params when deleting
|
|
let exportQueryParams;
|
|
this.server.get('/members/upload', (schema, request) => {
|
|
exportQueryParams = request.queryParams;
|
|
});
|
|
|
|
await click('[data-test-button="confirm"]');
|
|
|
|
expect(exportQueryParams).to.deep.equal({filter: 'label:[label-0]', limit: 'all'});
|
|
|
|
expect(find('[data-test-text="deleted-count"]')).to.have.text('5 members');
|
|
expect(find('[data-test-button="confirm"]')).to.not.exist;
|
|
|
|
// members filter is reset
|
|
expect(currentURL()).to.equal('/members');
|
|
expect(findAll('[data-test-member]').length).to.equal(6);
|
|
|
|
await click('[data-test-button="close-modal"]');
|
|
|
|
expect(find('[data-test-modal="delete-members"]')).to.not.exist;
|
|
});
|
|
|
|
it('can delete a member (via list)', async function () {
|
|
const newsletter = this.server.create('newsletter');
|
|
const label = this.server.create('label');
|
|
this.server.createList('member', 2, {newsletters: [newsletter], labels: [label]});
|
|
|
|
await visit('/members');
|
|
|
|
expect(findAll('[data-test-member]').length).to.equal(2);
|
|
|
|
await click('[data-test-member] a');
|
|
|
|
expect(currentURL()).to.match(/members\/\d+/);
|
|
|
|
await click('[data-test-button="member-actions"]');
|
|
await click('[data-test-button="delete-member"]');
|
|
|
|
expect(find('[data-test-modal="delete-member"]')).to.exist;
|
|
|
|
await click('[data-test-modal="delete-member"] [data-test-button="cancel"]');
|
|
|
|
expect(currentURL()).to.match(/members\/\d+/);
|
|
expect(find('[data-test-modal="delete-member"]')).to.not.exist;
|
|
|
|
await click('[data-test-button="member-actions"]');
|
|
await click('[data-test-button="delete-member"]');
|
|
await click('[data-test-modal="delete-member"] [data-test-button="confirm"]');
|
|
|
|
expect(currentURL()).to.equal('/members');
|
|
expect(findAll('[data-test-modal]')).to.have.length(0);
|
|
expect(findAll('[data-test-member]')).to.have.length(1);
|
|
});
|
|
|
|
it('can delete a member (via url)', async function () {
|
|
const newsletter = this.server.create('newsletter');
|
|
const label = this.server.create('label');
|
|
const [memberOne] = this.server.createList('member', 2, {newsletters: [newsletter], labels: [label]});
|
|
|
|
await visit(`/members/${memberOne.id}`);
|
|
|
|
await click('[data-test-button="member-actions"]');
|
|
await click('[data-test-button="delete-member"]');
|
|
|
|
expect(find('[data-test-modal="delete-member"]')).to.exist;
|
|
|
|
await click('[data-test-modal="delete-member"] [data-test-button="cancel"]');
|
|
|
|
expect(currentURL()).to.match(/members\/\d+/);
|
|
expect(find('[data-test-modal="delete-member"]')).to.not.exist;
|
|
|
|
await click('[data-test-button="member-actions"]');
|
|
await click('[data-test-button="delete-member"]');
|
|
await click('[data-test-modal="delete-member"] [data-test-button="confirm"]');
|
|
|
|
expect(currentURL()).to.equal('/members');
|
|
expect(findAll('[data-test-modal]')).to.have.length(0);
|
|
expect(findAll('[data-test-member]')).to.have.length(1);
|
|
});
|
|
});
|
|
});
|