diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-11-28 17:49:26 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-28 18:04:24 +0100 |
| commit | 29a1dec4de0aa7ba64ef068a83b1b8589fbc3ad0 (patch) | |
| tree | 8db871542e878e9fdf589bccd1be7b5ed1378f72 | |
| parent | f564d181bc428e25a02bf1d98c4449a6b3eb8e9e (diff) | |
fix(services,types): make queries and types coherent for Topic
* some nodes was missing in topicQuery
* a node was mispelled in topicsListQuery
* add tests for all topics fetchers
19 files changed, 420 insertions, 7 deletions
diff --git a/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.test.ts b/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.test.ts new file mode 100644 index 0000000..2e3bbb2 --- /dev/null +++ b/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.test.ts @@ -0,0 +1,26 @@ +import { afterEach, describe, expect, it } from '@jest/globals'; +import { wpTopicsFixture } from '../../../../../tests/fixtures'; +import { fetchAllTopicsSlugs } from './fetch-all-topics-slugs'; + +describe('fetch-all-topics-slugs', () => { + afterEach(() => { + window.history.replaceState({}, '', '/'); + }); + + it('returns the WordPress topics using GraphQL', async () => { + const result = await fetchAllTopicsSlugs(wpTopicsFixture.length); + + expect.assertions(1); + + expect(result).toStrictEqual(wpTopicsFixture.map((post) => post.slug)); + }); + + it('rejects with an error when no topics are found', async () => { + window.history.replaceState({}, '', '/?error=true'); + expect.assertions(1); + + await expect(async () => + fetchAllTopicsSlugs(wpTopicsFixture.length) + ).rejects.toEqual(new Error('Unable to find the topics slugs.')); + }); +}); diff --git a/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.ts b/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.ts index 1df0039..8af2757 100644 --- a/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.ts +++ b/src/services/graphql/fetchers/topics/fetch-all-topics-slugs.ts @@ -1,7 +1,7 @@ import type { GraphQLNodes, Nullable, SlugNode } from '../../../../types'; import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers'; -type TopicsSlugsResponse = { +export type TopicsSlugsResponse = { topics: Nullable<GraphQLNodes<SlugNode>>; }; diff --git a/src/services/graphql/fetchers/topics/fetch-topic.test.ts b/src/services/graphql/fetchers/topics/fetch-topic.test.ts new file mode 100644 index 0000000..5fae313 --- /dev/null +++ b/src/services/graphql/fetchers/topics/fetch-topic.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from '@jest/globals'; +import { wpTopicsFixture } from '../../../../../tests/fixtures'; +import { fetchTopic } from './fetch-topic'; + +describe('fetch-topic', () => { + it('returns a topic by slug', async () => { + const result = await fetchTopic(wpTopicsFixture[2].slug); + + expect.assertions(1); + + expect(result).toStrictEqual(wpTopicsFixture[2]); + }); + + it('rejects with an error when the slug does not exist', async () => { + const slug = '/inexistent-slug'; + + expect.assertions(1); + + await expect(async () => fetchTopic(slug)).rejects.toEqual( + new Error(`No topic found for the following slug ${slug}.`) + ); + }); +}); diff --git a/src/services/graphql/fetchers/topics/fetch-topic.ts b/src/services/graphql/fetchers/topics/fetch-topic.ts index efc1d9e..9c01096 100644 --- a/src/services/graphql/fetchers/topics/fetch-topic.ts +++ b/src/services/graphql/fetchers/topics/fetch-topic.ts @@ -1,7 +1,7 @@ import type { Nullable, WPTopic } from '../../../../types'; import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers'; -type TopicResponse = { +export type TopicResponse = { topic: Nullable<WPTopic>; }; @@ -55,6 +55,8 @@ const topicQuery = `query Topic($slug: ID!) { afterMore beforeMore } + databaseId + date featuredImage { node { altText @@ -66,6 +68,7 @@ const topicQuery = `query Topic($slug: ID!) { title } } + modified seo { metaDesc title diff --git a/src/services/graphql/fetchers/topics/fetch-topics-count.test.ts b/src/services/graphql/fetchers/topics/fetch-topics-count.test.ts new file mode 100644 index 0000000..0e3bb90 --- /dev/null +++ b/src/services/graphql/fetchers/topics/fetch-topics-count.test.ts @@ -0,0 +1,26 @@ +import { afterEach, describe, expect, it } from '@jest/globals'; +import { wpTopicsFixture } from '../../../../../tests/fixtures'; +import { fetchTopicsCount } from './fetch-topics-count'; + +describe('fetch-topics-count', () => { + afterEach(() => { + window.history.replaceState({}, '', '/'); + }); + + it('returns the WordPress topics count using GraphQL', async () => { + const result = await fetchTopicsCount(); + + expect.assertions(1); + + expect(result).toBe(wpTopicsFixture.length); + }); + + it('rejects with an error when no topics are found', async () => { + window.history.replaceState({}, '', '/?error=true'); + expect.assertions(1); + + await expect(async () => fetchTopicsCount()).rejects.toEqual( + new Error('Unable to find the total number of topics.') + ); + }); +}); diff --git a/src/services/graphql/fetchers/topics/fetch-topics-count.ts b/src/services/graphql/fetchers/topics/fetch-topics-count.ts index 868b01e..0f8bdfc 100644 --- a/src/services/graphql/fetchers/topics/fetch-topics-count.ts +++ b/src/services/graphql/fetchers/topics/fetch-topics-count.ts @@ -5,7 +5,7 @@ import type { } from '../../../../types'; import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers'; -type TopicsCountResponse = { +export type TopicsCountResponse = { topics: Nullable<{ pageInfo: Pick<GraphQLPageInfo, 'total'>; }>; diff --git a/src/services/graphql/fetchers/topics/fetch-topics-list.test.ts b/src/services/graphql/fetchers/topics/fetch-topics-list.test.ts new file mode 100644 index 0000000..b841974 --- /dev/null +++ b/src/services/graphql/fetchers/topics/fetch-topics-list.test.ts @@ -0,0 +1,26 @@ +import { afterEach, describe, expect, it } from '@jest/globals'; +import { wpTopicsFixture } from '../../../../../tests/fixtures'; +import { fetchTopicsList } from './fetch-topics-list'; + +describe('fetch-topics-list', () => { + afterEach(() => { + window.history.replaceState({}, '', '/'); + }); + + it('returns the WordPress topics using GraphQL', async () => { + const result = await fetchTopicsList({}); + + expect.assertions(1); + + expect(result.pageInfo.total).toBe(wpTopicsFixture.length); + }); + + it('rejects with an error when no topics are found', async () => { + window.history.replaceState({}, '', '/?error=true'); + expect.assertions(1); + + await expect(async () => + fetchTopicsList({ where: { title: 'inexistent-title' } }) + ).rejects.toEqual(new Error('No topics found.')); + }); +}); diff --git a/src/services/graphql/fetchers/topics/fetch-topics-list.ts b/src/services/graphql/fetchers/topics/fetch-topics-list.ts index 1bc2e38..2ede721 100644 --- a/src/services/graphql/fetchers/topics/fetch-topics-list.ts +++ b/src/services/graphql/fetchers/topics/fetch-topics-list.ts @@ -8,7 +8,7 @@ import type { } from '../../../../types'; import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers'; -type TopicsListResponse = { +export type TopicsListResponse = { topics: Nullable<GraphQLConnection<WPTopicPreview>>; }; @@ -34,7 +34,7 @@ const topicsListQuery = `query TopicsList($after: String, $before: String, $firs height width } - slug + sourceUrl title } } diff --git a/src/types/data.ts b/src/types/data.ts index c582709..7b9a879 100644 --- a/src/types/data.ts +++ b/src/types/data.ts @@ -123,7 +123,7 @@ export type WPThematicPreview = Pick< >; type WPAcfTopics = { - officialWebsite: string; + officialWebsite: Nullable<string>; postsInTopic: Nullable<WPPostPreview[]>; }; diff --git a/tests/fixtures/index.ts b/tests/fixtures/index.ts index abe49be..2ae0185 100644 --- a/tests/fixtures/index.ts +++ b/tests/fixtures/index.ts @@ -1,3 +1,4 @@ export * from './wp-comments.fixture'; export * from './wp-posts.fixture'; export * from './wp-thematics.fixture'; +export * from './wp-topics.fixture'; diff --git a/tests/fixtures/wp-topics.fixture.ts b/tests/fixtures/wp-topics.fixture.ts new file mode 100644 index 0000000..d790dea --- /dev/null +++ b/tests/fixtures/wp-topics.fixture.ts @@ -0,0 +1,118 @@ +import type { WPTopic } from '../../src/types'; + +export const wpTopicsFixture: WPTopic[] = [ + { + acfTopics: null, + contentParts: { + afterMore: + 'Ut nemo quia repellendus aut aliquid voluptatum rerum incidunt in. Voluptatem modi quia enim est molestiae at facere non. Sunt dolores debitis ipsam praesentium et unde quas veritatis. Mollitia illo corporis aut quia distinctio.', + beforeMore: + 'Veniam cumque ut nulla. Aspernatur quos similique nesciunt. Dignissimos maxime ex iure non ut nemo ea ducimus. Vero sit sed quis. Et sed facilis quia possimus animi. Debitis illum et et dolores minus.', + }, + databaseId: 1, + date: '2022-10-03', + featuredImage: null, + modified: '2022-10-04', + seo: { + metaDesc: 'Et magnam id dicta iste molestiae illum officia provident.', + title: 'est dolores tempore', + }, + slug: '/eveniet-ut-quis', + title: 'eveniet ut quis', + }, + { + acfTopics: { + officialWebsite: 'https://example.test', + postsInTopic: null, + }, + contentParts: { + afterMore: + 'Est odit et quia qui vero nemo necessitatibus. Culpa molestias illo earum aut inventore amet aut numquam. Nulla qui delectus aperiam ipsa voluptates expedita nihil. Sit explicabo sunt exercitationem nesciunt quae.', + beforeMore: + 'Ut doloremque nisi facilis officiis officiis et id ipsam. Aut ex quaerat autem a libero aut suscipit illum. Quasi quia commodi reiciendis. Voluptas ratione totam qui possimus fuga.', + }, + databaseId: 2, + date: '2022-10-06', + featuredImage: { + node: { + altText: null, + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: null, + }, + }, + modified: '2022-10-06', + seo: { + metaDesc: + 'Consequuntur aut excepturi dicta et qui quasi enim voluptas cum.', + title: 'aliquid consectetur corporis', + }, + slug: '/nisi-ut-facere', + title: 'nisi ut facere', + }, + { + acfTopics: { + officialWebsite: null, + postsInTopic: null, + }, + databaseId: 3, + contentParts: { + afterMore: + 'Quod distinctio soluta quam repudiandae assumenda numquam quos. Aliquid sint cupiditate voluptates cum fugiat natus ut. Aliquid totam beatae eveniet dicta itaque et asperiores quis. Sapiente impedit maiores illum. Totam magnam unde dolor eaque.', + beforeMore: + 'Quibusdam pariatur hic voluptas sit ipsam sed. Quisquam nisi est. Optio eaque sunt omnis eos repellat nostrum. Voluptatem eaque vero neque maxime consequatur sunt eligendi suscipit molestias.', + }, + date: '2022-08-27', + featuredImage: null, + modified: '2022-08-30', + seo: { + metaDesc: 'Sed impedit quisquam voluptate voluptatem consequuntur.', + title: 'porro autem sunt', + }, + slug: '/iusto-non-consectetur', + title: 'iusto non consectetur', + }, + { + acfTopics: { + officialWebsite: null, + postsInTopic: [ + { + acfPosts: null, + commentCount: 4, + contentParts: { + beforeMore: + 'Incidunt et sit ea nostrum rerum consectetur quia. Et quia quibusdam dolor sit itaque voluptas.', + }, + databaseId: 15, + date: '2021-09-10', + featuredImage: null, + info: { + wordsCount: 468, + }, + modified: '2021-09-15', + slug: '/possimus-temporibus-magni', + title: 'possimus temporibus magni', + }, + ], + }, + contentParts: { + afterMore: + 'Eius vel neque est minus eius nihil est aperiam dolorem. Vel vel vero esse dolore sapiente quia et. Iusto et ratione est quia eveniet porro tenetur. Adipisci dicta ut delectus dolor sit tenetur molestias.', + beforeMore: + 'Molestias et veniam impedit et ut sunt aliquid id. Molestiae debitis ipsa reprehenderit aperiam totam quia. Accusamus iste sit corrupti voluptatibus consequatur odio error. Asperiores ipsa error nulla perspiciatis accusantium inventore.', + }, + databaseId: 4, + date: '2022-10-07', + featuredImage: null, + modified: '2022-10-10', + seo: { + metaDesc: 'Quaerat non ex inventore officiis amet.', + title: 'in alias ipsa', + }, + slug: '/non-repellendus-ipsam', + title: 'non repellendus ipsam', + }, +]; diff --git a/tests/msw/handlers/index.ts b/tests/msw/handlers/index.ts index e42a0eb..85a2300 100644 --- a/tests/msw/handlers/index.ts +++ b/tests/msw/handlers/index.ts @@ -1,9 +1,11 @@ import { commentsHandlers } from './comments'; import { postsHandlers } from './posts'; import { thematicsHandlers } from './thematics'; +import { topicsHandlers } from './topics'; export const handlers = [ ...commentsHandlers, ...postsHandlers, ...thematicsHandlers, + ...topicsHandlers, ]; diff --git a/tests/msw/handlers/topics/index.ts b/tests/msw/handlers/topics/index.ts new file mode 100644 index 0000000..e29694b --- /dev/null +++ b/tests/msw/handlers/topics/index.ts @@ -0,0 +1,11 @@ +import { topicHandler } from './topic.handler'; +import { topicsCountHandler } from './topics-count.handler'; +import { topicsListHandler } from './topics-list.handler'; +import { topicsSlugsHandler } from './topics-slugs.handler'; + +export const topicsHandlers = [ + topicHandler, + topicsCountHandler, + topicsListHandler, + topicsSlugsHandler, +]; diff --git a/tests/msw/handlers/topics/topic.handler.ts b/tests/msw/handlers/topics/topic.handler.ts new file mode 100644 index 0000000..5df00ea --- /dev/null +++ b/tests/msw/handlers/topics/topic.handler.ts @@ -0,0 +1,21 @@ +import { type ExecutionResult, graphql as executeGraphql } from 'graphql'; +import { HttpResponse, graphql } from 'msw'; +import type { TopicResponse } from '../../../../src/services/graphql'; +import { wpTopicsFixture } from '../../../fixtures'; +import { schema } from '../../schema'; + +export const topicHandler = graphql.query< + TopicResponse, + Record<'slug', string> +>('Topic', async ({ query, variables }) => { + const { data, errors } = (await executeGraphql({ + schema, + source: query, + variableValues: variables, + rootValue: { + topic: wpTopicsFixture.find((wpTopic) => wpTopic.slug === variables.slug), + }, + })) as ExecutionResult<TopicResponse>; + + return HttpResponse.json({ data, errors }); +}); diff --git a/tests/msw/handlers/topics/topics-count.handler.ts b/tests/msw/handlers/topics/topics-count.handler.ts new file mode 100644 index 0000000..7e3dab9 --- /dev/null +++ b/tests/msw/handlers/topics/topics-count.handler.ts @@ -0,0 +1,43 @@ +import { type ExecutionResult, graphql as executeGraphql } from 'graphql'; +import { HttpResponse, graphql } from 'msw'; +import type { TopicsCountResponse } from '../../../../src/services/graphql'; +import type { GraphQLPostWhere } from '../../../../src/types'; +import { wpTopicsFixture } from '../../../fixtures'; +import { getConnection } from '../../../utils/graphql'; +import { schema } from '../../schema'; + +export const topicsCountHandler = graphql.query< + TopicsCountResponse, + GraphQLPostWhere +>('TopicsCount', async ({ query, variables }) => { + const pageParams = new URLSearchParams(window.location.search); + const isError = pageParams.get('error') === 'true'; + + if (isError) return HttpResponse.json({ data: { topics: null } }); + + const { data, errors } = (await executeGraphql({ + schema, + source: query, + variableValues: variables, + rootValue: { + topics({ search, title }: typeof variables) { + const filteredTopicsByTitle = title + ? wpTopicsFixture.filter((topic) => topic.title.includes(title)) + : wpTopicsFixture; + const filteredTopics = search + ? filteredTopicsByTitle.filter((topic) => + topic.title.includes(search) + ) + : filteredTopicsByTitle; + + return getConnection({ + after: null, + data: filteredTopics, + first: undefined, + }); + }, + }, + })) as ExecutionResult<TopicsCountResponse>; + + return HttpResponse.json({ data, errors }); +}); diff --git a/tests/msw/handlers/topics/topics-list.handler.ts b/tests/msw/handlers/topics/topics-list.handler.ts new file mode 100644 index 0000000..5e3e31a --- /dev/null +++ b/tests/msw/handlers/topics/topics-list.handler.ts @@ -0,0 +1,42 @@ +import { type ExecutionResult, graphql as executeGraphql } from 'graphql'; +import { HttpResponse, graphql } from 'msw'; +import type { + FetchTopicsListInput, + TopicsListResponse, +} from '../../../../src/services/graphql'; +import { wpTopicsFixture } from '../../../fixtures'; +import { getConnection } from '../../../utils/graphql'; +import { schema } from '../../schema'; + +export const topicsListHandler = graphql.query< + TopicsListResponse, + FetchTopicsListInput +>('TopicsList', async ({ query, variables }) => { + const pageParams = new URLSearchParams(window.location.search); + const isError = pageParams.get('error') === 'true'; + + if (isError) return HttpResponse.json({ data: { topics: null } }); + + const { data, errors } = (await executeGraphql({ + schema, + source: query, + variableValues: variables, + rootValue: { + topics({ after, first, where }: typeof variables) { + const { search, title } = where ?? {}; + const filteredTopicsByTitle = title + ? wpTopicsFixture.filter((topic) => topic.title.includes(title)) + : wpTopicsFixture; + const filteredTopics = search + ? filteredTopicsByTitle.filter((topic) => + topic.title.includes(search) + ) + : filteredTopicsByTitle; + + return getConnection({ after, data: filteredTopics, first }); + }, + }, + })) as ExecutionResult<TopicsListResponse>; + + return HttpResponse.json({ data, errors }); +}); diff --git a/tests/msw/handlers/topics/topics-slugs.handler.ts b/tests/msw/handlers/topics/topics-slugs.handler.ts new file mode 100644 index 0000000..960e411 --- /dev/null +++ b/tests/msw/handlers/topics/topics-slugs.handler.ts @@ -0,0 +1,26 @@ +import { type ExecutionResult, graphql as executeGraphql } from 'graphql'; +import { HttpResponse, graphql } from 'msw'; +import type { TopicsSlugsResponse } from '../../../../src/services/graphql'; +import { wpTopicsFixture } from '../../../fixtures'; +import { schema } from '../../schema'; + +export const topicsSlugsHandler = graphql.query< + TopicsSlugsResponse, + Record<'first', number> +>('TopicsSlugs', async ({ query, variables }) => { + const pageParams = new URLSearchParams(window.location.search); + const isError = pageParams.get('error') === 'true'; + + if (isError) return HttpResponse.json({ data: { topics: null } }); + + const { data, errors } = (await executeGraphql({ + schema, + source: query, + variableValues: variables, + rootValue: { + topics: { nodes: wpTopicsFixture }, + }, + })) as ExecutionResult<TopicsSlugsResponse>; + + return HttpResponse.json({ data, errors }); +}); diff --git a/tests/msw/schema/types/index.ts b/tests/msw/schema/types/index.ts index c570033..ada7f2c 100644 --- a/tests/msw/schema/types/index.ts +++ b/tests/msw/schema/types/index.ts @@ -38,6 +38,18 @@ const rootQueryType = `type Query { last: Int where: RootQueryToThematicConnectionWhereArgs ): RootQueryToThematicConnection + topic( + asPreview: Boolean + id: ID! + idType: TopicIdType + ): Topic + topics( + after: String + before: String + first: Int + last: Int + where: RootQueryToTopicConnectionWhereArgs + ): RootQueryToTopicConnection }`; export const types = [ diff --git a/tests/msw/schema/types/topic.types.ts b/tests/msw/schema/types/topic.types.ts index ba9abb7..2d54653 100644 --- a/tests/msw/schema/types/topic.types.ts +++ b/tests/msw/schema/types/topic.types.ts @@ -1,4 +1,11 @@ -export const topicTypes = `union Topic_Acftopics_PostsInTopic = Post +export const topicTypes = `enum TopicIdType { + DATABASE_ID + ID + SLUG + URI +} + +union Topic_Acftopics_PostsInTopic = Post type Topic_Acftopics { officialWebsite: String @@ -16,6 +23,32 @@ type Topic { seo: PostTypeSEO slug: String title(format: PostObjectFieldFormatEnum): String +} + +input RootQueryToTopicConnectionWhereArgs { + authorName: String + orderby: [PostObjectsConnectionOrderbyInput] + search: String + title: String +} + +type RootQueryToTopicConnectionPageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + total: Int +} + +type RootQueryToTopicConnectionEdge { + cursor: String + node: Topic! +} + +type RootQueryToTopicConnection { + edges: [RootQueryToTopicConnectionEdge!]! + nodes: [Topic!]! + pageInfo: RootQueryToTopicConnectionPageInfo! }`; // cSpell:ignore Acftopics |
