From ad0a55ec1635574ee449810d8975770f86bffba4 Mon Sep 17 00:00:00 2001 From: Ta5r Date: Tue, 11 Feb 2025 16:26:35 +0530 Subject: [PATCH 1/2] tests: add tests for QueryEngine and utils. --- .../src/query-engine/tests/index.test.ts | 48 ++++++++ .../query/src/utils/parse-global-styles.ts | 5 + packages/query/src/utils/parse-template.ts | 7 ++ .../utils/tests/parse-global-styles.test.ts | 100 ++++++++++++++++ .../src/utils/tests/parse-template.test.ts | 112 ++++++++++++++++++ 5 files changed, 272 insertions(+) create mode 100644 packages/query/src/query-engine/tests/index.test.ts create mode 100644 packages/query/src/utils/tests/parse-global-styles.test.ts create mode 100644 packages/query/src/utils/tests/parse-template.test.ts diff --git a/packages/query/src/query-engine/tests/index.test.ts b/packages/query/src/query-engine/tests/index.test.ts new file mode 100644 index 00000000..234f9e49 --- /dev/null +++ b/packages/query/src/query-engine/tests/index.test.ts @@ -0,0 +1,48 @@ +import { QueryEngine } from '@/query-engine'; +import { getConfig, getGraphqlUrl } from '@snapwp/core/config'; +import { ApolloClient, InMemoryCache } from '@apollo/client'; + +jest.mock( '@snapwp/core/config', () => ( { + getConfig: jest.fn(), + getGraphqlUrl: jest.fn(), +} ) ); + +jest.mock( '@apollo/client', () => ( { + ApolloClient: jest.fn(), + InMemoryCache: jest.fn(), +} ) ); + +describe( 'QueryEngine', () => { + const validConfig = { + homeUrl: 'https://home.example.com', + }; + const graphqlUrl = 'https://graphql.example.com'; + + beforeEach( () => { + jest.clearAllMocks(); + ( getConfig as jest.Mock ).mockReturnValue( validConfig ); + ( getGraphqlUrl as jest.Mock ).mockReturnValue( graphqlUrl ); + ( InMemoryCache as jest.Mock ).mockImplementation( () => ( { + writeQuery: jest.fn(), + readQuery: jest.fn(), + } ) ); + } ); + + it( 'should initialize the QueryEngine correctly', () => { + QueryEngine.initialize(); + + expect( getGraphqlUrl ).toHaveBeenCalled(); + expect( getConfig ).toHaveBeenCalled(); + expect( ( QueryEngine as any ).graphqlEndpoint ).toBe( graphqlUrl ); + expect( ( QueryEngine as any ).homeUrl ).toBe( validConfig.homeUrl ); + expect( ( QueryEngine as any ).apolloClient ).toBeInstanceOf( + ApolloClient + ); + } ); + + it( 'should be a singleton', () => { + const instance1 = QueryEngine.getInstance(); + const instance2 = QueryEngine.getInstance(); + expect( instance1 ).toBe( instance2 ); + } ); +} ); diff --git a/packages/query/src/utils/parse-global-styles.ts b/packages/query/src/utils/parse-global-styles.ts index 8fe18450..644862f5 100644 --- a/packages/query/src/utils/parse-global-styles.ts +++ b/packages/query/src/utils/parse-global-styles.ts @@ -24,6 +24,11 @@ export default function parseQueryResult( } ); } + // Check if globalStyles is null. + if ( queryData.data.globalStyles === null ) { + throw new GlobalStylesParseError( `Error fetching global styles.` ); + } + if ( ! queryData.data && queryData.errors?.length ) { throw new GlobalStylesParseError( `Error fetching global styles.` ); } diff --git a/packages/query/src/utils/parse-template.ts b/packages/query/src/utils/parse-template.ts index 7faefb1b..94fa32a6 100644 --- a/packages/query/src/utils/parse-template.ts +++ b/packages/query/src/utils/parse-template.ts @@ -34,6 +34,13 @@ export default function parseQueryResult( } ); } + // Check if data.templateByUri is null + if ( ! queryData.data.templateByUri ) { + throw new TemplateParseError( + `Error fetching template data for uri: ${ uri }` + ); + } + if ( ! queryData.data && queryData.errors?.length ) { throw new TemplateParseError( `Error fetching template data for uri: ${ uri }` diff --git a/packages/query/src/utils/tests/parse-global-styles.test.ts b/packages/query/src/utils/tests/parse-global-styles.test.ts new file mode 100644 index 00000000..509f5060 --- /dev/null +++ b/packages/query/src/utils/tests/parse-global-styles.test.ts @@ -0,0 +1,100 @@ +import { ApolloQueryResult } from '@apollo/client'; +import { Logger, GlobalStylesParseError } from '@snapwp/core'; +import parseQueryResult from '../parse-global-styles'; +import { GetGlobalStylesQuery } from '@graphqlTypes/graphql'; + +jest.mock( '@snapwp/core', () => ( { + ...jest.requireActual( '@snapwp/core' ), + Logger: { + error: jest.fn(), + }, +} ) ); + +describe( 'parseQueryResult', () => { + beforeEach( () => { + jest.clearAllMocks(); + } ); + + it( 'should parse valid query data correctly', () => { + const queryData: ApolloQueryResult< GetGlobalStylesQuery > = { + data: { + globalStyles: { + customCss: 'body { color: red; }', + stylesheet: 'styles.css', + renderedFontFaces: '@font-face { font-family: "MyFont"; }', + }, + }, + errors: [], + loading: false, + networkStatus: 7, + }; + + const result = parseQueryResult( queryData ); + + expect( result ).toEqual( { + customCss: 'body { color: red; }', + globalStylesheet: 'styles.css', + renderedFontFaces: '@font-face { font-family: "MyFont"; }', + } ); + } ); + + it( 'should log errors and still parse data if both data and errors exist', () => { + const queryData: ApolloQueryResult< GetGlobalStylesQuery > = { + data: { + globalStyles: { + customCss: 'body { color: blue; }', + stylesheet: 'theme.css', + renderedFontFaces: + '@font-face { font-family: "OtherFont"; }', + }, + }, + errors: [ { message: 'Sample error message' } ], // @todo : Fix this is not taking strings and hence inaccurate assertion below. + loading: false, + networkStatus: 7, + }; + + const result = parseQueryResult( queryData ); + + expect( Logger.error ).toHaveBeenCalledWith( + 'Error fetching global styles: [object Object]' // @todo : Fix + ); + expect( result ).toEqual( { + customCss: 'body { color: blue; }', + globalStylesheet: 'theme.css', + renderedFontFaces: '@font-face { font-family: "OtherFont"; }', + } ); + } ); + + it( 'should throw an error if `data.globalStyles` is null and errors exist', () => { + const queryData: ApolloQueryResult< GetGlobalStylesQuery > = { + data: { globalStyles: null }, + errors: [ { message: 'Critical error occurred' } ], + loading: false, + networkStatus: 7, + }; + + expect( () => parseQueryResult( queryData ) ).toThrow( + GlobalStylesParseError + ); + expect( Logger.error ).toHaveBeenCalledWith( + // 'Error fetching global styles: Critical error occurred' + 'Error fetching global styles: [object Object]' + ); + } ); + + it( 'should throw an error if `data.globalStyles` is null and no errors exist', () => { + const queryData: ApolloQueryResult< GetGlobalStylesQuery > = { + data: { globalStyles: null }, + // @ts-ignore + // data: null, + errors: [], + loading: false, + networkStatus: 7, + }; + + expect( () => parseQueryResult( queryData ) ).toThrow( + GlobalStylesParseError + ); + expect( Logger.error ).not.toHaveBeenCalled(); + } ); +} ); diff --git a/packages/query/src/utils/tests/parse-template.test.ts b/packages/query/src/utils/tests/parse-template.test.ts new file mode 100644 index 00000000..53aa135d --- /dev/null +++ b/packages/query/src/utils/tests/parse-template.test.ts @@ -0,0 +1,112 @@ +import { ApolloQueryResult } from '@apollo/client'; +import { + BlockData, + Logger, + TemplateParseError, + TemplateData, +} from '@snapwp/core'; +import parseQueryResult from '@/utils/parse-template'; +import { GetCurrentTemplateQuery } from '@graphqlTypes/graphql'; + +jest.mock( '@snapwp/core', () => ( { + ...jest.requireActual( '@snapwp/core' ), + Logger: { + error: jest.fn(), + }, +} ) ); + +describe( 'parseQueryResult', () => { + const wordpressUrl = 'https://test.com'; + const uri = '/sample-template'; + + beforeEach( () => { + jest.clearAllMocks(); + } ); + + it( 'should parse valid query data correctly', () => { + const queryData: ApolloQueryResult< GetCurrentTemplateQuery > = { + data: { + templateByUri: { + bodyClasses: [ 'class1', 'class2' ], + enqueuedScripts: { + nodes: [ + { + id: '122', + src: '/script.js', + handle: 'test-script', + }, + { + id: '123', + src: 'https://cdn.com/script.js', + handle: 'cdn-script', + }, + ], + }, + enqueuedStylesheets: { + nodes: [ + { + src: '/style.css', + handle: 'test-style', + before: [ 'before-content' ], + after: [ 'after-content' ], + }, + ], + }, + editorBlocks: [ + { + type: 'core/paragraph', + renderedHtml: '

