diff options
19 files changed, 437 insertions, 16 deletions
diff --git a/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.test.ts b/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.test.ts new file mode 100644 index 0000000..3650528 --- /dev/null +++ b/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.test.ts @@ -0,0 +1,26 @@ +import { afterEach, describe, expect, it } from '@jest/globals'; +import { wpThematicsFixture } from '../../../../../tests/fixtures'; +import { fetchAllThematicsSlugs } from './fetch-all-thematics-slugs'; + +describe('fetch-all-thematics-slugs', () => { + afterEach(() => { + window.history.replaceState({}, '', '/'); + }); + + it('returns the WordPress thematics using GraphQL', async () => { + const result = await fetchAllThematicsSlugs(wpThematicsFixture.length); + + expect.assertions(1); + + expect(result).toStrictEqual(wpThematicsFixture.map((post) => post.slug)); + }); + + it('rejects with an error when no thematics are found', async () => { + window.history.replaceState({}, '', '/?error=true'); + expect.assertions(1); + + await expect(async () => + fetchAllThematicsSlugs(wpThematicsFixture.length) + ).rejects.toEqual(new Error('Unable to find the thematics slugs.')); + }); +}); diff --git a/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.ts b/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.ts index c44bb6d..80da462 100644 --- a/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.ts +++ b/src/services/graphql/fetchers/thematics/fetch-all-thematics-slugs.ts @@ -1,7 +1,7 @@ import type { GraphQLNodes, Nullable, SlugNode } from '../../../../types'; import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers'; -type ThematicsSlugsResponse = { +export type ThematicsSlugsResponse = { thematics: Nullable<GraphQLNodes<SlugNode>>; }; diff --git a/src/services/graphql/fetchers/thematics/fetch-thematic.test.ts b/src/services/graphql/fetchers/thematics/fetch-thematic.test.ts new file mode 100644 index 0000000..bf6495a --- /dev/null +++ b/src/services/graphql/fetchers/thematics/fetch-thematic.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from '@jest/globals'; +import { wpThematicsFixture } from '../../../../../tests/fixtures'; +import { fetchThematic } from './fetch-thematic'; + +describe('fetch-thematic', () => { + it('returns a thematic by slug', async () => { + const result = await fetchThematic(wpThematicsFixture[2].slug); + + expect.assertions(1); + + expect(result).toStrictEqual(wpThematicsFixture[2]); + }); + + it('rejects with an error when the slug does not exist', async () => { + const slug = '/inexistent-slug'; + + expect.assertions(1); + + await expect(async () => fetchThematic(slug)).rejects.toEqual( + new Error(`No thematic found for the following slug ${slug}.`) + ); + }); +}); diff --git a/src/services/graphql/fetchers/thematics/fetch-thematic.ts b/src/services/graphql/fetchers/thematics/fetch-thematic.ts index a9958bc..4214785 100644 --- a/src/services/graphql/fetchers/thematics/fetch-thematic.ts +++ b/src/services/graphql/fetchers/thematics/fetch-thematic.ts @@ -1,7 +1,7 @@ import type { Nullable, WPThematic } from '../../../../types'; import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers'; -type ThematicResponse = { +export type ThematicResponse = { thematic: Nullable<WPThematic>; }; @@ -54,6 +54,8 @@ const thematicQuery = `query Thematic($slug: ID!) { afterMore beforeMore } + databaseId + date featuredImage { node { altText @@ -65,6 +67,7 @@ const thematicQuery = `query Thematic($slug: ID!) { title } } + modified seo { metaDesc title diff --git a/src/services/graphql/fetchers/thematics/fetch-thematics-count.test.ts b/src/services/graphql/fetchers/thematics/fetch-thematics-count.test.ts new file mode 100644 index 0000000..0d3ff2a --- /dev/null +++ b/src/services/graphql/fetchers/thematics/fetch-thematics-count.test.ts @@ -0,0 +1,26 @@ +import { afterEach, describe, expect, it } from '@jest/globals'; +import { wpThematicsFixture } from '../../../../../tests/fixtures'; +import { fetchThematicsCount } from './fetch-thematics-count'; + +describe('fetch-thematics-count', () => { + afterEach(() => { + window.history.replaceState({}, '', '/'); + }); + + it('returns the WordPress thematics count using GraphQL', async () => { + const result = await fetchThematicsCount(); + + expect.assertions(1); + + expect(result).toBe(wpThematicsFixture.length); + }); + + it('rejects with an error when no thematics are found', async () => { + window.history.replaceState({}, '', '/?error=true'); + expect.assertions(1); + + await expect(async () => fetchThematicsCount()).rejects.toEqual( + new Error('Unable to find the total number of thematics.') + ); + }); +}); diff --git a/src/services/graphql/fetchers/thematics/fetch-thematics-count.ts b/src/services/graphql/fetchers/thematics/fetch-thematics-count.ts index 29a3b17..d961b02 100644 --- a/src/services/graphql/fetchers/thematics/fetch-thematics-count.ts +++ b/src/services/graphql/fetchers/thematics/fetch-thematics-count.ts @@ -5,7 +5,7 @@ import type { } from '../../../../types'; import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers'; -type ThematicsCountResponse = { +export type ThematicsCountResponse = { thematics: Nullable<{ pageInfo: Pick<GraphQLPageInfo, 'total'>; }>; diff --git a/src/services/graphql/fetchers/thematics/fetch-thematics-list.test.ts b/src/services/graphql/fetchers/thematics/fetch-thematics-list.test.ts new file mode 100644 index 0000000..d52262d --- /dev/null +++ b/src/services/graphql/fetchers/thematics/fetch-thematics-list.test.ts @@ -0,0 +1,26 @@ +import { afterEach, describe, expect, it } from '@jest/globals'; +import { wpThematicsFixture } from '../../../../../tests/fixtures'; +import { fetchThematicsList } from './fetch-thematics-list'; + +describe('fetch-thematics-list', () => { + afterEach(() => { + window.history.replaceState({}, '', '/'); + }); + + it('returns the WordPress thematics using GraphQL', async () => { + const result = await fetchThematicsList({}); + + expect.assertions(1); + + expect(result.pageInfo.total).toBe(wpThematicsFixture.length); + }); + + it('rejects with an error when no thematics are found', async () => { + window.history.replaceState({}, '', '/?error=true'); + expect.assertions(1); + + await expect(async () => + fetchThematicsList({ where: { title: 'inexistent-title' } }) + ).rejects.toEqual(new Error('No thematics found.')); + }); +}); diff --git a/src/services/graphql/fetchers/thematics/fetch-thematics-list.ts b/src/services/graphql/fetchers/thematics/fetch-thematics-list.ts index f4d22c6..7e1e582 100644 --- a/src/services/graphql/fetchers/thematics/fetch-thematics-list.ts +++ b/src/services/graphql/fetchers/thematics/fetch-thematics-list.ts @@ -8,7 +8,7 @@ import type { } from '../../../../types'; import { fetchGraphQL, getGraphQLUrl } from '../../../../utils/helpers'; -type ThematicsListResponse = { +export type ThematicsListResponse = { thematics: Nullable<GraphQLConnection<WPThematicPreview>>; }; @@ -42,6 +42,11 @@ const thematicsListQuery = `query ThematicsList($after: String, $before: String, title } } + pageInfo { + endCursor + hasNextPage + total + } } }`; diff --git a/src/types/data.ts b/src/types/data.ts index 9a6d674..c582709 100644 --- a/src/types/data.ts +++ b/src/types/data.ts @@ -57,6 +57,8 @@ export type WPImage = { type WPInfo = { wordsCount: number }; type WPContent = { + contentParts: WPContentParts; + databaseId: number; date: string; featuredImage: Nullable<GraphQLNode<WPImage>>; modified: string; @@ -66,7 +68,6 @@ type WPContent = { }; export type WPPage = WPContent & { - contentParts: WPContentParts; info: WPInfo; }; @@ -81,8 +82,6 @@ export type WPPost = WPContent & { acfPosts: Nullable<Partial<WPAcfPosts>>; author: GraphQLNode<WPPostAuthor>; commentCount: Nullable<number>; - contentParts: WPContentParts; - databaseId: number; info: WPInfo; }; @@ -118,9 +117,10 @@ export type WPThematic = WPContent & { acfThematics: Nullable<WPAcfThematics>; }; -export type WPThematicPreview = Pick<WPThematic, 'slug' | 'title'> & { - databaseId: number; -}; +export type WPThematicPreview = Pick< + WPThematic, + 'databaseId' | 'slug' | 'title' +>; type WPAcfTopics = { officialWebsite: string; @@ -133,10 +133,8 @@ export type WPTopic = WPContent & { export type WPTopicPreview = Pick< WPTopic, - 'featuredImage' | 'slug' | 'title' -> & { - databaseId: number; -}; + 'databaseId' | 'featuredImage' | 'slug' | 'title' +>; //=========================================================================== // Data from MDX files diff --git a/tests/fixtures/index.ts b/tests/fixtures/index.ts index 0c624c2..abe49be 100644 --- a/tests/fixtures/index.ts +++ b/tests/fixtures/index.ts @@ -1,2 +1,3 @@ export * from './wp-comments.fixture'; export * from './wp-posts.fixture'; +export * from './wp-thematics.fixture'; diff --git a/tests/fixtures/wp-thematics.fixture.ts b/tests/fixtures/wp-thematics.fixture.ts new file mode 100644 index 0000000..ac9064a --- /dev/null +++ b/tests/fixtures/wp-thematics.fixture.ts @@ -0,0 +1,114 @@ +import type { WPThematic } from '../../src/types'; + +export const wpThematicsFixture: WPThematic[] = [ + { + acfThematics: null, + contentParts: { + afterMore: + 'Iure sit itaque itaque exercitationem. Fuga quos mollitia dolorem est pariatur harum corrupti rerum soluta. Perferendis nemo quae accusamus omnis omnis facere quis ducimus autem. Debitis quam ut non. Rerum qui ut. Earum ex quis aspernatur aut qui ut.', + beforeMore: + 'Dolorem quaerat expedita id soluta sequi. Est quia alias unde voluptatem repudiandae voluptatibus reiciendis dolorum.', + }, + databaseId: 1, + date: '2022-11-04', + featuredImage: null, + modified: '2022-11-05', + seo: { + metaDesc: 'In temporibus quis nihil veniam cum excepturi aliquam.', + title: 'aut veritatis molestias', + }, + slug: '/veritatis-autem-voluptas', + title: 'veritatis autem voluptas', + }, + { + acfThematics: null, + contentParts: { + afterMore: + 'Ut ut ullam est tenetur voluptatem aut facere. Cum necessitatibus dolor dolore. Magni rem qui odio laboriosam at sunt et sapiente. Earum incidunt enim eum voluptatum necessitatibus ipsa dolorum.', + beforeMore: + 'Et architecto libero aut ab ut alias nisi. Sed quibusdam sit expedita unde culpa et numquam unde. Hic amet eveniet sunt at exercitationem. Enim accusamus amet modi ea sunt perferendis. Numquam eaque maxime possimus. Et iure sequi facilis eum consequuntur rerum.', + }, + databaseId: 2, + date: '2022-11-06', + featuredImage: { + node: { + altText: null, + mediaDetails: { + height: 480, + width: 640, + }, + sourceUrl: 'https://picsum.photos/640/480', + title: null, + }, + }, + modified: '2022-11-06', + seo: { + metaDesc: 'Sed nulla nihil at dolores omnis repellendus qui sed.', + title: 'assumenda omnis placeat', + }, + slug: '/aliquid-quia-eius', + title: 'aliquid quia eius', + }, + { + acfThematics: { + postsInThematic: null, + }, + databaseId: 3, + contentParts: { + afterMore: + 'Enim quis placeat cupiditate sit vel repellat. Architecto ex nam corrupti. Aspernatur sequi et reiciendis voluptate laudantium odit est consectetur. Aut harum fugit.', + beforeMore: + 'Et nemo impedit quia molestiae. Corrupti qui necessitatibus vitae aut doloribus enim possimus.', + }, + date: '2022-11-07', + featuredImage: null, + modified: '2022-11-07', + seo: { + metaDesc: + 'Consequatur nisi dolorum fugiat mollitia aperiam ratione quibusdam.', + title: 'vel et velit', + }, + slug: '/quasi-sint-nulla', + title: 'quasi sint nulla', + }, + { + acfThematics: { + postsInThematic: [ + { + acfPosts: null, + commentCount: 1, + contentParts: { + beforeMore: + 'Et porro distinctio dolores dolor enim fugit. Autem excepturi distinctio rerum mollitia dolor. Animi est consectetur omnis quia qui consequatur porro inventore. Quo voluptas nihil porro et nostrum velit autem commodi nulla. Qui temporibus dolorem totam.', + }, + databaseId: 18, + date: '2021-10-10', + featuredImage: null, + info: { + wordsCount: 523, + }, + modified: '2021-10-15', + slug: '/incidunt-cupiditate-in', + title: 'incidunt cupiditate in', + }, + ], + }, + contentParts: { + afterMore: + 'Cupiditate quidem quis debitis. Quae maiores tenetur cupiditate quibusdam est iure officia. Omnis enim ut accusantium nisi et recusandae.', + beforeMore: + 'Eum consequatur sint. Quibusdam illo quasi molestiae architecto excepturi. Quia optio adipisci voluptatem ex accusamus consequatur consequatur vel eveniet.', + }, + databaseId: 4, + date: '2022-11-08', + featuredImage: null, + modified: '2022-11-10', + seo: { + metaDesc: + 'Recusandae et laboriosam repudiandae sit dignissimos accusantium.', + title: 'consectetur magni eum', + }, + slug: '/numquam-commodi-velit', + title: 'numquam commodi velit', + }, +]; diff --git a/tests/msw/handlers/index.ts b/tests/msw/handlers/index.ts index 5f331e7..e42a0eb 100644 --- a/tests/msw/handlers/index.ts +++ b/tests/msw/handlers/index.ts @@ -1,4 +1,9 @@ import { commentsHandlers } from './comments'; import { postsHandlers } from './posts'; +import { thematicsHandlers } from './thematics'; -export const handlers = [...commentsHandlers, ...postsHandlers]; +export const handlers = [ + ...commentsHandlers, + ...postsHandlers, + ...thematicsHandlers, +]; diff --git a/tests/msw/handlers/thematics/index.ts b/tests/msw/handlers/thematics/index.ts new file mode 100644 index 0000000..70ed3ca --- /dev/null +++ b/tests/msw/handlers/thematics/index.ts @@ -0,0 +1,11 @@ +import { thematicHandler } from './thematic.handler'; +import { thematicsCountHandler } from './thematics-count.handler'; +import { thematicsListHandler } from './thematics-list.handler'; +import { thematicsSlugsHandler } from './thematics-slugs.handler'; + +export const thematicsHandlers = [ + thematicHandler, + thematicsCountHandler, + thematicsListHandler, + thematicsSlugsHandler, +]; diff --git a/tests/msw/handlers/thematics/thematic.handler.ts b/tests/msw/handlers/thematics/thematic.handler.ts new file mode 100644 index 0000000..1e7d129 --- /dev/null +++ b/tests/msw/handlers/thematics/thematic.handler.ts @@ -0,0 +1,23 @@ +import { type ExecutionResult, graphql as executeGraphql } from 'graphql'; +import { HttpResponse, graphql } from 'msw'; +import type { ThematicResponse } from '../../../../src/services/graphql'; +import { wpThematicsFixture } from '../../../fixtures'; +import { schema } from '../../schema'; + +export const thematicHandler = graphql.query< + ThematicResponse, + Record<'slug', string> +>('Thematic', async ({ query, variables }) => { + const { data, errors } = (await executeGraphql({ + schema, + source: query, + variableValues: variables, + rootValue: { + thematic: wpThematicsFixture.find( + (wpThematic) => wpThematic.slug === variables.slug + ), + }, + })) as ExecutionResult<ThematicResponse>; + + return HttpResponse.json({ data, errors }); +}); diff --git a/tests/msw/handlers/thematics/thematics-count.handler.ts b/tests/msw/handlers/thematics/thematics-count.handler.ts new file mode 100644 index 0000000..4bcdf2d --- /dev/null +++ b/tests/msw/handlers/thematics/thematics-count.handler.ts @@ -0,0 +1,45 @@ +import { type ExecutionResult, graphql as executeGraphql } from 'graphql'; +import { HttpResponse, graphql } from 'msw'; +import type { ThematicsCountResponse } from '../../../../src/services/graphql'; +import type { GraphQLPostWhere } from '../../../../src/types'; +import { wpThematicsFixture } from '../../../fixtures'; +import { getConnection } from '../../../utils/graphql'; +import { schema } from '../../schema'; + +export const thematicsCountHandler = graphql.query< + ThematicsCountResponse, + GraphQLPostWhere +>('ThematicsCount', async ({ query, variables }) => { + const pageParams = new URLSearchParams(window.location.search); + const isError = pageParams.get('error') === 'true'; + + if (isError) return HttpResponse.json({ data: { thematics: null } }); + + const { data, errors } = (await executeGraphql({ + schema, + source: query, + variableValues: variables, + rootValue: { + thematics({ search, title }: typeof variables) { + const filteredThematicsByTitle = title + ? wpThematicsFixture.filter((thematic) => + thematic.title.includes(title) + ) + : wpThematicsFixture; + const filteredThematics = search + ? filteredThematicsByTitle.filter((thematic) => + thematic.title.includes(search) + ) + : filteredThematicsByTitle; + + return getConnection({ + after: null, + data: filteredThematics, + first: undefined, + }); + }, + }, + })) as ExecutionResult<ThematicsCountResponse>; + + return HttpResponse.json({ data, errors }); +}); diff --git a/tests/msw/handlers/thematics/thematics-list.handler.ts b/tests/msw/handlers/thematics/thematics-list.handler.ts new file mode 100644 index 0000000..f206247 --- /dev/null +++ b/tests/msw/handlers/thematics/thematics-list.handler.ts @@ -0,0 +1,44 @@ +import { type ExecutionResult, graphql as executeGraphql } from 'graphql'; +import { HttpResponse, graphql } from 'msw'; +import type { + FetchThematicsListInput, + ThematicsListResponse, +} from '../../../../src/services/graphql'; +import { wpThematicsFixture } from '../../../fixtures'; +import { getConnection } from '../../../utils/graphql'; +import { schema } from '../../schema'; + +export const thematicsListHandler = graphql.query< + ThematicsListResponse, + FetchThematicsListInput +>('ThematicsList', async ({ query, variables }) => { + const pageParams = new URLSearchParams(window.location.search); + const isError = pageParams.get('error') === 'true'; + + if (isError) return HttpResponse.json({ data: { thematics: null } }); + + const { data, errors } = (await executeGraphql({ + schema, + source: query, + variableValues: variables, + rootValue: { + thematics({ after, first, where }: typeof variables) { + const { search, title } = where ?? {}; + const filteredThematicsByTitle = title + ? wpThematicsFixture.filter((thematic) => + thematic.title.includes(title) + ) + : wpThematicsFixture; + const filteredThematics = search + ? filteredThematicsByTitle.filter((thematic) => + thematic.title.includes(search) + ) + : filteredThematicsByTitle; + + return getConnection({ after, data: filteredThematics, first }); + }, + }, + })) as ExecutionResult<ThematicsListResponse>; + + return HttpResponse.json({ data, errors }); +}); diff --git a/tests/msw/handlers/thematics/thematics-slugs.handler.ts b/tests/msw/handlers/thematics/thematics-slugs.handler.ts new file mode 100644 index 0000000..3a71c8e --- /dev/null +++ b/tests/msw/handlers/thematics/thematics-slugs.handler.ts @@ -0,0 +1,26 @@ +import { type ExecutionResult, graphql as executeGraphql } from 'graphql'; +import { HttpResponse, graphql } from 'msw'; +import type { ThematicsSlugsResponse } from '../../../../src/services/graphql'; +import { wpThematicsFixture } from '../../../fixtures'; +import { schema } from '../../schema'; + +export const thematicsSlugsHandler = graphql.query< + ThematicsSlugsResponse, + Record<'first', number> +>('ThematicsSlugs', async ({ query, variables }) => { + const pageParams = new URLSearchParams(window.location.search); + const isError = pageParams.get('error') === 'true'; + + if (isError) return HttpResponse.json({ data: { thematics: null } }); + + const { data, errors } = (await executeGraphql({ + schema, + source: query, + variableValues: variables, + rootValue: { + thematics: { nodes: wpThematicsFixture }, + }, + })) as ExecutionResult<ThematicsSlugsResponse>; + + return HttpResponse.json({ data, errors }); +}); diff --git a/tests/msw/schema/types/index.ts b/tests/msw/schema/types/index.ts index c34cacd..c570033 100644 --- a/tests/msw/schema/types/index.ts +++ b/tests/msw/schema/types/index.ts @@ -26,6 +26,18 @@ const rootQueryType = `type Query { last: Int where: RootQueryToPostConnectionWhereArgs ): RootQueryToPostConnection + thematic( + asPreview: Boolean + id: ID! + idType: ThematicIdType + ): Thematic + thematics( + after: String + before: String + first: Int + last: Int + where: RootQueryToThematicConnectionWhereArgs + ): RootQueryToThematicConnection }`; export const types = [ diff --git a/tests/msw/schema/types/thematic.types.ts b/tests/msw/schema/types/thematic.types.ts index afa95c9..2af4f9a 100644 --- a/tests/msw/schema/types/thematic.types.ts +++ b/tests/msw/schema/types/thematic.types.ts @@ -1,4 +1,11 @@ -export const thematicTypes = `union Thematic_Acfthematics_PostsInThematic = Post +export const thematicTypes = `enum ThematicIdType { + DATABASE_ID + ID + SLUG + URI +} + +union Thematic_Acfthematics_PostsInThematic = Post type Thematic_Acfthematics { postsInThematic: [Thematic_Acfthematics_PostsInThematic] @@ -15,6 +22,32 @@ type Thematic { seo: PostTypeSEO slug: String title(format: PostObjectFieldFormatEnum): String +} + +input RootQueryToThematicConnectionWhereArgs { + authorName: String + orderby: [PostObjectsConnectionOrderbyInput] + search: String + title: String +} + +type RootQueryToThematicConnectionPageInfo { + endCursor: String + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: String + total: Int +} + +type RootQueryToThematicConnectionEdge { + cursor: String + node: Thematic! +} + +type RootQueryToThematicConnection { + edges: [RootQueryToThematicConnectionEdge!]! + nodes: [Thematic!]! + pageInfo: RootQueryToThematicConnectionPageInfo! }`; // cSpell:ignore Acfthematics |
