mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-22 18:31:57 +03:00
683a8584ce
no issue Mixins are deprecated in Ember so we want to remove their usage. The `CurrentUserSettings` mixin was used in Route files to provide `transitionAuthor()` (that also transitions contributors) and `transitionEditor()` methods so the the consuming route could use them to prevent access to authors/editors. In practice the only reason this was used was to prevent access to admin-only routes. - added an `AdminRoute` class that inherits from our `AuthenticatedRoute` class - when any route inherits from this class it will only allow access to admins and owners, any other user will be redirected to the home screen (dashboard or site depending on permissions) - updated all of our admin-only routes to use the new `AdminRoute` - allowed for removal of `CurrentUserSettings` mixin usage - allowed for `beforeModel()` hooks to be removed from consuming routes in many cases - some admin-only routes were extending/inheriting directly from Ember's `Route` based on the assumption that the router hierarchy would have a parent route perform the redirect. Those have also been switched to `AdminRoute` for consistency and to prevent accidentally making them available if the router hierarchy changes - `/#/settings` does not use the `AdminRoute` so that it can redirect to the current user's setting page for non-admin users - removed `CurrentUserSettings` mixin file - cleaned up unnecessary computed property and function used for redirect-when-disabled in the Zapier route
484 lines
18 KiB
JavaScript
484 lines
18 KiB
JavaScript
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
|
import {beforeEach, describe, it} from 'mocha';
|
|
import {click, currentRouteName, currentURL, fillIn, find, findAll, triggerEvent} 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: Settings - Integrations - Custom', function () {
|
|
let hooks = setupApplicationTest();
|
|
setupMirage(hooks);
|
|
|
|
describe('access permissions', function () {
|
|
beforeEach(function () {
|
|
this.server.create('integration', {name: 'Test'});
|
|
});
|
|
|
|
it('redirects /integrations/ to signin when not authenticated', async function () {
|
|
await invalidateSession();
|
|
await visit('/settings/integrations');
|
|
|
|
expect(currentURL(), 'currentURL').to.equal('/signin');
|
|
});
|
|
|
|
it('redirects /integrations/ to home page when authenticated as contributor', async function () {
|
|
let role = this.server.create('role', {name: 'Contributor'});
|
|
this.server.create('user', {roles: [role], slug: 'test-user'});
|
|
|
|
await authenticateSession();
|
|
await visit('/settings/integrations');
|
|
|
|
expect(currentURL(), 'currentURL').to.equal('/site');
|
|
});
|
|
|
|
it('redirects /integrations/ to home page when authenticated as author', async function () {
|
|
let role = this.server.create('role', {name: 'Author'});
|
|
this.server.create('user', {roles: [role], slug: 'test-user'});
|
|
|
|
await authenticateSession();
|
|
await visit('/settings/integrations');
|
|
|
|
expect(currentURL(), 'currentURL').to.equal('/site');
|
|
});
|
|
|
|
it('redirects /integrations/ to home page when authenticated as editor', async function () {
|
|
let role = this.server.create('role', {name: 'Editor'});
|
|
this.server.create('user', {roles: [role], slug: 'test-user'});
|
|
|
|
await authenticateSession();
|
|
await visit('/settings/integrations/1');
|
|
|
|
expect(currentURL(), 'currentURL').to.equal('/site');
|
|
});
|
|
|
|
it('redirects /integrations/:id/ to signin when not authenticated', async function () {
|
|
await invalidateSession();
|
|
await visit('/settings/integrations/1');
|
|
|
|
expect(currentURL(), 'currentURL').to.equal('/signin');
|
|
});
|
|
|
|
it('redirects /integrations/:id/ to home page when authenticated as contributor', async function () {
|
|
let role = this.server.create('role', {name: 'Contributor'});
|
|
this.server.create('user', {roles: [role], slug: 'test-user'});
|
|
|
|
await authenticateSession();
|
|
await visit('/settings/integrations/1');
|
|
|
|
expect(currentURL(), 'currentURL').to.equal('/site');
|
|
});
|
|
|
|
it('redirects /integrations/:id/ to home page when authenticated as author', async function () {
|
|
let role = this.server.create('role', {name: 'Author'});
|
|
this.server.create('user', {roles: [role], slug: 'test-user'});
|
|
|
|
await authenticateSession();
|
|
await visit('/settings/integrations/1');
|
|
|
|
expect(currentURL(), 'currentURL').to.equal('/site');
|
|
});
|
|
|
|
it('redirects /integrations/:id/ to home page when authenticated as editor', async function () {
|
|
let role = this.server.create('role', {name: 'Editor'});
|
|
this.server.create('user', {roles: [role], slug: 'test-user'});
|
|
|
|
await authenticateSession();
|
|
await visit('/settings/integrations/1');
|
|
|
|
expect(currentURL(), 'currentURL').to.equal('/site');
|
|
});
|
|
});
|
|
|
|
describe('navigation', function () {
|
|
beforeEach(async function () {
|
|
let role = this.server.create('role', {name: 'Administrator'});
|
|
this.server.create('user', {roles: [role]});
|
|
|
|
return await authenticateSession();
|
|
});
|
|
|
|
it('renders correctly', async function () {
|
|
await visit('/settings/integrations');
|
|
|
|
// slack is not configured in the fixtures
|
|
expect(
|
|
find('[data-test-app="slack"] [data-test-app-status]').textContent.trim(),
|
|
'slack app status'
|
|
).to.equal('Configure');
|
|
|
|
// amp is enabled in the fixtures
|
|
expect(
|
|
find('[data-test-app="amp"] [data-test-app-status]').textContent.trim(),
|
|
'amp app status'
|
|
).to.equal('Active');
|
|
});
|
|
|
|
it('it redirects to Slack when clicking on the grid', async function () {
|
|
await visit('/settings/integrations');
|
|
|
|
// has correct url
|
|
expect(currentURL(), 'currentURL').to.equal('/settings/integrations');
|
|
|
|
await click('[data-test-link="slack"]');
|
|
|
|
// has correct url
|
|
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/slack');
|
|
});
|
|
|
|
it('it redirects to AMP when clicking on the grid', async function () {
|
|
await visit('/settings/integrations');
|
|
|
|
// has correct url
|
|
expect(currentURL(), 'currentURL').to.equal('/settings/integrations');
|
|
|
|
await click('[data-test-link="amp"]');
|
|
|
|
// has correct url
|
|
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/amp');
|
|
});
|
|
|
|
it('it redirects to Unsplash when clicking on the grid', async function () {
|
|
await visit('/settings/integrations');
|
|
|
|
// has correct url
|
|
expect(currentURL(), 'currentURL').to.equal('/settings/integrations');
|
|
|
|
await click('[data-test-link="unsplash"]');
|
|
|
|
// has correct url
|
|
expect(currentURL(), 'currentURL').to.equal('/settings/integrations/unsplash');
|
|
});
|
|
});
|
|
|
|
describe('custom integrations', function () {
|
|
beforeEach(async function () {
|
|
this.server.loadFixtures('configs');
|
|
let config = this.server.schema.configs.first();
|
|
config.update({
|
|
enableDeveloperExperiments: true
|
|
});
|
|
|
|
let role = this.server.create('role', {name: 'Administrator'});
|
|
this.server.create('user', {roles: [role]});
|
|
|
|
return await authenticateSession();
|
|
});
|
|
|
|
it('handles 404', async function () {
|
|
await visit('/settings/integrations/1');
|
|
expect(currentRouteName()).to.equal('error404');
|
|
});
|
|
|
|
it('can add new integration', async function () {
|
|
// sanity check
|
|
expect(
|
|
this.server.db.integrations.length,
|
|
'number of integrations in db at start'
|
|
).to.equal(0);
|
|
expect(
|
|
this.server.db.apiKeys.length,
|
|
'number of apiKeys in db at start'
|
|
).to.equal(0);
|
|
|
|
// blank slate
|
|
await visit('/settings/integrations');
|
|
|
|
expect(
|
|
find('[data-test-blank="custom-integrations"]'),
|
|
'initial blank slate'
|
|
).to.exist;
|
|
|
|
// new integration modal opens/closes
|
|
await click('[data-test-button="new-integration"]');
|
|
|
|
expect(currentURL(), 'url after clicking new').to.equal('/settings/integrations/new');
|
|
expect(find('[data-test-modal="new-integration"]'), 'modal after clicking new').to.exist;
|
|
|
|
await click('[data-test-button="cancel-new-integration"]');
|
|
|
|
expect(find('[data-test-modal="new-integration"]'), 'modal after clicking cancel')
|
|
.to.not.exist;
|
|
|
|
expect(
|
|
find('[data-test-blank="custom-integrations"]'),
|
|
'blank slate after cancelled creation'
|
|
).to.exist;
|
|
|
|
// new integration validations
|
|
await click('[data-test-button="new-integration"]');
|
|
await click('[data-test-button="create-integration"]');
|
|
|
|
expect(
|
|
find('[data-test-error="new-integration-name"]').textContent,
|
|
'name error after create with blank field'
|
|
).to.have.string('enter a name');
|
|
|
|
await fillIn('[data-test-input="new-integration-name"]', 'Duplicate');
|
|
await click('[data-test-button="create-integration"]');
|
|
|
|
expect(
|
|
find('[data-test-error="new-integration-name"]').textContent,
|
|
'name error after create with duplicate name'
|
|
).to.have.string('already been used');
|
|
|
|
// successful creation
|
|
await fillIn('[data-test-input="new-integration-name"]', 'Test');
|
|
|
|
expect(
|
|
find('[data-test-error="new-integration-name"]').textContent.trim(),
|
|
'name error after typing in field'
|
|
).to.be.empty;
|
|
|
|
await click('[data-test-button="create-integration"]');
|
|
|
|
expect(
|
|
find('[data-test-modal="new-integration"]'),
|
|
'modal after successful create'
|
|
).to.not.exist;
|
|
|
|
expect(
|
|
this.server.db.integrations.length,
|
|
'number of integrations in db after create'
|
|
).to.equal(1);
|
|
// mirage sanity check
|
|
expect(
|
|
this.server.db.apiKeys.length,
|
|
'number of api keys in db after create'
|
|
).to.equal(2);
|
|
|
|
expect(
|
|
currentURL(),
|
|
'url after integration creation'
|
|
).to.equal('/settings/integrations/1');
|
|
|
|
// test navigation back to list then back to new integration
|
|
await click('[data-test-link="integrations-back"]');
|
|
|
|
expect(
|
|
currentURL(),
|
|
'url after clicking "Back"'
|
|
).to.equal('/settings/integrations');
|
|
|
|
expect(
|
|
find('[data-test-blank="custom-integrations"]'),
|
|
'blank slate after creation'
|
|
).to.not.exist;
|
|
|
|
expect(
|
|
findAll('[data-test-custom-integration]').length,
|
|
'number of custom integrations after creation'
|
|
).to.equal(1);
|
|
|
|
await click(`[data-test-integration="1"]`);
|
|
|
|
expect(
|
|
currentURL(),
|
|
'url after clicking integration in list'
|
|
).to.equal('/settings/integrations/1');
|
|
});
|
|
|
|
it.skip('can manage an integration', async function () {
|
|
this.server.create('integration');
|
|
|
|
await visit('/settings/integrations/1');
|
|
|
|
expect(
|
|
currentURL(),
|
|
'initial URL'
|
|
).to.equal('/settings/integrations/1');
|
|
|
|
expect(
|
|
find('[data-test-screen-title]').textContent,
|
|
'screen title'
|
|
).to.have.string('Integration 1');
|
|
|
|
// fields have expected values
|
|
// TODO: add test for logo
|
|
|
|
expect(
|
|
find('[data-test-input="name"]').value,
|
|
'initial name value'
|
|
).to.equal('Integration 1');
|
|
|
|
expect(
|
|
find('[data-test-input="description"]').value,
|
|
'initial description value'
|
|
).to.equal('');
|
|
|
|
expect(
|
|
find('[data-test-text="content-key"]'),
|
|
'content key text'
|
|
).to.have.trimmed.text('integration-1_content_key-12345');
|
|
|
|
expect(
|
|
find('[data-test-text="admin-key"]'),
|
|
'admin key text'
|
|
).to.have.trimmed.text('integration-1_admin_key-12345');
|
|
|
|
expect(
|
|
find('[data-test-text="api-url"]'),
|
|
'api url text'
|
|
).to.have.trimmed.text(window.location.origin);
|
|
|
|
// it can modify integration fields and has validation
|
|
|
|
expect(
|
|
find('[data-test-error="name"]').textContent.trim(),
|
|
'initial name error'
|
|
).to.be.empty;
|
|
|
|
await fillIn('[data-test-input="name"]', '');
|
|
await triggerEvent('[data-test-input="name"]', 'blur');
|
|
|
|
expect(
|
|
find('[data-test-error="name"]').textContent,
|
|
'name validation for blank string'
|
|
).to.have.string('enter a name');
|
|
|
|
await click('[data-test-button="save"]');
|
|
|
|
expect(
|
|
this.server.schema.integrations.first().name,
|
|
'db integration name after failed save'
|
|
).to.equal('Integration 1');
|
|
|
|
await fillIn('[data-test-input="name"]', 'Test Integration');
|
|
await triggerEvent('[data-test-input="name"]', 'blur');
|
|
|
|
expect(
|
|
find('[data-test-error="name"]').textContent.trim(),
|
|
'name error after valid entry'
|
|
).to.be.empty;
|
|
|
|
await fillIn('[data-test-input="description"]', 'Description for Test Integration');
|
|
await triggerEvent('[data-test-input="description"]', 'blur');
|
|
await click('[data-test-button="save"]');
|
|
|
|
// changes are reflected in the integrations list
|
|
|
|
await click('[data-test-link="integrations-back"]');
|
|
|
|
expect(
|
|
currentURL(),
|
|
'url after saving and clicking "back"'
|
|
).to.equal('/settings/integrations');
|
|
|
|
expect(
|
|
find('[data-test-integration="1"] [data-test-text="name"]').textContent.trim(),
|
|
'integration name after save'
|
|
).to.equal('Test Integration');
|
|
|
|
expect(
|
|
find('[data-test-integration="1"] [data-test-text="description"]').textContent.trim(),
|
|
'integration description after save'
|
|
).to.equal('Description for Test Integration');
|
|
|
|
await click('[data-test-integration="1"]');
|
|
|
|
// warns of unsaved changes when leaving
|
|
|
|
await fillIn('[data-test-input="name"]', 'Unsaved test');
|
|
await click('[data-test-link="integrations-back"]');
|
|
|
|
expect(
|
|
find('[data-test-modal="unsaved-settings"]'),
|
|
'modal shown when navigating with unsaved changes'
|
|
).to.exist;
|
|
|
|
await click('[data-test-stay-button]');
|
|
|
|
expect(
|
|
find('[data-test-modal="unsaved-settings"]'),
|
|
'modal is closed after clicking "stay"'
|
|
).to.not.exist;
|
|
|
|
expect(
|
|
currentURL(),
|
|
'url after clicking "stay"'
|
|
).to.equal('/settings/integrations/1');
|
|
|
|
await click('[data-test-link="integrations-back"]');
|
|
await click('[data-test-leave-button]');
|
|
|
|
expect(
|
|
find('[data-test-modal="unsaved-settings"]'),
|
|
'modal is closed after clicking "leave"'
|
|
).to.not.exist;
|
|
|
|
expect(
|
|
currentURL(),
|
|
'url after clicking "leave"'
|
|
).to.equal('/settings/integrations');
|
|
|
|
expect(
|
|
find('[data-test-integration="1"] [data-test-text="name"]').textContent.trim(),
|
|
'integration name after leaving unsaved changes'
|
|
).to.equal('Test Integration');
|
|
});
|
|
|
|
it('can manage an integration\'s webhooks', async function () {
|
|
this.server.create('integration');
|
|
|
|
await visit('/settings/integrations/1');
|
|
|
|
expect(find('[data-test-webhooks-blank-slate]')).to.exist;
|
|
|
|
// open new webhook modal
|
|
await click('[data-test-link="add-webhook"]');
|
|
expect(find('[data-test-modal="webhook-form"]')).to.exist;
|
|
expect(find('[data-test-modal="webhook-form"] [data-test-text="title"]').textContent)
|
|
.to.have.string('New webhook');
|
|
|
|
// can cancel new webhook
|
|
await click('[data-test-button="cancel-webhook"]');
|
|
expect(find('[data-test-modal="webhook-form"]')).to.not.exist;
|
|
|
|
// create new webhook
|
|
await click('[data-test-link="add-webhook"]');
|
|
await fillIn('[data-test-input="webhook-name"]', 'First webhook');
|
|
await fillIn('[data-test-select="webhook-event"]', 'site.changed');
|
|
await fillIn('[data-test-input="webhook-targetUrl"]', 'https://example.com/first-webhook');
|
|
await click('[data-test-button="save-webhook"]');
|
|
|
|
// modal closed and 1 webhook listed with correct details
|
|
expect(find('[data-test-modal="webhook-form"]')).to.not.exist;
|
|
expect(find('[data-test-webhook-row]')).to.exist;
|
|
let row = find('[data-test-webhook-row="1"]');
|
|
expect(row.querySelector('[data-test-text="name"]').textContent)
|
|
.to.have.string('First webhook');
|
|
expect(row.querySelector('[data-test-text="event"]').textContent)
|
|
.to.have.string('Site changed (rebuild)');
|
|
expect(row.querySelector('[data-test-text="targetUrl"]').textContent)
|
|
.to.have.string('https://example.com/first-webhook');
|
|
expect(row.querySelector('[data-test-text="last-triggered"]').textContent)
|
|
.to.have.string('Not triggered');
|
|
|
|
// click edit webhook link
|
|
await click('[data-test-webhook-row="1"] [data-test-link="edit-webhook"]');
|
|
|
|
// modal appears and has correct title
|
|
expect(find('[data-test-modal="webhook-form"]')).to.exist;
|
|
expect(find('[data-test-modal="webhook-form"] [data-test-text="title"]').textContent)
|
|
.to.have.string('Edit webhook');
|
|
});
|
|
|
|
// test to ensure the `value=description` passed to `gh-text-input` is `readonly`
|
|
it('doesn\'t show unsaved changes modal after placing focus on description field', async function () {
|
|
this.server.create('integration');
|
|
|
|
await visit('/settings/integrations/1');
|
|
await click('[data-test-input="description"]');
|
|
await triggerEvent('[data-test-input="description"]', 'blur');
|
|
await click('[data-test-link="integrations-back"]');
|
|
|
|
expect(
|
|
find('[data-test-modal="unsaved-settings"]'),
|
|
'unsaved changes modal is not shown'
|
|
).to.not.exist;
|
|
|
|
expect(currentURL()).to.equal('/settings/integrations');
|
|
});
|
|
});
|
|
});
|