Text

', + }, + ], + }, + }, + errors: [], + loading: false, + networkStatus: 7, + }; + + const result = parseQueryResult( queryData, wordpressUrl, uri ); + + expect( result ).toEqual< TemplateData >( { + stylesheets: [ + { + src: 'https://test.com/style.css', + handle: 'test-style', + before: 'before-content', + after: 'after-content', + }, + ], + editorBlocks: [ + { + type: 'core/paragraph', + renderedHtml: '

Text

', + }, + ], + scriptModules: undefined, + scripts: [ + { src: 'https://test.com/script.js', handle: 'test-script' }, + { src: 'https://cdn.com/script.js', handle: 'cdn-script' }, + ], + bodyClasses: [ 'class1', 'class2' ], + } ); + } ); + + it( 'should throw an error if `data` is null and errors exist', () => { + const queryData: ApolloQueryResult< GetCurrentTemplateQuery > = { + data: { templateByUri: null }, + errors: [ { message: 'Critical error occurred' } ], + loading: false, + networkStatus: 7, + }; + + expect( () => + parseQueryResult( queryData, wordpressUrl, uri ) + ).toThrow( TemplateParseError ); + + expect( Logger.error ).toHaveBeenCalledWith( + 'Error fetching template data: Critical error occurred.', + '(Please refer to our FAQs for steps to debug and fix)', + { message: 'Critical error occurred' } + ); + } ); +} ); From d3ed4b8e569b072cbe7aee735c3a2408e71cecce Mon Sep 17 00:00:00 2001 From: Ta5r Date: Tue, 11 Feb 2025 17:12:52 +0530 Subject: [PATCH 2/2] fix : Error logging format to match with parse-template. --- packages/query/src/utils/parse-global-styles.ts | 5 ++++- .../query/src/utils/tests/parse-global-styles.test.ts | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/query/src/utils/parse-global-styles.ts b/packages/query/src/utils/parse-global-styles.ts index 644862f5..5c97d065 100644 --- a/packages/query/src/utils/parse-global-styles.ts +++ b/packages/query/src/utils/parse-global-styles.ts @@ -20,7 +20,10 @@ export default function parseQueryResult( ): GlobalHeadProps { if ( queryData.errors?.length ) { queryData.errors?.forEach( ( error ) => { - Logger.error( `Error fetching global styles: ${ error }` ); + Logger.error( + `Error fetching global styles: ${ error?.message }`, + error + ); } ); } diff --git a/packages/query/src/utils/tests/parse-global-styles.test.ts b/packages/query/src/utils/tests/parse-global-styles.test.ts index 509f5060..cbf4633c 100644 --- a/packages/query/src/utils/tests/parse-global-styles.test.ts +++ b/packages/query/src/utils/tests/parse-global-styles.test.ts @@ -48,7 +48,7 @@ describe( 'parseQueryResult', () => { '@font-face { font-family: "OtherFont"; }', }, }, - errors: [ { message: 'Sample error message' } ], // @todo : Fix this is not taking strings and hence inaccurate assertion below. + errors: [ { message: 'Sample error message' } ], loading: false, networkStatus: 7, }; @@ -56,7 +56,8 @@ describe( 'parseQueryResult', () => { const result = parseQueryResult( queryData ); expect( Logger.error ).toHaveBeenCalledWith( - 'Error fetching global styles: [object Object]' // @todo : Fix + 'Error fetching global styles: Sample error message', + { message: 'Sample error message' } ); expect( result ).toEqual( { customCss: 'body { color: blue; }', @@ -77,8 +78,8 @@ describe( 'parseQueryResult', () => { GlobalStylesParseError ); expect( Logger.error ).toHaveBeenCalledWith( - // 'Error fetching global styles: Critical error occurred' - 'Error fetching global styles: [object Object]' + 'Error fetching global styles: Critical error occurred', + { message: 'Critical error occurred' } ); } );