diff --git a/client/src/pages/challenges.test.ts b/client/src/pages/challenges.test.ts deleted file mode 100644 index 03784f35d8..0000000000 --- a/client/src/pages/challenges.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-nocheck -/** - * TODO: - * Passing incomplete objects here is causing TS to be angry. - * We should either make the expected properties optional, or reevaluate - * these tests. - */ -import { withPrefix } from 'gatsby'; -import toLearnPath from '../utils/to-learn-path'; - -describe('toLearnPath', () => { - it('should return a string', () => { - expect(typeof toLearnPath({})).toBe('string'); - }); - it('should include /learn', () => { - expect(toLearnPath({})).toMatch(withPrefix('/learn')); - }); - it('should include superBlock after learn', () => { - expect(toLearnPath({ superBlock: 'testSuper' })).toBe( - withPrefix('/learn/testSuper') - ); - }); - it('should include superBlock, then block after learn', () => { - expect(toLearnPath({ superBlock: 'testSuper', block: 'testBlock' })).toBe( - withPrefix('/learn/testSuper/testBlock') - ); - }); - it('should include superBlock, block, then challenge after learn', () => { - expect( - toLearnPath({ - superBlock: 'testSuper', - block: 'testBlock', - challenge: 'testChallenge' - }) - ).toBe(withPrefix('/learn/testSuper/testBlock/testChallenge')); - }); -}); diff --git a/client/src/pages/challenges.test.tsx b/client/src/pages/challenges.test.tsx new file mode 100644 index 0000000000..0be4a663ee --- /dev/null +++ b/client/src/pages/challenges.test.tsx @@ -0,0 +1,61 @@ +import { + createHistory, + createMemorySource, + LocationProvider +} from '@reach/router'; +import { render } from '@testing-library/react'; +import { navigate, withPrefix } from 'gatsby'; +import React from 'react'; + +import Challenges from './challenges'; + +describe('Challenges', () => { + // Source: https://testing-library.com/docs/example-reach-router/ + function renderWithRouterWrapper({ + route = '/', + history = createHistory(createMemorySource(route)) + } = {}) { + return { + ...render( + + + + ), + // adding `history` to the returned utilities to allow us + // to reference it in our tests (just try to avoid using + // this to test implementation details). + history + }; + } + + const challenges = withPrefix('/challenges'); + const learn = withPrefix('/learn'); + + it('should handle redirect to /learn', () => { + renderWithRouterWrapper({ route: challenges }); + + expect(navigate).toHaveBeenLastCalledWith(learn); + }); + + it('should handle redirect to /learn/:super-block', () => { + renderWithRouterWrapper({ route: `${challenges}/super-block` }); + + expect(navigate).toHaveBeenLastCalledWith(`${learn}/super-block`); + }); + + it('should handle redirect to /learn/:super-block/:block', () => { + renderWithRouterWrapper({ route: `${challenges}/super-block/block` }); + + expect(navigate).toHaveBeenLastCalledWith(`${learn}/super-block/block`); + }); + + it('should handle redirect to /learn/:super-block/:block/:challenge', () => { + renderWithRouterWrapper({ + route: `${challenges}/super-block/block/challenge` + }); + + expect(navigate).toHaveBeenLastCalledWith( + `${learn}/super-block/block/challenge` + ); + }); +}); diff --git a/client/src/pages/challenges.tsx b/client/src/pages/challenges.tsx index 86a5c4e478..d649516157 100644 --- a/client/src/pages/challenges.tsx +++ b/client/src/pages/challenges.tsx @@ -1,32 +1,25 @@ -// this exists purely to redirect legacy challenge paths to /learn -import { Router } from '@reach/router'; +// This exists purely to redirect legacy challenge paths to /learn that could +// exist in the web (posts, url shares, etc). + +import { Router, RouteComponentProps } from '@reach/router'; import { navigate, withPrefix } from 'gatsby'; import React from 'react'; import toLearnPath from '../utils/to-learn-path'; -// interface RedirectProps1 { -// superBlock: string; -// block: string; -// challenge: string; -// } +type RouteComponentPropsExtended = RouteComponentProps & { + block?: string; + challenge?: string; + superBlock?: string; +}; -// interface RedirectProps2 { -// path?: string; -// default?: boolean; -// } - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -function Redirect(props) { +function Redirect(props: RouteComponentPropsExtended): null { if (typeof window !== 'undefined') { void navigate(toLearnPath(props)); } + return null; } -// Unsure about Redirect props shape: -// toLearnPath() takes required superBlock, block, and challenge props -// but usage below has optional path and default props function Challenges(): JSX.Element { return ( diff --git a/client/src/utils/to-learn-path.test.ts b/client/src/utils/to-learn-path.test.ts new file mode 100644 index 0000000000..b886ce0cd3 --- /dev/null +++ b/client/src/utils/to-learn-path.test.ts @@ -0,0 +1,31 @@ +import { withPrefix } from 'gatsby'; + +import toLearnPath from './to-learn-path'; + +describe('To learn path utility (toLearnPath)', () => { + const learn = withPrefix('/learn'); + + it('should include /learn', () => { + expect(toLearnPath({})).toMatch(`${learn}`); + }); + + it('should include superBlock after learn', () => { + expect(toLearnPath({ superBlock: 'testSuper' })).toBe(`${learn}/testSuper`); + }); + + it('should include superBlock, then block after learn', () => { + expect(toLearnPath({ block: 'testBlock', superBlock: 'testSuper' })).toBe( + `${learn}/testSuper/testBlock` + ); + }); + + it('should include superBlock, block, then challenge after learn', () => { + expect( + toLearnPath({ + block: 'testBlock', + challenge: 'testChallenge', + superBlock: 'testSuper' + }) + ).toBe(`${learn}/testSuper/testBlock/testChallenge`); + }); +}); diff --git a/client/src/utils/to-learn-path.ts b/client/src/utils/to-learn-path.ts index 5fab37804f..e2c7d770a3 100644 --- a/client/src/utils/to-learn-path.ts +++ b/client/src/utils/to-learn-path.ts @@ -1,18 +1,30 @@ import { withPrefix } from 'gatsby'; interface ToLearnPathKwargs { - superBlock: string; - block: string; - challenge: string; + block?: string; + challenge?: string; + superBlock?: string; } + +/** + * Builds a learning url path from folders. + * - /learn/:superBlock/:block/:challenge + * @params {Object} Path kwargs. + * @returns {string} A learning url path. + */ export default function toLearnPath({ - superBlock, block, - challenge + challenge, + superBlock }: ToLearnPathKwargs): string { - let path = withPrefix('/learn'); - if (superBlock) path += `/${superBlock}`; - if (block) path += `/${block}`; - if (challenge) path += `/${challenge}`; - return path; + // Match path order /:super-block/:block/:challenge + const folders = [superBlock, block, challenge]; + + return folders.reduce((path: string, folder) => { + if (folder) { + return `${path}/${folder}`; + } + + return path; + }, withPrefix('/learn')); }