Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace fetch-mock dependency with custom fetch mock utility #12551

Merged
merged 13 commits into from
Oct 18, 2024
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
Loading