Skip to content

Commit 1bba309

Browse files
VIA-629 AJ/EO Hide how-to-get expander when campaign is active
1 parent 73d4c14 commit 1bba309

File tree

9 files changed

+235
-192
lines changed

9 files changed

+235
-192
lines changed

src/app/_components/content/MoreInformation.test.tsx

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,39 @@ import { MoreInformation } from "@src/app/_components/content/MoreInformation";
22
import { VaccineType } from "@src/models/vaccine";
33
import { mockStyledContent, mockStyledContentWithoutWhatSection } from "@test-data/content-api/data";
44
import { render, screen } from "@testing-library/react";
5-
import React from "react";
5+
6+
describe("MoreInformation component for COVID", () => {
7+
const covid19VaccineType: VaccineType = VaccineType.COVID_19;
8+
9+
it("should not show how-to-get expander section when campaign is active", async () => {
10+
render(
11+
<MoreInformation
12+
styledVaccineContent={mockStyledContent}
13+
vaccineType={covid19VaccineType}
14+
isCampaignActive={true}
15+
/>,
16+
);
17+
18+
// COVID-19 vaccine content (active campaign)
19+
expectExpanderBlockToNotBePresent("How to get the vaccine", "How Section styled component");
20+
});
21+
22+
it("should show how-to-get expander section when campaign is inactive", async () => {
23+
render(
24+
<MoreInformation
25+
styledVaccineContent={mockStyledContent}
26+
vaccineType={covid19VaccineType}
27+
isCampaignActive={false}
28+
/>,
29+
);
30+
31+
// COVID-19 vaccine content (closed campaign)
32+
expectExpanderBlockToBePresent("How to get the vaccine", "How Section styled component");
33+
});
34+
});
635

