Added Admin acceptance test for post revision restoration

ref https://linear.app/tryghost/issue/ENG-1078

- adds required setup for acceptance testing post revisions
- adds happy-path test for listing, previewing, and restoring a post revision
This commit is contained in:
Kevin Ansfield 2024-06-04 19:46:39 +01:00
parent 98b51b666d
commit 36ba47604f
15 changed files with 294 additions and 170 deletions

View File

@ -190,3 +190,7 @@ add|ember-template-lint|require-iframe-title|42|16|42|16|a3292b469dc37f2f4791e7f
add|ember-template-lint|no-autofocus-attribute|21|20|21|20|942419d05c04ded6716f09faecd6b1ab55418121|1712707200000|1723075200000|1728259200000|app/components/modals/new-custom-integration.hbs
add|ember-template-lint|no-invalid-interactive|2|37|2|37|e21ba31f54b631a428c28a1c9f88d0dc66f2f5fc|1712707200000|1723075200000|1728259200000|app/components/modals/search.hbs
remove|ember-template-lint|no-unknown-arguments-for-builtin-components|93|93|93|93|156670ca427c49c51f0a94f862b286ccc9466d92|1712707200000|1723075200000|1728259200000|app/components/gh-nav-menu/footer.hbs
remove|ember-template-lint|no-action|20|125|20|125|ba0f8b6ba2697f1b071200d1a3dae9c34fcb2882|1712707200000|1723075200000|1728259200000|app/components/modal-post-history.hbs
remove|ember-template-lint|no-action|31|34|31|34|141d456b03124abca146e58e4ae15825fdd040bb|1712707200000|1723075200000|1728259200000|app/components/modal-post-history.hbs
remove|ember-template-lint|no-action|43|46|43|46|2f3118270fbb1ff6e5da6b0d482ccd21e69df3b5|1712707200000|1723075200000|1728259200000|app/components/modal-post-history.hbs
remove|ember-template-lint|require-valid-alt-text|12|28|12|28|bc0bb4f51567cea7289bfcb30d02932f0f57d0d9|1712707200000|1723075200000|1728259200000|app/components/modal-post-history.hbs

View File

