diff --git a/client/src/client/frame-runner.js b/client/src/client/frame-runner.js index 84a94dce7d..e2bf59931f 100644 --- a/client/src/client/frame-runner.js +++ b/client/src/client/frame-runner.js @@ -1,5 +1,6 @@ import '@babel/polyfill'; import jQuery from 'jquery'; +import curriculumHelpers from '../utils/curriculum-helpers'; window.$ = jQuery; @@ -48,6 +49,7 @@ async function initTestFrame(e = { code: {} }) { // eslint-disable-next-line no-inline-comments const { default: chai } = await import(/* webpackChunkName: "chai" */ 'chai'); const assert = chai.assert; + const __helpers = curriculumHelpers; /* eslint-enable no-unused-vars */ let Enzyme; diff --git a/client/src/client/workers/test-evaluator.js b/client/src/client/workers/test-evaluator.js index 4f146101ac..cec40e4eba 100644 --- a/client/src/client/workers/test-evaluator.js +++ b/client/src/client/workers/test-evaluator.js @@ -2,6 +2,7 @@ import chai from 'chai'; import '@babel/polyfill'; import __toString from 'lodash/toString'; import { format as __format } from '../../utils/format'; +import curriculumHelpers from '../../utils/curriculum-helpers'; const __utils = (() => { const MAX_LOGS_SIZE = 64 * 1024; @@ -59,6 +60,7 @@ self.onmessage = async e => { const editableContents = (e.data?.code?.editableContents || '').slice(); const assert = chai.assert; + const __helpers = curriculumHelpers; // Fake Deep Equal dependency const DeepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b); diff --git a/client/src/utils/__fixtures/curriculum-helpers-css.js b/client/src/utils/__fixtures/curriculum-helpers-css.js new file mode 100644 index 0000000000..e34371664f --- /dev/null +++ b/client/src/utils/__fixtures/curriculum-helpers-css.js @@ -0,0 +1,45 @@ +const cssFullExample = ` +a { + color: green; + display: flex; +} +.aClass { + font-size: 32px; + /* the property below should not appear in final css string + width: 400px; + height: 200px; + */ + flex: 1; + flex-direction: row; +} +/* Set the background color to blue for screens that are 300px or less */ +@media screen and (max-width: 300px) { + body { + background-color: blue; + } +}`; + +const cssCodeWithCommentsRemoved = ` +a { + color: green; + display: flex; +} +.aClass { + font-size: 32px; + + flex: 1; + flex-direction: row; +} + +@media screen and (max-width: 300px) { + body { + background-color: blue; + } +}`; + +const testValues = { + cssFullExample, + cssCodeWithCommentsRemoved +}; + +export default testValues; diff --git a/client/src/utils/__fixtures/curriculum-helpers-html.js b/client/src/utils/__fixtures/curriculum-helpers-html.js new file mode 100644 index 0000000000..0e8580fc8a --- /dev/null +++ b/client/src/utils/__fixtures/curriculum-helpers-html.js @@ -0,0 +1,30 @@ +const htmlFullExample = ` + + +not a comment + +not a commment not a comment +not a comment + +not a comment +`; + +const htmlCodeWithCommentsRemoved = ` + + +not a comment + +not a commment not a comment +not a comment + +not a comment +`; + +const testValues = { + htmlFullExample, + htmlCodeWithCommentsRemoved +}; + +export default testValues; diff --git a/client/src/utils/__fixtures/curriculum-helpers-javascript.js b/client/src/utils/__fixtures/curriculum-helpers-javascript.js new file mode 100644 index 0000000000..399f5d8461 --- /dev/null +++ b/client/src/utils/__fixtures/curriculum-helpers-javascript.js @@ -0,0 +1,44 @@ +const jsCodeWithSingleAndMultLineComments = ` +function nonMutatingPush(original, newItem) { + /* This is a + multi-line comment + that should be removed. */ + return original.push(newItem); +} +var first = [1, 2, 3]; +// This is a single line comment +var second = [4, 5]; +nonMutatingPush(first, second);`; + +const jsCodeWithSingleAndMultLineCommentsRemoved = ` +function nonMutatingPush(original, newItem) { + + + + return original.push(newItem); +} +var first = [1, 2, 3]; + +var second = [4, 5]; +nonMutatingPush(first, second);`; + +const jsCodeWithUrl = ` +function nonMutatingPush(original, newItem) { + var url = 'https://freecodecamp.org'; // this comment should vanish + return original.push(newItem); +}`; + +const jsCodeWithUrlUnchanged = ` +function nonMutatingPush(original, newItem) { + var url = 'https://freecodecamp.org'; + return original.push(newItem); +}`; + +const testValues = { + jsCodeWithSingleAndMultLineComments, + jsCodeWithSingleAndMultLineCommentsRemoved, + jsCodeWithUrl, + jsCodeWithUrlUnchanged +}; + +export default testValues; diff --git a/client/src/utils/__fixtures/curriculum-helpers-remove-white-space.js b/client/src/utils/__fixtures/curriculum-helpers-remove-white-space.js new file mode 100644 index 0000000000..841279da24 --- /dev/null +++ b/client/src/utils/__fixtures/curriculum-helpers-remove-white-space.js @@ -0,0 +1,16 @@ +const stringWithWhiteSpaceChars = ` +This string sentence has various white spaces characters: +\t* This line starts with a tab character. +\t* This line has several preceding white space characters.`; + +/* eslint-disable max-len */ +const stringWithWhiteSpaceCharsRemoved = + 'Thisstringsentencehasvariouswhitespacescharacters:*Thislinestartswithatabcharacter.*Thislinehasseveralprecedingwhitespacecharacters.'; +/* esline-enable max-len */ + +const testValues = { + stringWithWhiteSpaceChars, + stringWithWhiteSpaceCharsRemoved +}; + +export default testValues; diff --git a/client/src/utils/curriculum-helpers.js b/client/src/utils/curriculum-helpers.js new file mode 100644 index 0000000000..fc9699dd0a --- /dev/null +++ b/client/src/utils/curriculum-helpers.js @@ -0,0 +1,35 @@ +import { parse } from '@babel/parser'; +import generate from '@babel/generator'; + +const removeHtmlComments = str => str.replace(//g, ''); + +const removeCssComments = str => str.replace(/\/\*[\s\S]+?\*\//g, ''); + +const removeJSComments = codeStr => { + // Note: removes trailing new lines and tailing spaces at end of lines + const options = { + comments: false, + retainLines: true, + compact: false, + concise: false, + minified: false + }; + try { + const ast = parse(codeStr); + const { code } = generate(ast, options, codeStr); + return code; + } catch (err) { + return codeStr; + } +}; + +const removeWhiteSpace = (str = '') => { + return str.replace(/\s/g, ''); +}; + +export default { + removeHtmlComments, + removeCssComments, + removeJSComments, + removeWhiteSpace +}; diff --git a/client/src/utils/curriculum-helpers.test.js b/client/src/utils/curriculum-helpers.test.js new file mode 100644 index 0000000000..b8c5388f2c --- /dev/null +++ b/client/src/utils/curriculum-helpers.test.js @@ -0,0 +1,87 @@ +/* global describe it expect */ + +import __testHelpers from './curriculum-helpers'; +import jsTestValues from './__fixtures/curriculum-helpers-javascript'; +import cssTestValues from './__fixtures/curriculum-helpers-css'; +import htmlTestValues from './__fixtures/curriculum-helpers-html'; +/* eslint-disable max-len */ +import whiteSpaceTestValues from './__fixtures/curriculum-helpers-remove-white-space'; +/* eslint-enable max-len */ + +const { + stringWithWhiteSpaceChars, + stringWithWhiteSpaceCharsRemoved +} = whiteSpaceTestValues; + +const { cssFullExample, cssCodeWithCommentsRemoved } = cssTestValues; + +const { htmlFullExample, htmlCodeWithCommentsRemoved } = htmlTestValues; + +const { + jsCodeWithSingleAndMultLineComments, + jsCodeWithSingleAndMultLineCommentsRemoved, + jsCodeWithUrl, + jsCodeWithUrlUnchanged +} = jsTestValues; + +describe('removeWhiteSpace', () => { + const { removeWhiteSpace } = __testHelpers; + it('returns a string', () => { + expect(typeof removeWhiteSpace('This should return a string')).toBe( + 'string' + ); + }); + + it('returns a string with no white space characters', () => { + expect(removeWhiteSpace(stringWithWhiteSpaceChars)).toBe( + stringWithWhiteSpaceCharsRemoved + ); + }); +}); + +describe('removeJSComments', () => { + const { removeJSComments } = __testHelpers; + it('returns a string', () => { + expect(typeof removeJSComments('const should = "return a string"')).toBe( + 'string' + ); + }); + + it('returns a string with no single or multi-line comments', () => { + expect(removeJSComments(jsCodeWithSingleAndMultLineComments)).toBe( + jsCodeWithSingleAndMultLineCommentsRemoved + ); + }); + + it('does not remove a url found in JS code', () => { + expect(removeJSComments(jsCodeWithUrl)).toBe(jsCodeWithUrlUnchanged); + }); +}); + +describe('removeCssComments', () => { + const { removeCssComments } = __testHelpers; + it('returns a string', () => { + expect(typeof removeCssComments('.aClass: { color: red; }')).toBe('string'); + }); + + it('returns a CSS string with no single or multi-line comments', () => { + expect(removeCssComments(cssFullExample)).toBe(cssCodeWithCommentsRemoved); + }); +}); + +describe('removeHtmlComments', () => { + const { removeHtmlComments } = __testHelpers; + it('returns a string', () => { + expect( + typeof removeHtmlComments( + '
ball1
and ball2
, add an ball1
should be linear.
- testString: const ball1Animation = $('#ball1').css('animation-timing-function').replace(/\s/g, '');assert(ball1Animation == 'linear' || ball1Animation == 'cubic-bezier(0,0,1,1)');
+ testString: const ball1Animation = __helpers.removeWhiteSpace($('#ball1').css('animation-timing-function'));assert(ball1Animation == 'linear' || ball1Animation == 'cubic-bezier(0,0,1,1)');
- text: The value of the animation-timing-function
property for the element with the id ball2
should be ease-out.
- testString: const ball2Animation = $('#ball2').css('animation-timing-function').replace(/\s/g, ''); assert(ball2Animation == 'ease-out' || ball2Animation == 'cubic-bezier(0,0,0.58,1)');
+ testString: const ball2Animation = __helpers.removeWhiteSpace($('#ball2').css('animation-timing-function')); assert(ball2Animation == 'ease-out' || ball2Animation == 'cubic-bezier(0,0,0.58,1)');
```
diff --git a/curriculum/challenges/english/01-responsive-web-design/applied-visual-design/learn-how-bezier-curves-work.english.md b/curriculum/challenges/english/01-responsive-web-design/applied-visual-design/learn-how-bezier-curves-work.english.md
index 15e8da813f..102eb9cd9a 100644
--- a/curriculum/challenges/english/01-responsive-web-design/applied-visual-design/learn-how-bezier-curves-work.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/applied-visual-design/learn-how-bezier-curves-work.english.md
@@ -28,7 +28,7 @@ tests:
- text: The value of the animation-timing-function
property for the element with the id ball1
should be the linear-equivalent cubic-bezier function.
testString: assert($('#ball1').css('animation-timing-function') == 'cubic-bezier(0.25, 0.25, 0.75, 0.75)');
- text: The value of the animation-timing-function
property for the element with the id ball2
should not change.
- testString: const ball2Animation = $('#ball2').css('animation-timing-function').replace(/\s/g, ''); assert(ball2Animation == 'ease-out' || ball2Animation == 'cubic-bezier(0,0,0.58,1)');
+ testString: const ball2Animation = __helpers.removeWhiteSpace($('#ball2').css('animation-timing-function')); assert(ball2Animation == 'ease-out' || ball2Animation == 'cubic-bezier(0,0,0.58,1)');
```
diff --git a/curriculum/challenges/english/01-responsive-web-design/applied-visual-design/use-a-bezier-curve-to-move-a-graphic.english.md b/curriculum/challenges/english/01-responsive-web-design/applied-visual-design/use-a-bezier-curve-to-move-a-graphic.english.md
index 9f4460f38f..ff0d021daf 100644
--- a/curriculum/challenges/english/01-responsive-web-design/applied-visual-design/use-a-bezier-curve-to-move-a-graphic.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/applied-visual-design/use-a-bezier-curve-to-move-a-graphic.english.md
@@ -29,7 +29,7 @@ tests:
- text: The element with the id red
should no longer have the animation-timing-function
property of linear.
testString: assert($('#red').css('animation-timing-function') !== 'linear');
- text: The value of the animation-timing-function
property for the element with the id blue
should not change.
- testString: const blueBallAnimation = $('#blue').css('animation-timing-function').replace(/\s/g, ''); assert(blueBallAnimation == 'ease-out' || blueBallAnimation == 'cubic-bezier(0,0,0.58,1)');
+ testString: const blueBallAnimation = __helpers.removeWhiteSpace($('#blue').css('animation-timing-function')); assert(blueBallAnimation == 'ease-out' || blueBallAnimation == 'cubic-bezier(0,0,0.58,1)');
```
diff --git a/curriculum/challenges/english/01-responsive-web-design/basic-css/use-clockwise-notation-to-specify-the-margin-of-an-element.english.md b/curriculum/challenges/english/01-responsive-web-design/basic-css/use-clockwise-notation-to-specify-the-margin-of-an-element.english.md
index 565ec4e833..3ffe096944 100644
--- a/curriculum/challenges/english/01-responsive-web-design/basic-css/use-clockwise-notation-to-specify-the-margin-of-an-element.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/basic-css/use-clockwise-notation-to-specify-the-margin-of-an-element.english.md
@@ -33,7 +33,7 @@ tests:
- text: Your blue-box
class should give the left of elements 40px
of margin
.
testString: assert($(".blue-box").css("margin-left") === "40px");
- text: You should use the clockwise notation to set the margin of blue-box
class.
- testString: const removeCssComments = str => str.replace(/\/\*[\s\S]+?\*\//g, '');assert(/\.blue-box\s*{[\s\S]*margin[\s]*:\s*\d+px\s+\d+px\s+\d+px\s+\d+px(;\s*[^}]+\s*}|;?\s*})/.test(removeCssComments($('style').text())));
+ testString: assert(/\.blue-box\s*{[\s\S]*margin[\s]*:\s*\d+px\s+\d+px\s+\d+px\s+\d+px(;\s*[^}]+\s*}|;?\s*})/.test(__helpers.removeCssComments($('style').text())));
```
diff --git a/curriculum/challenges/english/01-responsive-web-design/basic-css/use-clockwise-notation-to-specify-the-padding-of-an-element.english.md b/curriculum/challenges/english/01-responsive-web-design/basic-css/use-clockwise-notation-to-specify-the-padding-of-an-element.english.md
index 4a653fbc19..80ad2f5acf 100644
--- a/curriculum/challenges/english/01-responsive-web-design/basic-css/use-clockwise-notation-to-specify-the-padding-of-an-element.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/basic-css/use-clockwise-notation-to-specify-the-padding-of-an-element.english.md
@@ -32,7 +32,7 @@ tests:
- text: Your blue-box
class should give the left of elements 40px
of padding
.
testString: assert($(".blue-box").css("padding-left") === "40px");
- text: You should use the clockwise notation to set the padding of blue-box
class.
- testString: const removeCssComments = str => str.replace(/\/\*[\s\S]+?\*\//g, '');assert(/\.blue-box\s*{[\s\S]*padding[\s]*:\s*\d+px\s+\d+px\s+\d+px\s+\d+px(;\s*[^}]+\s*}|;?\s*})/.test(removeCssComments($('style').text())));
+ testString: assert(/\.blue-box\s*{[\s\S]*padding[\s]*:\s*\d+px\s+\d+px\s+\d+px\s+\d+px(;\s*[^}]+\s*}|;?\s*})/.test(__helpers.removeCssComments($('style').text())));
```
diff --git a/curriculum/challenges/english/01-responsive-web-design/basic-html-and-html5/add-images-to-your-website.english.md b/curriculum/challenges/english/01-responsive-web-design/basic-html-and-html5/add-images-to-your-website.english.md
index 4f82075ac9..b0c03dcd3c 100644
--- a/curriculum/challenges/english/01-responsive-web-design/basic-html-and-html5/add-images-to-your-website.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/basic-html-and-html5/add-images-to-your-website.english.md
@@ -38,7 +38,7 @@ tests:
- text: Your image should have a src
attribute that points to the kitten image.
testString: assert(/^https:\/\/bit\.ly\/fcc-relaxing-cat$/i.test($("img").attr("src")));
- text: Your image element's alt
attribute should not be empty.
- testString: assert($("img").attr("alt") && $("img").attr("alt").length && /li
element should have a closing tag.
testString: assert(code.match(/<\/li>/g) && code.match(/li
elements in your unordered list should not be empty.
- testString: $('ul li').each((i, val) => assert(val.textContent.replace(/\s/g, '')));
+ testString: $('ul li').each((i, val) => assert(__helpers.removeWhiteSpace(val.textContent)));
- text: The li
elements in your ordered list should not be empty.
- testString: $('ol li').each((i, val) => assert(!!val.textContent.replace(/\s/g, '')));
+ testString: $('ol li').each((i, val) => assert(!!__helpers.removeWhiteSpace(val.textContent)));
```
diff --git a/curriculum/challenges/english/01-responsive-web-design/css-grid/divide-the-grid-into-an-area-template.english.md b/curriculum/challenges/english/01-responsive-web-design/css-grid/divide-the-grid-into-an-area-template.english.md
index 1a91452b87..f8a7127713 100644
--- a/curriculum/challenges/english/01-responsive-web-design/css-grid/divide-the-grid-into-an-area-template.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/css-grid/divide-the-grid-into-an-area-template.english.md
@@ -33,7 +33,7 @@ Place the area template so that the cell labeled advert
becomes an
```yml
tests:
- text: container
class should have a grid-template-areas
property similar to the preview but has .
instead of the advert
area.
- testString: const removeCssComments = str => str.replace(/\/\*[\s\S]+?\*\//g, ''); assert(removeCssComments(code).match(/.container\s*?{[\s\S]*grid-template-areas\s*?:\s*?"\s*?header\s*?header\s*?header\s*?"\s*?"\s*?.\s*?content\s*?content\s*?"\s*?"\s*?footer\s*?footer\s*?footer\s*?"\s*?;[\s\S]*}/gi));
+ testString: assert(__helpers.removeCssComments(code).match(/.container\s*?{[\s\S]*grid-template-areas\s*?:\s*?"\s*?header\s*?header\s*?header\s*?"\s*?"\s*?.\s*?content\s*?content\s*?"\s*?"\s*?footer\s*?footer\s*?footer\s*?"\s*?;[\s\S]*}/gi));
```
diff --git a/curriculum/challenges/english/01-responsive-web-design/css-grid/place-items-in-grid-areas-using-the-grid-area-property.english.md b/curriculum/challenges/english/01-responsive-web-design/css-grid/place-items-in-grid-areas-using-the-grid-area-property.english.md
index d1b2b63334..60be806fca 100644
--- a/curriculum/challenges/english/01-responsive-web-design/css-grid/place-items-in-grid-areas-using-the-grid-area-property.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/css-grid/place-items-in-grid-areas-using-the-grid-area-property.english.md
@@ -30,7 +30,7 @@ Place an element with the item5
class in the footer
ar
```yml
tests:
- text: item5
class should have a grid-area
property that has the value of footer
.
- testString: const removeCssComments = str => str.replace(/\/\*[\s\S]+?\*\//g, '');assert(removeCssComments(code).match(/.item5\s*?{[\s\S]*grid-area\s*?:\s*?footer\s*?;[\s\S]*}/gi));
+ testString: assert(__helpers.removeCssComments(code).match(/.item5\s*?{[\s\S]*grid-area\s*?:\s*?footer\s*?;[\s\S]*}/gi));
```
diff --git a/curriculum/challenges/english/01-responsive-web-design/css-grid/use-grid-column-to-control-spacing.english.md b/curriculum/challenges/english/01-responsive-web-design/css-grid/use-grid-column-to-control-spacing.english.md
index 203a4852be..865ec3d707 100644
--- a/curriculum/challenges/english/01-responsive-web-design/css-grid/use-grid-column-to-control-spacing.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/css-grid/use-grid-column-to-control-spacing.english.md
@@ -33,7 +33,7 @@ Make the item with the class item5
consume the last two columns of
```yml
tests:
- text: item5
class should have a grid-column
property.
- testString: assert($('style').text().replace(/\s/g, '').match(/\.item5{.*grid-column:.*}/g));
+ testString: assert(__helpers.removeWhiteSpace($('style').text()).match(/\.item5{.*grid-column:.*}/g));
- text: item5
class should have a grid-column
property which results in it consuming the last two columns of the grid.
testString: "
const colStart = getComputedStyle($('.item5')[0]).gridColumnStart;
diff --git a/curriculum/challenges/english/01-responsive-web-design/css-grid/use-grid-row-to-control-spacing.english.md b/curriculum/challenges/english/01-responsive-web-design/css-grid/use-grid-row-to-control-spacing.english.md
index d497eed571..b32308830e 100644
--- a/curriculum/challenges/english/01-responsive-web-design/css-grid/use-grid-row-to-control-spacing.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/css-grid/use-grid-row-to-control-spacing.english.md
@@ -22,7 +22,7 @@ Make the element with the item5
class consume the last two rows.
```yml
tests:
- text: item5
class should have a grid-row
property.
- testString: assert($('style').text().replace(/\s/g, '').match(/\.item5{.*grid-row:.*}/g));
+ testString: assert(__helpers.removeWhiteSpace($('style').text()).match(/\.item5{.*grid-row:.*}/g));
- text: item5
class should have a grid-row
property which results in it consuming the last two rows of the grid.
testString: "
const rowStart = getComputedStyle($('.item5')[0]).gridRowStart;
diff --git a/curriculum/challenges/english/01-responsive-web-design/css-grid/use-media-queries-to-create-responsive-layouts.english.md b/curriculum/challenges/english/01-responsive-web-design/css-grid/use-media-queries-to-create-responsive-layouts.english.md
index 5fdc5102cb..1f682b7179 100644
--- a/curriculum/challenges/english/01-responsive-web-design/css-grid/use-media-queries-to-create-responsive-layouts.english.md
+++ b/curriculum/challenges/english/01-responsive-web-design/css-grid/use-media-queries-to-create-responsive-layouts.english.md
@@ -23,7 +23,7 @@ When the viewport width is 400px
or more, make the header area occu
```yml
tests:
- text: When the viewport is 400px
or more, container
class should have a grid-template-areas
property in which the header and footer areas occupy the top and bottom rows respectively and advert and content occupy the left and right columns of the middle row.
- testString: const removeCssComments = str => str.replace(/\/\*[\s\S]+?\*\//g, ''); assert(removeCssComments(code).match(/@media\s*?\(\s*?min-width\s*?:\s*?400px\s*?\)[\s\S]*.container\s*?{[\s\S]*grid-template-areas\s*?:\s*?"\s*?header\s*?header\s*?"\s*?"\s*?advert\s*?content\s*?"\s*?"\s*?footer\s*?footer\s*?"\s*?;[\s\S]*}/gi));
+ testString: assert(__helpers.removeCssComments(code).match(/@media\s*?\(\s*?min-width\s*?:\s*?400px\s*?\)[\s\S]*.container\s*?{[\s\S]*grid-template-areas\s*?:\s*?"\s*?header\s*?header\s*?"\s*?"\s*?advert\s*?content\s*?"\s*?"\s*?footer\s*?footer\s*?"\s*?;[\s\S]*}/gi));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-data-structures/copy-an-array-with-the-spread-operator.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-data-structures/copy-an-array-with-the-spread-operator.english.md
index c4351d1318..ff6bb4149e 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-data-structures/copy-an-array-with-the-spread-operator.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-data-structures/copy-an-array-with-the-spread-operator.english.md
@@ -38,7 +38,7 @@ tests:
- text: copyMachine(["it works"], 3)
should return [["it works"], ["it works"], ["it works"]]
testString: assert.deepEqual(copyMachine(['it works'], 3), [['it works'], ['it works'], ['it works']]);
- text: The copyMachine
function should utilize the spread operator
with array arr
- testString: assert(removeJSComments(code).match(/\.\.\.arr/));
+ testString: assert(__helpers.removeJSComments(code).match(/\.\.\.arr/));
```
@@ -66,15 +66,6 @@ console.log(copyMachine([true, false, true], 2));
-### After Test
-const arr = [2, 4, 5, 1, 7, 5, 2, 1];
.
- testString: assert(code.replace(/\s/g, '').match(/constarr=\[2,4,5,1,7,5,2,1\];?/));
+ testString: assert(__helpers.removeWhiteSpace(code).match(/constarr=\[2,4,5,1,7,5,2,1\];?/));
- text: arr
should only contain elements that sum to 10
.
testString: assert.strictEqual(arr.reduce((a, b) => a + b), 10);
- text: Your code should utilize the splice()
method on arr
.
- testString: assert(code.replace(/\s/g, '').match(/arr\.splice\(/));
+ testString: assert(__helpers.removeWhiteSpace(code).match(/arr\.splice\(/));
- text: The splice should only remove elements from arr
and not add any additional elements to arr
.
- testString: assert(!code.replace(/\s/g, '').match(/arr\.splice\(\d+,\d+,\d+.*\)/g));
+ testString: assert(!__helpers.removeWhiteSpace(code).match(/arr\.splice\(\d+,\d+,\d+.*\)/g));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/access-multi-dimensional-arrays-with-indexes.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/access-multi-dimensional-arrays-with-indexes.english.md
index be1b328d76..cbd2f8d3b9 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/access-multi-dimensional-arrays-with-indexes.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/access-multi-dimensional-arrays-with-indexes.english.md
@@ -39,7 +39,7 @@ tests:
- text: myData
should be equal to 8
.
testString: assert(myData === 8);
- text: You should be using bracket notation to read the correct value from myArray
.
- testString: assert(/myData=myArray\[2\]\[1\]/.test(code.replace(/\s/g, '')));
+ testString: assert(/myData=myArray\[2\]\[1\]/.test(__helpers.removeWhiteSpace(code)));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/iterate-through-an-array-with-a-for-loop.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/iterate-through-an-array-with-a-for-loop.english.md
index 179f5f6add..acd6d4e5b0 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/iterate-through-an-array-with-a-for-loop.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/iterate-through-an-array-with-a-for-loop.english.md
@@ -37,7 +37,7 @@ tests:
- text: You should use a for
loop to iterate through myArr
.
testString: assert(/for\s*\(/g.test(code) && /myArr\s*\[/g.test(code));
- text: You should not attempt to directly assign the value 20 to total
.
- testString: assert(!code.replace(/\s/g, '').match(/total[=+-]0*[1-9]+/gm));
+ testString: assert(!__helpers.removeWhiteSpace(code).match(/total[=+-]0*[1-9]+/gm));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/local-scope-and-functions.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/local-scope-and-functions.english.md
index 0f223c304e..9c858a5c4b 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/local-scope-and-functions.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/local-scope-and-functions.english.md
@@ -43,7 +43,7 @@ tests:
}
assert.throws(declared, ReferenceError);
- text: You should add a local myVar
variable.
- testString: assert(/functionmyLocalScope\(\)\{.+(var|let|const)myVar[\s\S]*}/.test(code.replace(/\s/g, '')));
+ testString: assert(/functionmyLocalScope\(\)\{.+(var|let|const)myVar[\s\S]*}/.test(__helpers.removeWhiteSpace(code)));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/replace-loops-using-recursion.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/replace-loops-using-recursion.english.md
index 0cf58778f4..ac1953f4a1 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/replace-loops-using-recursion.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/replace-loops-using-recursion.english.md
@@ -57,9 +57,9 @@ tests:
- text: sum([2, 3, 4, 5], 3)
should equal 9.
testString: assert.equal(sum([2, 3, 4, 5], 3), 9);
- text: Your code should not rely on any kind of loops (for
or while
or higher order functions such as forEach
, map
, filter
, or reduce
.).
- testString: assert(!removeJSComments(code).match(/for|while|forEach|map|filter|reduce/g));
+ testString: assert(!__helpers.removeJSComments(code).match(/for|while|forEach|map|filter|reduce/g));
- text: You should use recursion to solve this problem.
- testString: assert(removeJSComments(sum.toString()).match(/sum\(.*\)/g).length > 1);
+ testString: assert(__helpers.removeJSComments(sum.toString()).match(/sum\(.*\)/g).length > 1);
```
@@ -80,16 +80,6 @@ function sum(arr, n) {
-### After Test
-difference
should be equal to 12.
testString: assert(difference === 12);
- text: You should only subtract one number from 45.
- testString: assert(/difference=45-33;?/.test(code.replace(/\s/g, '')));
+ testString: assert(/difference=45-33;?/.test(__helpers.removeWhiteSpace(code)));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/understanding-undefined-value-returned-from-a-function.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/understanding-undefined-value-returned-from-a-function.english.md
index da06cb5d05..430712ec91 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/understanding-undefined-value-returned-from-a-function.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/understanding-undefined-value-returned-from-a-function.english.md
@@ -39,7 +39,7 @@ tests:
- text: Returned value from addFive
should be undefined
.
testString: assert(addFive() === undefined);
- text: Inside the addFive
function, you should add 5
to the sum
variable.
- testString: assert(addFive.toString().replace(/\s/g, '').match(/sum=sum\+5|sum\+=5/));
+ testString: assert(__helpers.removeWhiteSpace(addFive.toString()).match(/sum=sum\+5|sum\+=5/));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/use-recursion-to-create-a-countdown.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/use-recursion-to-create-a-countdown.english.md
index 067beecc4f..f043708d27 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/use-recursion-to-create-a-countdown.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/use-recursion-to-create-a-countdown.english.md
@@ -52,9 +52,9 @@ tests:
- text: countdown(5)
should return [5, 4, 3, 2, 1]
testString: assert.deepStrictEqual(countdown(5), [5, 4, 3, 2, 1]);
- text: Your code should not rely on any kind of loops (for
, while
or higher order functions such as forEach
, map
, filter
, and reduce
).
- testString: assert(!removeJSComments(code).match(/for|while|forEach|map|filter|reduce/g));
+ testString: assert(!__helpers.removeJSComments(code).match(/for|while|forEach|map|filter|reduce/g));
- text: You should use recursion to solve this problem.
- testString: assert(removeJSComments(countdown.toString()).match(/countdown\s*\(.+\)/));
+ testString: assert(__helpers.removeJSComments(countdown.toString()).match(/countdown\s*\(.+\)/));
```
@@ -76,15 +76,6 @@ function countdown(n){
-### After Test
-for
or while
or higher order functions such as forEach
, map
, filter
, or reduce
).
- testString: assert(!removeJSComments(code).match(/for|while|forEach|map|filter|reduce/g));
+ testString: assert(!__helpers.removeJSComments(code).match(/for|while|forEach|map|filter|reduce/g));
- text: rangeOfNumbers
should use recursion (call itself) to solve this challenge.
- testString: assert(removeJSComments(rangeOfNumbers.toString()).match(/rangeOfNumbers\s*\(.+\)/));
+ testString: assert(__helpers.removeJSComments(rangeOfNumbers.toString()).match(/rangeOfNumbers\s*\(.+\)/));
- text: rangeOfNumbers(1, 5)
should return [1, 2, 3, 4, 5]
.
testString: assert.deepStrictEqual(rangeOfNumbers(1, 5), [1, 2, 3, 4, 5]);
- text: rangeOfNumbers(6, 9)
should return [6, 7, 8, 9]
.
@@ -51,15 +51,6 @@ function rangeOfNumbers(startNum, endNum) {
-### After Test
-console.log
to log the output
variable. The
```yml
tests:
- text: You should use console.clear()
to clear the browser console.
- testString: const removeJSComments = code.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, ''); const noSpaces = removeJSComments.replace(/\s/g, ''); assert(noSpaces.match(/console.clear\(\)/));
+ testString: assert(__helpers.removeWhiteSpace(__helpers.removeJSComments(code)).match(/console.clear\(\)/));
- text: You should use console.log()
to print the output
variable.
- testString: const noSpaces = code.replace(/\s/g, ''); assert(noSpaces.match(/console\.log\(output\)/));
+ testString: assert(__helpers.removeWhiteSpace(code).match(/console\.log\(output\)/));
```
@@ -53,8 +53,6 @@ let output = "Get this to log once in the freeCodeCamp console and twice in the
-
-
## Solution
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/complete-a-promise-with-resolve-and-reject.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/complete-a-promise-with-resolve-and-reject.english.md
index 718d69f00f..b67a808b1e 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/complete-a-promise-with-resolve-and-reject.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/complete-a-promise-with-resolve-and-reject.english.md
@@ -33,9 +33,10 @@ Make the promise handle success and failure. If responseFromServer
```yml
tests:
- text: resolve
should be called with the expected string when the if
condition is true
.
- testString: assert(removeJSComments(code).match(/if\s*\(\s*responseFromServer\s*\)\s*{\s*resolve\s*\(\s*('|"|`)We got the data\1\s*\)(\s*|\s*;\s*)}/g));
+ testString: assert(__helpers.removeJSComments(code).match(/if\s*\(\s*responseFromServer\s*\)\s*{\s*resolve\s*\(\s*('|"|`)We got the data\1\s*\)(\s*|\s*;\s*)}/g));
- text: reject
should be called with the expected string when the if
condition is false
.
- testString: assert(removeJSComments(code).match(/}\s*else\s*{\s*reject\s*\(\s*('|"|`)Data not received\1\s*\)(\s*|\s*;\s*)}/g));
+ testString: assert(__helpers.removeJSComments(code).match(/}\s*else\s*{\s*reject\s*\(\s*('|"|`)Data not received\1\s*\)(\s*|\s*;\s*)}/g));
+
```
@@ -59,14 +60,6 @@ const makeServerRequest = new Promise((resolve, reject) => {
-### After Test
-then
method to your promise. Use result
as the
```yml
tests:
- text: You should call the then
method on the promise.
- testString: assert(codeWithoutSpaces.match(/(makeServerRequest|\))\.then\(/g));
+ testString: assert(__helpers.removeWhiteSpace(code).match(/(makeServerRequest|\))\.then\(/g));
- text: Your then
method should have a callback function with result
as its parameter.
testString: assert(resultIsParameter);
- text: You should log result
to the console.
- testString: assert(resultIsParameter && codeWithoutSpaces.match(/\.then\(.*?result.*?console.log\(result\).*?\)/));
+ testString: assert(resultIsParameter && __helpers.removeWhiteSpace(code).match(/\.then\(.*?result.*?console.log\(result\).*?\)/));
```
@@ -61,8 +61,7 @@ const makeServerRequest = new Promise((resolve, reject) => {
catch
method to your promise. Use error
as the
```yml
tests:
- text: You should call the catch
method on the promise.
- testString: assert(codeWithoutSpaces.match(/(makeServerRequest|\))\.catch\(/g));
+ testString: assert(__helpers.removeWhiteSpace(code).match(/(makeServerRequest|\))\.catch\(/g));
- text: Your catch
method should have a callback function with error
as its parameter.
testString: assert(errorIsParameter);
- text: You should log error
to the console.
- testString: assert(errorIsParameter && codeWithoutSpaces.match(/\.catch\(.*?error.*?console.log\(error\).*?\)/));
+ testString: assert(errorIsParameter && __helpers.removeWhiteSpace(code).match(/\.catch\(.*?error.*?console.log\(error\).*?\)/));
```
@@ -67,8 +67,7 @@ makeServerRequest.then(result => {
today
variable.
- testString: assert(removeJSComments(code).match(/(var|let|const)\s*{\s*(today[^}]*|[^,]*,\s*today)\s*}\s*=\s*HIGH_TEMPERATURES(;|\s+|\/\/)/g));
+ testString: assert(__helpers.removeJSComments(code).match(/(var|let|const)\s*{\s*(today[^}]*|[^,]*,\s*today)\s*}\s*=\s*HIGH_TEMPERATURES(;|\s+|\/\/)/g));
- text: You should use destructuring to create the tomorrow
variable.
- testString: assert(removeJSComments(code).match(/(var|let|const)\s*{\s*(tomorrow[^}]*|[^,]*,\s*tomorrow)\s*}\s*=\s*HIGH_TEMPERATURES(;|\s+|\/\/)/g));
+ testString: assert(__helpers.removeJSComments(code).match(/(var|let|const)\s*{\s*(tomorrow[^}]*|[^,]*,\s*tomorrow)\s*}\s*=\s*HIGH_TEMPERATURES(;|\s+|\/\/)/g));
- text: today
should be equal to 77
and tomorrow
should be equal to 80
.
testString: assert(today === 77 && tomorrow === 80);
@@ -74,15 +74,6 @@ const tomorrow = HIGH_TEMPERATURES.tomorrow;
-### After Test
-half(stats)
should be 28.015
testString: assert(half(stats) === 28.015);
- text: Destructuring should be used.
- testString: assert(code.replace(/\s/g, '').match(/half=\({\w+,\w+}\)/));
+ testString: assert(__helpers.removeWhiteSpace(code).match(/half=\({\w+,\w+}\)/));
- text: Destructured parameter should be used.
testString: assert(!code.match(/stats\.max|stats\.min/));
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/use-destructuring-assignment-with-the-rest-parameter-to-reassign-array-elements.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/use-destructuring-assignment-with-the-rest-parameter-to-reassign-array-elements.english.md
index 68e6b3335a..6d891165e6 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/use-destructuring-assignment-with-the-rest-parameter-to-reassign-array-elements.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/use-destructuring-assignment-with-the-rest-parameter-to-reassign-array-elements.english.md
@@ -37,7 +37,7 @@ tests:
- text: Array.slice()
should not be used.
testString: getUserInput => assert(!getUserInput('index').match(/slice/g));
- text: Destructuring on list
should be used.
- testString: assert(code.replace(/\s/g, '').match(/\[(([_$a-z]\w*)?,){1,}\.\.\.arr\]=list/i));
+ testString: assert(__helpers.removeWhiteSpace(code).match(/\[(([_$a-z]\w*)?,){1,}\.\.\.arr\]=list/i));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/use-the-rest-parameter-with-function-parameters.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/use-the-rest-parameter-with-function-parameters.english.md
index 5d003a699a..1dc09b8e9c 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/use-the-rest-parameter-with-function-parameters.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/use-the-rest-parameter-with-function-parameters.english.md
@@ -40,7 +40,7 @@ tests:
- text: The result of sum()
should be 0
testString: assert(sum() === 0);
- text: The sum
function should use the ...
rest parameter on the args
parameter.
- testString: assert(code.replace(/\s/g,'').match(/sum=\(\.\.\.args\)=>/));
+ testString: assert(__helpers.removeWhiteSpace(code).match(/sum=\(\.\.\.args\)=>/));
```
diff --git a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/write-concise-declarative-functions-with-es6.english.md b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/write-concise-declarative-functions-with-es6.english.md
index ea73909047..d7bda4a98d 100644
--- a/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/write-concise-declarative-functions-with-es6.english.md
+++ b/curriculum/challenges/english/02-javascript-algorithms-and-data-structures/es6/write-concise-declarative-functions-with-es6.english.md
@@ -42,7 +42,7 @@ Refactor the function setGear
inside the object bicycle
assert(!removeJSComments(code).match(/function/));
+ testString: getUserInput => assert(!__helpers.removeJSComments(code).match(/function/));
- text: setGear
should be a declarative function.
testString: assert(typeof bicycle.setGear === 'function' && code.match(/setGear\s*\(.+\)\s*\{/));
- text: bicycle.setGear(48)
should change the gear
value to 48.
@@ -72,15 +72,6 @@ console.log(bicycle.gear);
-### After Test
-squareList
should be a function
.
testString: assert.typeOf(squareList, 'function'), 'squareList
should be a function
';
- text: for or while loops or forEach should not be used.
- testString: assert(!removeJSComments(code).match(/for|while|forEach/g));
+ testString: assert(!__helpers.removeJSComments(code).match(/for|while|forEach/g));
- text: map
, filter
, or reduce
should be used.
- testString: assert(removeJSComments(code).match(/\.(map|filter|reduce)\s*\(/g));
+ testString: assert(__helpers.removeJSComments(code).match(/\.(map|filter|reduce)\s*\(/g));
- text: The function should return an array
.
testString: assert(Array.isArray(squareList([4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2])));
- text: squareList([4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2])
should return [16, 1764, 36]
.
@@ -55,14 +55,6 @@ console.log(squaredIntegers);
-watchList
variable should not change.
testString: assert(watchList[0].Title === "Inception" && watchList[4].Director == "James Cameron");
- text: Your code should not use a for
loop.
- testString: assert(!removeJSComments(code).match(/for\s*?\([\s\S]*?\)/));
+ testString: assert(!__helpers.removeJSComments(code).match(/for\s*?\([\s\S]*?\)/));
- text: Your code should use the map
method.
testString: assert(code.match(/\.map/g));
- text: ratings
should equal [{"title":"Inception","rating":"8.8"},{"title":"Interstellar","rating":"8.6"},{"title":"The Dark Knight","rating":"9.0"},{"title":"Batman Begins","rating":"8.3"},{"title":"Avatar","rating":"7.9"}]
.
@@ -185,15 +185,6 @@ console.log(JSON.stringify(ratings));
-### After Test
-DisplayMessages
component should render an empty div
element.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); return mockedComponent.find('div').text() === '' })());
- text: The DisplayMessages
constructor should be called properly with super
, passing in props
.
- testString: getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/\s/g,''); return noWhiteSpace.includes('constructor(props)') && noWhiteSpace.includes('super(props'); })());
+ testString: getUserInput => assert((function() { const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index')); return noWhiteSpace.includes('constructor(props)') && noWhiteSpace.includes('super(props'); })());
- text: 'The DisplayMessages
component should have an initial state equal to {input: "", messages: []}
.'
testString: "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const initialState = mockedComponent.state(); return typeof initialState === 'object' && initialState.input === '' && Array.isArray(initialState.messages) && initialState.messages.length === 0; })());"
diff --git a/curriculum/challenges/english/03-front-end-libraries/react-and-redux/use-provider-to-connect-redux-to-react.english.md b/curriculum/challenges/english/03-front-end-libraries/react-and-redux/use-provider-to-connect-redux-to-react.english.md
index 4a4590ff21..0ce519d4a1 100644
--- a/curriculum/challenges/english/03-front-end-libraries/react-and-redux/use-provider-to-connect-redux-to-react.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/react-and-redux/use-provider-to-connect-redux-to-react.english.md
@@ -33,7 +33,7 @@ tests:
- text: The AppWrapper
should render.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })());
- text: The Provider
wrapper component should have a prop of store
passed to it, equal to the Redux store.
- testString: getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return getUserInput('index').replace(/\s/g,'').includes('DisplayMessages
should render as a child of AppWrapper
.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1; })());
- text: The DisplayMessages
component should render an h2, input, button, and ul
element.
diff --git a/curriculum/challenges/english/03-front-end-libraries/react/pass-props-to-a-stateless-functional-component.english.md b/curriculum/challenges/english/03-front-end-libraries/react/pass-props-to-a-stateless-functional-component.english.md
index 230e78c457..9d3537e7e8 100644
--- a/curriculum/challenges/english/03-front-end-libraries/react/pass-props-to-a-stateless-functional-component.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/react/pass-props-to-a-stateless-functional-component.english.md
@@ -44,7 +44,7 @@ tests:
- text: The date
prop of the CurrentDate
should contain a string of text.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); const prop = mockedComponent.children().childAt(1).props().date; return( typeof prop === 'string' && prop.length > 0 ); })());
- text: The date
prop should be generated by calling Date()
- testString: assert(/CurrentDate
component should render the value from the date
prop in the p
tag.
testString: let date = "dummy date"; assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CurrentDate, {date})); return mockedComponent.find('p').html().includes(date); })());
diff --git a/curriculum/challenges/english/03-front-end-libraries/react/review-using-props-with-stateless-functional-components.english.md b/curriculum/challenges/english/03-front-end-libraries/react/review-using-props-with-stateless-functional-components.english.md
index b92f45d6af..b9eded62bf 100644
--- a/curriculum/challenges/english/03-front-end-libraries/react/review-using-props-with-stateless-functional-components.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/react/review-using-props-with-stateless-functional-components.english.md
@@ -28,9 +28,9 @@ tests:
- text: The Camper
component should render.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('Camper').length === 1; })());
- text: The Camper
component should include default props which assign the string CamperBot
to the key name
.
- testString: assert(/Camper.defaultProps={name:(['"`])CamperBot\1,?}/.test(code.replace(/\s/g, '')));
+ testString: assert(/Camper.defaultProps={name:(['"`])CamperBot\1,?}/.test(__helpers.removeWhiteSpace(code)));
- text: The Camper
component should include prop types which require the name
prop to be of type string
.
- testString: assert(/Camper.propTypes={name:PropTypes.string.isRequired,?}/.test(code.replace(/\s/g, '')));
+ testString: assert(/Camper.propTypes={name:PropTypes.string.isRequired,?}/.test(__helpers.removeWhiteSpace(code)));
- text: The Camper
component should contain a p
element with only the text from the name
prop.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('p').text() === mockedComponent.find('Camper').props().name; })());
diff --git a/curriculum/challenges/english/03-front-end-libraries/react/use-proptypes-to-define-the-props-you-expect.english.md b/curriculum/challenges/english/03-front-end-libraries/react/use-proptypes-to-define-the-props-you-expect.english.md
index be1486971f..8d5f038fb1 100644
--- a/curriculum/challenges/english/03-front-end-libraries/react/use-proptypes-to-define-the-props-you-expect.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/react/use-proptypes-to-define-the-props-you-expect.english.md
@@ -33,7 +33,7 @@ tests:
- text: The Items
component should render.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('Items').length === 1; })());
- text: The Items
component should include a propTypes
check that requires quantity
to be a number
.
- testString: getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/ /g, ''); return noWhiteSpace.includes('quantity:PropTypes.number.isRequired') && noWhiteSpace.includes('Items.propTypes='); })());
+ testString: getUserInput => assert((function() { const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index')); return noWhiteSpace.includes('quantity:PropTypes.number.isRequired') && noWhiteSpace.includes('Items.propTypes='); })());
```
diff --git a/curriculum/challenges/english/03-front-end-libraries/react/use-state-to-toggle-an-element.english.md b/curriculum/challenges/english/03-front-end-libraries/react/use-state-to-toggle-an-element.english.md
index 9eb51983d5..468ffb4586 100644
--- a/curriculum/challenges/english/03-front-end-libraries/react/use-state-to-toggle-an-element.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/react/use-state-to-toggle-an-element.english.md
@@ -77,7 +77,7 @@ tests:
assert(!firstValue && secondValue && !thirdValue);
})();"
- text: An anonymous function should be passed to setState
.
- testString: const paramRegex = '[a-zA-Z$_]\\w*(,[a-zA-Z$_]\\w*)?'; const noSpaces = code.replace(/\s/g, ''); assert(new RegExp('this\\.setState\\((function\\(' + paramRegex + '\\){|([a-zA-Z$_]\\w*|\\(' + paramRegex + '\\))=>)').test(noSpaces));
+ testString: const paramRegex = '[a-zA-Z$_]\\w*(,[a-zA-Z$_]\\w*)?'; assert(new RegExp('this\\.setState\\((function\\(' + paramRegex + '\\){|([a-zA-Z$_]\\w*|\\(' + paramRegex + '\\))=>)').test(__helpers.removeWhiteSpace(code)));
- text: this
should not be used inside setState
testString: assert(!/this\.setState\([^}]*this/.test(code));
```
diff --git a/curriculum/challenges/english/03-front-end-libraries/react/write-a-react-component-from-scratch.english.md b/curriculum/challenges/english/03-front-end-libraries/react/write-a-react-component-from-scratch.english.md
index 4b77cdf6a9..c40c423d7a 100644
--- a/curriculum/challenges/english/03-front-end-libraries/react/write-a-react-component-from-scratch.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/react/write-a-react-component-from-scratch.english.md
@@ -23,7 +23,7 @@ Render this component to the DOM using ReactDOM.render()
. There is
```yml
tests:
- text: There should be a React component called MyComponent
.
- testString: getUserInput => assert(getUserInput('index').replace(/\s/g, '').includes('classMyComponentextendsReact.Component{'));
+ testString: getUserInput => assert(__helpers.removeWhiteSpace(getUserInput('index')).includes('classMyComponentextendsReact.Component{'));
- text: MyComponent
should contain an h1
tag with text My First React Component!
Case and punctuation matter.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('h1').text() === 'My First React Component!'; })());
- text: MyComponent
should render to the DOM.
diff --git a/curriculum/challenges/english/03-front-end-libraries/redux/combine-multiple-reducers.english.md b/curriculum/challenges/english/03-front-end-libraries/redux/combine-multiple-reducers.english.md
index 3a57e2c57c..724fc7425b 100644
--- a/curriculum/challenges/english/03-front-end-libraries/redux/combine-multiple-reducers.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/redux/combine-multiple-reducers.english.md
@@ -39,7 +39,7 @@ tests:
- text: 'The store state
should have two keys: count
, which holds a number, and auth
, which holds an object. The auth
object should have a property of authenticated
, which holds a boolean.'
testString: "assert((function() { const state = store.getState(); return typeof state.auth === 'object' && typeof state.auth.authenticated === 'boolean' && typeof state.count === 'number' })());"
- text: The rootReducer
should be a function that combines the counterReducer
and the authReducer
.
- testString: getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/\s/g,''); return typeof rootReducer === 'function' && noWhiteSpace.includes('Redux.combineReducers') })());
+ testString: getUserInput => assert((function() { const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index')); return typeof rootReducer === 'function' && noWhiteSpace.includes('Redux.combineReducers') })());
```
diff --git a/curriculum/challenges/english/03-front-end-libraries/redux/use-const-for-action-types.english.md b/curriculum/challenges/english/03-front-end-libraries/redux/use-const-for-action-types.english.md
index cdd2afee83..a75df764b9 100644
--- a/curriculum/challenges/english/03-front-end-libraries/redux/use-const-for-action-types.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/redux/use-const-for-action-types.english.md
@@ -35,9 +35,9 @@ tests:
- text: The authReducer
function should handle multiple action types with a switch statement.
testString: getUserInput => assert((function() { return typeof authReducer === 'function' && getUserInput('index').toString().includes('switch') && getUserInput('index').toString().includes('case') && getUserInput('index').toString().includes('default') })());
- text: LOGIN
and LOGOUT
should be declared as const
values and should be assigned strings of LOGIN
and LOGOUT
.
- testString: const noWhiteSpace = code.replace(/\s/g, ''); assert(/constLOGIN=(['"`])LOGIN\1/.test(noWhiteSpace) && /constLOGOUT=(['"`])LOGOUT\1/.test(noWhiteSpace));
+ testString: const noWhiteSpace = __helpers.removeWhiteSpace(code); assert(/constLOGIN=(['"`])LOGIN\1/.test(noWhiteSpace) && /constLOGOUT=(['"`])LOGOUT\1/.test(noWhiteSpace));
- text: The action creators and the reducer should reference the LOGIN
and LOGOUT
constants.
- testString: getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/\s/g,''); return noWhiteSpace.includes('caseLOGIN:') && noWhiteSpace.includes('caseLOGOUT:') && noWhiteSpace.includes('type:LOGIN') && noWhiteSpace.includes('type:LOGOUT') })());
+ testString: getUserInput => assert((function() { const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index').toString()); return noWhiteSpace.includes('caseLOGIN:') && noWhiteSpace.includes('caseLOGOUT:') && noWhiteSpace.includes('type:LOGIN') && noWhiteSpace.includes('type:LOGOUT') })());
```
diff --git a/curriculum/challenges/english/03-front-end-libraries/redux/use-middleware-to-handle-asynchronous-actions.english.md b/curriculum/challenges/english/03-front-end-libraries/redux/use-middleware-to-handle-asynchronous-actions.english.md
index 578e585214..6ee818aace 100644
--- a/curriculum/challenges/english/03-front-end-libraries/redux/use-middleware-to-handle-asynchronous-actions.english.md
+++ b/curriculum/challenges/english/03-front-end-libraries/redux/use-middleware-to-handle-asynchronous-actions.english.md
@@ -33,7 +33,7 @@ tests:
- text: Dispatching the requestingData action creator should update the store state
property of fetching to true
.
testString: assert((function() { const initialState = store.getState(); store.dispatch(requestingData()); const reqState = store.getState(); return initialState.fetching === false && reqState.fetching === true })());
- text: Dispatching handleAsync
should dispatch the data request action and then dispatch the received data action after a delay.
- testString: assert((function() { const noWhiteSpace = handleAsync.toString().replace(/\s/g,''); return noWhiteSpace.includes('dispatch(requestingData())') === true && noWhiteSpace.includes('dispatch(receivedData(data))') === true })());
+ testString: assert((function() { const noWhiteSpace = __helpers.removeWhiteSpace(handleAsync.toString()); return noWhiteSpace.includes('dispatch(requestingData())') === true && noWhiteSpace.includes('dispatch(receivedData(data))') === true })());
```
diff --git a/curriculum/challenges/english/10-coding-interview-prep/project-euler/problem-43-sub-string-divisibility.english.md b/curriculum/challenges/english/10-coding-interview-prep/project-euler/problem-43-sub-string-divisibility.english.md
index 522356ce70..bed55bd073 100644
--- a/curriculum/challenges/english/10-coding-interview-prep/project-euler/problem-43-sub-string-divisibility.english.md
+++ b/curriculum/challenges/english/10-coding-interview-prep/project-euler/problem-43-sub-string-divisibility.english.md
@@ -41,7 +41,7 @@ tests:
- text: substringDivisibility()
should return [ 1430952867, 1460357289, 1406357289, 4130952867, 4160357289, 4106357289 ].
testString: assert.sameMembers(substringDivisibility(), [ 1430952867, 1460357289, 1406357289, 4130952867, 4160357289, 4106357289 ]);
- text: You should not copy and return the array.
- testString: assert(!removeJSComments(code).match(/(1430952867)|(1460357289)|(1406357289)|(4130952867)|(4160357289)|(4106357289)/))
+ testString: assert(!__helpers.removeJSComments(code).match(/(1430952867)|(1460357289)|(1406357289)|(4130952867)|(4160357289)|(4106357289)/))
```
@@ -63,15 +63,6 @@ substringDivisibility();
-### After Test
-