-
Notifications
You must be signed in to change notification settings - Fork 204
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: create component to decode params
- Loading branch information
1 parent
aa380e8
commit 52235eb
Showing
5 changed files
with
186 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`DecodePageRoute should not modify the url if it does not need to be decoded 1`] = ` | ||
<div> | ||
PageRoute: { | ||
"computedMatch": { | ||
"path": "/course/:courseId/home", | ||
"url": "/course/course-v1:edX+DemoX+Demo_Course/home", | ||
"isExact": true, | ||
"params": { | ||
"courseId": "course-v1:edX+DemoX+Demo_Course" | ||
} | ||
} | ||
} | ||
</div> | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import PropTypes from 'prop-types'; | ||
import { PageRoute } from '@edx/frontend-platform/react'; | ||
import React from 'react'; | ||
import { useHistory, generatePath } from 'react-router'; | ||
|
||
export const decodeUrl = (encodedUrl) => { | ||
const decodedUrl = decodeURIComponent(encodedUrl); | ||
if (encodedUrl === decodedUrl) { | ||
return encodedUrl; | ||
} | ||
return decodeUrl(decodedUrl); | ||
}; | ||
|
||
const DecodePageRoute = (props) => { | ||
const history = useHistory(); | ||
if (props.computedMatch) { | ||
const { url, path, params } = props.computedMatch; | ||
|
||
Object.keys(params).forEach((param) => { | ||
// only decode params not the entire url. | ||
// it is just to be safe and less prone to errors | ||
params[param] = decodeUrl(params[param]); | ||
}); | ||
|
||
const newUrl = generatePath(path, params); | ||
|
||
// if the url get decoded, reroute to the decoded url | ||
if (newUrl !== url) { | ||
history.replace(newUrl); | ||
} | ||
} | ||
|
||
return <PageRoute {...props} />; | ||
}; | ||
|
||
DecodePageRoute.propTypes = { | ||
computedMatch: PropTypes.shape({ | ||
url: PropTypes.string.isRequired, | ||
path: PropTypes.string.isRequired, | ||
// eslint-disable-next-line react/forbid-prop-types | ||
params: PropTypes.any, | ||
}), | ||
}; | ||
|
||
DecodePageRoute.defaultProps = { | ||
computedMatch: null, | ||
}; | ||
|
||
export default DecodePageRoute; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import { createMemoryHistory } from 'history'; | ||
import { Router, matchPath } from 'react-router'; | ||
import DecodePageRoute, { decodeUrl } from '.'; | ||
|
||
const decodedCourseId = 'course-v1:edX+DemoX+Demo_Course'; | ||
const encodedCourseId = encodeURIComponent(decodedCourseId); | ||
const deepEncodedCourseId = (() => { | ||
let path = encodedCourseId; | ||
for (let i = 0; i < 5; i++) { | ||
path = encodeURIComponent(path); | ||
} | ||
return path; | ||
})(); | ||
|
||
jest.mock('@edx/frontend-platform/react', () => ({ | ||
PageRoute: (props) => `PageRoute: ${JSON.stringify(props, null, 2)}`, | ||
})); | ||
|
||
const renderPage = (props) => { | ||
const memHistory = createMemoryHistory({ | ||
initialEntries: [props?.path], | ||
}); | ||
|
||
const history = { | ||
...memHistory, | ||
replace: jest.fn(), | ||
}; | ||
|
||
const { container } = render( | ||
<Router history={history}> | ||
<DecodePageRoute computedMatch={props} /> | ||
</Router>, | ||
); | ||
|
||
return { | ||
container, | ||
history, | ||
props, | ||
}; | ||
}; | ||
|
||
describe('DecodePageRoute', () => { | ||
it('should not modify the url if it does not need to be decoded', () => { | ||
const props = matchPath(`/course/${decodedCourseId}/home`, { | ||
path: '/course/:courseId/home', | ||
}); | ||
const { container, history } = renderPage(props); | ||
|
||
expect(props.url).toContain(decodedCourseId); | ||
expect(history.replace).not.toHaveBeenCalled(); | ||
expect(container).toMatchSnapshot(); | ||
}); | ||
|
||
it('should decode the url and replace the history if necessary', () => { | ||
const props = matchPath(`/course/${encodedCourseId}/home`, { | ||
path: '/course/:courseId/home', | ||
}); | ||
const { history } = renderPage(props); | ||
|
||
expect(props.url).not.toContain(decodedCourseId); | ||
expect(props.url).toContain(encodedCourseId); | ||
expect(history.replace.mock.calls[0][0]).toContain(decodedCourseId); | ||
}); | ||
|
||
it('should decode the url multiple times if necessary', () => { | ||
const props = matchPath(`/course/${deepEncodedCourseId}/home`, { | ||
path: '/course/:courseId/home', | ||
}); | ||
const { history } = renderPage(props); | ||
|
||
expect(props.url).not.toContain(decodedCourseId); | ||
expect(props.url).toContain(deepEncodedCourseId); | ||
expect(history.replace.mock.calls[0][0]).toContain(decodedCourseId); | ||
}); | ||
|
||
it('should only decode the url params and not the entire url', () => { | ||
const decodedUnitId = 'some+thing'; | ||
const encodedUnitId = encodeURIComponent(decodedUnitId); | ||
const props = matchPath(`/course/${deepEncodedCourseId}/${encodedUnitId}/${encodedUnitId}`, { | ||
path: `/course/:courseId/${encodedUnitId}/:unitId`, | ||
}); | ||
const { history } = renderPage(props); | ||
|
||
const decodedUrls = history.replace.mock.calls[0][0].split('/'); | ||
|
||
// unitId get decoded | ||
expect(decodedUrls.pop()).toContain(decodedUnitId); | ||
|
||
// path remain encoded | ||
expect(decodedUrls.pop()).toContain(encodedUnitId); | ||
|
||
// courseId get decoded | ||
expect(decodedUrls.pop()).toContain(decodedCourseId); | ||
}); | ||
}); | ||
|
||
describe('decodeUrl', () => { | ||
expect(decodeUrl(decodedCourseId)).toEqual(decodedCourseId); | ||
expect(decodeUrl(encodedCourseId)).toEqual(decodedCourseId); | ||
expect(decodeUrl(deepEncodedCourseId)).toEqual(decodedCourseId); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters