3d9d552271
ref https://linear.app/tryghost/issue/ONC-111 - changed posts fetching/display behavior to be client-side instead of server-side - admin will issue (potentially multiple) requests based on the desired status(es) - updated admin acceptance test for missing coverage I've pulled the sort from the database query as this triple sort performs very poorly at scale (taking ~4s+ past ~20k posts sometimes). Instead, we now split up the fetch to grab only one status at a time and use the front-end logic to handle displaying scheduled, then drafts, then published. This should result in a much more responsive view. We will separately change the default sort on the Admin API as that was the ultimate intent for this change.
275 lines
14 KiB
JavaScript
275 lines
14 KiB
JavaScript
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
|
import {beforeEach, describe, it} from 'mocha';
|
|
import {blur, click, currentURL, fillIn, find, findAll, visit} from '@ember/test-helpers';
|
|
import {clickTrigger, selectChoose} from 'ember-power-select/test-support/helpers';
|
|
import {expect} from 'chai';
|
|
import {setupApplicationTest} from 'ember-mocha';
|
|
import {setupMirage} from 'ember-cli-mirage/test-support';
|
|
|
|
describe('Acceptance: Content', function () {
|
|
let hooks = setupApplicationTest();
|
|
setupMirage(hooks);
|
|
|
|
beforeEach(async function () {
|
|
this.server.loadFixtures('configs');
|
|
});
|
|
|
|
it('redirects to signin when not authenticated', async function () {
|
|
await invalidateSession();
|
|
await visit('/posts');
|
|
|
|
expect(currentURL()).to.equal('/signin');
|
|
});
|
|
|
|
describe('as admin', function () {
|
|
let admin, editor, publishedPost, scheduledPost, draftPost, authorPost;
|
|
|
|
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 = 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'});
|
|
authorPost = this.server.create('post', {authors: [editor], status: 'published', title: 'Editor Published Post'});
|
|
|
|
// pages shouldn't appear in the list
|
|
this.server.create('page', {authors: [admin], status: 'published', title: 'Published Page'});
|
|
|
|
return await authenticateSession();
|
|
});
|
|
|
|
it('displays and filters posts', async function () {
|
|
await visit('/posts');
|
|
// Not checking request here as it won't be the last request made
|
|
// Displays all posts + pages
|
|
expect(findAll('[data-test-post-id]').length, 'all posts count').to.equal(4);
|
|
|
|
// show draft posts
|
|
await selectChoose('[data-test-type-select]', 'Draft posts');
|
|
|
|
// API request is correct
|
|
let [lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
|
expect(lastRequest.queryParams.filter, '"drafts" request status filter').to.have.string('status:draft');
|
|
// Displays draft post
|
|
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] = this.server.pretender.handledRequests.slice(-1);
|
|
expect(lastRequest.queryParams.filter, '"published" request status filter').to.have.string('status:published');
|
|
// Displays three published posts + pages
|
|
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;
|
|
|
|
// show scheduled posts
|
|
await selectChoose('[data-test-type-select]', 'Scheduled posts');
|
|
|
|
// API request is correct
|
|
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
|
expect(lastRequest.queryParams.filter, '"scheduled" request status filter').to.have.string('status:scheduled');
|
|
// Displays scheduled post
|
|
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 all posts
|
|
await selectChoose('[data-test-type-select]', 'All posts');
|
|
|
|
// Posts are ordered scheduled -> draft -> published/sent
|
|
// check API request is correct - we submit one request for scheduled, one for drafts, and one for published+sent
|
|
[lastRequest] = this.server.pretender.handledRequests.slice(-3);
|
|
expect(lastRequest.queryParams.filter, '"all" request status filter').to.have.string('status:scheduled');
|
|
[lastRequest] = this.server.pretender.handledRequests.slice(-2);
|
|
expect(lastRequest.queryParams.filter, '"all" request status filter').to.have.string('status:draft');
|
|
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
|
expect(lastRequest.queryParams.filter, '"all" request status filter').to.have.string('status:[published,sent]');
|
|
|
|
// check order display is correct
|
|
let postIds = findAll('[data-test-post-id]').map(el => el.getAttribute('data-test-post-id'));
|
|
expect(postIds, 'post order').to.deep.equal([scheduledPost.id, draftPost.id, publishedPost.id, authorPost.id]);
|
|
|
|
// show all posts by editor
|
|
await selectChoose('[data-test-type-select]', 'Published posts');
|
|
await selectChoose('[data-test-author-select]', editor.name);
|
|
|
|
// API request is correct
|
|
[lastRequest] = this.server.pretender.handledRequests.slice(-1);
|
|
expect(lastRequest.queryParams.filter, '"editor" request status filter')
|
|
.to.have.string('status:published');
|
|
expect(lastRequest.queryParams.filter, '"editor" request filter param')
|
|
.to.have.string(`authors:${editor.slug}`);
|
|
});
|
|
|
|
// TODO: skipped due to consistently random failures on Travis
|
|
// options[0] is undefined
|
|
// https://github.com/TryGhost/Ghost/issues/10308
|
|
it.skip('sorts tags filter alphabetically', async function () {
|
|
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('/posts');
|
|
await clickTrigger('[data-test-tag-select]');
|
|
|
|
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');
|
|
expect(options[2].textContent.trim()).to.equal('B - Second');
|
|
expect(options[3].textContent.trim()).to.equal('Z - Last');
|
|
});
|
|
|
|
it('can add and edit custom views', async function () {
|
|
// actions are not visible when there's no filter
|
|
await visit('/posts');
|
|
expect(find('[data-test-button="edit-view"]'), 'edit-view button (no filter)').to.not.exist;
|
|
expect(find('[data-test-button="add-view"]'), 'add-view button (no filter)').to.not.exist;
|
|
|
|
// add action is visible after filtering to a non-default filter
|
|
await selectChoose('[data-test-author-select]', admin.name);
|
|
expect(find('[data-test-button="add-view"]'), 'add-view button (with filter)').to.exist;
|
|
|
|
// adding view shows it in the sidebar
|
|
await click('[data-test-button="add-view"]'), 'add-view button';
|
|
expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (on add)').to.exist;
|
|
expect(find('[data-test-modal="custom-view-form"] h1').textContent.trim()).to.equal('New view');
|
|
await fillIn('[data-test-input="custom-view-name"]', 'Test view');
|
|
await click('[data-test-button="save-custom-view"]');
|
|
// modal closes on save
|
|
expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (after add save)').to.not.exist;
|
|
// UI updates
|
|
expect(find('[data-test-nav-custom="posts-Test view"]'), 'new view nav').to.exist;
|
|
expect(find('[data-test-nav-custom="posts-Test view"]').textContent.trim()).to.equal('Test view');
|
|
expect(find('[data-test-button="add-view"]'), 'add-view button (on existing view)').to.not.exist;
|
|
expect(find('[data-test-button="edit-view"]'), 'edit-view button (on existing view)').to.exist;
|
|
|
|
// editing view
|
|
await click('[data-test-button="edit-view"]'), 'edit-view button';
|
|
expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (on edit)').to.exist;
|
|
expect(find('[data-test-modal="custom-view-form"] h1').textContent.trim()).to.equal('Edit view');
|
|
await fillIn('[data-test-input="custom-view-name"]', 'Updated view');
|
|
await click('[data-test-button="save-custom-view"]');
|
|
// modal closes on save
|
|
expect(find('[data-test-modal="custom-view-form"]'), 'custom view modal (after edit save)').to.not.exist;
|
|
// UI updates
|
|
expect(find('[data-test-nav-custom="posts-Updated view"]')).to.exist;
|
|
expect(find('[data-test-nav-custom="posts-Updated view"]').textContent.trim()).to.equal('Updated view');
|
|
expect(find('[data-test-button="add-view"]'), 'add-view button (after edit)').to.not.exist;
|
|
expect(find('[data-test-button="edit-view"]'), 'edit-view button (after edit)').to.exist;
|
|
});
|
|
|
|
it('can navigate to custom views', async function () {
|
|
this.server.create('setting', {
|
|
group: 'site',
|
|
key: 'shared_views',
|
|
value: JSON.stringify([{
|
|
route: 'posts',
|
|
name: 'My posts',
|
|
filter: {
|
|
author: admin.slug
|
|
}
|
|
}])
|
|
});
|
|
|
|
await visit('/posts');
|
|
|
|
// nav bar contains default + custom views
|
|
expect(find('[data-test-nav-custom="posts-Drafts"]')).to.exist;
|
|
expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.exist;
|
|
expect(find('[data-test-nav-custom="posts-Published"]')).to.exist;
|
|
expect(find('[data-test-nav-custom="posts-My posts"]')).to.exist;
|
|
|
|
// screen has default title and sidebar is showing inactive custom view
|
|
expect(find('[data-test-screen-title]')).to.have.rendered.text('Posts');
|
|
expect(find('[data-test-nav="posts"]')).to.have.class('active');
|
|
|
|
// clicking sidebar custom view link works
|
|
await click('[data-test-nav-custom="posts-Scheduled"]');
|
|
expect(currentURL()).to.equal('/posts?type=scheduled');
|
|
expect(find('[data-test-screen-title]').innerText).to.match(/Scheduled/);
|
|
expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.have.class('active');
|
|
|
|
// clicking the main posts link resets
|
|
await click('[data-test-nav="posts"]');
|
|
expect(currentURL()).to.equal('/posts');
|
|
expect(find('[data-test-screen-title]')).to.have.rendered.text('Posts');
|
|
expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.not.have.class('active');
|
|
|
|
// changing a filter to match a custom view shows custom view
|
|
await selectChoose('[data-test-type-select]', 'Scheduled posts');
|
|
expect(currentURL()).to.equal('/posts?type=scheduled');
|
|
expect(find('[data-test-nav-custom="posts-Scheduled"]')).to.have.class('active');
|
|
expect(find('[data-test-screen-title]').innerText).to.match(/Scheduled/);
|
|
});
|
|
});
|
|
|
|
describe('as author', function () {
|
|
let author, authorPost;
|
|
|
|
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 = this.server.create('post', {authors: [author], status: 'published', title: 'Author Post'});
|
|
this.server.create('post', {authors: [admin], status: 'scheduled', title: 'Admin Post'});
|
|
|
|
return await authenticateSession();
|
|
});
|
|
|
|
it('only fetches the author\'s posts', async function () {
|
|
await visit('/posts');
|
|
// trigger a filter request so we can grab the posts API request easily
|
|
await selectChoose('[data-test-type-select]', 'Published posts');
|
|
|
|
// API request includes author filter
|
|
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(findAll('[data-test-post-id]').length, 'post count').to.equal(1);
|
|
expect(find(`[data-test-post-id="${authorPost.id}"]`), 'author post').to.exist;
|
|
});
|
|
});
|
|
|
|
describe('as contributor', function () {
|
|
beforeEach(async function () {
|
|
let contributorRole = this.server.create('role', {name: 'Contributor'});
|
|
this.server.create('user', {roles: [contributorRole]});
|
|
|
|
return await authenticateSession();
|
|
});
|
|
|
|
it('shows posts list and allows post creation', async function () {
|
|
await visit('/posts');
|
|
|
|
// has an empty state
|
|
expect(findAll('[data-test-post-id]')).to.have.length(0);
|
|
expect(find('[data-test-no-posts-box]')).to.exist;
|
|
expect(find('[data-test-link="write-a-new-post"]')).to.exist;
|
|
|
|
await click('[data-test-link="write-a-new-post"]');
|
|
|
|
expect(currentURL()).to.equal('/editor/post');
|
|
|
|
await fillIn('[data-test-editor-title-input]', 'First contributor post');
|
|
await blur('[data-test-editor-title-input]');
|
|
|
|
expect(currentURL()).to.equal('/editor/post/1');
|
|
|
|
await click('[data-test-link="posts"]');
|
|
|
|
expect(findAll('[data-test-post-id]')).to.have.length(1);
|
|
expect(find('[data-test-no-posts-box]')).to.not.exist;
|
|
});
|
|
});
|
|
});
|