fix: blockquote-formatting-in-challenges (#17590)

This commit is contained in:
Tom
2018-06-16 22:10:06 -05:00
committed by mrugesh mohapatra
parent fb3d904b64
commit d31e0a3a24
14 changed files with 5744 additions and 3205 deletions

View File

@ -6,7 +6,8 @@
"challenges": [ "challenges": [
{ {
"id": "587d774c367417b2b2512a9c", "id": "587d774c367417b2b2512a9c",
"title": "Add a Text Alternative to Images for Visually Impaired Accessibility", "title":
"Add a Text Alternative to Images for Visually Impaired Accessibility",
"description": [ "description": [
"It's likely you've seen an <code>alt</code> attribute on an <code>img</code> tag in other challenges. <code>Alt</code> text describes the content of the image and provides a text-alternative. This helps in case the image fails to load or can't be seen by a user. It's also used by search engines to understand what an image contains to include it in search results. Here's an example:", "It's likely you've seen an <code>alt</code> attribute on an <code>img</code> tag in other challenges. <code>Alt</code> text describes the content of the image and provides a text-alternative. This helps in case the image fails to load or can't be seen by a user. It's also used by search engines to understand what an image contains to include it in search results. Here's an example:",
"<code>&lt;img src=&quot;importantLogo.jpeg&quot; alt=&quot;Company logo&quot;&gt;</code>", "<code>&lt;img src=&quot;importantLogo.jpeg&quot; alt=&quot;Company logo&quot;&gt;</code>",
@ -17,8 +18,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your <code>img</code> tag should have an <code>alt</code> attribute, and it should not be empty.", "text":
"testString": "assert($('img').attr('alt'), 'Your <code>img</code> tag should have an <code>alt</code> attribute, and it should not be empty.');" "Your <code>img</code> tag should have an <code>alt</code> attribute, and it should not be empty.",
"testString":
"assert($('img').attr('alt'), 'Your <code>img</code> tag should have an <code>alt</code> attribute, and it should not be empty.');"
} }
], ],
"solutions": [], "solutions": [],
@ -26,15 +29,14 @@
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
"challengeType": 0, "challengeType": 0,
"translations": {}, "translations": {},
"guideUrl": "https://guide.freecodecamp.org/certificates/add-alt-text-to-an-image-for-accessibility", "guideUrl":
"https://guide.freecodecamp.org/certificates/add-alt-text-to-an-image-for-accessibility",
"files": { "files": {
"indexhtml": { "indexhtml": {
"key": "indexhtml", "key": "indexhtml",
"ext": "html", "ext": "html",
"name": "index", "name": "index",
"contents": [ "contents": ["<img src=\"doingKarateWow.jpeg\">"],
"<img src=\"doingKarateWow.jpeg\">"
],
"head": [], "head": [],
"tail": [] "tail": []
} }
@ -54,12 +56,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your <code>img</code> tag should have an <code>alt</code> attribute.", "text":
"testString": "assert(!($('img').attr('alt') == undefined), 'Your <code>img</code> tag should have an <code>alt</code> attribute.');" "Your <code>img</code> tag should have an <code>alt</code> attribute.",
"testString":
"assert(!($('img').attr('alt') == undefined), 'Your <code>img</code> tag should have an <code>alt</code> attribute.');"
}, },
{ {
"text": "The <code>alt</code> attribute should be set to an empty string.", "text":
"testString": "assert($('img').attr('alt') == '', 'The <code>alt</code> attribute should be set to an empty string.');" "The <code>alt</code> attribute should be set to an empty string.",
"testString":
"assert($('img').attr('alt') == '', 'The <code>alt</code> attribute should be set to an empty string.');"
} }
], ],
"solutions": [], "solutions": [],
@ -107,11 +113,13 @@
"tests": [ "tests": [
{ {
"text": "Your code should have six <code>h3</code> tags.", "text": "Your code should have six <code>h3</code> tags.",
"testString": "assert($('h3').length === 6, 'Your code should have six <code>h3</code> tags.');" "testString":
"assert($('h3').length === 6, 'Your code should have six <code>h3</code> tags.');"
}, },
{ {
"text": "Your code should not have any <code>h5</code> tags.", "text": "Your code should not have any <code>h5</code> tags.",
"testString": "assert($('h5').length === 0, 'Your code should not have any <code>h5</code> tags.');" "testString":
"assert($('h5').length === 0, 'Your code should not have any <code>h5</code> tags.');"
} }
], ],
"solutions": [], "solutions": [],
@ -161,11 +169,14 @@
"tests": [ "tests": [
{ {
"text": "Your code should have one <code>main</code> tag.", "text": "Your code should have one <code>main</code> tag.",
"testString": "assert($('main').length == 1, 'Your code should have one <code>main</code> tag.');" "testString":
"assert($('main').length == 1, 'Your code should have one <code>main</code> tag.');"
}, },
{ {
"text": "The <code>main</code> tags should be between the closing <code>header</code> tag and the opening <code>footer</code> tag.", "text":
"testString": "assert(code.match(/<\\/header>\\s*?<main>\\s*?<\\/main>/gi), 'The <code>main</code> tags should be between the closing <code>header</code> tag and the opening <code>footer</code> tag.');" "The <code>main</code> tags should be between the closing <code>header</code> tag and the opening <code>footer</code> tag.",
"testString":
"assert(code.match(/<\\/header>\\s*?<main>\\s*?<\\/main>/gi), 'The <code>main</code> tags should be between the closing <code>header</code> tag and the opening <code>footer</code> tag.');"
} }
], ],
"solutions": [], "solutions": [],
@ -207,11 +218,13 @@
"tests": [ "tests": [
{ {
"text": "Your code should have three <code>article</code> tags.", "text": "Your code should have three <code>article</code> tags.",
"testString": "assert($('article').length == 3, 'Your code should have three <code>article</code> tags.');" "testString":
"assert($('article').length == 3, 'Your code should have three <code>article</code> tags.');"
}, },
{ {
"text": "Your code should not have any <code>div</code> tags.", "text": "Your code should not have any <code>div</code> tags.",
"testString": "assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');" "testString":
"assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');"
} }
], ],
"solutions": [], "solutions": [],
@ -265,19 +278,25 @@
"tests": [ "tests": [
{ {
"text": "Your code should have one <code>header</code> tag.", "text": "Your code should have one <code>header</code> tag.",
"testString": "assert($('header').length == 1, 'Your code should have one <code>header</code> tag.');" "testString":
"assert($('header').length == 1, 'Your code should have one <code>header</code> tag.');"
}, },
{ {
"text": "Your <code>header</code> tags should wrap around the <code>h1</code>.", "text":
"testString": "assert($('header').children('h1').length == 1, 'Your <code>header</code> tags should wrap around the <code>h1</code>.');" "Your <code>header</code> tags should wrap around the <code>h1</code>.",
"testString":
"assert($('header').children('h1').length == 1, 'Your <code>header</code> tags should wrap around the <code>h1</code>.');"
}, },
{ {
"text": "Your code should not have any <code>div</code> tags.", "text": "Your code should not have any <code>div</code> tags.",
"testString": "assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');" "testString":
"assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');"
}, },
{ {
"text": "Make sure your <code>header</code> element has a closing tag.", "text":
"testString": "assert(code.match(/<\\/header>/g) && code.match(/<\\/header>/g).length === code.match(/<header>/g).length, 'Make sure your <code>header</code> element has a closing tag.');" "Make sure your <code>header</code> element has a closing tag.",
"testString":
"assert(code.match(/<\\/header>/g) && code.match(/<\\/header>/g).length === code.match(/<header>/g).length, 'Make sure your <code>header</code> element has a closing tag.');"
} }
], ],
"solutions": [], "solutions": [],
@ -334,19 +353,24 @@
"tests": [ "tests": [
{ {
"text": "Your code should have one <code>nav</code> tag.", "text": "Your code should have one <code>nav</code> tag.",
"testString": "assert($('nav').length == 1, 'Your code should have one <code>nav</code> tag.');" "testString":
"assert($('nav').length == 1, 'Your code should have one <code>nav</code> tag.');"
}, },
{ {
"text": "Your <code>nav</code> tags should wrap around the <code>ul</code> and its list items.", "text":
"testString": "assert($('nav').children('ul').length == 1, 'Your <code>nav</code> tags should wrap around the <code>ul</code> and its list items.');" "Your <code>nav</code> tags should wrap around the <code>ul</code> and its list items.",
"testString":
"assert($('nav').children('ul').length == 1, 'Your <code>nav</code> tags should wrap around the <code>ul</code> and its list items.');"
}, },
{ {
"text": "Your code should not have any <code>div</code> tags.", "text": "Your code should not have any <code>div</code> tags.",
"testString": "assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');" "testString":
"assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');"
}, },
{ {
"text": "Make sure your <code>nav</code> element has a closing tag.", "text": "Make sure your <code>nav</code> element has a closing tag.",
"testString": "assert(code.match(/<\\/nav>/g) && code.match(/<\\/nav>/g).length === code.match(/<nav>/g).length, 'Make sure your <code>nav</code> element has a closing tag.');" "testString":
"assert(code.match(/<\\/nav>/g) && code.match(/<\\/nav>/g).length === code.match(/<nav>/g).length, 'Make sure your <code>nav</code> element has a closing tag.');"
} }
], ],
"solutions": [], "solutions": [],
@ -408,15 +432,19 @@
"tests": [ "tests": [
{ {
"text": "Your code should have one <code>footer</code> tag.", "text": "Your code should have one <code>footer</code> tag.",
"testString": "assert($('footer').length == 1, 'Your code should have one <code>footer</code> tag.');" "testString":
"assert($('footer').length == 1, 'Your code should have one <code>footer</code> tag.');"
}, },
{ {
"text": "Your code should not have any <code>div</code> tags.", "text": "Your code should not have any <code>div</code> tags.",
"testString": "assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');" "testString":
"assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');"
}, },
{ {
"text": "Your code should have an opening and closing <code>footer</code> tag.", "text":
"testString": "assert(code.match(/<footer>\\s*&copy; 2016 Camper Cat\\s*<\\/footer>/g), 'Your code should have an opening and closing <code>footer</code> tag.');" "Your code should have an opening and closing <code>footer</code> tag.",
"testString":
"assert(code.match(/<footer>\\s*&copy; 2016 Camper Cat\\s*<\\/footer>/g), 'Your code should have an opening and closing <code>footer</code> tag.');"
} }
], ],
"solutions": [], "solutions": [],
@ -477,7 +505,7 @@
"HTML5's <code>audio</code> element gives semantic meaning when it wraps sound or audio stream content in your markup. Audio content also needs a text alternative to be accessible to people who are deaf or hard of hearing. This can be done with nearby text on the page or a link to a transcript.", "HTML5's <code>audio</code> element gives semantic meaning when it wraps sound or audio stream content in your markup. Audio content also needs a text alternative to be accessible to people who are deaf or hard of hearing. This can be done with nearby text on the page or a link to a transcript.",
"The <code>audio</code> tag supports the <code>controls</code> attribute. This shows the browser default play, pause, and other controls, and supports keyboard functionality. This is a boolean attribute, meaning it doesn't need a value, its presence on the tag turns the setting on.", "The <code>audio</code> tag supports the <code>controls</code> attribute. This shows the browser default play, pause, and other controls, and supports keyboard functionality. This is a boolean attribute, meaning it doesn't need a value, its presence on the tag turns the setting on.",
"Here's an example:", "Here's an example:",
"<blockquote>&lt;audio id=&quot;meowClip&quot; controls&gt;<br> &lt;source src=&quot;audio/meow.mp3&quot; type=&quot;audio/mpeg&quot; /&gt;<br> &lt;source src=&quot;audio/meow.ogg&quot; type=&quot;audio/ogg&quot; /&gt;<br>&lt;/audio&gt;<br></blockquote>", "<blockquote>&lt;audio id=&quot;meowClip&quot; controls&gt;<br>&nbsp;&nbsp;&lt;source src=&quot;audio/meow.mp3&quot; type=&quot;audio/mpeg&quot; /&gt;<br>&nbsp;&nbsp;&lt;source src=&quot;audio/meow.ogg&quot; type=&quot;audio/ogg&quot; /&gt;<br>&lt;/audio&gt;<br></blockquote>",
"<strong>Note</strong><br>Multimedia content usually has both visual and auditory components. It needs synchronized captions and a transcript so users with visual and/or auditory impairments can access it. Generally, a web developer is not responsible for creating the captions or transcript, but needs to know to include them.", "<strong>Note</strong><br>Multimedia content usually has both visual and auditory components. It needs synchronized captions and a transcript so users with visual and/or auditory impairments can access it. Generally, a web developer is not responsible for creating the captions or transcript, but needs to know to include them.",
"<hr>", "<hr>",
"Time to take a break from Camper Cat and meet fellow camper Zersiax (@zersiax), a champion of accessibility and a screen reader user. To hear a clip of his screen reader in action, add an <code>audio</code> element after the <code>p</code>. Include the <code>controls</code> attribute. Then place a <code>source</code> tag inside the <code>audio</code> tags with the <code>src</code> attribute set to \"https://s3.amazonaws.com/freecodecamp/screen-reader.mp3\" and <code>type</code> attribute set to \"audio/mpeg\".", "Time to take a break from Camper Cat and meet fellow camper Zersiax (@zersiax), a champion of accessibility and a screen reader user. To hear a clip of his screen reader in action, add an <code>audio</code> element after the <code>p</code>. Include the <code>controls</code> attribute. Then place a <code>source</code> tag inside the <code>audio</code> tags with the <code>src</code> attribute set to \"https://s3.amazonaws.com/freecodecamp/screen-reader.mp3\" and <code>type</code> attribute set to \"audio/mpeg\".",
@ -486,31 +514,43 @@
"tests": [ "tests": [
{ {
"text": "Your code should have one <code>audio</code> tag.", "text": "Your code should have one <code>audio</code> tag.",
"testString": "assert($('audio').length === 1, 'Your code should have one <code>audio</code> tag.');" "testString":
"assert($('audio').length === 1, 'Your code should have one <code>audio</code> tag.');"
}, },
{ {
"text": "Make sure your <code>audio</code> element has a closing tag.", "text":
"testString": "assert(code.match(/<\\/audio>/g) && code.match(/<audio controls>/g) && code.match(/<\\/audio>/g).length === code.match(/<audio controls>/g).length, 'Make sure your <code>audio</code> element has a closing tag.');" "Make sure your <code>audio</code> element has a closing tag.",
"testString":
"assert(code.match(/<\\/audio>/g) && code.match(/<audio controls>/g) && code.match(/<\\/audio>/g).length === code.match(/<audio controls>/g).length, 'Make sure your <code>audio</code> element has a closing tag.');"
}, },
{ {
"text": "The <code>audio</code> tag should have the <code>controls</code> attribute.", "text":
"testString": "assert($('audio').attr('controls'), 'The <code>audio</code> tag should have the <code>controls</code> attribute.');" "The <code>audio</code> tag should have the <code>controls</code> attribute.",
"testString":
"assert($('audio').attr('controls'), 'The <code>audio</code> tag should have the <code>controls</code> attribute.');"
}, },
{ {
"text": "Your code should have one <code>source</code> tag.", "text": "Your code should have one <code>source</code> tag.",
"testString": "assert($('source').length === 1, 'Your code should have one <code>source</code> tag.');" "testString":
"assert($('source').length === 1, 'Your code should have one <code>source</code> tag.');"
}, },
{ {
"text": "Your <code>source</code> tag should be inside the <code>audio</code> tags.", "text":
"testString": "assert($('audio').children('source').length === 1, 'Your <code>source</code> tag should be inside the <code>audio</code> tags.');" "Your <code>source</code> tag should be inside the <code>audio</code> tags.",
"testString":
"assert($('audio').children('source').length === 1, 'Your <code>source</code> tag should be inside the <code>audio</code> tags.');"
}, },
{ {
"text": "The value for the <code>src</code> attribute on the <code>source</code> tag should match the link in the instructions exactly.", "text":
"testString": "assert($('source').attr('src') === 'https://s3.amazonaws.com/freecodecamp/screen-reader.mp3', 'The value for the <code>src</code> attribute on the <code>source</code> tag should match the link in the instructions exactly.');" "The value for the <code>src</code> attribute on the <code>source</code> tag should match the link in the instructions exactly.",
"testString":
"assert($('source').attr('src') === 'https://s3.amazonaws.com/freecodecamp/screen-reader.mp3', 'The value for the <code>src</code> attribute on the <code>source</code> tag should match the link in the instructions exactly.');"
}, },
{ {
"text": "Your code should include a <code>type</code> attribute on the <code>source</code> tag with a value of audio/mpeg.", "text":
"testString": "assert($('source').attr('type') === 'audio/mpeg', 'Your code should include a <code>type</code> attribute on the <code>source</code> tag with a value of audio/mpeg.');" "Your code should include a <code>type</code> attribute on the <code>source</code> tag with a value of audio/mpeg.",
"testString":
"assert($('source').attr('type') === 'audio/mpeg', 'Your code should include a <code>type</code> attribute on the <code>source</code> tag with a value of audio/mpeg.');"
} }
], ],
"solutions": [], "solutions": [],
@ -548,34 +588,42 @@
"HTML5 introduced the <code>figure</code> element, along with the related <code>figcaption</code>. Used together, these items wrap a visual representation (like an image, diagram, or chart) along with its caption. This gives a two-fold accessibility boost by both semantically grouping related content, and providing a text alternative that explains the <code>figure</code>.", "HTML5 introduced the <code>figure</code> element, along with the related <code>figcaption</code>. Used together, these items wrap a visual representation (like an image, diagram, or chart) along with its caption. This gives a two-fold accessibility boost by both semantically grouping related content, and providing a text alternative that explains the <code>figure</code>.",
"For data visualizations like charts, the caption can be used to briefly note the trends or conclusions for users with visual impairments. Another challenge covers how to move a table version of the chart's data off-screen (using CSS) for screen reader users.", "For data visualizations like charts, the caption can be used to briefly note the trends or conclusions for users with visual impairments. Another challenge covers how to move a table version of the chart's data off-screen (using CSS) for screen reader users.",
"Here's an example - note that the <code>figcaption</code> goes inside the <code>figure</code> tags and can be combined with other elements:", "Here's an example - note that the <code>figcaption</code> goes inside the <code>figure</code> tags and can be combined with other elements:",
"<blockquote>&lt;figure&gt;<br> &lt;img src=&quot;roundhouseDestruction.jpeg&quot; alt=&quot;Photo of Camper Cat executing a roundhouse kick&quot;&gt;<br> &lt;br&gt;<br> &lt;figcaption&gt;<br> Master Camper Cat demonstrates proper form of a roundhouse kick.<br> &lt;/figcaption&gt;<br>&lt;/figure&gt;<br></blockquote>", "<blockquote>&lt;figure&gt;<br>&nbsp;&nbsp;&lt;img src=&quot;roundhouseDestruction.jpeg&quot; alt=&quot;Photo of Camper Cat executing a roundhouse kick&quot;&gt;<br>&nbsp;&nbsp;&lt;br&gt;<br>&nbsp;&nbsp;&lt;figcaption&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;Master Camper Cat demonstrates proper form of a roundhouse kick.<br>&nbsp;&nbsp;&lt;/figcaption&gt;<br>&lt;/figure&gt;<br></blockquote>",
"<hr>", "<hr>",
"Camper Cat is hard at work creating a stacked bar chart showing the amount of time per week to spend training in stealth, combat, and weapons. Help him structure his page better by changing the <code>div</code> tag he used to a <code>figure</code> tag, and the <code>p</code> tag that surrounds the caption to a <code>figcaption</code> tag." "Camper Cat is hard at work creating a stacked bar chart showing the amount of time per week to spend training in stealth, combat, and weapons. Help him structure his page better by changing the <code>div</code> tag he used to a <code>figure</code> tag, and the <code>p</code> tag that surrounds the caption to a <code>figcaption</code> tag."
], ],
"tests": [ "tests": [
{ {
"text": "Your code should have one <code>figure</code> tag.", "text": "Your code should have one <code>figure</code> tag.",
"testString": "assert($('figure').length == 1, 'Your code should have one <code>figure</code> tag.');" "testString":
"assert($('figure').length == 1, 'Your code should have one <code>figure</code> tag.');"
}, },
{ {
"text": "Your code should have one <code>figcaption</code> tag.", "text": "Your code should have one <code>figcaption</code> tag.",
"testString": "assert($('figcaption').length == 1, 'Your code should have one <code>figcaption</code> tag.');" "testString":
"assert($('figcaption').length == 1, 'Your code should have one <code>figcaption</code> tag.');"
}, },
{ {
"text": "Your code should not have any <code>div</code> tags.", "text": "Your code should not have any <code>div</code> tags.",
"testString": "assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');" "testString":
"assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');"
}, },
{ {
"text": "Your code should not have any <code>p</code> tags.", "text": "Your code should not have any <code>p</code> tags.",
"testString": "assert($('p').length == 0, 'Your code should not have any <code>p</code> tags.');" "testString":
"assert($('p').length == 0, 'Your code should not have any <code>p</code> tags.');"
}, },
{ {
"text": "The <code>figcaption</code> should be a child of the <code>figure</code> tag.", "text":
"testString": "assert($('figure').children('figcaption').length == 1, 'The <code>figcaption</code> should be a child of the <code>figure</code> tag.');" "The <code>figcaption</code> should be a child of the <code>figure</code> tag.",
"testString":
"assert($('figure').children('figcaption').length == 1, 'The <code>figcaption</code> should be a child of the <code>figure</code> tag.');"
}, },
{ {
"text": "Make sure your <code>figure</code> element has a closing tag.", "text":
"testString": "assert(code.match(/<\\/figure>/g) && code.match(/<\\/figure>/g).length === code.match(/<figure>/g).length, 'Make sure your <code>figure</code> element has a closing tag.');" "Make sure your <code>figure</code> element has a closing tag.",
"testString":
"assert(code.match(/<\\/figure>/g) && code.match(/<\\/figure>/g).length === code.match(/<figure>/g).length, 'Make sure your <code>figure</code> element has a closing tag.');"
} }
], ],
"solutions": [], "solutions": [],
@ -644,18 +692,22 @@
"The <code>label</code> tag wraps the text for a specific form control item, usually the name or label for a choice. This ties meaning to the item and makes the form more readable. The <code>for</code> attribute on a <code>label</code> tag explicitly associates that <code>label</code> with the form control and is used by screen readers.", "The <code>label</code> tag wraps the text for a specific form control item, usually the name or label for a choice. This ties meaning to the item and makes the form more readable. The <code>for</code> attribute on a <code>label</code> tag explicitly associates that <code>label</code> with the form control and is used by screen readers.",
"You learned about radio buttons and their labels in a lesson in the Basic HTML section. In that lesson, we wrapped the radio button input element inside a <code>label</code> element along with the label text in order to make the text clickable. Another way to achieve this is by using the <code>for</code> attribute as explained in this lesson.", "You learned about radio buttons and their labels in a lesson in the Basic HTML section. In that lesson, we wrapped the radio button input element inside a <code>label</code> element along with the label text in order to make the text clickable. Another way to achieve this is by using the <code>for</code> attribute as explained in this lesson.",
"The value of the <code>for</code> attribute must be the same as the value of the <code>id</code> attribute of the form control. Here's an example:", "The value of the <code>for</code> attribute must be the same as the value of the <code>id</code> attribute of the form control. Here's an example:",
"<blockquote>&lt;form&gt;<br> &lt;label for=&quot;name&quot;&gt;Name:&lt;/label&gt;<br> &lt;input type=&quottext&quot; id=&quot;name&quot; name=&quot;name&quot;&gt;<br>&lt;/form&gt;<br></blockquote>", "<blockquote>&lt;form&gt;<br>&nbsp;&nbsp;&lt;label for=&quot;name&quot;&gt;Name:&lt;/label&gt;<br>&nbsp;&nbsp;&lt;input type=&quottext&quot; id=&quot;name&quot; name=&quot;name&quot;&gt;<br>&lt;/form&gt;<br></blockquote>",
"<hr>", "<hr>",
"Camper Cat expects a lot of interest in his thoughtful blog posts, and wants to include an email sign up form. Add a <code>for</code> attribute on the email <code>label</code> that matches the <code>id</code> on its <code>input</code> field." "Camper Cat expects a lot of interest in his thoughtful blog posts, and wants to include an email sign up form. Add a <code>for</code> attribute on the email <code>label</code> that matches the <code>id</code> on its <code>input</code> field."
], ],
"tests": [ "tests": [
{ {
"text": "Your code should have a <code>for</code> attribute on the <code>label</code> tag that is not empty.", "text":
"testString": "assert($('label').attr('for'), 'Your code should have a <code>for</code> attribute on the <code>label</code> tag that is not empty.');" "Your code should have a <code>for</code> attribute on the <code>label</code> tag that is not empty.",
"testString":
"assert($('label').attr('for'), 'Your code should have a <code>for</code> attribute on the <code>label</code> tag that is not empty.');"
}, },
{ {
"text": "Your <code>for</code> attribute value should match the <code>id</code> value on the email <code>input</code>.", "text":
"testString": "assert($('label').attr('for') == 'email', 'Your <code>for</code> attribute value should match the <code>id</code> value on the email <code>input</code>.');" "Your <code>for</code> attribute value should match the <code>id</code> value on the email <code>input</code>.",
"testString":
"assert($('label').attr('for') == 'email', 'Your <code>for</code> attribute value should match the <code>id</code> value on the email <code>input</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -709,36 +761,46 @@
}, },
{ {
"id": "587d778b367417b2b2512aa7", "id": "587d778b367417b2b2512aa7",
"title": "Wrap Radio Buttons in a fieldset Element for Better Accessibility", "title":
"Wrap Radio Buttons in a fieldset Element for Better Accessibility",
"description": [ "description": [
"The next form topic covers accessibility of radio buttons. Each choice is given a <code>label</code> with a <code>for</code> attribute tying to the <code>id</code> of the corresponding item as covered in the last challenge. Since radio buttons often come in a group where the user must choose one, there's a way to semantically show the choices are part of a set.", "The next form topic covers accessibility of radio buttons. Each choice is given a <code>label</code> with a <code>for</code> attribute tying to the <code>id</code> of the corresponding item as covered in the last challenge. Since radio buttons often come in a group where the user must choose one, there's a way to semantically show the choices are part of a set.",
"The <code>fieldset</code> tag surrounds the entire grouping of radio buttons to achieve this. It often uses a <code>legend</code> tag to provide a description for the grouping, which is read by screen readers for each choice in the <code>fieldset</code> element.", "The <code>fieldset</code> tag surrounds the entire grouping of radio buttons to achieve this. It often uses a <code>legend</code> tag to provide a description for the grouping, which is read by screen readers for each choice in the <code>fieldset</code> element.",
"The <code>fieldset</code> wrapper and <code>legend</code> tag are not necessary when the choices are self-explanatory, like a gender selection. Using a <code>label</code> with the <code>for</code> attribute for each radio button is sufficient.", "The <code>fieldset</code> wrapper and <code>legend</code> tag are not necessary when the choices are self-explanatory, like a gender selection. Using a <code>label</code> with the <code>for</code> attribute for each radio button is sufficient.",
"Here's an example:", "Here's an example:",
"<blockquote>&lt;form&gt;<br> &lt;fieldset&gt;<br> &lt;legend&gt;Choose one of these three items:&lt;/legend&gt;<br> &lt;input id=&quot;one&quot; type=&quot;radio&quot; name=&quot;items&quot; value=&quot;one&quot;&gt;<br> &lt;label for=&quot;one&quot;&gt;Choice One&lt;/label&gt;&lt;br&gt;<br> &lt;input id=&quot;two&quot; type=&quot;radio&quot; name=&quot;items&quot; value=&quot;two&quot;&gt;<br> &lt;label for=&quot;two&quot;&gt;Choice Two&lt;/label&gt;&lt;br&gt;<br> &lt;input id=&quot;three&quot; type=&quot;radio&quot; name=&quot;items&quot; value=&quot;three&quot;&gt;<br> &lt;label for=&quot;three&quot;&gt;Choice Three&lt;/label&gt;<br> &lt;/fieldset&gt;<br>&lt;/form&gt;<br></blockquote>", "<blockquote>&lt;form&gt;<br>&nbsp;&nbsp;&lt;fieldset&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;legend&gt;Choose one of these three items:&lt;/legend&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;input id=&quot;one&quot; type=&quot;radio&quot; name=&quot;items&quot; value=&quot;one&quot;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;label for=&quot;one&quot;&gt;Choice One&lt;/label&gt;&lt;br&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;input id=&quot;two&quot; type=&quot;radio&quot; name=&quot;items&quot; value=&quot;two&quot;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;label for=&quot;two&quot;&gt;Choice Two&lt;/label&gt;&lt;br&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;input id=&quot;three&quot; type=&quot;radio&quot; name=&quot;items&quot; value=&quot;three&quot;&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;label for=&quot;three&quot;&gt;Choice Three&lt;/label&gt;<br>&nbsp;&nbsp;&lt;/fieldset&gt;<br>&lt;/form&gt;<br></blockquote>",
"<hr>", "<hr>",
"Camper Cat wants information about the ninja level of his users when they sign up for his email list. He's added a set of radio buttons, and learned from our last lesson to use label tags with <code>for</code> attributes for each choice. Go Camper Cat! However, his code still needs some help. Change the div tag surrounding the radio buttons to a fieldset tag, and change the p tag inside it to a legend." "Camper Cat wants information about the ninja level of his users when they sign up for his email list. He's added a set of radio buttons, and learned from our last lesson to use label tags with <code>for</code> attributes for each choice. Go Camper Cat! However, his code still needs some help. Change the div tag surrounding the radio buttons to a fieldset tag, and change the p tag inside it to a legend."
], ],
"tests": [ "tests": [
{ {
"text": "Your code should have a <code>fieldset</code> tag around the radio button set.", "text":
"testString": "assert($('fieldset').length == 1, 'Your code should have a <code>fieldset</code> tag around the radio button set.');" "Your code should have a <code>fieldset</code> tag around the radio button set.",
"testString":
"assert($('fieldset').length == 1, 'Your code should have a <code>fieldset</code> tag around the radio button set.');"
}, },
{ {
"text": "Make sure your <code>fieldset</code> element has a closing tag.", "text":
"testString": "assert(code.match(/<\\/fieldset>/g) && code.match(/<\\/fieldset>/g).length === code.match(/<fieldset>/g).length, 'Make sure your <code>fieldset</code> element has a closing tag.');" "Make sure your <code>fieldset</code> element has a closing tag.",
"testString":
"assert(code.match(/<\\/fieldset>/g) && code.match(/<\\/fieldset>/g).length === code.match(/<fieldset>/g).length, 'Make sure your <code>fieldset</code> element has a closing tag.');"
}, },
{ {
"text": "Your code should have a <code>legend</code> tag around the text asking what level ninja a user is.", "text":
"testString": "assert($('legend').length == 1, 'Your code should have a <code>legend</code> tag around the text asking what level ninja a user is.');" "Your code should have a <code>legend</code> tag around the text asking what level ninja a user is.",
"testString":
"assert($('legend').length == 1, 'Your code should have a <code>legend</code> tag around the text asking what level ninja a user is.');"
}, },
{ {
"text": "Your code should not have any <code>div</code> tags.", "text": "Your code should not have any <code>div</code> tags.",
"testString": "assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');" "testString":
"assert($('div').length == 0, 'Your code should not have any <code>div</code> tags.');"
}, },
{ {
"text": "Your code should no longer have a <code>p</code> tag around the text asking what level ninja a user is.", "text":
"testString": "assert($('p').length == 4, 'Your code should no longer have a <code>p</code> tag around the text asking what level ninja a user is.');" "Your code should no longer have a <code>p</code> tag around the text asking what level ninja a user is.",
"testString":
"assert($('p').length == 4, 'Your code should no longer have a <code>p</code> tag around the text asking what level ninja a user is.');"
} }
], ],
"solutions": [], "solutions": [],
@ -815,20 +877,28 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should add one <code>input</code> tag for the date selector field.", "text":
"testString": "assert($('input').length == 2, 'Your code should add one <code>input</code> tag for the date selector field.');" "Your code should add one <code>input</code> tag for the date selector field.",
"testString":
"assert($('input').length == 2, 'Your code should add one <code>input</code> tag for the date selector field.');"
}, },
{ {
"text": "Your <code>input</code> tag should have a <code>type</code> attribute with a value of date.", "text":
"testString": "assert($('input').attr('type') == 'date', 'Your <code>input</code> tag should have a <code>type</code> attribute with a value of date.');" "Your <code>input</code> tag should have a <code>type</code> attribute with a value of date.",
"testString":
"assert($('input').attr('type') == 'date', 'Your <code>input</code> tag should have a <code>type</code> attribute with a value of date.');"
}, },
{ {
"text": "Your <code>input</code> tag should have an <code>id</code> attribute with a value of pickdate.", "text":
"testString": "assert($('input').attr('id') == 'pickdate', 'Your <code>input</code> tag should have an <code>id</code> attribute with a value of pickdate.');" "Your <code>input</code> tag should have an <code>id</code> attribute with a value of pickdate.",
"testString":
"assert($('input').attr('id') == 'pickdate', 'Your <code>input</code> tag should have an <code>id</code> attribute with a value of pickdate.');"
}, },
{ {
"text": "Your <code>input</code> tag should have a <code>name</code> attribute with a value of date.", "text":
"testString": "assert($('input').attr('name') == 'date', 'Your <code>input</code> tag should have a <code>name</code> attribute with a value of date.');" "Your <code>input</code> tag should have a <code>name</code> attribute with a value of date.",
"testString":
"assert($('input').attr('name') == 'date', 'Your <code>input</code> tag should have a <code>name</code> attribute with a value of date.');"
} }
], ],
"solutions": [], "solutions": [],
@ -883,20 +953,27 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your <code>time</code> tags should wrap around the text \"Thursday, September 15&lt;sup&gt;th&lt;/sup&gt;\".", "text":
"testString": "assert($('time').text().match(/Thursday, September 15th/g), 'Your <code>time</code> tags should wrap around the text \"Thursday, September 15&lt;sup&gt;th&lt;/sup&gt;\".');" "Your <code>time</code> tags should wrap around the text \"Thursday, September 15&lt;sup&gt;th&lt;/sup&gt;\".",
"testString":
"assert($('time').text().match(/Thursday, September 15th/g), 'Your <code>time</code> tags should wrap around the text \"Thursday, September 15&lt;sup&gt;th&lt;/sup&gt;\".');"
}, },
{ {
"text": "Your <code>time</code> tag should have a <code>datetime</code> attribute that is not empty.", "text":
"testString": "assert($('time').attr('datetime'), 'Your <code>time</code> tag should have a <code>datetime</code> attribute that is not empty.');" "Your <code>time</code> tag should have a <code>datetime</code> attribute that is not empty.",
"testString":
"assert($('time').attr('datetime'), 'Your <code>time</code> tag should have a <code>datetime</code> attribute that is not empty.');"
}, },
{ {
"text": "Your <code>datetime</code> attribute should be set to a value of 2016-09-15.", "text":
"testString": "assert($('time').attr('datetime') === \"2016-09-15\", 'Your <code>datetime</code> attribute should be set to a value of 2016-09-15.');" "Your <code>datetime</code> attribute should be set to a value of 2016-09-15.",
"testString":
"assert($('time').attr('datetime') === \"2016-09-15\", 'Your <code>datetime</code> attribute should be set to a value of 2016-09-15.');"
}, },
{ {
"text": "Make sure your <code>time</code> element has a closing tag.", "text": "Make sure your <code>time</code> element has a closing tag.",
"testString": "assert(code.match(/<\\/time>/g) && code.match(/<\\/time>/g).length === 4, 'Make sure your <code>time</code> element has a closing tag.');" "testString":
"assert(code.match(/<\\/time>/g) && code.match(/<\\/time>/g).length === 4, 'Make sure your <code>time</code> element has a closing tag.');"
} }
], ],
"solutions": [], "solutions": [],
@ -949,12 +1026,13 @@
}, },
{ {
"id": "587d778d367417b2b2512aaa", "id": "587d778d367417b2b2512aaa",
"title": "Make Elements Only Visible to a Screen Reader by Using Custom CSS", "title":
"Make Elements Only Visible to a Screen Reader by Using Custom CSS",
"description": [ "description": [
"Have you noticed that all of the applied accessibility challenges so far haven't used any CSS? This is to show the importance of a logical document outline, and using semantically meaningful tags around your content before introducing the visual design aspect.", "Have you noticed that all of the applied accessibility challenges so far haven't used any CSS? This is to show the importance of a logical document outline, and using semantically meaningful tags around your content before introducing the visual design aspect.",
"However, CSS's magic can also improve accessibility on your page when you want to visually hide content meant only for screen readers. This happens when information is in a visual format (like a chart), but screen reader users need an alternative presentation (like a table) to access the data. CSS is used to position the screen reader-only elements off the visual area of the browser window.", "However, CSS's magic can also improve accessibility on your page when you want to visually hide content meant only for screen readers. This happens when information is in a visual format (like a chart), but screen reader users need an alternative presentation (like a table) to access the data. CSS is used to position the screen reader-only elements off the visual area of the browser window.",
"Here's an example of the CSS rules that accomplish this:", "Here's an example of the CSS rules that accomplish this:",
"<blockquote>.sr-only {<br> position: absolute;<br> left: -10000px;<br> width: 1px;<br> height: 1px;<br> top: auto;<br> overflow: hidden;<br>}</blockquote>", "<blockquote>.sr-only {<br>&nbsp;&nbsp;position: absolute;<br>&nbsp;&nbsp;left: -10000px;<br>&nbsp;&nbsp;width: 1px;<br>&nbsp;&nbsp;height: 1px;<br>&nbsp;&nbsp;top: auto;<br>&nbsp;&nbsp;overflow: hidden;<br>}</blockquote>",
"<strong>Note</strong><br>The following CSS approaches will NOT do the same thing:", "<strong>Note</strong><br>The following CSS approaches will NOT do the same thing:",
"<ul>", "<ul>",
"<li><code>display: none;</code> or <code>visibility: hidden;</code> hides content for everyone, including screen reader users</li>", "<li><code>display: none;</code> or <code>visibility: hidden;</code> hides content for everyone, including screen reader users</li>",
@ -965,20 +1043,28 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should set the <code>position</code> property of the <code>sr-only</code> class to a value of absolute.", "text":
"testString": "assert($('.sr-only').css('position') == 'absolute', 'Your code should set the <code>position</code> property of the <code>sr-only</code> class to a value of absolute.');" "Your code should set the <code>position</code> property of the <code>sr-only</code> class to a value of absolute.",
"testString":
"assert($('.sr-only').css('position') == 'absolute', 'Your code should set the <code>position</code> property of the <code>sr-only</code> class to a value of absolute.');"
}, },
{ {
"text": "Your code should set the <code>left</code> property of the <code>sr-only</code> class to a value of -10000px.", "text":
"testString": "assert($('.sr-only').css('left') == '-10000px', 'Your code should set the <code>left</code> property of the <code>sr-only</code> class to a value of -10000px.');" "Your code should set the <code>left</code> property of the <code>sr-only</code> class to a value of -10000px.",
"testString":
"assert($('.sr-only').css('left') == '-10000px', 'Your code should set the <code>left</code> property of the <code>sr-only</code> class to a value of -10000px.');"
}, },
{ {
"text": "Your code should set the <code>width</code> property of the <code>sr-only</code> class to a value of 1 pixel.", "text":
"testString": "assert(code.match(/width:\\s*?1px/gi), 'Your code should set the <code>width</code> property of the <code>sr-only</code> class to a value of 1 pixel.');" "Your code should set the <code>width</code> property of the <code>sr-only</code> class to a value of 1 pixel.",
"testString":
"assert(code.match(/width:\\s*?1px/gi), 'Your code should set the <code>width</code> property of the <code>sr-only</code> class to a value of 1 pixel.');"
}, },
{ {
"text": "Your code should set the <code>height</code> property of the <code>sr-only</code> class to a value of 1 pixel.", "text":
"testString": "assert(code.match(/height:\\s*?1px/gi), 'Your code should set the <code>height</code> property of the <code>sr-only</code> class to a value of 1 pixel.');" "Your code should set the <code>height</code> property of the <code>sr-only</code> class to a value of 1 pixel.",
"testString":
"assert(code.match(/height:\\s*?1px/gi), 'Your code should set the <code>height</code> property of the <code>sr-only</code> class to a value of 1 pixel.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1093,12 +1179,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should change the text <code>color</code> for the <code>body</code> to the darker gray.", "text":
"testString": "assert($('body').css('color') == 'rgb(99, 99, 99)', 'Your code should change the text <code>color</code> for the <code>body</code> to the darker gray.');" "Your code should change the text <code>color</code> for the <code>body</code> to the darker gray.",
"testString":
"assert($('body').css('color') == 'rgb(99, 99, 99)', 'Your code should change the text <code>color</code> for the <code>body</code> to the darker gray.');"
}, },
{ {
"text": "Your code should not change the <code>background-color</code> for the <code>body</code>.", "text":
"testString": "assert($('body').css('background-color') == 'rgb(255, 255, 255)', 'Your code should not change the <code>background-color</code> for the <code>body</code>.');" "Your code should not change the <code>background-color</code> for the <code>body</code>.",
"testString":
"assert($('body').css('background-color') == 'rgb(255, 255, 255)', 'Your code should not change the <code>background-color</code> for the <code>body</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1149,12 +1239,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should only change the lightness value for the text <code>color</code> property to a value of 15%.", "text":
"testString": "assert(code.match(/color:\\s*?hsl\\(0,\\s*?55%,\\s*?15%\\)/gi), 'Your code should only change the lightness value for the text <code>color</code> property to a value of 15%.');" "Your code should only change the lightness value for the text <code>color</code> property to a value of 15%.",
"testString":
"assert(code.match(/color:\\s*?hsl\\(0,\\s*?55%,\\s*?15%\\)/gi), 'Your code should only change the lightness value for the text <code>color</code> property to a value of 15%.');"
}, },
{ {
"text": "Your code should only change the lightness value for the <code>background-color</code> property to a value of 55%.", "text":
"testString": "assert(code.match(/background-color:\\s*?hsl\\(120,\\s*?25%,\\s*?55%\\)/gi), 'Your code should only change the lightness value for the <code>background-color</code> property to a value of 55%.');" "Your code should only change the lightness value for the <code>background-color</code> property to a value of 55%.",
"testString":
"assert(code.match(/background-color:\\s*?hsl\\(120,\\s*?25%,\\s*?55%\\)/gi), 'Your code should only change the lightness value for the <code>background-color</code> property to a value of 55%.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1194,7 +1288,8 @@
}, },
{ {
"id": "587d778f367417b2b2512aad", "id": "587d778f367417b2b2512aad",
"title": "Avoid Colorblindness Issues by Carefully Choosing Colors that Convey Information", "title":
"Avoid Colorblindness Issues by Carefully Choosing Colors that Convey Information",
"description": [ "description": [
"There are various forms of colorblindness. These can range from a reduced sensitivity to a certain wavelength of light to the inability to see color at all. The most common form is a reduced sensitivity to detect greens.", "There are various forms of colorblindness. These can range from a reduced sensitivity to a certain wavelength of light to the inability to see color at all. The most common form is a reduced sensitivity to detect greens.",
"For example, if two similar green colors are the foreground and background color of your content, a colorblind user may not be able to distinguish them. Close colors can be thought of as neighbors on the color wheel, and those combinations should be avoided when conveying important information.", "For example, if two similar green colors are the foreground and background color of your content, a colorblind user may not be able to distinguish them. Close colors can be thought of as neighbors on the color wheel, and those combinations should be avoided when conveying important information.",
@ -1204,8 +1299,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should change the text <code>color</code> for the <code>button</code> to the dark blue.", "text":
"testString": "assert($('button').css('color') == 'rgb(0, 51, 102)', 'Your code should change the text <code>color</code> for the <code>button</code> to the dark blue.');" "Your code should change the text <code>color</code> for the <code>button</code> to the dark blue.",
"testString":
"assert($('button').css('color') == 'rgb(0, 51, 102)', 'Your code should change the text <code>color</code> for the <code>button</code> to the dark blue.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1252,12 +1349,15 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should move the anchor <code>a</code> tags from around the words \"Click here\" to wrap around the words \"information about batteries\".", "text":
"testString": "assert($('a').text().match(/^(information about batteries)$/g), 'Your code should move the anchor <code>a</code> tags from around the words \"Click here\" to wrap around the words \"information about batteries\".');" "Your code should move the anchor <code>a</code> tags from around the words \"Click here\" to wrap around the words \"information about batteries\".",
"testString":
"assert($('a').text().match(/^(information about batteries)$/g), 'Your code should move the anchor <code>a</code> tags from around the words \"Click here\" to wrap around the words \"information about batteries\".');"
}, },
{ {
"text": "Make sure your <code>a</code> element has a closing tag.", "text": "Make sure your <code>a</code> element has a closing tag.",
"testString": "assert(code.match(/<\\/a>/g) && code.match(/<\\/a>/g).length === code.match(/<a href=(''|\"\")>/g).length, 'Make sure your <code>a</code> element has a closing tag.');" "testString":
"assert(code.match(/<\\/a>/g) && code.match(/<\\/a>/g).length === code.match(/<a href=(''|\"\")>/g).length, 'Make sure your <code>a</code> element has a closing tag.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1299,20 +1399,28 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should add an <code>accesskey</code> attribute to the <code>a</code> tag with the <code>id</code> of \"first\".", "text":
"testString": "assert($('#first').attr('accesskey'), 'Your code should add an <code>accesskey</code> attribute to the <code>a</code> tag with the <code>id</code> of \"first\".');" "Your code should add an <code>accesskey</code> attribute to the <code>a</code> tag with the <code>id</code> of \"first\".",
"testString":
"assert($('#first').attr('accesskey'), 'Your code should add an <code>accesskey</code> attribute to the <code>a</code> tag with the <code>id</code> of \"first\".');"
}, },
{ {
"text": "Your code should add an <code>accesskey</code> attribute to the <code>a</code> tag with the <code>id</code> of \"second\".", "text":
"testString": "assert($('#second').attr('accesskey'), 'Your code should add an <code>accesskey</code> attribute to the <code>a</code> tag with the <code>id</code> of \"second\".');" "Your code should add an <code>accesskey</code> attribute to the <code>a</code> tag with the <code>id</code> of \"second\".",
"testString":
"assert($('#second').attr('accesskey'), 'Your code should add an <code>accesskey</code> attribute to the <code>a</code> tag with the <code>id</code> of \"second\".');"
}, },
{ {
"text": "Your code should set the <code>accesskey</code> attribute on the <code>a</code> tag with the <code>id</code> of \"first\" to \"g\". Note that case matters.", "text":
"testString": "assert($('#first').attr('accesskey') == 'g', 'Your code should set the <code>accesskey</code> attribute on the <code>a</code> tag with the <code>id</code> of \"first\" to \"g\". Note that case matters.');" "Your code should set the <code>accesskey</code> attribute on the <code>a</code> tag with the <code>id</code> of \"first\" to \"g\". Note that case matters.",
"testString":
"assert($('#first').attr('accesskey') == 'g', 'Your code should set the <code>accesskey</code> attribute on the <code>a</code> tag with the <code>id</code> of \"first\" to \"g\". Note that case matters.');"
}, },
{ {
"text": "Your code should set the <code>accesskey</code> attribute on the <code>a</code> tag with the <code>id</code> of \"second\" to \"c\". Note that case matters.", "text":
"testString": "assert($('#second').attr('accesskey') == 'c', 'Your code should set the <code>accesskey</code> attribute on the <code>a</code> tag with the <code>id</code> of \"second\" to \"c\". Note that case matters.');" "Your code should set the <code>accesskey</code> attribute on the <code>a</code> tag with the <code>id</code> of \"second\" to \"c\". Note that case matters.",
"testString":
"assert($('#second').attr('accesskey') == 'c', 'Your code should set the <code>accesskey</code> attribute on the <code>a</code> tag with the <code>id</code> of \"second\" to \"c\". Note that case matters.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1367,12 +1475,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should add a <code>tabindex</code> attribute to the <code>p</code> tag that holds the form instructions.", "text":
"testString": "assert($('p').attr('tabindex'), 'Your code should add a <code>tabindex</code> attribute to the <code>p</code> tag that holds the form instructions.');" "Your code should add a <code>tabindex</code> attribute to the <code>p</code> tag that holds the form instructions.",
"testString":
"assert($('p').attr('tabindex'), 'Your code should add a <code>tabindex</code> attribute to the <code>p</code> tag that holds the form instructions.');"
}, },
{ {
"text": "Your code should set the <code>tabindex</code> attribute on the <code>p</code> tag to a value of 0.", "text":
"testString": "assert($('p').attr('tabindex') == '0', 'Your code should set the <code>tabindex</code> attribute on the <code>p</code> tag to a value of 0.');" "Your code should set the <code>tabindex</code> attribute on the <code>p</code> tag to a value of 0.",
"testString":
"assert($('p').attr('tabindex') == '0', 'Your code should set the <code>tabindex</code> attribute on the <code>p</code> tag to a value of 0.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1441,7 +1553,8 @@
}, },
{ {
"id": "587d7790367417b2b2512ab1", "id": "587d7790367417b2b2512ab1",
"title": "Use tabindex to Specify the Order of Keyboard Focus for Several Elements", "title":
"Use tabindex to Specify the Order of Keyboard Focus for Several Elements",
"description": [ "description": [
"The <code>tabindex</code> attribute also specifies the exact tab order of elements. This is achieved when the value of the attribute is set to a positive number of 1 or higher.", "The <code>tabindex</code> attribute also specifies the exact tab order of elements. This is achieved when the value of the attribute is set to a positive number of 1 or higher.",
"Setting a tabindex=\"1\" will bring keyboard focus to that element first. Then it cycles through the sequence of specified <code>tabindex</code> values (2, 3, etc.), before moving to default and <code>tabindex=\"0\"</code> items.", "Setting a tabindex=\"1\" will bring keyboard focus to that element first. Then it cycles through the sequence of specified <code>tabindex</code> values (2, 3, etc.), before moving to default and <code>tabindex=\"0\"</code> items.",
@ -1454,20 +1567,28 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should add a <code>tabindex</code> attribute to the search <code>input</code> tag.", "text":
"testString": "assert($('#search').attr('tabindex'), 'Your code should add a <code>tabindex</code> attribute to the search <code>input</code> tag.');" "Your code should add a <code>tabindex</code> attribute to the search <code>input</code> tag.",
"testString":
"assert($('#search').attr('tabindex'), 'Your code should add a <code>tabindex</code> attribute to the search <code>input</code> tag.');"
}, },
{ {
"text": "Your code should add a <code>tabindex</code> attribute to the submit <code>input</code> tag.", "text":
"testString": "assert($('#submit').attr('tabindex'), 'Your code should add a <code>tabindex</code> attribute to the submit <code>input</code> tag.');" "Your code should add a <code>tabindex</code> attribute to the submit <code>input</code> tag.",
"testString":
"assert($('#submit').attr('tabindex'), 'Your code should add a <code>tabindex</code> attribute to the submit <code>input</code> tag.');"
}, },
{ {
"text": "Your code should set the <code>tabindex</code> attribute on the search <code>input</code> tag to a value of 1.", "text":
"testString": "assert($('#search').attr('tabindex') == '1', 'Your code should set the <code>tabindex</code> attribute on the search <code>input</code> tag to a value of 1.');" "Your code should set the <code>tabindex</code> attribute on the search <code>input</code> tag to a value of 1.",
"testString":
"assert($('#search').attr('tabindex') == '1', 'Your code should set the <code>tabindex</code> attribute on the search <code>input</code> tag to a value of 1.');"
}, },
{ {
"text": "Your code should set the <code>tabindex</code> attribute on the submit <code>input</code> tag to a value of 2.", "text":
"testString": "assert($('#submit').attr('tabindex') == '2', 'Your code should set the <code>tabindex</code> attribute on the submit <code>input</code> tag to a value of 2.');" "Your code should set the <code>tabindex</code> attribute on the submit <code>input</code> tag to a value of 2.",
"testString":
"assert($('#submit').attr('tabindex') == '2', 'Your code should set the <code>tabindex</code> attribute on the submit <code>input</code> tag to a value of 2.');"
} }
], ],
"solutions": [], "solutions": [],

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>display</code> property with a value of <code>grid</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*display\\s*?:\\s*?grid\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>display</code> property with a value of <code>grid</code>.');" "<code>container</code> class should have a <code>display</code> property with a value of <code>grid</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*display\\s*?:\\s*?grid\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>display</code> property with a value of <code>grid</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -66,7 +68,7 @@
"title": "Add Columns with grid-template-columns", "title": "Add Columns with grid-template-columns",
"description": [ "description": [
"Simply creating a grid element doesn't get you very far. You need to define the structure of the grid as well. To add some columns to the grid, use the <code>grid-template-columns</code> property on a grid container as demonstrated below:", "Simply creating a grid element doesn't get you very far. You need to define the structure of the grid as well. To add some columns to the grid, use the <code>grid-template-columns</code> property on a grid container as demonstrated below:",
"<blockquote>.container {<br>display: grid;<br>grid-template-columns: 50px 50px;<br>}</blockquote>", "<blockquote>.container {<br>&nbsp;&nbsp;display: grid;<br>&nbsp;&nbsp;grid-template-columns: 50px 50px;<br>}</blockquote>",
"This will give your grid two columns that are 50px wide each.", "This will give your grid two columns that are 50px wide each.",
"The number of parameters given to the <code>grid-template-columns</code> property indicates the number of columns in the grid, and the value of each parameter indicates the width of each column.", "The number of parameters given to the <code>grid-template-columns</code> property indicates the number of columns in the grid, and the value of each parameter indicates the width of each column.",
"<hr>", "<hr>",
@ -74,8 +76,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-template-columns</code> property with three units of <code>100px</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?100px\\s*?100px\\s*?100px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property with three units of <code>100px</code>.');" "<code>container</code> class should have a <code>grid-template-columns</code> property with three units of <code>100px</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?100px\\s*?100px\\s*?100px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property with three units of <code>100px</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -131,8 +135,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-template-rows</code> property with two units of <code>50px</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-rows\\s*?:\\s*?50px\\s*?50px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-rows</code> property with two units of <code>50px</code>.');" "<code>container</code> class should have a <code>grid-template-rows</code> property with two units of <code>50px</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-rows\\s*?:\\s*?50px\\s*?50px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-rows</code> property with two units of <code>50px</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -195,8 +201,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-template-columns</code> property that has three columns with the following widths: <code>1fr, 100px, and 2fr</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?1fr\\s*?100px\\s*?2fr\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property that has three columns with the following widths: <code>1fr, 100px, and 2fr</code>.');" "<code>container</code> class should have a <code>grid-template-columns</code> property that has three columns with the following widths: <code>1fr, 100px, and 2fr</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?1fr\\s*?100px\\s*?2fr\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property that has three columns with the following widths: <code>1fr, 100px, and 2fr</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -256,8 +264,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-column-gap</code> property that has the value of <code>20px</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-column-gap\\s*?:\\s*?20px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-column-gap</code> property that has the value of <code>20px</code>.');" "<code>container</code> class should have a <code>grid-column-gap</code> property that has the value of <code>20px</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-column-gap\\s*?:\\s*?20px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-column-gap</code> property that has the value of <code>20px</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -316,8 +326,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-row-gap</code> property that has the value of <code>5px</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-row-gap\\s*?:\\s*?5px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-row-gap</code> property that has the value of <code>5px</code>.');" "<code>container</code> class should have a <code>grid-row-gap</code> property that has the value of <code>5px</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-row-gap\\s*?:\\s*?5px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-row-gap</code> property that has the value of <code>5px</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -376,8 +388,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-gap</code> property that introduces <code>10px</code> gap between the rows and <code>20px</code> gap between the columns.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-gap\\s*?:\\s*?10px\\s*?20px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-gap</code> property that introduces <code>10px</code> gap between the rows and <code>20px</code> gap between the columns.');" "<code>container</code> class should have a <code>grid-gap</code> property that introduces <code>10px</code> gap between the rows and <code>20px</code> gap between the columns.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-gap\\s*?:\\s*?10px\\s*?20px\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-gap</code> property that introduces <code>10px</code> gap between the rows and <code>20px</code> gap between the columns.');"
} }
], ],
"solutions": [], "solutions": [],
@ -442,8 +456,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>item5</code> class should have a <code>grid-column</code> property that has the value of <code>2 / 4</code>.", "text":
"testString": "assert(code.match(/.item5\\s*?{[\\s\\S]*grid-column\\s*?:\\s*?2\\s*?\\/\\s*?4\\s*?;[\\s\\S]*}/gi), '<code>item5</code> class should have a <code>grid-column</code> property that has the value of <code>2 / 4</code>.');" "<code>item5</code> class should have a <code>grid-column</code> property that has the value of <code>2 / 4</code>.",
"testString":
"assert(code.match(/.item5\\s*?{[\\s\\S]*grid-column\\s*?:\\s*?2\\s*?\\/\\s*?4\\s*?;[\\s\\S]*}/gi), '<code>item5</code> class should have a <code>grid-column</code> property that has the value of <code>2 / 4</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -506,8 +522,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>item5</code> class should have a <code>grid-row</code> property that has the value of <code>2 / 4</code>.", "text":
"testString": "assert(code.match(/.item5\\s*?{[\\s\\S]*grid-row\\s*?:\\s*?2\\s*?\\/\\s*?4\\s*?;[\\s\\S]*}/gi), '<code>item5</code> class should have a <code>grid-row</code> property that has the value of <code>2 / 4</code>.');" "<code>item5</code> class should have a <code>grid-row</code> property that has the value of <code>2 / 4</code>.",
"testString":
"assert(code.match(/.item5\\s*?{[\\s\\S]*grid-row\\s*?:\\s*?2\\s*?\\/\\s*?4\\s*?;[\\s\\S]*}/gi), '<code>item5</code> class should have a <code>grid-row</code> property that has the value of <code>2 / 4</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -574,8 +592,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>item2</code> class should have a <code>justify-self</code> property that has the value of <code>center</code>.", "text":
"testString": "assert(code.match(/.item2\\s*?{[\\s\\S]*justify-self\\s*?:\\s*?center\\s*?;[\\s\\S]*}/gi), '<code>item2</code> class should have a <code>justify-self</code> property that has the value of <code>center</code>.');" "<code>item2</code> class should have a <code>justify-self</code> property that has the value of <code>center</code>.",
"testString":
"assert(code.match(/.item2\\s*?{[\\s\\S]*justify-self\\s*?:\\s*?center\\s*?;[\\s\\S]*}/gi), '<code>item2</code> class should have a <code>justify-self</code> property that has the value of <code>center</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -639,8 +659,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>item3</code> class should have a <code>align-self</code> property that has the value of <code>end</code>.", "text":
"testString": "assert(code.match(/.item3\\s*?{[\\s\\S]*align-self\\s*?:\\s*?end\\s*?;[\\s\\S]*}/gi), '<code>item3</code> class should have a <code>align-self</code> property that has the value of <code>end</code>.');" "<code>item3</code> class should have a <code>align-self</code> property that has the value of <code>end</code>.",
"testString":
"assert(code.match(/.item3\\s*?{[\\s\\S]*align-self\\s*?:\\s*?end\\s*?;[\\s\\S]*}/gi), '<code>item3</code> class should have a <code>align-self</code> property that has the value of <code>end</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -704,8 +726,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>justify-items</code> property that has the value of <code>center</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*justify-items\\s*?:\\s*?center\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>justify-items</code> property that has the value of <code>center</code>.');" "<code>container</code> class should have a <code>justify-items</code> property that has the value of <code>center</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*justify-items\\s*?:\\s*?center\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>justify-items</code> property that has the value of <code>center</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -765,8 +789,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>align-items</code> property that has the value of <code>end</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*align-items\\s*?:\\s*?end\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>align-items</code> property that has the value of <code>end</code>.');" "<code>container</code> class should have a <code>align-items</code> property that has the value of <code>end</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*align-items\\s*?:\\s*?end\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>align-items</code> property that has the value of <code>end</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -821,7 +847,7 @@
"title": "Divide the Grid Into an Area Template", "title": "Divide the Grid Into an Area Template",
"description": [ "description": [
"You can group cells of your grid together into an <dfn>area</dfn> and give the area a custom name. Do this by using <code>grid-template-areas</code> on the container like this:", "You can group cells of your grid together into an <dfn>area</dfn> and give the area a custom name. Do this by using <code>grid-template-areas</code> on the container like this:",
"<blockquote>grid-template-areas:<br>\"header header header\"<br>\"advert content content\"<br>\"footer footer footer\";</blockquote>", "<blockquote>grid-template-areas:<br>&nbsp;&nbsp;\"header header header\"<br>&nbsp;&nbsp;\"advert content content\"<br>&nbsp;&nbsp;\"footer footer footer\";</blockquote>",
"The code above merges the top three cells together into an area named <code>header</code>, the bottom three cells into a <code>footer</code> area, and it makes two areas in the middle row; <code>advert</code> and <code>content</code>.", "The code above merges the top three cells together into an area named <code>header</code>, the bottom three cells into a <code>footer</code> area, and it makes two areas in the middle row; <code>advert</code> and <code>content</code>.",
"<strong>Note</strong><br>Every word in the code represents a cell and every pair of quotation marks represent a row.", "<strong>Note</strong><br>Every word in the code represents a cell and every pair of quotation marks represent a row.",
"In addition to custom labels, you can use a period (<code>.</code>) to designate an empty cell in the grid.", "In addition to custom labels, you can use a period (<code>.</code>) to designate an empty cell in the grid.",
@ -830,8 +856,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-template-areas</code> propertiy similar to the preview but has <code>.</code> instead of the <code>advert</code> area.", "text":
"testString": "assert(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), '<code>container</code> class should have a <code>grid-template-areas</code> propertiy similar to the preview but has <code>.</code> instead of the <code>advert</code> area.');" "<code>container</code> class should have a <code>grid-template-areas</code> propertiy similar to the preview but has <code>.</code> instead of the <code>advert</code> area.",
"testString":
"assert(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), '<code>container</code> class should have a <code>grid-template-areas</code> propertiy similar to the preview but has <code>.</code> instead of the <code>advert</code> area.');"
} }
], ],
"solutions": [], "solutions": [],
@ -896,8 +924,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>footer</code>.", "text":
"testString": "assert(code.match(/.item5\\s*?{[\\s\\S]*grid-area\\s*?:\\s*?footer\\s*?;[\\s\\S]*}/gi), '<code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>footer</code>.');" "<code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>footer</code>.",
"testString":
"assert(code.match(/.item5\\s*?{[\\s\\S]*grid-area\\s*?:\\s*?footer\\s*?;[\\s\\S]*}/gi), '<code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>footer</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -968,8 +998,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>3/1/4/4</code>.", "text":
"testString": "assert(code.match(/.item5\\s*?{[\\s\\S]*grid-area\\s*?:\\s*?3\\s*?\\/\\s*?1\\s*?\\/\\s*?4\\s*?\\/\\s*?4\\s*?;[\\s\\S]*}/gi), '<code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>3/1/4/4</code>.');" "<code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>3/1/4/4</code>.",
"testString":
"assert(code.match(/.item5\\s*?{[\\s\\S]*grid-area\\s*?:\\s*?3\\s*?\\/\\s*?1\\s*?\\/\\s*?4\\s*?\\/\\s*?4\\s*?;[\\s\\S]*}/gi), '<code>item5</code> class should have a <code>grid-area</code> property that has the value of <code>3/1/4/4</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1040,8 +1072,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-template-columns</code> property that is set to repeat 3 columns with the width of <code>1fr</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?repeat\\s*?\\(\\s*?3\\s*?,\\s*?1fr\\s*?\\)\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property that is set to repeat 3 columns with the width of <code>1fr</code>.');" "<code>container</code> class should have a <code>grid-template-columns</code> property that is set to repeat 3 columns with the width of <code>1fr</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?repeat\\s*?\\(\\s*?3\\s*?,\\s*?1fr\\s*?\\)\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property that is set to repeat 3 columns with the width of <code>1fr</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1103,8 +1137,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-template-columns</code> property that is set to repeat 3 columns with the minimum width of <code>90px</code> and maximum width of <code>1fr</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?repeat\\s*?\\(\\s*?3\\s*?,\\s*?minmax\\s*?\\(\\s*?90px\\s*?,\\s*?1fr\\s*?\\)\\s*?\\)\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property that is set to repeat 3 columns with the minimum width of <code>90px</code> and maximum width of <code>1fr</code>.');" "<code>container</code> class should have a <code>grid-template-columns</code> property that is set to repeat 3 columns with the minimum width of <code>90px</code> and maximum width of <code>1fr</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?repeat\\s*?\\(\\s*?3\\s*?,\\s*?minmax\\s*?\\(\\s*?90px\\s*?,\\s*?1fr\\s*?\\)\\s*?\\)\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property that is set to repeat 3 columns with the minimum width of <code>90px</code> and maximum width of <code>1fr</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1168,8 +1204,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container</code> class should have a <code>grid-template-columns</code> property with <code>repeat</code> and <code>auto-fill</code> that will fill the grid with columns that have a minimum width of <code>60px</code> and maximum of <code>1fr</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?repeat\\s*?\\(\\s*?auto-fill\\s*?,\\s*?minmax\\s*?\\(\\s*?60px\\s*?,\\s*?1fr\\s*?\\)\\s*?\\)\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property with <code>repeat</code> and <code>auto-fill</code> that will fill the grid with columns that have a minimum width of <code>60px</code> and maximum of <code>1fr</code>.');" "<code>container</code> class should have a <code>grid-template-columns</code> property with <code>repeat</code> and <code>auto-fill</code> that will fill the grid with columns that have a minimum width of <code>60px</code> and maximum of <code>1fr</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?repeat\\s*?\\(\\s*?auto-fill\\s*?,\\s*?minmax\\s*?\\(\\s*?60px\\s*?,\\s*?1fr\\s*?\\)\\s*?\\)\\s*?;[\\s\\S]*}/gi), '<code>container</code> class should have a <code>grid-template-columns</code> property with <code>repeat</code> and <code>auto-fill</code> that will fill the grid with columns that have a minimum width of <code>60px</code> and maximum of <code>1fr</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1247,8 +1285,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>container2</code> class should have a <code>grid-template-columns</code> property with <code>repeat</code> and <code>auto-fit</code> that will fill the grid with columns that have a minimum width of <code>60px</code> and maximum of <code>1fr</code>.", "text":
"testString": "assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?repeat\\s*?\\(\\s*?auto-fit\\s*?,\\s*?minmax\\s*?\\(\\s*?60px\\s*?,\\s*?1fr\\s*?\\)\\s*?\\)\\s*?;[\\s\\S]*}/gi), '<code>container2</code> class should have a <code>grid-template-columns</code> property with <code>repeat</code> and <code>auto-fit</code> that will fill the grid with columns that have a minimum width of <code>60px</code> and maximum of <code>1fr</code>.');" "<code>container2</code> class should have a <code>grid-template-columns</code> property with <code>repeat</code> and <code>auto-fit</code> that will fill the grid with columns that have a minimum width of <code>60px</code> and maximum of <code>1fr</code>.",
"testString":
"assert(code.match(/.container\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?repeat\\s*?\\(\\s*?auto-fit\\s*?,\\s*?minmax\\s*?\\(\\s*?60px\\s*?,\\s*?1fr\\s*?\\)\\s*?\\)\\s*?;[\\s\\S]*}/gi), '<code>container2</code> class should have a <code>grid-template-columns</code> property with <code>repeat</code> and <code>auto-fit</code> that will fill the grid with columns that have a minimum width of <code>60px</code> and maximum of <code>1fr</code>.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1327,8 +1367,10 @@
], ],
"tests": [ "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 footer and header areas occupy the top and bottom rows respectively and advert and content occupy the left and right columns of the middle row.", "text":
"testString": "assert(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), '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 footer and header areas occupy the top and bottom rows respectively and advert and content occupy the left and right columns of the middle row.');" "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 footer and header areas occupy the top and bottom rows respectively and advert and content occupy the left and right columns of the middle row.",
"testString":
"assert(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), '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 footer and header areas occupy the top and bottom rows respectively and advert and content occupy the left and right columns of the middle row.');"
} }
], ],
"solutions": [], "solutions": [],
@ -1427,12 +1469,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>item3</code> class should have a <code>grid-template-columns</code> property with <code>auto</code> and <code>1fr</code> as values.", "text":
"testString": "assert(code.match(/.item3\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?auto\\s*?1fr\\s*?;[\\s\\S]*}/gi), '<code>item3</code> class should have a <code>grid-template-columns</code> property with <code>auto</code> and <code>1fr</code> as values.');" "<code>item3</code> class should have a <code>grid-template-columns</code> property with <code>auto</code> and <code>1fr</code> as values.",
"testString":
"assert(code.match(/.item3\\s*?{[\\s\\S]*grid-template-columns\\s*?:\\s*?auto\\s*?1fr\\s*?;[\\s\\S]*}/gi), '<code>item3</code> class should have a <code>grid-template-columns</code> property with <code>auto</code> and <code>1fr</code> as values.');"
}, },
{ {
"text": "<code>item3</code> class should have a <code>display</code> property with the value of <code>grid</code>.", "text":
"testString": "assert(code.match(/.item3\\s*?{[\\s\\S]*display\\s*?:\\s*?grid\\s*?;[\\s\\S]*}/gi), '<code>item3</code> class should have a <code>display</code> property with the value of <code>grid</code>.');" "<code>item3</code> class should have a <code>display</code> property with the value of <code>grid</code>.",
"testString":
"assert(code.match(/.item3\\s*?{[\\s\\S]*display\\s*?:\\s*?grid\\s*?;[\\s\\S]*}/gi), '<code>item3</code> class should have a <code>display</code> property with the value of <code>grid</code>.');"
} }
], ],
"solutions": [], "solutions": [],

