Skip to content

Commit

Permalink
Merge pull request #12551 from guardian/marji/fix-fetch-mock-issue
Browse files Browse the repository at this point in the history
Replace fetch-mock dependency with custom fetch mock utility
  • Loading branch information
marjisound authored Oct 18, 2024
2 parents e8569d9 + 98880a2 commit 778a9fb
Show file tree
Hide file tree
Showing 21 changed files with 644 additions and 817 deletions.
1 change: 0 additions & 1 deletion apps-rendering/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-react": "7.33.2",
"express": "4.21.0",
"fetch-mock": "9.11.0",
"html-webpack-plugin": "5.6.0",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
Expand Down
63 changes: 26 additions & 37 deletions apps-rendering/src/components/Callout/Callout.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
type ArticleFormat,
Pillar,
} from '../../articleFormat';
import fetchMock from 'fetch-mock';
import { campaignDescription, mockCampaign } from 'fixtures/campaign';
import type { ReactElement } from 'react';
import Callout from '.';
Expand All @@ -20,16 +19,30 @@ futureDate.setDate(futureDate.getDate() + 2);
const pastDate = new Date();
pastDate.setDate(pastDate.getDate() - 1);

const mockFetch =
(mockedStatus: number) => (input: RequestInfo, init?: RequestInit) => {
const url = new Request(input).url;

if (
url ===
'https://callouts.code.dev-guardianapis.com/formstack-campaign/submit' &&
init?.method === 'POST'
) {
console.log(`url called: ${url}`);
return Promise.resolve(
new Response(null, { status: mockedStatus }),
);
} else {
return Promise.resolve(
new Response(JSON.stringify({ error: 'Not Found' }), {
status: 404,
}),
);
}
};

