feat(client, learn): add helper functions for common validation operations (#38605)

Co-authored-by: Randell Dawson <5313213+RandellDawson@users.noreply.github.com>
This commit is contained in:
Hassaan Pasha 2020-09-17 19:08:01 +05:00 committed by GitHub
parent aecbc28798
commit 80438cac3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 326 additions and 157 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -0,0 +1,30 @@
const htmlFullExample = `
<!--
multi line html comment
-->
not a comment
not a commment <!-- single line html comment --> not a comment
not a comment
<!-- this is my blog: <mynixworld.inf> -->
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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,35 @@
import { parse } from '@babel/parser';
import generate from '@babel/generator';
const removeHtmlComments = str => str.replace(/<!--(.|\s)*?-->/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
};

View File

@ -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(
'<h1>hello world</h1><!-- a comment--><h2>h2 element</h2>'
)
).toBe('string');
});
it('returns an HTML string with no single or multi-line comments', () => {
expect(removeHtmlComments(htmlFullExample)).toBe(
htmlCodeWithCommentsRemoved
);
});
});

View File

@ -23,9 +23,9 @@ For the elements with id of <code>ball1</code> and <code>ball2</code>, add an <c
```yml
tests:
- text: The value of the <code>animation-timing-function</code> property for the element with the id <code>ball1</code> 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 <code>animation-timing-function</code> property for the element with the id <code>ball2</code> 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)');
```

View File

@ -28,7 +28,7 @@ tests:
- text: The value of the <code>animation-timing-function</code> property for the element with the id <code>ball1</code> 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 <code>animation-timing-function</code> property for the element with the id <code>ball2</code> 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)');
```

View File

@ -29,7 +29,7 @@ tests:
- text: The element with the id <code>red</code> should no longer have the <code>animation-timing-function</code> property of linear.
testString: assert($('#red').css('animation-timing-function') !== 'linear');
- text: The value of the <code>animation-timing-function</code> property for the element with the id <code>blue</code> 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)');
```

View File

@ -33,7 +33,7 @@ tests:
- text: Your <code>blue-box</code> class should give the left of elements <code>40px</code> of <code>margin</code>.
testString: assert($(".blue-box").css("margin-left") === "40px");
- text: You should use the clockwise notation to set the margin of <code>blue-box</code> 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())));
```

View File

@ -32,7 +32,7 @@ tests:
- text: Your <code>blue-box</code> class should give the left of elements <code>40px</code> of <code>padding</code>.
testString: assert($(".blue-box").css("padding-left") === "40px");
- text: You should use the clockwise notation to set the padding of <code>blue-box</code> 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())));
```

View File

@ -38,7 +38,7 @@ tests:
- text: Your image should have a <code>src</code> 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 <code>alt</code> attribute should not be empty.
testString: assert($("img").attr("alt") && $("img").attr("alt").length && /<img\S*alt=(['"])(?!\1|>)\S+\1\S*\/?>/.test(code.replace(/\s/g,'')));
testString: assert($("img").attr("alt") && $("img").attr("alt").length && /<img\S*alt=(['"])(?!\1|>)\S+\1\S*\/?>/.test(__helpers.removeWhiteSpace(code)));
```

View File

@ -52,9 +52,9 @@ tests:
- text: Your <code>li</code> element should have a closing tag.
testString: assert(code.match(/<\/li>/g) && code.match(/<li>/g) && code.match(/<\/li>/g).length === code.match(/<li>/g).length);
- text: The <code>li</code> 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 <code>li</code> 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)));
```

View File

@ -33,7 +33,7 @@ Place the area template so that the cell labeled <code>advert</code> becomes an
```yml
tests:
- text: <code>container</code> class should have a <code>grid-template-areas</code> property similar to the preview but has <code>.</code> instead of the <code>advert</code> 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));
```

View File

@ -30,7 +30,7 @@ Place an element with the <code>item5</code> class in the <code>footer</code> ar
```yml
tests:
- text: <code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>footer</code>.
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));
```

View File

@ -33,7 +33,7 @@ Make the item with the class <code>item5</code> consume the last two columns of
```yml
tests:
- text: <code>item5</code> class should have a <code>grid-column</code> 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: <code>item5</code> class should have a <code>grid-column</code> property which results in it consuming the last two columns of the grid.
testString: "
const colStart = getComputedStyle($('.item5')[0]).gridColumnStart;

