+
{contentArray.map((entry, index) => (
-
+
))}
);
}
-function useBookstoreContentLink(slug) {
- const [url, setUrl] = React.useState(null);
+function useBookstoreContentLink(slug: string) {
+ const [url, setUrl] = React.useState
(null);
- React.useEffect(
- () => cmsFetch(slug).then((data) => setUrl(data.amazon_link)),
- [slug]
- );
+ React.useEffect(() => {
+ cmsFetch(slug).then((data) => setUrl(data.amazon_link));
+ }, [slug]);
return url;
}
-export default function OrderPrintCopy({slug}) {
+export default function OrderPrintCopy({slug}: {slug: string}) {
const {formatMessage} = useIntl();
const bookstoreLink = useBookstoreContentLink(slug);
const contentArray = React.useMemo(() => {
+ if (!bookstoreLink) {
+ return null;
+ }
const individual = formatMessage({
id: 'printcopy.individual',
defaultMessage: 'Individual'
@@ -130,17 +128,18 @@ export default function OrderPrintCopy({slug}) {
headerText: bookstore,
headerIcon: faUsers,
buttonText: button2Text,
- buttonUrl: 'https://he.kendallhunt.com/sites/default/files/uploadedFiles/Kendall_Hunt/OPENSTAX_PRICE_LIST_and_ORDER_FORM.pdf'
+ buttonUrl:
+ 'https://he.kendallhunt.com/sites/default/files/uploadedFiles/Kendall_Hunt/OPENSTAX_PRICE_LIST_and_ORDER_FORM.pdf'
}
];
}, [formatMessage, bookstoreLink]);
- if (!bookstoreLink) {
+ if (!contentArray) {
return null;
}
return (
-
+
diff --git a/src/app/pages/details/common/get-this-title-files/recommended-callout/recommended-callout.js b/src/app/pages/details/common/get-this-title-files/recommended-callout/recommended-callout.tsx
similarity index 60%
rename from src/app/pages/details/common/get-this-title-files/recommended-callout/recommended-callout.js
rename to src/app/pages/details/common/get-this-title-files/recommended-callout/recommended-callout.tsx
index be5f13a4a..6e47bcd7e 100644
--- a/src/app/pages/details/common/get-this-title-files/recommended-callout/recommended-callout.js
+++ b/src/app/pages/details/common/get-this-title-files/recommended-callout/recommended-callout.tsx
@@ -5,7 +5,15 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faTimes} from '@fortawesome/free-solid-svg-icons/faTimes';
import './recommended-callout.scss';
-export default function RecommendedCallout({title, blurb, onPutAway}) {
+export default function RecommendedCallout({
+ title,
+ blurb,
+ onPutAway
+}: {
+ title?: string;
+ blurb?: string;
+ onPutAway: () => void;
+}) {
const intl = useIntl();
const titleToUse = title || intl.formatMessage({id: 'recommended'});
@@ -13,12 +21,15 @@ export default function RecommendedCallout({title, blurb, onPutAway}) {
{titleToUse}
-
+
- {
- blurb &&
- }
+ {blurb && }
);
diff --git a/src/app/pages/details/common/get-this-title-files/recommended-callout/use-callout-counter.js b/src/app/pages/details/common/get-this-title-files/recommended-callout/use-callout-counter.js
deleted file mode 100644
index e5853f2d5..000000000
--- a/src/app/pages/details/common/get-this-title-files/recommended-callout/use-callout-counter.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from 'react';
-
-const MAX_CALLOUTS = 3;
-const RESET_DATE = new Date('2020/01/29');
-
-export default function useCalloutCounter(slug) {
- const [incremented, setIncremented] = React.useState(false);
- const index = React.useMemo(
- () => {
- if (slug === null) {
- throw new Error('calloutCounter: no slug set');
- }
- return `callout-${slug}`;
- },
- [slug]
- );
- const savedCount = React.useMemo(
- () => Number(window.localStorage?.getItem(index)) || 0,
- [index]
- );
- const [count, setCount] = React.useState(savedCount);
- const resetIndex = React.useMemo(
- () => `callout-reset-${slug}`,
- [slug]
- );
- const savedLastReset = React.useMemo(
- () => new Date(
- Number(window.localStorage?.getItem(resetIndex)) ||
- Number(RESET_DATE) - 100
- ),
- [resetIndex]
- );
- const [lastReset, setLastReset] = React.useState(savedLastReset);
- const showCallout = React.useMemo(
- () => count < MAX_CALLOUTS,
- [count]
- );
- const hideForever = React.useCallback(
- () => setCount(MAX_CALLOUTS),
- []
- );
-
- React.useEffect(
- () => {
- setIncremented(false);
- if (slug && Date.now() > RESET_DATE && lastReset < RESET_DATE) {
- setCount(0);
- setLastReset(Date.now());
- }
- },
- [slug, lastReset]
- );
-
- React.useEffect(
- () => {
- if (slug && !incremented) {
- setCount(count + 1);
- setIncremented(true);
- }
- },
- [slug, incremented, count]
- );
-
- React.useEffect(
- () => {
- window.localStorage?.setItem(resetIndex, Number(Date.now()).toString());
- },
- [resetIndex]
- );
-
- React.useEffect(
- () => window.localStorage?.setItem(index, count),
- [index, count]
- );
-
- return [showCallout, hideForever];
-}
diff --git a/src/app/pages/details/common/get-this-title-files/recommended-callout/use-callout-counter.ts b/src/app/pages/details/common/get-this-title-files/recommended-callout/use-callout-counter.ts
new file mode 100644
index 000000000..96eda6d24
--- /dev/null
+++ b/src/app/pages/details/common/get-this-title-files/recommended-callout/use-callout-counter.ts
@@ -0,0 +1,56 @@
+import React from 'react';
+
+const MAX_CALLOUTS = 3;
+const RESET_DATE = new Date('2020/01/29');
+
+export default function useCalloutCounter(
+ slug: string
+): [show: boolean, hideForever: () => void] {
+ const [incremented, setIncremented] = React.useState(false);
+ const index = `callout-${slug}`;
+ const savedCount = React.useMemo(
+ () => Number(window.localStorage?.getItem(index)) || 0,
+ [index]
+ );
+ const [count, setCount] = React.useState(savedCount);
+ const resetIndex = `callout-reset-${slug}`;
+ const savedLastReset = React.useMemo(
+ () =>
+ new Date(
+ Number(window.localStorage?.getItem(resetIndex)) ||
+ Number(RESET_DATE) - 100
+ ),
+ [resetIndex]
+ );
+ const [lastReset, setLastReset] = React.useState(savedLastReset);
+ const showCallout = React.useMemo(() => count < MAX_CALLOUTS, [count]);
+ const hideForever = React.useCallback(() => setCount(MAX_CALLOUTS), []);
+
+ React.useEffect(() => {
+ setIncremented(false);
+ const now = new Date(Date.now());
+
+ if (slug && now > RESET_DATE && lastReset < RESET_DATE) {
+ setCount(0);
+ setLastReset(now);
+ }
+ }, [slug, lastReset]);
+
+ React.useEffect(() => {
+ if (slug && !incremented) {
+ setCount(count + 1);
+ setIncremented(true);
+ }
+ }, [slug, incremented, count]);
+
+ React.useEffect(() => {
+ window.localStorage?.setItem(resetIndex, Number(Date.now()).toString());
+ }, [resetIndex]);
+
+ React.useEffect(
+ () => window.localStorage?.setItem(index, count.toString()),
+ [index, count]
+ );
+
+ return [showCallout, hideForever];
+}
diff --git a/src/app/pages/details/common/get-this-title.tsx b/src/app/pages/details/common/get-this-title.tsx
index 35c938df2..b9a3b23ec 100644
--- a/src/app/pages/details/common/get-this-title.tsx
+++ b/src/app/pages/details/common/get-this-title.tsx
@@ -14,12 +14,25 @@ import OrderPrintCopy from './get-this-title-files/order-print-copy/order-print-
import './get-this-title-files/get-this-title.scss';
import trackLink from './track-link';
-type Model = {
+export type Model = {
id: string;
slug: string;
+ bookState: string;
+ comingSoon: boolean;
+ title: string;
bookshareLink: string;
ibookLink: string;
+ ibookLink2: string;
kindleLink: string;
+ webviewRexLink: string;
+ webviewLink: string;
+ contentWarningText?: string;
+ rexCalloutTitle?: string;
+ rexCalloutBlurb?: string;
+ highResolutionPdfUrl: string;
+ lowResolutionPdfUrl: string;
+ cheggLink: string; // These may not be supported at all anymore,
+ cheggLinkText: string; // but the CMS is still serving them.
};
type ModelKey = 'bookshareLink' | 'ibookLink' | 'kindleLink';
type TrackedMouseEvent = Parameters[0];
diff --git a/src/app/pages/details/common/resource-box/resource-boxes.tsx b/src/app/pages/details/common/resource-box/resource-boxes.tsx
index 4885472d5..14b645f0c 100644
--- a/src/app/pages/details/common/resource-box/resource-boxes.tsx
+++ b/src/app/pages/details/common/resource-box/resource-boxes.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import RawHTML from '~/components/jsx-helpers/raw-html';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons/faExternalLinkAlt';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import {faExternalLinkAlt} from '@fortawesome/free-solid-svg-icons/faExternalLinkAlt';
import ResourceBox from './resource-box';
import useDetailsContext from '../../context';
import './resource-box.scss';
@@ -43,9 +43,21 @@ function CommonsHubBox() {
);
}
-export default function ResourceBoxes({ models, includeCommonsHub = false }: {
- models: {heading: string;
- }[]; // Will be a real type when other stuff becomes TS
+// There's more, but this is all we need for now
+export type ResourceModel = {
+ heading: string;
+ description?: string;
+ link: {
+ text: string;
+ url: string;
+ }
+};
+
+export default function ResourceBoxes({
+ models,
+ includeCommonsHub = false
+}: {
+ models: ResourceModel[];
includeCommonsHub?: boolean;
}) {
return (
@@ -57,4 +69,3 @@ export default function ResourceBoxes({ models, includeCommonsHub = false }: {
);
}
-
diff --git a/src/app/pages/details/desktop-view/instructor-resource-tab/instructor-resource-tab.js b/src/app/pages/details/desktop-view/instructor-resource-tab/instructor-resource-tab.js
index 636d12226..9b202e963 100644
--- a/src/app/pages/details/desktop-view/instructor-resource-tab/instructor-resource-tab.js
+++ b/src/app/pages/details/desktop-view/instructor-resource-tab/instructor-resource-tab.js
@@ -1,5 +1,5 @@
import React from 'react';
-import FeaturedResourcesSection from '../../common/featured-resources/featured-resources.js';
+import FeaturedResourcesSection from '../../common/featured-resources/featured-resources';
import {resourceBoxModel, useResources} from '../../common/resource-box/resource-box';
import ResourceBoxes from '../../common/resource-box/resource-boxes';
import VideoResourceBoxes from '../../common/resource-box/video-resource-box';
diff --git a/src/app/pages/details/phone-view/instructor-resources-pane/instructor-resources-pane.js b/src/app/pages/details/phone-view/instructor-resources-pane/instructor-resources-pane.js
index a7b1e7617..903dfc6aa 100644
--- a/src/app/pages/details/phone-view/instructor-resources-pane/instructor-resources-pane.js
+++ b/src/app/pages/details/phone-view/instructor-resources-pane/instructor-resources-pane.js
@@ -3,7 +3,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faSignOutAlt} from '@fortawesome/free-solid-svg-icons/faSignOutAlt';
import {useNavigate} from 'react-router-dom';
import {resourceBoxModel, useResources} from '../../common/resource-box/resource-box';
-import FeaturedResourcesSection from '../../common/featured-resources/featured-resources.js';
+import FeaturedResourcesSection from '../../common/featured-resources/featured-resources';
import ResourceBoxes from '../../common/resource-box/resource-boxes';
import VideoResourceBoxes from '../../common/resource-box/video-resource-box';
import useUserContext from '~/contexts/user';
diff --git a/test/src/components/__snapshots__/book-tile.test.tsx.snap b/test/src/components/__snapshots__/book-tile.test.tsx.snap
deleted file mode 100644
index 11f6f81ae..000000000
--- a/test/src/components/__snapshots__/book-tile.test.tsx.snap
+++ /dev/null
@@ -1,103 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`book-tile renders 1`] = `
-
-
-
-
-
-
-`;
diff --git a/test/src/components/book-tile.test.tsx b/test/src/components/book-tile.test.tsx
index 96e2dff57..89ba919a9 100644
--- a/test/src/components/book-tile.test.tsx
+++ b/test/src/components/book-tile.test.tsx
@@ -5,6 +5,7 @@ import ShellContextProvider from '~/../../test/helpers/shell-context';
import {Book as BookInfo} from '~/pages/subjects/new/specific/context';
import BookTile from '~/components/book-tile/book-tile';
import userEvent from '@testing-library/user-event';
+import * as CF from '~/helpers/cms-fetch';
const bookData: BookInfo = {
id: 46,
@@ -69,49 +70,66 @@ jest.mock('~/models/book-titles', () => ({
}));
describe('book-tile', () => {
- it('renders', () => {
- const {baseElement} = render( );
+ const user = userEvent.setup();
+ const originalError = console.error;
- expect(baseElement).toMatchSnapshot();
- });
it('renders coming soon', async () => {
render( );
await screen.findByText('Coming soon');
});
it('falls back on webviewLink', async () => {
- const save = bookData.webviewRexLink;
+ const noRexData = {...bookData, webviewRexLink: ''};
- bookData.webviewRexLink = '';
- render( );
- const user = userEvent.setup();
+ render( );
await user.click(screen.getByRole('button'));
- const links = screen.getAllByRole('link');
-
- expect(links).toHaveLength(1);
+ const link = screen.getByRole('link');
- await user.click(screen.getAllByRole('link')[0]);
-
- bookData.webviewRexLink = save;
+ console.error = jest.fn();
+ await user.click(link);
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining('Not implemented: navigation'),
+ undefined
+ );
+ console.error = originalError;
});
it('falls back on lowResolutionPdfUrl (may be obsolete)', async () => {
- const save = bookData.highResolutionPdfUrl;
-
- bookData.lowResolutionPdfUrl = 'low-res.pdf';
- bookData.highResolutionPdfUrl = '';
-
+ const lowResPdfData = {
+ ...bookData,
+ highResolutionPdfUrl: '',
+ lowResolutionPdfUrl: 'low-res.pdf'
+ };
+
+ render( );
+
+ const link = screen.getByRole('link');
+
+ console.error = jest.fn();
+ await user.click(link);
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining('Not implemented: navigation'),
+ undefined
+ );
+ console.error = originalError;
+ });
+ it('brings up dialog when selecting print copy', async () => {
render( );
-
- const user = userEvent.setup();
-
- const links = screen.getAllByRole('link');
-
- expect(links).toHaveLength(1);
-
- await user.click(screen.getAllByRole('link')[0]);
-
- bookData.highResolutionPdfUrl = save;
+ const printCopyLink = screen.getByRole('menuitem', {
+ name: 'Order a print copy'
+ });
+ const mockCmsFetch = jest
+ .spyOn(CF, 'default')
+ .mockImplementation(() => {
+ return Promise.resolve({
+ amazon_link: 'where you go'
+ });
+ });
+
+ await userEvent.click(printCopyLink);
+
+ expect(screen.getAllByRole('dialog')).toHaveLength(2);
+ mockCmsFetch.mockRestore();
});
});
diff --git a/test/src/data/details-college-algebra.js b/test/src/data/details-college-algebra.js
index 012d96762..8bfa44107 100644
--- a/test/src/data/details-college-algebra.js
+++ b/test/src/data/details-college-algebra.js
@@ -1,7 +1,7 @@
const details = {
"id": 39,
"meta": {
- "slug": "college-algebra",
+ "slug": "books/college-algebra",
"seo_title": "Free College Algebra Textbook Available for Download",
"search_description": "Study algebra online free by downloading OpenStax's College Algebra book and using our accompanying online resources including an algebra study guide.",
"type": "books.Book",
diff --git a/test/src/pages/details/common/featured-resources.test.tsx b/test/src/pages/details/common/featured-resources.test.tsx
new file mode 100644
index 000000000..866d2f79b
--- /dev/null
+++ b/test/src/pages/details/common/featured-resources.test.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import {render, screen} from '@testing-library/preact';
+import FeaturedResourcesSection from '~/pages/details/common/featured-resources/featured-resources';
+import ShellContextProvider from '../../../../helpers/shell-context';
+import {MemoryRouter} from 'react-router-dom';
+import userEvent from '@testing-library/user-event';
+
+const mockUseUserContext = jest.fn();
+
+jest.mock('~/contexts/user', () => ({
+ ...jest.requireActual('~/contexts/user'),
+ __esModule: true,
+ default: () => mockUseUserContext()
+}));
+
+const resourceModels = [
+ {
+ heading: 'one',
+ link: {
+ text: 'link1',
+ url: 'url1'
+ }
+ },
+ {heading: 'two',
+ link: {
+ text: 'link2',
+ url: 'url2'
+ }
+ }
+];
+
+describe('details/featured-resources', () => {
+ const user = userEvent.setup();
+ const originalError = console.error;
+
+ it('renders', async () => {
+ mockUseUserContext.mockReturnValue({
+ userStatus: {
+ isInstructor: true
+ }
+ });
+ render(
+
+
+
+
+
+ );
+ console.error = jest.fn();
+ await user.click(screen.getByRole('link', {name: 'Go to one'}));
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Not implemented: navigation'), undefined);
+ console.error = originalError;
+ });
+});
diff --git a/test/src/pages/details/get-this-title/get-this-title.test.tsx b/test/src/pages/details/get-this-title/get-this-title.test.tsx
index 0118157a7..b96758221 100644
--- a/test/src/pages/details/get-this-title/get-this-title.test.tsx
+++ b/test/src/pages/details/get-this-title/get-this-title.test.tsx
@@ -4,15 +4,19 @@ import userEvent from '@testing-library/user-event';
import GetThisTitle from '~/pages/details/common/get-this-title';
import {TOCContextProvider} from '~/pages/details/common/toc-slideout/context';
import BookDetailsLoader from '../book-details-context';
+import * as UDH from '~/helpers/use-document-head';
+import $ from '~/helpers/$';
+import * as TL from '~/pages/details/common/track-link';
+import * as UC from '~/contexts/user';
// College algebra book details
import details from '../../../data/details-college-algebra';
import {transformData, camelCaseKeys} from '~/helpers/page-data-utils';
-const model = camelCaseKeys(transformData(details));
+const baseModel = camelCaseKeys(transformData(details));
-function GTTinContext() {
+function GTTinContext({model = baseModel}) {
return (
-
+
@@ -28,9 +32,19 @@ jest.mock('~/helpers/device', () => ({
isMobileDisplay: () => mockIsMobileDisplay()
}));
+jest.spyOn(UDH, 'setPageTitleAndDescriptionFromBookData').mockImplementation(
+ () => null
+);
+jest.spyOn(UC, 'UserContextProvider').mockImplementation(
+ ({children}: any) => children // eslint-disable-line
+);
+const mockTrackLink = jest.spyOn(TL, 'default');
+
const user = userEvent.setup();
describe('get-this-title', () => {
+ const originalError = console.error;
+
it('renders with unexpanded options', async () => {
render( );
const expander = await screen.findByText('+ 1 more option...');
@@ -38,7 +52,51 @@ describe('get-this-title', () => {
await user.click(expander);
await screen.findByText('See 1 fewer option');
// Exercise link tracking
+
+ console.error = jest.fn();
await user.click(screen.getByText('Download for Kindle'));
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining('Not implemented: navigation'),
+ undefined
+ );
+ console.error = originalError;
+ });
+ it('shows no expander if not needed', async () => {
+ const noKindleModel = {...baseModel, kindleLink: ''};
+
+ render( );
+ const links = await screen.findAllByRole('link');
+
+ expect(links).toHaveLength(2);
+ expect(
+ links.find((el) => el.textContent?.includes('more option'))
+ ).toBeUndefined();
+ });
+ it('opens give dialog for Webview', async () => {
+ render( );
+ const wvLink = await screen.findByText('View online');
+
+ const closeRecommendedCallout = screen.getByRole('button', {
+ name: 'close-popup'
+ });
+
+ await user.click(closeRecommendedCallout);
+
+ await user.click(wvLink);
+ expect(screen.getAllByRole('dialog')).toHaveLength(2);
+ const trackingLink = await screen.findByRole('link', {
+ name: 'Go to your book'
+ });
+
+ console.error = jest.fn();
+ await user.click(trackingLink);
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining('Not implemented: navigation'),
+ undefined
+ );
+ console.error = originalError;
+ expect(mockTrackLink).toHaveBeenCalled();
+ mockTrackLink.mockReset();
});
it('opens give dialog for PDF', async () => {
render( );
@@ -46,13 +104,82 @@ describe('get-this-title', () => {
await user.click(pdfLink);
expect(screen.getAllByRole('dialog')).toHaveLength(2);
+ const trackingLink = await screen.findByRole('link', {
+ name: 'Go to your file'
+ });
+
+ console.error = jest.fn();
+ await user.click(trackingLink);
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining('Not implemented: navigation'),
+ undefined
+ );
+ console.error = originalError;
+ expect(mockTrackLink).toHaveBeenCalled();
+ mockTrackLink.mockReset();
});
it('no dialog on mobile display', async () => {
mockIsMobileDisplay.mockReturnValue(true);
render( );
const pdfLink = await screen.findByText('Download a PDF');
+ console.error = jest.fn();
await user.click(pdfLink);
- expect(screen.findByRole('dialog')).toBeTruthy();
+ expect(screen.queryAllByRole('dialog')).toHaveLength(0);
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining('Not implemented: navigation'),
+ undefined
+ );
+ console.error = originalError;
+ });
+ it('expands TOC option (Polish)', async () => {
+ const mockIsPolish = jest.spyOn($, 'isPolish').mockReturnValue(true);
+
+ render( );
+ const toggleLink = await screen.findByRole('button', {
+ name: 'Spis treści'
+ });
+
+ expect(toggleLink.getAttribute('aria-pressed')).toBe('false');
+ await user.click(toggleLink);
+ expect(toggleLink.getAttribute('aria-pressed')).toBe('true');
+ mockIsPolish.mockReset();
+ });
+ it('excludes TOC option for retired books', async () => {
+ const retiredModel = {
+ ...baseModel,
+ bookState: 'comingSoon',
+ webviewRexLink: ''
+ };
+
+ render( );
+ await screen.findByText('Download a PDF');
+ expect(
+ screen.queryAllByRole('button', {name: 'Table of contents'})
+ ).toHaveLength(0);
+ });
+ it('shows PDF sample text for comingsoon books with PDF)', async () => {
+ const comingSoonModel = {
+ ...baseModel,
+ bookState: 'comingSoon',
+ comingSoon: true
+ };
+
+ render( );
+ await screen.findByText('Download a PDF sample');
+ });
+ it('shows iBooks options (2 volumes)', async () => {
+ const ibooksModel = {
+ ...baseModel,
+ ibookLink: 'first-volume',
+ ibookLink2: 'second-volume'
+ };
+
+ render( );
+ const expander = await screen.findByText('+ 2 more options...');
+
+ await user.click(expander);
+ screen.getByRole('link', {name: 'iBooks part 1'});
+ screen.getByRole('link', {name: 'iBooks part 2'});
});
});
diff --git a/test/src/pages/details/get-this-title/give-before-other.test.tsx b/test/src/pages/details/get-this-title/give-before-other.test.tsx
index 7c83ab39f..9736faadf 100644
--- a/test/src/pages/details/get-this-title/give-before-other.test.tsx
+++ b/test/src/pages/details/get-this-title/give-before-other.test.tsx
@@ -21,6 +21,7 @@ describe('give-before-other', () => {
close={close}
data={data}
onDownload={onDownload}
+ variant=''
/>
);
diff --git a/test/src/pages/details/get-this-title/give-before-pdf.test.tsx b/test/src/pages/details/get-this-title/give-before-pdf.test.tsx
index 1dcbebec4..5b1dbebcb 100644
--- a/test/src/pages/details/get-this-title/give-before-pdf.test.tsx
+++ b/test/src/pages/details/get-this-title/give-before-pdf.test.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import {render, screen} from '@testing-library/preact';
+import {fireEvent, render, screen} from '@testing-library/preact';
import userEvent from '@testing-library/user-event';
import {MemoryRouter} from 'react-router-dom';
import GiveBeforePdf from '~/pages/details/common/get-this-title-files/give-before-pdf/give-before-pdf';
@@ -19,7 +19,18 @@ const data: DonationPopupData = {
} as unknown as DonationPopupData; // doesn't matter
/* eslint-enable camelcase */
+function ShowThankYouButton({children}: React.PropsWithChildren) {
+ const {showThankYou, onThankYouClick} = TY.useOnThankYouClick();
+
+ if (showThankYou) {
+ return children;
+ }
+ return Show thank you ;
+}
+
describe('give-before-pdf', () => {
+ const originalError = console.error;
+
afterEach(() => {
jest.resetAllMocks();
jest.restoreAllMocks();
@@ -41,9 +52,27 @@ describe('give-before-pdf', () => {
await screen.findByText('your download is ready');
});
it('shows Thank You', async () => {
+ render(
+
+
+
+
+
+ );
+ await user.click(await screen.findByRole('button'));
+ await screen.findByText('Go to your file');
+ });
+ it('Thank You note can be filled and submitted', async () => {
+ const thankYouClick = jest.fn(() => console.info('** Thank you'));
+
jest.spyOn(TY, 'useOnThankYouClick').mockReturnValue({
showThankYou: true,
- onThankYouClick: () => null
+ onThankYouClick: () => thankYouClick()
});
render(
@@ -52,11 +81,34 @@ describe('give-before-pdf', () => {
close={close}
data={data}
onDownload={onDownload}
+ track="thanks"
/>
);
jest.runAllTimers();
- await screen.findByText('Send us a thank you note');
+ await screen.findByRole('heading', {
+ level: 1,
+ name: 'Send us a thank you note'
+ });
+ screen.getAllByRole('textbox').forEach((el) => {
+ fireEvent.input(el, {target: {value: 'something'}});
+ });
+ await user.click(screen.getByRole('checkbox'));
+ console.error = jest.fn();
+ // This is the submit button, but submit doesn't work in testing :(
+ await user.click(screen.getByRole('button'));
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining(
+ 'Not implemented: HTMLFormElement.prototype.submit'
+ ),
+ undefined
+ );
+ // So we can test the "never mind" link
+ await user.click(screen.getByRole('link'));
+ // Even firing the submit event doesn't cause anything to happen?
+ fireEvent.submit(screen.getByRole('form'));
+ // So we directly fire the load on the form target iframe
+ fireEvent.load(screen.getByTitle('form-response'));
});
it('Exercise data-track and datalayer effect', async () => {
(window as unknown as Window & {dataLayer: object[]}).dataLayer = [];
@@ -67,7 +119,7 @@ describe('give-before-pdf', () => {
close={close}
data={data}
onDownload={onDownload}
- track='something'
+ track="something"
/>
);
@@ -83,11 +135,7 @@ describe('give-before-pdf', () => {
expect(close).not.toHaveBeenCalled();
render(
-
+
);
screen.getByText('Downloading...');
@@ -99,11 +147,7 @@ describe('give-before-pdf', () => {
it('handles Give link click', async () => {
render(
-
+
);
screen.getByText('Downloading...');
@@ -113,6 +157,12 @@ describe('give-before-pdf', () => {
const links = screen.getAllByRole('link');
expect(links.length).toBe(2);
+ console.error = jest.fn();
await user.click(links[0]);
+ expect(console.error).toHaveBeenCalledWith(
+ expect.stringContaining('Not implemented: window.open'),
+ undefined
+ );
+ console.error = originalError;
});
});
diff --git a/test/src/pages/details/get-this-title/recommended-callout.test.js b/test/src/pages/details/get-this-title/recommended-callout.test.js
deleted file mode 100644
index 468fc227b..000000000
--- a/test/src/pages/details/get-this-title/recommended-callout.test.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import React from 'react';
-import {render, screen} from '@testing-library/preact';
-import {LanguageContextProvider} from '~/contexts/language';
-import RecommendedCallout from '~/pages/details/common/get-this-title-files/recommended-callout/recommended-callout';
-import BookDetailsLoader from '../book-details-context';
-
-function LangWrapRecommendedCallout({...args}) {
- return (
-
-
-
-
-
- );
-}
-
-test('defaults to "Recommended" and no blurb', async () => {
- render( );
- await screen.findByText('Recommended');
- expect(screen.getByRole('button').nextSibling).toBeNull();
-});
-test('displays custom title', async () => {
- render( )
- await screen.findByText('custom title');
-});
-test('displays custom blurb', async () => {
- const blurbHtml = 'some text ';
-
- render( )
- await screen.findByText('some text');
- expect(screen.getByRole('button').nextSibling).not.toBeNull();
-})
diff --git a/test/src/pages/details/get-this-title/recommended-callout.test.tsx b/test/src/pages/details/get-this-title/recommended-callout.test.tsx
new file mode 100644
index 000000000..e2fa3ed89
--- /dev/null
+++ b/test/src/pages/details/get-this-title/recommended-callout.test.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import {render, screen} from '@testing-library/preact';
+import {LanguageContextProvider} from '~/contexts/language';
+import RecommendedCallout from '~/pages/details/common/get-this-title-files/recommended-callout/recommended-callout';
+import BookDetailsLoader from '../book-details-context';
+
+const onPutAway = jest.fn();
+
+function LangWrapRecommendedCallout({...args}) {
+ return (
+
+
+
+
+
+ );
+}
+
+describe('details/recommended-callout', () => {
+ test('defaults to "Recommended" and no blurb', async () => {
+ render( );
+ await screen.findByText('Recommended');
+ expect(screen.getByRole('button').nextSibling).toBeNull();
+ });
+ test('displays custom title', async () => {
+ render( );
+ await screen.findByText('custom title');
+ });
+ test('displays custom blurb', async () => {
+ const blurbHtml = 'some text ';
+
+ render( );
+ await screen.findByText('some text');
+ expect(screen.getByRole('button').nextSibling).not.toBeNull();
+ });
+});