const callout = (): ReactElement => {
fetchMock
.restore()
.post(
'https://callouts.code.dev-guardianapis.com/formstack-campaign/submit',
{
status: 201,
body: null,
},
);
global.fetch = mockFetch(201) as unknown as typeof fetch;
return (
<Callout
name={mockCampaign.name}
Expand Down Expand Up @@ -61,15 +74,7 @@ const closedCallout = (): ReactElement => (
/>
);
const nonCollapsableCallout = (): ReactElement => {
fetchMock
.restore()
.post(
'https://callouts.code.dev-guardianapis.com/formstack-campaign/submit',
{
status: 201,
body: null,
},
);
global.fetch = mockFetch(201) as unknown as typeof fetch;
return (
<Callout
isNonCollapsible={true}
Expand All @@ -86,15 +91,7 @@ const nonCollapsableCallout = (): ReactElement => {
);
};
const minimalCallout = (): ReactElement => {
fetchMock
.restore()
.post(
'https://callouts.code.dev-guardianapis.com/formstack-campaign/submit',
{
status: 201,
body: null,
},
);
global.fetch = mockFetch(201) as unknown as typeof fetch;
return (
<>
A callouts prompt, title and description are optional
Expand All @@ -114,15 +111,7 @@ const minimalCallout = (): ReactElement => {
};

const calloutWithFormFailure = (): ReactElement => {
fetchMock
.restore()
.post(
'https://callouts.code.dev-guardianapis.com/formstack-campaign/submit',
{
status: 400,
body: null,
},
);
global.fetch = mockFetch(400) as unknown as typeof fetch;
return (
<Callout
isNonCollapsible={true}
Expand Down
6 changes: 3 additions & 3 deletions dotcom-rendering/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { resets } from '@guardian/source/foundations';

import { Lazy } from '../src/components/Lazy';
import { Picture } from '../src/components/Picture';
import { mockRESTCalls } from '../src/lib/mockRESTCalls';
import { mockFetch } from '../src/lib/mockRESTCalls';
import { setABTests } from '../src/lib/useAB';
import { ConfigContextDecorator } from './decorators/configContextDecorator';
import { Preview } from '@storybook/react';
Expand All @@ -22,11 +22,11 @@ import {
Lazy.disabled = isChromatic();
Picture.disableLazyLoading = isChromatic();

global.fetch = mockFetch;

// Fix the date to prevent false negatives
MockDate.set('Sat Jan 1 2022 12:00:00 GMT+0000 (Greenwich Mean Time)');

mockRESTCalls();

setABTests({
api: new AB({
mvtMaxValue: 1_000_000,
Expand Down
1 change: 0 additions & 1 deletion dotcom-rendering/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@
"eslint-stats": "1.0.1",
"execa": "5.1.1",
"express": "4.21.0",
"fetch-mock": "9.11.0",
"find": "0.3.0",
"he": "1.2.0",
"html-minifier-terser": "7.2.0",
Expand Down
45 changes: 19 additions & 26 deletions dotcom-rendering/src/components/CalloutBlockComponent.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { StoryObj } from '@storybook/react';
import fetchMock from 'fetch-mock';
import { splitTheme } from '../../.storybook/decorators/splitThemeDecorator';
import { calloutCampaign as calloutCampaignV2 } from '../../fixtures/manual/calloutCampaignV2';
import {
Expand All @@ -8,38 +7,31 @@ import {
type ArticleFormat,
Pillar,
} from '../lib/articleFormat';
import { customMockFetch } from '../lib/mockRESTCalls';
import { CalloutBlockComponent } from './CalloutBlockComponent.importable';

const tomorrow = new Date().setDate(new Date().getDate() + 1) / 1000;
const yesterday = new Date().setDate(new Date().getDate() - 1) / 1000;
const pageId =
'world/2023/mar/01/tell-us-have-you-been-affected-by-the-train-crash-in-greece';

const goodRequest = () => {
fetchMock
.restore()
.post(
const mockGoodRequestFetch = customMockFetch([
{
mockedMethod: 'POST',
mockedUrl:
'https://callouts.code.dev-guardianapis.com/formstack-campaign/submit',
{
status: 201,
body: null,
},
)
.spy('end:.hot-update.json');
};
mockedStatus: 201,
},
]);

const badRequest = () => {
fetchMock
.restore()
.post(
const mockBadRequestFetch = customMockFetch([
{
mockedMethod: 'POST',
mockedUrl:
'https://callouts.code.dev-guardianapis.com/formstack-campaign/submit',
{
status: 400,
body: null,
},
)
.spy('end:.hot-update.json');
};
mockedStatus: 400,
},
]);

/** ensure that multiple form IDs are not present on the same page */
let counter = 0;
Expand All @@ -56,7 +48,7 @@ export default {
};

export const Collapsible: StoryObj = () => {
goodRequest();
global.fetch = mockGoodRequestFetch;
return (
<CalloutBlockComponent
callout={{
Expand All @@ -73,7 +65,7 @@ export const Collapsible: StoryObj = () => {
Collapsible.decorators = [splitTheme([defaultFormat])];

export const NonCollapsible: StoryObj = () => {
goodRequest();
global.fetch = mockGoodRequestFetch;
return (
<CalloutBlockComponent
callout={{ ...calloutCampaignV2, activeUntil: tomorrow }}
Expand All @@ -85,7 +77,7 @@ NonCollapsible.storyName = 'NonCollapsible';
NonCollapsible.decorators = [splitTheme([defaultFormat])];

export const SubmissionFailure: StoryObj = () => {
badRequest();
global.fetch = mockBadRequestFetch;
return (
<CalloutBlockComponent
callout={{ ...calloutCampaignV2, activeUntil: tomorrow }}
Expand Down Expand Up @@ -117,6 +109,7 @@ export const Expired: StoryObj = () => {
Expired.decorators = [splitTheme([defaultFormat])];

export const MinimalCallout: StoryObj = () => {
global.fetch = mockGoodRequestFetch;
return (
<>
<div css={{ fontWeight: 'bold', paddingBottom: '16px' }}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { css } from '@emotion/react';
import fetchMock from 'fetch-mock';
import { calloutCampaign } from '../../fixtures/manual/calloutCampaign';
import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat';
import { customMockFetch } from '../lib/mockRESTCalls';
import { CalloutEmbedBlockComponent } from './CalloutEmbedBlockComponent.importable';

export default {
component: CalloutEmbedBlockComponent,
title: 'Components/CalloutEmbedBlockComponent',
};

export const Default = () => {
fetchMock
.restore()
.post(
const mockGoodRequestFetch = customMockFetch([
{
mockedMethod: 'POST',
mockedUrl:
'https://callouts.code.dev-guardianapis.com/formstack-campaign/submit',
{
status: 201,
body: null,
},
)
.spy('end:.hot-update.json');
mockedStatus: 201,
},
]);

export const Default = () => {
global.fetch = mockGoodRequestFetch;

return (
<div
Expand Down
34 changes: 17 additions & 17 deletions dotcom-rendering/src/components/Carousel.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { breakpoints } from '@guardian/source/foundations';
import type { StoryObj } from '@storybook/react';
import fetchMock from 'fetch-mock';
import { splitTheme } from '../../.storybook/decorators/splitThemeDecorator';
import type { StoryProps } from '../../.storybook/decorators/splitThemeDecorator';
import { discussionApiUrl } from '../../fixtures/manual/discussionApiUrl';
Expand All @@ -10,6 +9,7 @@ import {
ArticleSpecial,
Pillar,
} from '../lib/articleFormat';
import { customMockFetch } from '../lib/mockRESTCalls';
import { palette } from '../palette';
import type { TrailType } from '../types/trails';
import { Carousel } from './Carousel.importable';
Expand Down Expand Up @@ -46,17 +46,17 @@ const mockDiscussionId = '/p/pf8fm';
const encodedMockDiscussionId = '%2Fp%2Fpf8fm';

const mockDiscussionUrl = `${discussionApiUrl}/getCommentCounts?short-urls=${encodedMockDiscussionId}`;
const mockCommentCount = () => {
fetchMock
.restore()
.get(mockDiscussionUrl, {
status: 200,
body: {
[mockDiscussionId]: 1506,
} as Record<string, number>,
})
.spy('end:.hot-update.json');
};

const mockCommentCountFetch = customMockFetch([
{
mockedMethod: 'GET',
mockedUrl: mockDiscussionUrl,
mockedStatus: 200,
mockedBody: {
[mockDiscussionId]: 1506,
} as Record<string, number>,
},
]);

const trails: TrailType[] = [
{
Expand Down Expand Up @@ -256,7 +256,7 @@ const sportFormat = {
};

export const Headlines: StoryObj = ({ format }: StoryProps) => {
mockCommentCount();
global.fetch = mockCommentCountFetch;
return (
<Section
fullWidth={true}
Expand All @@ -281,7 +281,7 @@ Headlines.decorators = [
];

export const SingleItemCarousel = () => {
mockCommentCount();
global.fetch = mockCommentCountFetch;
return (
<Section
fullWidth={true}
Expand All @@ -305,7 +305,7 @@ SingleItemCarousel.decorators = [
splitTheme([defaultFormat], { orientation: 'vertical' }),
];
export const SingleOpinionCarousel = () => {
mockCommentCount();
global.fetch = mockCommentCountFetch;
const comment = {
url: 'https://www.theguardian.com/sport/2023/dec/04/golf-pga-tour-liv-rory-mcilroy-tiger-woods-jon-rahm',
linkText:
Expand Down Expand Up @@ -360,7 +360,7 @@ SingleOpinionCarousel.decorators = [
];

export const Immersive = () => {
mockCommentCount();
global.fetch = mockCommentCountFetch;
return (
<>
<Section
Expand Down Expand Up @@ -404,7 +404,7 @@ const specialReportAltFormat = {
};

export const SpecialReportAlt = () => {
mockCommentCount();
global.fetch = mockCommentCountFetch;
const specialReportTrails = [...trails];

for (const trail of specialReportTrails) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import '@testing-library/jest-dom';
import { fireEvent, render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { reportAbuse } from '../../lib/discussionApi';
import { mockRESTCalls } from '../../lib/mockRESTCalls';
import { jestMockFetch } from '../../lib/mockRESTCallsInJest';
import { ok } from '../../lib/result';
import { AbuseReportForm } from './AbuseReportForm';

const fetchMock = mockRESTCalls();

describe('Dropdown', () => {
beforeEach(() => {
jestMockFetch();
});

it('Should show the expected label names', () => {
const { getByLabelText } = render(
<AbuseReportForm
Expand Down Expand Up @@ -51,10 +53,12 @@ describe('Dropdown', () => {
await user.selectOptions(getByLabelText('Category'), 'Trolling');
await user.click(getByRole('button', { name: 'Report' }));

const [, requestInit]: [string, RequestInit | undefined] = (
global.fetch as jest.Mock
).mock.calls[0];

await waitFor(() => {
expect(fetchMock.lastOptions(/reportAbuse/)?.body).toBe(
'categoryId=4',
);
expect(requestInit?.body).toBe('categoryId=4');
});

await waitFor(() => {
Expand Down
Loading

0 comments on commit 778a9fb

Please sign in to comment.