From 694ab1d32d7f2dc6e78740d0ea449e1f1a951a77 Mon Sep 17 00:00:00 2001 From: Naz Date: Mon, 24 Jul 2023 21:49:43 +0800 Subject: [PATCH] Added support for expansions in collection filters refs https://github.com/TryGhost/Arch/issues/46 - Similarly to post filters, collection filters now support both 'tag' and 'tags' nql filter keys when defining a filter for related tag slugs. For example, both `tag:avocado` and `tags:avocado` would both be valid collection filters that would filter by the same 'slug' property of the tags assigned to a post. - Along with these changes had to rework the tags property of the collection posts to match the shape used in post resources. Moved from: `tags: ['bacon', 'broc']` to `tags:[{slug: 'bacon'}, {slug: 'broc'}]` --- ghost/collections/package.json | 1 + ghost/collections/src/Collection.ts | 7 +- ghost/collections/src/CollectionPost.ts | 2 +- ghost/collections/src/CollectionsService.ts | 2 +- .../collections/src/events/PostAddedEvent.ts | 2 +- .../collections/src/events/PostEditedEvent.ts | 4 +- ghost/collections/test/Collection.test.ts | 48 ++ .../services/collections/PostsRepository.js | 4 +- .../__snapshots__/collections.test.js.snap | 686 ++++++++++++++++++ .../test/e2e-api/admin/collections.test.js | 39 +- .../src/ModelToDomainEventInterceptor.ts | 8 +- .../model-to-domain-event-interceptor.test.ts | 4 +- 12 files changed, 793 insertions(+), 14 deletions(-) diff --git a/ghost/collections/package.json b/ghost/collections/package.json index 8872dd09cd..42e7df68fe 100644 --- a/ghost/collections/package.json +++ b/ghost/collections/package.json @@ -30,6 +30,7 @@ "@tryghost/in-memory-repository": "0.0.0", "@tryghost/logging": "2.4.5", "@tryghost/nql": "0.11.0", + "@tryghost/nql-filter-expansions": "0.0.0", "@tryghost/tpl": "0.1.25", "bson-objectid": "2.0.4" }, diff --git a/ghost/collections/src/Collection.ts b/ghost/collections/src/Collection.ts index d6b2c1e370..3975d4aaca 100644 --- a/ghost/collections/src/Collection.ts +++ b/ghost/collections/src/Collection.ts @@ -1,7 +1,8 @@ import {UniqueChecker} from './UniqueChecker'; import {ValidationError} from '@tryghost/errors'; import tpl from '@tryghost/tpl'; -import nql = require('@tryghost/nql'); +import nql from '@tryghost/nql'; +import {posts as postExpansions} from '@tryghost/nql-filter-expansions'; import {CollectionPost} from './CollectionPost'; import ObjectID from 'bson-objectid'; @@ -95,7 +96,9 @@ export class Collection { } postMatchesFilter(post: CollectionPost) { - const filterNql = nql(this.filter); + const filterNql = nql(this.filter, { + expansions: postExpansions + }); return filterNql.queryJSON(post); } diff --git a/ghost/collections/src/CollectionPost.ts b/ghost/collections/src/CollectionPost.ts index 5a30f76097..516e77e697 100644 --- a/ghost/collections/src/CollectionPost.ts +++ b/ghost/collections/src/CollectionPost.ts @@ -3,5 +3,5 @@ export type CollectionPost = { id: string; featured?: boolean; published_at?: Date; - tags?: string[]; + tags?: Array<{slug: string}>; }; diff --git a/ghost/collections/src/CollectionsService.ts b/ghost/collections/src/CollectionsService.ts index 0401eb6d4d..d7a8f77858 100644 --- a/ghost/collections/src/CollectionsService.ts +++ b/ghost/collections/src/CollectionsService.ts @@ -48,7 +48,7 @@ type CollectionPostListItemDTO = { created_at: Date; updated_at: Date; published_at: Date, - tags?: string[]; + tags?: Array<{slug: string}>; } type ManualCollection = { diff --git a/ghost/collections/src/events/PostAddedEvent.ts b/ghost/collections/src/events/PostAddedEvent.ts index b036662470..1d00bf6a86 100644 --- a/ghost/collections/src/events/PostAddedEvent.ts +++ b/ghost/collections/src/events/PostAddedEvent.ts @@ -2,7 +2,7 @@ type PostData = { id: string; featured: boolean; published_at: Date; - tags: string[]; + tags: Array<{slug: string}>; }; export class PostAddedEvent { diff --git a/ghost/collections/src/events/PostEditedEvent.ts b/ghost/collections/src/events/PostEditedEvent.ts index bffe43f811..1711d6f89d 100644 --- a/ghost/collections/src/events/PostEditedEvent.ts +++ b/ghost/collections/src/events/PostEditedEvent.ts @@ -6,7 +6,7 @@ type PostEditData = { status: string; featured: boolean; published_at: Date; - tags: string[]; + tags: Array<{slug: string}>; }, previous: { id: string; @@ -14,7 +14,7 @@ type PostEditData = { status: string; featured: boolean; published_at: Date; - tags: string[]; + tags: Array<{slug: string}>; } }; diff --git a/ghost/collections/test/Collection.test.ts b/ghost/collections/test/Collection.test.ts index c3a0e30f42..f809395b17 100644 --- a/ghost/collections/test/Collection.test.ts +++ b/ghost/collections/test/Collection.test.ts @@ -363,5 +363,53 @@ describe('Collection', function () { assert.ok(collection.postMatchesFilter(featuredPost), 'Post should match the filter'); assert.ok(!collection.postMatchesFilter(nonFeaturedPost), 'Post should not match the filter'); }); + + it('Can match a post with a tag filter', async function () { + const collection = await Collection.create({ + title: 'Testing filtering posts', + type: 'automatic', + filter: 'tag:avocado' + }); + + const avocadoPost = { + id: '0', + tags: [{ + slug: 'avocado' + }] + }; + const nonAvocadoPost = { + id: '1', + tags: [{ + slug: 'not-avocado' + }] + }; + + assert.ok(collection.postMatchesFilter(avocadoPost), 'Post should match the filter'); + assert.ok(!collection.postMatchesFilter(nonAvocadoPost), 'Post should not match the filter'); + }); + + it('Can match a post with a tags filter', async function () { + const collection = await Collection.create({ + title: 'Testing filtering posts', + type: 'automatic', + filter: 'tags:avocado' + }); + + const avocadoPost = { + id: '0', + tags: [{ + slug: 'avocado' + }] + }; + const nonAvocadoPost = { + id: '1', + tags: [{ + slug: 'not-avocado' + }] + }; + + assert.ok(collection.postMatchesFilter(avocadoPost), 'Post should match the filter'); + assert.ok(!collection.postMatchesFilter(nonAvocadoPost), 'Post should not match the filter'); + }); }); }); diff --git a/ghost/core/core/server/services/collections/PostsRepository.js b/ghost/core/core/server/services/collections/PostsRepository.js index e774395c04..2371c60059 100644 --- a/ghost/core/core/server/services/collections/PostsRepository.js +++ b/ghost/core/core/server/services/collections/PostsRepository.js @@ -20,7 +20,9 @@ class PostsRepository { id: postJson.id, featured: postJson.featured, published_at: this.moment(postJson.published_at).toISOString(true), - tags: postJson.tags.map(tag => tag.slug) + tags: postJson.tags.map(tag => ({ + slug: tag.slug + })) }; }); } diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/collections.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/collections.test.js.snap index eed3b24ac2..e283bf752f 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/collections.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/collections.test.js.snap @@ -457,6 +457,692 @@ Object { } `; +exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a tag filter, checking filter aliases 1: [body] 1`] = ` +Object { + "collections": Array [ + Object { + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "BACON!", + "feature_image": null, + "filter": "tag:['bacon']", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "posts": Array [ + Object { + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "sort_order": 0, + }, + Object { + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "sort_order": 1, + }, + ], + "slug": "bacon-tag-expansion", + "title": "Test Collection with tag filter alias", + "type": "automatic", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + }, + ], +} +`; + +exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a tag filter, checking filter aliases 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "http://127.0.0.1:2369", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "404", + "content-type": "application/json; charset=utf-8", + "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/collections\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-cache-invalidate": "/*", + "x-powered-by": "Express", +} +`; + +exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a tag filter, checking filter aliases 3: [body] 1`] = ` +Object { + "meta": Object { + "pagination": Object { + "limit": 15, + "next": null, + "page": 1, + "pages": 1, + "prev": null, + "total": 2, + }, + }, + "posts": Array [ + Object { + "authors": Any, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "comment_id": Any, + "count": Object { + "clicks": 0, + "negative_feedback": 0, + "positive_feedback": 0, + }, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "custom_excerpt": null, + "custom_template": null, + "email": null, + "email_only": false, + "email_segment": "all", + "email_subject": null, + "excerpt": "HTML Ipsum Presents + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum o", + "feature_image": "http://127.0.0.1:2369/content/images/2018/hey.jpg", + "feature_image_alt": null, + "feature_image_caption": null, + "featured": false, + "frontmatter": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • Aliquam tincidunt mauris eu risus.
#header h1 a{display: block;width: 300px;height: 80px;}
\\"}]],\\"sections\\":[[10,0]]}", + "newsletter": null, + "og_description": null, + "og_image": null, + "og_title": null, + "post_revisions": Any, + "primary_author": Any, + "primary_tag": Any, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "reading_time": 1, + "slug": "ghostly-kitchen-sink", + "status": "published", + "tags": Array [ + Object { + "accent_color": null, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "description", + "feature_image": "https://example.com/super_photo.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "name": "kitchen sink", + "og_description": null, + "og_image": null, + "og_title": null, + "slug": "kitchen-sink", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": "http://127.0.0.1:2369/tag/kitchen-sink/", + "visibility": "public", + }, + Object { + "accent_color": null, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "description", + "feature_image": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "name": "bacon", + "og_description": null, + "og_image": null, + "og_title": null, + "slug": "bacon", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": "http://127.0.0.1:2369/tag/bacon/", + "visibility": "public", + }, + ], + "tiers": Array [ + Object { + "active": true, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "currency": null, + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "monthly_price": null, + "monthly_price_id": null, + "name": "Free", + "slug": "free", + "trial_days": 0, + "type": "free", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "visibility": "public", + "welcome_page_url": null, + "yearly_price": null, + "yearly_price_id": null, + }, + Object { + "active": true, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "currency": "usd", + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "monthly_price": 500, + "monthly_price_id": null, + "name": "Default Product", + "slug": "default-product", + "trial_days": 0, + "type": "paid", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "visibility": "public", + "welcome_page_url": null, + "yearly_price": 5000, + "yearly_price_id": null, + }, + ], + "title": "Ghostly Kitchen Sink", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": Any, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "public", + }, + Object { + "authors": Any, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "comment_id": Any, + "count": Object { + "clicks": 0, + "negative_feedback": 0, + "positive_feedback": 0, + }, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "custom_excerpt": "This is my custom excerpt!", + "custom_template": null, + "email": null, + "email_only": false, + "email_segment": "all", + "email_subject": null, + "excerpt": "This is my custom excerpt!", + "feature_image": "https://example.com/super_photo.jpg", + "feature_image_alt": null, + "feature_image_caption": null, + "featured": false, + "frontmatter": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • Aliquam tincidunt mauris eu risus.
#header h1 a{display: block;width: 300px;height: 80px;}
\\"}]],\\"sections\\":[[10,0]]}", + "newsletter": null, + "og_description": null, + "og_image": null, + "og_title": null, + "post_revisions": Any, + "primary_author": Any, + "primary_tag": Any, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "reading_time": 1, + "slug": "html-ipsum", + "status": "published", + "tags": Array [ + Object { + "accent_color": null, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "description", + "feature_image": "https://example.com/super_photo.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "name": "kitchen sink", + "og_description": null, + "og_image": null, + "og_title": null, + "slug": "kitchen-sink", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": "http://127.0.0.1:2369/tag/kitchen-sink/", + "visibility": "public", + }, + Object { + "accent_color": null, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "description", + "feature_image": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "name": "bacon", + "og_description": null, + "og_image": null, + "og_title": null, + "slug": "bacon", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": "http://127.0.0.1:2369/tag/bacon/", + "visibility": "public", + }, + ], + "tiers": Array [ + Object { + "active": true, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "currency": null, + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "monthly_price": null, + "monthly_price_id": null, + "name": "Free", + "slug": "free", + "trial_days": 0, + "type": "free", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "visibility": "public", + "welcome_page_url": null, + "yearly_price": null, + "yearly_price_id": null, + }, + Object { + "active": true, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "currency": "usd", + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "monthly_price": 500, + "monthly_price_id": null, + "name": "Default Product", + "slug": "default-product", + "trial_days": 0, + "type": "paid", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "visibility": "public", + "welcome_page_url": null, + "yearly_price": 5000, + "yearly_price_id": null, + }, + ], + "title": "HTML Ipsum", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": Any, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "public", + }, + ], +} +`; + +exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a tag filter, checking filter aliases 4: [headers] 1`] = ` +Object { + "access-control-allow-origin": "http://127.0.0.1:2369", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "14189", + "content-type": "application/json; charset=utf-8", + "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + +exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a tags filter 1: [body] 1`] = ` +Object { + "collections": Array [ + Object { + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "BACON!", + "feature_image": null, + "filter": "tags:['bacon']", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "posts": Array [ + Object { + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "sort_order": 0, + }, + Object { + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "sort_order": 1, + }, + ], + "slug": "bacon", + "title": "Test Collection with tags filter", + "type": "automatic", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + }, + ], +} +`; + +exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a tags filter 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "http://127.0.0.1:2369", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "386", + "content-type": "application/json; charset=utf-8", + "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/collections\\\\/\\[a-f0-9\\]\\{24\\}\\\\//, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-cache-invalidate": "/*", + "x-powered-by": "Express", +} +`; + +exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a tags filter 3: [body] 1`] = ` +Object { + "meta": Object { + "pagination": Object { + "limit": 15, + "next": null, + "page": 1, + "pages": 1, + "prev": null, + "total": 2, + }, + }, + "posts": Array [ + Object { + "authors": Any, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "comment_id": Any, + "count": Object { + "clicks": 0, + "negative_feedback": 0, + "positive_feedback": 0, + }, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "custom_excerpt": null, + "custom_template": null, + "email": null, + "email_only": false, + "email_segment": "all", + "email_subject": null, + "excerpt": "HTML Ipsum Presents + +Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum o", + "feature_image": "http://127.0.0.1:2369/content/images/2018/hey.jpg", + "feature_image_alt": null, + "feature_image_caption": null, + "featured": false, + "frontmatter": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • Aliquam tincidunt mauris eu risus.
#header h1 a{display: block;width: 300px;height: 80px;}
\\"}]],\\"sections\\":[[10,0]]}", + "newsletter": null, + "og_description": null, + "og_image": null, + "og_title": null, + "post_revisions": Any, + "primary_author": Any, + "primary_tag": Any, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "reading_time": 1, + "slug": "ghostly-kitchen-sink", + "status": "published", + "tags": Array [ + Object { + "accent_color": null, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "description", + "feature_image": "https://example.com/super_photo.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "name": "kitchen sink", + "og_description": null, + "og_image": null, + "og_title": null, + "slug": "kitchen-sink", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": "http://127.0.0.1:2369/tag/kitchen-sink/", + "visibility": "public", + }, + Object { + "accent_color": null, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "description", + "feature_image": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "name": "bacon", + "og_description": null, + "og_image": null, + "og_title": null, + "slug": "bacon", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": "http://127.0.0.1:2369/tag/bacon/", + "visibility": "public", + }, + ], + "tiers": Array [ + Object { + "active": true, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "currency": null, + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "monthly_price": null, + "monthly_price_id": null, + "name": "Free", + "slug": "free", + "trial_days": 0, + "type": "free", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "visibility": "public", + "welcome_page_url": null, + "yearly_price": null, + "yearly_price_id": null, + }, + Object { + "active": true, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "currency": "usd", + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "monthly_price": 500, + "monthly_price_id": null, + "name": "Default Product", + "slug": "default-product", + "trial_days": 0, + "type": "paid", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "visibility": "public", + "welcome_page_url": null, + "yearly_price": 5000, + "yearly_price_id": null, + }, + ], + "title": "Ghostly Kitchen Sink", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": Any, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "public", + }, + Object { + "authors": Any, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "comment_id": Any, + "count": Object { + "clicks": 0, + "negative_feedback": 0, + "positive_feedback": 0, + }, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "custom_excerpt": "This is my custom excerpt!", + "custom_template": null, + "email": null, + "email_only": false, + "email_segment": "all", + "email_subject": null, + "excerpt": "This is my custom excerpt!", + "feature_image": "https://example.com/super_photo.jpg", + "feature_image_alt": null, + "feature_image_caption": null, + "featured": false, + "frontmatter": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"

HTML Ipsum Presents

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.

Header Level 2

  1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  2. Aliquam tincidunt mauris eu risus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.

Header Level 3

  • Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
  • Aliquam tincidunt mauris eu risus.
#header h1 a{display: block;width: 300px;height: 80px;}
\\"}]],\\"sections\\":[[10,0]]}", + "newsletter": null, + "og_description": null, + "og_image": null, + "og_title": null, + "post_revisions": Any, + "primary_author": Any, + "primary_tag": Any, + "published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "reading_time": 1, + "slug": "html-ipsum", + "status": "published", + "tags": Array [ + Object { + "accent_color": null, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "description", + "feature_image": "https://example.com/super_photo.jpg", + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "name": "kitchen sink", + "og_description": null, + "og_image": null, + "og_title": null, + "slug": "kitchen-sink", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": "http://127.0.0.1:2369/tag/kitchen-sink/", + "visibility": "public", + }, + Object { + "accent_color": null, + "canonical_url": null, + "codeinjection_foot": null, + "codeinjection_head": null, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "description": "description", + "feature_image": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "meta_description": null, + "meta_title": null, + "name": "bacon", + "og_description": null, + "og_image": null, + "og_title": null, + "slug": "bacon", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": "http://127.0.0.1:2369/tag/bacon/", + "visibility": "public", + }, + ], + "tiers": Array [ + Object { + "active": true, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "currency": null, + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "monthly_price": null, + "monthly_price_id": null, + "name": "Free", + "slug": "free", + "trial_days": 0, + "type": "free", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "visibility": "public", + "welcome_page_url": null, + "yearly_price": null, + "yearly_price_id": null, + }, + Object { + "active": true, + "created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "currency": "usd", + "description": null, + "id": StringMatching /\\[a-f0-9\\]\\{24\\}/, + "monthly_price": 500, + "monthly_price_id": null, + "name": "Default Product", + "slug": "default-product", + "trial_days": 0, + "type": "paid", + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "visibility": "public", + "welcome_page_url": null, + "yearly_price": 5000, + "yearly_price_id": null, + }, + ], + "title": "HTML Ipsum", + "twitter_description": null, + "twitter_image": null, + "twitter_title": null, + "updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/, + "url": Any, + "uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/, + "visibility": "public", + }, + ], +} +`; + +exports[`Collections API Automatic Collection Filtering Creates an automatic Collection with a tags filter 4: [headers] 1`] = ` +Object { + "access-control-allow-origin": "http://127.0.0.1:2369", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "14189", + "content-type": "application/json; charset=utf-8", + "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; + exports[`Collections API Browse Can browse Collections 1: [body] 1`] = ` Object { "collections": Array [ diff --git a/ghost/core/test/e2e-api/admin/collections.test.js b/ghost/core/test/e2e-api/admin/collections.test.js index 3435b84fff..6b0cf148b6 100644 --- a/ghost/core/test/e2e-api/admin/collections.test.js +++ b/ghost/core/test/e2e-api/admin/collections.test.js @@ -357,9 +357,9 @@ describe('Collections API', function () { }); }); - it('Creates an automatic Collection with a tag filter', async function () { + it('Creates an automatic Collection with a tags filter', async function () { const collection = { - title: 'Test Collection with tag filter', + title: 'Test Collection with tags filter', slug: 'bacon', description: 'BACON!', type: 'automatic', @@ -391,5 +391,40 @@ describe('Collections API', function () { posts: new Array(2).fill(matchPostShallowIncludes) }); }); + + it('Creates an automatic Collection with a tag filter, checking filter aliases', async function () { + const collection = { + title: 'Test Collection with tag filter alias', + slug: 'bacon-tag-expansion', + description: 'BACON!', + type: 'automatic', + filter: 'tag:[\'bacon\']' + }; + + await agent + .post('/collections/') + .body({ + collections: [collection] + }) + .expectStatus(201) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag, + location: anyLocationFor('collections') + }) + .matchBodySnapshot({ + collections: [buildMatcher(2)] + }); + + await agent.get(`posts/?collection=${collection.slug}`) + .expectStatus(200) + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }) + .matchBodySnapshot({ + posts: new Array(2).fill(matchPostShallowIncludes) + }); + }); }); }); diff --git a/ghost/model-to-domain-event-interceptor/src/ModelToDomainEventInterceptor.ts b/ghost/model-to-domain-event-interceptor/src/ModelToDomainEventInterceptor.ts index 3377eacaeb..4bb59a5375 100644 --- a/ghost/model-to-domain-event-interceptor/src/ModelToDomainEventInterceptor.ts +++ b/ghost/model-to-domain-event-interceptor/src/ModelToDomainEventInterceptor.ts @@ -69,7 +69,9 @@ export class ModelToDomainEventInterceptor { status: data.attributes.status, featured: data.attributes.featured, published_at: data.attributes.published_at, - tags: data.relations?.tags?.models.map((tag: any) => (tag.get('slug'))) + tags: data.relations?.tags?.models.map((tag: any) => ({ + slug: tag.get('slug') + })) }, // @NOTE: this will need to represent the previous state of the post // will be needed to optimize the query for the collection @@ -79,7 +81,9 @@ export class ModelToDomainEventInterceptor { status: data._previousAttributes?.status, featured: data._previousAttributes?.featured, published_at: data._previousAttributes?.published_at, - tags: data._previousRelations?.tags?.models.map((tag: any) => (tag.get('slug'))) + tags: data._previousRelations?.tags?.models.map((tag: any) => ({ + slug: tag.get('slug') + })) } }); break; diff --git a/ghost/model-to-domain-event-interceptor/test/model-to-domain-event-interceptor.test.ts b/ghost/model-to-domain-event-interceptor/test/model-to-domain-event-interceptor.test.ts index 1342c130ef..a1690b0359 100644 --- a/ghost/model-to-domain-event-interceptor/test/model-to-domain-event-interceptor.test.ts +++ b/ghost/model-to-domain-event-interceptor/test/model-to-domain-event-interceptor.test.ts @@ -84,8 +84,8 @@ describe('ModelToDomainEventInterceptor', function () { assert.equal(event.data.current.status, 'draft'); assert.equal(event.data.previous.status, 'published'); - assert.equal(event.data.current.tags[0], 'tag-current-slug'); - assert.equal(event.data.previous.tags[0], 'tag-previous-slug'); + assert.deepEqual(event.data.current.tags[0], {slug: 'tag-current-slug'}); + assert.deepEqual(event.data.previous.tags[0], {slug: 'tag-previous-slug'}); interceptedEvent = event; });