View File

@ -22,7 +22,7 @@ Make the element with the <code>item5</code> class consume the last two rows.
```yml
tests:
- text: <code>item5</code> class should have a <code>grid-row</code> 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: <code>item5</code> class should have a <code>grid-row</code> property which results in it consuming the last two rows of the grid.
testString: "
const rowStart = getComputedStyle($('.item5')[0]).gridRowStart;

View File

@ -23,7 +23,7 @@ When the viewport width is <code>400px</code> or more, make the header area occu
```yml
tests:
- text: When the viewport is <code>400px</code> or more, <code>container</code> class should have a <code>grid-template-areas</code> 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));
```

View File

@ -38,7 +38,7 @@ tests:
- text: <code>copyMachine(["it works"], 3)</code> should return <code>[["it works"], ["it works"], ["it works"]]</code>
testString: assert.deepEqual(copyMachine(['it works'], 3), [['it works'], ['it works'], ['it works']]);
- text: The <code>copyMachine</code> function should utilize the <code>spread operator</code> with array <code>arr</code>
testString: assert(removeJSComments(code).match(/\.\.\.arr/));
testString: assert(__helpers.removeJSComments(code).match(/\.\.\.arr/));
```
@ -66,15 +66,6 @@ console.log(copyMachine([true, false, true], 2));
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -42,13 +42,13 @@ We've initialized an array `arr`. Use `splice()` to remove elements from `arr`,
```yml
tests:
- text: You should not change the original line of <code>const arr = [2, 4, 5, 1, 7, 5, 2, 1];</code>.
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: <code>arr</code> should only contain elements that sum to <code>10</code>.
testString: assert.strictEqual(arr.reduce((a, b) => a + b), 10);
- text: Your code should utilize the <code>splice()</code> method on <code>arr</code>.
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 <code>arr</code> and not add any additional elements to <code>arr</code>.
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));
```
</section>

View File

@ -39,7 +39,7 @@ tests:
- text: <code>myData</code> should be equal to <code>8</code>.
testString: assert(myData === 8);
- text: You should be using bracket notation to read the correct value from <code>myArray</code>.
testString: assert(/myData=myArray\[2\]\[1\]/.test(code.replace(/\s/g, '')));
testString: assert(/myData=myArray\[2\]\[1\]/.test(__helpers.removeWhiteSpace(code)));
```

View File

@ -37,7 +37,7 @@ tests:
- text: You should use a <code>for</code> loop to iterate through <code>myArr</code>.
testString: assert(/for\s*\(/g.test(code) && /myArr\s*\[/g.test(code));
- text: You should not attempt to directly assign the value 20 to <code>total</code>.
testString: assert(!code.replace(/\s/g, '').match(/total[=+-]0*[1-9]+/gm));
testString: assert(!__helpers.removeWhiteSpace(code).match(/total[=+-]0*[1-9]+/gm));
```
</section>

View File

@ -43,7 +43,7 @@ tests:
}
assert.throws(declared, ReferenceError);
- text: You should add a local <code>myVar</code> 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)));
```

View File

@ -57,9 +57,9 @@ tests:
- text: <code>sum([2, 3, 4, 5], 3)</code> should equal 9.
testString: assert.equal(sum([2, 3, 4, 5], 3), 9);
- text: Your code should not rely on any kind of loops (<code>for</code> or <code>while</code> or higher order functions such as <code>forEach</code>, <code>map</code>, <code>filter</code>, or <code>reduce</code>.).
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);
```
</section>
@ -80,16 +80,6 @@ function sum(arr, n) {
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -33,7 +33,7 @@ tests:
- text: The variable <code>difference</code> 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)));
```
</section>

View File

@ -39,7 +39,7 @@ tests:
- text: Returned value from <code>addFive</code> should be <code>undefined</code>.
testString: assert(addFive() === undefined);
- text: Inside the <code>addFive</code> function, you should add <code>5</code> to the <code>sum</code> 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/));
```

View File