View File

@ -20,12 +20,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your <code>p</code> element should have the <code>font-size</code> of 10px when the device <code>height</code> is less than or equal to 800px.", "text":
"testString": "assert($('p').css('font-size') == '10px', 'Your <code>p</code> element should have the <code>font-size</code> of 10px when the device <code>height</code> is less than or equal to 800px.');" "Your <code>p</code> element should have the <code>font-size</code> of 10px when the device <code>height</code> is less than or equal to 800px.",
"testString":
"assert($('p').css('font-size') == '10px', 'Your <code>p</code> element should have the <code>font-size</code> of 10px when the device <code>height</code> is less than or equal to 800px.');"
}, },
{ {
"text": "Declare a <code>@media</code> query for devices with a <code>height</code> less than or equal to 800px.", "text":
"testString": "assert(code.match(/@media\\s?\\(max-height:\\s*?800px\\)/g), 'Declare a <code>@media</code> query for devices with a <code>height</code> less than or equal to 800px.');" "Declare a <code>@media</code> query for devices with a <code>height</code> less than or equal to 800px.",
"testString":
"assert(code.match(/@media\\s?\\(max-height:\\s*?800px\\)/g), 'Declare a <code>@media</code> query for devices with a <code>height</code> less than or equal to 800px.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -75,23 +79,29 @@
"Making images responsive with CSS is actually very simple. Instead of applying an absolute width to an element:", "Making images responsive with CSS is actually very simple. Instead of applying an absolute width to an element:",
"<code>img { width: 720px; }</code>", "<code>img { width: 720px; }</code>",
"You can use:", "You can use:",
"<blockquote>img {<br> max-width: 100%;<br> display: block;<br> height: auto;<br>}</blockquote>", "<blockquote>img {<br>&nbsp;&nbsp;max-width: 100%;<br>&nbsp;&nbsp;display: block;<br>&nbsp;&nbsp;height: auto;<br>}</blockquote>",
"The <code>max-width</code> property of 100% scales the image to fit the width of its container, but the image won't stretch wider than its original width. Setting the <code>display</code> property to block changes the image from an inline element (its default), to a block element on its own line. The <code>height</code> property of auto keeps the original aspect ratio of the image.", "The <code>max-width</code> property of 100% scales the image to fit the width of its container, but the image won't stretch wider than its original width. Setting the <code>display</code> property to block changes the image from an inline element (its default), to a block element on its own line. The <code>height</code> property of auto keeps the original aspect ratio of the image.",
"<hr>", "<hr>",
"Add style rules for the <code>img</code> tag to make it responsive to the size of its container. It should display as a block-level element, it should fit the full width of its container without stretching, and it should keep its original aspect ratio." "Add style rules for the <code>img</code> tag to make it responsive to the size of its container. It should display as a block-level element, it should fit the full width of its container without stretching, and it should keep its original aspect ratio."
], ],
"tests": [ "tests": [
{ {
"text": "Your <code>img</code> tag should have a <code>max-width</code> set to 100%.", "text":
"testString": "assert(code.match(/max-width:\\s*?100%;/g), 'Your <code>img</code> tag should have a <code>max-width</code> set to 100%.');" "Your <code>img</code> tag should have a <code>max-width</code> set to 100%.",
"testString":
"assert(code.match(/max-width:\\s*?100%;/g), 'Your <code>img</code> tag should have a <code>max-width</code> set to 100%.');"
}, },
{ {
"text": "Your <code>img</code> tag should have a <code>display</code> set to block.", "text":
"testString": "assert($('img').css('display') == 'block', 'Your <code>img</code> tag should have a <code>display</code> set to block.');" "Your <code>img</code> tag should have a <code>display</code> set to block.",
"testString":
"assert($('img').css('display') == 'block', 'Your <code>img</code> tag should have a <code>display</code> set to block.');"
}, },
{ {
"text": "Your <code>img</code> tag should have a <code>height</code> set to auto.", "text":
"testString": "assert(code.match(/height:\\s*?auto;/g), 'Your <code>img</code> tag should have a <code>height</code> set to auto.');" "Your <code>img</code> tag should have a <code>height</code> set to auto.",
"testString":
"assert(code.match(/height:\\s*?auto;/g), 'Your <code>img</code> tag should have a <code>height</code> set to auto.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -105,7 +115,7 @@
"Fazer com que imagens sejam responsivas é muito simples com CSS. Ao invés de aplicar uma largura absoluta a um elemento:", "Fazer com que imagens sejam responsivas é muito simples com CSS. Ao invés de aplicar uma largura absoluta a um elemento:",
"<code>img { width: 720px; }</code>", "<code>img { width: 720px; }</code>",
"Você pode usar:", "Você pode usar:",
"<blockquote>img {<br> max-width: 100%;<br> display: block;<br> height: auto;<br>}</blockquote>", "<blockquote>img {<br>&nbsp;&nbsp;max-width: 100%;<br>&nbsp;&nbsp;display: block;<br>&nbsp;&nbsp;height: auto;<br>}</blockquote>",
"A propriedade <code>max-width</code> em 100% ajusta o tamanho da imagem para preencher a largura de seu container, mas a imagem não irá esticar mais que sua largura original. Ajustando a propriedade <code>display</code> como block muda imagem de elemento inline (o padrão) para um elemento block com uma linha própria. A propriedade <code>height</code> na configuração auto mantem a proporção original da imagem.", "A propriedade <code>max-width</code> em 100% ajusta o tamanho da imagem para preencher a largura de seu container, mas a imagem não irá esticar mais que sua largura original. Ajustando a propriedade <code>display</code> como block muda imagem de elemento inline (o padrão) para um elemento block com uma linha própria. A propriedade <code>height</code> na configuração auto mantem a proporção original da imagem.",
"<hr>", "<hr>",
"Adicione regras de estilo para a tag <code>img</code> para torná-la responsiva com relação ao tamanho do seu container. Ela deve ser exibida como um elemento de nível block, e deve preencher toda a largura de seu container sem esticar, mantendo as proporções originais." "Adicione regras de estilo para a tag <code>img</code> para torná-la responsiva com relação ao tamanho do seu container. Ela deve ser exibida como um elemento de nível block, e deve preencher toda a largura de seu container sem esticar, mantendo as proporções originais."
@ -135,18 +145,22 @@
"description": [ "description": [
"The simplest way to make your images appear \"retina\" (and optimize them for retina displays) is to define their <code>width</code> and <code>height</code> values as only half of what the original file is.", "The simplest way to make your images appear \"retina\" (and optimize them for retina displays) is to define their <code>width</code> and <code>height</code> values as only half of what the original file is.",
"Here is an example of an image that is only using half of the original height and width:", "Here is an example of an image that is only using half of the original height and width:",
"<blockquote>&lt;style&gt;<br> img { height: 250px; width: 250px; }<br>&lt;/style&gt;<br>&lt;img src=&quot;coolPic500x500&quot; alt=&quot;A most excellent picture&quot;&gt;</blockquote>", "<blockquote>&lt;style&gt;<br>&nbsp;&nbsp;img { height: 250px; width: 250px; }<br>&lt;/style&gt;<br>&lt;img src=&quot;coolPic500x500&quot; alt=&quot;A most excellent picture&quot;&gt;</blockquote>",
"<hr>", "<hr>",
"Set the <code>width</code> and <code>height</code> of the <code>img</code> tag to half of their original values. In this case, both the original <code>height</code> and the original <code>width</code> are 200px." "Set the <code>width</code> and <code>height</code> of the <code>img</code> tag to half of their original values. In this case, both the original <code>height</code> and the original <code>width</code> are 200px."
], ],
"tests": [ "tests": [
{ {
"text": "Your <code>img</code> tag should have a <code>width</code> of 100 pixels.", "text":
"testString": "assert($('img').css('width') == '100px', 'Your <code>img</code> tag should have a <code>width</code> of 100 pixels.');" "Your <code>img</code> tag should have a <code>width</code> of 100 pixels.",
"testString":
"assert($('img').css('width') == '100px', 'Your <code>img</code> tag should have a <code>width</code> of 100 pixels.');"
}, },
{ {
"text": "Your <code>img</code> tag should have a <code>height</code> of 100 pixels.", "text":
"testString": "assert($('img').css('height') == '100px', 'Your <code>img</code> tag should have a <code>height</code> of 100 pixels.');" "Your <code>img</code> tag should have a <code>height</code> of 100 pixels.",
"testString":
"assert($('img').css('height') == '100px', 'Your <code>img</code> tag should have a <code>height</code> of 100 pixels.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -159,7 +173,7 @@
"description": [ "description": [
"A maneira mais simples de fazer com que suas imagens tenham uma aparência \"retina\" (e otimizadas para telas retina) é definindo seus valores de <code>width</code> e <code>height</code> como somente metade do tamanho original dos arquivos.", "A maneira mais simples de fazer com que suas imagens tenham uma aparência \"retina\" (e otimizadas para telas retina) é definindo seus valores de <code>width</code> e <code>height</code> como somente metade do tamanho original dos arquivos.",
"Segue um exemplo de imagem que possui somente metade dos valores originais de altura e largura:", "Segue um exemplo de imagem que possui somente metade dos valores originais de altura e largura:",
"<blockquote>&lt;style&gt;<br> img { height: 250px; width: 250px; }<br>&lt;/style&gt;<br>&lt;img src=&quot;coolPic500x500&quot; alt=&quot;A most excellent picture&quot;&gt;</blockquote>", "<blockquote>&lt;style&gt;<br>&nbsp;&nbsp;img { height: 250px; width: 250px; }<br>&lt;/style&gt;<br>&lt;img src=&quot;coolPic500x500&quot; alt=&quot;A most excellent picture&quot;&gt;</blockquote>",
"<hr>", "<hr>",
"Configure os valores de <code>width</code> e <code>height</code> da tag <code>img</code> como metade do seu tamanho original. Nesse caso, o valor original de <code>height</code> e o valor original de <code>width</code> são de 200px." "Configure os valores de <code>width</code> e <code>height</code> da tag <code>img</code> como metade do seu tamanho original. Nesse caso, o valor original de <code>height</code> e o valor original de <code>width</code> são de 200px."
] ]
@ -194,12 +208,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your <code>h2</code> tag should have a <code>width</code> of 80vw.", "text":
"testString": "assert(code.match(/h2\\s*?{\\s*?width:\\s*?80vw;\\s*?}/g), 'Your <code>h2</code> tag should have a <code>width</code> of 80vw.');" "Your <code>h2</code> tag should have a <code>width</code> of 80vw.",
"testString":
"assert(code.match(/h2\\s*?{\\s*?width:\\s*?80vw;\\s*?}/g), 'Your <code>h2</code> tag should have a <code>width</code> of 80vw.');"
}, },
{ {
"text": "Your <code>p</code> tag should have a <code>width</code> of 75vmin.", "text":
"testString": "assert(code.match(/p\\s*?{\\s*?width:\\s*?75vmin;\\s*?}/g), 'Your <code>p</code> tag should have a <code>width</code> of 75vmin.');" "Your <code>p</code> tag should have a <code>width</code> of 75vmin.",
"testString":
"assert(code.match(/p\\s*?{\\s*?width:\\s*?75vmin;\\s*?}/g), 'Your <code>p</code> tag should have a <code>width</code> of 75vmin.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",

View File

@ -12,30 +12,38 @@
"<blockquote>let simpleArray = ['one', 2, 'three, true, false, undefined, null];<br>console.log(simpleArray.length);<br>// logs 7</blockquote>", "<blockquote>let simpleArray = ['one', 2, 'three, true, false, undefined, null];<br>console.log(simpleArray.length);<br>// logs 7</blockquote>",
"All array's have a length property, which as shown above, can be very easily accessed with the syntax <code>Array.length</code>.", "All array's have a length property, which as shown above, can be very easily accessed with the syntax <code>Array.length</code>.",
"A more complex implementation of an array can be seen below. This is known as a <dfn>multi-dimensional array</dfn>, or an array that contains other arrays. Notice that this array also contains JavaScript <dfn>objects</dfn>, which we will examine very closely in our next section, but for now, all you need to know is that arrays are also capable of storing complex objects.", "A more complex implementation of an array can be seen below. This is known as a <dfn>multi-dimensional array</dfn>, or an array that contains other arrays. Notice that this array also contains JavaScript <dfn>objects</dfn>, which we will examine very closely in our next section, but for now, all you need to know is that arrays are also capable of storing complex objects.",
"<blockquote>let complexArray = [<br> [<br> {<br> one: 1,<br> two: 2<br> },<br> {<br> three: 3,<br> four: 4<br> }<br> ],<br> [<br> {<br> a: \"a\",<br> b: \"b\"<br> },<br> {<br> c: \"c\",<br> d: “d”<br> }<br> ]<br>];</blockquote>", "<blockquote>let complexArray = [<br>&nbsp;&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;one: 1,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;two: 2<br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;three: 3,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;four: 4<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;],<br>&nbsp;&nbsp;[<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a: \"a\",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b: \"b\"<br>&nbsp;&nbsp;&nbsp;&nbsp;},<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;c: \"c\",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;d: “d”<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;]<br>];</blockquote>",
"<hr>", "<hr>",
"We have defined a variable called <code>yourArray</code>. Complete the statement by assigning an array of at least 5 elements in length to the <code>yourArray</code> variable. Your array should contain at least one <dfn>string</dfn>, one <dfn>number</dfn>, and one <dfn>boolean</dfn>." "We have defined a variable called <code>yourArray</code>. Complete the statement by assigning an array of at least 5 elements in length to the <code>yourArray</code> variable. Your array should contain at least one <dfn>string</dfn>, one <dfn>number</dfn>, and one <dfn>boolean</dfn>."
], ],
"tests": [ "tests": [
{ {
"text": "yourArray is an array", "text": "yourArray is an array",
"testString": "assert.strictEqual(Array.isArray(yourArray), true, 'yourArray is an array');" "testString":
"assert.strictEqual(Array.isArray(yourArray), true, 'yourArray is an array');"
}, },
{ {
"text": "<code>yourArray</code> is at least 5 elements long", "text": "<code>yourArray</code> is at least 5 elements long",
"testString": "assert.isAtLeast(yourArray.length, 5, '<code>yourArray</code> is at least 5 elements long');" "testString":
"assert.isAtLeast(yourArray.length, 5, '<code>yourArray</code> is at least 5 elements long');"
}, },
{ {
"text": "<code>yourArray</code> contains at least one <code>boolean</code>", "text":
"testString": "assert(yourArray.filter( el => typeof el === 'boolean').length >= 1, '<code>yourArray</code> contains at least one <code>boolean</code>');" "<code>yourArray</code> contains at least one <code>boolean</code>",
"testString":
"assert(yourArray.filter( el => typeof el === 'boolean').length >= 1, '<code>yourArray</code> contains at least one <code>boolean</code>');"
}, },
{ {
"text": "<code>yourArray</code> contains at least one <code>number</code>", "text":
"testString": "assert(yourArray.filter( el => typeof el === 'number').length >= 1, '<code>yourArray</code> contains at least one <code>number</code>');" "<code>yourArray</code> contains at least one <code>number</code>",
"testString":
"assert(yourArray.filter( el => typeof el === 'number').length >= 1, '<code>yourArray</code> contains at least one <code>number</code>');"
}, },
{ {
"text": "<code>yourArray</code> contains at least one <code>string</code>", "text":
"testString": "assert(yourArray.filter( el => typeof el === 'string').length >= 1, '<code>yourArray</code> contains at least one <code>string</code>');" "<code>yourArray</code> contains at least one <code>string</code>",
"testString":
"assert(yourArray.filter( el => typeof el === 'string').length >= 1, '<code>yourArray</code> contains at least one <code>string</code>');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -47,9 +55,7 @@
"key": "indexjs", "key": "indexjs",
"ext": "js", "ext": "js",
"name": "index", "name": "index",
"contents": [ "contents": ["let yourArray; // change this line"],
"let yourArray; // change this line"
],
"head": [], "head": [],
"tail": [] "tail": []
} }
@ -75,19 +81,24 @@
"tests": [ "tests": [
{ {
"text": "<code>myArray[0]</code> is equal to <code>\"a\"</code>", "text": "<code>myArray[0]</code> is equal to <code>\"a\"</code>",
"testString": "assert.strictEqual(myArray[0], \"a\", '<code>myArray[0]</code> is equal to <code>\"a\"</code>');" "testString":
"assert.strictEqual(myArray[0], \"a\", '<code>myArray[0]</code> is equal to <code>\"a\"</code>');"
}, },
{ {
"text": "<code>myArray[1]</code> is no longer set to <code>\"b\"</code>", "text":
"testString": "assert.notStrictEqual(myArray[1], \"b\", '<code>myArray[1]</code> is no longer set to <code>\"b\"</code>');" "<code>myArray[1]</code> is no longer set to <code>\"b\"</code>",
"testString":
"assert.notStrictEqual(myArray[1], \"b\", '<code>myArray[1]</code> is no longer set to <code>\"b\"</code>');"
}, },
{ {
"text": "<code>myArray[2]</code> is equal to <code>\"c\"</code>", "text": "<code>myArray[2]</code> is equal to <code>\"c\"</code>",
"testString": "assert.strictEqual(myArray[2], \"c\", '<code>myArray[2]</code> is equal to <code>\"c\"</code>');" "testString":
"assert.strictEqual(myArray[2], \"c\", '<code>myArray[2]</code> is equal to <code>\"c\"</code>');"
}, },
{ {
"text": "<code>myArray[3]</code> is equal to <code>\"d\"</code>", "text": "<code>myArray[3]</code> is equal to <code>\"d\"</code>",
"testString": "assert.strictEqual(myArray[3], \"d\", '<code>myArray[3]</code> is equal to <code>\"d\"</code>');" "testString":
"assert.strictEqual(myArray[3], \"d\", '<code>myArray[3]</code> is equal to <code>\"d\"</code>');"
} }
], ],
"solutions": [], "solutions": [],
@ -123,16 +134,22 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>mixedNumbers([\"IV\", 5, \"six\"])</code> should now return <code>[\"I\", 2, \"three\", \"IV\", 5, \"six\", 7, \"VIII\", 9]</code>", "text":
"testString": "assert.deepEqual(mixedNumbers(['IV', 5, 'six']), ['I', 2, 'three', 'IV', 5, 'six', 7, 'VIII', 9], '<code>mixedNumbers([\"IV\", 5, \"six\"])</code> should now return <code>[\"I\", 2, \"three\", \"IV\", 5, \"six\", 7, \"VIII\", 9]</code>');" "<code>mixedNumbers([\"IV\", 5, \"six\"])</code> should now return <code>[\"I\", 2, \"three\", \"IV\", 5, \"six\", 7, \"VIII\", 9]</code>",
"testString":
"assert.deepEqual(mixedNumbers(['IV', 5, 'six']), ['I', 2, 'three', 'IV', 5, 'six', 7, 'VIII', 9], '<code>mixedNumbers([\"IV\", 5, \"six\"])</code> should now return <code>[\"I\", 2, \"three\", \"IV\", 5, \"six\", 7, \"VIII\", 9]</code>');"
}, },
{ {
"text": "The <code>mixedNumbers</code> function should utilize the <code>push()</code> method", "text":
"testString": "assert.notStrictEqual(mixedNumbers.toString().search(/\\.push\\(/), -1, 'The <code>mixedNumbers</code> function should utilize the <code>push()</code> method');" "The <code>mixedNumbers</code> function should utilize the <code>push()</code> method",
"testString":
"assert.notStrictEqual(mixedNumbers.toString().search(/\\.push\\(/), -1, 'The <code>mixedNumbers</code> function should utilize the <code>push()</code> method');"
}, },
{ {
"text": "The <code>mixedNumbers</code> function should utilize the <code>unshift()</code> method", "text":
"testString": "assert.notStrictEqual(mixedNumbers.toString().search(/\\.unshift\\(/), -1, 'The <code>mixedNumbers</code> function should utilize the <code>unshift()</code> method');" "The <code>mixedNumbers</code> function should utilize the <code>unshift()</code> method",
"testString":
"assert.notStrictEqual(mixedNumbers.toString().search(/\\.unshift\\(/), -1, 'The <code>mixedNumbers</code> function should utilize the <code>unshift()</code> method');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -174,16 +191,22 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>popShift([\"challenge\", \"is\", \"not\", \"complete\"])</code> should return <code>[\"challenge\", \"complete\"]</code>", "text":
"testString": "assert.deepEqual(popShift(['challenge', 'is', 'not', 'complete']), [\"challenge\", \"complete\"], '<code>popShift([\"challenge\", \"is\", \"not\", \"complete\"])</code> should return <code>[\"challenge\", \"complete\"]</code>');" "<code>popShift([\"challenge\", \"is\", \"not\", \"complete\"])</code> should return <code>[\"challenge\", \"complete\"]</code>",
"testString":
"assert.deepEqual(popShift(['challenge', 'is', 'not', 'complete']), [\"challenge\", \"complete\"], '<code>popShift([\"challenge\", \"is\", \"not\", \"complete\"])</code> should return <code>[\"challenge\", \"complete\"]</code>');"
}, },
{ {
"text": "The <code>popShift</code> function should utilize the <code>pop()</code> method", "text":
"testString": "assert.notStrictEqual(popShift.toString().search(/\\.pop\\(/), -1, 'The <code>popShift</code> function should utilize the <code>pop()</code> method');" "The <code>popShift</code> function should utilize the <code>pop()</code> method",
"testString":
"assert.notStrictEqual(popShift.toString().search(/\\.pop\\(/), -1, 'The <code>popShift</code> function should utilize the <code>pop()</code> method');"
}, },
{ {
"text": "The <code>popShift</code> function should utilize the <code>shift()</code> method", "text":
"testString": "assert.notStrictEqual(popShift.toString().search(/\\.shift\\(/), -1, 'The <code>popShift</code> function should utilize the <code>shift()</code> method');" "The <code>popShift</code> function should utilize the <code>shift()</code> method",
"testString":
"assert.notStrictEqual(popShift.toString().search(/\\.shift\\(/), -1, 'The <code>popShift</code> function should utilize the <code>shift()</code> method');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -225,11 +248,14 @@
"tests": [ "tests": [
{ {
"text": "<code>sumOfTen</code> should return 10", "text": "<code>sumOfTen</code> should return 10",
"testString": "assert.strictEqual(sumOfTen([2, 5, 1, 5, 2, 1]), 10, '<code>sumOfTen</code> should return 10');" "testString":
"assert.strictEqual(sumOfTen([2, 5, 1, 5, 2, 1]), 10, '<code>sumOfTen</code> should return 10');"
}, },
{ {
"text": "The <code>sumOfTen</code> function should utilize the <code>splice()</code> method", "text":
"testString": "assert.notStrictEqual(sumOfTen.toString().search(/\\.splice\\(/), -1, 'The <code>sumOfTen</code> function should utilize the <code>splice()</code> method');" "The <code>sumOfTen</code> function should utilize the <code>splice()</code> method",
"testString":
"assert.notStrictEqual(sumOfTen.toString().search(/\\.splice\\(/), -1, 'The <code>sumOfTen</code> function should utilize the <code>splice()</code> method');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -269,20 +295,27 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>htmlColorNames</code> should return <code>[\"DarkSalmon\", \"BlanchedAlmond\", \"LavenderBlush\", \"PaleTurqoise\", \"FireBrick\"]</code>", "text":
"testString": "assert.deepEqual(htmlColorNames(['DarkGoldenRod', 'WhiteSmoke', 'LavenderBlush', 'PaleTurqoise', 'FireBrick']), ['DarkSalmon', 'BlanchedAlmond', 'LavenderBlush', 'PaleTurqoise', 'FireBrick'], '<code>htmlColorNames</code> should return <code>[\"DarkSalmon\", \"BlanchedAlmond\", \"LavenderBlush\", \"PaleTurqoise\", \"FireBrick\"]</code>');" "<code>htmlColorNames</code> should return <code>[\"DarkSalmon\", \"BlanchedAlmond\", \"LavenderBlush\", \"PaleTurqoise\", \"FireBrick\"]</code>",
"testString":
"assert.deepEqual(htmlColorNames(['DarkGoldenRod', 'WhiteSmoke', 'LavenderBlush', 'PaleTurqoise', 'FireBrick']), ['DarkSalmon', 'BlanchedAlmond', 'LavenderBlush', 'PaleTurqoise', 'FireBrick'], '<code>htmlColorNames</code> should return <code>[\"DarkSalmon\", \"BlanchedAlmond\", \"LavenderBlush\", \"PaleTurqoise\", \"FireBrick\"]</code>');"
}, },
{ {
"text": "The <code>htmlColorNames</code> function should utilize the <code>splice()</code> method", "text":
"testString": "assert(/.splice/.test(code), 'The <code>htmlColorNames</code> function should utilize the <code>splice()</code> method');" "The <code>htmlColorNames</code> function should utilize the <code>splice()</code> method",
"testString":
"assert(/.splice/.test(code), 'The <code>htmlColorNames</code> function should utilize the <code>splice()</code> method');"
}, },
{ {
"text": "You should not use <code>shift()</code> or <code>unshift()</code>.", "text":
"testString": "assert(!/shift|unshift/.test(code), 'You should not use <code>shift()</code> or <code>unshift()</code>.');" "You should not use <code>shift()</code> or <code>unshift()</code>.",
"testString":
"assert(!/shift|unshift/.test(code), 'You should not use <code>shift()</code> or <code>unshift()</code>.');"
}, },
{ {
"text": "You should not use array bracket notation.", "text": "You should not use array bracket notation.",
"testString": "assert(!/\\[\\d\\]\\s*=/.test(code), 'You should not use array bracket notation.');" "testString":
"assert(!/\\[\\d\\]\\s*=/.test(code), 'You should not use array bracket notation.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -322,12 +355,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>forecast</code> should return <code>[\"warm\", \"sunny\"]", "text":
"testString": "assert.deepEqual(forecast(['cold', 'rainy', 'warm', 'sunny', 'cool', 'thunderstorms']), ['warm', 'sunny'], '<code>forecast</code> should return <code>[\"warm\", \"sunny\"]');" "<code>forecast</code> should return <code>[\"warm\", \"sunny\"]",
"testString":
"assert.deepEqual(forecast(['cold', 'rainy', 'warm', 'sunny', 'cool', 'thunderstorms']), ['warm', 'sunny'], '<code>forecast</code> should return <code>[\"warm\", \"sunny\"]');"
}, },
{ {
"text": "The <code>forecast</code> function should utilize the <code>slice()</code> method", "text":
"testString": "assert(/\\.slice\\(/.test(code), 'The <code>forecast</code> function should utilize the <code>slice()</code> method');" "The <code>forecast</code> function should utilize the <code>slice()</code> method",
"testString":
"assert(/\\.slice\\(/.test(code), 'The <code>forecast</code> function should utilize the <code>slice()</code> method');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -366,24 +403,34 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>copyMachine([true, false, true], 2)</code> should return <code>[[true, false, true], [true, false, true]]</code>", "text":
"testString": "assert.deepEqual(copyMachine([true, false, true], 2), [[true, false, true], [true, false, true]], '<code>copyMachine([true, false, true], 2)</code> should return <code>[[true, false, true], [true, false, true]]</code>');" "<code>copyMachine([true, false, true], 2)</code> should return <code>[[true, false, true], [true, false, true]]</code>",
"testString":
"assert.deepEqual(copyMachine([true, false, true], 2), [[true, false, true], [true, false, true]], '<code>copyMachine([true, false, true], 2)</code> should return <code>[[true, false, true], [true, false, true]]</code>');"
}, },
{ {
"text": "<code>copyMachine([1, 2, 3], 5)</code> should return <code>[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]</code>", "text":
"testString": "assert.deepEqual(copyMachine([1, 2, 3], 5), [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], '<code>copyMachine([1, 2, 3], 5)</code> should return <code>[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]</code>');" "<code>copyMachine([1, 2, 3], 5)</code> should return <code>[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]</code>",
"testString":
"assert.deepEqual(copyMachine([1, 2, 3], 5), [[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]], '<code>copyMachine([1, 2, 3], 5)</code> should return <code>[[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3]]</code>');"
}, },
{ {
"text": "<code>copyMachine([true, true, null], 1)</code> should return <code>[[true, true, null]]</code>", "text":
"testString": "assert.deepEqual(copyMachine([true, true, null], 1), [[true, true, null]], '<code>copyMachine([true, true, null], 1)</code> should return <code>[[true, true, null]]</code>');" "<code>copyMachine([true, true, null], 1)</code> should return <code>[[true, true, null]]</code>",
"testString":
"assert.deepEqual(copyMachine([true, true, null], 1), [[true, true, null]], '<code>copyMachine([true, true, null], 1)</code> should return <code>[[true, true, null]]</code>');"
}, },
{ {
"text": "<code>copyMachine([\"it works\"], 3)</code> should return <code>[[\"it works\"], [\"it works\"], [\"it works\"]]</code>", "text":
"testString": "assert.deepEqual(copyMachine(['it works'], 3), [['it works'], ['it works'], ['it works']], '<code>copyMachine([\"it works\"], 3)</code> should return <code>[[\"it works\"], [\"it works\"], [\"it works\"]]</code>');" "<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']], '<code>copyMachine([\"it works\"], 3)</code> should return <code>[[\"it works\"], [\"it works\"], [\"it works\"]]</code>');"
}, },
{ {
"text": "The <code>copyMachine</code> function should utilize the <code>spread operator</code> with array <code>arr</code>", "text":
"testString": "assert.notStrictEqual(copyMachine.toString().indexOf('.concat(_toConsumableArray(arr))'), -1, 'The <code>copyMachine</code> function should utilize the <code>spread operator</code> with array <code>arr</code>');" "The <code>copyMachine</code> function should utilize the <code>spread operator</code> with array <code>arr</code>",
"testString":
"assert.notStrictEqual(copyMachine.toString().indexOf('.concat(_toConsumableArray(arr))'), -1, 'The <code>copyMachine</code> function should utilize the <code>spread operator</code> with array <code>arr</code>');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -427,12 +474,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>spreadOut</code> should return <code>[\"learning\", \"to\", \"code\", \"is\", \"fun\"]</code>", "text":
"testString": "assert.deepEqual(spreadOut(), ['learning', 'to', 'code', 'is', 'fun'], '<code>spreadOut</code> should return <code>[\"learning\", \"to\", \"code\", \"is\", \"fun\"]</code>');" "<code>spreadOut</code> should return <code>[\"learning\", \"to\", \"code\", \"is\", \"fun\"]</code>",
"testString":
"assert.deepEqual(spreadOut(), ['learning', 'to', 'code', 'is', 'fun'], '<code>spreadOut</code> should return <code>[\"learning\", \"to\", \"code\", \"is\", \"fun\"]</code>');"
}, },
{ {
"text": "The <code>spreadOut</code> function should utilize spread syntax", "text":
"testString": "assert.notStrictEqual(spreadOut.toString().search(/[...]/), -1, 'The <code>spreadOut</code> function should utilize spread syntax');" "The <code>spreadOut</code> function should utilize spread syntax",
"testString":
"assert.notStrictEqual(spreadOut.toString().search(/[...]/), -1, 'The <code>spreadOut</code> function should utilize spread syntax');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -471,24 +522,34 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>quickCheck([\"squash\", \"onions\", \"shallots\"], \"mushrooms\")</code> should return <code>false</code>", "text":
"testString": "assert.strictEqual(quickCheck(['squash', 'onions', 'shallots'], 'mushrooms'), false, '<code>quickCheck([\"squash\", \"onions\", \"shallots\"], \"mushrooms\")</code> should return <code>false</code>');" "<code>quickCheck([\"squash\", \"onions\", \"shallots\"], \"mushrooms\")</code> should return <code>false</code>",
"testString":
"assert.strictEqual(quickCheck(['squash', 'onions', 'shallots'], 'mushrooms'), false, '<code>quickCheck([\"squash\", \"onions\", \"shallots\"], \"mushrooms\")</code> should return <code>false</code>');"
}, },
{ {
"text": "<code>quickCheck([\"squash\", \"onions\", \"shallots\"], \"onions\")</code> should return <code>true</code>", "text":
"testString": "assert.strictEqual(quickCheck(['squash', 'onions', 'shallots'], 'onions'), true, '<code>quickCheck([\"squash\", \"onions\", \"shallots\"], \"onions\")</code> should return <code>true</code>');" "<code>quickCheck([\"squash\", \"onions\", \"shallots\"], \"onions\")</code> should return <code>true</code>",
"testString":
"assert.strictEqual(quickCheck(['squash', 'onions', 'shallots'], 'onions'), true, '<code>quickCheck([\"squash\", \"onions\", \"shallots\"], \"onions\")</code> should return <code>true</code>');"
}, },
{ {
"text": "<code>quickCheck([3, 5, 9, 125, 45, 2], 125)</code> should return <code>true</code>", "text":
"testString": "assert.strictEqual(quickCheck([3, 5, 9, 125, 45, 2], 125), true, '<code>quickCheck([3, 5, 9, 125, 45, 2], 125)</code> should return <code>true</code>');" "<code>quickCheck([3, 5, 9, 125, 45, 2], 125)</code> should return <code>true</code>",
"testString":
"assert.strictEqual(quickCheck([3, 5, 9, 125, 45, 2], 125), true, '<code>quickCheck([3, 5, 9, 125, 45, 2], 125)</code> should return <code>true</code>');"
}, },
{ {
"text": "<code>quickCheck([true, false, false], undefined)</code> should return <code>false</code>", "text":
"testString": "assert.strictEqual(quickCheck([true, false, false], undefined), false, '<code>quickCheck([true, false, false], undefined)</code> should return <code>false</code>');" "<code>quickCheck([true, false, false], undefined)</code> should return <code>false</code>",
"testString":
"assert.strictEqual(quickCheck([true, false, false], undefined), false, '<code>quickCheck([true, false, false], undefined)</code> should return <code>false</code>');"
}, },
{ {
"text": "The <code>quickCheck</code> function should utilize the <code>indexOf()</code> method", "text":
"testString": "assert.notStrictEqual(quickCheck.toString().search(/\\.indexOf\\(/), -1, 'The <code>quickCheck</code> function should utilize the <code>indexOf()</code> method');" "The <code>quickCheck</code> function should utilize the <code>indexOf()</code> method",
"testString":
"assert.notStrictEqual(quickCheck.toString().search(/\\.indexOf\\(/), -1, 'The <code>quickCheck</code> function should utilize the <code>indexOf()</code> method');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -528,24 +589,34 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>filteredArray([[10, 8, 3], [14, 6, 23], [3, 18, 6]], 18)</code> should return <code>[ [10, 8, 3], [14, 6, 23] ]</code>", "text":
"testString": "assert.deepEqual(filteredArray([ [10, 8, 3], [14, 6, 23], [3, 18, 6] ], 18), [[10, 8, 3], [14, 6, 23]], '<code>filteredArray([[10, 8, 3], [14, 6, 23], [3, 18, 6]], 18)</code> should return <code>[ [10, 8, 3], [14, 6, 23] ]</code>');" "<code>filteredArray([[10, 8, 3], [14, 6, 23], [3, 18, 6]], 18)</code> should return <code>[ [10, 8, 3], [14, 6, 23] ]</code>",
"testString":
"assert.deepEqual(filteredArray([ [10, 8, 3], [14, 6, 23], [3, 18, 6] ], 18), [[10, 8, 3], [14, 6, 23]], '<code>filteredArray([[10, 8, 3], [14, 6, 23], [3, 18, 6]], 18)</code> should return <code>[ [10, 8, 3], [14, 6, 23] ]</code>');"
}, },
{ {
"text": "<code>filteredArray([ [\"trumpets\", 2], [\"flutes\", 4], [\"saxophones\", 2] ], 2)</code> should return <code>[ [\"flutes\", 4] ]</code>", "text":
"testString": "assert.deepEqual(filteredArray([ ['trumpets', 2], ['flutes', 4], ['saxophones', 2] ], 2), [['flutes', 4]], '<code>filteredArray([ [\"trumpets\", 2], [\"flutes\", 4], [\"saxophones\", 2] ], 2)</code> should return <code>[ [\"flutes\", 4] ]</code>');" "<code>filteredArray([ [\"trumpets\", 2], [\"flutes\", 4], [\"saxophones\", 2] ], 2)</code> should return <code>[ [\"flutes\", 4] ]</code>",
"testString":
"assert.deepEqual(filteredArray([ ['trumpets', 2], ['flutes', 4], ['saxophones', 2] ], 2), [['flutes', 4]], '<code>filteredArray([ [\"trumpets\", 2], [\"flutes\", 4], [\"saxophones\", 2] ], 2)</code> should return <code>[ [\"flutes\", 4] ]</code>');"
}, },
{ {
"text": "<code>filteredArray([ [\"amy\", \"beth\", \"sam\"], [\"dave\", \"sean\", \"peter\"] ], \"peter\")</code> should return <code>[ [\"amy\", \"beth\", \"sam\"] ]</code>", "text":
"testString": "assert.deepEqual(filteredArray([['amy', 'beth', 'sam'], ['dave', 'sean', 'peter']], 'peter'), [['amy', 'beth', 'sam']], '<code>filteredArray([ [\"amy\", \"beth\", \"sam\"], [\"dave\", \"sean\", \"peter\"] ], \"peter\")</code> should return <code>[ [\"amy\", \"beth\", \"sam\"] ]</code>');" "<code>filteredArray([ [\"amy\", \"beth\", \"sam\"], [\"dave\", \"sean\", \"peter\"] ], \"peter\")</code> should return <code>[ [\"amy\", \"beth\", \"sam\"] ]</code>",
"testString":
"assert.deepEqual(filteredArray([['amy', 'beth', 'sam'], ['dave', 'sean', 'peter']], 'peter'), [['amy', 'beth', 'sam']], '<code>filteredArray([ [\"amy\", \"beth\", \"sam\"], [\"dave\", \"sean\", \"peter\"] ], \"peter\")</code> should return <code>[ [\"amy\", \"beth\", \"sam\"] ]</code>');"
}, },
{ {
"text": "<code>filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3)</code> should return <code>[ ]</code>", "text":
"testString": "assert.deepEqual(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3), [], '<code>filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3)</code> should return <code>[ ]</code>');" "<code>filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3)</code> should return <code>[ ]</code>",
"testString":
"assert.deepEqual(filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3), [], '<code>filteredArray([[3, 2, 3], [1, 6, 3], [3, 13, 26], [19, 3, 9]], 3)</code> should return <code>[ ]</code>');"
}, },
{ {
"text": "The <code>filteredArray</code> function should utilize a <code>for</code> loop", "text":
"testString": "assert.notStrictEqual(filteredArray.toString().search(/for/), -1, 'The <code>filteredArray</code> function should utilize a <code>for</code> loop');" "The <code>filteredArray</code> function should utilize a <code>for</code> loop",
"testString":
"assert.notStrictEqual(filteredArray.toString().search(/for/), -1, 'The <code>filteredArray</code> function should utilize a <code>for</code> loop');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -591,24 +662,34 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>myNestedArray</code> should contain only numbers, booleans, and strings as data elements", "text":
"testString": "assert.strictEqual((function(arr) { let flattened = (function flatten(arr) { const flat = [].concat(...arr); return flat.some (Array.isArray) ? flatten(flat) : flat; })(arr); for (let i = 0; i < flattened.length; i++) { if ( typeof flattened[i] !== 'number' && typeof flattened[i] !== 'string' && typeof flattened[i] !== 'boolean') { return false } } return true })(myNestedArray), true, '<code>myNestedArray</code> should contain only numbers, booleans, and strings as data elements');" "<code>myNestedArray</code> should contain only numbers, booleans, and strings as data elements",
"testString":
"assert.strictEqual((function(arr) { let flattened = (function flatten(arr) { const flat = [].concat(...arr); return flat.some (Array.isArray) ? flatten(flat) : flat; })(arr); for (let i = 0; i < flattened.length; i++) { if ( typeof flattened[i] !== 'number' && typeof flattened[i] !== 'string' && typeof flattened[i] !== 'boolean') { return false } } return true })(myNestedArray), true, '<code>myNestedArray</code> should contain only numbers, booleans, and strings as data elements');"
}, },
{ {
"text": "<code>myNestedArray</code> should have exactly 5 levels of depth", "text":
"testString": "assert.strictEqual((function(arr) {let depth = 0;function arrayDepth(array, i, d) { if (Array.isArray(array[i])) { arrayDepth(array[i], 0, d + 1);} else { depth = (d > depth) ? d : depth;}if (i < array.length) { arrayDepth(array, i + 1, d);} }arrayDepth(arr, 0, 0);return depth;})(myNestedArray), 4, '<code>myNestedArray</code> should have exactly 5 levels of depth');" "<code>myNestedArray</code> should have exactly 5 levels of depth",
"testString":
"assert.strictEqual((function(arr) {let depth = 0;function arrayDepth(array, i, d) { if (Array.isArray(array[i])) { arrayDepth(array[i], 0, d + 1);} else { depth = (d > depth) ? d : depth;}if (i < array.length) { arrayDepth(array, i + 1, d);} }arrayDepth(arr, 0, 0);return depth;})(myNestedArray), 4, '<code>myNestedArray</code> should have exactly 5 levels of depth');"
}, },
{ {
"text": "<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deep\"</code> on an array nested 3 levels deep", "text":
"testString": "assert((function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deep').length === 1 && (function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deep')[0] === 2, '<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deep\"</code> on an array nested 3 levels deep');" "<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deep\"</code> on an array nested 3 levels deep",
"testString":
"assert((function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deep').length === 1 && (function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deep')[0] === 2, '<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deep\"</code> on an array nested 3 levels deep');"
}, },
{ {
"text": "<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deeper\"</code> on an array nested 4 levels deep", "text":
"testString": "assert((function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deeper').length === 1 && (function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deeper')[0] === 3, '<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deeper\"</code> on an array nested 4 levels deep');" "<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deeper\"</code> on an array nested 4 levels deep",
"testString":
"assert((function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deeper').length === 1 && (function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deeper')[0] === 3, '<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deeper\"</code> on an array nested 4 levels deep');"
}, },
{ {
"text": "<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deepest\"</code> on an array nested 5 levels deep", "text":
"testString": "assert((function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deepest').length === 1 && (function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deepest')[0] === 4, '<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deepest\"</code> on an array nested 5 levels deep');" "<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deepest\"</code> on an array nested 5 levels deep",
"testString":
"assert((function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deepest').length === 1 && (function howDeep(array, target, depth = 0) {return array.reduce((combined, current) => {if (Array.isArray(current)) { return combined.concat(howDeep(current, target, depth + 1));} else if (current === target) { return combined.concat(depth);} else { return combined;}}, []);})(myNestedArray, 'deepest')[0] === 4, '<code>myNestedArray</code> should contain exactly one occurrence of the string <code>\"deepest\"</code> on an array nested 5 levels deep');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -641,7 +722,7 @@
"title": "Add Key-Value Pairs to JavaScript Objects", "title": "Add Key-Value Pairs to JavaScript Objects",
"description": [ "description": [
"At their most basic, objects are just collections of <dfn>key-value pairs</dfn>, or in other words, pieces of data mapped to unique identifiers that we call <dfn>properties</dfn> or <dfn>keys</dfn>. Let's take a look at a very simple example:", "At their most basic, objects are just collections of <dfn>key-value pairs</dfn>, or in other words, pieces of data mapped to unique identifiers that we call <dfn>properties</dfn> or <dfn>keys</dfn>. Let's take a look at a very simple example:",
"<blockquote>let FCC_User = {<br> username: 'awesome_coder',<br> followers: 572,<br> points: 1741,<br> completedProjects: 15<br>};</blockquote>", "<blockquote>let FCC_User = {<br>&nbsp;&nbsp;username: 'awesome_coder',<br>&nbsp;&nbsp;followers: 572,<br>&nbsp;&nbsp;points: 1741,<br>&nbsp;&nbsp;completedProjects: 15<br>};</blockquote>",
"The above code defines an object called <code>FCC_User</code> that has four <dfn>properties</dfn>, each of which map to a specific value. If we wanted to know the number of <code>followers</code> <code>FCC_User</code> has, we can access that property by writing:", "The above code defines an object called <code>FCC_User</code> that has four <dfn>properties</dfn>, each of which map to a specific value. If we wanted to know the number of <code>followers</code> <code>FCC_User</code> has, we can access that property by writing:",
"<blockquote>let userData = FCC_User.followers;<br>// userData equals 572</blockquote>", "<blockquote>let userData = FCC_User.followers;<br>// userData equals 572</blockquote>",
"This is called <dfn>dot notation</dfn>. Alternatively, we can also access the property with brackets, like so:", "This is called <dfn>dot notation</dfn>. Alternatively, we can also access the property with brackets, like so:",
@ -653,23 +734,32 @@
"tests": [ "tests": [
{ {
"text": "<code>foods</code> is an object", "text": "<code>foods</code> is an object",
"testString": "assert(typeof foods === 'object', '<code>foods</code> is an object');" "testString":
"assert(typeof foods === 'object', '<code>foods</code> is an object');"
}, },
{ {
"text": "The <code>foods</code> object has a key <code>\"bananas\"</code> with a value of <code>13</code>", "text":
"testString": "assert(foods.bananas === 13, 'The <code>foods</code> object has a key <code>\"bananas\"</code> with a value of <code>13</code>');" "The <code>foods</code> object has a key <code>\"bananas\"</code> with a value of <code>13</code>",
"testString":
"assert(foods.bananas === 13, 'The <code>foods</code> object has a key <code>\"bananas\"</code> with a value of <code>13</code>');"
}, },
{ {
"text": "The <code>foods</code> object has a key <code>\"grapes\"</code> with a value of <code>35</code>", "text":
"testString": "assert(foods.grapes === 35, 'The <code>foods</code> object has a key <code>\"grapes\"</code> with a value of <code>35</code>');" "The <code>foods</code> object has a key <code>\"grapes\"</code> with a value of <code>35</code>",
"testString":
"assert(foods.grapes === 35, 'The <code>foods</code> object has a key <code>\"grapes\"</code> with a value of <code>35</code>');"
}, },
{ {
"text": "The <code>foods</code> object has a key <code>\"strawberries\"</code> with a value of <code>27</code>", "text":
"testString": "assert(foods.strawberries === 27, 'The <code>foods</code> object has a key <code>\"strawberries\"</code> with a value of <code>27</code>');" "The <code>foods</code> object has a key <code>\"strawberries\"</code> with a value of <code>27</code>",
"testString":
"assert(foods.strawberries === 27, 'The <code>foods</code> object has a key <code>\"strawberries\"</code> with a value of <code>27</code>');"
}, },
{ {
"text": "The key-value pairs should be set using dot or bracket notation", "text":
"testString": "assert(code.search(/bananas:/) === -1 && code.search(/grapes:/) === -1 && code.search(/strawberries:/) === -1, 'The key-value pairs should be set using dot or bracket notation');" "The key-value pairs should be set using dot or bracket notation",
"testString":
"assert(code.search(/bananas:/) === -1 && code.search(/grapes:/) === -1 && code.search(/strawberries:/) === -1, 'The key-value pairs should be set using dot or bracket notation');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -704,27 +794,35 @@
"title": "Modify an Object Nested Within an Object", "title": "Modify an Object Nested Within an Object",
"description": [ "description": [
"Now let's take a look at a slightly more complex object. Object properties can be nested to an arbitrary depth, and their values can be any type of data supported by JavaScript, including arrays and even other objects. Consider the following:", "Now let's take a look at a slightly more complex object. Object properties can be nested to an arbitrary depth, and their values can be any type of data supported by JavaScript, including arrays and even other objects. Consider the following:",
"<blockquote>let nestedObject = {<br> id: 28802695164,<br> date: 'December 31, 2016',<br> data: {<br> totalUsers: 99,<br> online: 80,<br> onlineStatus: {<br> active: 67,<br> away: 13<br> }<br> }<br>};</blockquote>", "<blockquote>let nestedObject = {<br>&nbsp;&nbsp;id: 28802695164,<br>&nbsp;&nbsp;date: 'December 31, 2016',<br>&nbsp;&nbsp;data: {<br>&nbsp;&nbsp;&nbsp;&nbsp;totalUsers: 99,<br>&nbsp;&nbsp;&nbsp;&nbsp;online: 80,<br>&nbsp;&nbsp;&nbsp;&nbsp;onlineStatus: {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;active: 67,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;away: 13<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}<br>};</blockquote>",
"<code>nestedObject</code> has three unique keys: <code>id</code>, whose value is a number, <code>date</code> whose value is a string, and <code>data</code>, whose value is an object which has yet another object nested within it. While structures can quickly become complex, we can still use the same notations to access the information we need.", "<code>nestedObject</code> has three unique keys: <code>id</code>, whose value is a number, <code>date</code> whose value is a string, and <code>data</code>, whose value is an object which has yet another object nested within it. While structures can quickly become complex, we can still use the same notations to access the information we need.",
"<hr>", "<hr>",
"Here we've defined an object, <code>userActivity</code>, which includes another object nested within it. You can modify properties on this nested object in the same way you modified properties in the last challenge. Set the value of the <code>online</code> key to <code>45</code>." "Here we've defined an object, <code>userActivity</code>, which includes another object nested within it. You can modify properties on this nested object in the same way you modified properties in the last challenge. Set the value of the <code>online</code> key to <code>45</code>."
], ],
"tests": [ "tests": [
{ {
"text": "<code>userActivity</code> has <code>id</code>, <code>date</code> and <code>data</code> properties", "text":
"testString": "assert('id' in userActivity && 'date' in userActivity && 'data' in userActivity, '<code>userActivity</code> has <code>id</code>, <code>date</code> and <code>data</code> properties');" "<code>userActivity</code> has <code>id</code>, <code>date</code> and <code>data</code> properties",
"testString":
"assert('id' in userActivity && 'date' in userActivity && 'data' in userActivity, '<code>userActivity</code> has <code>id</code>, <code>date</code> and <code>data</code> properties');"
}, },
{ {
"text": "<code>userActivity</code> has a <code>data</code> key set to an object with keys <code>totalUsers</code> and <code>online</code>", "text":
"testString": "assert('totalUsers' in userActivity.data && 'online' in userActivity.data, '<code>userActivity</code> has a <code>data</code> key set to an object with keys <code>totalUsers</code> and <code>online</code>');" "<code>userActivity</code> has a <code>data</code> key set to an object with keys <code>totalUsers</code> and <code>online</code>",
"testString":
"assert('totalUsers' in userActivity.data && 'online' in userActivity.data, '<code>userActivity</code> has a <code>data</code> key set to an object with keys <code>totalUsers</code> and <code>online</code>');"
}, },
{ {
"text": "The <code>online</code> property nested in the <code>data</code> key of <code>userActivity</code> should be set to <code>45</code>", "text":
"testString": "assert(userActivity.data.online === 45, 'The <code>online</code> property nested in the <code>data</code> key of <code>userActivity</code> should be set to <code>45</code>');" "The <code>online</code> property nested in the <code>data</code> key of <code>userActivity</code> should be set to <code>45</code>",
"testString":
"assert(userActivity.data.online === 45, 'The <code>online</code> property nested in the <code>data</code> key of <code>userActivity</code> should be set to <code>45</code>');"
}, },
{ {
"text": "The <code>online</code> property is set using dot or bracket notation", "text":
"testString": "assert.strictEqual(code.search(/online: 45/), -1, 'The <code>online</code> property is set using dot or bracket notation');" "The <code>online</code> property is set using dot or bracket notation",
"testString":
"assert.strictEqual(code.search(/online: 45/), -1, 'The <code>online</code> property is set using dot or bracket notation');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -770,23 +868,32 @@
"tests": [ "tests": [
{ {
"text": "<code>checkInventory</code> is a function", "text": "<code>checkInventory</code> is a function",
"testString": "assert.strictEqual(typeof checkInventory, 'function', '<code>checkInventory</code> is a function');" "testString":
"assert.strictEqual(typeof checkInventory, 'function', '<code>checkInventory</code> is a function');"
}, },
{ {
"text": "The <code>foods</code> object should have only the following key-value pairs: <code>apples: 25</code>, <code>oranges: 32</code>, <code>plums: 28</code>, <code>bananas: 13</code>, <code>grapes: 35</code>, <code>strawberries: 27</code>", "text":
"testString": "assert.deepEqual(foods, {apples: 25, oranges: 32, plums: 28, bananas: 13, grapes: 35, strawberries: 27}, 'The <code>foods</code> object should have only the following key-value pairs: <code>apples: 25</code>, <code>oranges: 32</code>, <code>plums: 28</code>, <code>bananas: 13</code>, <code>grapes: 35</code>, <code>strawberries: 27</code>');" "The <code>foods</code> object should have only the following key-value pairs: <code>apples: 25</code>, <code>oranges: 32</code>, <code>plums: 28</code>, <code>bananas: 13</code>, <code>grapes: 35</code>, <code>strawberries: 27</code>",
"testString":
"assert.deepEqual(foods, {apples: 25, oranges: 32, plums: 28, bananas: 13, grapes: 35, strawberries: 27}, 'The <code>foods</code> object should have only the following key-value pairs: <code>apples: 25</code>, <code>oranges: 32</code>, <code>plums: 28</code>, <code>bananas: 13</code>, <code>grapes: 35</code>, <code>strawberries: 27</code>');"
}, },
{ {
"text": "<code>checkInventory(\"apples\")</code> should return <code>25</code>", "text":
"testString": "assert.strictEqual(checkInventory('apples'), 25, '<code>checkInventory(\"apples\")</code> should return <code>25</code>');" "<code>checkInventory(\"apples\")</code> should return <code>25</code>",
"testString":
"assert.strictEqual(checkInventory('apples'), 25, '<code>checkInventory(\"apples\")</code> should return <code>25</code>');"
}, },
{ {
"text": "<code>checkInventory(\"bananas\")</code> should return <code>13</code>", "text":
"testString": "assert.strictEqual(checkInventory('bananas'), 13, '<code>checkInventory(\"bananas\")</code> should return <code>13</code>');" "<code>checkInventory(\"bananas\")</code> should return <code>13</code>",
"testString":
"assert.strictEqual(checkInventory('bananas'), 13, '<code>checkInventory(\"bananas\")</code> should return <code>13</code>');"
}, },
{ {
"text": "<code>checkInventory(\"strawberries\")</code> should return <code>27</code>", "text":
"testString": "assert.strictEqual(checkInventory('strawberries'), 27, '<code>checkInventory(\"strawberries\")</code> should return <code>27</code>');" "<code>checkInventory(\"strawberries\")</code> should return <code>27</code>",
"testString":
"assert.strictEqual(checkInventory('strawberries'), 27, '<code>checkInventory(\"strawberries\")</code> should return <code>27</code>');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -835,12 +942,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "The <code>foods</code> object only has three keys: <code>apples</code>, <code>grapes</code>, and <code>bananas</code>", "text":
"testString": "assert(!foods.hasOwnProperty('oranges') && !foods.hasOwnProperty('plums') && !foods.hasOwnProperty('strawberries') && Object.keys(foods).length === 3, 'The <code>foods</code> object only has three keys: <code>apples</code>, <code>grapes</code>, and <code>bananas</code>');" "The <code>foods</code> object only has three keys: <code>apples</code>, <code>grapes</code>, and <code>bananas</code>",
"testString":
"assert(!foods.hasOwnProperty('oranges') && !foods.hasOwnProperty('plums') && !foods.hasOwnProperty('strawberries') && Object.keys(foods).length === 3, 'The <code>foods</code> object only has three keys: <code>apples</code>, <code>grapes</code>, and <code>bananas</code>');"
}, },
{ {
"text": "The <code>oranges</code>, <code>plums</code>, and <code>strawberries</code> keys are removed using <code>delete</code>", "text":
"testString": "assert(code.search(/oranges:/) !== -1 && code.search(/plums:/) !== -1 && code.search(/strawberries:/) !== -1, 'The <code>oranges</code>, <code>plums</code>, and <code>strawberries</code> keys are removed using <code>delete</code>');" "The <code>oranges</code>, <code>plums</code>, and <code>strawberries</code> keys are removed using <code>delete</code>",
"testString":
"assert(code.search(/oranges:/) !== -1 && code.search(/plums:/) !== -1 && code.search(/strawberries:/) !== -1, 'The <code>oranges</code>, <code>plums</code>, and <code>strawberries</code> keys are removed using <code>delete</code>');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -884,16 +995,22 @@
], ],
"tests": [ "tests": [
{ {
"text": "The <code>users</code> object only contains the keys <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code>", "text":
"testString": "assert('Alan' in users && 'Jeff' in users && 'Sarah' in users && 'Ryan' in users && Object.keys(users).length === 4, 'The <code>users</code> object only contains the keys <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code>');" "The <code>users</code> object only contains the keys <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code>",
"testString":
"assert('Alan' in users && 'Jeff' in users && 'Sarah' in users && 'Ryan' in users && Object.keys(users).length === 4, 'The <code>users</code> object only contains the keys <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code>');"
}, },
{ {
"text": "The function <code>isEveryoneHere</code> returns <code>true</code> if <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code> are properties on the <code>users</code> object", "text":
"testString": "assert(isEveryoneHere(users) === true, 'The function <code>isEveryoneHere</code> returns <code>true</code> if <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code> are properties on the <code>users</code> object');" "The function <code>isEveryoneHere</code> returns <code>true</code> if <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code> are properties on the <code>users</code> object",
"testString":
"assert(isEveryoneHere(users) === true, 'The function <code>isEveryoneHere</code> returns <code>true</code> if <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code> are properties on the <code>users</code> object');"
}, },
{ {
"text": "The function <code>isEveryoneHere</code> returns <code>false</code> if <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code> are not properties on the <code>users</code> object", "text":
"testString": "assert((function() { delete users.Alan; delete users.Jeff; delete users.Sarah; delete users.Ryan; return isEveryoneHere(users) })() === false, 'The function <code>isEveryoneHere</code> returns <code>false</code> if <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code> are not properties on the <code>users</code> object');" "The function <code>isEveryoneHere</code> returns <code>false</code> if <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code> are not properties on the <code>users</code> object",
"testString":
"assert((function() { delete users.Alan; delete users.Jeff; delete users.Sarah; delete users.Ryan; return isEveryoneHere(users) })() === false, 'The function <code>isEveryoneHere</code> returns <code>false</code> if <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code> are not properties on the <code>users</code> object');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -940,10 +1057,11 @@
}, },
{ {
"id": "587d7b7d367417b2b2512b1d", "id": "587d7b7d367417b2b2512b1d",
"title": " Iterate Through the Keys of an Object with a for...in Statement", "title":
" Iterate Through the Keys of an Object with a for...in Statement",
"description": [ "description": [
"Sometimes you may need to iterate through all the keys within an object. This requires a specific syntax in JavaScript called a <dfn>for...in</dfn> statement. For our <code>users</code> object, this could look like:", "Sometimes you may need to iterate through all the keys within an object. This requires a specific syntax in JavaScript called a <dfn>for...in</dfn> statement. For our <code>users</code> object, this could look like:",
"<blockquote>for (let user in users) {<br> console.log(user);<br>};<br><br>// logs:<br>Alan<br>Jeff<br>Sarah<br>Ryan</blockquote>", "<blockquote>for (let user in users) {<br>&nbsp;&nbsp;console.log(user);<br>};<br><br>// logs:<br>Alan<br>Jeff<br>Sarah<br>Ryan</blockquote>",
"In this statement, we defined a variable <code>user</code>, and as you can see, this variable was reset during each iteration to each of the object's keys as the statement looped through the object, resulting in each user's name being printed to the console.", "In this statement, we defined a variable <code>user</code>, and as you can see, this variable was reset during each iteration to each of the object's keys as the statement looped through the object, resulting in each user's name being printed to the console.",
"<strong>NOTE:</strong><br>Objects do not maintain an ordering to stored keys like arrays do; thus a keys position on an object, or the relative order in which it appears, is irrelevant when referencing or accessing that key.", "<strong>NOTE:</strong><br>Objects do not maintain an ordering to stored keys like arrays do; thus a keys position on an object, or the relative order in which it appears, is irrelevant when referencing or accessing that key.",
"<hr>", "<hr>",
@ -951,12 +1069,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "The <code>users</code> object contains users <code>Jeff</code> and <code>Ryan</code> with <code>online</code> set to <code>true</code> and users <code>Alan</code> and <code>Sarah</code> with <code>online</code> set to <code>false</code>", "text":
"testString": "assert(users.Alan.online === false && users.Jeff.online === true && users.Sarah.online === false && users.Ryan.online === true, 'The <code>users</code> object contains users <code>Jeff</code> and <code>Ryan</code> with <code>online</code> set to <code>true</code> and users <code>Alan</code> and <code>Sarah</code> with <code>online</code> set to <code>false</code>');" "The <code>users</code> object contains users <code>Jeff</code> and <code>Ryan</code> with <code>online</code> set to <code>true</code> and users <code>Alan</code> and <code>Sarah</code> with <code>online</code> set to <code>false</code>",
"testString":
"assert(users.Alan.online === false && users.Jeff.online === true && users.Sarah.online === false && users.Ryan.online === true, 'The <code>users</code> object contains users <code>Jeff</code> and <code>Ryan</code> with <code>online</code> set to <code>true</code> and users <code>Alan</code> and <code>Sarah</code> with <code>online</code> set to <code>false</code>');"
}, },
{ {
"text": "The function <code>countOnline</code> returns the number of users with the <code>online</code> property set to <code>true</code>", "text":
"testString": "assert((function() { users.Harry = {online: true}; users.Sam = {online: true}; users.Carl = {online: true}; return countOnline(users) })() === 5, 'The function <code>countOnline</code> returns the number of users with the <code>online</code> property set to <code>true</code>');" "The function <code>countOnline</code> returns the number of users with the <code>online</code> property set to <code>true</code>",
"testString":
"assert((function() { users.Harry = {online: true}; users.Sam = {online: true}; users.Carl = {online: true}; return countOnline(users) })() === 5, 'The function <code>countOnline</code> returns the number of users with the <code>online</code> property set to <code>true</code>');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1011,12 +1133,16 @@
], ],
"tests": [ "tests": [
{ {
"text": "The <code>users</code> object only contains the keys <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code>", "text":
"testString": "assert('Alan' in users && 'Jeff' in users && 'Sarah' in users && 'Ryan' in users && Object.keys(users).length === 4, 'The <code>users</code> object only contains the keys <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code>');" "The <code>users</code> object only contains the keys <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code>",
"testString":
"assert('Alan' in users && 'Jeff' in users && 'Sarah' in users && 'Ryan' in users && Object.keys(users).length === 4, 'The <code>users</code> object only contains the keys <code>Alan</code>, <code>Jeff</code>, <code>Sarah</code>, and <code>Ryan</code>');"
}, },
{ {
"text": "The <code>getArrayOfUsers</code> function returns an array which contains all the keys in the <code>users</code> object", "text":
"testString": "assert((function() { users.Sam = {}; users.Lewis = {}; let R = getArrayOfUsers(users); return (R.indexOf('Alan') !== -1 && R.indexOf('Jeff') !== -1 && R.indexOf('Sarah') !== -1 && R.indexOf('Ryan') !== -1 && R.indexOf('Sam') !== -1 && R.indexOf('Lewis') !== -1); })() === true, 'The <code>getArrayOfUsers</code> function returns an array which contains all the keys in the <code>users</code> object');" "The <code>getArrayOfUsers</code> function returns an array which contains all the keys in the <code>users</code> object",
"testString":
"assert((function() { users.Sam = {}; users.Lewis = {}; let R = getArrayOfUsers(users); return (R.indexOf('Alan') !== -1 && R.indexOf('Jeff') !== -1 && R.indexOf('Sarah') !== -1 && R.indexOf('Ryan') !== -1 && R.indexOf('Sam') !== -1 && R.indexOf('Lewis') !== -1); })() === true, 'The <code>getArrayOfUsers</code> function returns an array which contains all the keys in the <code>users</code> object');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1071,16 +1197,22 @@
], ],
"tests": [ "tests": [
{ {
"text": "The <code>user</code> object has <code>name</code>, <code>age</code>, and <code>data</code> keys", "text":
"testString": "assert('name' in user && 'age' in user && 'data' in user, 'The <code>user</code> object has <code>name</code>, <code>age</code>, and <code>data</code> keys');" "The <code>user</code> object has <code>name</code>, <code>age</code>, and <code>data</code> keys",
"testString":
"assert('name' in user && 'age' in user && 'data' in user, 'The <code>user</code> object has <code>name</code>, <code>age</code>, and <code>data</code> keys');"
}, },
{ {
"text": "The <code>addFriend</code> function accepts a <code>user</code> object and a <code>friend</code> string as arguments and adds the friend to the array of <code>friends</code> in the <code>user</code> object", "text":
"testString": "assert((function() { let L1 = user.data.friends.length; addFriend(user, 'Sean'); let L2 = user.data.friends.length; return (L2 === L1 + 1); })(), 'The <code>addFriend</code> function accepts a <code>user</code> object and a <code>friend</code> string as arguments and adds the friend to the array of <code>friends</code> in the <code>user</code> object');" "The <code>addFriend</code> function accepts a <code>user</code> object and a <code>friend</code> string as arguments and adds the friend to the array of <code>friends</code> in the <code>user</code> object",
"testString":
"assert((function() { let L1 = user.data.friends.length; addFriend(user, 'Sean'); let L2 = user.data.friends.length; return (L2 === L1 + 1); })(), 'The <code>addFriend</code> function accepts a <code>user</code> object and a <code>friend</code> string as arguments and adds the friend to the array of <code>friends</code> in the <code>user</code> object');"
}, },
{ {
"text": "<code>addFriend(user, \"Pete\")</code> should return <code>[\"Sam\", \"Kira\", \"Tomo\", \"Pete\"]</code>", "text":
"testString": "assert.deepEqual((function() { delete user.data.friends; user.data.friends = ['Sam', 'Kira', 'Tomo']; return addFriend(user, 'Pete') })(), ['Sam', 'Kira', 'Tomo', 'Pete'], '<code>addFriend(user, \"Pete\")</code> should return <code>[\"Sam\", \"Kira\", \"Tomo\", \"Pete\"]</code>');" "<code>addFriend(user, \"Pete\")</code> should return <code>[\"Sam\", \"Kira\", \"Tomo\", \"Pete\"]</code>",
"testString":
"assert.deepEqual((function() { delete user.data.friends; user.data.friends = ['Sam', 'Kira', 'Tomo']; return addFriend(user, 'Pete') })(), ['Sam', 'Kira', 'Tomo', 'Pete'], '<code>addFriend(user, \"Pete\")</code> should return <code>[\"Sam\", \"Kira\", \"Tomo\", \"Pete\"]</code>');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",

View File

@ -26,15 +26,19 @@
"tests": [ "tests": [
{ {
"text": "<code>var</code> does not exist in code.", "text": "<code>var</code> does not exist in code.",
"testString": "getUserInput => assert(!getUserInput('index').match(/var/g),'<code>var</code> does not exist in code.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/var/g),'<code>var</code> does not exist in code.');"
}, },
{ {
"text": "<code>catName</code> should be <code>Oliver</code>.", "text": "<code>catName</code> should be <code>Oliver</code>.",
"testString": "assert(catName === \"Oliver\", '<code>catName</code> should be <code>Oliver</code>.');" "testString":
"assert(catName === \"Oliver\", '<code>catName</code> should be <code>Oliver</code>.');"
}, },
{ {
"text": "<code>quote</code> should be <code>\"Oliver says Meow!\"</code>", "text":
"testString": "assert(quote === \"Oliver says Meow!\", '<code>quote</code> should be <code>\"Oliver says Meow!\"</code>');" "<code>quote</code> should be <code>\"Oliver says Meow!\"</code>",
"testString":
"assert(quote === \"Oliver says Meow!\", '<code>quote</code> should be <code>\"Oliver says Meow!\"</code>');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -69,13 +73,13 @@
"When you declare a variable with the <code>var</code> keyword, it is declared globally, or locally if declared inside a function.", "When you declare a variable with the <code>var</code> keyword, it is declared globally, or locally if declared inside a function.",
"The <code>let</code> keyword behaves similarly, but with some extra features. When you declare a variable with the <code>let</code> keyword inside a block, statement, or expression, its scope is limited to that block, statement, or expression.", "The <code>let</code> keyword behaves similarly, but with some extra features. When you declare a variable with the <code>let</code> keyword inside a block, statement, or expression, its scope is limited to that block, statement, or expression.",
"For example:", "For example:",
"<blockquote>var numArray = [];<br>for (var i = 0; i < 3; i++) {<br> numArray.push(i);<br>}<br>console.log(numArray);<br>// returns [0, 1, 2]<br>console.log(i);<br>// returns 3</blockquote>", "<blockquote>var numArray = [];<br>for (var i = 0; i < 3; i++) {<br>&nbsp;&nbsp;numArray.push(i);<br>}<br>console.log(numArray);<br>// returns [0, 1, 2]<br>console.log(i);<br>// returns 3</blockquote>",
"With the <code>var</code> keyword, <code>i</code> is declared globally. So when <code>i++</code> is executed, it updates the global variable. This code is similar to the following:", "With the <code>var</code> keyword, <code>i</code> is declared globally. So when <code>i++</code> is executed, it updates the global variable. This code is similar to the following:",
"<blockquote>var numArray = [];<br>var i;<br>for (i = 0; i < 3; i++) {<br> numArray.push(i);<br>}<br>console.log(numArray);<br>// returns [0, 1, 2]<br>console.log(i);<br>// returns 3</blockquote>", "<blockquote>var numArray = [];<br>var i;<br>for (i = 0; i < 3; i++) {<br>&nbsp;&nbsp;numArray.push(i);<br>}<br>console.log(numArray);<br>// returns [0, 1, 2]<br>console.log(i);<br>// returns 3</blockquote>",
"This behavior will cause problems if you were to create a function and store it for later use inside a for loop that uses the <code>i</code> variable. This is because the stored function will always refer to the value of the updated global <code>i</code> variable.", "This behavior will cause problems if you were to create a function and store it for later use inside a for loop that uses the <code>i</code> variable. This is because the stored function will always refer to the value of the updated global <code>i</code> variable.",
"<blockquote>var printNumTwo;<br>for (var i = 0; i < 3; i++) {<br> if(i === 2){<br> printNumTwo = function() {<br> return i;<br> };<br> }<br>}<br>console.log(printNumTwo());<br>// returns 3</blockquote>", "<blockquote>var printNumTwo;<br>for (var i = 0; i < 3; i++) {<br>&nbsp;&nbsp;if(i === 2){<br>&nbsp;&nbsp;&nbsp;&nbsp;printNumTwo = function() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return i;<br>&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;}<br>}<br>console.log(printNumTwo());<br>// returns 3</blockquote>",
"As you can see, <code>printNumTwo()</code> prints 3 and not 2. This is because the value assigned to <code>i</code> was updated and the <code>printNumTwo()</code> returns the global <code>i</code> and not the value <code>i</code> had when the function was created in the for loop. The <code>let</code> keyword does not follow this behavior:", "As you can see, <code>printNumTwo()</code> prints 3 and not 2. This is because the value assigned to <code>i</code> was updated and the <code>printNumTwo()</code> returns the global <code>i</code> and not the value <code>i</code> had when the function was created in the for loop. The <code>let</code> keyword does not follow this behavior:",
"<blockquote>'use strict';<br>let printNumTwo;<br>for (let i = 0; i < 3; i++) {<br> if (i === 2) {<br> printNumTwo = function() {<br> return i;<br> };<br> }<br>}<br>console.log(printNumTwo());<br>// returns 2<br>console.log(i);<br>// returns \"i is not defined\"</blockquote>", "<blockquote>'use strict';<br>let printNumTwo;<br>for (let i = 0; i < 3; i++) {<br>&nbsp;&nbsp;if (i === 2) {<br>&nbsp;&nbsp;&nbsp;&nbsp;printNumTwo = function() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return i;<br>&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;}<br>}<br>console.log(printNumTwo());<br>// returns 2<br>console.log(i);<br>// returns \"i is not defined\"</blockquote>",
"<code>i</code> is not defined because it was not declared in the global scope. It is only declared within the for loop statement. <code>printNumTwo()</code> returned the correct value because three different <code>i</code> variables with unique values (0, 1, and 2) were created by the <code>let</code> keyword within the loop statement.", "<code>i</code> is not defined because it was not declared in the global scope. It is only declared within the for loop statement. <code>printNumTwo()</code> returned the correct value because three different <code>i</code> variables with unique values (0, 1, and 2) were created by the <code>let</code> keyword within the loop statement.",
"<hr>", "<hr>",
"Fix the code so that <code>i</code> declared in the if statement is a separate variable than <code>i</code> declared in the first line of the function. Be certain not to use the <code>var</code> keyword anywhere in your code.", "Fix the code so that <code>i</code> declared in the if statement is a separate variable than <code>i</code> declared in the first line of the function. Be certain not to use the <code>var</code> keyword anywhere in your code.",
@ -84,15 +88,19 @@
"tests": [ "tests": [
{ {
"text": "<code>var</code> does not exist in code.", "text": "<code>var</code> does not exist in code.",
"testString": "getUserInput => assert(!getUserInput('index').match(/var/g),'<code>var</code> does not exist in code.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/var/g),'<code>var</code> does not exist in code.');"
}, },
{ {
"text": "The variable <code>i</code> declared in the if statement should equal \"block scope\".", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/(i\\s*=\\s*).*\\s*.*\\s*.*\\1('|\")block\\s*scope\\2/g), 'The variable <code>i</code> declared in the if statement should equal \"block scope\".');" "The variable <code>i</code> declared in the if statement should equal \"block scope\".",
"testString":
"getUserInput => assert(getUserInput('index').match(/(i\\s*=\\s*).*\\s*.*\\s*.*\\1('|\")block\\s*scope\\2/g), 'The variable <code>i</code> declared in the if statement should equal \"block scope\".');"
}, },
{ {
"text": "<code>checkScope()</code> should return \"function scope\"", "text": "<code>checkScope()</code> should return \"function scope\"",
"testString": "assert(checkScope() === \"function scope\", '<code>checkScope()</code> should return \"function scope\"');" "testString":
"assert(checkScope() === \"function scope\", '<code>checkScope()</code> should return \"function scope\"');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -134,19 +142,25 @@
"tests": [ "tests": [
{ {
"text": "<code>var</code> does not exist in your code.", "text": "<code>var</code> does not exist in your code.",
"testString": "getUserInput => assert(!getUserInput('index').match(/var/g),'<code>var</code> does not exist in your code.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/var/g),'<code>var</code> does not exist in your code.');"
}, },
{ {
"text": "<code>SENTENCE</code> should be a constant variable declared with <code>const</code>.", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/(const SENTENCE)/g), '<code>SENTENCE</code> should be a constant variable declared with <code>const</code>.');" "<code>SENTENCE</code> should be a constant variable declared with <code>const</code>.",
"testString":
"getUserInput => assert(getUserInput('index').match(/(const SENTENCE)/g), '<code>SENTENCE</code> should be a constant variable declared with <code>const</code>.');"
}, },
{ {
"text": "<code>i</code> should be declared with <code>let</code>.", "text": "<code>i</code> should be declared with <code>let</code>.",
"testString": "getUserInput => assert(getUserInput('index').match(/(let i)/g), '<code>i</code> should be declared with <code>let</code>.');" "testString":
"getUserInput => assert(getUserInput('index').match(/(let i)/g), '<code>i</code> should be declared with <code>let</code>.');"
}, },
{ {
"text": "<code>console.log</code> should be changed to print the <code>SENTENCE</code> variable.", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/console.log/(/s*?SENTENCE/s*?/)/s*?;/g), '<code>console.log</code> should be adjusted to print the variable <code>SENTENCE</code>.');" "<code>console.log</code> should be changed to print the <code>SENTENCE</code> variable.",
"testString":
"getUserInput => assert(getUserInput('index').match(/console.log/(/s*?SENTENCE/s*?/)/s*?;/g), '<code>console.log</code> should be adjusted to print the variable <code>SENTENCE</code>.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -193,19 +207,24 @@
"tests": [ "tests": [
{ {
"text": "Do not replace <code>const</code> keyword.", "text": "Do not replace <code>const</code> keyword.",
"testString": "getUserInput => assert(getUserInput('index').match(/const/g), 'Do not replace <code>const</code> keyword.');" "testString":
"getUserInput => assert(getUserInput('index').match(/const/g), 'Do not replace <code>const</code> keyword.');"
}, },
{ {
"text": "<code>s</code> should be a constant variable (by using <code>const</code>).", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/const\\s+s/g), '<code>s</code> should be a constant variable (by using <code>const</code>).');" "<code>s</code> should be a constant variable (by using <code>const</code>).",
"testString":
"getUserInput => assert(getUserInput('index').match(/const\\s+s/g), '<code>s</code> should be a constant variable (by using <code>const</code>).');"
}, },
{ {
"text": "Do not change the original array declaration.", "text": "Do not change the original array declaration.",
"testString": "getUserInput => assert(getUserInput('index').match(/const\\s+s\\s*=\\s*\\[\\s*5\\s*,\\s*7\\s*,\\s*2\\s*\\]\\s*;?/g), 'Do not change the original array declaration.');" "testString":
"getUserInput => assert(getUserInput('index').match(/const\\s+s\\s*=\\s*\\[\\s*5\\s*,\\s*7\\s*,\\s*2\\s*\\]\\s*;?/g), 'Do not change the original array declaration.');"
}, },
{ {
"text": "<code>s</code> should be equal to <code>[2, 5, 7]</code>.", "text": "<code>s</code> should be equal to <code>[2, 5, 7]</code>.",
"testString": "assert.deepEqual(s, [2, 5, 7], '<code>s</code> should be equal to <code>[2, 5, 7]</code>.');" "testString":
"assert.deepEqual(s, [2, 5, 7], '<code>s</code> should be equal to <code>[2, 5, 7]</code>.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -239,26 +258,31 @@
"description": [ "description": [
"As seen in the previous challenge, <code>const</code> declaration alone doesn't really protect your data from mutation. To ensure your data doesn't change, JavaScript provides a function <code>Object.freeze</code> to prevent data mutation.", "As seen in the previous challenge, <code>const</code> declaration alone doesn't really protect your data from mutation. To ensure your data doesn't change, JavaScript provides a function <code>Object.freeze</code> to prevent data mutation.",
"Once the object is frozen, you can no longer add, update, or delete properties from it. Any attempt at changing the object will be rejected without an error.", "Once the object is frozen, you can no longer add, update, or delete properties from it. Any attempt at changing the object will be rejected without an error.",
"<blockquote><br>let obj = {<br> name:\"FreeCodeCamp\"<br> review:\"Awesome\"<br>};<br>Object.freeze(obj);<br>obj.review = \"bad\"; //will be ignored. Mutation not allowed<br>obj.newProp = \"Test\"; // will be ignored. Mutation not allowed<br>console.log(obj); <br>// { name: \"FreeCodeCamp\", review:\"Awesome\"}<br></blockquote>", "<blockquote><br>let obj = {<br>&nbsp;&nbsp;name:\"FreeCodeCamp\"<br>&nbsp;&nbsp;review:\"Awesome\"<br>};<br>Object.freeze(obj);<br>obj.review = \"bad\"; //will be ignored. Mutation not allowed<br>obj.newProp = \"Test\"; // will be ignored. Mutation not allowed<br>console.log(obj); <br>// { name: \"FreeCodeCamp\", review:\"Awesome\"}<br></blockquote>",
"<hr>", "<hr>",
"In this challenge you are going to use <code>Object.freeze</code> to prevent mathematical constants from changing. You need to freeze the <code>MATH_CONSTANTS</code> object so that no one is able alter the value of <code>PI</code>, add, or delete properties ." "In this challenge you are going to use <code>Object.freeze</code> to prevent mathematical constants from changing. You need to freeze the <code>MATH_CONSTANTS</code> object so that no one is able alter the value of <code>PI</code>, add, or delete properties ."
], ],
"tests": [ "tests": [
{ {
"text": "Do not replace <code>const</code> keyword.", "text": "Do not replace <code>const</code> keyword.",
"testString": "getUserInput => assert(getUserInput('index').match(/const/g), 'Do not replace <code>const</code> keyword.');" "testString":
"getUserInput => assert(getUserInput('index').match(/const/g), 'Do not replace <code>const</code> keyword.');"
}, },
{ {
"text": "<code>MATH_CONSTANTS</code> should be a constant variable (by using <code>const</code>).", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/const\\s+MATH_CONSTANTS/g), '<code>MATH_CONSTANTS</code> should be a constant variable (by using <code>const</code>).');" "<code>MATH_CONSTANTS</code> should be a constant variable (by using <code>const</code>).",
"testString":
"getUserInput => assert(getUserInput('index').match(/const\\s+MATH_CONSTANTS/g), '<code>MATH_CONSTANTS</code> should be a constant variable (by using <code>const</code>).');"
}, },
{ {
"text": "Do not change original <code>MATH_CONSTANTS</code>.", "text": "Do not change original <code>MATH_CONSTANTS</code>.",
"testString": "getUserInput => assert(getUserInput('index').match(/const\\s+MATH_CONSTANTS\\s+=\\s+{\\s+PI:\\s+3.14\\s+};/g), 'Do not change original <code>MATH_CONSTANTS</code>.');" "testString":
"getUserInput => assert(getUserInput('index').match(/const\\s+MATH_CONSTANTS\\s+=\\s+{\\s+PI:\\s+3.14\\s+};/g), 'Do not change original <code>MATH_CONSTANTS</code>.');"
}, },
{ {
"text": "<code>PI</code> equals <code>3.14</code>.", "text": "<code>PI</code> equals <code>3.14</code>.",
"testString": "assert(PI === 3.14, '<code>PI</code> equals <code>3.14</code>.');" "testString":
"assert(PI === 3.14, '<code>PI</code> equals <code>3.14</code>.');"
} }
], ],
"releasedOn": "Aug 12, 2017", "releasedOn": "Aug 12, 2017",
@ -299,9 +323,9 @@
"description": [ "description": [
"In JavaScript, we often don't need to name our functions, especially when passing a function as an argument to another function. Instead, we create inline functions. We don't need to name these functions because we do not reuse them anywhere else.", "In JavaScript, we often don't need to name our functions, especially when passing a function as an argument to another function. Instead, we create inline functions. We don't need to name these functions because we do not reuse them anywhere else.",
"To achieve this, we often use the following syntax:", "To achieve this, we often use the following syntax:",
"<blockquote>const myFunc = function() {<br> const myVar = \"value\";<br> return myVar;<br>}</blockquote>", "<blockquote>const myFunc = function() {<br>&nbsp;&nbsp;const myVar = \"value\";<br>&nbsp;&nbsp;return myVar;<br>}</blockquote>",
"ES6 provides us with the syntactic sugar to not have to write anonymous functions this way. Instead, you can use <strong>arrow function syntax</strong>:", "ES6 provides us with the syntactic sugar to not have to write anonymous functions this way. Instead, you can use <strong>arrow function syntax</strong>:",
"<blockquote>const myFunc = () => {<br> const myVar = \"value\";<br> return myVar;<br>}</blockquote>", "<blockquote>const myFunc = () => {<br>&nbsp;&nbsp;const myVar = \"value\";<br>&nbsp;&nbsp;return myVar;<br>}</blockquote>",
"When there is no function body, and only a return value, arrow function syntax allows you to omit the keyword <code>return</code> as well as the brackets surrounding the code. This helps simplify smaller functions into one-line statements:", "When there is no function body, and only a return value, arrow function syntax allows you to omit the keyword <code>return</code> as well as the brackets surrounding the code. This helps simplify smaller functions into one-line statements:",
"<blockquote>const myFunc= () => \"value\"</blockquote>", "<blockquote>const myFunc= () => \"value\"</blockquote>",
"This code will still return <code>value</code> by default.", "This code will still return <code>value</code> by default.",
@ -311,23 +335,29 @@
"tests": [ "tests": [
{ {
"text": "User did replace <code>var</code> keyword.", "text": "User did replace <code>var</code> keyword.",
"testString": "getUserInput => assert(!getUserInput('index').match(/var/g), 'User did replace <code>var</code> keyword.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/var/g), 'User did replace <code>var</code> keyword.');"
}, },
{ {
"text": "<code>magic</code> should be a constant variable (by using <code>const</code>).", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/const\\s+magic/g), '<code>magic</code> should be a constant variable (by using <code>const</code>).');" "<code>magic</code> should be a constant variable (by using <code>const</code>).",
"testString":
"getUserInput => assert(getUserInput('index').match(/const\\s+magic/g), '<code>magic</code> should be a constant variable (by using <code>const</code>).');"
}, },
{ {
"text": "<code>magic</code> is a <code>function</code>.", "text": "<code>magic</code> is a <code>function</code>.",
"testString": "assert(typeof magic === 'function', '<code>magic</code> is a <code>function</code>.');" "testString":
"assert(typeof magic === 'function', '<code>magic</code> is a <code>function</code>.');"
}, },
{ {
"text": "<code>magic()</code> returns correct date.", "text": "<code>magic()</code> returns correct date.",
"testString": "assert(magic().getDate() == new Date().getDate(), '<code>magic()</code> returns correct date.');" "testString":
"assert(magic().getDate() == new Date().getDate(), '<code>magic()</code> returns correct date.');"
}, },
{ {
"text": "<code>function</code> keyword was not used.", "text": "<code>function</code> keyword was not used.",
"testString": "getUserInput => assert(!getUserInput('index').match(/function/g), '<code>function</code> keyword was not used.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/function/g), '<code>function</code> keyword was not used.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -362,23 +392,30 @@
"tests": [ "tests": [
{ {
"text": "User did replace <code>var</code> keyword.", "text": "User did replace <code>var</code> keyword.",
"testString": "getUserInput => assert(!getUserInput('index').match(/var/g), 'User did replace <code>var</code> keyword.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/var/g), 'User did replace <code>var</code> keyword.');"
}, },
{ {
"text": "<code>myConcat</code> should be a constant variable (by using <code>const</code>).", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/const\\s+myConcat/g), '<code>myConcat</code> should be a constant variable (by using <code>const</code>).');" "<code>myConcat</code> should be a constant variable (by using <code>const</code>).",
"testString":
"getUserInput => assert(getUserInput('index').match(/const\\s+myConcat/g), '<code>myConcat</code> should be a constant variable (by using <code>const</code>).');"
}, },
{ {
"text": "<code>myConcat</code> should be a function", "text": "<code>myConcat</code> should be a function",
"testString": "assert(typeof myConcat === 'function', '<code>myConcat</code> should be a function');" "testString":
"assert(typeof myConcat === 'function', '<code>myConcat</code> should be a function');"
}, },
{ {
"text": "<code>myConcat()</code> returns the correct <code>array</code>", "text":
"testString": "assert(() => { const a = myConcat([1], [2]); return a[0] == 1 && a[1] == 2; }, '<code>myConcat()</code> returns the correct <code>array</code>');" "<code>myConcat()</code> returns the correct <code>array</code>",
"testString":
"assert(() => { const a = myConcat([1], [2]); return a[0] == 1 && a[1] == 2; }, '<code>myConcat()</code> returns the correct <code>array</code>');"
}, },
{ {
"text": "<code>function</code> keyword was not used.", "text": "<code>function</code> keyword was not used.",
"testString": "getUserInput => assert(!getUserInput('index').match(/function/g), '<code>function</code> keyword was not used.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/function/g), '<code>function</code> keyword was not used.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -409,7 +446,7 @@
"It's time we see how powerful arrow functions are when processing data.", "It's time we see how powerful arrow functions are when processing data.",
"Arrow functions work really well with higher order functions, such as <code>map()</code>, <code>filter()</code>, and <code>reduce()</code>, that take other functions as arguments for processing collections of data.", "Arrow functions work really well with higher order functions, such as <code>map()</code>, <code>filter()</code>, and <code>reduce()</code>, that take other functions as arguments for processing collections of data.",
"Read the following code:", "Read the following code:",
"<blockquote>FBPosts.filter(function(post) {<br> return post.thumbnail !== null && post.shares > 100 && post.likes > 500;<br>})</blockquote>", "<blockquote>FBPosts.filter(function(post) {<br>&nbsp;&nbsp;return post.thumbnail !== null && post.shares > 100 && post.likes > 500;<br>})</blockquote>",
"We have written this with <code>filter()</code> to at least make it somewhat readable. Now compare it to the following code which uses arrow function syntax instead:", "We have written this with <code>filter()</code> to at least make it somewhat readable. Now compare it to the following code which uses arrow function syntax instead:",
"<blockquote>FBPosts.filter((post) => post.thumbnail !== null && post.shares > 100 && post.likes > 500)</blockquote>", "<blockquote>FBPosts.filter((post) => post.thumbnail !== null && post.shares > 100 && post.likes > 500)</blockquote>",
"This code is more succinct and accomplishes the same task with fewer lines of code.", "This code is more succinct and accomplishes the same task with fewer lines of code.",
@ -419,31 +456,42 @@
"tests": [ "tests": [
{ {
"text": "User did replace <code>var</code> keyword.", "text": "User did replace <code>var</code> keyword.",
"testString": "getUserInput => assert(!getUserInput('index').match(/var/g), 'User did replace <code>var</code> keyword.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/var/g), 'User did replace <code>var</code> keyword.');"
}, },
{ {
"text": "<code>squaredIntegers</code> should be a constant variable (by using <code>const</code>).", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/const\\s+squaredIntegers/g), '<code>squaredIntegers</code> should be a constant variable (by using <code>const</code>).');" "<code>squaredIntegers</code> should be a constant variable (by using <code>const</code>).",
"testString":
"getUserInput => assert(getUserInput('index').match(/const\\s+squaredIntegers/g), '<code>squaredIntegers</code> should be a constant variable (by using <code>const</code>).');"
}, },
{ {
"text": "<code>squaredIntegers</code> should be an <code>array</code>", "text":
"testString": "assert(Array.isArray(squaredIntegers), '<code>squaredIntegers</code> should be an <code>array</code>');" "<code>squaredIntegers</code> should be an <code>array</code>",
"testString":
"assert(Array.isArray(squaredIntegers), '<code>squaredIntegers</code> should be an <code>array</code>');"
}, },
{ {
"text": "<code>squaredIntegers</code> should be <code>[16, 1764, 36]</code>", "text":
"testString": "assert(squaredIntegers[0] === 16 && squaredIntegers[1] === 1764 && squaredIntegers[2] === 36, '<code>squaredIntegers</code> should be <code>[16, 1764, 36]</code>');" "<code>squaredIntegers</code> should be <code>[16, 1764, 36]</code>",
"testString":
"assert(squaredIntegers[0] === 16 && squaredIntegers[1] === 1764 && squaredIntegers[2] === 36, '<code>squaredIntegers</code> should be <code>[16, 1764, 36]</code>');"
}, },
{ {
"text": "<code>function</code> keyword was not used.", "text": "<code>function</code> keyword was not used.",
"testString": "getUserInput => assert(!getUserInput('index').match(/function/g), '<code>function</code> keyword was not used.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/function/g), '<code>function</code> keyword was not used.');"
}, },
{ {
"text": "loop should not be used", "text": "loop should not be used",
"testString": "getUserInput => assert(!getUserInput('index').match(/(for)|(while)/g), 'loop should not be used');" "testString":
"getUserInput => assert(!getUserInput('index').match(/(for)|(while)/g), 'loop should not be used');"
}, },
{ {
"text": "<code>map</code>, <code>filter</code>, or <code>reduce</code> should be used", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/map|filter|reduce/g), '<code>map</code>, <code>filter</code>, or <code>reduce</code> should be used');" "<code>map</code>, <code>filter</code>, or <code>reduce</code> should be used",
"testString":
"getUserInput => assert(getUserInput('index').match(/map|filter|reduce/g), '<code>map</code>, <code>filter</code>, or <code>reduce</code> should be used');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -478,23 +526,29 @@
"description": [ "description": [
"In order to help us create more flexible functions, ES6 introduces <dfn>default parameters</dfn> for functions.", "In order to help us create more flexible functions, ES6 introduces <dfn>default parameters</dfn> for functions.",
"Check out this code:", "Check out this code:",
"<blockquote>function greeting(name = \"Anonymous\") {<br> return \"Hello \" + name;<br>}<br>console.log(greeting(\"John\")); // Hello John<br>console.log(greeting()); // Hello Anonymous</blockquote>", "<blockquote>function greeting(name = \"Anonymous\") {<br>&nbsp;&nbsp;return \"Hello \" + name;<br>}<br>console.log(greeting(\"John\")); // Hello John<br>console.log(greeting()); // Hello Anonymous</blockquote>",
"The default parameter kicks in when the argument is not specified (it is undefined). As you can see in the example above, the parameter <code>name</code> will receive its default value <code>\"Anonymous\"</code> when you do not provide a value for the parameter. You can add default values for as many parameters as you want.", "The default parameter kicks in when the argument is not specified (it is undefined). As you can see in the example above, the parameter <code>name</code> will receive its default value <code>\"Anonymous\"</code> when you do not provide a value for the parameter. You can add default values for as many parameters as you want.",
"<hr>", "<hr>",
"Modify the function <code>increment</code> by adding default parameters so that it will add 1 to <code>number</code> if <code>value</code> is not specified." "Modify the function <code>increment</code> by adding default parameters so that it will add 1 to <code>number</code> if <code>value</code> is not specified."
], ],
"tests": [ "tests": [
{ {
"text": "The result of <code>increment(5, 2)</code> should be <code>7</code>.", "text":
"testString": "assert(increment(5, 2) === 7, 'The result of <code>increment(5, 2)</code> should be <code>7</code>.');" "The result of <code>increment(5, 2)</code> should be <code>7</code>.",
"testString":
"assert(increment(5, 2) === 7, 'The result of <code>increment(5, 2)</code> should be <code>7</code>.');"
}, },
{ {
"text": "The result of <code>increment(5)</code> should be <code>6</code>.", "text":
"testString": "assert(increment(5) === 6, 'The result of <code>increment(5)</code> should be <code>6</code>.');" "The result of <code>increment(5)</code> should be <code>6</code>.",
"testString":
"assert(increment(5) === 6, 'The result of <code>increment(5)</code> should be <code>6</code>.');"
}, },
{ {
"text": "default parameter <code>1</code> was used for <code>value</code>.", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/value\\s*=\\s*1/g), 'default parameter <code>1</code> was used for <code>value</code>.');" "default parameter <code>1</code> was used for <code>value</code>.",
"testString":
"getUserInput => assert(getUserInput('index').match(/value\\s*=\\s*1/g), 'default parameter <code>1</code> was used for <code>value</code>.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -526,7 +580,7 @@
"description": [ "description": [
"In order to help us create more flexible functions, ES6 introduces the <dfn>rest operator</dfn> for function parameters. With the rest operator, you can create functions that take a variable number of arguments. These arguments are stored in an array that can be accessed later from inside the function.", "In order to help us create more flexible functions, ES6 introduces the <dfn>rest operator</dfn> for function parameters. With the rest operator, you can create functions that take a variable number of arguments. These arguments are stored in an array that can be accessed later from inside the function.",
"Check out this code:", "Check out this code:",
"<blockquote>function howMany(...args) {<br> return \"You have passed \" + args.length + \" arguments.\";<br>}<br>console.log(howMany(0, 1, 2)); // You have passed 3 arguments<br>console.log(howMany(\"string\", null, [1, 2, 3], { })); // You have passed 4 arguments.</blockquote>", "<blockquote>function howMany(...args) {<br>&nbsp;&nbsp;return \"You have passed \" + args.length + \" arguments.\";<br>}<br>console.log(howMany(0, 1, 2)); // You have passed 3 arguments<br>console.log(howMany(\"string\", null, [1, 2, 3], { })); // You have passed 4 arguments.</blockquote>",
"The rest operator eliminates the need to check the <code>args</code> array and allows us to apply <code>map()</code>, <code>filter()</code> and <code>reduce()</code> on the parameters array.", "The rest operator eliminates the need to check the <code>args</code> array and allows us to apply <code>map()</code>, <code>filter()</code> and <code>reduce()</code> on the parameters array.",
"<hr>", "<hr>",
"Modify the function <code>sum</code> so that it uses the rest operator and it works in the same way with any number of parameters." "Modify the function <code>sum</code> so that it uses the rest operator and it works in the same way with any number of parameters."
@ -534,23 +588,29 @@
"tests": [ "tests": [
{ {
"text": "The result of <code>sum(0,1,2)</code> should be 3", "text": "The result of <code>sum(0,1,2)</code> should be 3",
"testString": "assert(sum(0,1,2) === 3, 'The result of <code>sum(0,1,2)</code> should be 3');" "testString":
"assert(sum(0,1,2) === 3, 'The result of <code>sum(0,1,2)</code> should be 3');"
}, },
{ {
"text": "The result of <code>sum(1,2,3,4)</code> should be 10", "text": "The result of <code>sum(1,2,3,4)</code> should be 10",
"testString": "assert(sum(1,2,3,4) === 10, 'The result of <code>sum(1,2,3,4)</code> should be 10');" "testString":
"assert(sum(1,2,3,4) === 10, 'The result of <code>sum(1,2,3,4)</code> should be 10');"
}, },
{ {
"text": "The result of <code>sum(5)</code> should be 5", "text": "The result of <code>sum(5)</code> should be 5",
"testString": "assert(sum(5) === 5, 'The result of <code>sum(5)</code> should be 5');" "testString":
"assert(sum(5) === 5, 'The result of <code>sum(5)</code> should be 5');"
}, },
{ {
"text": "The result of <code>sum()</code> should be 0", "text": "The result of <code>sum()</code> should be 0",
"testString": "assert(sum() === 0, 'The result of <code>sum()</code> should be 0');" "testString":
"assert(sum() === 0, 'The result of <code>sum()</code> should be 0');"
}, },
{ {
"text": "The <code>sum</code> function uses the <code>...</code> spread operator on the <code>args</code> parameter.", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/function\\s+sum\\s*\\(\\s*...args\\s*\\)\\s*{/g), 'The <code>sum</code> function uses the <code>...</code> spread operator on the <code>args</code> parameter.');" "The <code>sum</code> function uses the <code>...</code> spread operator on the <code>args</code> parameter.",
"testString":
"getUserInput => assert(getUserInput('index').match(/function\\s+sum\\s*\\(\\s*...args\\s*\\)\\s*{/g), 'The <code>sum</code> function uses the <code>...</code> spread operator on the <code>args</code> parameter.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -595,15 +655,20 @@
"tests": [ "tests": [
{ {
"text": "<code>arr2</code> is correct copy of <code>arr1</code>.", "text": "<code>arr2</code> is correct copy of <code>arr1</code>.",
"testString": "assert(arr2.every((v, i) => v === arr1[i]), '<code>arr2</code> is correct copy of <code>arr1</code>.');" "testString":
"assert(arr2.every((v, i) => v === arr1[i]), '<code>arr2</code> is correct copy of <code>arr1</code>.');"
}, },
{ {
"text": "<code>...</code> spread operator was used to duplicate <code>arr1</code>.", "text":
"testString": "getUserInput => assert(getUserInput('index').match(/\\[\\s*...arr1\\s*\\]/g),'<code>...</code> spread operator was used to duplicate <code>arr1</code>.');" "<code>...</code> spread operator was used to duplicate <code>arr1</code>.",
"testString":
"getUserInput => assert(getUserInput('index').match(/\\[\\s*...arr1\\s*\\]/g),'<code>...</code> spread operator was used to duplicate <code>arr1</code>.');"
}, },
{ {
"text": "<code>arr2</code> remains unchanged when <code>arr1</code> is changed.", "text":
"testString": "assert((arr1, arr2) => {arr1.push('JUN'); return arr2.length < arr1.length},'<code>arr2</code> remains unchanged when <code>arr1</code> is changed.');" "<code>arr2</code> remains unchanged when <code>arr1</code> is changed.",
"testString":
"assert((arr1, arr2) => {arr1.push('JUN'); return arr2.length < arr1.length},'<code>arr2</code> remains unchanged when <code>arr1</code> is changed.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -647,15 +712,19 @@
"tests": [ "tests": [
{ {
"text": "the function <code>getLength()</code> returns a number.", "text": "the function <code>getLength()</code> returns a number.",
"testString": "assert(typeof getLength('') === 'number', 'the function <code>getLength()</code> returns a number.');" "testString":
"assert(typeof getLength('') === 'number', 'the function <code>getLength()</code> returns a number.');"
}, },
{ {
"text": "<code>getLength(\"FreeCodeCamp\")</code> should be <code>12</code>", "text":
"testString": "assert(getLength(\"FreeCodeCamp\") === 12, '<code>getLength(\"FreeCodeCamp\")</code> should be <code>12</code>');" "<code>getLength(\"FreeCodeCamp\")</code> should be <code>12</code>",
"testString":
"assert(getLength(\"FreeCodeCamp\") === 12, '<code>getLength(\"FreeCodeCamp\")</code> should be <code>12</code>');"
}, },
{ {
"text": "destructuring with reassignment was used", "text": "destructuring with reassignment was used",
"testString": "getUserInput => assert(getUserInput('index').match(/\\{\\s*length\\s*:\\s*len\\s*}\\s*=\\s*str/g),'destructuring with reassignment was used');" "testString":
"getUserInput => assert(getUserInput('index').match(/\\{\\s*length\\s*:\\s*len\\s*}\\s*=\\s*str/g),'destructuring with reassignment was used');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -687,11 +756,12 @@
}, },
{ {
"id": "587d7b89367417b2b2512b4a", "id": "587d7b89367417b2b2512b4a",
"title": "Use Destructuring Assignment to Assign Variables from Nested Objects", "title":
"Use Destructuring Assignment to Assign Variables from Nested Objects",
"description": [ "description": [
"We can similarly destructure <em>nested</em> objects into variables.", "We can similarly destructure <em>nested</em> objects into variables.",
"Consider the following code:", "Consider the following code:",
"<blockquote>const a = {<br> start: { x: 5, y: 6},<br> end: { x: 6, y: -9 }<br>};<br>const { start : { x: startX, y: startY }} = a;<br>console.log(startX, startY); // 5, 6</blockquote>", "<blockquote>const a = {<br>&nbsp;&nbsp;start: { x: 5, y: 6},<br>&nbsp;&nbsp;end: { x: 6, y: -9 }<br>};<br>const { start : { x: startX, y: startY }} = a;<br>console.log(startX, startY); // 5, 6</blockquote>",
"In the example above, the variable <code>start</code> is assigned the value of <code>a.start</code>, which is also an object.", "In the example above, the variable <code>start</code> is assigned the value of <code>a.start</code>, which is also an object.",
"<hr>", "<hr>",
"Use destructuring assignment to obtain <code>max</code> of <code>forecast.tomorrow</code> and assign it to <code>maxOfTomorrow</code>." "Use destructuring assignment to obtain <code>max</code> of <code>forecast.tomorrow</code> and assign it to <code>maxOfTomorrow</code>."
@ -699,11 +769,13 @@
"tests": [ "tests": [
{ {
"text": "<code>maxOfTomorrow</code> equals <code>84.6</code>", "text": "<code>maxOfTomorrow</code> equals <code>84.6</code>",
"testString": "assert(getMaxOfTmrw(LOCAL_FORECAST) === 84.6, '<code>maxOfTomorrow</code> equals <code>84.6</code>');" "testString":
"assert(getMaxOfTmrw(LOCAL_FORECAST) === 84.6, '<code>maxOfTomorrow</code> equals <code>84.6</code>');"
}, },
{ {
"text": "nested destructuring was used", "text": "nested destructuring was used",
"testString": "getUserInput => assert(getUserInput('index').match(/\\{\\s*tomorrow\\s*:\\s*\\{\\s*max\\s*:\\s*maxOfTomorrow\\s*\\}\\s*\\}\\s*=\\s*forecast/g),'nested destructuring was used');" "testString":
"getUserInput => assert(getUserInput('index').match(/\\{\\s*tomorrow\\s*:\\s*\\{\\s*max\\s*:\\s*maxOfTomorrow\\s*\\}\\s*\\}\\s*=\\s*forecast/g),'nested destructuring was used');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -752,15 +824,18 @@
"tests": [ "tests": [
{ {
"text": "Value of <code>a</code> should be 6, after swapping.", "text": "Value of <code>a</code> should be 6, after swapping.",
"testString": "assert(a === 6, 'Value of <code>a</code> should be 6, after swapping.');" "testString":
"assert(a === 6, 'Value of <code>a</code> should be 6, after swapping.');"
}, },
{ {
"text": "Value of <code>b</code> should be 8, after swapping.", "text": "Value of <code>b</code> should be 8, after swapping.",
"testString": "assert(b === 8, 'Value of <code>b</code> should be 8, after swapping.');" "testString":
"assert(b === 8, 'Value of <code>b</code> should be 8, after swapping.');"
}, },
{ {
"text": "Use array destructuring to swap a and b.", "text": "Use array destructuring to swap a and b.",
"testString": "// assert(/\\[\\s*(\\w)\\s*,\\s*(\\w)\\s*\\]\\s*=\\s*\\[\\s*\\2\\s*,\\s*\\1\\s*\\]/g.test(code), 'Use array destructuring to swap a and b.');" "testString":
"// assert(/\\[\\s*(\\w)\\s*,\\s*(\\w)\\s*\\]\\s*=\\s*\\[\\s*\\2\\s*,\\s*\\1\\s*\\]/g.test(code), 'Use array destructuring to swap a and b.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -789,7 +864,8 @@
}, },
{ {
"id": "587d7b8a367417b2b2512b4c", "id": "587d7b8a367417b2b2512b4c",
"title": "Use Destructuring Assignment with the Rest Operator to Reassign Array Elements", "title":
"Use Destructuring Assignment with the Rest Operator to Reassign Array Elements",
"description": [ "description": [
"In some situations involving array destructuring, we might want to collect the rest of the elements into a separate array.", "In some situations involving array destructuring, we might want to collect the rest of the elements into a separate array.",
"The result is similar to <code>Array.prototype.slice()</code>, as shown below:", "The result is similar to <code>Array.prototype.slice()</code>, as shown below:",
@ -802,15 +878,18 @@
"tests": [ "tests": [
{ {
"text": "<code>arr</code> should be <code>[3,4,5,6,7,8,9,10]</code>", "text": "<code>arr</code> should be <code>[3,4,5,6,7,8,9,10]</code>",
"testString": "assert(arr.every((v, i) => v === i + 3),'<code>arr</code> should be <code>[3,4,5,6,7,8,9,10]</code>');" "testString":
"assert(arr.every((v, i) => v === i + 3),'<code>arr</code> should be <code>[3,4,5,6,7,8,9,10]</code>');"
}, },
{ {
"text": "destructuring was used.", "text": "destructuring was used.",
"testString": "getUserInput => assert(getUserInput('index').match(/\\[\\s*\\w*\\s*,\\s*\\w*\\s*,\\s*...arr\\s*\\]/g),'destructuring was used.');" "testString":
"getUserInput => assert(getUserInput('index').match(/\\[\\s*\\w*\\s*,\\s*\\w*\\s*,\\s*...arr\\s*\\]/g),'destructuring was used.');"
}, },
{ {
"text": "<code>Array.slice()</code> was not used.", "text": "<code>Array.slice()</code> was not used.",
"testString": "getUserInput => assert(!getUserInput('index').match(/Array.slice/g), '<code>Array.slice()</code> was not used.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/Array.slice/g), '<code>Array.slice()</code> was not used.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -841,13 +920,14 @@
}, },
{ {
"id": "587d7b8a367417b2b2512b4d", "id": "587d7b8a367417b2b2512b4d",
"title": "Use Destructuring Assignment to Pass an Object as a Function's Parameters", "title":
"Use Destructuring Assignment to Pass an Object as a Function's Parameters",
"description": [ "description": [
"In some cases, you can destructure the object in a function argument itself.", "In some cases, you can destructure the object in a function argument itself.",
"Consider the code below:", "Consider the code below:",
"<blockquote>const profileUpdate = (profileData) => {<br> const { name, age, nationality, location } = profileData;<br> // do something with these variables<br>}</blockquote>", "<blockquote>const profileUpdate = (profileData) => {<br>&nbsp;&nbsp;const { name, age, nationality, location } = profileData;<br>&nbsp;&nbsp;// do something with these variables<br>}</blockquote>",
"This effectively destructures the object sent into the function. This can also be done in-place:", "This effectively destructures the object sent into the function. This can also be done in-place:",
"<blockquote>const profileUpdate = ({ name, age, nationality, location }) => {<br> /* do something with these fields */<br>}</blockquote>", "<blockquote>const profileUpdate = ({ name, age, nationality, location }) => {<br>&nbsp;&nbsp;/* do something with these fields */<br>}</blockquote>",
"This removes some extra lines and makes our code look neat.", "This removes some extra lines and makes our code look neat.",
"This has the added benefit of not having to manipulate an entire object in a function; only the fields that are needed are copied inside the function.", "This has the added benefit of not having to manipulate an entire object in a function; only the fields that are needed are copied inside the function.",
"<hr>", "<hr>",
@ -856,15 +936,18 @@
"tests": [ "tests": [
{ {
"text": "<code>stats</code> should be an <code>object</code>.", "text": "<code>stats</code> should be an <code>object</code>.",
"testString": "assert(typeof stats === 'object', '<code>stats</code> should be an <code>object</code>.');" "testString":
"assert(typeof stats === 'object', '<code>stats</code> should be an <code>object</code>.');"
}, },
{ {
"text": "<code>half(stats)</code> should be <code>28.015</code>", "text": "<code>half(stats)</code> should be <code>28.015</code>",
"testString": "assert(half(stats) === 28.015, '<code>half(stats)</code> should be <code>28.015</code>');" "testString":
"assert(half(stats) === 28.015, '<code>half(stats)</code> should be <code>28.015</code>');"
}, },
{ {
"text": "Destructuring was used.", "text": "Destructuring was used.",
"testString": "getUserInput => assert(getUserInput('index').match(/\\(\\s*\\{\\s*\\w+\\s*,\\s*\\w+\\s*\\}\\s*\\)/g), 'Destructuring was used.');" "testString":
"getUserInput => assert(getUserInput('index').match(/\\(\\s*\\{\\s*\\w+\\s*,\\s*\\w+\\s*\\}\\s*\\)/g), 'Destructuring was used.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -909,7 +992,7 @@
"description": [ "description": [
"A new feature of ES6 is the <dfn>template literal</dfn>. This is a special type of string that allows you to use string interpolation features to create strings.", "A new feature of ES6 is the <dfn>template literal</dfn>. This is a special type of string that allows you to use string interpolation features to create strings.",
"Consider the code below:", "Consider the code below:",
"<blockquote>const person = {<br> name: \"Zodiac Hasbro\",<br> age: 56<br>};<br><br>// string interpolation<br>const greeting = `Hello, my name is ${person.name}!<br>I am ${person.age} years old.`;<br><br>console.log(greeting); // prints<br>// Hello, my name is Zodiac Hasbro!<br>// I am 56 years old.<br></blockquote>", "<blockquote>const person = {<br>&nbsp;&nbsp;name: \"Zodiac Hasbro\",<br>&nbsp;&nbsp;age: 56<br>};<br><br>// string interpolation<br>const greeting = `Hello, my name is ${person.name}!<br>I am ${person.age} years old.`;<br><br>console.log(greeting); // prints<br>// Hello, my name is Zodiac Hasbro!<br>// I am 56 years old.<br></blockquote>",
"A lot of things happened there.", "A lot of things happened there.",
"Firstly, the <code>${variable}</code> syntax used above is a place holder. Basically, you won't have to use concatenation with the <code>+</code> operator anymore. To add variables to strings, you just drop the variable in a template string and wrap it with <code>${</code> and <code>}</code>.", "Firstly, the <code>${variable}</code> syntax used above is a place holder. Basically, you won't have to use concatenation with the <code>+</code> operator anymore. To add variables to strings, you just drop the variable in a template string and wrap it with <code>${</code> and <code>}</code>.",
"Secondly, the example uses backticks (<code>`</code>), not quotes (<code>'</code> or <code>\"</code>), to wrap the string. Notice that the string is multi-line.", "Secondly, the example uses backticks (<code>`</code>), not quotes (<code>'</code> or <code>\"</code>), to wrap the string. Notice that the string is multi-line.",
@ -919,16 +1002,20 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>resultDisplayArray</code> is a list containing <code>result failure</code> messages.", "text":
"testString": "assert(typeof makeList(result.failure) === 'object' && resultDisplayArray.length === 3, '<code>resultDisplayArray</code> is a list containing <code>result failure</code> messages.');" "<code>resultDisplayArray</code> is a list containing <code>result failure</code> messages.",
"testString":
"assert(typeof makeList(result.failure) === 'object' && resultDisplayArray.length === 3, '<code>resultDisplayArray</code> is a list containing <code>result failure</code> messages.');"
}, },
{ {
"text": "<code>resultDisplayArray</code> is the desired output.", "text": "<code>resultDisplayArray</code> is the desired output.",
"testString": "assert(makeList(result.failure).every((v, i) => v === `<li class=\"text-warning\">${result.failure[i]}</li>`), '<code>resultDisplayArray</code> is the desired output.');" "testString":
"assert(makeList(result.failure).every((v, i) => v === `<li class=\"text-warning\">${result.failure[i]}</li>`), '<code>resultDisplayArray</code> is the desired output.');"
}, },
{ {
"text": "Template strings were used", "text": "Template strings were used",
"testString": "getUserInput => assert(getUserInput('index').match(/\\`<li class=\"text-warning\">\\$\\{\\w+\\}<\\/li>\\`/g), 'Template strings were used');" "testString":
"getUserInput => assert(getUserInput('index').match(/\\`<li class=\"text-warning\">\\$\\{\\w+\\}<\\/li>\\`/g), 'Template strings were used');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -973,7 +1060,7 @@
"description": [ "description": [
"ES6 adds some nice support for easily defining object literals.", "ES6 adds some nice support for easily defining object literals.",
"Consider the following code:", "Consider the following code:",
"<blockquote>const getMousePosition = (x, y) => ({<br> x: x,<br> y: y<br>});</blockquote>", "<blockquote>const getMousePosition = (x, y) => ({<br>&nbsp;&nbsp;x: x,<br>&nbsp;&nbsp;y: y<br>});</blockquote>",
"<code>getMousePosition</code> is a simple function that returns an object containing two fields.", "<code>getMousePosition</code> is a simple function that returns an object containing two fields.",
"ES6 provides the syntactic sugar to eliminate the redundancy of having to write <code>x: x</code>. You can simply write <code>x</code> once, and it will be converted to<code>x: x</code> (or something equivalent) under the hood.", "ES6 provides the syntactic sugar to eliminate the redundancy of having to write <code>x: x</code>. You can simply write <code>x</code> once, and it will be converted to<code>x: x</code> (or something equivalent) under the hood.",
"Here is the same function from above rewritten to use this new syntax:", "Here is the same function from above rewritten to use this new syntax:",
@ -983,12 +1070,15 @@
], ],
"tests": [ "tests": [
{ {
"text": "the output is <code>{name: \"Zodiac Hasbro\", age: 56, gender: \"male\"}</code>.", "text":
"testString": "assert(() => {const res={name:\"Zodiac Hasbro\",age:56,gender:\"male\"}; const person=createPerson(\"Zodiac Hasbro\", 56, \"male\"); return Object.keys(person).every(k => person[k] === res[k]);}, 'the output is <code>{name: \"Zodiac Hasbro\", age: 56, gender: \"male\"}</code>.');" "the output is <code>{name: \"Zodiac Hasbro\", age: 56, gender: \"male\"}</code>.",
"testString":
"assert(() => {const res={name:\"Zodiac Hasbro\",age:56,gender:\"male\"}; const person=createPerson(\"Zodiac Hasbro\", 56, \"male\"); return Object.keys(person).every(k => person[k] === res[k]);}, 'the output is <code>{name: \"Zodiac Hasbro\", age: 56, gender: \"male\"}</code>.');"
}, },
{ {
"text": "No <code>:</code> were used.", "text": "No <code>:</code> were used.",
"testString": "getUserInput => assert(!getUserInput('index').match(/:/g), 'No <code>:</code> were used.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/:/g), 'No <code>:</code> were used.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1022,20 +1112,23 @@
"title": "Write Concise Declarative Functions with ES6", "title": "Write Concise Declarative Functions with ES6",
"description": [ "description": [
"When defining functions within objects in ES5, we have to use the keyword <code>function</code> as follows:", "When defining functions within objects in ES5, we have to use the keyword <code>function</code> as follows:",
"<blockquote>const person = {<br> name: \"Taylor\",<br> sayHello: function() {<br> return `Hello! My name is ${this.name}.`;<br> }<br>};</blockquote>", "<blockquote>const person = {<br>&nbsp;&nbsp;name: \"Taylor\",<br>&nbsp;&nbsp;sayHello: function() {<br>&nbsp;&nbsp;&nbsp;&nbsp;return `Hello! My name is ${this.name}.`;<br>&nbsp;&nbsp;}<br>};</blockquote>",
"With ES6, You can remove the <code>function</code> keyword and colon altogether when defining functions in objects. Here's an example of this syntax:", "With ES6, You can remove the <code>function</code> keyword and colon altogether when defining functions in objects. Here's an example of this syntax:",
"<blockquote>const person = {<br> name: \"Taylor\",<br> sayHello() {<br> return `Hello! My name is ${this.name}.`;<br> }<br>};</blockquote>", "<blockquote>const person = {<br>&nbsp;&nbsp;name: \"Taylor\",<br>&nbsp;&nbsp;sayHello() {<br>&nbsp;&nbsp;&nbsp;&nbsp;return `Hello! My name is ${this.name}.`;<br>&nbsp;&nbsp;}<br>};</blockquote>",
"<hr>", "<hr>",
"Refactor the function <code>setGear</code> inside the object <code>bicycle</code> to use the shorthand syntax described above." "Refactor the function <code>setGear</code> inside the object <code>bicycle</code> to use the shorthand syntax described above."
], ],
"tests": [ "tests": [
{ {
"text": "<code>setGear</code> is a function and changes the <code>gear</code> variable.", "text":
"testString": "assert(() => { bicycle.setGear(48); return bicycle.gear === 48 }, '<code>setGear</code> is a function and changes the <code>gear</code> variable.');" "<code>setGear</code> is a function and changes the <code>gear</code> variable.",
"testString":
"assert(() => { bicycle.setGear(48); return bicycle.gear === 48 }, '<code>setGear</code> is a function and changes the <code>gear</code> variable.');"
}, },
{ {
"text": "Declarative function was used.", "text": "Declarative function was used.",
"testString": "getUserInput => assert(!getUserInput('index').match(/:\\s*function\\s*\\(\\)/g), 'Declarative function was used.');" "testString":
"getUserInput => assert(!getUserInput('index').match(/:\\s*function\\s*\\(\\)/g), 'Declarative function was used.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1071,9 +1164,9 @@
"ES6 provides a new syntax to help create objects, using the keyword <dfn>class</dfn>.", "ES6 provides a new syntax to help create objects, using the keyword <dfn>class</dfn>.",
"This is to be noted, that the <code>class</code> syntax is just a syntax, and not a full-fledged class based implementation of object oriented paradigm, unlike in languages like Java, or Python, or Ruby etc.", "This is to be noted, that the <code>class</code> syntax is just a syntax, and not a full-fledged class based implementation of object oriented paradigm, unlike in languages like Java, or Python, or Ruby etc.",
"In ES5, we usually define a constructor function, and use the <code>new</code> keyword to instantiate an object.", "In ES5, we usually define a constructor function, and use the <code>new</code> keyword to instantiate an object.",
"<blockquote>var SpaceShuttle = function(targetPlanet){<br> this.targetPlanet = targetPlanet;<br>}<br>var zeus = new spaceShuttle('Jupiter');</blockquote>", "<blockquote>var SpaceShuttle = function(targetPlanet){<br>&nbsp;&nbsp;this.targetPlanet = targetPlanet;<br>}<br>var zeus = new SpaceShuttle('Jupiter');</blockquote>",
"The class syntax simply replaces the constructor function creation:", "The class syntax simply replaces the constructor function creation:",
"<blockquote>class SpaceShuttle {<br> constructor(targetPlanet){<br> this.targetPlanet = targetPlanet;<br> }<br>}<br>const zeus = new spaceShuttle('Jupiter');</blockquote>", "<blockquote>class SpaceShuttle {<br>&nbsp;&nbsp;constructor(targetPlanet){<br>&nbsp;&nbsp;&nbsp;&nbsp;this.targetPlanet = targetPlanet;<br>&nbsp;&nbsp;}<br>}<br>const zeus = new SpaceShuttle('Jupiter');</blockquote>",
"Notice that the <code>class</code> keyword declares a new function, and a constructor was added, which would be invoked when <code>new</code> is called - to create a new object.", "Notice that the <code>class</code> keyword declares a new function, and a constructor was added, which would be invoked when <code>new</code> is called - to create a new object.",
"<hr>", "<hr>",
"Use <code>class</code> keyword and write a proper constructor to create the <code>Vegetable</code> class.", "Use <code>class</code> keyword and write a proper constructor to create the <code>Vegetable</code> class.",
@ -1081,16 +1174,20 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>Vegetable</code> should be a <code>class</code> with a defined <code>constructor</code> method.", "text":
"testString": "assert(typeof Vegetable === 'function' && typeof Vegetable.constructor === 'function', '<code>Vegetable</code> should be a <code>class</code> with a defined <code>constructor</code> method.');" "<code>Vegetable</code> should be a <code>class</code> with a defined <code>constructor</code> method.",
"testString":
"assert(typeof Vegetable === 'function' && typeof Vegetable.constructor === 'function', '<code>Vegetable</code> should be a <code>class</code> with a defined <code>constructor</code> method.');"
}, },
{ {
"text": "<code>class</code> keyword was used.", "text": "<code>class</code> keyword was used.",
"testString": "getUserInput => assert(getUserInput('index').match(/class/g),'<code>class</code> keyword was used.');" "testString":
"getUserInput => assert(getUserInput('index').match(/class/g),'<code>class</code> keyword was used.');"
}, },
{ {
"text": "<code>Vegetable</code> can be instantiated.", "text": "<code>Vegetable</code> can be instantiated.",
"testString": "assert(() => {const a = new Vegetable(\"apple\"); return typeof a === 'object';},'<code>Vegetable</code> can be instantiated.');" "testString":
"assert(() => {const a = new Vegetable(\"apple\"); return typeof a === 'object';},'<code>Vegetable</code> can be instantiated.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1126,7 +1223,7 @@
"These are classically called <dfn>getters</dfn> and <dfn>setters</dfn>.", "These are classically called <dfn>getters</dfn> and <dfn>setters</dfn>.",
"Getter functions are meant to simply return (get) the value of an object's private variable to the user without the user directly accessing the private variable.", "Getter functions are meant to simply return (get) the value of an object's private variable to the user without the user directly accessing the private variable.",
"Setter functions are meant to modify (set) the value of an object's private variable based on the value passed into the setter function. This change could involve calculations, or even overwriting the previous value completely.", "Setter functions are meant to modify (set) the value of an object's private variable based on the value passed into the setter function. This change could involve calculations, or even overwriting the previous value completely.",
"<blockquote>class Book {<br> constructor(author) {<br> this._author = author;<br> }<br> // getter<br> get writer(){<br> return this._author;<br> }<br> // setter<br> set writer(updatedAuthor){<br> this._author = updatedAuthor;<br> }<br>}<br>const lol = new Book('anonymous');<br>console.log(lol.writer);<br>lol.writer = 'wut';<br>console.log(lol.writer);</blockquote>", "<blockquote>class Book {<br>&nbsp;&nbsp;constructor(author) {<br>&nbsp;&nbsp;&nbsp;&nbsp;this._author = author;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;// getter<br>&nbsp;&nbsp;get writer(){<br>&nbsp;&nbsp;&nbsp;&nbsp;return this._author;<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;// setter<br>&nbsp;&nbsp;set writer(updatedAuthor){<br>&nbsp;&nbsp;&nbsp;&nbsp;this._author = updatedAuthor;<br>&nbsp;&nbsp;}<br>}<br>const lol = new Book('anonymous');<br>console.log(lol.writer);<br>lol.writer = 'wut';<br>console.log(lol.writer);</blockquote>",
"Notice the syntax we are using to invoke the getter and setter - as if they are not even functions.", "Notice the syntax we are using to invoke the getter and setter - as if they are not even functions.",
"Getters and setters are important, because they hide internal implementation details.", "Getters and setters are important, because they hide internal implementation details.",
"<hr>", "<hr>",
@ -1140,16 +1237,20 @@
], ],
"tests": [ "tests": [
{ {
"text": "<code>Thermostat</code> should be a <code>class</code> with a defined <code>constructor</code> method.", "text":
"testString": "assert(typeof Thermostat === 'function' && typeof Thermostat.constructor === 'function','<code>Thermostat</code> should be a <code>class</code> with a defined <code>constructor</code> method.');" "<code>Thermostat</code> should be a <code>class</code> with a defined <code>constructor</code> method.",
"testString":
"assert(typeof Thermostat === 'function' && typeof Thermostat.constructor === 'function','<code>Thermostat</code> should be a <code>class</code> with a defined <code>constructor</code> method.');"
}, },
{ {
"text": "<code>class</code> keyword was used.", "text": "<code>class</code> keyword was used.",
"testString": "getUserInput => assert(getUserInput('index').match(/class/g),'<code>class</code> keyword was used.');" "testString":
"getUserInput => assert(getUserInput('index').match(/class/g),'<code>class</code> keyword was used.');"
}, },
{ {
"text": "<code>Thermostat</code> can be instantiated.", "text": "<code>Thermostat</code> can be instantiated.",
"testString": "assert(() => {const t = new Thermostat(32); return typeof t === 'object' && t.temperature === 0;}, '<code>Thermostat</code> can be instantiated.');" "testString":
"assert(() => {const t = new Thermostat(32); return typeof t === 'object' && t.temperature === 0;}, '<code>Thermostat</code> can be instantiated.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1199,7 +1300,8 @@
"tests": [ "tests": [
{ {
"text": "valid <code>import</code> statement", "text": "valid <code>import</code> statement",
"testString": "getUserInput => assert(getUserInput('index').match(/import\\s+\\{\\s?capitalizeString\\s?\\}\\s+from\\s+\"string_functions\"/g), 'valid <code>import</code> statement');" "testString":
"getUserInput => assert(getUserInput('index').match(/import\\s+\\{\\s?capitalizeString\\s?\\}\\s+from\\s+\"string_functions\"/g), 'valid <code>import</code> statement');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1210,10 +1312,7 @@
"key": "indexjs", "key": "indexjs",
"ext": "js", "ext": "js",
"name": "index", "name": "index",
"contents": [ "contents": ["\"use strict\";", "capitalizeString(\"hello!\");"],
"\"use strict\";",
"capitalizeString(\"hello!\");"
],
"head": [ "head": [
"window.require = function (str) {", "window.require = function (str) {",
"if (str === 'string_functions') {", "if (str === 'string_functions') {",
@ -1231,9 +1330,9 @@
"description": [ "description": [
"In the previous challenge, you learned about <code>import</code> and how it can be leveraged to import small amounts of code from large files. In order for this to work, though, we must utilize one of the statements that goes with <code>import</code>, known as <dfn>export</dfn>. When we want some code - a function, or a variable - to be usable in another file, we must export it in order to import it into another file. Like <code>import</code>, <code>export</code> is a non-browser feature.", "In the previous challenge, you learned about <code>import</code> and how it can be leveraged to import small amounts of code from large files. In order for this to work, though, we must utilize one of the statements that goes with <code>import</code>, known as <dfn>export</dfn>. When we want some code - a function, or a variable - to be usable in another file, we must export it in order to import it into another file. Like <code>import</code>, <code>export</code> is a non-browser feature.",
"The following is what we refer to as a <dfn>named export</dfn>. With this, we can import any code we export into another file with the <code>import</code> syntax you learned in the last lesson. Here's an example:", "The following is what we refer to as a <dfn>named export</dfn>. With this, we can import any code we export into another file with the <code>import</code> syntax you learned in the last lesson. Here's an example:",
"<blockquote>const capitalizeString = (string) => {<br> return string.charAt(0).toUpperCase() + string.slice(1);<br>}<br>export { capitalizeString } //How to export functions.<br>export const foo = \"bar\"; //How to export variables.</blockquote>", "<blockquote>const capitalizeString = (string) => {<br>&nbsp;&nbsp;return string.charAt(0).toUpperCase() + string.slice(1);<br>}<br>export { capitalizeString } //How to export functions.<br>export const foo = \"bar\"; //How to export variables.</blockquote>",
"Alternatively, if you would like to compact all your <code>export</code> statements into one line, you can take this approach:", "Alternatively, if you would like to compact all your <code>export</code> statements into one line, you can take this approach:",
"<blockquote>const capitalizeString = (string) => {<br> return string.charAt(0).toUpperCase() + string.slice(1);<br>}<br>const foo = \"bar\";<br>export { capitalizeString, foo }</blockquote>", "<blockquote>const capitalizeString = (string) => {<br>&nbsp;&nbsp;return string.charAt(0).toUpperCase() + string.slice(1);<br>}<br>const foo = \"bar\";<br>export { capitalizeString, foo }</blockquote>",
"Either approach is perfectly acceptable.", "Either approach is perfectly acceptable.",
"<hr>", "<hr>",
"Below are two variables that I want to make available for other files to use. Utilizing the first way I demonstrated <code>export</code>, export the two variables." "Below are two variables that I want to make available for other files to use. Utilizing the first way I demonstrated <code>export</code>, export the two variables."
@ -1241,11 +1340,13 @@
"tests": [ "tests": [
{ {
"text": "<code>foo</code> is exported.", "text": "<code>foo</code> is exported.",
"testString": "getUserInput => assert(getUserInput('index').match(/export\\s+const\\s+foo\\s+=+\\s\"bar\"/g), '<code>foo</code> is exported.');" "testString":
"getUserInput => assert(getUserInput('index').match(/export\\s+const\\s+foo\\s+=+\\s\"bar\"/g), '<code>foo</code> is exported.');"
}, },
{ {
"text": "<code>bar</code> is exported.", "text": "<code>bar</code> is exported.",
"testString": "getUserInput => assert(getUserInput('index').match(/export\\s+const\\s+bar\\s+=+\\s\"foo\"/g), '<code>bar</code> is exported.');" "testString":
"getUserInput => assert(getUserInput('index').match(/export\\s+const\\s+bar\\s+=+\\s\"foo\"/g), '<code>bar</code> is exported.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1261,9 +1362,7 @@
"const foo = \"bar\";", "const foo = \"bar\";",
"const bar= \"foo\";" "const bar= \"foo\";"
], ],
"head": [ "head": ["window.exports = function(){};"],
"window.exports = function(){};"
],
"tail": [] "tail": []
} }
} }
@ -1284,7 +1383,8 @@
"tests": [ "tests": [
{ {
"text": "Properly uses <code>import * as</code> syntax.", "text": "Properly uses <code>import * as</code> syntax.",
"testString": "assert(code.match(/import\\s+\\*\\s+as\\s+[a-zA-Z0-9_$]+\\s+from\\s*\"\\s*capitalize_strings\\s*\"\\s*;/gi), 'Properly uses <code>import * as</code> syntax.');" "testString":
"assert(code.match(/import\\s+\\*\\s+as\\s+[a-zA-Z0-9_$]+\\s+from\\s*\"\\s*capitalize_strings\\s*\"\\s*;/gi), 'Properly uses <code>import * as</code> syntax.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1295,9 +1395,7 @@
"key": "indexjs", "key": "indexjs",
"ext": "js", "ext": "js",
"name": "index", "name": "index",
"contents": [ "contents": ["\"use strict\";"],
"\"use strict\";"
],
"head": [ "head": [
"window.require = function(str) {", "window.require = function(str) {",
"if (str === 'capitalize_strings') {", "if (str === 'capitalize_strings') {",
@ -1317,7 +1415,7 @@
"In the <code>export</code> lesson, you learned about the syntax referred to as a <dfn>named export</dfn>. This allowed you to make multiple functions and variables available for use in other files.", "In the <code>export</code> lesson, you learned about the syntax referred to as a <dfn>named export</dfn>. This allowed you to make multiple functions and variables available for use in other files.",
"There is another <code>export</code> syntax you need to know, known as <dfn>export default</dfn>. Usually you will use this syntax if only one value is being exported from a file. It is also used to create a fallback value for a file or module.", "There is another <code>export</code> syntax you need to know, known as <dfn>export default</dfn>. Usually you will use this syntax if only one value is being exported from a file. It is also used to create a fallback value for a file or module.",
"Here is a quick example of <code>export default</code>:", "Here is a quick example of <code>export default</code>:",
"<blockquote>export default function add(x,y) {<br> return x + y;<br>}</blockquote>", "<blockquote>export default function add(x,y) {<br>&nbsp;&nbsp;return x + y;<br>}</blockquote>",
"Note: Since <code>export default</code> is used to declare a fallback value for a module or file, you can only have one value be a default export in each module or file. Additionally, you cannot use <code>export default</code> with <code>var</code>, <code>let</code>, or <code>const</code>", "Note: Since <code>export default</code> is used to declare a fallback value for a module or file, you can only have one value be a default export in each module or file. Additionally, you cannot use <code>export default</code> with <code>var</code>, <code>let</code>, or <code>const</code>",
"<hr>", "<hr>",
"The following function should be the fallback value for the module. Please add the necessary code to do so." "The following function should be the fallback value for the module. Please add the necessary code to do so."
@ -1325,7 +1423,8 @@
"tests": [ "tests": [
{ {
"text": "Proper used of <code>export</code> fallback.", "text": "Proper used of <code>export</code> fallback.",
"testString": "getUserInput => assert(getUserInput('index').match(/export\\s+default\\s+function\\s+subtract\\(x,y\\)\\s+{return\\s+x\\s-\\s+y;}/g), 'Proper used of <code>export</code> fallback.');" "testString":
"getUserInput => assert(getUserInput('index').match(/export\\s+default\\s+function\\s+subtract\\(x,y\\)\\s+{return\\s+x\\s-\\s+y;}/g), 'Proper used of <code>export</code> fallback.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1340,9 +1439,7 @@
"\"use strict\";", "\"use strict\";",
"function subtract(x,y) {return x - y;}" "function subtract(x,y) {return x - y;}"
], ],
"head": [ "head": ["window.exports = function(){};"],
"window.exports = function(){};"
],
"tail": [] "tail": []
} }
} }
@ -1361,7 +1458,8 @@
"tests": [ "tests": [
{ {
"text": "Properly imports <code>export default</code> method.", "text": "Properly imports <code>export default</code> method.",
"testString": "getUserInput => assert(getUserInput('index').match(/import\\s+subtract\\s+from\\s+\"math_functions\"/g), 'Properly imports <code>export default</code> method.');" "testString":
"getUserInput => assert(getUserInput('index').match(/import\\s+subtract\\s+from\\s+\"math_functions\"/g), 'Properly imports <code>export default</code> method.');"
} }
], ],
"releasedOn": "Feb 17, 2017", "releasedOn": "Feb 17, 2017",
@ -1372,10 +1470,7 @@
"key": "indexjs", "key": "indexjs",
"ext": "js", "ext": "js",
"name": "index", "name": "index",
"contents": [ "contents": ["\"use strict\";", "subtract(7,4);"],
"\"use strict\";",
"subtract(7,4);"
],
"head": [ "head": [
"window.require = function(str) {", "window.require = function(str) {",
"if (str === 'math_functions') {", "if (str === 'math_functions') {",

View File

@ -5,16 +5,18 @@
"helpRoom": "Help", "helpRoom": "Help",
"required": [ "required": [
{ {
"src":"https://unpkg.com/react@16.4.0/umd/react.production.min.js" "src": "https://unpkg.com/react@16.4.0/umd/react.production.min.js"
}, },
{ {
"src":"https://unpkg.com/react-dom@16.4.0/umd/react-dom.production.min.js" "src":
"https://unpkg.com/react-dom@16.4.0/umd/react-dom.production.min.js"
}, },
{ {
"src": "https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js" "src": "https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js"
}, },
{ {
"src": "https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.7/react-redux.min.js" "src":
"https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.7/react-redux.min.js"
} }
], ],
"template": "<body><div id='root'></div>${ source || '' }</body>", "template": "<body><div id='root'></div>${ source || '' }</body>",
@ -53,16 +55,22 @@
}, },
"tests": [ "tests": [
{ {
"text": "The <code>DisplayMessages</code> component should render an empty <code>div</code> element.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); return mockedComponent.find('div').text() === '' })(), 'The <code>DisplayMessages</code> component should render an empty <code>div</code> element.');" "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() === '' })(), 'The <code>DisplayMessages</code> component should render an empty <code>div</code> element.');"
}, },
{ {
"text": "The <code>DisplayMessages</code> constructor should be called properly with <code>super</code>, passing in <code>props</code>.", "text":
"testString": "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/\\s/g,''); return noWhiteSpace.includes('constructor(props)') && noWhiteSpace.includes('super(props'); })(), 'The <code>DisplayMessages</code> constructor should be called properly with <code>super</code>, passing in <code>props</code>.');" "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'); })(), 'The <code>DisplayMessages</code> constructor should be called properly with <code>super</code>, passing in <code>props</code>.');"
}, },
{ {
"text": "The <code>DisplayMessages</code> component should have an initial state equal to <code>{input: \"\", messages: []}</code>.", "text":
"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; })(), 'The <code>DisplayMessages</code> component should have an initial state equal to <code>{input: \"\", messages: []}</code>.');" "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; })(), 'The <code>DisplayMessages</code> component should have an initial state equal to <code>{input: \"\", messages: []}</code>.');"
} }
], ],
"solutions": [ "solutions": [
@ -120,28 +128,40 @@
}, },
"tests": [ "tests": [
{ {
"text": "The <code>DisplayMessages</code> component should initialize with a state equal to <code>{ input: \"\", messages: [] }</code>.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const initialState = mockedComponent.state(); return ( typeof initialState === 'object' && initialState.input === '' && initialState.messages.length === 0); })(), 'The <code>DisplayMessages</code> component should initialize with a state equal to <code>{ input: \"\", messages: [] }</code>.');" "The <code>DisplayMessages</code> component should initialize with a 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 === '' && initialState.messages.length === 0); })(), 'The <code>DisplayMessages</code> component should initialize with a state equal to <code>{ input: \"\", messages: [] }</code>.');"
}, },
{ {
"text": "The <code>DisplayMessages</code> component should render a <code>div</code> containing an <code>h2</code> element, a <code>button</code> element, a <code>ul</code> element, and <code>li</code> elements as children.", "text":
"testString": "async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const state = () => { mockedComponent.setState({messages: ['__TEST__MESSAGE']}); return waitForIt(() => mockedComponent )}; const updated = await state(); assert(updated.find('div').length === 1 && updated.find('h2').length === 1 && updated.find('button').length === 1 && updated.find('ul').length === 1, 'The <code>DisplayMessages</code> component should render a <code>div</code> containing an <code>h2</code> element, a <code>button</code> element, a <code>ul</code> element, and <code>li</code> elements as children.'); }; " "The <code>DisplayMessages</code> component should render a <code>div</code> containing an <code>h2</code> element, a <code>button</code> element, a <code>ul</code> element, and <code>li</code> elements as children.",
"testString":
"async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const state = () => { mockedComponent.setState({messages: ['__TEST__MESSAGE']}); return waitForIt(() => mockedComponent )}; const updated = await state(); assert(updated.find('div').length === 1 && updated.find('h2').length === 1 && updated.find('button').length === 1 && updated.find('ul').length === 1, 'The <code>DisplayMessages</code> component should render a <code>div</code> containing an <code>h2</code> element, a <code>button</code> element, a <code>ul</code> element, and <code>li</code> elements as children.'); }; "
}, },
{ {
"text": "The <code>input</code> element should render the value of <code>input</code> in local state.", "text":
"testString": "async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const testValue = '__TEST__EVENT__INPUT'; const changed = () => { causeChange(mockedComponent, testValue); return waitForIt(() => mockedComponent )}; const updated = await changed(); assert(updated.find('input').props().value === testValue, 'The <code>input</code> element should render the value of <code>input</code> in local state.'); }; " "The <code>input</code> element should render the value of <code>input</code> in local state.",
"testString":
"async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const testValue = '__TEST__EVENT__INPUT'; const changed = () => { causeChange(mockedComponent, testValue); return waitForIt(() => mockedComponent )}; const updated = await changed(); assert(updated.find('input').props().value === testValue, 'The <code>input</code> element should render the value of <code>input</code> in local state.'); }; "
}, },
{ {
"text": "Calling the method <code>handleChange</code> should update the <code>input</code> value in state to the current input.", "text":
"testString": "async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const initialState = mockedComponent.state(); const testMessage = '__TEST__EVENT__MESSAGE__'; const changed = () => { causeChange(mockedComponent, testMessage); return waitForIt(() => mockedComponent )}; const afterInput = await changed(); assert(initialState.input === '' && afterInput.state().input === '__TEST__EVENT__MESSAGE__', 'Calling the method <code>handleChange</code> should update the <code>input</code> value in state to the current input.'); }; " "Calling the method <code>handleChange</code> should update the <code>input</code> value in state to the current input.",
"testString":
"async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const initialState = mockedComponent.state(); const testMessage = '__TEST__EVENT__MESSAGE__'; const changed = () => { causeChange(mockedComponent, testMessage); return waitForIt(() => mockedComponent )}; const afterInput = await changed(); assert(initialState.input === '' && afterInput.state().input === '__TEST__EVENT__MESSAGE__', 'Calling the method <code>handleChange</code> should update the <code>input</code> value in state to the current input.'); }; "
}, },
{ {
"text": "Clicking the <code>Add message</code> button should call the method <code>submitMessage</code> which should add the current <code>input</code> to the <code>messages</code> array in state.", "text":
"testString": "async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const initialState = mockedComponent.state(); const testMessage_1 = '__FIRST__MESSAGE__'; const firstChange = () => { causeChange(mockedComponent, testMessage_1); return waitForIt(() => mockedComponent )}; const firstResult = await firstChange(); const firstSubmit = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterSubmit_1 = await firstSubmit(); const submitState_1 = afterSubmit_1.state(); const testMessage_2 = '__SECOND__MESSAGE__'; const secondChange = () => { causeChange(mockedComponent, testMessage_2); return waitForIt(() => mockedComponent )}; const secondResult = await secondChange(); const secondSubmit = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterSubmit_2 = await secondSubmit(); const submitState_2 = afterSubmit_2.state(); assert(initialState.messages.length === 0 && submitState_1.messages.length === 1 && submitState_2.messages.length === 2 && submitState_2.messages[1] === testMessage_2, 'Clicking the <code>Add message</code> button should call the method <code>submitMessage</code> which should add the current <code>input</code> to the <code>messages</code> array in state.'); }; " "Clicking the <code>Add message</code> button should call the method <code>submitMessage</code> which should add the current <code>input</code> to the <code>messages</code> array in state.",
"testString":
"async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const initialState = mockedComponent.state(); const testMessage_1 = '__FIRST__MESSAGE__'; const firstChange = () => { causeChange(mockedComponent, testMessage_1); return waitForIt(() => mockedComponent )}; const firstResult = await firstChange(); const firstSubmit = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterSubmit_1 = await firstSubmit(); const submitState_1 = afterSubmit_1.state(); const testMessage_2 = '__SECOND__MESSAGE__'; const secondChange = () => { causeChange(mockedComponent, testMessage_2); return waitForIt(() => mockedComponent )}; const secondResult = await secondChange(); const secondSubmit = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterSubmit_2 = await secondSubmit(); const submitState_2 = afterSubmit_2.state(); assert(initialState.messages.length === 0 && submitState_1.messages.length === 1 && submitState_2.messages.length === 2 && submitState_2.messages[1] === testMessage_2, 'Clicking the <code>Add message</code> button should call the method <code>submitMessage</code> which should add the current <code>input</code> to the <code>messages</code> array in state.'); }; "
}, },
{ {
"text": "The <code>submitMessage</code> method should clear the current input.", "text":
"testString": "async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const initialState = mockedComponent.state(); const testMessage = '__FIRST__MESSAGE__'; const firstChange = () => { causeChange(mockedComponent, testMessage); return waitForIt(() => mockedComponent )}; const firstResult = await firstChange(); const firstState = firstResult.state(); const firstSubmit = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterSubmit = await firstSubmit(); const submitState = afterSubmit.state(); assert(firstState.input === testMessage && submitState.input === '', 'The <code>submitMessage</code> method should clear the current input.'); }; " "The <code>submitMessage</code> method should clear the current input.",
"testString":
"async () => { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const initialState = mockedComponent.state(); const testMessage = '__FIRST__MESSAGE__'; const firstChange = () => { causeChange(mockedComponent, testMessage); return waitForIt(() => mockedComponent )}; const firstResult = await firstChange(); const firstState = firstResult.state(); const firstSubmit = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterSubmit = await firstSubmit(); const submitState = afterSubmit.state(); assert(firstState.input === testMessage && submitState.input === '', 'The <code>submitMessage</code> method should clear the current input.'); }; "
} }
], ],
"solutions": [ "solutions": [
@ -177,28 +197,39 @@
}, },
"tests": [ "tests": [
{ {
"text": "The const <code>ADD</code> should exist and hold a value equal to the string <code>ADD</code>", "text":
"testString": "assert(ADD === 'ADD', 'The const <code>ADD</code> should exist and hold a value equal to the string <code>ADD</code>');" "The const <code>ADD</code> should exist and hold a value equal to the string <code>ADD</code>",
"testString":
"assert(ADD === 'ADD', 'The const <code>ADD</code> should exist and hold a value equal to the string <code>ADD</code>');"
}, },
{ {
"text": "The action creator <code>addMessage</code> should return an object with <code>type</code> equal to <code>ADD</code> and message equal to the message that is passed in.", "text":
"testString": "assert((function() { const addAction = addMessage('__TEST__MESSAGE__'); return addAction.type === ADD && addAction.message === '__TEST__MESSAGE__'; })(), 'The action creator <code>addMessage</code> should return an object with <code>type</code> equal to <code>ADD</code> and message equal to the message that is passed in.');" "The action creator <code>addMessage</code> should return an object with <code>type</code> equal to <code>ADD</code> and message equal to the message that is passed in.",
"testString":
"assert((function() { const addAction = addMessage('__TEST__MESSAGE__'); return addAction.type === ADD && addAction.message === '__TEST__MESSAGE__'; })(), 'The action creator <code>addMessage</code> should return an object with <code>type</code> equal to <code>ADD</code> and message equal to the message that is passed in.');"
}, },
{ {
"text": "<code>messageReducer</code> should be a function.", "text": "<code>messageReducer</code> should be a function.",
"testString": "assert(typeof messageReducer === 'function', '<code>messageReducer</code> should be a function.');" "testString":
"assert(typeof messageReducer === 'function', '<code>messageReducer</code> should be a function.');"
}, },
{ {
"text": "The store should exist and have an initial state set to an empty array.", "text":
"testString": "assert((function() { const initialState = store.getState(); return typeof store === 'object' && initialState.length === 0; })(), 'The store should exist and have an initial state set to an empty array.');" "The store should exist and have an initial state set to an empty array.",
"testString":
"assert((function() { const initialState = store.getState(); return typeof store === 'object' && initialState.length === 0; })(), 'The store should exist and have an initial state set to an empty array.');"
}, },
{ {
"text": "Dispatching <code>addMessage</code> against the store should immutably add a new message to the array of messages held in state.", "text":
"testString": "assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch(addMessage('__A__TEST__MESSAGE')); const addState = store.getState(); return (isFrozen && addState[0] === '__A__TEST__MESSAGE'); })(), 'Dispatching <code>addMessage</code> against the store should immutably add a new message to the array of messages held in state.');" "Dispatching <code>addMessage</code> against the store should immutably add a new message to the array of messages held in state.",
"testString":
"assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch(addMessage('__A__TEST__MESSAGE')); const addState = store.getState(); return (isFrozen && addState[0] === '__A__TEST__MESSAGE'); })(), 'Dispatching <code>addMessage</code> against the store should immutably add a new message to the array of messages held in state.');"
}, },
{ {
"text": "The <code>messageReducer</code> should return the current state if called with any other actions.", "text":
"testString": "assert((function() { const addState = store.getState(); store.dispatch({type: 'FAKE_ACTION'}); const testState = store.getState(); return (addState === testState); })(), 'The <code>messageReducer</code> should return the current state if called with any other actions.');" "The <code>messageReducer</code> should return the current state if called with any other actions.",
"testString":
"assert((function() { const addState = store.getState(); store.dispatch({type: 'FAKE_ACTION'}); const testState = store.getState(); return (addState === testState); })(), 'The <code>messageReducer</code> should return the current state if called with any other actions.');"
} }
], ],
"solutions": [ "solutions": [
@ -216,7 +247,7 @@
"description": [ "description": [
"In the last challenge, you created a Redux store to handle the messages array and created an action for adding new messages. The next step is to provide React access to the Redux store and the actions it needs to dispatch updates. React Redux provides its <code>react-redux</code> package to help accomplish these tasks.", "In the last challenge, you created a Redux store to handle the messages array and created an action for adding new messages. The next step is to provide React access to the Redux store and the actions it needs to dispatch updates. React Redux provides its <code>react-redux</code> package to help accomplish these tasks.",
"React Redux provides a small API with two key features: <code>Provider</code> and <code>connect</code>. Another challenge covers <code>connect</code>. The <code>Provider</code> is a wrapper component from React Redux that wraps your React app. This wrapper then allows you to access the Redux <code>store</code> and <code>dispatch</code> functions throughout your component tree. <code>Provider</code> takes two props, the Redux store and the child components of your app. Defining the <code>Provider</code> for an App component might look like this:", "React Redux provides a small API with two key features: <code>Provider</code> and <code>connect</code>. Another challenge covers <code>connect</code>. The <code>Provider</code> is a wrapper component from React Redux that wraps your React app. This wrapper then allows you to access the Redux <code>store</code> and <code>dispatch</code> functions throughout your component tree. <code>Provider</code> takes two props, the Redux store and the child components of your app. Defining the <code>Provider</code> for an App component might look like this:",
"<blockquote>&lt;Provider store={store}&gt;<br> &lt;App/&gt;<br>&lt;/Provider&gt;</blockquote>", "<blockquote>&lt;Provider store={store}&gt;<br>&nbsp;&nbsp;&lt;App/&gt;<br>&lt;/Provider&gt;</blockquote>",
"<hr>", "<hr>",
"The code editor now shows all your Redux and React code from the past several challenges. It includes the Redux store, actions, and the <code>DisplayMessages</code> component. The only new piece is the <code>AppWrapper</code> component at the bottom. Use this top level component to render the <code>Provider</code> from <code>ReactRedux</code>, and pass the Redux store as a prop. Then render the <code>DisplayMessages</code> component as a child. Once you are finished, you should see your React component rendered to the page.", "The code editor now shows all your Redux and React code from the past several challenges. It includes the Redux store, actions, and the <code>DisplayMessages</code> component. The only new piece is the <code>AppWrapper</code> component at the bottom. Use this top level component to render the <code>Provider</code> from <code>ReactRedux</code>, and pass the Redux store as a prop. Then render the <code>DisplayMessages</code> component as a child. Once you are finished, you should see your React component rendered to the page.",
"<strong>Note:</strong>&nbsp;React Redux is available as a global variable here, so you can access the Provider with dot notation. The code in the editor takes advantage of this and sets it to a constant <code>Provider</code> for you to use in the <code>AppWrapper</code> render method." "<strong>Note:</strong>&nbsp;React Redux is available as a global variable here, so you can access the Provider with dot notation. The code in the editor takes advantage of this and sets it to a constant <code>Provider</code> for you to use in the <code>AppWrapper</code> render method."
@ -315,19 +346,26 @@
"tests": [ "tests": [
{ {
"text": "The <code>AppWrapper</code> should render.", "text": "The <code>AppWrapper</code> should render.",
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })(), 'The <code>AppWrapper</code> should render.');" "testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })(), 'The <code>AppWrapper</code> should render.');"
}, },
{ {
"text": "The <code>Provider</code> wrapper component should have a prop of <code>store</code> passed to it, equal to the Redux store.", "text":
"testString": "getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return getUserInput('index').replace(/\\s/g,'').includes('<Providerstore={store}>'); })(), 'The <code>Provider</code> wrapper component should have a prop of <code>store</code> passed to it, equal to the Redux store.');" "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}>'); })(), 'The <code>Provider</code> wrapper component should have a prop of <code>store</code> passed to it, equal to the Redux store.');"
}, },
{ {
"text": "<code>DisplayMessages</code> should render as a child of <code>AppWrapper</code>.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1; })(), '<code>DisplayMessages</code> should render as a child of <code>AppWrapper</code>.');" "<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; })(), '<code>DisplayMessages</code> should render as a child of <code>AppWrapper</code>.');"
}, },
{ {
"text": "The <code>DisplayMessages</code> component should render an h2, input, button, and <code>ul</code> element.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('div').length === 1 && mockedComponent.find('h2').length === 1 && mockedComponent.find('button').length === 1 && mockedComponent.find('ul').length === 1; })(), 'The <code>DisplayMessages</code> component should render an h2, input, button, and <code>ul</code> element.');" "The <code>DisplayMessages</code> component should render an h2, input, button, and <code>ul</code> element.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('div').length === 1 && mockedComponent.find('h2').length === 1 && mockedComponent.find('button').length === 1 && mockedComponent.find('ul').length === 1; })(), 'The <code>DisplayMessages</code> component should render an h2, input, button, and <code>ul</code> element.');"
} }
], ],
"solutions": [ "solutions": [
@ -367,19 +405,24 @@
"tests": [ "tests": [
{ {
"text": "The const <code>state</code> should be an empty array.", "text": "The const <code>state</code> should be an empty array.",
"testString": "assert(Array.isArray(state) && state.length === 0, 'The const <code>state</code> should be an empty array.');" "testString":
"assert(Array.isArray(state) && state.length === 0, 'The const <code>state</code> should be an empty array.');"
}, },
{ {
"text": "<code>mapStateToProps</code> should be a function.", "text": "<code>mapStateToProps</code> should be a function.",
"testString": "assert(typeof mapStateToProps === 'function', '<code>mapStateToProps</code> should be a function.');" "testString":
"assert(typeof mapStateToProps === 'function', '<code>mapStateToProps</code> should be a function.');"
}, },
{ {
"text": "<code>mapStateToProps</code> should return an object.", "text": "<code>mapStateToProps</code> should return an object.",
"testString": "assert(typeof mapStateToProps() === 'object', '<code>mapStateToProps</code> should return an object.');" "testString":
"assert(typeof mapStateToProps() === 'object', '<code>mapStateToProps</code> should return an object.');"
}, },
{ {
"text": "Passing an array as state to <code>mapStateToProps</code> should return this array assigned to a key of <code>messages</code>.", "text":
"testString": "assert(mapStateToProps(['messages']).messages.pop() === 'messages', 'Passing an array as state to <code>mapStateToProps</code> should return this array assigned to a key of <code>messages</code>.');" "Passing an array as state to <code>mapStateToProps</code> should return this array assigned to a key of <code>messages</code>.",
"testString":
"assert(mapStateToProps(['messages']).messages.pop() === 'messages', 'Passing an array as state to <code>mapStateToProps</code> should return this array assigned to a key of <code>messages</code>.');"
} }
], ],
"solutions": [ "solutions": [
@ -397,7 +440,7 @@
"description": [ "description": [
"The <code>mapDispatchToProps()</code> function is used to provide specific action creators to your React components so they can dispatch actions against the Redux store. It's similar in structure to the <code>mapStateToProps()</code> function you wrote in the last challenge. It returns an object that maps dispatch actions to property names, which become component <code>props</code>. However, instead of returning a piece of <code>state</code>, each property returns a function that calls <code>dispatch</code> with an action creator and any relevant action data. You have access to this <code>dispatch</code> because it's passed in to <code>mapDispatchToProps()</code> as a parameter when you define the function, just like you passed <code>state</code> to <code>mapStateToProps()</code>. Behind the scenes, React Redux is using Redux's <code>store.dispatch()</code> to conduct these dispatches with <code>mapDispatchToProps()</code>. This is similar to how it uses <code>store.subscribe()</code> for components that are mapped to <code>state</code>.", "The <code>mapDispatchToProps()</code> function is used to provide specific action creators to your React components so they can dispatch actions against the Redux store. It's similar in structure to the <code>mapStateToProps()</code> function you wrote in the last challenge. It returns an object that maps dispatch actions to property names, which become component <code>props</code>. However, instead of returning a piece of <code>state</code>, each property returns a function that calls <code>dispatch</code> with an action creator and any relevant action data. You have access to this <code>dispatch</code> because it's passed in to <code>mapDispatchToProps()</code> as a parameter when you define the function, just like you passed <code>state</code> to <code>mapStateToProps()</code>. Behind the scenes, React Redux is using Redux's <code>store.dispatch()</code> to conduct these dispatches with <code>mapDispatchToProps()</code>. This is similar to how it uses <code>store.subscribe()</code> for components that are mapped to <code>state</code>.",
"For example, you have a <code>loginUser()</code> action creator that takes a <code>username</code> as an action payload. The object returned from <code>mapDispatchToProps()</code> for this action creator would look something like:", "For example, you have a <code>loginUser()</code> action creator that takes a <code>username</code> as an action payload. The object returned from <code>mapDispatchToProps()</code> for this action creator would look something like:",
"<blockquote>{<br> submitLoginUser: function(username) {<br> dispatch(loginUser(username));<br> }<br>}</blockquote>", "<blockquote>{<br>&nbsp;&nbsp;submitLoginUser: function(username) {<br>&nbsp;&nbsp;&nbsp;&nbsp;dispatch(loginUser(username));<br>&nbsp;&nbsp;}<br>}</blockquote>",
"<hr>", "<hr>",
"The code editor provides an action creator called <code>addMessage()</code>. Write the function <code>mapDispatchToProps()</code> that takes <code>dispatch</code> as an argument, then returns an object. The object should have a property <code>submitNewMessage</code> set to the dispatch function, which takes a parameter for the new message to add when it dispatches <code>addMessage()</code>." "The code editor provides an action creator called <code>addMessage()</code>. Write the function <code>mapDispatchToProps()</code> that takes <code>dispatch</code> as an argument, then returns an object. The object should have a property <code>submitNewMessage</code> set to the dispatch function, which takes a parameter for the new message to add when it dispatches <code>addMessage()</code>."
], ],
@ -423,20 +466,26 @@
}, },
"tests": [ "tests": [
{ {
"text": "<code>addMessage</code> should return an object with keys <code>type</code> and <code>message</code>.", "text":
"testString": "assert((function() { const addMessageTest = addMessage(); return ( addMessageTest.hasOwnProperty('type') && addMessageTest.hasOwnProperty('message')); })(), '<code>addMessage</code> should return an object with keys <code>type</code> and <code>message</code>.');" "<code>addMessage</code> should return an object with keys <code>type</code> and <code>message</code>.",
"testString":
"assert((function() { const addMessageTest = addMessage(); return ( addMessageTest.hasOwnProperty('type') && addMessageTest.hasOwnProperty('message')); })(), '<code>addMessage</code> should return an object with keys <code>type</code> and <code>message</code>.');"
}, },
{ {
"text": "<code>mapDispatchToProps</code> should be a function.", "text": "<code>mapDispatchToProps</code> should be a function.",
"testString": "assert(typeof mapDispatchToProps === 'function', '<code>mapDispatchToProps</code> should be a function.');" "testString":
"assert(typeof mapDispatchToProps === 'function', '<code>mapDispatchToProps</code> should be a function.');"
}, },
{ {
"text": "<code>mapDispatchToProps</code> should return an object.", "text": "<code>mapDispatchToProps</code> should return an object.",
"testString": "assert(typeof mapDispatchToProps() === 'object', '<code>mapDispatchToProps</code> should return an object.');" "testString":
"assert(typeof mapDispatchToProps() === 'object', '<code>mapDispatchToProps</code> should return an object.');"
}, },
{ {
"text": "Dispatching <code>addMessage</code> with <code>submitNewMessage</code> from <code>mapDispatchToProps</code> should return a message to the dispatch function.", "text":
"testString": "assert((function() { let testAction; const dispatch = (fn) => { testAction = fn; }; let dispatchFn = mapDispatchToProps(dispatch); dispatchFn.submitNewMessage('__TEST__MESSAGE__'); return (testAction.type === 'ADD' && testAction.message === '__TEST__MESSAGE__'); })(), 'Dispatching <code>addMessage</code> with <code>submitNewMessage</code> from <code>mapDispatchToProps</code> should return a message to the dispatch function.');" "Dispatching <code>addMessage</code> with <code>submitNewMessage</code> from <code>mapDispatchToProps</code> should return a message to the dispatch function.",
"testString":
"assert((function() { let testAction; const dispatch = (fn) => { testAction = fn; }; let dispatchFn = mapDispatchToProps(dispatch); dispatchFn.submitNewMessage('__TEST__MESSAGE__'); return (testAction.type === 'ADD' && testAction.message === '__TEST__MESSAGE__'); })(), 'Dispatching <code>addMessage</code> with <code>submitNewMessage</code> from <code>mapDispatchToProps</code> should return a message to the dispatch function.');"
} }
], ],
"solutions": [ "solutions": [
@ -521,15 +570,20 @@
"tests": [ "tests": [
{ {
"text": "The <code>Presentational</code> component should render.", "text": "The <code>Presentational</code> component should render.",
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('Presentational').length === 1; })(), 'The <code>Presentational</code> component should render.');" "testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('Presentational').length === 1; })(), 'The <code>Presentational</code> component should render.');"
}, },
{ {
"text": "The <code>Presentational</code> component should receive a prop <code>messages</code> via <code>connect</code>.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const props = mockedComponent.find('Presentational').props(); return props.messages === '__INITIAL__STATE__'; })(), 'The <code>Presentational</code> component should receive a prop <code>messages</code> via <code>connect</code>.');" "The <code>Presentational</code> component should receive a prop <code>messages</code> via <code>connect</code>.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const props = mockedComponent.find('Presentational').props(); return props.messages === '__INITIAL__STATE__'; })(), 'The <code>Presentational</code> component should receive a prop <code>messages</code> via <code>connect</code>.');"
}, },
{ {
"text": "The <code>Presentational</code> component should receive a prop <code>submitNewMessage</code> via <code>connect</code>.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const props = mockedComponent.find('Presentational').props(); return typeof props.submitNewMessage === 'function'; })(), 'The <code>Presentational</code> component should receive a prop <code>submitNewMessage</code> via <code>connect</code>.');" "The <code>Presentational</code> component should receive a prop <code>submitNewMessage</code> via <code>connect</code>.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const props = mockedComponent.find('Presentational').props(); return typeof props.submitNewMessage === 'function'; })(), 'The <code>Presentational</code> component should receive a prop <code>submitNewMessage</code> via <code>connect</code>.');"
} }
], ],
"solutions": [ "solutions": [
@ -662,23 +716,32 @@
"tests": [ "tests": [
{ {
"text": "The <code>AppWrapper</code> should render to the page.", "text": "The <code>AppWrapper</code> should render to the page.",
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })(), 'The <code>AppWrapper</code> should render to the page.');" "testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })(), 'The <code>AppWrapper</code> should render to the page.');"
}, },
{ {
"text": "The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('Presentational').length === 1; })(), 'The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.');" "The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('Presentational').length === 1; })(), 'The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.');"
}, },
{ {
"text": "The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); return ( PresentationalComponent.find('div').length === 1 && PresentationalComponent.find('h2').length === 1 && PresentationalComponent.find('button').length === 1 && PresentationalComponent.find('ul').length === 1 ); })(), 'The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.');" "The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); return ( PresentationalComponent.find('div').length === 1 && PresentationalComponent.find('h2').length === 1 && PresentationalComponent.find('button').length === 1 && PresentationalComponent.find('ul').length === 1 ); })(), 'The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.');"
}, },
{ {
"text": "The <code>Presentational</code> component should receive <code>messages</code> from the Redux store as a prop.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); const props = PresentationalComponent.props(); return Array.isArray(props.messages); })(), 'The <code>Presentational</code> component should receive <code>messages</code> from the Redux store as a prop.');" "The <code>Presentational</code> component should receive <code>messages</code> from the Redux store as a prop.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); const props = PresentationalComponent.props(); return Array.isArray(props.messages); })(), 'The <code>Presentational</code> component should receive <code>messages</code> from the Redux store as a prop.');"
}, },
{ {
"text": "The <code>Presentational</code> component should receive the <code>submitMessage</code> action creator as a prop.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); const props = PresentationalComponent.props(); return typeof props.submitNewMessage === 'function'; })(), 'The <code>Presentational</code> component should receive the <code>submitMessage</code> action creator as a prop.');" "The <code>Presentational</code> component should receive the <code>submitMessage</code> action creator as a prop.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); const props = PresentationalComponent.props(); return typeof props.submitNewMessage === 'function'; })(), 'The <code>Presentational</code> component should receive the <code>submitMessage</code> action creator as a prop.');"
} }
], ],
"solutions": [ "solutions": [
@ -810,39 +873,56 @@
"tests": [ "tests": [
{ {
"text": "The <code>AppWrapper</code> should render to the page.", "text": "The <code>AppWrapper</code> should render to the page.",
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })(), 'The <code>AppWrapper</code> should render to the page.');" "testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })(), 'The <code>AppWrapper</code> should render to the page.');"
}, },
{ {
"text": "The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('Presentational').length === 1; })(), 'The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.');" "The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('Presentational').length === 1; })(), 'The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.');"
}, },
{ {
"text": "The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); return ( PresentationalComponent.find('div').length === 1 && PresentationalComponent.find('h2').length === 1 && PresentationalComponent.find('button').length === 1 && PresentationalComponent.find('ul').length === 1 ); })(), 'The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.');" "The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); return ( PresentationalComponent.find('div').length === 1 && PresentationalComponent.find('h2').length === 1 && PresentationalComponent.find('button').length === 1 && PresentationalComponent.find('ul').length === 1 ); })(), 'The <code>Presentational</code> component should render an <code>h2</code>, <code>input</code>, <code>button</code>, and <code>ul</code> elements.');"
}, },
{ {
"text": "The <code>Presentational</code> component should receive <code>messages</code> from the Redux store as a prop.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); const props = PresentationalComponent.props(); return Array.isArray(props.messages); })(), 'The <code>Presentational</code> component should receive <code>messages</code> from the Redux store as a prop.');" "The <code>Presentational</code> component should receive <code>messages</code> from the Redux store as a prop.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); const props = PresentationalComponent.props(); return Array.isArray(props.messages); })(), 'The <code>Presentational</code> component should receive <code>messages</code> from the Redux store as a prop.');"
}, },
{ {
"text": "The <code>Presentational</code> component should receive the <code>submitMessage</code> action creator as a prop.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); const props = PresentationalComponent.props(); return typeof props.submitNewMessage === 'function'; })(), 'The <code>Presentational</code> component should receive the <code>submitMessage</code> action creator as a prop.');" "The <code>Presentational</code> component should receive the <code>submitMessage</code> action creator as a prop.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalComponent = mockedComponent.find('Presentational'); const props = PresentationalComponent.props(); return typeof props.submitNewMessage === 'function'; })(), 'The <code>Presentational</code> component should receive the <code>submitMessage</code> action creator as a prop.');"
}, },
{ {
"text": "The state of the <code>Presentational</code> component should contain one property, <code>input</code>, which is initialized to an empty string.", "text":
"testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalState = mockedComponent.find('Presentational').instance().state; return typeof PresentationalState.input === 'string' && Object.keys(PresentationalState).length === 1; })(), 'The state of the <code>Presentational</code> component should contain one property, <code>input</code>, which is initialized to an empty string.');" "The state of the <code>Presentational</code> component should contain one property, <code>input</code>, which is initialized to an empty string.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const PresentationalState = mockedComponent.find('Presentational').instance().state; return typeof PresentationalState.input === 'string' && Object.keys(PresentationalState).length === 1; })(), 'The state of the <code>Presentational</code> component should contain one property, <code>input</code>, which is initialized to an empty string.');"
}, },
{ {
"text": "Typing in the <code>input</code> element should update the state of the <code>Presentational</code> component.", "text":
"testString": "async () => { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const testValue = '__MOCK__INPUT__'; const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); let initialInput = mockedComponent.find('Presentational').find('input'); const changed = () => { causeChange(mockedComponent, testValue); return waitForIt(() => mockedComponent )}; const updated = await changed(); const updatedInput = updated.find('Presentational').find('input'); assert(initialInput.props().value === '' && updatedInput.props().value === '__MOCK__INPUT__', 'Typing in the <code>input</code> element should update the state of the <code>Presentational</code> component.'); }; " "Typing in the <code>input</code> element should update the state of the <code>Presentational</code> component.",
"testString":
"async () => { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const testValue = '__MOCK__INPUT__'; const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); let initialInput = mockedComponent.find('Presentational').find('input'); const changed = () => { causeChange(mockedComponent, testValue); return waitForIt(() => mockedComponent )}; const updated = await changed(); const updatedInput = updated.find('Presentational').find('input'); assert(initialInput.props().value === '' && updatedInput.props().value === '__MOCK__INPUT__', 'Typing in the <code>input</code> element should update the state of the <code>Presentational</code> component.'); }; "
}, },
{ {
"text": "Dispatching the <code>submitMessage</code> on the <code>Presentational</code> component should update Redux store and clear the input in local state.", "text":
"testString": "async () => { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); let beforeProps = mockedComponent.find('Presentational').props(); const testValue = '__TEST__EVENT__INPUT__'; const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const changed = () => { causeChange(mockedComponent, testValue); return waitForIt(() => mockedComponent )}; const clickButton = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterChange = await changed(); const afterChangeInput = afterChange.find('input').props().value; const afterClick = await clickButton(); const afterProps = mockedComponent.find('Presentational').props(); assert(beforeProps.messages.length === 0 && afterChangeInput === testValue && afterProps.messages.pop() === testValue && afterClick.find('input').props().value === '', 'Dispatching the <code>submitMessage</code> on the <code>Presentational</code> component should update Redux store and clear the input in local state.'); }; " "Dispatching the <code>submitMessage</code> on the <code>Presentational</code> component should update Redux store and clear the input in local state.",
"testString":
"async () => { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); let beforeProps = mockedComponent.find('Presentational').props(); const testValue = '__TEST__EVENT__INPUT__'; const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const changed = () => { causeChange(mockedComponent, testValue); return waitForIt(() => mockedComponent )}; const clickButton = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterChange = await changed(); const afterChangeInput = afterChange.find('input').props().value; const afterClick = await clickButton(); const afterProps = mockedComponent.find('Presentational').props(); assert(beforeProps.messages.length === 0 && afterChangeInput === testValue && afterProps.messages.pop() === testValue && afterClick.find('input').props().value === '', 'Dispatching the <code>submitMessage</code> on the <code>Presentational</code> component should update Redux store and clear the input in local state.'); }; "
}, },
{ {
"text": "The <code>Presentational</code> component should render the <code>messages</code> from the Redux store.", "text":
"testString": "async () => { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); let beforeProps = mockedComponent.find('Presentational').props(); const testValue = '__TEST__EVENT__INPUT__'; const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const changed = () => { causeChange(mockedComponent, testValue); return waitForIt(() => mockedComponent )}; const clickButton = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterChange = await changed(); const afterChangeInput = afterChange.find('input').props().value; const afterClick = await clickButton(); const afterProps = mockedComponent.find('Presentational').props(); assert(beforeProps.messages.length === 0 && afterChangeInput === testValue && afterProps.messages.pop() === testValue && afterClick.find('input').props().value === '' && afterClick.find('ul').childAt(0).text() === testValue, 'The <code>Presentational</code> component should render the <code>messages</code> from the Redux store.'); }; " "The <code>Presentational</code> component should render the <code>messages</code> from the Redux store.",
"testString":
"async () => { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); let beforeProps = mockedComponent.find('Presentational').props(); const testValue = '__TEST__EVENT__INPUT__'; const causeChange = (c, v) => c.find('input').simulate('change', { target: { value: v }}); const changed = () => { causeChange(mockedComponent, testValue); return waitForIt(() => mockedComponent )}; const clickButton = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent )}; const afterChange = await changed(); const afterChangeInput = afterChange.find('input').props().value; const afterClick = await clickButton(); const afterProps = mockedComponent.find('Presentational').props(); assert(beforeProps.messages.length === 0 && afterChangeInput === testValue && afterProps.messages.pop() === testValue && afterClick.find('input').props().value === '' && afterClick.find('ul').childAt(0).text() === testValue, 'The <code>Presentational</code> component should render the <code>messages</code> from the Redux store.'); }; "
} }
], ],
"solutions": [ "solutions": [
@ -901,13 +981,13 @@
}, },
"tests": [ "tests": [
{ {
"text": "The message <code>Now I know React and Redux!</code> should be logged to the console.", "text":
"testString": "assert(editor.getValue().includes('console.log(\"Now I know React and Redux!\")') || editor.getValue().includes('console.log(\\'Now I know React and Redux!\\')'), 'The message <code>Now I know React and Redux!</code> should be logged to the console.');" "The message <code>Now I know React and Redux!</code> should be logged to the console.",
"testString":
"assert(editor.getValue().includes('console.log(\"Now I know React and Redux!\")') || editor.getValue().includes('console.log(\\'Now I know React and Redux!\\')'), 'The message <code>Now I know React and Redux!</code> should be logged to the console.');"
} }
], ],
"solutions": [ "solutions": ["console.log('Now I know React and Redux!');"],
"console.log('Now I know React and Redux!');"
],
"challengeType": 6, "challengeType": 6,
"isRequired": false, "isRequired": false,
"translations": {}, "translations": {},

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,8 @@
"src": "https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js" "src": "https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js"
}, },
{ {
"src": "https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.2.0/redux-thunk.min.js" "src":
"https://cdnjs.cloudflare.com/ajax/libs/redux-thunk/2.2.0/redux-thunk.min.js"
} }
], ],
"challenges": [ "challenges": [
@ -48,11 +49,13 @@
"tests": [ "tests": [
{ {
"text": "The redux store exists.", "text": "The redux store exists.",
"testString": "assert(typeof store.getState === 'function', 'The redux store exists.');" "testString":
"assert(typeof store.getState === 'function', 'The redux store exists.');"
}, },
{ {
"text": "The redux store has a value of 5 for the state.", "text": "The redux store has a value of 5 for the state.",
"testString": "assert(store.getState()=== 5, 'The redux store has a value of 5 for the state.');" "testString":
"assert(store.getState()=== 5, 'The redux store has a value of 5 for the state.');"
} }
], ],
"solutions": [ "solutions": [
@ -91,12 +94,16 @@
}, },
"tests": [ "tests": [
{ {
"text": "The redux store should have a value of 5 for the initial state.", "text":
"testString": "assert(store.getState()===5, 'The redux store should have a value of 5 for the initial state.');" "The redux store should have a value of 5 for the initial state.",
"testString":
"assert(store.getState()===5, 'The redux store should have a value of 5 for the initial state.');"
}, },
{ {
"text": "A variable <code>currentState</code> should exist and should be assigned the current state of the Redux store.", "text":
"testString": "getUserInput => assert(currentState === 5 && getUserInput('index').includes('store.getState()'), 'A variable <code>currentState</code> should exist and should be assigned the current state of the Redux store.');" "A variable <code>currentState</code> should exist and should be assigned the current state of the Redux store.",
"testString":
"getUserInput => assert(currentState === 5 && getUserInput('index').includes('store.getState()'), 'A variable <code>currentState</code> should exist and should be assigned the current state of the Redux store.');"
} }
], ],
"solutions": [ "solutions": [
@ -122,10 +129,7 @@
"key": "indexjsx", "key": "indexjsx",
"ext": "jsx", "ext": "jsx",
"name": "index", "name": "index",
"contents": [ "contents": ["// Define an action here:", ""],
"// Define an action here:",
""
],
"head": [], "head": [],
"tail": [] "tail": []
} }
@ -133,16 +137,17 @@
"tests": [ "tests": [
{ {
"text": "An action object should exist.", "text": "An action object should exist.",
"testString": "assert((function() { return typeof action === 'object' })(), 'An action object should exist.');" "testString":
"assert((function() { return typeof action === 'object' })(), 'An action object should exist.');"
}, },
{ {
"text": "The action should have a key property type with value <code>LOGIN</code>.", "text":
"testString": "assert((function() { return action.type === 'LOGIN' })(), 'The action should have a key property type with value <code>LOGIN</code>.');" "The action should have a key property type with value <code>LOGIN</code>.",
"testString":
"assert((function() { return action.type === 'LOGIN' })(), 'The action should have a key property type with value <code>LOGIN</code>.');"
} }
], ],
"solutions": [ "solutions": ["const action = {\n type: 'LOGIN'\n}"],
"const action = {\n type: 'LOGIN'\n}"
],
"challengeType": 6, "challengeType": 6,
"isRequired": false, "isRequired": false,
"translations": {}, "translations": {},
@ -176,15 +181,20 @@
"tests": [ "tests": [
{ {
"text": "The function <code>actionCreator</code> should exist.", "text": "The function <code>actionCreator</code> should exist.",
"testString": "assert(typeof actionCreator === 'function', 'The function <code>actionCreator</code> should exist.');" "testString":
"assert(typeof actionCreator === 'function', 'The function <code>actionCreator</code> should exist.');"
}, },
{ {
"text": "Running the <code>actionCreator</code> function should return the action object.", "text":
"testString": "assert(typeof action === 'object', 'Running the <code>actionCreator</code> function should return the action object.');" "Running the <code>actionCreator</code> function should return the action object.",
"testString":
"assert(typeof action === 'object', 'Running the <code>actionCreator</code> function should return the action object.');"
}, },
{ {
"text": "The returned action should have a key property type with value <code>LOGIN</code>.", "text":
"testString": "assert(action.type === 'LOGIN', 'The returned action should have a key property type with value <code>LOGIN</code>.');" "The returned action should have a key property type with value <code>LOGIN</code>.",
"testString":
"assert(action.type === 'LOGIN', 'The returned action should have a key property type with value <code>LOGIN</code>.');"
} }
], ],
"solutions": [ "solutions": [
@ -231,16 +241,22 @@
}, },
"tests": [ "tests": [
{ {
"text": "Calling the function <code>loginAction</code> should return an object with <code>type</code> property set to the string <code>LOGIN</code>.", "text":
"testString": "assert(loginAction().type === 'LOGIN', 'Calling the function <code>loginAction</code> should return an object with <code>type</code> property set to the string <code>LOGIN</code>.');" "Calling the function <code>loginAction</code> should return an object with <code>type</code> property set to the string <code>LOGIN</code>.",
"testString":
"assert(loginAction().type === 'LOGIN', 'Calling the function <code>loginAction</code> should return an object with <code>type</code> property set to the string <code>LOGIN</code>.');"
}, },
{ {
"text": "The store should be initialized with an object with property <code>login</code> set to <code>false</code>.", "text":
"testString": "assert(store.getState().login === false, 'The store should be initialized with an object with property <code>login</code> set to <code>false</code>.');" "The store should be initialized with an object with property <code>login</code> set to <code>false</code>.",
"testString":
"assert(store.getState().login === false, 'The store should be initialized with an object with property <code>login</code> set to <code>false</code>.');"
}, },
{ {
"text": "The <code>store.dispatch()</code> method should be used to dispatch an action of type <code>LOGIN</code>.", "text":
"testString": "getUserInput => assert((function() { let noWhiteSpace = getUserInput('index').replace(/\\s/g,''); return noWhiteSpace.includes('store.dispatch(loginAction())') || noWhiteSpace.includes('store.dispatch({type: \\'LOGIN\\'})') === true })(), 'The <code>store.dispatch()</code> method should be used to dispatch an action of type <code>LOGIN</code>.');" "The <code>store.dispatch()</code> method should be used to dispatch an action of type <code>LOGIN</code>.",
"testString":
"getUserInput => assert((function() { let noWhiteSpace = getUserInput('index').replace(/\\s/g,''); return noWhiteSpace.includes('store.dispatch(loginAction())') || noWhiteSpace.includes('store.dispatch({type: \\'LOGIN\\'})') === true })(), 'The <code>store.dispatch()</code> method should be used to dispatch an action of type <code>LOGIN</code>.');"
} }
], ],
"solutions": [ "solutions": [
@ -291,20 +307,28 @@
}, },
"tests": [ "tests": [
{ {
"text": "Calling the function <code>loginAction</code> should return an object with type property set to the string <code>LOGIN</code>.", "text":
"testString": "assert(loginAction().type === 'LOGIN', 'Calling the function <code>loginAction</code> should return an object with type property set to the string <code>LOGIN</code>.');" "Calling the function <code>loginAction</code> should return an object with type property set to the string <code>LOGIN</code>.",
"testString":
"assert(loginAction().type === 'LOGIN', 'Calling the function <code>loginAction</code> should return an object with type property set to the string <code>LOGIN</code>.');"
}, },
{ {
"text": "The store should be initialized with an object with property <code>login</code> set to <code>false</code>.", "text":
"testString": "assert(store.getState().login === false, 'The store should be initialized with an object with property <code>login</code> set to <code>false</code>.');" "The store should be initialized with an object with property <code>login</code> set to <code>false</code>.",
"testString":
"assert(store.getState().login === false, 'The store should be initialized with an object with property <code>login</code> set to <code>false</code>.');"
}, },
{ {
"text": "Dispatching <code>loginAction</code> should update the <code>login</code> property in the store state to <code>true</code>.", "text":
"testString": "assert((function() { const initialState = store.getState(); store.dispatch(loginAction()); const afterState = store.getState(); return initialState.login === false && afterState.login === true })(), 'Dispatching <code>loginAction</code> should update the <code>login</code> property in the store state to <code>true</code>.');" "Dispatching <code>loginAction</code> should update the <code>login</code> property in the store state to <code>true</code>.",
"testString":
"assert((function() { const initialState = store.getState(); store.dispatch(loginAction()); const afterState = store.getState(); return initialState.login === false && afterState.login === true })(), 'Dispatching <code>loginAction</code> should update the <code>login</code> property in the store state to <code>true</code>.');"
}, },
{ {
"text": "If the action is not of type <code>LOGIN</code>, the store should return the current state.", "text":
"testString": "assert((function() { store.dispatch({type: '__TEST__ACTION__'}); let afterTest = store.getState(); return typeof afterTest === 'object' && afterTest.hasOwnProperty('login') })(), 'If the action is not of type <code>LOGIN</code>, the store should return the current state.');" "If the action is not of type <code>LOGIN</code>, the store should return the current state.",
"testString":
"assert((function() { store.dispatch({type: '__TEST__ACTION__'}); let afterTest = store.getState(); return typeof afterTest === 'object' && afterTest.hasOwnProperty('login') })(), 'If the action is not of type <code>LOGIN</code>, the store should return the current state.');"
} }
], ],
"solutions": [ "solutions": [
@ -361,28 +385,40 @@
}, },
"tests": [ "tests": [
{ {
"text": "Calling the function <code>loginUser</code> should return an object with type property set to the string <code>LOGIN</code>.", "text":
"testString": "assert(loginUser().type === 'LOGIN', 'Calling the function <code>loginUser</code> should return an object with type property set to the string <code>LOGIN</code>.');" "Calling the function <code>loginUser</code> should return an object with type property set to the string <code>LOGIN</code>.",
"testString":
"assert(loginUser().type === 'LOGIN', 'Calling the function <code>loginUser</code> should return an object with type property set to the string <code>LOGIN</code>.');"
}, },
{ {
"text": "Calling the function <code>logoutUser</code> should return an object with type property set to the string <code>LOGOUT</code>.", "text":
"testString": "assert(logoutUser().type === 'LOGOUT', 'Calling the function <code>logoutUser</code> should return an object with type property set to the string <code>LOGOUT</code>.');" "Calling the function <code>logoutUser</code> should return an object with type property set to the string <code>LOGOUT</code>.",
"testString":
"assert(logoutUser().type === 'LOGOUT', 'Calling the function <code>logoutUser</code> should return an object with type property set to the string <code>LOGOUT</code>.');"
}, },
{ {
"text": "The store should be initialized with an object with an <code>authenticated</code> property set to <code>false</code>.", "text":
"testString": "assert(store.getState().authenticated === false, 'The store should be initialized with an object with an <code>authenticated</code> property set to <code>false</code>.');" "The store should be initialized with an object with an <code>authenticated</code> property set to <code>false</code>.",
"testString":
"assert(store.getState().authenticated === false, 'The store should be initialized with an object with an <code>authenticated</code> property set to <code>false</code>.');"
}, },
{ {
"text": "Dispatching <code>loginUser</code> should update the <code>authenticated</code> property in the store state to <code>true</code>.", "text":
"testString": "assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })(), 'Dispatching <code>loginUser</code> should update the <code>authenticated</code> property in the store state to <code>true</code>.');" "Dispatching <code>loginUser</code> should update the <code>authenticated</code> property in the store state to <code>true</code>.",
"testString":
"assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })(), 'Dispatching <code>loginUser</code> should update the <code>authenticated</code> property in the store state to <code>true</code>.');"
}, },
{ {
"text": "Dispatching <code>logoutUser</code> should update the <code>authenticated</code> property in the store state to <code>false</code>.", "text":
"testString": "assert((function() { store.dispatch(loginUser()); const loggedIn = store.getState(); store.dispatch(logoutUser()); const afterLogout = store.getState(); return loggedIn.authenticated === true && afterLogout.authenticated === false })(), 'Dispatching <code>logoutUser</code> should update the <code>authenticated</code> property in the store state to <code>false</code>.');" "Dispatching <code>logoutUser</code> should update the <code>authenticated</code> property in the store state to <code>false</code>.",
"testString":
"assert((function() { store.dispatch(loginUser()); const loggedIn = store.getState(); store.dispatch(logoutUser()); const afterLogout = store.getState(); return loggedIn.authenticated === true && afterLogout.authenticated === false })(), 'Dispatching <code>logoutUser</code> should update the <code>authenticated</code> property in the store state to <code>false</code>.');"
}, },
{ {
"text": "The <code>authReducer</code> function should handle multiple action types with a <code>switch</code> statement.", "text":
"testString": "getUserInput => assert( getUserInput('index').toString().includes('switch') && getUserInput('index').toString().includes('case') && getUserInput('index').toString().includes('default'), 'The <code>authReducer</code> function should handle multiple action types with a <code>switch</code> statement.');" "The <code>authReducer</code> function should handle multiple action types with a <code>switch</code> statement.",
"testString":
"getUserInput => assert( getUserInput('index').toString().includes('switch') && getUserInput('index').toString().includes('case') && getUserInput('index').toString().includes('default'), 'The <code>authReducer</code> function should handle multiple action types with a <code>switch</code> statement.');"
} }
], ],
"solutions": [ "solutions": [
@ -458,36 +494,52 @@
}, },
"tests": [ "tests": [
{ {
"text": "Calling the function <code>loginUser</code> should return an object with <code>type</code> property set to the string <code>LOGIN</code>.", "text":
"testString": "assert(loginUser().type === 'LOGIN', 'Calling the function <code>loginUser</code> should return an object with <code>type</code> property set to the string <code>LOGIN</code>.');" "Calling the function <code>loginUser</code> should return an object with <code>type</code> property set to the string <code>LOGIN</code>.",
"testString":
"assert(loginUser().type === 'LOGIN', 'Calling the function <code>loginUser</code> should return an object with <code>type</code> property set to the string <code>LOGIN</code>.');"
}, },
{ {
"text": "Calling the function <code>logoutUser</code> should return an object with <code>type</code> property set to the string <code>LOGOUT</code>.", "text":
"testString": "assert(logoutUser().type === 'LOGOUT', 'Calling the function <code>logoutUser</code> should return an object with <code>type</code> property set to the string <code>LOGOUT</code>.');" "Calling the function <code>logoutUser</code> should return an object with <code>type</code> property set to the string <code>LOGOUT</code>.",
"testString":
"assert(logoutUser().type === 'LOGOUT', 'Calling the function <code>logoutUser</code> should return an object with <code>type</code> property set to the string <code>LOGOUT</code>.');"
}, },
{ {
"text": "The store should be initialized with an object with property <code>login</code> set to <code>false</code>.", "text":
"testString": "assert(store.getState().authenticated === false, 'The store should be initialized with an object with property <code>login</code> set to <code>false</code>.');" "The store should be initialized with an object with property <code>login</code> set to <code>false</code>.",
"testString":
"assert(store.getState().authenticated === false, 'The store should be initialized with an object with property <code>login</code> set to <code>false</code>.');"
}, },
{ {
"text": "Dispatching <code>loginUser</code> should update the <code>login</code> property in the store state to <code>true</code>.", "text":
"testString": "assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })(), 'Dispatching <code>loginUser</code> should update the <code>login</code> property in the store state to <code>true</code>.');" "Dispatching <code>loginUser</code> should update the <code>login</code> property in the store state to <code>true</code>.",
"testString":
"assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })(), 'Dispatching <code>loginUser</code> should update the <code>login</code> property in the store state to <code>true</code>.');"
}, },
{ {
"text": "Dispatching <code>logoutUser</code> should update the <code>login</code> property in the store state to <code>false</code>.", "text":
"testString": "assert((function() { store.dispatch(loginUser()); const loggedIn = store.getState(); store.dispatch(logoutUser()); const afterLogout = store.getState(); return loggedIn.authenticated === true && afterLogout.authenticated === false })(), 'Dispatching <code>logoutUser</code> should update the <code>login</code> property in the store state to <code>false</code>.');" "Dispatching <code>logoutUser</code> should update the <code>login</code> property in the store state to <code>false</code>.",
"testString":
"assert((function() { store.dispatch(loginUser()); const loggedIn = store.getState(); store.dispatch(logoutUser()); const afterLogout = store.getState(); return loggedIn.authenticated === true && afterLogout.authenticated === false })(), 'Dispatching <code>logoutUser</code> should update the <code>login</code> property in the store state to <code>false</code>.');"
}, },
{ {
"text": "The <code>authReducer</code> function should handle multiple action types with a switch statement.", "text":
"testString": "getUserInput => assert((function() { return typeof authReducer === 'function' && getUserInput('index').toString().includes('switch') && getUserInput('index').toString().includes('case') && getUserInput('index').toString().includes('default') })(), 'The <code>authReducer</code> function should handle multiple action types with a switch statement.');" "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') })(), 'The <code>authReducer</code> function should handle multiple action types with a switch statement.');"
}, },
{ {
"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>.", "text":
"testString": "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/\\s/g,''); return (noWhiteSpace.includes('constLOGIN=\\'LOGIN\\'') || noWhiteSpace.includes('constLOGIN=\"LOGIN\"')) && (noWhiteSpace.includes('constLOGOUT=\\'LOGOUT\\'') || noWhiteSpace.includes('constLOGOUT=\"LOGOUT\"')) })(), '<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>.');" "<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":
"getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/\\s/g,''); return (noWhiteSpace.includes('constLOGIN=\\'LOGIN\\'') || noWhiteSpace.includes('constLOGIN=\"LOGIN\"')) && (noWhiteSpace.includes('constLOGOUT=\\'LOGOUT\\'') || noWhiteSpace.includes('constLOGOUT=\"LOGOUT\"')) })(), '<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>.');"
}, },
{ {
"text": "The action creators and the reducer should reference the <code>LOGIN</code> and <code>LOGOUT</code> constants.", "text":
"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') })(), 'The action creators and the reducer should reference the <code>LOGIN</code> and <code>LOGOUT</code> constants.');" "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') })(), 'The action creators and the reducer should reference the <code>LOGIN</code> and <code>LOGOUT</code> constants.');"
} }
], ],
"solutions": [ "solutions": [
@ -512,9 +564,7 @@
"key": "indexjsx", "key": "indexjsx",
"ext": "jsx", "ext": "jsx",
"name": "index", "name": "index",
"head": [ "head": ["count = 0;"],
"count = 0;"
],
"contents": [ "contents": [
"const ADD = 'ADD';", "const ADD = 'ADD';",
"", "",
@ -548,16 +598,22 @@
}, },
"tests": [ "tests": [
{ {
"text": "Dispatching the <code>ADD</code> action on the store should increment the state by <code>1</code>.", "text":
"testString": "assert((function() { const initialState = store.getState(); store.dispatch({ type: 'ADD' }); const newState = store.getState(); return newState === (initialState + 1); })(), 'Dispatching the <code>ADD</code> action on the store should increment the state by <code>1</code>.');" "Dispatching the <code>ADD</code> action on the store should increment the state by <code>1</code>.",
"testString":
"assert((function() { const initialState = store.getState(); store.dispatch({ type: 'ADD' }); const newState = store.getState(); return newState === (initialState + 1); })(), 'Dispatching the <code>ADD</code> action on the store should increment the state by <code>1</code>.');"
}, },
{ {
"text": "There should be a listener function subscribed to the store using <code>store.subscribe</code>.", "text":
"testString": "getUserInput => assert(getUserInput('index').includes('store.subscribe('), 'There should be a listener function subscribed to the store using <code>store.subscribe</code>.');" "There should be a listener function subscribed to the store using <code>store.subscribe</code>.",
"testString":
"getUserInput => assert(getUserInput('index').includes('store.subscribe('), 'There should be a listener function subscribed to the store using <code>store.subscribe</code>.');"
}, },
{ {
"text": "The callback to <code>store.subscribe</code> should also increment the global <code>count</code> variable as the store is updated.", "text":
"testString": "assert(store.getState() === count, 'The callback to <code>store.subscribe</code> should also increment the global <code>count</code> variable as the store is updated.');" "The callback to <code>store.subscribe</code> should also increment the global <code>count</code> variable as the store is updated.",
"testString":
"assert(store.getState() === count, 'The callback to <code>store.subscribe</code> should also increment the global <code>count</code> variable as the store is updated.');"
} }
], ],
"solutions": [ "solutions": [
@ -576,7 +632,7 @@
"When the state of your app begins to grow more complex, it may be tempting to divide state into multiple pieces. Instead, remember the first principle of Redux: all app state is held in a single state object in the store. Therefore, Redux provides reducer composition as a solution for a complex state model. You define multiple reducers to handle different pieces of your application's state, then compose these reducers together into one root reducer. The root reducer is then passed into the Redux <code>createStore()</code> method.", "When the state of your app begins to grow more complex, it may be tempting to divide state into multiple pieces. Instead, remember the first principle of Redux: all app state is held in a single state object in the store. Therefore, Redux provides reducer composition as a solution for a complex state model. You define multiple reducers to handle different pieces of your application's state, then compose these reducers together into one root reducer. The root reducer is then passed into the Redux <code>createStore()</code> method.",
"In order to let us combine multiple reducers together, Redux provides the <code>combineReducers()</code> method. This method accepts an object as an argument in which you define properties which associate keys to specific reducer functions. The name you give to the keys will be used by Redux as the name for the associated piece of state.", "In order to let us combine multiple reducers together, Redux provides the <code>combineReducers()</code> method. This method accepts an object as an argument in which you define properties which associate keys to specific reducer functions. The name you give to the keys will be used by Redux as the name for the associated piece of state.",
"Typically, it is a good practice to create a reducer for each piece of application state when they are distinct or unique in some way. For example, in a note-taking app with user authentication, one reducer could handle authentication while another handles the text and notes that the user is submitting. For such an application, we might write the <code>combineReducers()</code> method like this:", "Typically, it is a good practice to create a reducer for each piece of application state when they are distinct or unique in some way. For example, in a note-taking app with user authentication, one reducer could handle authentication while another handles the text and notes that the user is submitting. For such an application, we might write the <code>combineReducers()</code> method like this:",
"<blockquote>const rootReducer = Redux.combineReducers({<br> auth: authenticationReducer,<br> notes: notesReducer<br>});</blockquote>", "<blockquote>const rootReducer = Redux.combineReducers({<br>&nbsp;&nbsp;auth: authenticationReducer,<br>&nbsp;&nbsp;notes: notesReducer<br>});</blockquote>",
"Now, the key <code>notes</code> will contain all of the state associated with our notes and handled by our <code>notesReducer</code>. This is how multiple reducers can be composed to manage more complex application state. In this example, the state held in the Redux store would then be a single object containing <code>auth</code> and <code>notes</code> properties.", "Now, the key <code>notes</code> will contain all of the state associated with our notes and handled by our <code>notesReducer</code>. This is how multiple reducers can be composed to manage more complex application state. In this example, the state held in the Redux store would then be a single object containing <code>auth</code> and <code>notes</code> properties.",
"<hr>", "<hr>",
"There are <code>counterReducer()</code> and <code>authReducer()</code> functions provided in the code editor, along with a Redux store. Finish writing the <code>rootReducer()</code> function using the <code>Redux.combineReducers()</code> method. Assign <code>counterReducer</code> to a key called <code>count</code> and <code>authReducer</code> to a key called <code>auth</code>." "There are <code>counterReducer()</code> and <code>authReducer()</code> functions provided in the code editor, along with a Redux store. Finish writing the <code>rootReducer()</code> function using the <code>Redux.combineReducers()</code> method. Assign <code>counterReducer</code> to a key called <code>count</code> and <code>authReducer</code> to a key called <code>auth</code>."
@ -630,20 +686,28 @@
}, },
"tests": [ "tests": [
{ {
"text": "The <code>counterReducer</code> should increment and decrement the <code>state</code>.", "text":
"testString": "assert((function() { const initalState = store.getState().count; store.dispatch({type: INCREMENT}); store.dispatch({type: INCREMENT}); const firstState = store.getState().count; store.dispatch({type: DECREMENT}); const secondState = store.getState().count; return firstState === initalState + 2 && secondState === firstState - 1 })(), 'The <code>counterReducer</code> should increment and decrement the <code>state</code>.');" "The <code>counterReducer</code> should increment and decrement the <code>state</code>.",
"testString":
"assert((function() { const initalState = store.getState().count; store.dispatch({type: INCREMENT}); store.dispatch({type: INCREMENT}); const firstState = store.getState().count; store.dispatch({type: DECREMENT}); const secondState = store.getState().count; return firstState === initalState + 2 && secondState === firstState - 1 })(), 'The <code>counterReducer</code> should increment and decrement the <code>state</code>.');"
}, },
{ {
"text": "The <code>authReducer</code> should toggle the <code>state</code> of <code>authenticated</code> between <code>true</code> and <code>false</code>.", "text":
"testString": "assert((function() { store.dispatch({type: LOGIN}); const loggedIn = store.getState().auth.authenticated; store.dispatch({type: LOGOUT}); const loggedOut = store.getState().auth.authenticated; return loggedIn === true && loggedOut === false })(), 'The <code>authReducer</code> should toggle the <code>state</code> of <code>authenticated</code> between <code>true</code> and <code>false</code>.');" "The <code>authReducer</code> should toggle the <code>state</code> of <code>authenticated</code> between <code>true</code> and <code>false</code>.",
"testString":
"assert((function() { store.dispatch({type: LOGIN}); const loggedIn = store.getState().auth.authenticated; store.dispatch({type: LOGOUT}); const loggedOut = store.getState().auth.authenticated; return loggedIn === true && loggedOut === false })(), 'The <code>authReducer</code> should toggle the <code>state</code> of <code>authenticated</code> between <code>true</code> and <code>false</code>.');"
}, },
{ {
"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.", "text":
"testString": "assert((function() { const state = store.getState(); return typeof state.auth === 'object' && typeof state.auth.authenticated === 'boolean' && typeof state.count === 'number' })(), '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.');" "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' })(), '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.');"
}, },
{ {
"text": "The <code>rootReducer</code> should be a function that combines the <code>counterReducer</code> and the <code>authReducer</code>.", "text":
"testString": "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/\\s/g,''); return typeof rootReducer === 'function' && noWhiteSpace.includes('Redux.combineReducers') })(), 'The <code>rootReducer</code> should be a function that combines the <code>counterReducer</code> and the <code>authReducer</code>.');" "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') })(), 'The <code>rootReducer</code> should be a function that combines the <code>counterReducer</code> and the <code>authReducer</code>.');"
} }
], ],
"solutions": [ "solutions": [
@ -701,12 +765,16 @@
}, },
"tests": [ "tests": [
{ {
"text": "The action creator <code>addNoteText</code> should return an object with keys <code>type</code> and <code>text</code>.", "text":
"testString": "assert((function() { const addNoteFn = addNoteText('__TEST__NOTE'); return addNoteFn.type === ADD_NOTE && addNoteFn.text === '__TEST__NOTE' })(), 'The action creator <code>addNoteText</code> should return an object with keys <code>type</code> and <code>text</code>.');" "The action creator <code>addNoteText</code> should return an object with keys <code>type</code> and <code>text</code>.",
"testString":
"assert((function() { const addNoteFn = addNoteText('__TEST__NOTE'); return addNoteFn.type === ADD_NOTE && addNoteFn.text === '__TEST__NOTE' })(), 'The action creator <code>addNoteText</code> should return an object with keys <code>type</code> and <code>text</code>.');"
}, },
{ {
"text": "Dispatching an action of type <code>ADD_NOTE</code> with the <code>addNoteText</code> action creator should update the <code>state</code> to the string passed to the action creator.", "text":
"testString": "assert((function() { const initialState = store.getState(); store.dispatch(addNoteText('__TEST__NOTE')); const newState = store.getState(); return initialState !== newState && newState === '__TEST__NOTE' })(), 'Dispatching an action of type <code>ADD_NOTE</code> with the <code>addNoteText</code> action creator should update the <code>state</code> to the string passed to the action creator.');" "Dispatching an action of type <code>ADD_NOTE</code> with the <code>addNoteText</code> action creator should update the <code>state</code> to the string passed to the action creator.",
"testString":
"assert((function() { const initialState = store.getState(); store.dispatch(addNoteText('__TEST__NOTE')); const newState = store.getState(); return initialState !== newState && newState === '__TEST__NOTE' })(), 'Dispatching an action of type <code>ADD_NOTE</code> with the <code>addNoteText</code> action creator should update the <code>state</code> to the string passed to the action creator.');"
} }
], ],
"solutions": [ "solutions": [
@ -788,24 +856,33 @@
}, },
"tests": [ "tests": [
{ {
"text": "The <code>requestingData</code> action creator should return an object of type equal to the value of <code>REQUESTING_DATA</code>.", "text":
"testString": "assert(requestingData().type === REQUESTING_DATA, 'The <code>requestingData</code> action creator should return an object of type equal to the value of <code>REQUESTING_DATA</code>.');" "The <code>requestingData</code> action creator should return an object of type equal to the value of <code>REQUESTING_DATA</code>.",
"testString":
"assert(requestingData().type === REQUESTING_DATA, 'The <code>requestingData</code> action creator should return an object of type equal to the value of <code>REQUESTING_DATA</code>.');"
}, },
{ {
"text": "The <code>receivedData</code> action creator should return an object of type equal to the value of <code>RECEIVED_DATA</code>.", "text":
"testString": "assert(receivedData('data').type === RECEIVED_DATA, 'The <code>receivedData</code> action creator should return an object of type equal to the value of <code>RECEIVED_DATA</code>.');" "The <code>receivedData</code> action creator should return an object of type equal to the value of <code>RECEIVED_DATA</code>.",
"testString":
"assert(receivedData('data').type === RECEIVED_DATA, 'The <code>receivedData</code> action creator should return an object of type equal to the value of <code>RECEIVED_DATA</code>.');"
}, },
{ {
"text": "<code>asyncDataReducer</code> should be a function.", "text": "<code>asyncDataReducer</code> should be a function.",
"testString": "assert(typeof asyncDataReducer === 'function', '<code>asyncDataReducer</code> should be a function.');" "testString":
"assert(typeof asyncDataReducer === 'function', '<code>asyncDataReducer</code> should be a function.');"
}, },
{ {
"text": "Dispatching the requestingData action creator should update the store <code>state</code> property of fetching to <code>true</code>.", "text":
"testString": "assert((function() { const initialState = store.getState(); store.dispatch(requestingData()); const reqState = store.getState(); return initialState.fetching === false && reqState.fetching === true })(), 'Dispatching the requestingData action creator should update the store <code>state</code> property of fetching to <code>true</code>.');" "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 })(), 'Dispatching the requestingData action creator should update the store <code>state</code> property of fetching to <code>true</code>.');"
}, },
{ {
"text": "Dispatching <code>handleAsync</code> should dispatch the data request action and then dispatch the received data action after a delay.", "text":
"testString": "assert((function() { const noWhiteSpace = handleAsync.toString().replace(/\\s/g,''); return noWhiteSpace.includes('dispatch(requestingData())') === true && noWhiteSpace.includes('dispatch(receivedData(data))') === true })(), 'Dispatching <code>handleAsync</code> should dispatch the data request action and then dispatch the received data action after a delay.');" "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 })(), 'Dispatching <code>handleAsync</code> should dispatch the data request action and then dispatch the received data action after a delay.');"
} }
], ],
"solutions": [ "solutions": [
@ -848,28 +925,39 @@
}, },
"tests": [ "tests": [
{ {
"text": "The action creator <code>incAction</code> should return an action object with <code>type</code> equal to the value of <code>INCREMENT</code>", "text":
"testString": "assert(incAction().type ===INCREMENT, 'The action creator <code>incAction</code> should return an action object with <code>type</code> equal to the value of <code>INCREMENT</code>');" "The action creator <code>incAction</code> should return an action object with <code>type</code> equal to the value of <code>INCREMENT</code>",
"testString":
"assert(incAction().type ===INCREMENT, 'The action creator <code>incAction</code> should return an action object with <code>type</code> equal to the value of <code>INCREMENT</code>');"
}, },
{ {
"text": "The action creator <code>decAction</code> should return an action object with <code>type</code> equal to the value of <code>DECREMENT</code>", "text":
"testString": "assert(decAction().type === DECREMENT, 'The action creator <code>decAction</code> should return an action object with <code>type</code> equal to the value of <code>DECREMENT</code>');" "The action creator <code>decAction</code> should return an action object with <code>type</code> equal to the value of <code>DECREMENT</code>",
"testString":
"assert(decAction().type === DECREMENT, 'The action creator <code>decAction</code> should return an action object with <code>type</code> equal to the value of <code>DECREMENT</code>');"
}, },
{ {
"text": "The Redux store should initialize with a <code>state</code> of 0.", "text":
"testString": "assert(store.getState() === 0, 'The Redux store should initialize with a <code>state</code> of 0.');" "The Redux store should initialize with a <code>state</code> of 0.",
"testString":
"assert(store.getState() === 0, 'The Redux store should initialize with a <code>state</code> of 0.');"
}, },
{ {
"text": "Dispatching <code>incAction</code> on the Redux store should increment the <code>state</code> by 1.", "text":
"testString": "assert((function() { const initialState = store.getState(); store.dispatch(incAction()); const incState = store.getState(); return initialState + 1 === incState })(), 'Dispatching <code>incAction</code> on the Redux store should increment the <code>state</code> by 1.');" "Dispatching <code>incAction</code> on the Redux store should increment the <code>state</code> by 1.",
"testString":
"assert((function() { const initialState = store.getState(); store.dispatch(incAction()); const incState = store.getState(); return initialState + 1 === incState })(), 'Dispatching <code>incAction</code> on the Redux store should increment the <code>state</code> by 1.');"
}, },
{ {
"text": "Dispatching <code>decAction</code> on the Redux store should decrement the <code>state</code> by 1.", "text":
"testString": "assert((function() { const initialState = store.getState(); store.dispatch(decAction()); const decState = store.getState(); return initialState - 1 === decState })(), 'Dispatching <code>decAction</code> on the Redux store should decrement the <code>state</code> by 1.');" "Dispatching <code>decAction</code> on the Redux store should decrement the <code>state</code> by 1.",
"testString":
"assert((function() { const initialState = store.getState(); store.dispatch(decAction()); const decState = store.getState(); return initialState - 1 === decState })(), 'Dispatching <code>decAction</code> on the Redux store should decrement the <code>state</code> by 1.');"
}, },
{ {
"text": "<code>counterReducer</code> should be a function", "text": "<code>counterReducer</code> should be a function",
"testString": "assert(typeof counterReducer === 'function', '<code>counterReducer</code> should be a function');" "testString":
"assert(typeof counterReducer === 'function', '<code>counterReducer</code> should be a function');"
} }
], ],
"solutions": [ "solutions": [
@ -933,16 +1021,22 @@
}, },
"tests": [ "tests": [
{ {
"text": "The Redux store should exist and initialize with a state equal to the <code>todos</code> array in the code editor.", "text":
"testString": "assert((function() { const todos = [ 'Go to the store', 'Clean the house', 'Cook dinner', 'Learn to code' ]; const initialState = store.getState(); return Array.isArray(initialState) && initialState.join(',') === todos.join(','); })(), 'The Redux store should exist and initialize with a state equal to the <code>todos</code> array in the code editor.');" "The Redux store should exist and initialize with a state equal to the <code>todos</code> array in the code editor.",
"testString":
"assert((function() { const todos = [ 'Go to the store', 'Clean the house', 'Cook dinner', 'Learn to code' ]; const initialState = store.getState(); return Array.isArray(initialState) && initialState.join(',') === todos.join(','); })(), 'The Redux store should exist and initialize with a state equal to the <code>todos</code> array in the code editor.');"
}, },
{ {
"text": "<code>addToDo</code> and <code>immutableReducer</code> both should be functions.", "text":
"testString": "assert(typeof addToDo === 'function' && typeof immutableReducer === 'function', '<code>addToDo</code> and <code>immutableReducer</code> both should be functions.');" "<code>addToDo</code> and <code>immutableReducer</code> both should be functions.",
"testString":
"assert(typeof addToDo === 'function' && typeof immutableReducer === 'function', '<code>addToDo</code> and <code>immutableReducer</code> both should be functions.');"
}, },
{ {
"text": "Dispatching an action of type <code>ADD_TO_DO</code> on the Redux store should add a <code>todo</code> item and should NOT mutate state.", "text":
"testString": "assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch(addToDo('__TEST__TO__DO__')); const finalState = store.getState(); const expectedState = [ 'Go to the store', 'Clean the house', 'Cook dinner', 'Learn to code', '__TEST__TO__DO__' ]; return( isFrozen && DeepEqual(finalState, expectedState)); })(), 'Dispatching an action of type <code>ADD_TO_DO</code> on the Redux store should add a <code>todo</code> item and should NOT mutate state.');" "Dispatching an action of type <code>ADD_TO_DO</code> on the Redux store should add a <code>todo</code> item and should NOT mutate state.",
"testString":
"assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch(addToDo('__TEST__TO__DO__')); const finalState = store.getState(); const expectedState = [ 'Go to the store', 'Clean the house', 'Cook dinner', 'Learn to code', '__TEST__TO__DO__' ]; return( isFrozen && DeepEqual(finalState, expectedState)); })(), 'Dispatching an action of type <code>ADD_TO_DO</code> on the Redux store should add a <code>todo</code> item and should NOT mutate state.');"
} }
], ],
"solutions": [ "solutions": [
@ -995,20 +1089,27 @@
}, },
"tests": [ "tests": [
{ {
"text": "The Redux store should exist and initialize with a state equal to <code>[Do not mutate state!]</code>.", "text":
"testString": "assert((function() { const initialState = store.getState(); return ( Array.isArray(initialState) === true && initialState[0] === 'Do not mutate state!'); })(), 'The Redux store should exist and initialize with a state equal to <code>[Do not mutate state!]</code>.');" "The Redux store should exist and initialize with a state equal to <code>[Do not mutate state!]</code>.",
"testString":
"assert((function() { const initialState = store.getState(); return ( Array.isArray(initialState) === true && initialState[0] === 'Do not mutate state!'); })(), 'The Redux store should exist and initialize with a state equal to <code>[Do not mutate state!]</code>.');"
}, },
{ {
"text": "<code>addToDo</code> and <code>immutableReducer</code> both should be functions.", "text":
"testString": "assert(typeof addToDo === 'function' && typeof immutableReducer === 'function', '<code>addToDo</code> and <code>immutableReducer</code> both should be functions.');" "<code>addToDo</code> and <code>immutableReducer</code> both should be functions.",
"testString":
"assert(typeof addToDo === 'function' && typeof immutableReducer === 'function', '<code>addToDo</code> and <code>immutableReducer</code> both should be functions.');"
}, },
{ {
"text": "Dispatching an action of type <code>ADD_TO_DO</code> on the Redux store should add a <code>todo</code> item and should NOT mutate state.", "text":
"testString": "assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch(addToDo('__TEST__TO__DO__')); const finalState = store.getState(); const expectedState = [ 'Do not mutate state!', '__TEST__TO__DO__' ]; return( isFrozen && DeepEqual(finalState, expectedState)); })(), 'Dispatching an action of type <code>ADD_TO_DO</code> on the Redux store should add a <code>todo</code> item and should NOT mutate state.');" "Dispatching an action of type <code>ADD_TO_DO</code> on the Redux store should add a <code>todo</code> item and should NOT mutate state.",
"testString":
"assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch(addToDo('__TEST__TO__DO__')); const finalState = store.getState(); const expectedState = [ 'Do not mutate state!', '__TEST__TO__DO__' ]; return( isFrozen && DeepEqual(finalState, expectedState)); })(), 'Dispatching an action of type <code>ADD_TO_DO</code> on the Redux store should add a <code>todo</code> item and should NOT mutate state.');"
}, },
{ {
"text": "The spread operator should be used to return new state.", "text": "The spread operator should be used to return new state.",
"testString": "getUserInput => assert(getUserInput('index').includes('...state'), 'The spread operator should be used to return new state.');" "testString":
"getUserInput => assert(getUserInput('index').includes('...state'), 'The spread operator should be used to return new state.');"
} }
], ],
"solutions": [ "solutions": [
@ -1059,16 +1160,22 @@
}, },
"tests": [ "tests": [
{ {
"text": "The Redux store should exist and initialize with a state equal to <code>[0,1,2,3,4,5]</code>", "text":
"testString": "assert((function() { const initialState = store.getState(); return (Array.isArray(initialState) === true && DeepEqual(initialState, [0, 1, 2, 3, 4, 5])); })(), 'The Redux store should exist and initialize with a state equal to <code>[0,1,2,3,4,5]</code>');" "The Redux store should exist and initialize with a state equal to <code>[0,1,2,3,4,5]</code>",
"testString":
"assert((function() { const initialState = store.getState(); return (Array.isArray(initialState) === true && DeepEqual(initialState, [0, 1, 2, 3, 4, 5])); })(), 'The Redux store should exist and initialize with a state equal to <code>[0,1,2,3,4,5]</code>');"
}, },
{ {
"text": "<code>removeItem</code> and <code>immutableReducer</code> both should be functions.", "text":
"testString": "assert(typeof removeItem === 'function' && typeof immutableReducer === 'function', '<code>removeItem</code> and <code>immutableReducer</code> both should be functions.');" "<code>removeItem</code> and <code>immutableReducer</code> both should be functions.",
"testString":
"assert(typeof removeItem === 'function' && typeof immutableReducer === 'function', '<code>removeItem</code> and <code>immutableReducer</code> both should be functions.');"
}, },
{ {
"text": "Dispatching the <code>removeItem</code> action creator should remove items from the state and should NOT mutate state.", "text":
"testString": "assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch(removeItem(3)); const state_1 = store.getState(); store.dispatch(removeItem(2)); const state_2 = store.getState(); store.dispatch(removeItem(0)); store.dispatch(removeItem(0)); store.dispatch(removeItem(0)); const state_3 = store.getState(); return isFrozen && DeepEqual(state_1, [0, 1, 2, 4, 5]) && DeepEqual(state_2, [0, 1, 4, 5]) && DeepEqual(state_3, [5]); })(), 'Dispatching the <code>removeItem</code> action creator should remove items from the state and should NOT mutate state.');" "Dispatching the <code>removeItem</code> action creator should remove items from the state and should NOT mutate state.",
"testString":
"assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch(removeItem(3)); const state_1 = store.getState(); store.dispatch(removeItem(2)); const state_2 = store.getState(); store.dispatch(removeItem(0)); store.dispatch(removeItem(0)); store.dispatch(removeItem(0)); const state_3 = store.getState(); return isFrozen && DeepEqual(state_1, [0, 1, 2, 4, 5]) && DeepEqual(state_2, [0, 1, 4, 5]) && DeepEqual(state_3, [5]); })(), 'Dispatching the <code>removeItem</code> action creator should remove items from the state and should NOT mutate state.');"
} }
], ],
"solutions": [ "solutions": [
@ -1127,20 +1234,28 @@
}, },
"tests": [ "tests": [
{ {
"text": "The Redux store should exist and initialize with a state that is equivalent to the <code>defaultState</code> object declared on line 1.", "text":
"testString": "assert((function() { const expectedState = { user: 'CamperBot', status: 'offline', friends: '732,982', community: 'freeCodeCamp' }; const initialState = store.getState(); return DeepEqual(expectedState, initialState); })(), 'The Redux store should exist and initialize with a state that is equivalent to the <code>defaultState</code> object declared on line 1.');" "The Redux store should exist and initialize with a state that is equivalent to the <code>defaultState</code> object declared on line 1.",
"testString":
"assert((function() { const expectedState = { user: 'CamperBot', status: 'offline', friends: '732,982', community: 'freeCodeCamp' }; const initialState = store.getState(); return DeepEqual(expectedState, initialState); })(), 'The Redux store should exist and initialize with a state that is equivalent to the <code>defaultState</code> object declared on line 1.');"
}, },
{ {
"text": "<code>wakeUp</code> and <code>immutableReducer</code> both should be functions.", "text":
"testString": "assert(typeof wakeUp === 'function' && typeof immutableReducer === 'function', '<code>wakeUp</code> and <code>immutableReducer</code> both should be functions.');" "<code>wakeUp</code> and <code>immutableReducer</code> both should be functions.",
"testString":
"assert(typeof wakeUp === 'function' && typeof immutableReducer === 'function', '<code>wakeUp</code> and <code>immutableReducer</code> both should be functions.');"
}, },
{ {
"text": "Dispatching an action of type <code>ONLINE</code> should update the property <code>status</code> in state to <code>online</code> and should NOT mutate state.", "text":
"testString": "assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch({type: 'ONLINE'}); const finalState = store.getState(); const expectedState = { user: 'CamperBot', status: 'online', friends: '732,982', community: 'freeCodeCamp' }; return isFrozen && DeepEqual(finalState, expectedState); })(), 'Dispatching an action of type <code>ONLINE</code> should update the property <code>status</code> in state to <code>online</code> and should NOT mutate state.');" "Dispatching an action of type <code>ONLINE</code> should update the property <code>status</code> in state to <code>online</code> and should NOT mutate state.",
"testString":
"assert((function() { const initialState = store.getState(); const isFrozen = DeepFreeze(initialState); store.dispatch({type: 'ONLINE'}); const finalState = store.getState(); const expectedState = { user: 'CamperBot', status: 'online', friends: '732,982', community: 'freeCodeCamp' }; return isFrozen && DeepEqual(finalState, expectedState); })(), 'Dispatching an action of type <code>ONLINE</code> should update the property <code>status</code> in state to <code>online</code> and should NOT mutate state.');"
}, },
{ {
"text": "<code>Object.assign</code> should be used to return new state.", "text":
"testString": "getUserInput => assert(getUserInput('index').includes('Object.assign'), '<code>Object.assign</code> should be used to return new state.');" "<code>Object.assign</code> should be used to return new state.",
"testString":
"getUserInput => assert(getUserInput('index').includes('Object.assign'), '<code>Object.assign</code> should be used to return new state.');"
} }
], ],
"solutions": [ "solutions": [

View File

@ -5,7 +5,8 @@
"helpRoom": "Help", "helpRoom": "Help",
"required": [ "required": [
{ {
"link": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" "link":
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css"
} }
], ],
"challenges": [ "challenges": [
@ -22,12 +23,15 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should use the <code>document.getElementById</code> method to select the <code>getMessage</code> element.", "text":
"testString": "assert(code.match(/document\\.getElementById\\(\\s*?('|\")getMessage\\1\\s*?\\)/g), 'Your code should use the <code>document.getElementById</code> method to select the <code>getMessage</code> element.');" "Your code should use the <code>document.getElementById</code> method to select the <code>getMessage</code> element.",
"testString":
"assert(code.match(/document\\.getElementById\\(\\s*?('|\")getMessage\\1\\s*?\\)/g), 'Your code should use the <code>document.getElementById</code> method to select the <code>getMessage</code> element.');"
}, },
{ {
"text": "Your code should add an <code>onclick</code> event handler.", "text": "Your code should add an <code>onclick</code> event handler.",
"testString": "assert(typeof document.getElementById('getMessage').onclick === 'function', 'Your code should add an <code>onclick</code> event handler.');" "testString":
"assert(typeof document.getElementById('getMessage').onclick === 'function', 'Your code should add an <code>onclick</code> event handler.');"
} }
], ],
"solutions": [], "solutions": [],
@ -103,8 +107,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should use the <code>document.getElementsByClassName</code> method to select the element with class <code>message</code> and set its <code>innerHTML</code> to the given string.", "text":
"testString": "assert(code.match(/document\\.getElementsByClassName\\(\\s*?('|\")message\\1\\s*?\\)\\[0\\]\\.innerHTML\\s*?=\\s*?('|\")Here is the message\\2/g), 'Your code should use the <code>document.getElementsByClassName</code> method to select the element with class <code>message</code> and set its <code>innerHTML</code> to the given string.');" "Your code should use the <code>document.getElementsByClassName</code> method to select the element with class <code>message</code> and set its <code>innerHTML</code> to the given string.",
"testString":
"assert(code.match(/document\\.getElementsByClassName\\(\\s*?('|\")message\\1\\s*?\\)\\[0\\]\\.innerHTML\\s*?=\\s*?('|\")Here is the message\\2/g), 'Your code should use the <code>document.getElementsByClassName</code> method to select the element with class <code>message</code> and set its <code>innerHTML</code> to the given string.');"
} }
], ],
"solutions": [], "solutions": [],
@ -180,7 +186,7 @@
"These properties and their values are often referred to as \"key-value pairs\".", "These properties and their values are often referred to as \"key-value pairs\".",
"However, JSON transmitted by APIs are sent as <code>bytes</code>, and your application receives it as a <code>string</code>. These can be converted into JavaScript objects, but they are not JavaScript objects by default. The <code>JSON.parse</code> method parses the string and constructs the JavaScript object described by it.", "However, JSON transmitted by APIs are sent as <code>bytes</code>, and your application receives it as a <code>string</code>. These can be converted into JavaScript objects, but they are not JavaScript objects by default. The <code>JSON.parse</code> method parses the string and constructs the JavaScript object described by it.",
"You can request the JSON from freeCodeCamp's Cat Photo API. Here's the code you can put in your click event to do this:", "You can request the JSON from freeCodeCamp's Cat Photo API. Here's the code you can put in your click event to do this:",
"<blockquote>req=new XMLHttpRequest();<br>req.open(\"GET\",'/json/cats.json',true);<br>req.send();<br>req.onload=function(){<br> json=JSON.parse(req.responseText);<br> document.getElementsByClassName('message')[0].innerHTML=JSON.stringify(json);<br>};</blockquote>", "<blockquote>req=new XMLHttpRequest();<br>req.open(\"GET\",'/json/cats.json',true);<br>req.send();<br>req.onload=function(){<br>&nbsp;&nbsp;json=JSON.parse(req.responseText);<br>&nbsp;&nbsp;document.getElementsByClassName('message')[0].innerHTML=JSON.stringify(json);<br>};</blockquote>",
"Here's a review of what each piece is doing. The JavaScript <code>XMLHttpRequest</code> object has a number of properties and methods that are used to transfer data. First, an instance of the <code>XMLHttpRequest</code> object is created and saved in the <code>req</code> variable.", "Here's a review of what each piece is doing. The JavaScript <code>XMLHttpRequest</code> object has a number of properties and methods that are used to transfer data. First, an instance of the <code>XMLHttpRequest</code> object is created and saved in the <code>req</code> variable.",
"Next, the <code>open</code> method initializes a request - this example is requesting data from an API, therefore is a \"GET\" request. The second argument for <code>open</code> is the URL of the API you are requesting data from. The third argument is a Boolean value where <code>true</code> makes it an asynchronous request.", "Next, the <code>open</code> method initializes a request - this example is requesting data from an API, therefore is a \"GET\" request. The second argument for <code>open</code> is the URL of the API you are requesting data from. The third argument is a Boolean value where <code>true</code> makes it an asynchronous request.",
"The <code>send</code> method sends the request. Finally, the <code>onload</code> event handler parses the returned data and applies the <code>JSON.stringify</code> method to convert the JavaScript object into a string. This string is then inserted as the message text.", "The <code>send</code> method sends the request. Finally, the <code>onload</code> event handler parses the returned data and applies the <code>JSON.stringify</code> method to convert the JavaScript object into a string. This string is then inserted as the message text.",
@ -190,27 +196,38 @@
"tests": [ "tests": [
{ {
"text": "Your code should create a new <code>XMLHttpRequest</code>.", "text": "Your code should create a new <code>XMLHttpRequest</code>.",
"testString": "assert(code.match(/new\\s+?XMLHttpRequest\\(\\s*?\\)/g), 'Your code should create a new <code>XMLHttpRequest</code>.');" "testString":
"assert(code.match(/new\\s+?XMLHttpRequest\\(\\s*?\\)/g), 'Your code should create a new <code>XMLHttpRequest</code>.');"
}, },
{ {
"text": "Your code should use the <code>open</code> method to initialize a \"GET\" request to the freeCodeCamp Cat Photo API.", "text":
"testString": "assert(code.match(/\\.open\\(\\s*?('|\")GET\\1\\s*?,\\s*?('|\")\\/json\\/cats\\.json\\2\\s*?,\\s*?true\\s*?\\)/g), 'Your code should use the <code>open</code> method to initialize a \"GET\" request to the freeCodeCamp Cat Photo API.');" "Your code should use the <code>open</code> method to initialize a \"GET\" request to the freeCodeCamp Cat Photo API.",
"testString":
"assert(code.match(/\\.open\\(\\s*?('|\")GET\\1\\s*?,\\s*?('|\")\\/json\\/cats\\.json\\2\\s*?,\\s*?true\\s*?\\)/g), 'Your code should use the <code>open</code> method to initialize a \"GET\" request to the freeCodeCamp Cat Photo API.');"
}, },
{ {
"text": "Your code should use the <code>send</code> method to send the request.", "text":
"testString": "assert(code.match(/\\.send\\(\\s*\\)/g), 'Your code should use the <code>send</code> method to send the request.');" "Your code should use the <code>send</code> method to send the request.",
"testString":
"assert(code.match(/\\.send\\(\\s*\\)/g), 'Your code should use the <code>send</code> method to send the request.');"
}, },
{ {
"text": "Your code should have an <code>onload</code> event handler set to a function.", "text":
"testString": "assert(code.match(/\\.onload\\s*=\\s*function\\(\\s*?\\)\\s*?{/g), 'Your code should have an <code>onload</code> event handler set to a function.');" "Your code should have an <code>onload</code> event handler set to a function.",
"testString":
"assert(code.match(/\\.onload\\s*=\\s*function\\(\\s*?\\)\\s*?{/g), 'Your code should have an <code>onload</code> event handler set to a function.');"
}, },
{ {
"text": "Your code should use the <code>JSON.parse</code> method to parse the <code>responseText</code>.", "text":
"testString": "assert(code.match(/JSON\\.parse\\(.*\\.responseText\\)/g), 'Your code should use the <code>JSON.parse</code> method to parse the <code>responseText</code>.');" "Your code should use the <code>JSON.parse</code> method to parse the <code>responseText</code>.",
"testString":
"assert(code.match(/JSON\\.parse\\(.*\\.responseText\\)/g), 'Your code should use the <code>JSON.parse</code> method to parse the <code>responseText</code>.');"
}, },
{ {
"text": "Your code should get the element with class <code>message</code> and change its inner HTML to the string of JSON data.", "text":
"testString": "assert(code.match(/document\\.getElementsByClassName\\(\\s*?('|\")message\\1\\s*?\\)\\[0\\]\\.innerHTML\\s*?=\\s*?JSON\\.stringify\\(.+?\\)/g), 'Your code should get the element with class <code>message</code> and change its inner HTML to the string of JSON data.');" "Your code should get the element with class <code>message</code> and change its inner HTML to the string of JSON data.",
"testString":
"assert(code.match(/document\\.getElementsByClassName\\(\\s*?('|\")message\\1\\s*?\\)\\[0\\]\\.innerHTML\\s*?=\\s*?JSON\\.stringify\\(.+?\\)/g), 'Your code should get the element with class <code>message</code> and change its inner HTML to the string of JSON data.');"
} }
], ],
"solutions": [], "solutions": [],
@ -294,8 +311,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "Your code should use bracket and dot notation to access the proper code name, and print \"Loki\" to the console.", "text":
"testString": "assert(code.match(/(?:json\\[2\\]\\.codeNames\\[1\\]|json\\[2\\]\\[('|\")codeNames\\1\\]\\[1\\])/g), 'Your code should use bracket and dot notation to access the proper code name, and print \"Loki\" to the console.');" "Your code should use bracket and dot notation to access the proper code name, and print \"Loki\" to the console.",
"testString":
"assert(code.match(/(?:json\\[2\\]\\.codeNames\\[1\\]|json\\[2\\]\\[('|\")codeNames\\1\\]\\[1\\])/g), 'Your code should use bracket and dot notation to access the proper code name, and print \"Loki\" to the console.');"
} }
], ],
"solutions": [], "solutions": [],
@ -376,24 +395,30 @@
"First, declare an html variable with <code>var html = \"\";</code>.", "First, declare an html variable with <code>var html = \"\";</code>.",
"Then, loop through the JSON, adding HTML to the variable that wraps the key names in <code>strong</code> tags, followed by the value. When the loop is finished, you render it.", "Then, loop through the JSON, adding HTML to the variable that wraps the key names in <code>strong</code> tags, followed by the value. When the loop is finished, you render it.",
"Here's the code that does this:", "Here's the code that does this:",
"<blockquote>json.forEach(function(val) {</br> var keys = Object.keys(val);</br> html += \"&lt;div class = 'cat'&gt;\";</br> keys.forEach(function(key) {</br> html += \"&lt;strong&gt;\" + key + \"&lt;/strong&gt;: \" + val[key] + \"&lt;br&gt;\";</br> });</br> html += \"&lt;/div&gt;&lt;br&gt;\";</br>});</blockquote>", "<blockquote>json.forEach(function(val) {</br>&nbsp;&nbsp;var keys = Object.keys(val);</br>&nbsp;&nbsp;html += \"&lt;div class = 'cat'&gt;\";</br>&nbsp;&nbsp;keys.forEach(function(key) {</br>&nbsp;&nbsp;&nbsp;&nbsp;html += \"&lt;strong&gt;\" + key + \"&lt;/strong&gt;: \" + val[key] + \"&lt;br&gt;\";</br>&nbsp;&nbsp;});</br>&nbsp;&nbsp;html += \"&lt;/div&gt;&lt;br&gt;\";</br>});</blockquote>",
"<hr>", "<hr>",
"Add a <code>forEach</code> method to loop over the JSON data and create the HTML elements to display it.", "Add a <code>forEach</code> method to loop over the JSON data and create the HTML elements to display it.",
"Here is some example JSON", "Here is some example JSON",
"<blockquote>[</br> {</br> \"id\":0,</br> \"imageLink\":\"https://s3.amazonaws.com/freecodecamp/funny-cat.jpg\",</br> \"altText\":\"A white cat wearing a green helmet shaped melon on its head. \",</br> \"codeNames\":[</br> \"Juggernaut\",</br> \"Mrs. Wallace\",</br> \"Buttercup\"</br> ]</br> }</br>]</blockquote>" "<blockquote>[</br>&nbsp;&nbsp;{</br>&nbsp;&nbsp;&nbsp;&nbsp;\"id\":0,</br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"imageLink\":\"https://s3.amazonaws.com/freecodecamp/funny-cat.jpg\",</br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"altText\":\"A white cat wearing a green helmet shaped melon on its head. \",</br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\"codeNames\":[ \"Juggernaut\", \"Mrs. Wallace\", \"Buttercup\"</br>&nbsp;&nbsp;&nbsp;&nbsp;]</br>&nbsp;&nbsp;}</br>]</blockquote>"
], ],
"tests": [ "tests": [
{ {
"text": "Your code should store the data in the <code>html</code> variable", "text":
"testString": "assert(code.match(/html\\s+?(\\+=|=\\shtml\\s\\+)/g), 'Your code should store the data in the <code>html</code> variable');" "Your code should store the data in the <code>html</code> variable",
"testString":
"assert(code.match(/html\\s+?(\\+=|=\\shtml\\s\\+)/g), 'Your code should store the data in the <code>html</code> variable');"
}, },
{ {
"text": "Your code should use a <code>forEach</code> method to loop over the JSON data from the API.", "text":
"testString": "assert(code.match(/json\\.forEach/g), 'Your code should use a <code>forEach</code> method to loop over the JSON data from the API.');" "Your code should use a <code>forEach</code> method to loop over the JSON data from the API.",
"testString":
"assert(code.match(/json\\.forEach/g), 'Your code should use a <code>forEach</code> method to loop over the JSON data from the API.');"
}, },
{ {
"text": "Your code should wrap the key names in <code>strong</code> tags.", "text":
"testString": "assert(code.match(/<strong>.+<\\/strong>/g), 'Your code should wrap the key names in <code>strong</code> tags.');" "Your code should wrap the key names in <code>strong</code> tags.",
"testString":
"assert(code.match(/<strong>.+<\\/strong>/g), 'Your code should wrap the key names in <code>strong</code> tags.');"
} }
], ],
"solutions": [], "solutions": [],
@ -480,8 +505,10 @@
], ],
"tests": [ "tests": [
{ {
"text": "You should use the <code>imageLink</code> property to display the images.", "text":
"testString": "assert(code.match(/val\\.imageLink/g), 'You should use the <code>imageLink</code> property to display the images.');" "You should use the <code>imageLink</code> property to display the images.",
"testString":
"assert(code.match(/val\\.imageLink/g), 'You should use the <code>imageLink</code> property to display the images.');"
} }
], ],
"solutions": [], "solutions": [],
@ -565,14 +592,15 @@
"If you don't want to render every cat photo you get from the freeCodeCamp Cat Photo API, you can pre-filter the JSON before looping through it.", "If you don't want to render every cat photo you get from the freeCodeCamp Cat Photo API, you can pre-filter the JSON before looping through it.",
"Given that the JSON data is stored in an array, you can use the <code>filter</code> method to filter out the cat whose \"id\" key has a value of 1.", "Given that the JSON data is stored in an array, you can use the <code>filter</code> method to filter out the cat whose \"id\" key has a value of 1.",
"Here's the code to do this:", "Here's the code to do this:",
"<blockquote>json = json.filter(function(val) {<br> return (val.id !== 1);<br>});</blockquote>", "<blockquote>json = json.filter(function(val) {<br>&nbsp;&nbsp;return (val.id !== 1);<br>});</blockquote>",
"<hr>", "<hr>",
"Add code to <code>filter</code> the json data to remove the cat with the \"id\" value of 1." "Add code to <code>filter</code> the json data to remove the cat with the \"id\" value of 1."
], ],
"tests": [ "tests": [
{ {
"text": "Your code should use the <code>filter</code> method.", "text": "Your code should use the <code>filter</code> method.",
"testString": "assert(code.match(/json\\.filter/g), 'Your code should use the <code>filter</code> method.');" "testString":
"assert(code.match(/json\\.filter/g), 'Your code should use the <code>filter</code> method.');"
} }
], ],
"solutions": [], "solutions": [],
@ -661,27 +689,35 @@
"You will see a prompt to allow or block this site from knowing your current location. The challenge can be completed either way, as long as the code is correct.", "You will see a prompt to allow or block this site from knowing your current location. The challenge can be completed either way, as long as the code is correct.",
"By selecting allow, you will see the text on the output phone change to your latitude and longitude.", "By selecting allow, you will see the text on the output phone change to your latitude and longitude.",
"Here's code that does this:", "Here's code that does this:",
"<blockquote>if (navigator.geolocation){<br> navigator.geolocation.getCurrentPosition(function(position) {<br> document.getElementById('data').innerHTML=\"latitude: \"+ position.coords.latitude + \"&lt;br&gt;longitude: \" + position.coords.longitude;<br> });<br>}</blockquote>", "<blockquote>if (navigator.geolocation){<br>&nbsp;&nbsp;navigator.geolocation.getCurrentPosition(function(position) {<br>&nbsp;&nbsp;&nbsp;&nbsp;document.getElementById('data').innerHTML=\"latitude: \"+ position.coords.latitude + \"&lt;br&gt;longitude: \" + position.coords.longitude;<br>&nbsp;&nbsp;});<br>}</blockquote>",
"First, it checks if the <code>navigator.geolocation</code> object exists. If it does, the <code>getCurrentPosition</code> method on that object is called, which initiates an asynchronous request for the user's position. If the request is successful, the callback function in the method runs. This function accesses the <code>position</code> object's values for latitude and longitude using dot notation and updates the HTML.", "First, it checks if the <code>navigator.geolocation</code> object exists. If it does, the <code>getCurrentPosition</code> method on that object is called, which initiates an asynchronous request for the user's position. If the request is successful, the callback function in the method runs. This function accesses the <code>position</code> object's values for latitude and longitude using dot notation and updates the HTML.",
"<hr>", "<hr>",
"Add the example code inside the <code>script</code> tags to check a user's current location and insert it into the HTML." "Add the example code inside the <code>script</code> tags to check a user's current location and insert it into the HTML."
], ],
"tests": [ "tests": [
{ {
"text": "Your code should use <code>navigator.geolocation</code> to access the user&#39;s current location.", "text":
"testString": "assert(code.match(/navigator\\.geolocation\\.getCurrentPosition/g), 'Your code should use <code>navigator.geolocation</code> to access the user&#39;s current location.');" "Your code should use <code>navigator.geolocation</code> to access the user&#39;s current location.",
"testString":
"assert(code.match(/navigator\\.geolocation\\.getCurrentPosition/g), 'Your code should use <code>navigator.geolocation</code> to access the user&#39;s current location.');"
}, },
{ {
"text": "Your code should use <code>position.coords.latitude</code> to display the user&#39;s latitudinal location.", "text":
"testString": "assert(code.match(/position\\.coords\\.latitude/g), 'Your code should use <code>position.coords.latitude</code> to display the user&#39;s latitudinal location.');" "Your code should use <code>position.coords.latitude</code> to display the user&#39;s latitudinal location.",
"testString":
"assert(code.match(/position\\.coords\\.latitude/g), 'Your code should use <code>position.coords.latitude</code> to display the user&#39;s latitudinal location.');"
}, },
{ {
"text": "Your code should use <code>position.coords.longitude</code> to display the user&#39;s longitudinal location.", "text":
"testString": "assert(code.match(/position\\.coords\\.longitude/g), 'Your code should use <code>position.coords.longitude</code> to display the user&#39;s longitudinal location.');" "Your code should use <code>position.coords.longitude</code> to display the user&#39;s longitudinal location.",
"testString":
"assert(code.match(/position\\.coords\\.longitude/g), 'Your code should use <code>position.coords.longitude</code> to display the user&#39;s longitudinal location.');"
}, },
{ {
"text": "You should display the user&#39;s position within the <code>data</code> div element.", "text":
"testString": "assert(code.match(/document\\.getElementById\\(\\s*?('|\")data\\1\\s*?\\)\\.innerHTML/g), 'You should display the user&#39;s position within the <code>data</code> div element.');" "You should display the user&#39;s position within the <code>data</code> div element.",
"testString":
"assert(code.match(/document\\.getElementById\\(\\s*?('|\")data\\1\\s*?\\)\\.innerHTML/g), 'You should display the user&#39;s position within the <code>data</code> div element.');"
} }
], ],
"solutions": [], "solutions": [],
@ -717,7 +753,7 @@
"description": [ "description": [
"In the previous examples, you received data from an external resource. You can also send data to an external resource, as long as that resource supports AJAX requests and you know the URL.", "In the previous examples, you received data from an external resource. You can also send data to an external resource, as long as that resource supports AJAX requests and you know the URL.",
"JavaScript's <code>XMLHttpRequest</code> method is also used to post data to a server. Here's an example:", "JavaScript's <code>XMLHttpRequest</code> method is also used to post data to a server. Here's an example:",
"<blockquote>req=new XMLHttpRequest();<br>req.open(\"POST\",url,true);<br>req.setRequestHeader('Content-Type','text/plain');<br>req.onreadystatechange=function(){<br> if(req.readyState==4 && req.status==200){<br> document.getElementsByClassName('message')[0].innerHTML=req.responseText;<br> }<br>};<br>req.send(userName);</blockquote>", "<blockquote>req=new XMLHttpRequest();<br>req.open(\"POST\",url,true);<br>req.setRequestHeader('Content-Type','text/plain');<br>req.onreadystatechange=function(){<br>&nbsp;&nbsp;if(req.readyState==4 && req.status==200){<br>&nbsp;&nbsp;&nbsp;&nbsp;document.getElementsByClassName('message')[0].innerHTML=req.responseText;<br>&nbsp;&nbsp;}<br>};<br>req.send(userName);</blockquote>",
"You've seen several of these methods before. Here the <code>open</code> method initializes the request as a \"POST\" to the given URL of the external resource, and uses the <code>true</code> Boolean to make it asynchronous.", "You've seen several of these methods before. Here the <code>open</code> method initializes the request as a \"POST\" to the given URL of the external resource, and uses the <code>true</code> Boolean to make it asynchronous.",
"The <code>setRequestHeader</code> method sets the value of an HTTP request header, which contains information about the sender and the request. It must be called after the <code>open</code> method, but before the <code>send</code> method. The two parameters are the name of the header and the value to set as the body of that header.", "The <code>setRequestHeader</code> method sets the value of an HTTP request header, which contains information about the sender and the request. It must be called after the <code>open</code> method, but before the <code>send</code> method. The two parameters are the name of the header and the value to set as the body of that header.",
"Next, the <code>onreadystatechange</code> event listener handles a change in the state of the request. A <code>readyState</code> of 4 means the operation is complete, and a <code>status</code> of 200 means it was a successful request. The document's HTML can be updated.", "Next, the <code>onreadystatechange</code> event listener handles a change in the state of the request. A <code>readyState</code> of 4 means the operation is complete, and a <code>status</code> of 200 means it was a successful request. The document's HTML can be updated.",
@ -728,27 +764,37 @@
"tests": [ "tests": [
{ {
"text": "Your code should create a new <code>XMLHttpRequest</code>.", "text": "Your code should create a new <code>XMLHttpRequest</code>.",
"testString": "assert(code.match(/new\\s+?XMLHttpRequest\\(\\s*?\\)/g), 'Your code should create a new <code>XMLHttpRequest</code>.');" "testString":
"assert(code.match(/new\\s+?XMLHttpRequest\\(\\s*?\\)/g), 'Your code should create a new <code>XMLHttpRequest</code>.');"
}, },
{ {
"text": "Your code should use the <code>open</code> method to initialize a \"POST\" request to the server.", "text":
"testString": "assert(code.match(/\\.open\\(\\s*?('|\")POST\\1\\s*?,\\s*?url\\s*?,\\s*?true\\s*?\\)/g), 'Your code should use the <code>open</code> method to initialize a \"POST\" request to the server.');" "Your code should use the <code>open</code> method to initialize a \"POST\" request to the server.",
"testString":
"assert(code.match(/\\.open\\(\\s*?('|\")POST\\1\\s*?,\\s*?url\\s*?,\\s*?true\\s*?\\)/g), 'Your code should use the <code>open</code> method to initialize a \"POST\" request to the server.');"
}, },
{ {
"text": "Your code should use the <code>setRequestHeader</code> method.", "text":
"testString": "assert(code.match(/\\.setRequestHeader\\(\\s*?('|\")Content-Type\\1\\s*?,\\s*?('|\")text\\/plain\\2\\s*?\\)/g), 'Your code should use the <code>setRequestHeader</code> method.');" "Your code should use the <code>setRequestHeader</code> method.",
"testString":
"assert(code.match(/\\.setRequestHeader\\(\\s*?('|\")Content-Type\\1\\s*?,\\s*?('|\")text\\/plain\\2\\s*?\\)/g), 'Your code should use the <code>setRequestHeader</code> method.');"
}, },
{ {
"text": "Your code should have an <code>onreadystatechange</code> event handler set to a function.", "text":
"testString": "assert(code.match(/\\.onreadystatechange\\s*?=/g), 'Your code should have an <code>onreadystatechange</code> event handler set to a function.');" "Your code should have an <code>onreadystatechange</code> event handler set to a function.",
"testString":
"assert(code.match(/\\.onreadystatechange\\s*?=/g), 'Your code should have an <code>onreadystatechange</code> event handler set to a function.');"
}, },
{ {
"text": "Your code should get the element with class <code>message</code> and change its inner HTML to the <code>responseText</code>.", "text":
"testString": "assert(code.match(/document\\.getElementsByClassName\\(\\s*?('|\")message\\1\\s*?\\)\\[0\\]\\.innerHTML\\s*?=\\s*?.+?\\.responseText/g), 'Your code should get the element with class <code>message</code> and change its inner HTML to the <code>responseText</code>.');" "Your code should get the element with class <code>message</code> and change its inner HTML to the <code>responseText</code>.",
"testString":
"assert(code.match(/document\\.getElementsByClassName\\(\\s*?('|\")message\\1\\s*?\\)\\[0\\]\\.innerHTML\\s*?=\\s*?.+?\\.responseText/g), 'Your code should get the element with class <code>message</code> and change its inner HTML to the <code>responseText</code>.');"
}, },
{ {
"text": "Your code should use the <code>send</code> method.", "text": "Your code should use the <code>send</code> method.",
"testString": "assert(code.match(/\\.send\\(\\s*?userName\\s*?\\)/g), 'Your code should use the <code>send</code> method.');" "testString":
"assert(code.match(/\\.send\\(\\s*?userName\\s*?\\)/g), 'Your code should use the <code>send</code> method.');"
} }
], ],
"solutions": [], "solutions": [],