diff --git a/src/app/pages/details/common/get-this-title-files/give-before-pdf/use-give-links.ts b/src/app/pages/details/common/get-this-title-files/give-before-pdf/use-give-links.ts index 38efe4bb2..b5106b94b 100644 --- a/src/app/pages/details/common/get-this-title-files/give-before-pdf/use-give-links.ts +++ b/src/app/pages/details/common/get-this-title-files/give-before-pdf/use-give-links.ts @@ -9,13 +9,15 @@ export default function useGiveLinks() { } function getLinks(search: string) { - if (search.includes('Instructor')) { + const keys = Array.from(new URLSearchParams(search).keys()).map((k) => k.toLowerCase()); + + if (keys.includes('instructor resources')) { return [ 'https://riceconnect.rice.edu/donation/support-openstax-instructor-resources', 'https://riceconnect.rice.edu/donation/support-openstax-instructor-resources-b' ]; } - if (search.includes('Student')) { + if (keys.includes('student resources')) { return [ 'https://riceconnect.rice.edu/donation/support-openstax-student-resources', 'https://riceconnect.rice.edu/donation/support-openstax-student-resources-b' diff --git a/src/app/pages/details/common/resource-box/left-content.tsx b/src/app/pages/details/common/resource-box/left-content.tsx index 88fc76046..0cf6608c5 100644 --- a/src/app/pages/details/common/resource-box/left-content.tsx +++ b/src/app/pages/details/common/resource-box/left-content.tsx @@ -82,11 +82,12 @@ const iconLookup: {[key: string]: IconDefinition} = { // string says nothing about the resource there. function useVariant(): VariantValue { const {search} = useLocation(); + const keys = Array.from(new URLSearchParams(search).keys()).map((k) => k.toLowerCase()); - if (search.includes('Instructor')) { + if (keys.includes('instructor resources')) { return 'Instructor resource'; } - if (search.includes('Student')) { + if (keys.includes('student resources')) { return 'Student resource'; } return '? resource'; diff --git a/src/app/pages/details/common/tab-utils.ts b/src/app/pages/details/common/tab-utils.ts index 97e6c02be..e1af9058d 100644 --- a/src/app/pages/details/common/tab-utils.ts +++ b/src/app/pages/details/common/tab-utils.ts @@ -1,17 +1,23 @@ export function findSelectedTab(labels: string[]) { const possibleTabs = Array.from(new window.URLSearchParams(window.location.search).keys()); - return labels.find((label) => possibleTabs.includes(label)) || labels[0]; + return ( + labels.find((label) => + possibleTabs.some((tab) => tab.toLowerCase() === label.toLowerCase()) + ) || labels[0] + ); } export function replaceSearchTerm(labels: string[], newValue: string) { + const lowerLabels = labels.map((label) => label.toLowerCase()); const possibleTabs = Array.from(new window.URLSearchParams(window.location.search).keys()); - const index = possibleTabs.findIndex((t) => labels.includes(t)); + const firstIndex = possibleTabs.findIndex((tab) => lowerLabels.includes(tab.toLowerCase())); + const filtered = possibleTabs.filter((tab) => !lowerLabels.includes(tab.toLowerCase())); - if (index < 0) { - possibleTabs.unshift(encodeURIComponent(newValue)); + if (firstIndex < 0) { + filtered.unshift(newValue); } else { - possibleTabs[index] = encodeURIComponent(newValue); + filtered.splice(firstIndex, 0, newValue); } - return `?${possibleTabs.join('&')}`; + return `?${filtered.map((tab) => encodeURIComponent(tab)).join('&')}`; } diff --git a/test/src/pages/details/common/tab-utils.test.ts b/test/src/pages/details/common/tab-utils.test.ts index 12d9c5d1e..58cac57d0 100644 --- a/test/src/pages/details/common/tab-utils.test.ts +++ b/test/src/pages/details/common/tab-utils.test.ts @@ -1,17 +1,47 @@ -import { replaceSearchTerm } from '~/pages/details/common/tab-utils'; +import {findSelectedTab, replaceSearchTerm} from '~/pages/details/common/tab-utils'; + +function setSearch(search: string) { + Reflect.defineProperty(window, 'location', { + writable: true, + value: { search } + }); +} describe('tab-utils', () => { const labels = ['one', 'two']; const newValue = 'three'; it('adds new param if no tabs in search params', () => { + setSearch(''); expect(replaceSearchTerm(labels, newValue)).toBe('?three'); }); it('replaces param if tab is in search params', () => { - Reflect.defineProperty(window, 'location', { - writable: true, - value: { search: '?fluff&two' } - }); - expect(replaceSearchTerm(labels, newValue)).toBe('?fluff&three'); + setSearch('?fluff&two'); + expect(replaceSearchTerm(labels, newValue)).toBe('?fluff&three'); + }); + it('replaces param when the key case does not match a label', () => { + setSearch('?TWO'); + expect(replaceSearchTerm(labels, newValue)).toBe('?three'); + }); + + describe('findSelectedTab', () => { + const tabLabels = ['Book details', 'Instructor resources', 'Student resources']; + + it('selects the matching tab', () => { + setSearch('?Instructor%20resources'); + expect(findSelectedTab(tabLabels)).toBe('Instructor resources'); + }); + it('matches a lower-case deep link', () => { + setSearch('?instructor%20resources'); + expect(findSelectedTab(tabLabels)).toBe('Instructor resources'); + }); + it('falls back to the first label when no key matches', () => { + setSearch('?nothing'); + expect(findSelectedTab(tabLabels)).toBe('Book details'); + }); + it('replaces a lower-case key instead of keeping it', () => { + setSearch('?instructor%20resources'); + expect(replaceSearchTerm(tabLabels, 'Student resources')).toBe('?Student%20resources'); + }); }); }); diff --git a/test/src/pages/details/left-content.test.tsx b/test/src/pages/details/left-content.test.tsx index 9791b8ab1..0d8dc5d9d 100644 --- a/test/src/pages/details/left-content.test.tsx +++ b/test/src/pages/details/left-content.test.tsx @@ -80,7 +80,7 @@ describe('left-content', () => { }); const model = {link, ...baseModel, ...{iconType: 'unlock'}}; - render(); + render(); const foundLink = screen.getByRole('link'); expect(foundLink.textContent).toBe('button-label'); @@ -93,14 +93,16 @@ describe('left-content', () => { }); const model = {link, ...baseModel, iconType: 'download'}; - render(); + render(); const foundLink = screen.getByRole('link'); expect(foundLink.textContent).toBe('button-label'); await user.click(foundLink); - await screen.findByText('Give today'); + const giveToday = await screen.findByText('Give today'); const downloadLink = await screen.findByText('Go to your resource'); + expect(giveToday.closest('a')?.getAttribute('href')).toContain('support-openstax-instructor-resources'); + expect(document.querySelector('[data-nudge-placement="Instructor resource"]')).toBeTruthy(); await user.click(downloadLink); }); it('tracks a download for a signed-in non-instructor', async () => { @@ -113,14 +115,16 @@ describe('left-content', () => { }); const model = {link, ...baseModel, iconType: 'download'}; - render(); + render(); const foundLink = screen.getByRole('link'); expect(foundLink.textContent).toBe('button-label'); await user.click(foundLink); - await screen.findByText('Give today'); + const giveToday = await screen.findByText('Give today'); const downloadLink = await screen.findByText('Go to your resource'); + expect(giveToday.closest('a')?.getAttribute('href')).toContain('support-openstax-student-resources'); + expect(document.querySelector('[data-nudge-placement="Student resource"]')).toBeTruthy(); await user.click(downloadLink); expect(trackLink).toHaveBeenCalledWith(expect.anything(), '1'); @@ -159,9 +163,11 @@ describe('left-content', () => { expect(foundLink.textContent).toBe('button-label'); await user.click(foundLink); - await screen.findByText('Give today'); + const giveToday = await screen.findByText('Give today'); const downloadLink = await screen.findByText('Go to your resource'); + expect(giveToday.closest('a')?.getAttribute('href')).toContain('support-openstax-subject'); + expect(document.querySelector('[data-nudge-placement="? resource"]')).toBeTruthy(); await user.click(downloadLink); }); it('handles unknown icon and unknown search', async () => {