@ -52,9 +52,9 @@ tests:
- text: <code>countdown(5)</code> should return <code>[5, 4, 3, 2, 1]</code>
testString: assert.deepStrictEqual(countdown(5), [5, 4, 3, 2, 1]);
- text: Your code should not rely on any kind of loops (<code>for</code>, <code>while</code> or higher order functions such as <code>forEach</code>, <code>map</code>, <code>filter</code>, and <code>reduce</code>).
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*\(.+\)/));
```
</section>
@ -76,15 +76,6 @@ function countdown(n){
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -25,9 +25,9 @@ tests:
- text: Your function should return an array.
testString: assert(Array.isArray(rangeOfNumbers(5, 10)));
- text: Your code should not use any loop syntax (<code>for</code> or <code>while</code> or higher order functions such as <code>forEach</code>, <code>map</code>, <code>filter</code>, or <code>reduce</code>).
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: <code>rangeOfNumbers</code> 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: <code>rangeOfNumbers(1, 5)</code> should return <code>[1, 2, 3, 4, 5]</code>.
testString: assert.deepStrictEqual(rangeOfNumbers(1, 5), [1, 2, 3, 4, 5]);
- text: <code>rangeOfNumbers(6, 9)</code> should return <code>[6, 7, 8, 9]</code>.
@ -51,15 +51,6 @@ function rangeOfNumbers(startNum, endNum) {
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -28,9 +28,9 @@ First, use <code>console.log</code> to log the <code>output</code> variable. The
```yml
tests:
- text: You should use <code>console.clear()</code> 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 <code>console.log()</code> to print the <code>output</code> 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
</div>
</section>
## Solution

View File