736
describe("MoreInformation component ", () => {
8-
describe("moreInformationHeadersFromContentApi false", () => {
37+
describe("When vaccineInfo.moreInformationHeadersFromContentApi=false", () => {
938
it("should display whatItIsFor expander block", async () => {
1039
const vaccineType = VaccineType.RSV;
1140
render(<MoreInformation styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
@@ -35,7 +64,7 @@ describe("MoreInformation component ", () => {
3564
});
3665
});
3766

38-
describe("moreInformationHeadersFromContentApi true", () => {
67+
describe("When vaccineInfo.moreInformationHeadersFromContentApi=true", () => {
3968
it("should display whatItIsFor expander block", async () => {
4069
const vaccineType = VaccineType.FLU_IN_PREGNANCY;
4170
render(<MoreInformation styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
@@ -65,53 +94,55 @@ describe("MoreInformation component ", () => {
6594
});
6695
});
6796

68-
it("should not include 'how to get' section for RSV_PREGNANCY ", async () => {
69-
const vaccineType = VaccineType.RSV_PREGNANCY;
70-
render(<MoreInformation styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
97+
describe("For RSV and RSV in pregnancy", () => {
98+
it("should not include 'how to get' section for RSV_PREGNANCY ", async () => {
99+
const vaccineType = VaccineType.RSV_PREGNANCY;
100+
render(<MoreInformation styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
71101

72-
const heading: HTMLElement | null = screen.queryByText("How to get the vaccine");
102+
const heading: HTMLElement | null = screen.queryByText("How to get the vaccine");
73103

74-
expect(heading).not.toBeInTheDocument();
75-
});
104+
expect(heading).not.toBeInTheDocument();
105+
});
76106

77-
it("should not include 'how to get' section for RSV ", async () => {
78-
const vaccineType = VaccineType.RSV;
79-
render(<MoreInformation styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
107+
it("should not include 'how to get' section for RSV ", async () => {
108+
const vaccineType = VaccineType.RSV;
109+
render(<MoreInformation styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
80110

81-
const heading: HTMLElement | null = screen.queryByText("How to get the vaccine");
111+
const heading: HTMLElement | null = screen.queryByText("How to get the vaccine");
82112

83-
expect(heading).not.toBeInTheDocument();
84-
});
113+
expect(heading).not.toBeInTheDocument();
114+
});
85115

86-
it("should display webpage link to more information about vaccine", async () => {
87-
const vaccineType = VaccineType.RSV;
88-
render(<MoreInformation styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
116+
it("should display webpage link to more information about vaccine", async () => {
117+
const vaccineType = VaccineType.RSV;
118+
render(<MoreInformation styledVaccineContent={mockStyledContent} vaccineType={vaccineType} />);
89119

90-
const webpageLink: HTMLElement = screen.getByRole("link", {
91-
name: "Find out more about the RSV vaccine",
92-
});
120+
const webpageLink: HTMLElement = screen.getByRole("link", {
121+
name: "Find out more about the RSV vaccine",
122+
});
93123

94-
expect(webpageLink).toBeInTheDocument();
95-
expect(webpageLink).toHaveAttribute("href", "https://test.example.com/");
96-
expect(webpageLink).toHaveAttribute("target", "_blank");
97-
});
124+
expect(webpageLink).toBeInTheDocument();
125+
expect(webpageLink).toHaveAttribute("href", "https://test.example.com/");
126+
expect(webpageLink).toHaveAttribute("target", "_blank");
127+
});
98128

99-
it("should not display whatItIsFor section if undefined in content", async () => {
100-
const vaccineType = VaccineType.RSV;
101-
render(<MoreInformation styledVaccineContent={mockStyledContentWithoutWhatSection} vaccineType={vaccineType} />);
129+
it("should not display whatItIsFor section if undefined in content", async () => {
130+
const vaccineType = VaccineType.RSV;
131+
render(<MoreInformation styledVaccineContent={mockStyledContentWithoutWhatSection} vaccineType={vaccineType} />);
102132

103-
const whatItIsForHeading: HTMLElement | null = screen.queryByText("What the vaccine is for");
104-
const whatItIsForContent: HTMLElement | null = screen.queryByText("What Section styled component");
133+
const whatItIsForHeading: HTMLElement | null = screen.queryByText("What the vaccine is for");
134+
const whatItIsForContent: HTMLElement | null = screen.queryByText("What Section styled component");
105135

106-
expect(whatItIsForHeading).not.toBeInTheDocument();
107-
expect(whatItIsForContent).not.toBeInTheDocument();
108-
});
136+
expect(whatItIsForHeading).not.toBeInTheDocument();
137+
expect(whatItIsForContent).not.toBeInTheDocument();
138+
});
109139

110-
it("should display whoVaccineIsFor section even if whatItIsFor is undefined in content", async () => {
111-
const vaccineType = VaccineType.RSV;
112-
render(<MoreInformation styledVaccineContent={mockStyledContentWithoutWhatSection} vaccineType={vaccineType} />);
140+
it("should display whoVaccineIsFor section even if whatItIsFor is undefined in content", async () => {
141+
const vaccineType = VaccineType.RSV;
142+
render(<MoreInformation styledVaccineContent={mockStyledContentWithoutWhatSection} vaccineType={vaccineType} />);
113143

114-
expectExpanderBlockToBePresent("Who should have the vaccine", "Who Section styled component");
144+
expectExpanderBlockToBePresent("Who should have the vaccine", "Who Section styled component");
145+
});
115146
});
116147
});
117148

@@ -122,3 +153,8 @@ const expectExpanderBlockToBePresent = (expanderHeading: string, expanderContent
122153
expect(heading).toBeInTheDocument();
123154
expect(content).toBeInTheDocument();
124155
};
156+
157+
const expectExpanderBlockToNotBePresent = (expanderHeading: string, expanderContent: string) => {
158+
expect(screen.queryByText(expanderHeading)).toBeNull();
159+
expect(screen.queryByText(expanderContent)).toBeNull();
160+
};

src/app/_components/content/MoreInformation.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import React, { JSX } from "react";
99
const MoreInformation = (props: {
1010
styledVaccineContent: StyledVaccineContent;
1111
vaccineType: VaccineType;
12+
isCampaignActive?: boolean;
1213
}): JSX.Element => {
1314
const vaccineInfo = VaccineInfo[props.vaccineType];
14-
const showHowToGetExpander =
15-
vaccineInfo.removeHowToGetExpanderFromMoreInformationSection === undefined ||
16-
!vaccineInfo.removeHowToGetExpanderFromMoreInformationSection;
15+
const showHowToGetExpander = vaccineInfo.removeHowToGetExpanderFromMoreInformationSection
16+
? false
17+
: !props.isCampaignActive;
1718

1819
return (
1920
<>

src/app/_components/vaccine/Vaccine.integration.test.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import { mockNHSAppJSFunctions } from "@src/utils/nhsapp-js.test";
77
import { ConfigMock, configBuilder } from "@test-data/config/builders";
88
import { eligibilityApiResponseBuilder } from "@test-data/eligibility-api/builders";
99
import { render, screen } from "@testing-library/react";
10-
import { ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapters/headers";
11-
import { headers } from "next/headers";
1210

1311
jest.mock("@src/utils/auth/generate-auth-payload", () => jest.fn());
1412
jest.mock("@src/services/eligibility-api/gateway/fetch-eligibility-content", () => ({
@@ -45,6 +43,7 @@ const nhsNumber = "5123456789";
4543

4644
describe("Vaccine", () => {
4745
const mockedConfig = config as ConfigMock;
46+
4847
beforeAll(() => {
4948
const defaultConfig = configBuilder().withContentCachePath("wiremock/__files/").withPinoLogLevel("info").build();
5049
Object.assign(mockedConfig, defaultConfig);
@@ -55,12 +54,6 @@ describe("Vaccine", () => {
5554
nhs_number: nhsNumber,
5655
},
5756
});
58-
const fakeHeaders: ReadonlyHeaders = {
59-
get(name: string): string | null {
60-
return `fake-${name}-header`;
61-
},
62-
} as ReadonlyHeaders;
63-
(headers as jest.Mock).mockResolvedValue(fakeHeaders);
6457
});
6558

6659
it.each([VaccineType.RSV, VaccineType.RSV_PREGNANCY])(

src/app/_components/vaccine/Vaccine.test.tsx

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ import {
1212
EligibilityForPersonType,
1313
EligibilityStatus,
1414
} from "@src/services/eligibility-api/types";
15+
import { Campaigns } from "@src/utils/campaigns/types";
16+
import config from "@src/utils/config";
17+
import { ConfigMock, configBuilder } from "@test-data/config/builders";
1518
import { mockStyledContent } from "@test-data/content-api/data";
1619
import { eligibilityContentBuilder } from "@test-data/eligibility-api/builders";
1720
import { render, screen } from "@testing-library/react";
18-
import { ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapters/headers";
19-
import { headers } from "next/headers";
2021
import React, { JSX } from "react";
2122

2223
jest.mock("@src/services/content-api/content-service", () => ({
@@ -58,17 +59,16 @@ jest.mock("next/headers", () => ({
5859
headers: jest.fn(),
5960
}));
6061
jest.mock("sanitize-data", () => ({ sanitize: jest.fn() }));
62+
jest.mock("@src/utils/config");
6163
jest.mock("cheerio", () => ({
6264
load: jest.fn(() => {
6365
const selectorImpl = jest.fn(() => ({
6466
attr: jest.fn(),
6567
}));
6668

67-
const $ = Object.assign(selectorImpl, {
69+
return Object.assign(selectorImpl, {
6870
html: jest.fn(() => "<p>HTML fragment</p>"),
6971
});
70-
71-
return $;
7272
}),
7373
}));
7474

@@ -97,9 +97,20 @@ const contentErrorResponse = {
9797
};
9898

9999
describe("Any vaccine page", () => {
100-
const renderNamedVaccinePage = async (vaccineType: VaccineType) => {
101-
render(await Vaccine({ vaccineType: vaccineType }));
102-
};
100+
const mockedConfig = config as ConfigMock;
101+
102+
beforeEach(() => {
103+
const defaultConfig = configBuilder()
104+
.withCampaigns(
105+
Campaigns.fromJson(
106+
JSON.stringify({
107+
COVID_19: [{ start: "2025-11-01T09:00:00Z", end: "2026-01-31T09:00:00Z" }],
108+
}),
109+
)!,
110+
)
111+
.build();
112+
Object.assign(mockedConfig, defaultConfig);
113+
});
103114

104115
const renderRsvVaccinePage = async () => {
105116
await renderNamedVaccinePage(VaccineType.RSV);
@@ -119,12 +130,6 @@ describe("Any vaccine page", () => {
119130
nhs_number: nhsNumber,
120131
},
121132
});
122-
const fakeHeaders: ReadonlyHeaders = {
123-
get(name: string): string | null {
124-
return `fake-${name}-header`;
125-
},
126-
} as ReadonlyHeaders;
127-
(headers as jest.Mock).mockResolvedValue(fakeHeaders);
128133
});
129134

130135
describe("shows content section, when content available", () => {
@@ -215,6 +220,61 @@ describe("Any vaccine page", () => {
215220
});
216221
});
217222

223+
describe("shows callouts and actions for Vaccines that handle campaigns (COVID_19)", () => {
224+
const mockedConfig = config as ConfigMock;
225+
const campaigns = new Campaigns({});
226+
const covid19VaccineType = VaccineType.COVID_19;
227+
228+
beforeEach(() => {
229+
(getContentForVaccine as jest.Mock).mockResolvedValue(contentSuccessResponse);
230+
(getEligibilityForPerson as jest.Mock).mockResolvedValue(eligibilitySuccessResponse);
231+
const defaultConfig = configBuilder().withCampaigns(campaigns).build();
232+
Object.assign(mockedConfig, defaultConfig);
233+
});
234+
235+
it("should include callout text when campaign is inactive", async () => {
236+
const inactiveCampaignSpy = jest.spyOn(campaigns, "isActive").mockReturnValue(false);
237+
await renderNamedVaccinePage(covid19VaccineType);
238+
239+
const calloutText: HTMLElement = screen.getByTestId("callout");
240+
241+
expect(inactiveCampaignSpy).toHaveBeenCalledWith(covid19VaccineType, expect.any(Date));
242+
expect(calloutText).toBeInTheDocument();
243+
inactiveCampaignSpy.mockRestore();
244+
});
245+
246+
it("should not include callout text when campaign is active", async () => {
247+
const activeCampaignSpy = jest.spyOn(campaigns, "isActive").mockReturnValue(true);
248+
await renderNamedVaccinePage(covid19VaccineType);
249+
250+
const calloutText: HTMLElement | null = screen.queryByTestId("callout");
251+
252+
expect(activeCampaignSpy).toHaveBeenCalledWith(covid19VaccineType, expect.any(Date));
253+
expect(calloutText).toBeNull();
254+
activeCampaignSpy.mockRestore();
255+
});
256+
257+
it("should include actions when campaign is active", async () => {
258+
const activeCampaignSpy = jest.spyOn(campaigns, "isActive").mockReturnValue(true);
259+
await renderNamedVaccinePage(covid19VaccineType);
260+
261+
const actions: HTMLElement = screen.getByTestId("eligibility-actions-mock");
262+
expect(activeCampaignSpy).toHaveBeenCalledWith(covid19VaccineType, expect.any(Date));
263+
expect(actions).toBeInTheDocument();
264+
activeCampaignSpy.mockRestore();
265+
});
266+
267+
it("should not include actions when campaign is inactive", async () => {
268+
const inactiveCampaignSpy = jest.spyOn(campaigns, "isActive").mockReturnValue(false);
269+
await renderNamedVaccinePage(covid19VaccineType);
270+
271+
const actions: HTMLElement | null = screen.queryByTestId("eligibility-actions-mock");
272+
expect(inactiveCampaignSpy).toHaveBeenCalledWith(covid19VaccineType, expect.any(Date));
273+
expect(actions).toBeNull();
274+
inactiveCampaignSpy.mockRestore();
275+
});
276+
});
277+
218278
describe("shows content section, when content load fails", () => {
219279
beforeEach(() => {
220280
(getContentForVaccine as jest.Mock).mockResolvedValue(contentErrorResponse);
@@ -398,6 +458,10 @@ describe("Any vaccine page", () => {
398458
});
399459
});
400460

461+
const renderNamedVaccinePage = async (vaccineType: VaccineType) => {
462+
render(await Vaccine({ vaccineType: vaccineType }));
463+
};
464+
401465
const expectRenderEligibilitySectionWith = (
402466
vaccineType: VaccineType,
403467
eligibilityForPerson: EligibilityForPersonType,

src/app/_components/vaccine/Vaccine.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { getContentForVaccine } from "@src/services/content-api/content-service"
1515
import { ContentErrorTypes, StyledVaccineContent } from "@src/services/content-api/types";
1616
import { getEligibilityForPerson } from "@src/services/eligibility-api/domain/eligibility-filter-service";
1717
import { EligibilityErrorTypes, EligibilityForPersonType } from "@src/services/eligibility-api/types";
18+
import config from "@src/utils/config";
19+
import { getNow } from "@src/utils/date";
1820
import { profilePerformanceEnd, profilePerformanceStart } from "@src/utils/performance";
1921
import { requestScopedStorageWrapper } from "@src/utils/requestScopedStorageWrapper";
2022
import { Session } from "next-auth";
@@ -39,6 +41,9 @@ const VaccineComponent = async ({ vaccineType }: VaccineProps): Promise<JSX.Elem
3941
const nhsNumber: NhsNumber | undefined = session?.user.nhs_number as NhsNumber;
4042
const vaccineInfo: VaccineDetails = VaccineInfo[vaccineType];
4143

44+
const campaigns = await config.CAMPAIGNS;
45+
const isCampaignActive: boolean = campaigns.isActive(vaccineType, await getNow());
46+
4247
let styledVaccineContent: StyledVaccineContent | undefined;
4348
let contentError: ContentErrorTypes | undefined;
4449
let eligibilityForPerson: EligibilityForPersonType | undefined;
@@ -71,8 +76,10 @@ const VaccineComponent = async ({ vaccineType }: VaccineProps): Promise<JSX.Elem
7176
<>
7277
<Overview overview={styledVaccineContent.overview} vaccineType={vaccineType} />
7378
<Recommendation styledVaccineContent={styledVaccineContent} />
74-
<WarningCallout styledVaccineContent={styledVaccineContent} vaccineType={vaccineType} />
75-
<EligibilityActions actions={styledVaccineContent.actions} vaccineType={vaccineType} />
79+
{!isCampaignActive && (
80+
<WarningCallout styledVaccineContent={styledVaccineContent} vaccineType={vaccineType} />
81+
)}
82+
{isCampaignActive && <EligibilityActions actions={styledVaccineContent.actions} vaccineType={vaccineType} />}
7683
<Overview overview={styledVaccineContent.overviewConclusion} vaccineType={vaccineType} />
7784
</>
7885
)}
@@ -97,7 +104,11 @@ const VaccineComponent = async ({ vaccineType }: VaccineProps): Promise<JSX.Elem
97104
<h2 className="nhsuk-heading-s">{`More information about the ${vaccineInfo.displayName.midSentenceCase} ${vaccineInfo.displayName.suffix}`}</h2>
98105
{/* Expandable sections */}
99106
{contentError != ContentErrorTypes.CONTENT_LOADING_ERROR && styledVaccineContent != undefined ? (
100-
<MoreInformation styledVaccineContent={styledVaccineContent} vaccineType={vaccineType} />
107+
<MoreInformation
108+
styledVaccineContent={styledVaccineContent}
109+
vaccineType={vaccineType}
110+
isCampaignActive={isCampaignActive}
111+
/>
101112
) : (
102113
<FindOutMoreLink findOutMoreUrl={vaccineInfo.nhsWebpageLink} vaccineType={vaccineType} />
103114
)}

0 commit comments

Comments
 (0)