diff --git a/client/gatsby-node.js b/client/gatsby-node.js index 78bd8275af..270ed7da40 100644 --- a/client/gatsby-node.js +++ b/client/gatsby-node.js @@ -208,6 +208,18 @@ exports.onCreateBabelConfig = ({ actions }) => { }); }; +exports.onCreatePage = async ({ page, actions }) => { + const { createPage } = actions; + // Only update the `/challenges` page. + if (page.path.match(/^\/challenges/)) { + // page.matchPath is a special key that's used for matching pages + // with corresponding routes only on the client. + page.matchPath = '/challenges/*'; + // Update the page. + createPage(page); + } +}; + // TODO: this broke the React challenges, not sure why, but I'll investigate // further and reimplement if it's possible and necessary (Oliver) // Typically the schema can be inferred, but not when some challenges are diff --git a/client/src/pages/challenges.js b/client/src/pages/challenges.js new file mode 100644 index 0000000000..0291e098ff --- /dev/null +++ b/client/src/pages/challenges.js @@ -0,0 +1,36 @@ +// this exists purely to redirect legacy challenge paths to /learn +import React from 'react'; +import { Router } from '@reach/router'; +import { navigate } from 'gatsby'; + +import createRedirect from '../components/createRedirect'; + +const RedirectToLearn = createRedirect('/learn'); + +const Redirect = props => { + if (typeof window !== 'undefined') { + navigate(toLearnPath(props)); + } + return null; +}; + +const Challenges = () => ( + + + + + + +); + +Challenges.displayName = 'Challenges'; + +export function toLearnPath({ superBlock, block, challenge }) { + let path = '/learn'; + if (superBlock) path += `/${superBlock}`; + if (block) path += `/${block}`; + if (challenge) path += `/${challenge}`; + return path; +} + +export default Challenges; diff --git a/client/src/pages/challenges.test.js b/client/src/pages/challenges.test.js new file mode 100644 index 0000000000..86fd4dcc21 --- /dev/null +++ b/client/src/pages/challenges.test.js @@ -0,0 +1,28 @@ +/* global expect */ +import { toLearnPath } from './challenges'; + +describe('toLearnPath', () => { + it('should return a string', () => { + expect(typeof toLearnPath({})).toBe('string'); + }); + 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({ superBlock: 'testSuper', block: 'testBlock' })).toBe( + '/learn/testSuper/testBlock' + ); + }); + it('should include superBlock, block, then challenge after learn', () => { + expect( + toLearnPath({ + superBlock: 'testSuper', + block: 'testBlock', + challenge: 'testChallenge' + }) + ).toBe('/learn/testSuper/testBlock/testChallenge'); + }); +}); diff --git a/cypress/integration/learn/redirects/challenges.js b/cypress/integration/learn/redirects/challenges.js new file mode 100644 index 0000000000..9cefdd456c --- /dev/null +++ b/cypress/integration/learn/redirects/challenges.js @@ -0,0 +1,51 @@ +/* global cy expect */ + +const locations = { + chalSuper: '/challenges/responsive-web-design/', + chalBlock: '/challenges/responsive-web-design/basic-html-and-html5', + chalChallenge: + // eslint-disable-next-line max-len + '/challenges/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements', + learnSuper: '/learn/responsive-web-design', + learnBlock: '/learn/responsive-web-design/basic-html-and-html5', + learnChallenge: + // eslint-disable-next-line max-len + '/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements' +}; + +describe('challenges/superblock redirect', function() { + it('redirects to learn/superblock', () => { + cy.visit(locations.chalSuper); + + cy.title().should('eq', 'Responsive Web Design | freeCodeCamp.org'); + cy.location().should(loc => { + expect(loc.pathname).to.eq(locations.learnSuper); + }); + }); +}); + +describe('challenges/superblock/block redirect', function() { + it('redirects to learn/superblock/block', () => { + cy.visit(locations.chalBlock); + + cy.title().should('eq', 'Basic HTML and HTML5 | freeCodeCamp.org'); + cy.location().should(loc => { + expect(loc.pathname).to.eq(locations.learnBlock); + }); + }); +}); + +describe('challenges/superblock/block/challenge redirect', function() { + it('redirects to learn/superblock/block/challenge', () => { + cy.visit(locations.chalChallenge); + + cy.title().should( + 'eq', + // eslint-disable-next-line max-len + 'Learn Basic HTML and HTML5: Say Hello to HTML Elements | freeCodeCamp.org' + ); + cy.location().should(loc => { + expect(loc.pathname).to.eq(locations.learnChallenge); + }); + }); +});