@ -33,9 +33,10 @@ Make the promise handle success and failure. If <code>responseFromServer</code>
```yml
tests:
- text: <code>resolve</code> should be called with the expected string when the <code>if</code> condition is <code>true</code>.
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: <code>reject</code> should be called with the expected string when the <code>if</code> condition is <code>false</code>.
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));
```
</section>
@ -59,14 +60,6 @@ const makeServerRequest = new Promise((resolve, reject) => {
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -29,11 +29,11 @@ Add the <code>then</code> method to your promise. Use <code>result</code> as the
```yml
tests:
- text: You should call the <code>then</code> method on the promise.
testString: assert(codeWithoutSpaces.match(/(makeServerRequest|\))\.then\(/g));
testString: assert(__helpers.removeWhiteSpace(code).match(/(makeServerRequest|\))\.then\(/g));
- text: Your <code>then</code> method should have a callback function with <code>result</code> as its parameter.
testString: assert(resultIsParameter);
- text: You should log <code>result</code> 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\).*?\)/));
```
</section>
@ -61,8 +61,7 @@ const makeServerRequest = new Promise((resolve, reject) => {
<div id='js-teardown'>
```js
const codeWithoutSpaces = code.replace(/\s/g, '');
const resultIsParameter = /\.then\((function\(result\){|result|\(result\)=>)/.test(codeWithoutSpaces);
const resultIsParameter = /\.then\((function\(result\){|result|\(result\)=>)/.test(__helpers.removeWhiteSpace(code));
```
</div>

View File

@ -31,11 +31,11 @@ Add the <code>catch</code> method to your promise. Use <code>error</code> as the
```yml
tests:
- text: You should call the <code>catch</code> method on the promise.
testString: assert(codeWithoutSpaces.match(/(makeServerRequest|\))\.catch\(/g));
testString: assert(__helpers.removeWhiteSpace(code).match(/(makeServerRequest|\))\.catch\(/g));
- text: Your <code>catch</code> method should have a callback function with <code>error</code> as its parameter.
testString: assert(errorIsParameter);
- text: You should log <code>error</code> 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\).*?\)/));
```
</section>
@ -67,8 +67,7 @@ makeServerRequest.then(result => {
<div id='js-teardown'>
```js
const codeWithoutSpaces = code.replace(/\s/g, '');
const errorIsParameter = /\.catch\((function\(error\){|error|\(error\)=>)/.test(codeWithoutSpaces);
const errorIsParameter = /\.catch\((function\(error\){|error|\(error\)=>)/.test(__helpers.removeWhiteSpace(code));
```
</div>

View File

@ -41,11 +41,11 @@ Replace the two assignments with an equivalent destructuring assignment. It shou
```yml
tests:
- text: You should remove the ES5 assignment syntax.
testString: assert(!removeJSComments(code).match(/today\s*=\s*HIGH_TEMPERATURES\.(today|tomorrow)/g))
testString: assert(!__helpers.removeJSComments(code).match(/today\s*=\s*HIGH_TEMPERATURES\.(today|tomorrow)/g))
- text: You should use destructuring to create the <code>today</code> 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 <code>tomorrow</code> 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: <code>today</code> should be equal to <code>77</code> and <code>tomorrow</code> should be equal to <code>80</code>.
testString: assert(today === 77 && tomorrow === 80);
@ -74,15 +74,6 @@ const tomorrow = HIGH_TEMPERATURES.tomorrow;
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -43,7 +43,7 @@ tests:
- text: <code>half(stats)</code> should be <code>28.015</code>
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/));

View File

@ -37,7 +37,7 @@ tests:
- text: <code>Array.slice()</code> should not be used.
testString: getUserInput => assert(!getUserInput('index').match(/slice/g));
- text: Destructuring on <code>list</code> 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));
```

View File

@ -40,7 +40,7 @@ tests:
- text: The result of <code>sum()</code> should be 0
testString: assert(sum() === 0);
- text: The <code>sum</code> function should use the <code>...</code> rest parameter on the <code>args</code> parameter.
testString: assert(code.replace(/\s/g,'').match(/sum=\(\.\.\.args\)=>/));
testString: assert(__helpers.removeWhiteSpace(code).match(/sum=\(\.\.\.args\)=>/));
```

View File

@ -42,7 +42,7 @@ Refactor the function <code>setGear</code> inside the object <code>bicycle</code
```yml
tests:
- text: Traditional function expression should not be used.
testString: getUserInput => assert(!removeJSComments(code).match(/function/));
testString: getUserInput => assert(!__helpers.removeJSComments(code).match(/function/));
- text: <code>setGear</code> should be a declarative function.
testString: assert(typeof bicycle.setGear === 'function' && code.match(/setGear\s*\(.+\)\s*\{/));
- text: <code>bicycle.setGear(48)</code> should change the <code>gear</code> value to 48.
@ -72,15 +72,6 @@ console.log(bicycle.gear);
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -24,9 +24,9 @@ tests:
- text: <code>squareList</code> should be a <code>function</code>.
testString: assert.typeOf(squareList, 'function'), '<code>squareList</code> should be a <code>function</code>';
- 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: <code>map</code>, <code>filter</code>, or <code>reduce</code> 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 <code>array</code>.
testString: assert(Array.isArray(squareList([4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2])));
- text: <code>squareList([4, 5.6, -9.8, 3.14, 42, 6, 8.34, -2])</code> should return <code>[16, 1764, 36]</code>.
@ -55,14 +55,6 @@ console.log(squaredIntegers);
</div>
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -41,7 +41,7 @@ tests:
- text: The <code>watchList</code> variable should not change.
testString: assert(watchList[0].Title === "Inception" && watchList[4].Director == "James Cameron");
- text: Your code should not use a <code>for</code> 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 <code>map</code> method.
testString: assert(code.match(/\.map/g));
- text: <code>ratings</code> should equal <code>[{"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"}]</code>.
@ -185,15 +185,6 @@ console.log(JSON.stringify(ratings));
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -26,7 +26,7 @@ tests:
- text: The <code>DisplayMessages</code> component should render an empty <code>div</code> element.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); return mockedComponent.find('div').text() === '' })());
- text: The <code>DisplayMessages</code> constructor should be called properly with <code>super</code>, passing in <code>props</code>.
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 <code>DisplayMessages</code> component should have an initial state equal to <code>{input: "", messages: []}</code>.'
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; })());"

View File

@ -33,7 +33,7 @@ tests:
- text: The <code>AppWrapper</code> should render.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })());
- text: The <code>Provider</code> wrapper component should have a prop of <code>store</code> 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('<Providerstore={store}>'); })());
testString: getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return __helpers.removeWhiteSpace(getUserInput('index')).includes('<Providerstore={store}>'); })());
- text: <code>DisplayMessages</code> should render as a child of <code>AppWrapper</code>.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1; })());
- text: The <code>DisplayMessages</code> component should render an h2, input, button, and <code>ul</code> element.

View File

@ -44,7 +44,7 @@ tests:
- text: The <code>date</code> prop of the <code>CurrentDate</code> 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 <code>date</code> prop should be generated by calling <code>Date()</code>
testString: assert(/<CurrentDatedate={Date\(\)}\/>/.test(code.replace(/\s/g, '')))
testString: assert(/<CurrentDatedate={Date\(\)}\/>/.test(__helpers.removeWhiteSpace(code)))
- text: The <code>CurrentDate</code> component should render the value from the <code>date</code> prop in the <code>p</code> tag.
testString: let date = "dummy date"; assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CurrentDate, {date})); return mockedComponent.find('p').html().includes(date); })());