@ -1,6 +1,6 @@
{{!-- template-lint-disable no-invalid-interactive --}}
<div class="gh-post-history" {{did-insert this.onInsert}}>
<div class="gh-post-history-main">
<div class="gh-post-history-main" data-test-post-history-preview>
<div class="gh-koenig-editor-pane flex flex-column mih-100">
{{#if this.selectedHTML}}
{{{this.selectedHTML}}}
@ -9,15 +9,23 @@
<div class="gh-editor-feature-image-container">
<div class="gh-editor-feature-image">
{{#if this.selectedRevision.feature_image}}
<img src="{{this.selectedRevision.feature_image}}">
<img
src={{this.selectedRevision.feature_image}}
alt={{this.selectedRevision.feature_image_alt}}
data-test-post-history-preview-feature-image
>
{{/if}}
{{#if this.selectedRevision.feature_image_caption}}
<p>{{{this.selectedRevision.feature_image_caption}}}</p>
<p data-test-post-history-preview-feature-image-caption>{{{this.selectedRevision.feature_image_caption}}}</p>
{{/if}}
</div>
</div>
<div class="gh-editor-title">{{this.currentTitle}}</div>
<KoenigLexicalEditor @lexical={{this.selectedRevision.lexical}} @cardConfig={{this.cardConfig}} @registerAPI={{action "registerSelectedEditorApi"}}/>
<div class="gh-editor-title" data-test-post-history-preview-title>{{this.currentTitle}}</div>
<KoenigLexicalEditor
@lexical={{this.selectedRevision.lexical}}
@cardConfig={{this.cardConfig}}
@registerAPI={{this.registerSelectedEditorApi}}
/>
</div>
</div>
</div>
@ -28,7 +36,8 @@
aria-label="Close meta data panel"
class="back settings-menu-header-action"
data-test-button="close-psm-subview"
type="button" {{action "closeModal"}}
type="button"
{{on "click" this.closeModal}}
{{on "mousedown" (optional this.noop)}}
>
{{svg-jar "arrow-left"}}
@ -39,19 +48,19 @@
<div class="settings-menu-content">
<ul class="nav-list">
{{#each this.revisionList as |revision index|}}
<li class="nav-list-item {{if revision.selected "selected" ""}}">
<button type="button" {{action "handleClick" index}}>
<li class="nav-list-item {{if revision.selected "selected" ""}}" data-test-revision-item={{index}}>
<button type="button" {{on "click" (fn this.handleClick index)}} data-test-button="preview-revision">
<div class="flex items-center">
<span class="gh-post-history-version">{{gh-format-post-time revision.createdAt format="D MMM YYYY, HH:mm"}}{{this.timezone}}</span>
{{#if revision.latest}}
<span class="gh-post-history-version-tag current">Latest</span>
<span class="gh-post-history-version-tag current" data-test-revision-latest>Latest</span>
{{/if}}
{{#if revision.new_publish}}
<span class="gh-post-history-version-tag published">Published</span>
<span class="gh-post-history-version-tag published" data-test-revision-published>Published</span>
{{/if}}
{{#if (eq revision.reason "unpublished")}}
<span class="gh-post-history-version-tag unpublished">Unpublished</span>
<span class="gh-post-history-version-tag unpublished" data-test-revision-unpublished>Unpublished</span>
{{/if}}
</div>
@ -67,6 +76,7 @@
type="button"
class="gh-post-history-version-restore"
{{on "click" (fn this.restoreRevision index)}}
data-test-button="restore-revision"
>
<span>Restore</span>
</button>

View File

@ -3,6 +3,7 @@ import RestoreRevisionModal from '../components/modals/restore-revision';
import {action, set} from '@ember/object';
import {inject as service} from '@ember/service';
import {tracked} from '@glimmer/tracking';
import {waitFor} from '@ember/test-waiters';
function checkFinishedRendering(element, done) {
let last = element.innerHTML;
@ -84,9 +85,11 @@ export default class ModalPostHistory extends Component {
}
@action
handleClick(index) {
@waitFor
async handleClick(index) {
this.selectedRevisionIndex = index;
this.updateSelectedHTML();
// async with @waitFor so tests will wait for the action to complete
await this.updateSelectedHTML();
}
@action
@ -138,28 +141,31 @@ export default class ModalPostHistory extends Component {
}
updateSelectedHTML() {
if (this.selectedEditor) {
let selectedState = this.selectedEditor.editorInstance.parseEditorState(this.selectedRevision.lexical);
return new Promise((resolve) => {
if (this.selectedEditor) {
let selectedState = this.selectedEditor.editorInstance.parseEditorState(this.selectedRevision.lexical);
this.selectedEditor.editorInstance.setEditorState(selectedState);
}
let current = document.querySelector('.gh-post-history-hidden-lexical.current');
let currentDone = false;
let updateIfDone = () => {
if (currentDone) {
this.selectedHTML = this.stripInitialPlaceholder(current.innerHTML);
this.selectedEditor.editorInstance.setEditorState(selectedState);
}
};
checkFinishedRendering(current, () => {
current.querySelectorAll('[contenteditable]').forEach((el) => {
el.setAttribute('contenteditable', false);
let current = document.querySelector('.gh-post-history-hidden-lexical.current');
let currentDone = false;
let updateIfDone = () => {
if (currentDone) {
this.selectedHTML = this.stripInitialPlaceholder(current.innerHTML);
}
resolve();
};
checkFinishedRendering(current, () => {
current.querySelectorAll('[contenteditable]').forEach((el) => {
el.setAttribute('contenteditable', false);
});
currentDone = true;
updateIfDone();
});
currentDone = true;
updateIfDone();
});
}
}

View File

@ -1,4 +1,4 @@
<div class="modal-content" {{on-key "Enter" (perform this.restoreRevisionTask)}}>
<div class="modal-content" {{on-key "Enter" (perform this.restoreRevisionTask)}} data-test-modal="restore-revision">
<header class="modal-header">
<h1>{{this.title}}</h1>
</header>
@ -15,6 +15,7 @@
@successText="Restored"
@task={{this.restoreRevisionTask}}
@class="gh-btn gh-btn-black gh-btn-icon"
data-test-button="restore"
/>
</div>
</div>

View File

@ -34,6 +34,9 @@ export default class RestoreRevisionModal extends Component {
post.lexical = revision.lexical;
post.title = revision.title;
post.featureImage = revision.feature_image;
post.featureImageAlt = revision.feature_image_alt;
post.featureImageCaption = revision.feature_image_caption;
yield post.save({adapterOptions: {saveRevision: true}});

View File

@ -0,0 +1,5 @@
import {Factory} from 'miragejs';
export default Factory.extend({
});

View File

@ -1,5 +1,5 @@
import PostModel from './post';
export default PostModel.extend({
postRevisions: null
});

View File

@ -0,0 +1,6 @@
import {Model, belongsTo} from 'miragejs';
export default Model.extend({
post: belongsTo(),
author: belongsTo('user')
});

View File

@ -4,5 +4,6 @@ export default Model.extend({
tags: hasMany(),
authors: hasMany('user'),
email: belongsTo(),
newsletter: belongsTo()
newsletter: belongsTo(),
postRevisions: hasMany()
});

View File

@ -6,5 +6,6 @@ export default Model.extend({
postCount: false,
roles: hasMany(),
posts: hasMany()
posts: hasMany(),
postRevision: hasMany()
});

View File

@ -0,0 +1,9 @@
import BaseSerializer from './application';
export default BaseSerializer.extend({
embed: true,
include() {
return ['author'];
}
});

View File

@ -1,15 +1,25 @@
import BaseSerializer from './application';
import {camelize} from '@ember/string';
export default BaseSerializer.extend({
embed: true,
include(/*request*/) {
let includes = [];
include(request) {
const queryIncludes = (request.queryParams.include || '').split(',').compact().map(camelize);
const includes = new Set(queryIncludes);
includes.push('tags');
includes.push('authors');
// embedded records that are included by default in the API
includes.add('tags');
includes.add('authors');
return includes;
// clean up some things that mirage doesn't understand
includes.delete('authorsRoles');
includes.delete('countClicks');
includes.delete('postRevisionsAuthor');
includes.delete('tiers');
const result = Array.from(includes);
return result;
},
serialize(postModelOrCollection, request) {

View File

@ -36,6 +36,7 @@
"@ember/optional-features": "2.0.0",
"@ember/render-modifiers": "2.1.0",
"@ember/test-helpers": "2.9.4",
"@ember/test-waiters": "^3.1.0",
"@embroider/macros": "1.13.4",
"@glimmer/component": "1.1.2",
"@html-next/vertical-collection": "3.0.0",
@ -204,4 +205,4 @@
}
}
}
}
}

View File

@ -0,0 +1,78 @@
import loginAsRole from '../../helpers/login-as-role';
import moment from 'moment-timezone';
import {click, 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: Post revisions', function () {
let hooks = setupApplicationTest();
setupMirage(hooks);
beforeEach(async function () {
this.server.loadFixtures();
await loginAsRole('Administrator', this.server);
});
it('can restore a draft post revision', async function () {
const post = this.server.create('post', {
title: 'Current Title',
status: 'draft'
});
this.server.create('post-revision', {
post,
title: post.title,
featureImage: post.featureImage,
featureImageAlt: post.featureImageAlt,
featureImageCaption: post.featureImageCaption,
postStatus: 'draft',
author: post.authors.models[0],
createdAt: moment(post.updatedAt).subtract(1, 'hour'),
reason: 'explicit_save'
});
this.server.create('post-revision', {
post,
title: 'Old Title',
featureImage: post.featureImage,
featureImageAlt: post.featureImageAlt,
featureImageCaption: post.featureImageCaption,
postStatus: 'draft',
author: post.authors.models[0],
createdAt: moment(post.updatedAt).subtract(1, 'day'),
reason: 'initial_revision'
});
// this.timeout(0);
await visit(`/editor/post/${post.id}`);
// open post history menu
await click('[data-test-psm-trigger]');
await click('[data-test-toggle="post-history"]');
// latest and previous post listed
expect(findAll('[data-test-revision-item]').length).to.equal(2);
expect(find('[data-test-revision-item="0"]')).to.contain.trimmed.text('19 Oct 2015, 15:25');
expect(find('[data-test-revision-item="0"] [data-test-revision-latest]')).to.exist;
expect(find('[data-test-revision-item="1"]')).to.contain.trimmed.text('18 Oct 2015, 16:25');
expect(find('[data-test-revision-item="1"] [data-test-revision-latest]')).to.not.exist;
// latest post is previewed by default
expect(find('[data-test-post-history-preview-title]')).to.have.trimmed.text('Current Title');
// previous post can be previewed
await click('[data-test-revision-item="1"] [data-test-button="preview-revision"]');
expect(find('[data-test-post-history-preview-title]')).to.have.trimmed.text('Old Title');
// previous post can be restored
expect(find('[data-test-revision-item="1"] [data-test-button="restore-revision"]')).to.exist;
await click('[data-test-revision-item="1"] [data-test-button="restore-revision"]');
expect(find('[data-test-modal="restore-revision"]')).to.exist;
await click('[data-test-modal="restore-revision"] [data-test-button="restore"]');
expect(find('[data-test-modal="restore-revision"]')).to.not.exist;
expect(find('[data-test-editor-title-input]')).to.have.value('Old Title');
});
});

247
yarn.lock
View File

@ -2430,7 +2430,7 @@
ember-cli-babel "^7.22.1"
ember-compatibility-helpers "^1.1.1"
"@ember/render-modifiers@2.1.0", "@ember/render-modifiers@^1.0.2 || ^2.0.0", "@ember/render-modifiers@^2.0.0", "@ember/render-modifiers@^2.0.4":
"@ember/render-modifiers@2.1.0", "@ember/render-modifiers@^1.0.2 || ^2.0.0", "@ember/render-modifiers@^2.0.4":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-2.1.0.tgz#f4fff95a8b5cfbe947ec46644732d511711c5bf9"
integrity sha512-LruhfoDv2itpk0fA0IC76Sxjcnq/7BC6txpQo40hOko8Dn6OxwQfxkPIbZGV0Cz7df+iX+VJrcYzNIvlc3w2EQ==
@ -2489,6 +2489,16 @@
ember-cli-version-checker "^5.1.2"
semver "^7.3.5"
"@ember/test-waiters@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@ember/test-waiters/-/test-waiters-3.1.0.tgz#61399919cbf793978da0b8bfdfdb7bca0cb80e9e"
integrity sha512-bb9h95ktG2wKY9+ja1sdsFBdOms2lB19VWs8wmNpzgHv1NCetonBoV5jHBV4DHt0uS1tg9z66cZqhUVlYs96KQ==
dependencies:
calculate-cache-key-for-tree "^2.0.0"
ember-cli-babel "^7.26.6"
ember-cli-version-checker "^5.1.2"
semver "^7.3.5"
"@embroider/addon-shim@^0.50.2":
version "0.50.2"
resolved "https://registry.yarnpkg.com/@embroider/addon-shim/-/addon-shim-0.50.2.tgz#9be2ed8cee1086cef63c5baecf8dde8dcdacc24e"
@ -2568,19 +2578,6 @@
resolve "^1.8.1"
semver "^7.3.2"
"@embroider/macros@0.47.2", "@embroider/macros@^0.47.2":
version "0.47.2"
resolved "https://registry.npmjs.org/@embroider/macros/-/macros-0.47.2.tgz#23cbe92cac3c24747f054e1eea2a22538bf7ebd0"
integrity sha512-ViNWluJCeM5OPlM3rs8kdOz3RV5rpfXX5D2rDnc/q86xRS0xf4NFEjYRV7W6fBcD0b3v5jSHDTwrjq9Kee4rHg==
dependencies:
"@embroider/shared-internals" "0.47.2"
assert-never "^1.2.1"
ember-cli-babel "^7.26.6"
find-up "^5.0.0"
lodash "^4.17.21"
resolve "^1.20.0"
semver "^7.3.2"
"@embroider/macros@1.13.4", "@embroider/macros@^0.50.0 || ^1.0.0", "@embroider/macros@^1.0.0", "@embroider/macros@^1.10.0", "@embroider/macros@^1.12.2", "@embroider/macros@^1.2.0", "@embroider/macros@^1.8.0", "@embroider/macros@^1.8.3", "@embroider/macros@^1.9.0":
version "1.13.4"
resolved "https://registry.yarnpkg.com/@embroider/macros/-/macros-1.13.4.tgz#4fefb79d68bcfbc4619551572b2ca3040a224fb5"
@ -2608,19 +2605,6 @@
semver "^7.3.2"
typescript-memoize "^1.0.0-alpha.3"
"@embroider/shared-internals@0.47.2":
version "0.47.2"
resolved "https://registry.npmjs.org/@embroider/shared-internals/-/shared-internals-0.47.2.tgz#24e9fa0dd9c529d5c996ee1325729ea08d1fa19f"
integrity sha512-SxdZYjAE0fiM5zGDz+12euWIsQZ1tsfR1k+NKmiWMyLhA5T3pNgbR2/Djvx/cVIxOtEavGGSllYbzRKBtV4xMg==
dependencies:
babel-import-util "^0.2.0"
ember-rfc176-data "^0.3.17"
fs-extra "^9.1.0"
lodash "^4.17.21"
resolve-package-path "^4.0.1"
semver "^7.3.5"
typescript-memoize "^1.0.1"
"@embroider/shared-internals@2.5.1", "@embroider/shared-internals@^2.0.0":
version "2.5.1"
resolved "https://registry.yarnpkg.com/@embroider/shared-internals/-/shared-internals-2.5.1.tgz#a4d8c057cbff293ef6eb29ee6537f263d206b444"
@ -2681,15 +2665,6 @@
broccoli-funnel "^3.0.5"
ember-cli-babel "^7.23.1"
"@embroider/util@^0.47.2":
version "0.47.2"
resolved "https://registry.npmjs.org/@embroider/util/-/util-0.47.2.tgz#d06497b4b84c07ed9c7b628293bb019c533f4556"
integrity sha512-g9OqnFJPktGu9NS0Ug3Pxz1JE3jeDceeVE4IrlxDrVmBXMA/GrBvpwjolWgl6jh97cMJyExdz62jIvPHV4256Q==
dependencies:
"@embroider/macros" "0.47.2"
broccoli-funnel "^3.0.5"
ember-cli-babel "^7.23.1"
"@emotion/babel-plugin@^11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c"
@ -3347,6 +3322,18 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@isaacs/cliui@^8.0.2":
version "8.0.2"
resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550"
integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==
dependencies:
string-width "^5.1.2"
string-width-cjs "npm:string-width@^4.2.0"
strip-ansi "^7.0.1"
strip-ansi-cjs "npm:strip-ansi@^6.0.1"
wrap-ansi "^8.1.0"
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
"@isaacs/ttlcache@1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2"
@ -6913,7 +6900,7 @@
"@tryghost/root-utils" "^0.3.28"
debug "^4.3.1"
"@tryghost/elasticsearch@^3.0.19":
"@tryghost/elasticsearch@^3.0.16", "@tryghost/elasticsearch@^3.0.19":
version "3.0.19"
resolved "https://registry.yarnpkg.com/@tryghost/elasticsearch/-/elasticsearch-3.0.19.tgz#a0a94b667c83575a57775027aea5cb4ff4f216ef"
integrity sha512-7yOPEnkebsMuHeIH5oYRQoHa1vz1AkjHRPN4GWJhcywrH20S3Kj66oUPj+8jyqDOvfwqPc7Vfa6jc7rTcy3AQQ==
@ -6943,7 +6930,15 @@
focus-trap "^6.7.2"
postcss-preset-env "^7.3.1"
"@tryghost/errors@1.3.1", "@tryghost/errors@1.3.2", "@tryghost/errors@^1.2.26", "@tryghost/errors@^1.2.3", "@tryghost/errors@^1.3.2":
"@tryghost/errors@1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@tryghost/errors/-/errors-1.3.1.tgz#32a00c5e5293c46e54d03a66da871ac34b2ab35c"
integrity sha512-iZqT0vZ3NVZNq9o1HYxW00k1mcUAC+t5OLiI8O29/uQwAfy7NemY+Cabl9mWoIwgvBmw7l0Z8pHTcXMo1c+xMw==
dependencies:
"@stdlib/utils-copy" "^0.0.7"
uuid "^9.0.0"
"@tryghost/errors@1.3.2", "@tryghost/errors@^1.2.26", "@tryghost/errors@^1.2.3", "@tryghost/errors@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@tryghost/errors/-/errors-1.3.2.tgz#3612f6f59ca07e37d1095f9eb31ff6a069a3b7f2"
integrity sha512-Aqi2vz7HHwN6p2juIYUu8vpMtaKavjULBKNnL0l1req9qXjPs90i/HV8zhvK0ceeWuPdEXaCkfHSRr/yxG3/uw==
@ -6981,7 +6976,7 @@
resolved "https://registry.yarnpkg.com/@tryghost/http-cache-utils/-/http-cache-utils-0.1.15.tgz#721ffd1f3b7173da679f6c8c25cda6f0c728264b"
integrity sha512-G0x9ZUkBbTWIFg2Dnng6qKLststZKuUYx98pcowNwpYuEGUPOka4eAJj3frz/60F1CzMb1qYy9WEjD/xRGd5oQ==
"@tryghost/http-stream@^0.1.30":
"@tryghost/http-stream@^0.1.27", "@tryghost/http-stream@^0.1.30":
version "0.1.30"
resolved "https://registry.yarnpkg.com/@tryghost/http-stream/-/http-stream-0.1.30.tgz#ef2a9a55a2ec7fab7e889a11ce23e7ebfaab8a0b"
integrity sha512-ZlcwtWQICN2OeHcVAL+BXxkzO5tICdLYZeQ2EuVk2E+mC+1ARFrpVUbpTnIbCXDL+uV3cOLqPh2KNGGJAdDChw==
@ -7162,7 +7157,24 @@
lodash "^4.17.21"
luxon "^1.26.0"
"@tryghost/logging@2.4.10", "@tryghost/logging@2.4.15", "@tryghost/logging@^2.4.7":
"@tryghost/logging@2.4.10":
version "2.4.10"
resolved "https://registry.yarnpkg.com/@tryghost/logging/-/logging-2.4.10.tgz#2e5b56c53364be330c1e6f2ffa33e3c30b7bac8e"
integrity sha512-l356vLSQmszY14y7ef5YxY4CZ3418NXn5+LvFdlweeTRk0ilWx1mVUoXi8IlVh90rIVbemv+pXi1dusJB6peQA==
dependencies:
"@tryghost/bunyan-rotating-filestream" "^0.0.7"
"@tryghost/elasticsearch" "^3.0.16"
"@tryghost/http-stream" "^0.1.27"
"@tryghost/pretty-stream" "^0.1.21"
"@tryghost/root-utils" "^0.3.25"
bunyan "^1.8.15"
bunyan-loggly "^1.4.2"
fs-extra "^11.0.0"
gelf-stream "^1.1.1"
json-stringify-safe "^5.0.1"
lodash "^4.17.21"
"@tryghost/logging@2.4.15", "@tryghost/logging@^2.4.7":
version "2.4.15"
resolved "https://registry.yarnpkg.com/@tryghost/logging/-/logging-2.4.15.tgz#a94e37d760a62d6f2fc2868e4cd8bf6f219b2a2e"
integrity sha512-mSVdSR/9bd1D/DCFpfeFn2AnPE/0lK78ePHBrtteOipA7ogL0Kd+QvabHK5iKLe+/20flBZs4BvnU/DBuS8Pvw==
@ -7251,7 +7263,7 @@
chalk "^4.1.0"
sywac "^1.3.0"
"@tryghost/pretty-stream@^0.1.24":
"@tryghost/pretty-stream@^0.1.21", "@tryghost/pretty-stream@^0.1.24":
version "0.1.24"
resolved "https://registry.yarnpkg.com/@tryghost/pretty-stream/-/pretty-stream-0.1.24.tgz#f670dd66d0d3f7fa733f72294b42fc1b40b0fbd3"
integrity sha512-s83KJXIt2nN4JRtBkkWWPLgmh8d/FKd8pvlln4L+EOKkNiVie2vEeATphFsWiK2NZOc4GSgv/fZHxoKBvnOrnA==
@ -7282,7 +7294,7 @@
got "13.0.0"
lodash "^4.17.21"
"@tryghost/root-utils@0.3.28", "@tryghost/root-utils@^0.3.24", "@tryghost/root-utils@^0.3.28":
"@tryghost/root-utils@0.3.28", "@tryghost/root-utils@^0.3.24", "@tryghost/root-utils@^0.3.25", "@tryghost/root-utils@^0.3.28":
version "0.3.28"
resolved "https://registry.yarnpkg.com/@tryghost/root-utils/-/root-utils-0.3.28.tgz#43ae0047927a7753c9b526ea12ce6e382ec7fb1f"
integrity sha512-/izwMw9tCJIQ3DVHumzEWgKhKAw5FwTgrrYcCNHl89yijJKaVRBOJUhlB/u2ST6UWfhahodjaYauq7ymTItaeg==
@ -8896,7 +8908,7 @@ ansi-styles@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
ansi-styles@^6.0.0, ansi-styles@^6.2.1:
ansi-styles@^6.0.0, ansi-styles@^6.1.0, ansi-styles@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
@ -13932,6 +13944,11 @@ duplexify@^3.4.2, duplexify@^3.5.0, duplexify@^3.6.0:
readable-stream "^2.0.0"
stream-shift "^1.0.0"
eastasianwidth@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
@ -14117,7 +14134,7 @@ ember-auto-import@2.7.3, "ember-auto-import@^1.12.1 || ^2.4.3", ember-auto-impor
typescript-memoize "^1.0.0-alpha.3"
walk-sync "^3.0.0"
ember-auto-import@^1.11.2, ember-auto-import@^1.11.3, ember-auto-import@^1.12.0:
ember-auto-import@^1.11.3, ember-auto-import@^1.12.0:
version "1.12.2"
resolved "https://registry.yarnpkg.com/ember-auto-import/-/ember-auto-import-1.12.2.tgz#cc7298ee5c0654b0249267de68fb27a2861c3579"
integrity sha512-gLqML2k77AuUiXxWNon1FSzuG1DV7PEPpCLCU5aJvf6fdL6rmFfElsZRh+8ELEB/qP9dT+LHjNEunVzd2dYc8A==
@ -14152,25 +14169,7 @@ ember-auto-import@^1.11.2, ember-auto-import@^1.11.3, ember-auto-import@^1.12.0:
walk-sync "^0.3.3"
webpack "^4.43.0"
ember-basic-dropdown@^3.0.11:
version "3.1.0"
resolved "https://registry.npmjs.org/ember-basic-dropdown/-/ember-basic-dropdown-3.1.0.tgz#47c292de890d1958057736c00b8eb2b8017d530b"
integrity sha512-UISvgJHfiJ8FeXqH8ZN+NmoImN8p6Sb+85qlEv853hLuEfEYnFUqLNhea8nNl9CpFqcD3yU4dKbhYtc6nB39aQ==
dependencies:
"@ember/render-modifiers" "^2.0.0"
"@embroider/macros" "^0.47.2"
"@embroider/util" "^0.47.2"
"@glimmer/component" "^1.0.4"
"@glimmer/tracking" "^1.0.4"
ember-cli-babel "^7.26.6"
ember-cli-htmlbars "^6.0.0"
ember-cli-typescript "^4.2.1"
ember-element-helper "^0.5.5"
ember-maybe-in-element "^2.0.3"
ember-style-modifier "^0.7.0"
ember-truth-helpers "^2.1.0 || ^3.0.0"
ember-basic-dropdown@^6.0.0:
ember-basic-dropdown@6.0.2, ember-basic-dropdown@^3.0.11, ember-basic-dropdown@^6.0.0:
version "6.0.2"
resolved "https://registry.yarnpkg.com/ember-basic-dropdown/-/ember-basic-dropdown-6.0.2.tgz#af47dbd544c605cf9cbc62225185616356aeef52"
integrity sha512-JgI/cy7eS/Y2WoQl7B2Mko/1aFTAlxr5d+KpQeH7rBKOFml7IQtLvhiDQrpU/FLkrQ9aLNEJtzwtDZV1xQxAKA==
@ -14800,7 +14799,7 @@ ember-cli@3.24.0:
workerpool "^6.0.3"
yam "^1.0.0"
ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1, ember-compatibility-helpers@^1.2.4, ember-compatibility-helpers@^1.2.5:
ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1, ember-compatibility-helpers@^1.2.5:
version "1.2.7"
resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.7.tgz#b4f138bba844f8f38f0b8f4d7e928841cd5e6591"
integrity sha512-BtkjulweiXo9c3yVWrtexw2dTmBrvavD/xixNC6TKOBdrixUwU+6nuOO9dufDWsMxoid7MvtmDpzc9+mE8PdaA==
@ -14925,7 +14924,7 @@ ember-drag-drop@0.4.8:
dependencies:
ember-cli-babel "^6.6.0"
ember-element-helper@^0.5.0, ember-element-helper@^0.5.5:
ember-element-helper@^0.5.0:
version "0.5.5"
resolved "https://registry.yarnpkg.com/ember-element-helper/-/ember-element-helper-0.5.5.tgz#4a9ecb4dce57ee7f5ceb868a53c7b498c729f056"
integrity sha512-Tu3hsI+/mjHBUvw62Qi+YDZtKkn59V66CjwbgfNTZZ7aHf4gFm1ow4zJ4WLnpnie8p9FvOmIUxwl5HvgPJIcFA==
@ -15036,7 +15035,7 @@ ember-in-element-polyfill@^1.0.1:
ember-cli-htmlbars "^5.3.1"
ember-cli-version-checker "^5.1.2"
ember-in-viewport@4.1.0:
ember-in-viewport@4.1.0, ember-in-viewport@~3.10.2:
version "4.1.0"
resolved "https://registry.yarnpkg.com/ember-in-viewport/-/ember-in-viewport-4.1.0.tgz#a9359a1e4a99d9d6ab32e926749dc131084ed896"
integrity sha512-3y6qWXuJPPc6vX2GfxWgtr+sDjb+bdZF9babstr0lTd8t8c1b42gJ13GaJqlylZIyZz2dEXFCimX9WAeudPv9g==
@ -15050,18 +15049,6 @@ ember-in-viewport@4.1.0:
intersection-observer-admin "~0.3.2"
raf-pool "~0.1.4"
ember-in-viewport@~3.10.2:
version "3.10.3"
resolved "https://registry.npmjs.org/ember-in-viewport/-/ember-in-viewport-3.10.3.tgz#317472bb82bed11f7895821b799349c6a7406e81"
integrity sha512-hSX7p+G6hJjZaY2BAqzyuiMP7QIHzQ4g0+ZBnEwAa8GMbILFAtzPx5A4XEX8wY6dSzhHB9n9jkcWZdmaML6q8A==
dependencies:
ember-auto-import "^1.11.2"
ember-cli-babel "^7.26.3"
ember-modifier "^2.1.0"
fast-deep-equal "^2.0.1"
intersection-observer-admin "~0.3.2"
raf-pool "~0.1.4"
ember-infinity@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/ember-infinity/-/ember-infinity-2.3.0.tgz#73fe13883c212147bfba4f0b2fe8c8d2a96887d9"
@ -15164,20 +15151,7 @@ ember-modifier@4.1.0, "ember-modifier@^2.1.2 || ^3.0.0 || ^4.0.0", "ember-modifi
ember-cli-normalize-entity-name "^1.0.0"
ember-cli-string-utils "^1.1.0"
ember-modifier@^2.1.0:
version "2.1.2"
resolved "https://registry.npmjs.org/ember-modifier/-/ember-modifier-2.1.2.tgz#62d18faedf972dcd9d34f90d5321fbc943d139b1"
integrity sha512-3Lsu1fV1sIGa66HOW07RZc6EHISwKt5VA5AUnFss2HX6OTfpxTJ2qvPctt2Yt0XPQXJ4G6BQasr/F35CX7UGJA==
dependencies:
ember-cli-babel "^7.22.1"
ember-cli-normalize-entity-name "^1.0.0"
ember-cli-string-utils "^1.1.0"
ember-cli-typescript "^3.1.3"
ember-compatibility-helpers "^1.2.4"
ember-destroyable-polyfill "^2.0.2"
ember-modifier-manager-polyfill "^1.2.0"
ember-modifier@^3.0.0, ember-modifier@^3.2.7:
ember-modifier@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-3.2.7.tgz#f2d35b7c867cbfc549e1acd8d8903c5ecd02ea4b"
integrity sha512-ezcPQhH8jUfcJQbbHji4/ZG/h0yyj1jRDknfYue/ypQS8fM8LrGcCMo0rjDZLzL1Vd11InjNs3BD7BdxFlzGoA==
@ -15335,14 +15309,6 @@ ember-source@3.24.0:
semver "^6.1.1"
silent-error "^1.1.1"
ember-style-modifier@^0.7.0:
version "0.7.0"
resolved "https://registry.npmjs.org/ember-style-modifier/-/ember-style-modifier-0.7.0.tgz#85b3dfd7e4bc2bd546df595f2dab4fb141cf7d87"
integrity sha512-iDzffiwJcb9j6gu3g8CxzZOTvRZ0BmLMEFl+uyqjiaj72VVND9+HbLyQRw1/ewPAtinhSktxxTTdwU/JO+stLw==
dependencies:
ember-cli-babel "^7.26.6"
ember-modifier "^3.0.0"
"ember-style-modifier@^0.8.0 || ^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/ember-style-modifier/-/ember-style-modifier-1.0.0.tgz#96e5d342a255d8c1cba1637779dbb1949322e139"
@ -15512,6 +15478,11 @@ emoji-regex@^8.0.0:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
emoji-regex@^9.2.2:
version "9.2.2"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
emojis-list@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
@ -19899,12 +19870,12 @@ iterate-value@^1.0.2:
es-get-iterator "^1.0.2"
iterate-iterator "^1.0.1"
jackspeak@2.1.1, jackspeak@^2.3.5:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.1.1.tgz#2a42db4cfbb7e55433c28b6f75d8b796af9669cd"
integrity sha512-juf9stUEwUaILepraGOWIJTLwg48bUnBmRqd2ln2Os1sW987zeoj/hzhbvRB95oMuS2ZTpjULmdwHNX4rzZIZw==
jackspeak@^2.3.5:
version "2.3.6"
resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8"
integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==
dependencies:
cliui "^8.0.1"
"@isaacs/cliui" "^8.0.2"
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"
@ -28164,6 +28135,15 @@ string-template@~0.2.1:
resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@ -28173,15 +28153,6 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
@ -28190,6 +28161,15 @@ string-width@^2.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
dependencies:
eastasianwidth "^0.2.0"
emoji-regex "^9.2.2"
strip-ansi "^7.0.1"
string-width@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.1.0.tgz#d994252935224729ea3719c49f7206dc9c46550a"
@ -28270,6 +28250,13 @@ stringify-entities@^2.0.0:
is-decimal "^1.0.2"
is-hexadecimal "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@ -28291,14 +28278,7 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.1.0:
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
@ -30736,6 +30716,15 @@ workerpool@^6.0.2, workerpool@^6.0.3, workerpool@^6.1.5, workerpool@^6.4.0:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^6.0.1:
version "6.2.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
@ -30745,14 +30734,14 @@ wrap-ansi@^6.0.1:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
ansi-styles "^6.1.0"
string-width "^5.0.1"
strip-ansi "^7.0.1"
wrap-ansi@^9.0.0:
version "9.0.0"