Skip to content

Commit

Permalink
[Issue #2776] fix broken markup parsing when links are present (#2831)
Browse files Browse the repository at this point in the history
* markup splitter will ignore anything contained in quotes within a tag
* disables collapsed content if markup splitter returns no content to hide
  • Loading branch information
doug-s-nava authored Nov 13, 2024
1 parent 59fda82 commit 04457b3
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 0 deletions.
12 changes: 12 additions & 0 deletions frontend/src/components/opportunity/OpportunityDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ const SummaryDescriptionDisplay = ({
const purifiedSummary = DOMPurify.sanitize(summaryDescription);

const { preSplit, postSplit } = splitMarkup(purifiedSummary, 600);

if (!postSplit) {
return (
<div
dangerouslySetInnerHTML={{
__html: summaryDescription
? DOMPurify.sanitize(summaryDescription)
: "--",
}}
/>
);
}
return (
<>
<div
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/utils/generalUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ export const splitMarkup = (
tracker.postSplit += character;
return tracker;
}
// handle things like urls within tag params that need to be ignored
if (
(tracker.openTagIndicator && character === `"`) ||
character === `'`
) {
tracker.inQuotes = !tracker.inQuotes;
}
if (tracker.inQuotes) {
tracker.preSplit += character;
return tracker;
}
if (character === "<") {
if (tracker.openTagIndicator) {
throw new Error("Malformed markup: unclosed tag");
Expand Down Expand Up @@ -64,6 +75,8 @@ export const splitMarkup = (
openTagIndicator: false,
closeTagIndicator: false,
splitComplete: false,
tagCharacters: "",
inQuotes: false,
},
);
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import { useTranslationsMock } from "src/utils/testing/intlMocks";

import OpportunityDescription from "src/components/opportunity/OpportunityDescription";

const splitMarkupMock = jest
.fn()
.mockImplementation((content: string, index: number) => ({
preSplit: content.substring(0, index),
postSplit: content.substring(index),
}));

jest.mock("isomorphic-dompurify", () => ({
sanitize: jest.fn((input: string) => input),
}));
Expand All @@ -16,6 +23,15 @@ jest.mock("next-intl", () => ({
useTranslations: () => useTranslationsMock(),
}));

jest.mock("src/utils/generalUtils", () => ({
splitMarkup: (content: string, index: number) =>
// eslint-disable-next-line
splitMarkupMock(content, index),
}));

const longDescription =
"Its young really risk. College call month identify out east. Defense writer ahead trip smile. Picture data area system manager hour none. Doctor pay visit save test. Again feeling little throughout. Improve drug play remain face word somebody. Baby miss may drive treat letter. Laugh message as car position team. Want build last. Model or base within bag manager brother. How still teacher son fish pay until. Debate doctor visit because success. Message will white risk. Follow sell nearly individual family crime particularly understand. Police street federal six really major owner. Should friend minute team material trade special. Example above government usually deal fill few. Kid middle our sometimes appear. Ready share century nor take let. Water sort choice beat design she sport commercial. Nature if natural feel. Yes door cold realize. Receive trade central good realize number woman them. Actually there common order purpose within. Enough trouble develop station almost read. Who attack include company.";

const mockSummaryData: Summary = {
summary_description: "<p>Summary Description</p>",
applicant_types: [
Expand Down Expand Up @@ -46,6 +62,44 @@ describe("OpportunityDescription", () => {
);
});

it("splits opportunity description after 600 characters if description is longer than 750 characters", () => {
render(
<OpportunityDescription
summary={{ ...mockSummaryData, summary_description: longDescription }}
/>,
);

expect(
screen.getByText(
"Its young really risk. College call month identify out east. Defense writer ahead trip smile. Picture data area system manager hour none. Doctor pay visit save test. Again feeling little throughout. Improve drug play remain face word somebody. Baby miss may drive treat letter. Laugh message as car position team. Want build last. Model or base within bag manager brother. How still teacher son fish pay until. Debate doctor visit because success. Message will white risk. Follow sell nearly individual family crime particularly understand. Police street federal six really major owner. Should friend...",
),
).toBeInTheDocument();
expect(
screen.queryAllByText(
" minute team material trade special. Example above government usually deal fill few. Kid middle our sometimes appear. Ready share century nor take let. Water sort choice beat design she sport commercial. Nature if natural feel. Yes door cold realize. Receive trade central good realize number woman them. Actually there common order purpose within. Enough trouble develop station almost read. Who attack include company.",
),
).toHaveLength(0);
});

it("shows all description if parsed mark up comes out with no content to hide", () => {
splitMarkupMock.mockImplementation((content: string) => ({
preSplit: content,
postSplit: "",
}));

render(
<OpportunityDescription
summary={{ ...mockSummaryData, summary_description: longDescription }}
/>,
);

expect(
screen.getByText(
"Its young really risk. College call month identify out east. Defense writer ahead trip smile. Picture data area system manager hour none. Doctor pay visit save test. Again feeling little throughout. Improve drug play remain face word somebody. Baby miss may drive treat letter. Laugh message as car position team. Want build last. Model or base within bag manager brother. How still teacher son fish pay until. Debate doctor visit because success. Message will white risk. Follow sell nearly individual family crime particularly understand. Police street federal six really major owner. Should friend minute team material trade special. Example above government usually deal fill few. Kid middle our sometimes appear. Ready share century nor take let. Water sort choice beat design she sport commercial. Nature if natural feel. Yes door cold realize. Receive trade central good realize number woman them. Actually there common order purpose within. Enough trouble develop station almost read. Who attack include company.",
),
).toBeInTheDocument();
});

it("renders the eligible applicants with mapped values", () => {
render(<OpportunityDescription summary={mockSummaryData} />);

Expand Down
11 changes: 11 additions & 0 deletions frontend/tests/utils/generalUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ describe("splitMarkup", () => {
" <div>that I've been turning over <strong>in</strong> my mind ever since.</div>",
);
});
it("ignores anything that is within quotes within a tag and allows it to pass through", () => {
const exampleMarkup =
"<div>In <a href='http://things' fake='<<>>>><<//'>my younger</a> and more <p>vulnerable <strong>years my<ul><li>father</li><li>gave</li></ul>me</strong> some </p>advice</div> <div>that I've been turning over <strong>in</strong> my mind ever since.</div>";
const { preSplit, postSplit } = splitMarkup(exampleMarkup, 75);
expect(preSplit).toEqual(
"<div>In <a href='http://things' fake='<<>>>><<//'>my younger</a> and more <p>vulnerable <strong>years my<ul><li>father</li><li>gave</li></ul>me</strong> some </p>advice</div>",
);
expect(postSplit).toEqual(
" <div>that I've been turning over <strong>in</strong> my mind ever since.</div>",
);
});
});

describe("findFirstWhitespace", () => {
Expand Down

0 comments on commit 04457b3

Please sign in to comment.