diff --git a/src/_lambda/content-cache-hydrator/handler.ts b/src/_lambda/content-cache-hydrator/handler.ts index d90378ac..725e78bd 100644 --- a/src/_lambda/content-cache-hydrator/handler.ts +++ b/src/_lambda/content-cache-hydrator/handler.ts @@ -22,7 +22,7 @@ const checkContentPassesStylingAndWriteToCache = async ( filteredContent: VaccinePageContent, ): Promise => { try { - await getStyledContentForVaccine(filteredContent); + await getStyledContentForVaccine(filteredContent, vaccineType); await writeContentForVaccine(vaccineType, content); log.info({ context: { vaccineType } }, `Content written to cache for vaccine ${vaccineType} `); } catch (error) { diff --git a/src/app/_components/eligibility/RSVEligibilityFallback.test.tsx b/src/app/_components/eligibility/RSVEligibilityFallback.test.tsx index ce25d84e..803356bf 100644 --- a/src/app/_components/eligibility/RSVEligibilityFallback.test.tsx +++ b/src/app/_components/eligibility/RSVEligibilityFallback.test.tsx @@ -1,16 +1,9 @@ import { HowToGetVaccineFallback } from "@src/app/_components/content/HowToGetVaccineFallback"; import { RSVEligibilityFallback } from "@src/app/_components/eligibility/RSVEligibilityFallback"; -import { PharmacyBookingInfo } from "@src/app/_components/nbs/PharmacyBookingInfo"; import { VaccineType } from "@src/models/vaccine"; import { mockStyledContent } from "@test-data/content-api/data"; import { render, screen, within } from "@testing-library/react"; -jest.mock("@src/app/_components/nbs/PharmacyBookingInfo", () => ({ - PharmacyBookingInfo: jest - .fn() - .mockImplementation(() =>

Pharmacy booking test

), -})); - jest.mock("@src/app/_components/content/HowToGetVaccineFallback", () => ({ HowToGetVaccineFallback: jest .fn() @@ -24,40 +17,20 @@ describe("RSVEligibilityFallback", () => { render(); const fallbackHeading: HTMLElement = screen.getByText("The RSV vaccine is recommended if you:"); - const fallbackBulletPoint1: HTMLElement = screen.getByText("are aged between 75 and 79"); - const fallbackBulletPoint2: HTMLElement = screen.getByText("turned 80 after 1 September 2024"); + const fallbackBulletPoint1: HTMLElement = screen.getByText("are aged 75 or over"); + const fallbackBulletPoint2: HTMLElement = screen.getByText("live in a care home for older adults"); expect(fallbackHeading).toBeVisible(); expect(fallbackBulletPoint1).toBeVisible(); expect(fallbackBulletPoint2).toBeVisible(); }); - it("should include Pharmacy booking info for specified vaccine", () => { - render(); - - const pharmacyBookingInfo: HTMLElement = screen.getByTestId("pharmacy-booking-info"); - expect(pharmacyBookingInfo).toBeVisible(); - - expect(PharmacyBookingInfo).toHaveBeenCalledWith( - { - vaccineType: vaccineType, - }, - undefined, - ); - }); - - it("should display 'If this applies' paragraph with styled how-to-get from content API", async () => { + it("should display styled how-to-get from content API", async () => { render(); const fallback = screen.getByTestId("elid-fallback"); - - const fallbackHeading: HTMLElement = within(fallback).getByRole("heading", { - name: "If this applies to you", - level: 3, - }); const howToGetFromContentAPI: HTMLElement = within(fallback).getByText("How Section styled component"); - expect(fallbackHeading).toBeVisible(); expect(howToGetFromContentAPI).toBeVisible(); const howToGetFallback: HTMLElement | null = within(fallback).queryByText("How to get fallback"); @@ -68,14 +41,8 @@ describe("RSVEligibilityFallback", () => { render(); const fallback = screen.getByTestId("elid-fallback"); - - const fallbackHeading: HTMLElement = within(fallback).getByRole("heading", { - name: "If this applies to you", - level: 3, - }); const howToGetFallback: HTMLElement = within(fallback).getByText("How to get fallback"); - expect(fallbackHeading).toBeVisible(); expect(howToGetFallback).toBeVisible(); expect(HowToGetVaccineFallback).toHaveBeenCalledWith( diff --git a/src/app/_components/eligibility/RSVEligibilityFallback.tsx b/src/app/_components/eligibility/RSVEligibilityFallback.tsx index 2451f49a..80806727 100644 --- a/src/app/_components/eligibility/RSVEligibilityFallback.tsx +++ b/src/app/_components/eligibility/RSVEligibilityFallback.tsx @@ -1,7 +1,5 @@ import { HowToGetVaccineFallback } from "@src/app/_components/content/HowToGetVaccineFallback"; -import { PharmacyBookingInfo } from "@src/app/_components/nbs/PharmacyBookingInfo"; import NonUrgentCareCard from "@src/app/_components/nhs-frontend/NonUrgentCareCard"; -import { HEADINGS } from "@src/app/constants"; import { VaccineType } from "@src/models/vaccine"; import { StyledVaccineContent } from "@src/services/content-api/types"; import React, { JSX } from "react"; @@ -24,15 +22,13 @@ const RSVEligibilityFallback = (props: { content={ <>
    -
  • {"are aged between 75 and 79"}
  • -
  • {"turned 80 after 1 September 2024"}
  • +
  • {"are aged 75 or over"}
  • +
  • {"live in a care home for older adults"}
} /> -

{HEADINGS.IF_THIS_APPLIES}

{howToGetVaccineOrFallback} - ); }; diff --git a/src/app/_components/nbs/PharmacyBookingInfo.test.tsx b/src/app/_components/nbs/PharmacyBookingInfo.test.tsx index 06b975dc..bbf9b51e 100644 --- a/src/app/_components/nbs/PharmacyBookingInfo.test.tsx +++ b/src/app/_components/nbs/PharmacyBookingInfo.test.tsx @@ -30,9 +30,7 @@ describe("PharmacyBookingInfo", () => { it("should contain include correct text for older adults", () => { render(); - const pharmacyBookingInfo: HTMLElement = screen.getByText( - /This pharmacy service is only for adults aged 75 to 79./, - ); + const pharmacyBookingInfo: HTMLElement = screen.getByText(/In some areas you can /); expect(pharmacyBookingInfo).toBeInTheDocument(); }); @@ -40,10 +38,8 @@ describe("PharmacyBookingInfo", () => { it("should contain include correct text for younger adults", () => { render(); - const pharmacyBookingInfo: HTMLElement | null = screen.queryByText( - /This pharmacy service is only for adults aged 75 to 79./, - ); + const pharmacyBookingInfo: HTMLElement | null = screen.queryByText(/In some areas you can also /); - expect(pharmacyBookingInfo).not.toBeInTheDocument(); + expect(pharmacyBookingInfo).toBeInTheDocument(); }); }); diff --git a/src/app/_components/nbs/PharmacyBookingInfo.tsx b/src/app/_components/nbs/PharmacyBookingInfo.tsx index aee0f910..ba6a2f86 100644 --- a/src/app/_components/nbs/PharmacyBookingInfo.tsx +++ b/src/app/_components/nbs/PharmacyBookingInfo.tsx @@ -18,7 +18,7 @@ const PharmacyBookingInfo = ({ vaccineType }: PharmacyBookingProps): JSX.Element renderAs={"anchor"} reduceBottomPadding={false} /> - {vaccineInfo.forOlderAdults ? ". This pharmacy service is only for adults aged 75 to 79." : "."} + {"."}

); }; diff --git a/src/app/constants.ts b/src/app/constants.ts index 1ea41e76..0702662e 100644 --- a/src/app/constants.ts +++ b/src/app/constants.ts @@ -11,5 +11,4 @@ export const HEADINGS = { HOW_TO_GET_VACCINE: "How to get the vaccine", EXTRA_DOSES_SCHEDULE: "Extra doses of the vaccine", VACCINE_SIDE_EFFECTS: "Side effects of the vaccine", - IF_THIS_APPLIES: "If this applies to you", }; diff --git a/src/app/vaccines-for-all-ages/page.test.tsx b/src/app/vaccines-for-all-ages/page.test.tsx index e22a980c..52261f8f 100644 --- a/src/app/vaccines-for-all-ages/page.test.tsx +++ b/src/app/vaccines-for-all-ages/page.test.tsx @@ -89,7 +89,12 @@ describe("VaccinesForAllAges", () => { description: "65 to 67 and 70 to 79 years", path: "/vaccines/shingles-vaccine", }, - { section: AgeSectionTestId.ADULTS, cardTitle: "RSV", description: "75 years and over", path: "/vaccines/rsv" }, + { + section: AgeSectionTestId.ADULTS, + cardTitle: "RSV", + description: "75 years and over, or living in a care home for older adults", + path: "/vaccines/rsv", + }, { section: AgeSectionTestId.ADULTS, cardTitle: "COVID-19", diff --git a/src/models/ageBasedHub.test.tsx b/src/models/ageBasedHub.test.tsx index 03c4a98c..708d0800 100644 --- a/src/models/ageBasedHub.test.tsx +++ b/src/models/ageBasedHub.test.tsx @@ -133,8 +133,8 @@ describe("AgeBasedHubInfo", () => { expect(info.heading).toBe("Adults aged 65 to 74 should get these routine vaccines"); }); - it("has 3 vaccines", () => { - expect(info.vaccines).toHaveLength(4); + it("has expected #vaccines", () => { + expect(info.vaccines).toHaveLength(5); }); it("has correct vaccine names", () => { @@ -142,6 +142,7 @@ describe("AgeBasedHubInfo", () => { VaccineType.PNEUMOCOCCAL, VaccineType.FLU_FOR_ADULTS, VaccineType.SHINGLES, + VaccineType.RSV, VaccineType.COVID_19, ]); }); diff --git a/src/models/ageBasedHub.tsx b/src/models/ageBasedHub.tsx index bfe15456..bd7e365c 100644 --- a/src/models/ageBasedHub.tsx +++ b/src/models/ageBasedHub.tsx @@ -141,6 +141,7 @@ const AgeBasedHubInfo: Record = { { vaccineName: VaccineType.PNEUMOCOCCAL, cardLinkDescription: "65 years and over" }, { vaccineName: VaccineType.FLU_FOR_ADULTS, cardLinkDescription: "65 years and over" }, { vaccineName: VaccineType.SHINGLES, cardLinkDescription: "65 to 67 and 70 to 79 years" }, + { vaccineName: VaccineType.RSV, cardLinkDescription: "If living in a care home for older adults" }, { vaccineName: VaccineType.COVID_19, cardLinkDescription: "If living in a care home for older adults" }, ], showPregnancyHubContent: false, diff --git a/src/models/vaccine.ts b/src/models/vaccine.ts index 3090a851..2e10a192 100644 --- a/src/models/vaccine.ts +++ b/src/models/vaccine.ts @@ -395,7 +395,7 @@ const adultVaccines: VaccineCardDetails[] = [ { vaccineName: VaccineType.PNEUMOCOCCAL, cardLinkDescription: "65 years and over" }, { vaccineName: VaccineType.FLU_FOR_ADULTS, cardLinkDescription: "65 years and over" }, { vaccineName: VaccineType.SHINGLES, cardLinkDescription: "65 to 67 and 70 to 79 years" }, - { vaccineName: VaccineType.RSV, cardLinkDescription: "75 years and over" }, + { vaccineName: VaccineType.RSV, cardLinkDescription: "75 years and over, or living in a care home for older adults" }, { vaccineName: VaccineType.COVID_19, cardLinkDescription: "75 years and over, or living in a care home for older adults", diff --git a/src/services/content-api/content-api.integration.test.tsx b/src/services/content-api/content-api.integration.test.tsx index 076a4533..efbc6a68 100644 --- a/src/services/content-api/content-api.integration.test.tsx +++ b/src/services/content-api/content-api.integration.test.tsx @@ -76,7 +76,7 @@ describe("Content Filter Service Regex", () => { it("should extract how to get section text for Rsv Older Adults", async () => { const expectedHowToGetContentRsvOlderAdults = - "

If you're aged 75 to 79 (or turned 80 after 1 September 2024) contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

"; + "

If you're aged 75 or over

Contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

"; const defaultConfig = configBuilder().withContentCachePath("wiremock/__files/").build(); Object.assign(mockedConfig, defaultConfig); diff --git a/src/services/content-api/content-service.ts b/src/services/content-api/content-service.ts index 80d1136c..46490c5a 100644 --- a/src/services/content-api/content-service.ts +++ b/src/services/content-api/content-service.ts @@ -30,7 +30,7 @@ const getContentForVaccine = async (vaccineType: VaccineType): Promise ({ MarkdownWithStyling: jest.fn(), })); +jest.mock("@src/app/_components/nbs/PharmacyBookingInfo", () => ({ + PharmacyBookingInfo: () =>
Pharmacy Booking Info
, +})); + jest.mock("sanitize-data", () => ({ sanitize: jest.fn() })); jest.mock("cheerio", () => ({ load: jest.fn(() => { @@ -70,7 +74,7 @@ describe("ContentStylingService", () => { const mockHowToGetMarkdownSubsection: VaccinePageSubsection = { type: "simpleElement", - text: "

para

If you're aged 75 to 79

para1

para2

If you're pregnant

para3

para4

", + text: "

para

If you're aged 75 or over

para1

para2

If you live in a care home for older adults

para3

para4

If you're pregnant

para5

para6

", name: "markdown", headline: "How To Get Headline", }; @@ -334,40 +338,80 @@ describe("ContentStylingService", () => { additionalInformation: mockAdditionalInformationSection, }; - it.each(Object.values(VaccineType))("should return styled content for %s", async () => { - const styledVaccineContent: StyledVaccineContent = await getStyledContentForVaccine(mockContent); - + const assertCommonStyledSections = ( + styledVaccineContent: StyledVaccineContent, + expectedHowToGetHeadline: string, + ) => { expect(styledVaccineContent).not.toBeNull(); expect(styledVaccineContent.overview).toEqual(mockContent.overview); expect(styledVaccineContent.whatVaccineIsFor?.heading).toEqual(mockWhatSection.headline); expect(styledVaccineContent.whoVaccineIsFor.heading).toEqual(mockWhoSection.headline); - expect(styledVaccineContent.howToGetVaccine.heading).toEqual(mockHowSection.headline); + expect(styledVaccineContent.howToGetVaccine.heading).toEqual(expectedHowToGetHeadline); expect(styledVaccineContent.vaccineSideEffects.heading).toEqual(mockSideEffectsSection.headline); expect(styledVaccineContent.extraDosesSchedule?.heading).toEqual(mockExtraDosesScheduleSection.headline); expect(styledVaccineContent.callout?.heading).toEqual(mockCallout.heading); expect(styledVaccineContent.recommendation?.heading).toEqual(mockRecommendation.heading); expect(styledVaccineContent.additionalInformation?.heading).toEqual(mockAdditionalInformationSection.headline); - const expectedGenericVaccineHowToGetSection = - "

How To Get Headline

para

If you're aged 75 to 79

para1

para2

If you're pregnant

para3

para4

"; - const { container } = render(styledVaccineContent.howToGetVaccine.component); - expect(container.innerHTML).toBe(expectedGenericVaccineHowToGetSection); - expect(isValidElement(styledVaccineContent.whatVaccineIsFor?.component)).toBe(true); expect(isValidElement(styledVaccineContent.whoVaccineIsFor.component)).toBe(true); expect(isValidElement(styledVaccineContent.howToGetVaccine.component)).toBe(true); expect(isValidElement(styledVaccineContent.vaccineSideEffects.component)).toBe(true); - expect(isValidElement(styledVaccineContent.additionalInformation?.component)).toBe(true); expect(styledVaccineContent.webpageLink).toEqual(new URL("https://test.example.com/")); + }; + + it.each(Object.values(VaccineType).filter((t) => t !== VaccineType.RSV))( + "should return styled content for %s", + async (vaccineType) => { + const styledVaccineContent: StyledVaccineContent = await getStyledContentForVaccine(mockContent, vaccineType); + + assertCommonStyledSections(styledVaccineContent, mockHowSection.headline); + + const expectedGenericVaccineHowToGetSection = + "

How To Get Headline

para

If you're aged 75 or over

para1

para2

If you live in a care home for older adults

para3

para4

If you're pregnant

para5

para6

"; + const { container } = render(styledVaccineContent.howToGetVaccine.component); + expect(container.innerHTML).toBe(expectedGenericVaccineHowToGetSection); + }, + ); + + it("should return styled content for RSV with PharmacyBookingInfo between older adults and care home subsections", async () => { + const mockRsvOlderAdultsSubsection: VaccinePageSubsection = { + type: "simpleElement", + headline: "", + name: "markdown", + text: "

If you're aged 75 or over

para1

para2

", + }; + const mockRsvCareHomeSubsection: VaccinePageSubsection = { + type: "simpleElement", + headline: "", + name: "markdown", + text: "

If you live in a care home for older adults

para3

para4

", + }; + const mockRsvHowSection: VaccinePageSection = { + headline: "How to get this Vaccine", + subsections: [mockRsvOlderAdultsSubsection, mockRsvCareHomeSubsection], + }; + const mockRsvContent: VaccinePageContent = { ...mockContent, howToGetVaccine: mockRsvHowSection }; + + const styledVaccineContent = await getStyledContentForVaccine(mockRsvContent, VaccineType.RSV); + + assertCommonStyledSections(styledVaccineContent, mockRsvHowSection.headline); + + const expectedRsvHowToGetSection = + '

If you\'re aged 75 or over

para1

para2

Pharmacy Booking Info

If you live in a care home for older adults

para3

para4

'; + const { container } = render(styledVaccineContent.howToGetVaccine.component); + expect(container.innerHTML).toBe(expectedRsvHowToGetSection); }); it("should return styled content without what-section when what-section is missing", async () => { const mockContentWithoutWhatSection = { ...mockContent }; delete mockContentWithoutWhatSection.whatVaccineIsFor; - const styledVaccineContent: StyledVaccineContent = - await getStyledContentForVaccine(mockContentWithoutWhatSection); + const styledVaccineContent: StyledVaccineContent = await getStyledContentForVaccine( + mockContentWithoutWhatSection, + VaccineType.COVID_19, + ); expect(styledVaccineContent).not.toBeNull(); expect(styledVaccineContent.overview).toEqual({ content: "This is an overview", containsHtml: false }); @@ -384,6 +428,7 @@ describe("ContentStylingService", () => { const styledVaccineContent: StyledVaccineContent = await getStyledContentForVaccine( mockContentWithoutRecommendation, + VaccineType.COVID_19, ); expect(styledVaccineContent.recommendation).toBeUndefined(); @@ -395,6 +440,7 @@ describe("ContentStylingService", () => { const styledVaccineContent: StyledVaccineContent = await getStyledContentForVaccine( mockContentWithoutAdditionalInformation, + VaccineType.COVID_19, ); expect(styledVaccineContent.additionalInformation).toBeUndefined(); @@ -406,6 +452,7 @@ describe("ContentStylingService", () => { const styledVaccineContent: StyledVaccineContent = await getStyledContentForVaccine( mockContentWithoutExtraDosesSchedule, + VaccineType.COVID_19, ); expect(styledVaccineContent.extraDosesSchedule).toBeUndefined(); diff --git a/src/services/content-api/parsers/content-styling-service.tsx b/src/services/content-api/parsers/content-styling-service.tsx index 462eacbf..da55349d 100644 --- a/src/services/content-api/parsers/content-styling-service.tsx +++ b/src/services/content-api/parsers/content-styling-service.tsx @@ -1,4 +1,6 @@ import { MarkdownWithStyling } from "@project/src/app/_components/markdown/MarkdownWithStyling"; +import { PharmacyBookingInfo } from "@project/src/app/_components/nbs/PharmacyBookingInfo"; +import { VaccineType } from "@project/src/models/vaccine"; import EmergencyCareCard from "@src/app/_components/nhs-frontend/EmergencyCareCard"; import NonUrgentCareCard from "@src/app/_components/nhs-frontend/NonUrgentCareCard"; import UrgentCareCard from "@src/app/_components/nhs-frontend/UrgentCareCard"; @@ -156,6 +158,20 @@ const styleHowToGetSection = (section: VaccinePageSection): StyledPageSection => return styleSection(section); }; +const styleHowToGetSectionForRSV = (section: VaccinePageSection): StyledPageSection => { + const [olderAdultsSubsection, careHomeSubsection] = section.subsections; + + const styledComponent = ( + <> + {styleSubsection(olderAdultsSubsection, 0, false)} + + {styleSubsection(careHomeSubsection, 1, true)} + + ); + + return { heading: section.headline, component: styledComponent }; +}; + function styleCallout(callout: HeadingWithTypedContent | undefined): StyledPageSection | undefined { if (callout) { switch (callout?.contentType) { @@ -198,7 +214,10 @@ function styleRecommendation(recommendation: HeadingWithContent | undefined): St return undefined; } -const getStyledContentForVaccine = async (filteredContent: VaccinePageContent): Promise => { +const getStyledContentForVaccine = async ( + filteredContent: VaccinePageContent, + vaccineType: VaccineType, +): Promise => { const overview: Overview | undefined = filteredContent.overview; const callout: StyledPageSection | undefined = styleCallout(filteredContent.callout); let additionalInformation: StyledPageSection | undefined; @@ -214,7 +233,11 @@ const getStyledContentForVaccine = async (filteredContent: VaccinePageContent): whatVaccineIsFor = styleSection(filteredContent.whatVaccineIsFor); } const whoVaccineIsFor: StyledPageSection = styleSection(filteredContent.whoVaccineIsFor); - const howToGetVaccine: StyledPageSection = styleHowToGetSection(filteredContent.howToGetVaccine); + const howToGetVaccine: StyledPageSection = + vaccineType === VaccineType.RSV + ? styleHowToGetSectionForRSV(filteredContent.howToGetVaccine) + : styleHowToGetSection(filteredContent.howToGetVaccine); + const vaccineSideEffects: StyledPageSection = styleSection(filteredContent.vaccineSideEffects); let extraDosesSchedule; if (filteredContent.extraDosesSchedule) { diff --git a/src/services/content-api/parsers/custom/extract-html.test.ts b/src/services/content-api/parsers/custom/extract-html.test.ts index cb3e7a6a..2dfc1134 100644 --- a/src/services/content-api/parsers/custom/extract-html.test.ts +++ b/src/services/content-api/parsers/custom/extract-html.test.ts @@ -1,6 +1,6 @@ import { VaccinePageSubsection } from "../../types"; import { ContentParsingError } from "./exceptions"; -import { extractHtmlFromSubSectionByHeading } from "./extract-html"; +import { extractHtmlFromSubSectionByHeading, extractHtmlWithHeadingFromSubSectionByHeading } from "./extract-html"; jest.mock("sanitize-data", () => ({ sanitize: jest.fn() })); @@ -57,4 +57,57 @@ describe("extract-html", () => { }).toThrow(ContentParsingError); }); }); + + describe("extractHtmlWithHeadingFromSubSectionByHeading", () => { + const olderAdultsRegExp: RegExp = /

If you're aged \d+ or over<\/h3>((?:\s*

.*?<\/p>)+)/i; + + const subSection: VaccinePageSubsection = { + type: "simpleElement", + headline: "", + text: "

Intro.

If you're aged 75 or over

Para one.

Para two.

If you're pregnant

Pregnancy para.

", + name: "markdown", + }; + + it("should return the heading and paragraphs when matching section header is found", () => { + const expectedExtractedHtml = "

If you're aged 75 or over

Para one.

Para two.

"; + + const extractedHtml = extractHtmlWithHeadingFromSubSectionByHeading(subSection, olderAdultsRegExp); + + expect(extractedHtml).toEqual(expectedExtractedHtml); + }); + + it("throws ContentParsingError if no heading matching section heading is found", () => { + const subSectionWithNoMatchingHeader: VaccinePageSubsection = { + ...subSection, + text: "

Intro.

If you're pregnant

Pregnancy para.

", + }; + + expect(() => { + extractHtmlWithHeadingFromSubSectionByHeading(subSectionWithNoMatchingHeader, olderAdultsRegExp); + }).toThrow(ContentParsingError); + }); + + it("throws ContentParsingError if no paragraphs found for section heading", () => { + const subSectionWithNoParagraphs: VaccinePageSubsection = { + ...subSection, + text: "

Intro.

If you're aged 75 or over

If you're pregnant

Pregnancy para.

", + }; + + expect(() => { + extractHtmlWithHeadingFromSubSectionByHeading(subSectionWithNoParagraphs, olderAdultsRegExp); + }).toThrow(ContentParsingError); + }); + + it("throws ContentParsingError if type is not 'simpleElement'", () => { + const subSectionWithoutSimpleElement: VaccinePageSubsection = { + type: "tableElement", + name: "", + mainEntity: "", + }; + + expect(() => { + extractHtmlWithHeadingFromSubSectionByHeading(subSectionWithoutSimpleElement, olderAdultsRegExp); + }).toThrow(ContentParsingError); + }); + }); }); diff --git a/src/services/content-api/parsers/custom/extract-html.ts b/src/services/content-api/parsers/custom/extract-html.ts index de3ec06a..84b0ef47 100644 --- a/src/services/content-api/parsers/custom/extract-html.ts +++ b/src/services/content-api/parsers/custom/extract-html.ts @@ -7,29 +7,47 @@ const log: Logger = logger.child({ module: "services-content-api-parsers-custom- const paragraphsRegExp: RegExp = /

.*?<\/p>/g; -export const extractHtmlFromSubSectionByHeading = ( +const parseSubSectionMatches = ( subsection: VaccinePageSubsection, sectionHeadingRegEx: RegExp, -): string => { +): { heading: string; paragraphs: string[] } => { if (subsection.type !== "simpleElement") { log.warn({ context: { type: subsection.type } }, "HowToGetSubsection element not found"); throw new ContentParsingError("HowToGetSubsection element not found"); } - const howToGetSectionMatches = sectionHeadingRegEx.exec(subsection.text); - if (!howToGetSectionMatches) { + const sectionMatches = sectionHeadingRegEx.exec(subsection.text); + if (!sectionMatches) { log.warn({ context: { text: subsection.text } }, "HowToGetSubsection header not found - has the content changed?"); throw new ContentParsingError("HowToGetSubsection header not found - has the content changed?"); } - const paragraphsMatches = howToGetSectionMatches[1].match(paragraphsRegExp); + const [fullMatch, capturedContent] = sectionMatches; + + const paragraphsMatches = capturedContent.match(paragraphsRegExp); if (!paragraphsMatches) { log.warn( - { context: { text: howToGetSectionMatches[1] } }, + { context: { text: capturedContent } }, "HowToGetSubsection paragraph not found - has the content changed?", ); throw new ContentParsingError("HowToGetSubsection paragraph not found - has the content changed?"); } + const heading = fullMatch.slice(0, -capturedContent.length); + return { heading, paragraphs: paragraphsMatches }; +}; - return paragraphsMatches.join(""); +export const extractHtmlFromSubSectionByHeading = ( + subsection: VaccinePageSubsection, + sectionHeadingRegEx: RegExp, +): string => { + const { paragraphs } = parseSubSectionMatches(subsection, sectionHeadingRegEx); + return paragraphs.join(""); +}; + +export const extractHtmlWithHeadingFromSubSectionByHeading = ( + subsection: VaccinePageSubsection, + sectionHeadingRegEx: RegExp, +): string => { + const { heading, paragraphs } = parseSubSectionMatches(subsection, sectionHeadingRegEx); + return `${heading}${paragraphs.join("")}`; }; diff --git a/src/services/content-api/parsers/custom/rsv-pregnancy.test.tsx b/src/services/content-api/parsers/custom/rsv-pregnancy.test.tsx index 1c9e97ee..b5dd9b69 100644 --- a/src/services/content-api/parsers/custom/rsv-pregnancy.test.tsx +++ b/src/services/content-api/parsers/custom/rsv-pregnancy.test.tsx @@ -8,7 +8,7 @@ jest.mock("@src/services/nbs/nbs-service", () => ({ buildNbsUrl: jest.fn() })); jest.mock("@src/services/content-api/parsers/custom/extract-html"); const expectedHowToGetUnFilteredText = - "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 to 79 (or turned 80 after 1 September 2024)

If you're aged 75 to 79 (or turned 80 after 1 September 2024) contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

"; + "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 or over

Contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

If you live in a care home for older adults

Speak to a member of staff at your care home or your GP surgery about how to get the RSV vaccine.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

"; const expectedHowToGetFilteredText = "

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

"; const expectedRsvInPregnancyRegExp: RegExp = /

If you're pregnant<\/h3>((?:\s*

.*?<\/p>)+)/i; diff --git a/src/services/content-api/parsers/custom/rsv.test.tsx b/src/services/content-api/parsers/custom/rsv.test.tsx index ac986cdd..b85a1e5f 100644 --- a/src/services/content-api/parsers/custom/rsv.test.tsx +++ b/src/services/content-api/parsers/custom/rsv.test.tsx @@ -1,4 +1,4 @@ -import { extractHtmlFromSubSectionByHeading } from "@src/services/content-api/parsers/custom/extract-html"; +import { extractHtmlWithHeadingFromSubSectionByHeading } from "@src/services/content-api/parsers/custom/extract-html"; import { buildFilteredContentForRSVOlderAdultsVaccine } from "@src/services/content-api/parsers/custom/rsv"; import { SimpleSubsection } from "@src/services/content-api/types"; import { genericVaccineContentAPIResponseWithRSVGettingAccess } from "@test-data/content-api/data-rsv"; @@ -8,17 +8,22 @@ jest.mock("@src/services/nbs/nbs-service", () => ({ buildNbsUrl: jest.fn() })); jest.mock("@src/services/content-api/parsers/custom/extract-html"); const expectedHowToGetOlderAdultsUnFilteredText = - "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 to 79 (or turned 80 after 1 September 2024)

If you're aged 75 to 79 (or turned 80 after 1 September 2024) contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

"; -const expectedHowToGetOlderAdultsFilteredText = - "

If you're aged 75 to 79 (or turned 80 after 1 September 2024) contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

"; -const expectedOlderAdultsRegExp: RegExp = - /

If you're aged \d+ to \d+(?: \(or turned \d+ after .*\))?<\/h3>((?:\s*

.*?<\/p>)+)/i; + "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 or over

Contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

If you live in a care home for older adults

Speak to a member of staff at your care home or your GP surgery about how to get the RSV vaccine.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

"; +const expectedOlderAdultsFilteredText = + "

If you're aged 75 or over

Contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

"; +const expectedOlderAdultsInCareHomeFilteredText = + "

If you live in a care home for older adults

Speak to a member of staff at your care home or your GP surgery about how to get the RSV vaccine.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

"; +const expectedOlderAdultsRegExp: RegExp = /

If you're aged \d+ or over<\/h3>((?:\s*

.*?<\/p>)+)/i; +const expectedOlderAdultsInCareHomeRegExp: RegExp = + /

If you live in a care home for older adults<\/h3>((?:\s*

.*?<\/p>)+)/i; const apiResponse = JSON.stringify(genericVaccineContentAPIResponseWithRSVGettingAccess); describe("buildFilteredContentForRSVOlderAdultsVaccine", () => { beforeEach(() => { - (extractHtmlFromSubSectionByHeading as jest.Mock).mockReturnValue(expectedHowToGetOlderAdultsFilteredText); + (extractHtmlWithHeadingFromSubSectionByHeading as jest.Mock) + .mockReturnValueOnce(expectedOlderAdultsFilteredText) + .mockReturnValueOnce(expectedOlderAdultsInCareHomeFilteredText); }); it("should return standard vaccine content for most fields", async () => { @@ -35,17 +40,25 @@ describe("buildFilteredContentForRSVOlderAdultsVaccine", () => { expect(pageCopyForRSVOlderAdultsVaccine.extraDosesSchedule).toBeUndefined(); }); - it("should return howToGet section with only text matching pregnancy regex", async () => { + it("should return howToGet section with two subsections for older adults and care home", async () => { const pageCopyForRSVOlderAdultsVaccine = await buildFilteredContentForRSVOlderAdultsVaccine(apiResponse); expect(pageCopyForRSVOlderAdultsVaccine.howToGetVaccine).toBeDefined(); - expect(pageCopyForRSVOlderAdultsVaccine.howToGetVaccine.subsections.length).toBe(1); - const howToGetSubsection: SimpleSubsection = pageCopyForRSVOlderAdultsVaccine.howToGetVaccine - .subsections[0] as SimpleSubsection; - expect(howToGetSubsection.text).toBe(expectedHowToGetOlderAdultsFilteredText); - expect(extractHtmlFromSubSectionByHeading).toHaveBeenCalledWith( + expect(pageCopyForRSVOlderAdultsVaccine.howToGetVaccine.subsections.length).toBe(2); + + const olderAdultsSubsection = pageCopyForRSVOlderAdultsVaccine.howToGetVaccine.subsections[0] as SimpleSubsection; + const careHomeSubsection = pageCopyForRSVOlderAdultsVaccine.howToGetVaccine.subsections[1] as SimpleSubsection; + + expect(olderAdultsSubsection.text).toBe(expectedOlderAdultsFilteredText); + expect(careHomeSubsection.text).toBe(expectedOlderAdultsInCareHomeFilteredText); + + expect(extractHtmlWithHeadingFromSubSectionByHeading).toHaveBeenCalledWith( expect.objectContaining({ text: expectedHowToGetOlderAdultsUnFilteredText }), expectedOlderAdultsRegExp, ); + expect(extractHtmlWithHeadingFromSubSectionByHeading).toHaveBeenCalledWith( + expect.objectContaining({ text: expectedHowToGetOlderAdultsUnFilteredText }), + expectedOlderAdultsInCareHomeRegExp, + ); }); }); diff --git a/src/services/content-api/parsers/custom/rsv.tsx b/src/services/content-api/parsers/custom/rsv.tsx index 471fb51c..99e60935 100644 --- a/src/services/content-api/parsers/custom/rsv.tsx +++ b/src/services/content-api/parsers/custom/rsv.tsx @@ -1,16 +1,15 @@ import { buildFilteredContentForStandardVaccine } from "@src/services/content-api/parsers/content-filter-service"; -import { extractHtmlFromSubSectionByHeading } from "@src/services/content-api/parsers/custom/extract-html"; +import { extractHtmlWithHeadingFromSubSectionByHeading } from "@src/services/content-api/parsers/custom/extract-html"; import { VaccinePageContent, VaccinePageSection, VaccinePageSubsection } from "@src/services/content-api/types"; -const olderAdultsRegExp: RegExp = - /

If you're aged \d+ to \d+(?: \(or turned \d+ after .*\))?<\/h3>((?:\s*

.*?<\/p>)+)/i; +const olderAdultsRegExp: RegExp = /

If you're aged \d+ or over<\/h3>((?:\s*

.*?<\/p>)+)/i; +const olderAdultsInCareHomeRegExp: RegExp = + /

If you live in a care home for older adults<\/h3>((?:\s*

.*?<\/p>)+)/i; export const buildFilteredContentForRSVOlderAdultsVaccine = async (apiContent: string): Promise => { const standardFilteredContent: VaccinePageContent = await buildFilteredContentForStandardVaccine(apiContent); - const howToGetForRsvOlderAdults = filterHowToGetSectionToOnlyRsvOlderAdultsText( - standardFilteredContent.howToGetVaccine, - ); + const howToGetForRsvOlderAdults = filterHowToGetSectionForRsv(standardFilteredContent.howToGetVaccine); return { overview: standardFilteredContent.overview, @@ -25,14 +24,19 @@ export const buildFilteredContentForRSVOlderAdultsVaccine = async (apiContent: s }; }; -export const filterHowToGetSectionToOnlyRsvOlderAdultsText = ( - howToGetVaccine: VaccinePageSection, -): VaccinePageSection => { - const subsections = howToGetVaccine.subsections.map((subsection: VaccinePageSubsection) => { - return { - ...subsection, - text: extractHtmlFromSubSectionByHeading(subsection, olderAdultsRegExp), - }; +export const filterHowToGetSectionForRsv = (howToGetVaccine: VaccinePageSection): VaccinePageSection => { + const subsections = howToGetVaccine.subsections.flatMap((subsection: VaccinePageSubsection) => { + const olderAdultsText = extractHtmlWithHeadingFromSubSectionByHeading(subsection, olderAdultsRegExp); + + const olderAdultsInCareHomeText = extractHtmlWithHeadingFromSubSectionByHeading( + subsection, + olderAdultsInCareHomeRegExp, + ); + + return [ + { ...subsection, text: olderAdultsText }, + { ...subsection, text: olderAdultsInCareHomeText }, + ]; }); return { ...howToGetVaccine, subsections }; diff --git a/test-data/content-api/data-rsv.tsx b/test-data/content-api/data-rsv.tsx index 3c9ae3f9..af0194b3 100644 --- a/test-data/content-api/data-rsv.tsx +++ b/test-data/content-api/data-rsv.tsx @@ -441,7 +441,7 @@ export const genericVaccineContentAPIResponseWithRSVGettingAccess: ContentApiVac { position: 0, identifier: "1", - text: "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 to 79 (or turned 80 after 1 September 2024)

If you're aged 75 to 79 (or turned 80 after 1 September 2024) contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

", + text: "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 or over

Contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

If you live in a care home for older adults

Speak to a member of staff at your care home or your GP surgery about how to get the RSV vaccine.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

", "@type": "WebPageElement", name: "markdown", headline: "", @@ -453,7 +453,7 @@ export const genericVaccineContentAPIResponseWithRSVGettingAccess: ContentApiVac { position: 0, identifier: "1", - text: "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 to 79 (or turned 80 after 1 September 2024)

If you're aged 75 to 79 (or turned 80 after 1 September 2024) contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

", + text: "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 or over

Contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

If you live in a care home for older adults

Speak to a member of staff at your care home or your GP surgery about how to get the RSV vaccine.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

", "@type": "WebPageElement", name: "markdown", headline: "", diff --git a/wiremock/__files/rsv-vaccine.json b/wiremock/__files/rsv-vaccine.json index 34561386..a4cefec5 100644 --- a/wiremock/__files/rsv-vaccine.json +++ b/wiremock/__files/rsv-vaccine.json @@ -281,7 +281,7 @@ { "position": 0, "identifier": "1", - "text": "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 to 79 (or turned 80 after 1 September 2024)

If you're aged 75 to 79 (or turned 80 after 1 September 2024) contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

", + "text": "

There are different ways to get the RSV vaccine.

If you're pregnant

You should be offered the RSV vaccine around the time of your 28-week antenatal appointment.

Getting vaccinated as soon as possible from 28 weeks will provide the best protection for your baby. But the vaccine can be given later if needed, including up until you go into labour.

Speak to your maternity service or GP surgery if you're 28 weeks pregnant or more and have not been offered the vaccine.

If you're aged 75 or over

Contact your GP surgery to book your RSV vaccination.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

You do not need to wait to be contacted before booking your vaccination.

If you live in a care home for older adults

Speak to a member of staff at your care home or your GP surgery about how to get the RSV vaccine.

Your GP surgery may contact you about getting the RSV vaccine. This may be by letter, text, phone call or email.

", "@type": "WebPageElement", "name": "markdown", "headline": ""