View File

@ -28,9 +28,9 @@ tests:
- text: The <code>Camper</code> component should render.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('Camper').length === 1; })());
- text: The <code>Camper</code> component should include default props which assign the string <code>CamperBot</code> to the key <code>name</code>.
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 <code>Camper</code> component should include prop types which require the <code>name</code> prop to be of type <code>string</code>.
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 <code>Camper</code> component should contain a <code>p</code> element with only the text from the <code>name</code> prop.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('p').text() === mockedComponent.find('Camper').props().name; })());

View File

@ -33,7 +33,7 @@ tests:
- text: The <code>Items</code> component should render.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('Items').length === 1; })());
- text: The <code>Items</code> component should include a <code>propTypes</code> check that requires <code>quantity</code> to be a <code>number</code>.
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='); })());
```

View File

@ -77,7 +77,7 @@ tests:
assert(!firstValue && secondValue && !thirdValue);
})();"
- text: An anonymous function should be passed to <code>setState</code>.
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: <code>this</code> should not be used inside <code>setState</code>
testString: assert(!/this\.setState\([^}]*this/.test(code));
```

View File

@ -23,7 +23,7 @@ Render this component to the DOM using <code>ReactDOM.render()</code>. There is
```yml
tests:
- text: There should be a React component called <code>MyComponent</code>.
testString: getUserInput => assert(getUserInput('index').replace(/\s/g, '').includes('classMyComponentextendsReact.Component{'));
testString: getUserInput => assert(__helpers.removeWhiteSpace(getUserInput('index')).includes('classMyComponentextendsReact.Component{'));
- text: <code>MyComponent</code> should contain an <code>h1</code> tag with text <code>My First React Component!</code> Case and punctuation matter.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('h1').text() === 'My First React Component!'; })());
- text: <code>MyComponent</code> should render to the DOM.

View File

@ -39,7 +39,7 @@ tests:
- text: 'The store <code>state</code> should have two keys: <code>count</code>, which holds a number, and <code>auth</code>, which holds an object. The <code>auth</code> object should have a property of <code>authenticated</code>, 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 <code>rootReducer</code> should be a function that combines the <code>counterReducer</code> and the <code>authReducer</code>.
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') })());
```

View File

@ -35,9 +35,9 @@ tests:
- text: The <code>authReducer</code> 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: <code>LOGIN</code> and <code>LOGOUT</code> should be declared as <code>const</code> values and should be assigned strings of <code>LOGIN</code>and <code>LOGOUT</code>.
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 <code>LOGIN</code> and <code>LOGOUT</code> 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') })());
```

View File

@ -33,7 +33,7 @@ tests:
- text: Dispatching the requestingData action creator should update the store <code>state</code> property of fetching to <code>true</code>.
testString: assert((function() { const initialState = store.getState(); store.dispatch(requestingData()); const reqState = store.getState(); return initialState.fetching === false && reqState.fetching === true })());
- text: Dispatching <code>handleAsync</code> 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 })());
```

View File

@ -41,7 +41,7 @@ tests:
- text: <code>substringDivisibility()</code> 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();
</div>
### After Test
<div id='js-teardown'>
```js
const removeJSComments = str => str.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '');
```
</div>
</section>
## Solution

View File

@ -77,7 +77,7 @@ function isValid(iban) {
MC:27, MR:27, SM:27, AL:28, AZ:28, CY:28, DO:28, GT:28,
HU:28, LB:28, PL:28, BR:29, PS:29, KW:30, MU:30, MT:31
}
iban = iban.replace(/\s/g, '')
iban = __helpers.removeWhiteSpace(iban)
if (!iban.match(/^[\dA-Z]+$/)) return false
var len = iban.length
if (len != ibanLen[iban.substr(0,2)]) return false

View File

@ -66,7 +66,7 @@ function solve(input) {
function solve(input) {
var orig = {};
input.forEach(function(e) {
orig[e.replace(/\s/g, '').toLowerCase()] = e;
orig[__helpers.removeWhiteSpace(e).toLowerCase()] = e;
});
input = Object.keys(orig);