├── .gitignore
├── jasmine
└── jasmine-2.8.0
│ ├── ironhack-logo.png
│ ├── jasmine_favicon.png
│ ├── boot.js
│ ├── console.js
│ ├── jasmine-html.js
│ └── jasmine.css
├── SpecRunner.html
├── .github
├── workflows
│ └── test.yml
└── stale.yml
├── LICENSE
├── readme.md
└── tests
└── koans.spec.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .vscode
4 | package-lock.json
5 | yarn.lock
6 | *.log
7 | lab-solution.html
8 |
--------------------------------------------------------------------------------
/jasmine/jasmine-2.8.0/ironhack-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ironhack-labs/lab-javascript-koans/HEAD/jasmine/jasmine-2.8.0/ironhack-logo.png
--------------------------------------------------------------------------------
/jasmine/jasmine-2.8.0/jasmine_favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ironhack-labs/lab-javascript-koans/HEAD/jasmine/jasmine-2.8.0/jasmine_favicon.png
--------------------------------------------------------------------------------
/SpecRunner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Jasmine Spec Runner v2.8.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Automated Testing
2 | on: push
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - name: Load code
8 | uses: actions/checkout@v2
9 | - name: Prepare environment
10 | uses: actions/setup-node@v2
11 | with:
12 | node-version: '14'
13 | check-latest: true
14 | - name: Install dependencies
15 | run: npm i
16 | - name: Run tests
17 | run: npm run test -- --ci --reporters=default --reporters=jest-junit
18 | - name: Reports the results of tests
19 | uses: IgnusG/jest-report-action@v2.3.3
20 | if: always()
21 | with:
22 | access-token: ${{ secrets.GITHUB_TOKEN }}
23 | run-name: test
24 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-stale - https://github.com/probot/stale
2 |
3 | # Number of days of inactivity before an Issue or Pull Request becomes stale
4 | daysUntilStale: 30
5 |
6 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
7 | exemptLabels: []
8 |
9 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
10 | # onlyLabels:
11 | # - TestLabel
12 |
13 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
14 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
15 | daysUntilClose: 2
16 |
17 | # Label to use when marking as stale
18 | staleLabel: stale
19 |
20 | # Comment to post when marking as stale. Set to `false` to disable
21 | markComment: >
22 | This pull request has been automatically marked as stale because it didn't have any recent activity. It will be closed if no further activity occurs. Thank you
23 | for your contributions.
24 | closeComment: >
25 | This pull request is closed. Thank you.
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2014 Pivotal Labs
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/jasmine/jasmine-2.8.0/boot.js:
--------------------------------------------------------------------------------
1 | /**
2 | Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
3 |
4 | If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
5 |
6 | The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
7 |
8 | [jasmine-gem]: http://github.com/pivotal/jasmine-gem
9 | */
10 |
11 | (function() {
12 |
13 | /**
14 | * ## Require & Instantiate
15 | *
16 | * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
17 | */
18 | window.jasmine = jasmineRequire.core(jasmineRequire);
19 |
20 | /**
21 | * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
22 | */
23 | jasmineRequire.html(jasmine);
24 |
25 | /**
26 | * Create the Jasmine environment. This is used to run all specs in a project.
27 | */
28 | var env = jasmine.getEnv();
29 |
30 | /**
31 | * ## The Global Interface
32 | *
33 | * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
34 | */
35 | var jasmineInterface = jasmineRequire.interface(jasmine, env);
36 |
37 | /**
38 | * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
39 | */
40 | extend(window, jasmineInterface);
41 |
42 | /**
43 | * ## Runner Parameters
44 | *
45 | * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
46 | */
47 |
48 | var queryString = new jasmine.QueryString({
49 | getWindowLocation: function() { return window.location; }
50 | });
51 |
52 | var filterSpecs = !!queryString.getParam("spec");
53 |
54 | var catchingExceptions = queryString.getParam("catch");
55 | env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
56 |
57 | var throwingExpectationFailures = queryString.getParam("throwFailures");
58 | env.throwOnExpectationFailure(throwingExpectationFailures);
59 |
60 | var random = queryString.getParam("random");
61 | env.randomizeTests(random);
62 |
63 | var seed = queryString.getParam("seed");
64 | if (seed) {
65 | env.seed(seed);
66 | }
67 |
68 | /**
69 | * ## Reporters
70 | * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
71 | */
72 | var htmlReporter = new jasmine.HtmlReporter({
73 | env: env,
74 | onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
75 | onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); },
76 | onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); },
77 | addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
78 | getContainer: function() { return document.body; },
79 | createElement: function() { return document.createElement.apply(document, arguments); },
80 | createTextNode: function() { return document.createTextNode.apply(document, arguments); },
81 | timer: new jasmine.Timer(),
82 | filterSpecs: filterSpecs
83 | });
84 |
85 | /**
86 | * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
87 | */
88 | env.addReporter(jasmineInterface.jsApiReporter);
89 | env.addReporter(htmlReporter);
90 |
91 | /**
92 | * Filter which specs will be run by matching the start of the full name against the `spec` query param.
93 | */
94 | var specFilter = new jasmine.HtmlSpecFilter({
95 | filterString: function() { return queryString.getParam("spec"); }
96 | });
97 |
98 | env.specFilter = function(spec) {
99 | return specFilter.matches(spec.getFullName());
100 | };
101 |
102 | /**
103 | * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
104 | */
105 | window.setTimeout = window.setTimeout;
106 | window.setInterval = window.setInterval;
107 | window.clearTimeout = window.clearTimeout;
108 | window.clearInterval = window.clearInterval;
109 |
110 | /**
111 | * ## Execution
112 | *
113 | * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
114 | */
115 | var currentWindowOnload = window.onload;
116 |
117 | window.onload = function() {
118 | if (currentWindowOnload) {
119 | currentWindowOnload();
120 | }
121 | htmlReporter.initialize();
122 | env.execute();
123 | };
124 |
125 | /**
126 | * Helper function for readability above.
127 | */
128 | function extend(destination, source) {
129 | for (var property in source) destination[property] = source[property];
130 | return destination;
131 | }
132 |
133 | }());
134 |
--------------------------------------------------------------------------------
/jasmine/jasmine-2.8.0/console.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008-2017 Pivotal Labs
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 | function getJasmineRequireObj() {
24 | if (typeof module !== 'undefined' && module.exports) {
25 | return exports;
26 | } else {
27 | window.jasmineRequire = window.jasmineRequire || {};
28 | return window.jasmineRequire;
29 | }
30 | }
31 |
32 | getJasmineRequireObj().console = function(jRequire, j$) {
33 | j$.ConsoleReporter = jRequire.ConsoleReporter();
34 | };
35 |
36 | getJasmineRequireObj().ConsoleReporter = function() {
37 |
38 | var noopTimer = {
39 | start: function(){},
40 | elapsed: function(){ return 0; }
41 | };
42 |
43 | function ConsoleReporter(options) {
44 | var print = options.print,
45 | showColors = options.showColors || false,
46 | onComplete = options.onComplete || function() {},
47 | timer = options.timer || noopTimer,
48 | specCount,
49 | failureCount,
50 | failedSpecs = [],
51 | pendingCount,
52 | ansi = {
53 | green: '\x1B[32m',
54 | red: '\x1B[31m',
55 | yellow: '\x1B[33m',
56 | none: '\x1B[0m'
57 | },
58 | failedSuites = [];
59 |
60 | print('ConsoleReporter is deprecated and will be removed in a future version.');
61 |
62 | this.jasmineStarted = function() {
63 | specCount = 0;
64 | failureCount = 0;
65 | pendingCount = 0;
66 | print('Started');
67 | printNewline();
68 | timer.start();
69 | };
70 |
71 | this.jasmineDone = function() {
72 | printNewline();
73 | for (var i = 0; i < failedSpecs.length; i++) {
74 | specFailureDetails(failedSpecs[i]);
75 | }
76 |
77 | if(specCount > 0) {
78 | printNewline();
79 |
80 | var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' +
81 | failureCount + ' ' + plural('failure', failureCount);
82 |
83 | if (pendingCount) {
84 | specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount);
85 | }
86 |
87 | print(specCounts);
88 | } else {
89 | print('No specs found');
90 | }
91 |
92 | printNewline();
93 | var seconds = timer.elapsed() / 1000;
94 | print('Finished in ' + seconds + ' ' + plural('second', seconds));
95 | printNewline();
96 |
97 | for(i = 0; i < failedSuites.length; i++) {
98 | suiteFailureDetails(failedSuites[i]);
99 | }
100 |
101 | onComplete(failureCount === 0);
102 | };
103 |
104 | this.specDone = function(result) {
105 | specCount++;
106 |
107 | if (result.status == 'pending') {
108 | pendingCount++;
109 | print(colored('yellow', '*'));
110 | return;
111 | }
112 |
113 | if (result.status == 'passed') {
114 | print(colored('green', '.'));
115 | return;
116 | }
117 |
118 | if (result.status == 'failed') {
119 | failureCount++;
120 | failedSpecs.push(result);
121 | print(colored('red', 'F'));
122 | }
123 | };
124 |
125 | this.suiteDone = function(result) {
126 | if (result.failedExpectations && result.failedExpectations.length > 0) {
127 | failureCount++;
128 | failedSuites.push(result);
129 | }
130 | };
131 |
132 | return this;
133 |
134 | function printNewline() {
135 | print('\n');
136 | }
137 |
138 | function colored(color, str) {
139 | return showColors ? (ansi[color] + str + ansi.none) : str;
140 | }
141 |
142 | function plural(str, count) {
143 | return count == 1 ? str : str + 's';
144 | }
145 |
146 | function repeat(thing, times) {
147 | var arr = [];
148 | for (var i = 0; i < times; i++) {
149 | arr.push(thing);
150 | }
151 | return arr;
152 | }
153 |
154 | function indent(str, spaces) {
155 | var lines = (str || '').split('\n');
156 | var newArr = [];
157 | for (var i = 0; i < lines.length; i++) {
158 | newArr.push(repeat(' ', spaces).join('') + lines[i]);
159 | }
160 | return newArr.join('\n');
161 | }
162 |
163 | function specFailureDetails(result) {
164 | printNewline();
165 | print(result.fullName);
166 |
167 | for (var i = 0; i < result.failedExpectations.length; i++) {
168 | var failedExpectation = result.failedExpectations[i];
169 | printNewline();
170 | print(indent(failedExpectation.message, 2));
171 | print(indent(failedExpectation.stack, 2));
172 | }
173 |
174 | printNewline();
175 | }
176 |
177 | function suiteFailureDetails(result) {
178 | for (var i = 0; i < result.failedExpectations.length; i++) {
179 | printNewline();
180 | print(colored('red', 'An error was thrown in an afterAll'));
181 | printNewline();
182 | print(colored('red', 'AfterAll ' + result.failedExpectations[i].message));
183 |
184 | }
185 | printNewline();
186 | }
187 | }
188 |
189 | return ConsoleReporter;
190 | };
191 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # LAB | JS Koans
4 |
5 | ## Koans
6 |
7 | 
8 |
9 | ```
10 | Two monks were arguing on top of a hill about a big
11 | flag at the top of a temple. One monk reflected:
12 |
13 | > "The flag is moving"
14 |
15 | The other thought for a while and said:
16 |
17 | > "The wind is moving.".
18 |
19 | The sixth patriarch happened to be passing by.
20 |
21 | He told them:
22 |
23 | > "Not the wind, not the flag; The mind is moving."
24 | ```
25 |
26 |
27 |
28 | ### What are the Koans?
29 |
30 | [Koans](https://en.wikipedia.org/wiki/K%C5%8Dan) (公案) originate from Zen Buddhism, and are paradoxical riddles or stories used to test "students" on their path to enlightenment. They are designed to provoke thought or doubt in the student's mind. We are here to learn to code, so... what are the Koans?
31 |
32 | The Koans are a series of assertions you must solve to understand how a programming language works. This is the first step to become a better developer. The Koans become increasingly more difficult as you continue, so don't feel discouraged as you move forward through them.
33 |
34 | There are Koans for all the programming languages. We will work with JavaScript Koans. The mechanism is the following:
35 |
36 | - You get an assertion that is not passing a **test**.
37 | - You have to give the test the correct expected result to pass it.
38 |
39 | We are going to test the code assertions through Jasmine. We have introduced two new concepts: tests and [Jasmine](http://jasmine.github.io/). Let's make a brief introduction to both of them.
40 |
41 |
42 |
43 |
44 |
45 | ## Testing
46 |
47 | When we are coding, we have to be sure that our code is working as we expect. More than that, when we update our existing code, we have to be 100% sure that our old code is still working. As our website becomes larger, it becomes more difficult to check that all our features are working as we expect. How can we automate this process? The answer is with **testing**.
48 |
49 |
50 |
51 | Testing allows us to check if the full set of features we have build is working as we expect. Sometimes you can create new features that override or damage the old ones. We avoid this with testing.
52 |
53 |
54 |
55 |
56 |
57 | ### Anatomy of a test
58 |
59 | The syntax to create a test depends on the framework we are using. So we will explain the basic anatomy of a test with [pseudocode](https://en.wikipedia.org/wiki/Pseudocode).
60 |
61 |
62 |
63 | Given a function with some parameters, we expect to get a result. If the result is what we are expecting, the test will pass. If we get an unexpected result, the test will fail. The framework will show the tests that are passing in green, and the tests that are failing in red.
64 |
65 | Let's suppose we have the following function:
66 |
67 | ```javascript
68 | function add(num1, num2) {
69 | // ...
70 | }
71 | ```
72 |
73 | If we pass as parameters to the `add` function the numbers 1 and 2, we should get a 3 as a result. So we are calling the `add` function with two numbers, and we expect from the function to return us the sum of those numbers.
74 |
75 | If we get 3 as a result, the test will pass. If we get any other result, the test will fail. In pseudo code, it would be something like this:
76 |
77 | ```
78 | given the parameters 1 and 2 to the add function, we expect to get 3 as a result
79 | ```
80 |
81 | This is the anatomy of a test. We have the knowledge to understand what is a test and how it works. Now we will introduce you to a JavaScript framework to test our application.
82 |
83 |
84 |
85 |
86 |
87 | ## Jasmine
88 |
89 | [Jasmine](http://jasmine.github.io/) is a JavaScript framework used to test JavaScript code. If you take a look at the documentation, you will see that it has many options to test our code. In this lab, we will work with `expects` and `matchers`.
90 |
91 | ### Describe, it, expect and matchers
92 |
93 | To understand better what are the parts of the test in Jasmine, we will walk through an example. This is the first test we will find in our Koans:
94 |
95 | ```
96 | describe('the JavaScript language', () => {
97 | describe('has different types and operators', () => {
98 | it('considers numbers to be equal to their string representation', () => {
99 | expect(1 == '1').toBeTruthy();
100 | expect(1 != '1').toBeFalsy();
101 | });
102 | });
103 | });
104 | ```
105 |
106 | We will go through each different part of the test to explain all of them:
107 |
108 |
109 |
110 | #### `describe`
111 |
112 | It's used to group different tests on our code. This is very helpful when we see the results of the tests. In the code above, we have two different describes:
113 |
114 | - The first one will group **all** our tests. It has a description indicating that we are going to test the JavaScript language.
115 | - The second one indicates to us that we will test the different types and operators that JavaScript has.
116 |
117 | As you can see, those are just information strings. When we execute our tests, they appear in the page grouping the different tests we have. This is very helpful when we have a lot of tests to identify where is a test that is not passing.
118 |
119 |
120 |
121 | #### `it`
122 |
123 | It receives a `string` that indicates what we are testing. It has to be a clear description of what we are going to do. In our example, we are testing that JavaScript considers the numbers to be equal to their string representation.
124 |
125 |
126 |
127 | #### `expect`
128 |
129 | What we are expecting in the test. This function contains the expression we want to test. This expression has to coincide with the matcher of the test. If they agree, the test will pass. If they disagree, the test will fail.
130 |
131 |
132 |
133 | #### Matchers
134 |
135 | The matchers are going to determine if a test will pass or not. The expression in the expect has to agree with the matcher. In our example, we are testing that JavaScript considers the numbers to be equal to their string representation. The matcher we selected is `.toBeTruthy()`.
136 |
137 | So, the test `expect(1 == "1").toBeTruthy()` will pass. There is a huge list of matchers on testing. We don't have to know all of them for the exercise. We will work on testing in another lesson. The matchers we will use here are:
138 |
139 | - `.toBeTruthy()` and `.toBeFalsy()`. We expect the expression to be truthy or falsy
140 | - `.toEqual()`. We expect the expression to be equal than the value passed as a parameter, and it has also to be the same type: i.e. `expect(1).toBe(1)`
141 | - `.toBe()`. We expect the expression to be equal than the value passed as a parameter, but not necessarily the same type: i.e. `expect(1).toBe("1")`
142 |
143 | We will see there are many matchers we can use. Right now, we just need the ones described above to do the Koans.
144 |
145 |
146 |
147 | ## Exercise
148 |
149 | ### Requirements
150 |
151 | - Fork this repo
152 |
153 | - Clone this repo
154 |
155 |
156 |
157 |
158 |
159 | ### Submission
160 |
161 | Upon completion, run the following commands:
162 |
163 | ```bash
164 | $ git add .
165 | $ git commit -m "Solved lab"
166 | $ git push origin master
167 | ```
168 |
169 | Create Pull Request so your TAs can check up your work.
170 |
171 |
172 |
173 |
174 |
175 | ### Instructions
176 |
177 | We need to execute our tests. After you forked and cloned this repo, open the file `SpecRunner.html` in your browser.
178 |
179 | In the beginning, you will see all the tests in green. This is because only the tests that are passing are un-commented. The tests we have to implement are commented out.
180 |
181 | All the tests are located inside the `spec` folder. Open the `koans.js` file and uncomment the following line:
182 |
183 | ```js
184 | it ("surprises me, NaN is not comparable with NaN", () => { {
185 | expect(5 / "a").toEqual(5 / "a");
186 | //expect(typeof(NaN)).toEqual();
187 | expect(isNaN(5 / "a")).toBeTruthy();
188 | });
189 | ```
190 |
191 | When we uncomment the line and refresh de `SpecRunner.html` page, we will see something like that:
192 |
193 | [](https://camo.githubusercontent.com/8f20065f1c2b6a9bd19888350de78377056ac3c96780ad2ecc509eba9837dd52/68747470733a2f2f692e696d6775722e636f6d2f36614f424f50662e706e67)
194 |
195 | **The main goal is not to finish all the tests. We want you to understand why each test is failing and how JavaScript works in different scenarios.**
196 |
197 | To do that, the correct workflow is the one used on [TDD](https://en.wikipedia.org/wiki/Test-driven_development):
198 |
199 | - Uncomment the test
200 | - Refresh the page to see that the un-commented test is failing
201 | - Change the code to pass the test
202 | - Refresh the page to see that the test is passing
203 |
204 | This process has to be done for each test. **Do not uncomment all the tests and launch the app. It will be more difficult for you to see if your code is passing the tests.**
205 |
206 | As we said, this is the first step to become a better developer.
207 |
208 | Good luck to all of you and happy coding. ❤️
209 |
--------------------------------------------------------------------------------
/jasmine/jasmine-2.8.0/jasmine-html.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008-2017 Pivotal Labs
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 | jasmineRequire.html = function(j$) {
24 | j$.ResultsNode = jasmineRequire.ResultsNode();
25 | j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
26 | j$.QueryString = jasmineRequire.QueryString();
27 | j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
28 | };
29 |
30 | jasmineRequire.HtmlReporter = function(j$) {
31 |
32 | var noopTimer = {
33 | start: function() {},
34 | elapsed: function() { return 0; }
35 | };
36 |
37 | function HtmlReporter(options) {
38 | var env = options.env || {},
39 | getContainer = options.getContainer,
40 | createElement = options.createElement,
41 | createTextNode = options.createTextNode,
42 | onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
43 | onThrowExpectationsClick = options.onThrowExpectationsClick || function() {},
44 | onRandomClick = options.onRandomClick || function() {},
45 | addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
46 | filterSpecs = options.filterSpecs,
47 | timer = options.timer || noopTimer,
48 | results = [],
49 | specsExecuted = 0,
50 | failureCount = 0,
51 | pendingSpecCount = 0,
52 | htmlReporterMain,
53 | symbols,
54 | failedSuites = [];
55 |
56 | this.initialize = function() {
57 | clearPrior();
58 | htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
59 | createDom('div', {className: 'jasmine-banner'},
60 | createDom('a', {className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank'}),
61 | createDom('span', {className: 'jasmine-version'}, j$.version)
62 | ),
63 | createDom('ul', {className: 'jasmine-symbol-summary'}),
64 | createDom('div', {className: 'jasmine-labname'}),
65 | createDom('div', {className: 'jasmine-alert'}),
66 | createDom('div', {className: 'jasmine-results'},
67 | createDom('div', {className: 'jasmine-failures'})
68 | )
69 | );
70 | getContainer().appendChild(htmlReporterMain);
71 | };
72 |
73 | var totalSpecsDefined;
74 | this.jasmineStarted = function(options) {
75 | totalSpecsDefined = options.totalSpecsDefined || 0;
76 | timer.start();
77 | };
78 |
79 | var summary = createDom('div', {className: 'jasmine-summary'});
80 |
81 | var topResults = new j$.ResultsNode({}, '', null),
82 | currentParent = topResults;
83 |
84 | this.suiteStarted = function(result) {
85 | currentParent.addChild(result, 'suite');
86 | currentParent = currentParent.last();
87 | };
88 |
89 | this.suiteDone = function(result) {
90 | if (result.status == 'failed') {
91 | failedSuites.push(result);
92 | }
93 |
94 | if (currentParent == topResults) {
95 | return;
96 | }
97 |
98 | currentParent = currentParent.parent;
99 | };
100 |
101 | this.specStarted = function(result) {
102 | currentParent.addChild(result, 'spec');
103 | };
104 |
105 | var failures = [];
106 | this.specDone = function(result) {
107 | if (noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
108 | console.error('Spec \'' + result.fullName + '\' has no expectations.');
109 | }
110 |
111 | if (result.status != 'disabled') {
112 | specsExecuted++;
113 | }
114 |
115 | if (!symbols){
116 | symbols = find('.jasmine-symbol-summary');
117 | }
118 |
119 | symbols.appendChild(createDom('li', {
120 | className: noExpectations(result) ? 'jasmine-empty' : 'jasmine-' + result.status,
121 | id: 'spec_' + result.id,
122 | title: result.fullName
123 | }
124 | ));
125 |
126 | if (result.status == 'failed') {
127 | failureCount++;
128 |
129 | var failure =
130 | createDom('div', {className: 'jasmine-spec-detail jasmine-failed'},
131 | createDom('div', {className: 'jasmine-description'},
132 | createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
133 | ),
134 | createDom('div', {className: 'jasmine-messages'})
135 | );
136 | var messages = failure.childNodes[1];
137 |
138 | for (var i = 0; i < result.failedExpectations.length; i++) {
139 | var expectation = result.failedExpectations[i];
140 | var stringStart = expectation.stack.indexOf("at UserContext.");
141 | var shortStackTrace = expectation.stack.slice(stringStart);
142 | messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message));
143 | messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, shortStackTrace));
144 | }
145 |
146 |
147 | failures.push(failure);
148 | }
149 |
150 | if (result.status == 'pending') {
151 | pendingSpecCount++;
152 | }
153 | };
154 |
155 | this.jasmineDone = function(doneResult) {
156 | var banner = find('.jasmine-banner');
157 | var labName = find('.jasmine-labname');
158 | var alert = find('.jasmine-alert');
159 | var order = doneResult && doneResult.order;
160 | labName.appendChild(createDom('img', {src: 'jasmine/jasmine-2.8.0/ironhack-logo.png'}, ''));
161 | labName.appendChild(createDom('span', {}, 'Lab - Name of the LAB'));
162 | alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
163 |
164 | banner.appendChild(
165 | createDom('div', { className: 'jasmine-run-options' },
166 | createDom('span', { className: 'jasmine-trigger' }, 'Options'),
167 | createDom('div', { className: 'jasmine-payload' },
168 | createDom('div', { className: 'jasmine-exceptions' },
169 | createDom('input', {
170 | className: 'jasmine-raise',
171 | id: 'jasmine-raise-exceptions',
172 | type: 'checkbox'
173 | }),
174 | createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')),
175 | createDom('div', { className: 'jasmine-throw-failures' },
176 | createDom('input', {
177 | className: 'jasmine-throw',
178 | id: 'jasmine-throw-failures',
179 | type: 'checkbox'
180 | }),
181 | createDom('label', { className: 'jasmine-label', 'for': 'jasmine-throw-failures' }, 'stop spec on expectation failure')),
182 | createDom('div', { className: 'jasmine-random-order' },
183 | createDom('input', {
184 | className: 'jasmine-random',
185 | id: 'jasmine-random-order',
186 | type: 'checkbox'
187 | }),
188 | createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order'))
189 | )
190 | ));
191 |
192 | var raiseCheckbox = find('#jasmine-raise-exceptions');
193 |
194 | raiseCheckbox.checked = !env.catchingExceptions();
195 | raiseCheckbox.onclick = onRaiseExceptionsClick;
196 |
197 | var throwCheckbox = find('#jasmine-throw-failures');
198 | throwCheckbox.checked = env.throwingExpectationFailures();
199 | throwCheckbox.onclick = onThrowExpectationsClick;
200 |
201 | var randomCheckbox = find('#jasmine-random-order');
202 | randomCheckbox.checked = env.randomTests();
203 | randomCheckbox.onclick = onRandomClick;
204 |
205 | var optionsMenu = find('.jasmine-run-options'),
206 | optionsTrigger = optionsMenu.querySelector('.jasmine-trigger'),
207 | optionsPayload = optionsMenu.querySelector('.jasmine-payload'),
208 | isOpen = /\bjasmine-open\b/;
209 |
210 | optionsTrigger.onclick = function() {
211 | if (isOpen.test(optionsPayload.className)) {
212 | optionsPayload.className = optionsPayload.className.replace(isOpen, '');
213 | } else {
214 | optionsPayload.className += ' jasmine-open';
215 | }
216 | };
217 |
218 | if (specsExecuted < totalSpecsDefined) {
219 | var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
220 | var skippedLink = addToExistingQueryString('spec', '');
221 | alert.appendChild(
222 | createDom('span', {className: 'jasmine-bar jasmine-skipped'},
223 | createDom('a', {href: skippedLink, title: 'Run all specs'}, skippedMessage)
224 | )
225 | );
226 | }
227 | var statusBarMessage = '';
228 | var statusBarClassName = 'jasmine-bar ';
229 |
230 | if (totalSpecsDefined > 0) {
231 | statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
232 | if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
233 | statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
234 | } else {
235 | statusBarClassName += 'jasmine-skipped';
236 | statusBarMessage += 'No specs found';
237 | }
238 |
239 | var seedBar;
240 | if (order && order.random) {
241 | seedBar = createDom('span', {className: 'jasmine-seed-bar'},
242 | ', randomized with seed ',
243 | createDom('a', {title: 'randomized with seed ' + order.seed, href: seedHref(order.seed)}, order.seed)
244 | );
245 | }
246 |
247 | alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage, seedBar));
248 |
249 | var errorBarClassName = 'jasmine-bar jasmine-errored';
250 | var errorBarMessagePrefix = 'AfterAll ';
251 |
252 | for(var i = 0; i < failedSuites.length; i++) {
253 | var failedSuite = failedSuites[i];
254 | for(var j = 0; j < failedSuite.failedExpectations.length; j++) {
255 | alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessagePrefix + failedSuite.failedExpectations[j].message));
256 | }
257 | }
258 |
259 | var globalFailures = (doneResult && doneResult.failedExpectations) || [];
260 | for(i = 0; i < globalFailures.length; i++) {
261 | var failure = globalFailures[i];
262 | alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessagePrefix + failure.message));
263 | }
264 |
265 | var results = find('.jasmine-results');
266 | results.appendChild(summary);
267 |
268 | summaryList(topResults, summary);
269 |
270 | function summaryList(resultsTree, domParent) {
271 | var specListNode;
272 | for (var i = 0; i < resultsTree.children.length; i++) {
273 | var resultNode = resultsTree.children[i];
274 | if (filterSpecs && !hasActiveSpec(resultNode)) {
275 | continue;
276 | }
277 | if (resultNode.type == 'suite') {
278 | var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id},
279 | createDom('li', {className: 'jasmine-suite-detail'},
280 | createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
281 | )
282 | );
283 |
284 | summaryList(resultNode, suiteListNode);
285 | domParent.appendChild(suiteListNode);
286 | }
287 | if (resultNode.type == 'spec') {
288 | if (domParent.getAttribute('class') != 'jasmine-specs') {
289 | specListNode = createDom('ul', {className: 'jasmine-specs'});
290 | domParent.appendChild(specListNode);
291 | }
292 | var specDescription = resultNode.result.description;
293 | if(noExpectations(resultNode.result)) {
294 | specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
295 | }
296 | if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') {
297 | specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
298 | }
299 | specListNode.appendChild(
300 | createDom('li', {
301 | className: 'jasmine-' + resultNode.result.status,
302 | id: 'spec-' + resultNode.result.id
303 | },
304 | createDom('a', {href: specHref(resultNode.result)}, specDescription)
305 | )
306 | );
307 | }
308 | }
309 | }
310 |
311 | if (failures.length) {
312 | alert.appendChild(
313 | createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'},
314 | createDom('span', {}, 'Spec List | '),
315 | createDom('a', {className: 'jasmine-failures-menu', href: '#'}, 'Failures')));
316 | alert.appendChild(
317 | createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-failure-list'},
318 | createDom('a', {className: 'jasmine-spec-list-menu', href: '#'}, 'Spec List'),
319 | createDom('span', {}, ' | Failures ')));
320 |
321 | find('.jasmine-failures-menu').onclick = function() {
322 | setMenuModeTo('jasmine-failure-list');
323 | };
324 | find('.jasmine-spec-list-menu').onclick = function() {
325 | setMenuModeTo('jasmine-spec-list');
326 | };
327 |
328 | // Set the default list to be shown - test descriptions or failures
329 | // setMenuModeTo('jasmine-failure-list');
330 | setMenuModeTo('jasmine-spec-list');
331 |
332 |
333 | var failureNode = find('.jasmine-failures');
334 | for (i = 0; i < failures.length; i++) {
335 | failureNode.appendChild(failures[i]);
336 | }
337 | }
338 | };
339 |
340 | return this;
341 |
342 | function find(selector) {
343 | return getContainer().querySelector('.jasmine_html-reporter ' + selector);
344 | }
345 |
346 | function clearPrior() {
347 | // return the reporter
348 | var oldReporter = find('');
349 |
350 | if(oldReporter) {
351 | getContainer().removeChild(oldReporter);
352 | }
353 | }
354 |
355 | function createDom(type, attrs, childrenVarArgs) {
356 | var el = createElement(type);
357 |
358 | for (var i = 2; i < arguments.length; i++) {
359 | var child = arguments[i];
360 |
361 | if (typeof child === 'string') {
362 | el.appendChild(createTextNode(child));
363 | } else {
364 | if (child) {
365 | el.appendChild(child);
366 | }
367 | }
368 | }
369 |
370 | for (var attr in attrs) {
371 | if (attr == 'className') {
372 | el[attr] = attrs[attr];
373 | } else {
374 | el.setAttribute(attr, attrs[attr]);
375 | }
376 | }
377 |
378 | return el;
379 | }
380 |
381 | function pluralize(singular, count) {
382 | var word = (count == 1 ? singular : singular + 's');
383 |
384 | return '' + count + ' ' + word;
385 | }
386 |
387 | function specHref(result) {
388 | return addToExistingQueryString('spec', result.fullName);
389 | }
390 |
391 | function seedHref(seed) {
392 | return addToExistingQueryString('seed', seed);
393 | }
394 |
395 | function defaultQueryString(key, value) {
396 | return '?' + key + '=' + value;
397 | }
398 |
399 | function setMenuModeTo(mode) {
400 | htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
401 | }
402 |
403 | function noExpectations(result) {
404 | return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
405 | result.status === 'passed';
406 | }
407 |
408 | function hasActiveSpec(resultNode) {
409 | if (resultNode.type == 'spec' && resultNode.result.status != 'disabled') {
410 | return true;
411 | }
412 |
413 | if (resultNode.type == 'suite') {
414 | for (var i = 0, j = resultNode.children.length; i < j; i++) {
415 | if (hasActiveSpec(resultNode.children[i])) {
416 | return true;
417 | }
418 | }
419 | }
420 | }
421 | }
422 |
423 | return HtmlReporter;
424 | };
425 |
426 | jasmineRequire.HtmlSpecFilter = function() {
427 | function HtmlSpecFilter(options) {
428 | var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
429 | var filterPattern = new RegExp(filterString);
430 |
431 | this.matches = function(specName) {
432 | return filterPattern.test(specName);
433 | };
434 | }
435 |
436 | return HtmlSpecFilter;
437 | };
438 |
439 | jasmineRequire.ResultsNode = function() {
440 | function ResultsNode(result, type, parent) {
441 | this.result = result;
442 | this.type = type;
443 | this.parent = parent;
444 |
445 | this.children = [];
446 |
447 | this.addChild = function(result, type) {
448 | this.children.push(new ResultsNode(result, type, this));
449 | };
450 |
451 | this.last = function() {
452 | return this.children[this.children.length - 1];
453 | };
454 | }
455 |
456 | return ResultsNode;
457 | };
458 |
459 | jasmineRequire.QueryString = function() {
460 | function QueryString(options) {
461 |
462 | this.navigateWithNewParam = function(key, value) {
463 | options.getWindowLocation().search = this.fullStringWithNewParam(key, value);
464 | };
465 |
466 | this.fullStringWithNewParam = function(key, value) {
467 | var paramMap = queryStringToParamMap();
468 | paramMap[key] = value;
469 | return toQueryString(paramMap);
470 | };
471 |
472 | this.getParam = function(key) {
473 | return queryStringToParamMap()[key];
474 | };
475 |
476 | return this;
477 |
478 | function toQueryString(paramMap) {
479 | var qStrPairs = [];
480 | for (var prop in paramMap) {
481 | qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
482 | }
483 | return '?' + qStrPairs.join('&');
484 | }
485 |
486 | function queryStringToParamMap() {
487 | var paramStr = options.getWindowLocation().search.substring(1),
488 | params = [],
489 | paramMap = {};
490 |
491 | if (paramStr.length > 0) {
492 | params = paramStr.split('&');
493 | for (var i = 0; i < params.length; i++) {
494 | var p = params[i].split('=');
495 | var value = decodeURIComponent(p[1]);
496 | if (value === 'true' || value === 'false') {
497 | value = JSON.parse(value);
498 | }
499 | paramMap[decodeURIComponent(p[0])] = value;
500 | }
501 | }
502 |
503 | return paramMap;
504 | }
505 |
506 | }
507 |
508 | return QueryString;
509 | };
510 |
--------------------------------------------------------------------------------
/jasmine/jasmine-2.8.0/jasmine.css:
--------------------------------------------------------------------------------
1 | body { overflow-y: scroll; }
2 |
3 | .jasmine-labname {
4 | display: flex;
5 | align-items: center;
6 | justify-content: left;
7 | background-color: #2e354c; /* #32c3ff */
8 | padding: 8px;
9 | }
10 |
11 | .jasmine-labname span {
12 | color: white;
13 | font-size: 19px;
14 | margin-left: 10px;
15 | }
16 |
17 | .jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; }
18 | .jasmine_html-reporter a { text-decoration: none; }
19 | .jasmine_html-reporter a:hover { text-decoration: underline; }
20 | .jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; }
21 | .jasmine_html-reporter .jasmine-banner, .jasmine_html-reporter .jasmine-symbol-summary, .jasmine_html-reporter .jasmine-summary, .jasmine_html-reporter .jasmine-result-message, .jasmine_html-reporter .jasmine-spec .jasmine-description, .jasmine_html-reporter .jasmine-spec-detail .jasmine-description, .jasmine_html-reporter .jasmine-alert .jasmine-bar, .jasmine_html-reporter .jasmine-stack-trace { padding-left: 9px; padding-right: 9px; }
22 | .jasmine_html-reporter .jasmine-banner { position: relative; }
23 | .jasmine_html-reporter .jasmine-banner .jasmine-title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; }
24 | .jasmine_html-reporter .jasmine-banner .jasmine-version { margin-left: 14px; position: relative; top: 6px; }
25 | .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; }
26 | .jasmine_html-reporter .jasmine-version { color: #aaa; }
27 | .jasmine_html-reporter .jasmine-banner { margin-top: 14px; }
28 | .jasmine_html-reporter .jasmine-duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; }
29 | .jasmine_html-reporter .jasmine-symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
30 | .jasmine_html-reporter .jasmine-symbol-summary li { display: inline-block; height: 10px; width: 14px; font-size: 16px; }
31 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { font-size: 14px; }
32 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { color: #007069; content: "\02022"; }
33 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { line-height: 9px; }
34 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; }
35 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-disabled { font-size: 14px; }
36 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-disabled:before { color: #bababa; content: "\02022"; }
37 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { line-height: 17px; }
38 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { color: #ba9d37; content: "*"; }
39 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { font-size: 14px; }
40 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { color: #ba9d37; content: "\02022"; }
41 | .jasmine_html-reporter .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; }
42 | .jasmine_html-reporter .jasmine-run-options .jasmine-trigger { cursor: pointer; padding: 8px 16px; }
43 | .jasmine_html-reporter .jasmine-run-options .jasmine-payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; }
44 | .jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { display: block; }
45 | .jasmine_html-reporter .jasmine-bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
46 | .jasmine_html-reporter .jasmine-bar.jasmine-failed { background-color: #ca3a11; }
47 | .jasmine_html-reporter .jasmine-bar.jasmine-passed { background-color: #007069; }
48 | .jasmine_html-reporter .jasmine-bar.jasmine-skipped { background-color: #bababa; }
49 | .jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; }
50 | .jasmine_html-reporter .jasmine-bar.jasmine-menu { background-color: #fff; color: #aaa; }
51 | .jasmine_html-reporter .jasmine-bar.jasmine-menu a { color: #333; }
52 | .jasmine_html-reporter .jasmine-bar a { color: white; }
53 | .jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list, .jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures { display: none; }
54 | .jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine_html-reporter.jasmine-failure-list .jasmine-summary { display: none; }
55 | .jasmine_html-reporter .jasmine-results { margin-top: 14px; }
56 | .jasmine_html-reporter .jasmine-summary { margin-top: 14px; }
57 | .jasmine_html-reporter .jasmine-summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
58 | .jasmine_html-reporter .jasmine-summary ul.jasmine-suite { margin-top: 7px; margin-bottom: 7px; }
59 | .jasmine_html-reporter .jasmine-summary li.jasmine-passed a { color: #007069; }
60 | .jasmine_html-reporter .jasmine-summary li.jasmine-failed a { color: #ca3a11; }
61 | .jasmine_html-reporter .jasmine-summary li.jasmine-empty a { color: #ba9d37; }
62 | .jasmine_html-reporter .jasmine-summary li.jasmine-pending a { color: #ba9d37; }
63 | .jasmine_html-reporter .jasmine-summary li.jasmine-disabled a { color: #bababa; }
64 | .jasmine_html-reporter .jasmine-description + .jasmine-suite { margin-top: 0; }
65 | .jasmine_html-reporter .jasmine-suite { margin-top: 14px; }
66 | .jasmine_html-reporter .jasmine-suite a { color: #333; }
67 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { margin-bottom: 28px; }
68 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { background-color: #ca3a11; }
69 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { color: white; }
70 | .jasmine_html-reporter .jasmine-result-message { padding-top: 14px; color: #333; white-space: pre; }
71 | .jasmine_html-reporter .jasmine-result-message span.jasmine-result { display: block; }
72 | .jasmine_html-reporter .jasmine-stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; }
73 |
--------------------------------------------------------------------------------
/tests/koans.spec.js:
--------------------------------------------------------------------------------
1 | describe('the JavaScript language', () => {
2 | describe('has different types and operators', () => {
3 | it('considers numbers to be equal to their string representation', () => {
4 | expect(1 == '1').toBeTruthy();
5 | expect(1 != '1').toBeFalsy();
6 | });
7 |
8 | it('knows that numbers and strings are not exactly the same', () => {
9 | expect(1 === '1').toBeFalsy();
10 | expect(1 !== '1').toBeTruthy();
11 | });
12 |
13 | it('joins parts as string when using the plus operator', () => {
14 | expect(1 + 'a').toEqual('1a');
15 | });
16 |
17 | it('operates integers before joining the string', () => {
18 | expect(1 + 1 + '2').toEqual('22');
19 | });
20 |
21 | it('knows the type of the variable', () => {
22 | const x = 1;
23 |
24 | expect(typeof x).toEqual('number');
25 | });
26 |
27 | it('surprises me, NaN is not comparable with NaN', () => {
28 | expect(5 / 'a').toEqual(5 / 'a');
29 | // expect(typeof NaN).toEqual();
30 | expect(isNaN(5 / 'a')).toBeTruthy();
31 | });
32 |
33 | it('considers an empty string to be falsy', () => {
34 | //expect("" == false).toBe......();// Truthy or Falsy
35 | //expect("" === false).toBe.....();// Truthy or Falsy
36 | });
37 |
38 | it('considers zero to be falsy', () => {
39 | //expect(0 == false).toBe......();// Truthy or Falsy
40 | //expect(0 === false).toBe.....();// Truthy or Falsy
41 | });
42 |
43 | it('considers nulls to be falsy', () => {
44 | const x = null;
45 | let result;
46 |
47 | if (x) {
48 | result = true;
49 | } else {
50 | result = false;
51 | }
52 |
53 | //expect(result == false).toBe......();// Truthy or Falsy
54 | //expect(null === false).toBe.....();// Truthy or Falsy
55 | //expect(null == false).toBe....();// Truthy or Falsy
56 | });
57 |
58 | it('knows the type of a function', () => {
59 | function x() {
60 | // ...
61 | }
62 |
63 | expect(typeof x).toBe('function');
64 | //expect(typeof(xxx)).toBe('...');
65 | });
66 |
67 | it('has arrays and they can contain anything inside', () => {
68 | const arr = [1, 2, 3, 4];
69 | arr.push(5);
70 | arr[9] = 6;
71 |
72 | const matrix = [
73 | ['a', 'b', 'c'],
74 | ['d', 'e', 'f'],
75 | ['g', 7, 8]
76 | ];
77 |
78 | /*
79 | expect(arr[1]).toEqual();
80 | expect(arr[4]).toEqual();
81 | expect(arr[6]).toEqual();
82 | expect(arr[9]).toEqual();
83 | expect(matrix[0][2]).toEqual();
84 | */
85 | });
86 |
87 | it('may contain functions inside arrays', () => {
88 | const arr = [
89 | 1,
90 | 2,
91 | function (arg) {
92 | return 3 + arg;
93 | }
94 | ];
95 |
96 | //expect(arr[2](1)).toEqual();
97 | });
98 |
99 | it('concatenate arrays - well, kind of', () => {
100 | const a = [1, 2, 3];
101 | const b = [4, 5, 6];
102 |
103 | //expect(a + b).toEqual();
104 | });
105 |
106 | it('joins arrays and strings', () => {
107 | const a = [1, 2, 3];
108 |
109 | //expect ("1" + a).toEqual();
110 | //expect(a + "1").toEqual();
111 | });
112 |
113 | it('joins arrays and other things', () => {
114 | const a = [1, 2, 3];
115 | const b = ['x', 'y', 'z'];
116 |
117 | //expect(1 + a).toEqual();
118 | //expect(a + 1).toEqual();
119 | //expect(1 + b).toEqual();
120 | //expect(true + a).toEqual();
121 | });
122 |
123 | it("can't compare arrays", () => {
124 | const a = [1, 2, 3];
125 | const b = [1, 2, 3];
126 |
127 | //expect(a == b).toBe.....(); // Truthy or Falsy
128 | //expect(a === b).toBe.....(); // Truthy or Falsy
129 | });
130 |
131 | it('is not the same to compare by value than by reference ', () => {
132 | const a = [1, 2, 3];
133 | const b = [1, 2, 3];
134 |
135 | expect(a).toEqual(b); // Jasmine toEqual compares by value
136 | expect(a).not.toBe(b); // Jasmine toBe compares by reference
137 | });
138 | });
139 |
140 | describe('considers functions as first class citizens', () => {
141 | it('can declare named functions', () => {
142 | function example() {
143 | return 'some example';
144 | }
145 |
146 | //expect(example()).toEqual();
147 | });
148 |
149 | it('can declare anonymous functions', () => {
150 | const someVar = function (a, b) {
151 | return a + b;
152 | };
153 |
154 | //expect(typeof(someVar)).toBe();
155 | //expect(someVar(1,1)).toBe();
156 | });
157 |
158 | it('may return anything', () => {
159 | function example(arg) {
160 | return [arg, arg * 2, arg * 3];
161 | }
162 |
163 | const result = example(2);
164 |
165 | //expect(result[1]).toEqual();
166 | });
167 |
168 | it('may return arrays that contains functions and so on', () => {
169 | function example() {
170 | // write the missing code here
171 | }
172 |
173 | //expect(example()[0](1)[1]).toEqual(10);
174 | });
175 |
176 | it("doesn't care about the declaration order when they are named", () => {
177 | function exampleA() {
178 | return exampleB(1);
179 | }
180 |
181 | //expect(exampleA()).toEqual();
182 |
183 | function exampleB(arg1) {
184 | return arg1;
185 | }
186 | });
187 |
188 | it('matters, the declaration order when they are anonymous', () => {
189 | const exampleA = function () {
190 | return exampleB(1);
191 | };
192 |
193 | //expect(exampleA()).toEqual(1);
194 |
195 | const exampleB = function (arg1) {
196 | return arg1;
197 | };
198 | });
199 |
200 | it('can use optional parameters', () => {
201 | function example(a, b, c) {
202 | if (c) {
203 | return a + b + c;
204 | }
205 | return a + b;
206 | }
207 |
208 | //expect(example(1,1,1)).toBe();
209 | //expect(example(1,1)).toBe();
210 | });
211 |
212 | it('anonymous functions are anonymous', () => {
213 | const x = function z() {
214 | return 1;
215 | };
216 | //expect(typeof(z)).toEqual();
217 | //expect(x()).toEqual();
218 | });
219 |
220 | it('can create closures with free variables', () => {
221 | function external() {
222 | const a = 1;
223 |
224 | function internal() {
225 | return a + 1;
226 | }
227 |
228 | return internal();
229 | }
230 |
231 | //expect(external()).toBe();
232 | });
233 |
234 | it('can create closures with several free variables', () => {
235 | function external() {
236 | const a = 1,
237 | b = 2;
238 |
239 | function internal() {
240 | const c = 3;
241 | return a + b + c;
242 | }
243 | }
244 |
245 | //expect(external()).toBe(6);
246 | });
247 |
248 | it('defines a pure function when there are no free variables', () => {
249 | function external() {
250 | const a = 1,
251 | b = 2;
252 |
253 | function internal(a, b) {
254 | const c = 1;
255 | return a + b + c;
256 | }
257 |
258 | return internal(4, 4);
259 | }
260 |
261 | //expect(external()).toBe();
262 | });
263 |
264 | it('may return arrays that contains closures and so on', () => {
265 | function example() {
266 | // write the missing code here
267 | }
268 |
269 | //expect(example()[0](1)[1]).toEqual(10);
270 | //expect(example()[0](2)[1]).toEqual(11);
271 | //expect(example()[0](3)[1]).toEqual(12);
272 | });
273 |
274 | it('passes primitive types as values (a copy) to functions', () => {
275 | function example(arg) {
276 | arg = 'test!';
277 | }
278 |
279 | const x = 1;
280 | const y = 'example';
281 | const z = true;
282 |
283 | example(x);
284 | //expect(x).toEqual();
285 |
286 | example(y);
287 | //expect(y).toEqual();
288 |
289 | example(z);
290 | //expect(z).toEqual();
291 | });
292 |
293 | it('passes arrays by reference', () => {
294 | function example(arg) {
295 | arg[0] = 100;
296 | }
297 |
298 | const x = [1, 2, 3];
299 |
300 | example(x);
301 | //expect(x).toEqual();
302 | });
303 |
304 | it('passes objects by reference', () => {
305 | function example(arg) {
306 | arg.property = 'test';
307 | }
308 |
309 | const x = { property: 'cool!' };
310 |
311 | example(x);
312 | //expect(x).toEqual();
313 | });
314 |
315 | it('may return a function as the result of invoking a function', () => {
316 | function add(a, b) {
317 | return a + b;
318 | }
319 |
320 | function example() {
321 | return add;
322 | }
323 |
324 | //expect(example()(1,2)).toEqual();
325 | const f = example();
326 | //expect(f(2,2)).toEqual();
327 | });
328 |
329 | it('can return closures as a function result', () => {
330 | function plus(amount) {
331 | return function (number) {
332 | return number + amount;
333 | };
334 | }
335 |
336 | const f = plus(5);
337 |
338 | //expect(f(3)).toBe();
339 | });
340 |
341 | it('can have functions that receive other functions as arguments', () => {
342 | function add(a, b) {
343 | return a + b;
344 | }
345 |
346 | function example(arg) {
347 | return arg(2, 2) + 1;
348 | }
349 |
350 | //expect(example(add)).toEqual();
351 | });
352 |
353 | it('may have functions as the input and the output', () => {
354 | function plus(originalFunction) {
355 | return function (arg1) {
356 | return originalFunction() + arg1;
357 | };
358 | }
359 |
360 | const f = plus(function () {
361 | return 1;
362 | });
363 |
364 | //expect(f(2)).toBe();
365 | });
366 |
367 | it("can invoke functions indirectly using the special 'call'", () => {
368 | function f(a, b) {
369 | return a + b;
370 | }
371 |
372 | //expect(f.call(f,1,1)).toEqual();
373 | });
374 |
375 | it("can invoke functions indirectly using the special 'apply'", () => {
376 | function f(a, b) {
377 | return a + b;
378 | }
379 |
380 | //expect(f.apply(f, [1,1])).toEqual();
381 | });
382 |
383 | it("doesn't have a private scope inside blocks", () => {
384 | let j = 0;
385 | for (let i = 0; i < 5; i++) {
386 | j += i;
387 | }
388 |
389 | //expect(i).toEqual();
390 | //expect(j).toEqual();
391 | });
392 | });
393 |
394 | describe('has multiple ways to define and create objects', () => {
395 | it('can define object literals', () => {
396 | const obj = {
397 | name: 'bob',
398 | theName: function () {
399 | return this.name;
400 | }
401 | };
402 |
403 | //expect(obj.theName()).toBe();
404 | });
405 |
406 | it('can create properties dynamically', () => {
407 | const obj = {
408 | name: 'bob',
409 | surname: 'sponge'
410 | };
411 | obj.address = 'palm tree';
412 |
413 | //expect(obj.address).toEqual();
414 | //expect(obj['address']).toEqual();
415 | //expect(obj['name']).toEqual();
416 | });
417 |
418 | it('may define complex objects', () => {
419 | let user;
420 | // write the contents of the obj to make the satisfy the expectations:
421 |
422 | //expect(user.address.street).toEqual('sesame');
423 | //expect(user.friends[0].name).toEqual('triki');
424 | });
425 |
426 | it('has a pattern called, the Module Pattern', () => {
427 | function createObject() {
428 | let points = 0;
429 |
430 | return {
431 | addPoint: function () {
432 | ++points;
433 | },
434 | score: function () {
435 | return points;
436 | }
437 | };
438 | }
439 |
440 | const obj = createObject();
441 | obj.addPoint();
442 |
443 | //expect(obj.score()).toEqual();
444 | //expect(typeof(obj.points)).toEqual();
445 | });
446 |
447 | it('may create objects also with the module pattern', () => {
448 | function createObject(initialScore) {
449 | // write the code here
450 | }
451 |
452 | /*
453 | const obj = createObject(5, 'red');
454 | obj.incrementScoreIn(5);
455 | expect(obj.color).toEqual('red');
456 | expect(obj.points()).toEqual(10);
457 | */
458 | });
459 |
460 | it('can define constructors', () => {
461 | function Obj() {
462 | const name = 'bob';
463 |
464 | this.theName = function () {
465 | return name;
466 | };
467 | }
468 |
469 | const obj = new Obj();
470 | //expect(obj.theName()).toBe();
471 | });
472 |
473 | it("may contain 'static' methods", () => {
474 | function Obj() {
475 | const name = 'bob';
476 |
477 | this.theName = function () {
478 | return name;
479 | };
480 | }
481 |
482 | Obj.someStaticMethod = function () {
483 | return 22;
484 | };
485 |
486 | //expect(Obj.someStaticMethod()).toBe();
487 | });
488 |
489 | it('can have have methods in the prototype', () => {
490 | function Obj() {
491 | const name = 'bob';
492 | }
493 |
494 | Obj.prototype.theName = function () {
495 | return this.name;
496 | };
497 |
498 | const obj = new Obj();
499 | //expect(obj.theName()).toEqual();
500 | //expect(obj.theName).toBe(new Obj().theName);
501 | });
502 |
503 | it('can define a factory', () => {
504 | function obj() {
505 | const self = {};
506 | const name = 'bob';
507 |
508 | self.theName = function () {
509 | return name;
510 | };
511 |
512 | return self;
513 | }
514 |
515 | const instance = obj();
516 | //expect(instance.theName()).toBe();
517 | //expect(instance.theName).not.toBe(obj().theName);
518 | });
519 |
520 | it('can create methods dynamically on an object instance', () => {
521 | const obj = {};
522 | const methodNames = ['meow', 'jump'];
523 |
524 | for (let i = 0; i < methodNames.length; i++) {
525 | obj[[methodNames[i]]] = function () {
526 | return 'it works';
527 | };
528 | }
529 |
530 | //expect(obj.meow()).toEqual();
531 | });
532 |
533 | describe('the polymorphism', () => {
534 | it('may use constructor plus prototype', () => {
535 | function Parent() {
536 | this.name = 'parent';
537 | }
538 | Parent.prototype.someMethod = function () {
539 | return 10;
540 | };
541 |
542 | function Child() {
543 | Parent.call(this); // constructor stealing
544 | this.name = 'child';
545 | }
546 | Child.prototype = Object.create(Parent.prototype); // prototype chaining
547 |
548 | const child = new Child();
549 | //expect(child.someMethod()).toEqual();
550 | //expect(child.name).toEqual();
551 | });
552 |
553 | it('may use the functional inheritance', () => {
554 | function parent() {
555 | const name = 'parent';
556 | const self = {};
557 | self.someMethod = function () {
558 | return 10;
559 | };
560 | return self;
561 | }
562 |
563 | function child() {
564 | const name = 'child';
565 | const self = parent();
566 | return self;
567 | }
568 |
569 | const instance = child();
570 | //expect(instance.someMethod()).toBe();
571 | });
572 | });
573 | });
574 |
575 | describe('commons patterns with functions and behaviors', () => {
576 | it('can invoke functions immediately to take advantage of scopes', () => {
577 | const myNamespace = {};
578 |
579 | (function (theNamespace) {
580 | let counter = 0;
581 |
582 | theNamespace.addOne = function () {
583 | counter++;
584 | };
585 |
586 | theNamespace.giveMeTheCount = function () {
587 | return counter;
588 | };
589 | })(myNamespace);
590 |
591 | myNamespace.addOne();
592 | myNamespace.addOne();
593 |
594 | //expect(myNamespace.giveMeTheCount()).toBe();
595 | });
596 |
597 | it("hoists variables the way you probably don't expect", () => {
598 | function generate() {
599 | const functions = [];
600 | for (let i = 0; i < 5; i++) {
601 | functions.push(function () {
602 | return i;
603 | });
604 | }
605 | return functions;
606 | }
607 |
608 | //expect(generate()[0]()).toEqual();
609 | //expect(generate()[1]()).toEqual();
610 | });
611 | });
612 |
613 | describe('has ways to simulate classes', () => {
614 | // "Class"
615 | function Cat() {
616 | this.kilos = 1;
617 | this.feed = function () {
618 | this.kilos++;
619 | };
620 | this.isPurring = function () {
621 | return true;
622 | };
623 | }
624 |
625 | //////////////////////////////////////
626 | // "Class"
627 | //////////////////////////////////////
628 | function Lion(energy) {
629 | Cat.call(this);
630 | this.energy = energy || 100;
631 | const self = this;
632 |
633 | const run = function () {
634 | // private method
635 | self.energy -= 10;
636 | };
637 | const attack = function () {
638 | // private method
639 | self.energy -= 5;
640 | };
641 | this.playWithFriend = function (friend) {
642 | if (friend.isPurring()) self.energy += 10;
643 | };
644 | this.hunt = function () {
645 | // public method
646 | run();
647 | attack();
648 | this.onHunting(); // fire event
649 | };
650 | this.onHunting = function () {
651 | /* event */
652 | };
653 | }
654 |
655 | describe('and the THIS keyword', () => {
656 | let cat;
657 |
658 | beforeEach(function () {
659 | cat = new Cat();
660 | window.kilos = 0;
661 | });
662 |
663 | it('sometimes works as expected in other languages', () => {
664 | cat.feed();
665 | cat.feed();
666 |
667 | //expect(cat.kilos).toEqual();
668 | });
669 |
670 | it('works different on detached functions', () => {
671 | window.kilos = 10;
672 | let feed = cat.feed;
673 |
674 | feed();
675 |
676 | //expect(window.kilos).toEqual();
677 | //expect(cat.kilos).toEqual();
678 | });
679 |
680 | it('can be bound explicitly with CALL and APPLY', () => {
681 | const feed = cat.feed;
682 | feed.apply(cat);
683 |
684 | //expect(cat.kilos).toEqual();
685 | });
686 |
687 | it('can be bound in modern browsers with BIND', () => {
688 | const feed = cat.feed;
689 | const bound = feed.bind(cat);
690 |
691 | bound();
692 |
693 | //expect(cat.kilos).toEqual();
694 | });
695 |
696 | it('works different when function is attached to other object', () => {
697 | const otherCat = new Cat();
698 | otherCat.kilos = 10;
699 | otherCat.feed = cat.feed;
700 |
701 | otherCat.feed();
702 | //expect(otherCat.kilos).toEqual();
703 | //expect(cat.kilos).toEqual();
704 | });
705 |
706 | it('can be handled using the SELF trick', () => {
707 | const energy = 200;
708 | const lion = new Lion(energy);
709 |
710 | lion.hunt();
711 |
712 | //expect(lion.energy).toEqual();
713 | });
714 |
715 | it('interprets the THIS when the function is executed', () => {
716 | const energy = 200;
717 | const lion = new Lion();
718 |
719 | lion.hunt = function () {
720 | this.energy = 4000;
721 | };
722 | lion.hunt();
723 |
724 | //expect(lion.energy).toEqual();
725 | });
726 | });
727 | });
728 | });
729 |
--------------------------------------------------------------------------------