├── .gitignore ├── README.md ├── code.js ├── jasmine ├── MIT.LICENSE ├── SpecRunner.html ├── lib │ └── jasmine-3.3.0 │ │ ├── boot.js │ │ ├── jasmine-html.js │ │ ├── jasmine.css │ │ ├── jasmine.js │ │ └── jasmine_favicon.png ├── spec │ ├── PlayerSpec.js │ └── SpecHelper.js └── src │ ├── Player.js │ └── Song.js ├── report.html └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | ## The .gitignore file exists to ensure that git ignores specific files from source control 2 | ## This is where you would reference any files that contain passwords or API keys you should keep secret. 3 | 4 | # Operating System files to ignore 5 | .DS_Store 6 | Thumbs.db 7 | 8 | # Dependency directories 9 | node_modules/ 10 | jspm_packages/ 11 | 12 | # Editor configuration files to ignore 13 | *.iml 14 | .idea/ 15 | 16 | # Logs 17 | logs 18 | *.log 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | # dotenv environment variables file 24 | .env 25 | .env.test 26 | 27 | # Optional eslint cache 28 | .eslintcache 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Runtime data 34 | pids 35 | *.pid 36 | *.seed 37 | *.pid.lock 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intro to Testing in JS 2 | 3 | ## Summary 4 | This is an introduction to automated testing in JavaScript. The idea behind automated testing is to provide immediate feedback on the code you write to solve a problem, add a feature, or fix a bug. The feedback from the tests comes in the form of green passing tests or red failing tests. Tests are functions that test, or exercise, OTHER functions, comparing the results of actual output vs. expected output. 5 | 6 | Specifically, we will be working with "unit tests", as our type of test. A unit means the smallest possible block of functionality. Most frequently, this means a single user-defined function in code. Unit tests aim to test the building blocks of functionality. In this way, they test application code from the inside-out. Other types of test such as end-to-end tests test the entire application from the outside-in 7 | 8 | Additionally, we will be using a process called Test Driven Development, commonly called TDD, where we author tests to assert expected vs. actual results before authoring the code that produces those results. While TDD can be used on a variety of types of tests, we'll be applying the TDD workflow and process with unit tests. 9 | 10 | ## Key Vocabulary 11 |
12 |
Implementation
13 |
The code the a developer authors to realize the sequence of steps that the code runs to solve a problem (called an algorithm).
14 |
Refactor
15 |
To refactor means to re-write an implementation. [Refactoring](https://martinfowler.com/bliki/DefinitionOfRefactoring.html) aims to increase the code quality without changing its observable behavior. Developers refactor code to increase readability, increase flexibility, add a fix for edge-case bugs, reduce duplication, or make the code more accessible for yourself and other developers moving forward.
16 |
Unit
17 |
An individual piece of functionality which is single, whole, and complete but which also forms an individual component of a larger or more complex whole.
18 |
Unit Test
19 |
Unit tests are automated tests that test the accuracy, reliability, and appropriateness of the implementation for a unit of functionality. Unit tests compare the expected vs. actual outputs for user-defined functions when provided various inputs.
20 |
Test Driven Development, TDD
21 |
The practice of writing tests for functionality before writing the implementation of that functionality. Rather than authoring tests after the functionality exists, the TDD workflow aims to use the automated tests to provide feedback before and as the developer authors their implementation
22 |
23 | 24 | ## Overview 25 | - Green tests are passing, red tests mean that the code is incomplete, inaccurate, missing, or you have a syntax error somewhere in your code. 26 | - Syntax errors in either the `tests.js` or the `code.js` file will keep things from running accurately. If you go from a page of many green tests to all red, there's likely a typo or syntax errors somewhere. 27 | - The approach of writing tests before writing the code that passes is called Test-Driven Development, or TDD. 28 | - Writing small tests, like the ones provided, is called `unit testing`. 29 | - "Unit testing" means to test a piece of functionality as small as a single function. Testing entire applications from end-to-end is another topic. 30 | - This introduction will introduce unit tests, the TDD practice of writing tests first, and writing the code to pass unit tests. 31 | 32 | ## Prerequisites for this exercise 33 | - Understanding of valid JavaScript syntax 34 | - Primitive data types and basic operators in JavaScript 35 | - Control statements (if, if/else, if/elseif/else) 36 | - Authoring user defined functions 37 | - This material can be delivered after data types, conditionals, and functions. This exercise is appropriate to introduce before loops, arrays, objects, etc... 38 | 39 | ## Reference: The Test Driven Development means that tests "drive" the development. 40 | [Test Driven Development](https://en.wikipedia.org/wiki/Test-driven_development), TDD, is the process of authoring a test before writing any other code. Here's the TDD workflow: 41 | 42 | 1. Write the smallest possible test: Identify the simplest, smallest thing you want to confirm. For example, before a function exists, assert that the function is defined. 43 | 2. Run all the tests to ensure that the new test fails. We're **supposed** to see a red, failing test, since there's no code yet to green it. Red means we did the first step correctly. Seeing the newest test turn red is critical because our tests drive the development, not the other way around. 44 | 3. Write only enough code to green that newest test. No more, no less. It's OK if things are hard-coded here. 45 | 4. Run all tests. We're looking for green tests across the board. 46 | 5. Refactor the code. 47 | 6. Repeat the process by going back to Step 1. The repeat step means following the steps to add new tests. 48 | 49 | This entire TDD workflow is often explained as the repetition of "Red, Green, Refactor". This encapsulates the idea of writing a single, small failing test, writing only enough code to green the test, and then to refactor, when possible. 50 | 51 | ## Three Laws of Test Driven Development 52 | - You are not allowed to write any production code unless it is to make a failing unit test pass. 53 | - You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures. 54 | - You are not allowed to write any more production code than is sufficient to pass the one failing unit test. 55 | 56 | ## Getting Started 57 | 1. Fork this repository to make a copy on your own GitHub account. 58 | 1. Make sure that your browser is showing this project in your own repositories list in your own account. 59 | 1. Click the green button on the right that says "Clone or Download". 60 | 1. The clone address should look like `git@github.com:your-github-username/intro-to-testing-js.git`, where `your-github-username` is actually your own username on GitHub. 61 | 1. Once you've copied your repo's clone address, it's time to clone the project in one of two ways: 62 | - If you're using IntelliJ, choose New->Project From Version Control->Git and then paste in the clone address.`git clone git@github.com:your-github-username/intro-to-testing-js.git`. 63 | - If you're using command line, then execute the following command line command: `git clone git@github.com:your-github-username/intro-to-testing-js.git`. 64 | 1. Once cloned to your projects directory, open up the project. 65 | 1. Launch `report.html` in your browser. You should see a set of green tests for the `helloWorld` function. 66 | 1. Refresh `report.html` to re-run new code in `test.js` or `code.js`. Do this any time the test or the implementation code changes. 67 | 68 | ### Project Structure 69 | - The `report.html` file is the test running tool. In this case, the HTML page is loading both the `tests.js` and `code.js` files. 70 | - The `tests.js` file contains the assertions that provide feedback on the appropriateness of the solutions in `code.js`. 71 | - The `code.js` file contains the implementation code. An "implementation" means the code that is meant to solve a problem, fix a bug, or add a feature. 72 | 73 | ## Exercise #0 - look, guess, test, conclude 74 | 75 | 1. Clone this repo to your projects folder following the "Getting Started" directions. Take a moment to orient yourself with the test runner, the existing tests, and the implementation inside of `code.js`. 76 | 77 | 1. Once you're setup and comfortable, go to `code.js` and change the name of the `helloWorld` function to `hello`. Then refresh `report.html` in your browser. 78 | - What do you notice about the test results? 79 | - What are some ways you think we could get the tests to turn green again? 80 | - Set the function name in `code.js` back to `helloWorld` and re-run the tests. 81 | 82 | 1. Inside of the `helloWorld` function in `code.js`, replace `return "Hello, World!"` with `return "Hello"`. 83 | - Run the tests by refreshing `report.html` in your browser. 84 | - Which tests fail? Which tests are still green? 85 | - Set the implementation back to `return "Hello, World!"` 86 | 87 | 1. Inside of the `helloWorld` function in `code.js`, change the line `return "Hello, World!"` to `console.log("Hello, World!")`. Then refresh `report.html`. 88 | - What happens to the tests? Identify which tests stay green and which ones turn red. 89 | - Why do you think that is? 90 | - Consider, what is the return value of a `console.log`? *hint*, it's always the same 91 | - Consider, what is the return value of a `return`? 92 | - Fix your `helloWorld` implementation so that it greens all the tests. 93 | 94 | 1. Now, let's purposefully put a syntax error into the `helloWorld` function, to see what happens with the tests. 95 | - Open up `code.js` and remove the closing curly brace from the end of the `helloWorld` function definition. 96 | - Refresh `report.html` in your browser. 97 | - Fix the syntax error and confirm that tests are all green. 98 | 99 | 1. Now, go to `code.js` and replace the function statement for `helloWorld` with a function expression. Do all the tests stay green or not? Why or why not? Double check your syntax. These are interchangeable because functions are *first class* citizens in the JS language. 100 | ```js 101 | // function statement syntax 102 | function helloWorld() { 103 | return "Hello, World!"; 104 | } 105 | ``` 106 | 107 | ```js 108 | // function expression syntax (assigning an anonymous function to a variable) 109 | const helloWorld = function() { 110 | return "Hello, World!"; 111 | } 112 | ``` 113 | 114 | **Before moving on, ensure that all tests are green.** 115 | 116 | ## Let's Test Drive a `sayHello` function 117 | - We'll build up our solution incrementally, in a Test-Driven manner. 118 | - Be careful not to refactor too early. Only refactor once we have sufficient tests. 119 | - Ultimately, `sayHello` should say "Hello" to any string we pass to it. 120 | - We'll handle some edge cases once we've solved the heart of the matter. 121 | 122 | ### Exercise #1 Take your first "Test Drive" by writing your first test! 123 | Our next exercise is to follow the TDD workflow to develop incremental tests and solutions for testing a `sayHello()` function that takes in a name as an argument and returns a string that says hello to that name. 124 | - Step 1: Let's write the smallest test possible. Open up `tests.js`. Add a `describe`, an `it`, and an `expect` to assert that `sayHello` is a defined function. Use your tests for `helloWorld` as a guide. 125 | - Step 2: Run all the tests. At this point, we're expecting and *hoping* for a single, red failing test that we just now authored. 126 | - Step 3: Now, let's go to `code.js` and create an empty function definition for `sayHello`. 127 | - Step 4: Run all the tests. We're expecting all tests, including the new test for `sayHello` to be green. 128 | - Step 5: Given that this is our first (tiny) test and our first implementation, there is not yet the opportunity to refactor. 129 | - Step 6: The last TDD step is to "repeat" the process of adding another test. What we're going to do is add our work to git and then move to Exercise #2, which is to add the second test. 130 | 131 | ### Before proceeding, add your work to GitHub! 132 | - Open your terminal and navigate to the local directory where you cloned this project. 133 | - First, `git status`. Notice which files are tracked by git and which files have changes. 134 | - Second, type `git add -A` to tell git that you want to get all the changed files staged for commit. 135 | - Now, type `git status`. You should see file names in green. This means that the files are ready for commit. 136 | - Next, type `git commit -m "add the first test and solution for intro-to-testing"` 137 | - Type `git status`, again, to make sure that all files are added and committed. 138 | - Finally, push your work by running `git push`. Pushing uploads your new commits to your remote repository, meaning your own fork on GitHub. 139 | 140 | ### Exercise #2 Ensure our function returns the right data type. 141 | - New tests for `sayHello` will each have their own `expect` and `it` inside the `describe` previously created. Each function you are writing and testing should typically have its own `describe` 142 | - Step 1: The smallest possible test, now that the function exists, is to ensure that calling the function gives us a string. Inside of `tests.js`, add an assertion to `sayHello` that it "should return a string when called.". The test should look similar to `expect(typeof sayHello()).toBe("string")` 143 | - Step 2: Run all tests to make sure that the new test starts red. 144 | - Step 3: Have your `sayHello` function return a string. The simplest code and smallest change possible is to return an empty string `return ""`. 145 | - Step 4: Now, run all the tests to ensure that the previously red test is now turned green by our impelementation. 146 | - Step 5: There's nothing to refactor. 147 | - Step 6: Repeat (Repeat the process by moving to build the next, small test) 148 | - Always: Add, commit, and push your work to GitHub. 149 | 150 | ### Exercise #3 - Add a test to confirm actual vs. expected output. 151 | - Step 1: Now that the function exists and returns the right data type, let's add our first realistic assertion. In `tests.js`, assert that `sayHello("Jane")` returns `"Hello, Jane!"`. Our first test should be *super* simple and *super* small. 152 | - Step 2: Run all tests and make sure that this newly added test is red. 153 | - Step 3: If the test wants us to return `"Hello, Jane!"` then literally write `return "Hello, Jane!";` because that's the simplest way to green a test looking for `"Hello, Jane!"`. 154 | - Step 4: Run all tests. They should all be green at this point. 155 | - Step 5: It's too soon to refactor. 156 | - Step 6: Repeat step means to add another test, so let's move to the next exercise. 157 | 158 | ### Exercise #4 Add another small, simple test 159 | - Step 1: In `tests.js`, assert that `sayHello("Alex")` returns `"Hello, Alex!"`. Our first test should be *super* simple and *super* small. This means that our next test should look like `expect(sayHello("Alex")).toBe("Hello, Alex!")`. 160 | - Step 2: Run all tests and make sure that this newly added test is red. 161 | - Step 3: It's challenging not to jump to the "correct" answer already, but let's stay close to the TDD method. Write *just* enough code to green the test. This means making sure that the `sayHello` function definition inside of `code.js` takes an an input argument. If `input === "Alex"`, then we `return "Hello, Alex!"` else `return "Hello, Jane!"`. Don't get too fancy. A cornerstone of TDD is refactoring only once you have a handful of green tests, not just one or two with new inputs. 162 | - Step 4: Run all tests, expecting that all are now green. Does each test turn green? If so, then we can proceed. We can't refactor unless we have greened a test, even with a hard-coded implementation. 163 | - Step 5: If you feel the urge to refactor already, hang on! Let's add one more test! 164 | - Step 6: Repeat the TDD cycle, so let's add another test in the next exercise. 165 | 166 | ### Exercise #5 One more test before refactoring... 167 | - Step 1: Add another (tiny) assertion! In `tests.js`, assert that `sayHello("Pat")` returns `"Hello, Pat!"`. Since our tests should be super simple, the assertion should be `expect(sayHello("Pat")).toBe("Hello, Pat!")` 168 | - Step 2: Run all tests and make sure that this newly added test is red and failing. 169 | - Step 3: Again, you may feel the urge to jump to the "correct" answer already. Let's stay on target. Write *just* enough code to green the test. For this case, *just enough* code means adding another conditional such that if `input === "Pat"`, then the function should have `return "Hello, Pat!"`. 170 | - Step 4: Run all tests. Does each test turn green? If so, then we can proceed. 171 | - Step 5: Refactor! It's definitely refactoring time! 172 | 173 | ### When to Refactor 174 | - How do we know that it's time to refactor? The answer: Once we have a handful of green tests, but the logic feels hard-coded, funky, or incomplete, then it's probably refactoring time. 175 | - Notice that when the input is "Jane", "Pat", or "Alex", the tests green. But what if we sent in any other name as the argument? 176 | - When every new test means that we're adding another `if` or `else if` to the code, is there a better way of doing things? 177 | - Refactoring is only possible once we have a handful of passing, green tests. These give us safety and guidance. 178 | - This may feel slow, but each new test cycle should only take 2-3 minutes, if not shorter! 179 | - Since our goal is to have a sayHello function that says hello to any input string, then adding a new conditional for each input is not scalable. 180 | - In the TDD approach, refactoring is only possible if you have enough tests and enough code that all the tests are green. 181 | - In this way, your tests provide a target for the refactor. If your refactoring causes previously written tests to fail, then reexamine the refactored code to ensure it is correctly written. 182 | 183 | 184 | ### Exercise #6 Implement the refactor! 185 | - Inside `sayHello` in `code.js`, what's a change you can identify that will improve the overall functioning of this function? 186 | - Can you get the implmentation of `sayHello` down to a function with only one line of code inside? 187 | - If we have `return "Hello, " + input + "!";`, does this work for all names? 188 | - Does this bring up any other issues with other inputs? 189 | 190 | ### Exercise #7 Add, commit, and push your work to GitHub. 191 | - "If your code ain't checked-in to source control, then it doesn't exist." 192 | - In your terminal, ensure that the `pwd` command shows that you're in the directory for this project. 193 | - First, `git status`. Notice which files are tracked by git and which files have changes. 194 | - Second, type `git add -A` to tell git that you want to get all the changed files staged for commit. 195 | - Now, type `git status`. You should see file names in green. This means that the files are ready for commit. 196 | - Next, type `git commit -m "add tests and ability to say 'hello' to any input."` 197 | - Type `git status`, again, to make sure that all files are added and committed. 198 | - Finally, push your work with `git push`. 199 | 200 | ### Exercise #8 "Repeat" step (where we look for additional tests to add) 201 | - First, in `tests.js`, add `expect(sayHello()).toBe("Hello, World!")`. Then refresh `report.html` to see the failing test. 202 | - Follow that by adding just enough code inside of the `sayHello` function `code.js` to green that latest test. Recommend checking if the input variable's value is `undefined`. 203 | - Next, add `expect(sayHello(true)).toBe("Hello, World!")` to the `tests.js` file. Refresh to see the failing red test. 204 | - Add just enough code to `code.js` to green that latest test. `if (input === true)` then `return "Hello, World!"` 205 | - Now, add `expect(sayHello(false)).toBe("Hello, World!")` to the `tests.js` file. Refresh to see the failing test. 206 | - Add just enough code to `code.js` to green this test. 207 | - Once all the tests are green, identify refactor opportunities and refactor your solution. 208 | - Are there any other [edge cases](https://en.wikipedia.org/wiki/Edge_case) you want to write a test for? You have a green light to add more of your own tests to "drive" the implementation. 209 | - Some edge cases to consider. What if: 210 | - the input is `null`? 211 | - the input is an empty string like `""`? 212 | - the input is a number like `2.3`? 213 | - the input is a number inside a string like `"5"`? 214 | - the input is another data type like an array, object, or function? 215 | 216 | ### Exercise #9 Add, commit, and push your work to GitHub (make this a habit) 217 | - "If your code ain't checked-in to source control, then it doesn't exist." 218 | - In your terminal, ensure that the `pwd` command shows that you're in the directory for this project. 219 | - First, `git status`. Notice which files are tracked by git and which files have changes. 220 | - Second, type `git add -A` to tell git that you want to get all the changed files staged for commit. 221 | - Now, type `git status`. You should see file names in green. This means that the files are ready for commit. 222 | - Next, type `git commit -m "unit tests for edge cases."` 223 | - Type `git status`, again, to make sure that all files are added and committed. 224 | - Finally, push your work with `git push`. 225 | 226 | ## Exercise #10 Let's Test-Drive an `isFive` function! 227 | - Inside of `tests.js`, write a `describe` block for our new `isFive` function. 228 | - As your first, failing test, write an `it` and an `expect` asserting that a function named `isFive` exists. 229 | - Run the tests by refreshing `report.html` to show the red, failing test. 230 | - Write just enough code inside of `code.js` to define an empty function for `isFive`. 231 | - Now, refresh `report.html` to ensure that all tests are green. 232 | - What other tests and implementation cycles should you do for `isFive`? 233 | - Ensure that isFive returns a boolean no matter what the input 234 | - Ensure that isFive returns true when passed 5 235 | - What about if we pass in the string `"5"`? Do you want isFive to return true for that? 236 | - If so, write the test, ensure that the test is failing, and then write the implementation 237 | - Commit your work to git and push to GitHub before moving forward. 238 | 239 | ## Exercise #11 TDD process for testing and creating an `isEven` function 240 | - Start with the smallest tests first. Assert that the function is defined. 241 | - Write just enough code to green the test 242 | - Build up functionality one small piece at a time. 243 | - Write each assertion, confirm the test fails, write only enough code to green that specific test, refactor, then repeat. 244 | - Remember to add and then "green" one test at a time. That's part of the fundamental approach of TDD. 245 | - Assert that isEven: 246 | - returns a boolean no matter the input 247 | - returns true when executed with `isEven(2)` 248 | - returns true when executed with `isEven(-4)` 249 | - returns false when executed with `isEven(3)` 250 | - returns false when called with `isEven("banana")` 251 | - returns true when called with `isEven("8")` 252 | - returns false when called with `isEven(Infinity)` 253 | - return false when called with a boolean input like `isEven(true)` or `isEven(false)` 254 | - returns false when called without an argument like `isEven()` 255 | - Refactor when and where you can. Be careful not to refactor before you have a handful of green tests. 256 | - Repeat until the tests are robust and the function works as intended. 257 | - Commit your work to git and push to GitHub before moving forward. 258 | 259 | ## Exercise #12 Test Drive an `isVowel` function 260 | - Start with the smallest tests first. 261 | - Write just enough code to green the test 262 | - Build up functionality one small piece at a time. 263 | - Commit your work to git at each step. 264 | - Write each assertion, confirm the test fails, write only enough code to green that specific test, refactor, then repeat. 265 | - Remember to add and then "green" one test at a time. That's part of the fundamental approach of TDD. 266 | - Assert that: 267 | - `isVowel` always returns a boolean 268 | - `isVowel("a")` returns true 269 | - `isVowel("A")` returns true 270 | - `isVowel("y")` returns false 271 | - `isVowel(4)` returns false 272 | - `isVowel(true)` or `isVowel(false)` both return false 273 | - `isVowel("banana")` returns false 274 | - `isVowel()` returns false 275 | - Refactor when appropriate and possible. 276 | - Repeat until the tests are robust and the function works as intended. 277 | - Commit your work to git and push to GitHub before moving forward. 278 | 279 | ## Exercise #13 Test Drive an `add` function 280 | - The `add` function should sum two numbers, as long as each input is a number or a string containing a number. 281 | - Write each assertion, confirm the test fails, write only enough code to green that specific test, refactor, then repeat (move onto the next test.) 282 | - Assert that `add`: 283 | - `add(2, 3)` returns 5 284 | - `add(-3, -9)` returns -12 285 | - `add("5", 6)` returns 11 286 | - `add("-4", "10")` returns 6 287 | - `add("banana", "split")` returns NaN 288 | - `add(2, "apples")` returns NaN 289 | - `add()` returns NaN 290 | - Start with the smallest tests first. 291 | - Write just enough code to green the test 292 | - Build up functionality one small piece at a time. 293 | - If any input is not a number, return NaN 294 | - Refactor, if possible 295 | - Repeat until the tests are robust and the function works as intented. 296 | - Commit your work to git and push to GitHub. 297 | 298 | ## Conclusion and completeness 299 | - With each successive assertion/expectation in a test for a specific function, we make that unit more robust and reliable, and usually easier to refactor. 300 | - Completeness of the unit: 301 | - If the implementation for an `add` function only passes one assertion that `add(2, 3)` returns `5`, but does not work with any other numbers, then the unit is not considered complete. The implementation is incomplete, and the unit test composed of multiple assertions/expectations should demonstrate this clearly. 302 | - Another example: if the `isVowel` function only works for lowercase letters but fails to account for uppercase letters, then we would consider the implementation to be incorrect. The "unit" of functionality is incomplete. 303 | - Another example: 304 | - It is not feasible to test an infinite number of inputs with our assertions/expectations in a unit test. I.e., you CANNOT write all possible unit tests for realistic functions. To prove that a function works in all cases is a practice closer to mathematical proofs. This is known as [Correctness](https://en.wikipedia.org/wiki/Correctness_(computer_science)) and is outside the scope of most software development due to economic and time constraints. So... how do you know when you have written ENOUGH test cases? That is a very good question and there is no simple answer. Generally though, test the boundaries of data types, including null, undefined, empty strings, valid and invalid values, very large and very small positive and negative numbers, etc. You should also test permutations of argument values. For example, if a function foo has 2 parameters: a number and a string, then two of the unit tests should be a positive value for the number and an empty string, and a negative value for the number and an empty string. 305 | - Moving forward, any time you find a bug in your implementation, here is the best practice: 306 | 1. Author test code that reproduces that bug in an automated way. This may involve adding one or more assertions/expectations to a unit test. 307 | 2. Refactor your implementation, relying on your newly added automated test to guide the solution. 308 | 3. Now that the steps to reproduce the bug are part of your test suite, you may move forward with more confidence. 309 | 310 | ## Jasmine Documentation 311 | - [Jasmine Global Functions](https://jasmine.github.io/api/3.3/global.html) 312 | - [Jasmine Matchers](https://jasmine.github.io/api/3.3/matchers.html) 313 | 314 | ## More resources 315 | - [Intro to TDD](https://www.youtube.com/watch?v=QCif_-r8eK4) 316 | - [Sandi Metz on testing and what to test](https://www.youtube.com/watch?v=URSWYvyc42M) 317 | - [Bob Martin on TDD and Code Quality](https://www.youtube.com/watch?v=is41fgDrqn0) 318 | - [Three Laws of TDD](http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd) 319 | -------------------------------------------------------------------------------- /code.js: -------------------------------------------------------------------------------- 1 | // helloWorld function 2 | function helloWorld() { 3 | return "Hello, World!"; 4 | } -------------------------------------------------------------------------------- /jasmine/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2017 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/SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jasmine Spec Runner v3.3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /jasmine/lib/jasmine-3.3.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 config = { 55 | failFast: queryString.getParam("failFast"), 56 | oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"), 57 | hideDisabled: queryString.getParam("hideDisabled") 58 | }; 59 | 60 | var random = queryString.getParam("random"); 61 | 62 | if (random !== undefined && random !== "") { 63 | config.random = random; 64 | } 65 | 66 | var seed = queryString.getParam("seed"); 67 | if (seed) { 68 | config.seed = seed; 69 | } 70 | 71 | /** 72 | * ## Reporters 73 | * 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). 74 | */ 75 | var htmlReporter = new jasmine.HtmlReporter({ 76 | env: env, 77 | navigateWithNewParam: function(key, value) { return queryString.navigateWithNewParam(key, value); }, 78 | addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, 79 | getContainer: function() { return document.body; }, 80 | createElement: function() { return document.createElement.apply(document, arguments); }, 81 | createTextNode: function() { return document.createTextNode.apply(document, arguments); }, 82 | timer: new jasmine.Timer(), 83 | filterSpecs: filterSpecs 84 | }); 85 | 86 | /** 87 | * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. 88 | */ 89 | env.addReporter(jasmineInterface.jsApiReporter); 90 | env.addReporter(htmlReporter); 91 | 92 | /** 93 | * Filter which specs will be run by matching the start of the full name against the `spec` query param. 94 | */ 95 | var specFilter = new jasmine.HtmlSpecFilter({ 96 | filterString: function() { return queryString.getParam("spec"); } 97 | }); 98 | 99 | config.specFilter = function(spec) { 100 | return specFilter.matches(spec.getFullName()); 101 | }; 102 | 103 | env.configure(config); 104 | 105 | /** 106 | * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. 107 | */ 108 | window.setTimeout = window.setTimeout; 109 | window.setInterval = window.setInterval; 110 | window.clearTimeout = window.clearTimeout; 111 | window.clearInterval = window.clearInterval; 112 | 113 | /** 114 | * ## Execution 115 | * 116 | * 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. 117 | */ 118 | var currentWindowOnload = window.onload; 119 | 120 | window.onload = function() { 121 | if (currentWindowOnload) { 122 | currentWindowOnload(); 123 | } 124 | htmlReporter.initialize(); 125 | env.execute(); 126 | }; 127 | 128 | /** 129 | * Helper function for readability above. 130 | */ 131 | function extend(destination, source) { 132 | for (var property in source) destination[property] = source[property]; 133 | return destination; 134 | } 135 | 136 | }()); 137 | -------------------------------------------------------------------------------- /jasmine/lib/jasmine-3.3.0/jasmine-html.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2018 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 ResultsStateBuilder() { 38 | this.topResults = new j$.ResultsNode({}, '', null); 39 | this.currentParent = this.topResults; 40 | this.specsExecuted = 0; 41 | this.failureCount = 0; 42 | this.pendingSpecCount = 0; 43 | } 44 | 45 | ResultsStateBuilder.prototype.suiteStarted = function(result) { 46 | this.currentParent.addChild(result, 'suite'); 47 | this.currentParent = this.currentParent.last(); 48 | }; 49 | 50 | ResultsStateBuilder.prototype.suiteDone = function(result) { 51 | this.currentParent.updateResult(result); 52 | if (this.currentParent !== this.topResults) { 53 | this.currentParent = this.currentParent.parent; 54 | } 55 | 56 | if (result.status === 'failed') { 57 | this.failureCount++; 58 | } 59 | }; 60 | 61 | ResultsStateBuilder.prototype.specStarted = function(result) { 62 | }; 63 | 64 | ResultsStateBuilder.prototype.specDone = function(result) { 65 | this.currentParent.addChild(result, 'spec'); 66 | 67 | if (result.status !== 'excluded') { 68 | this.specsExecuted++; 69 | } 70 | 71 | if (result.status === 'failed') { 72 | this.failureCount++; 73 | } 74 | 75 | if (result.status == 'pending') { 76 | this.pendingSpecCount++; 77 | } 78 | }; 79 | 80 | 81 | 82 | function HtmlReporter(options) { 83 | var config = function() { return (options.env && options.env.configuration()) || {}; }, 84 | getContainer = options.getContainer, 85 | createElement = options.createElement, 86 | createTextNode = options.createTextNode, 87 | navigateWithNewParam = options.navigateWithNewParam || function() {}, 88 | addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, 89 | filterSpecs = options.filterSpecs, 90 | timer = options.timer || noopTimer, 91 | htmlReporterMain, 92 | symbols, 93 | deprecationWarnings = []; 94 | 95 | this.initialize = function() { 96 | clearPrior(); 97 | htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'}, 98 | createDom('div', {className: 'jasmine-banner'}, 99 | createDom('a', {className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank'}), 100 | createDom('span', {className: 'jasmine-version'}, j$.version) 101 | ), 102 | createDom('ul', {className: 'jasmine-symbol-summary'}), 103 | createDom('div', {className: 'jasmine-alert'}), 104 | createDom('div', {className: 'jasmine-results'}, 105 | createDom('div', {className: 'jasmine-failures'}) 106 | ) 107 | ); 108 | getContainer().appendChild(htmlReporterMain); 109 | }; 110 | 111 | var totalSpecsDefined; 112 | this.jasmineStarted = function(options) { 113 | totalSpecsDefined = options.totalSpecsDefined || 0; 114 | timer.start(); 115 | }; 116 | 117 | var summary = createDom('div', {className: 'jasmine-summary'}); 118 | 119 | var stateBuilder = new ResultsStateBuilder(); 120 | 121 | this.suiteStarted = function(result) { 122 | stateBuilder.suiteStarted(result); 123 | }; 124 | 125 | this.suiteDone = function(result) { 126 | stateBuilder.suiteDone(result); 127 | 128 | if (result.status === 'failed') { 129 | failures.push(failureDom(result)); 130 | } 131 | addDeprecationWarnings(result); 132 | }; 133 | 134 | this.specStarted = function(result) { 135 | stateBuilder.specStarted(result); 136 | }; 137 | 138 | var failures = []; 139 | this.specDone = function(result) { 140 | stateBuilder.specDone(result); 141 | 142 | if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') { 143 | console.error('Spec \'' + result.fullName + '\' has no expectations.'); 144 | } 145 | 146 | if (!symbols){ 147 | symbols = find('.jasmine-symbol-summary'); 148 | } 149 | 150 | symbols.appendChild(createDom('li', { 151 | className: this.displaySpecInCorrectFormat(result), 152 | id: 'spec_' + result.id, 153 | title: result.fullName 154 | } 155 | )); 156 | 157 | if (result.status === 'failed') { 158 | failures.push(failureDom(result)); 159 | } 160 | 161 | addDeprecationWarnings(result); 162 | }; 163 | 164 | this.displaySpecInCorrectFormat = function(result) { 165 | return noExpectations(result) ? 'jasmine-empty' : this.resultStatus(result.status); 166 | }; 167 | 168 | this.resultStatus = function(status) { 169 | if(status === 'excluded') { 170 | return config().hideDisabled ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; 171 | } 172 | return 'jasmine-' + status; 173 | }; 174 | 175 | this.jasmineDone = function(doneResult) { 176 | var banner = find('.jasmine-banner'); 177 | var alert = find('.jasmine-alert'); 178 | var order = doneResult && doneResult.order; 179 | var i; 180 | alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); 181 | 182 | banner.appendChild(optionsMenu(config())); 183 | 184 | if (stateBuilder.specsExecuted < totalSpecsDefined) { 185 | var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; 186 | var skippedLink = addToExistingQueryString('spec', ''); 187 | alert.appendChild( 188 | createDom('span', {className: 'jasmine-bar jasmine-skipped'}, 189 | createDom('a', {href: skippedLink, title: 'Run all specs'}, skippedMessage) 190 | ) 191 | ); 192 | } 193 | var statusBarMessage = ''; 194 | var statusBarClassName = 'jasmine-overall-result jasmine-bar '; 195 | var globalFailures = (doneResult && doneResult.failedExpectations) || []; 196 | var failed = stateBuilder.failureCount + globalFailures.length > 0; 197 | 198 | if (totalSpecsDefined > 0 || failed) { 199 | statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount); 200 | if (stateBuilder.pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); } 201 | } 202 | 203 | if (doneResult.overallStatus === 'passed') { 204 | statusBarClassName += ' jasmine-passed '; 205 | } else if (doneResult.overallStatus === 'incomplete') { 206 | statusBarClassName += ' jasmine-incomplete '; 207 | statusBarMessage = 'Incomplete: ' + doneResult.incompleteReason + ', ' + statusBarMessage; 208 | } else { 209 | statusBarClassName += ' jasmine-failed '; 210 | } 211 | 212 | var seedBar; 213 | if (order && order.random) { 214 | seedBar = createDom('span', {className: 'jasmine-seed-bar'}, 215 | ', randomized with seed ', 216 | createDom('a', {title: 'randomized with seed ' + order.seed, href: seedHref(order.seed)}, order.seed) 217 | ); 218 | } 219 | 220 | alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage, seedBar)); 221 | 222 | var errorBarClassName = 'jasmine-bar jasmine-errored'; 223 | var afterAllMessagePrefix = 'AfterAll '; 224 | 225 | for(i = 0; i < globalFailures.length; i++) { 226 | alert.appendChild(createDom('span', {className: errorBarClassName}, globalFailureMessage(globalFailures[i]))); 227 | } 228 | 229 | function globalFailureMessage(failure) { 230 | if (failure.globalErrorType === 'load') { 231 | var prefix = 'Error during loading: ' + failure.message; 232 | 233 | if (failure.filename) { 234 | return prefix + ' in ' + failure.filename + ' line ' + failure.lineno; 235 | } else { 236 | return prefix; 237 | } 238 | } else { 239 | return afterAllMessagePrefix + failure.message; 240 | } 241 | } 242 | 243 | addDeprecationWarnings(doneResult); 244 | 245 | var warningBarClassName = 'jasmine-bar jasmine-warning'; 246 | for(i = 0; i < deprecationWarnings.length; i++) { 247 | var warning = deprecationWarnings[i]; 248 | alert.appendChild(createDom('span', {className: warningBarClassName}, 'DEPRECATION: ' + warning)); 249 | } 250 | 251 | var results = find('.jasmine-results'); 252 | results.appendChild(summary); 253 | 254 | summaryList(stateBuilder.topResults, summary); 255 | 256 | if (failures.length) { 257 | alert.appendChild( 258 | createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'}, 259 | createDom('span', {}, 'Spec List | '), 260 | createDom('a', {className: 'jasmine-failures-menu', href: '#'}, 'Failures'))); 261 | alert.appendChild( 262 | createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-failure-list'}, 263 | createDom('a', {className: 'jasmine-spec-list-menu', href: '#'}, 'Spec List'), 264 | createDom('span', {}, ' | Failures '))); 265 | 266 | find('.jasmine-failures-menu').onclick = function() { 267 | setMenuModeTo('jasmine-failure-list'); 268 | }; 269 | find('.jasmine-spec-list-menu').onclick = function() { 270 | setMenuModeTo('jasmine-spec-list'); 271 | }; 272 | 273 | setMenuModeTo('jasmine-failure-list'); 274 | 275 | var failureNode = find('.jasmine-failures'); 276 | for (i = 0; i < failures.length; i++) { 277 | failureNode.appendChild(failures[i]); 278 | } 279 | } 280 | }; 281 | 282 | return this; 283 | 284 | function failureDom(result) { 285 | var failure = 286 | createDom('div', {className: 'jasmine-spec-detail jasmine-failed'}, 287 | failureDescription(result, stateBuilder.currentParent), 288 | createDom('div', {className: 'jasmine-messages'}) 289 | ); 290 | var messages = failure.childNodes[1]; 291 | 292 | for (var i = 0; i < result.failedExpectations.length; i++) { 293 | var expectation = result.failedExpectations[i]; 294 | messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message)); 295 | messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack)); 296 | } 297 | 298 | return failure; 299 | } 300 | 301 | function summaryList(resultsTree, domParent) { 302 | var specListNode; 303 | for (var i = 0; i < resultsTree.children.length; i++) { 304 | var resultNode = resultsTree.children[i]; 305 | if (filterSpecs && !hasActiveSpec(resultNode)) { 306 | continue; 307 | } 308 | if (resultNode.type === 'suite') { 309 | var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id}, 310 | createDom('li', {className: 'jasmine-suite-detail jasmine-' + resultNode.result.status}, 311 | createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description) 312 | ) 313 | ); 314 | 315 | summaryList(resultNode, suiteListNode); 316 | domParent.appendChild(suiteListNode); 317 | } 318 | if (resultNode.type === 'spec') { 319 | if (domParent.getAttribute('class') !== 'jasmine-specs') { 320 | specListNode = createDom('ul', {className: 'jasmine-specs'}); 321 | domParent.appendChild(specListNode); 322 | } 323 | var specDescription = resultNode.result.description; 324 | if(noExpectations(resultNode.result)) { 325 | specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; 326 | } 327 | if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { 328 | specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; 329 | } 330 | specListNode.appendChild( 331 | createDom('li', { 332 | className: 'jasmine-' + resultNode.result.status, 333 | id: 'spec-' + resultNode.result.id 334 | }, 335 | createDom('a', {href: specHref(resultNode.result)}, specDescription) 336 | ) 337 | ); 338 | } 339 | } 340 | } 341 | 342 | function optionsMenu(config) { 343 | var optionsMenuDom = createDom('div', { className: 'jasmine-run-options' }, 344 | createDom('span', { className: 'jasmine-trigger' }, 'Options'), 345 | createDom('div', { className: 'jasmine-payload' }, 346 | createDom('div', { className: 'jasmine-stop-on-failure' }, 347 | createDom('input', { 348 | className: 'jasmine-fail-fast', 349 | id: 'jasmine-fail-fast', 350 | type: 'checkbox' 351 | }), 352 | createDom('label', { className: 'jasmine-label', 'for': 'jasmine-fail-fast' }, 'stop execution on spec failure')), 353 | createDom('div', { className: 'jasmine-throw-failures' }, 354 | createDom('input', { 355 | className: 'jasmine-throw', 356 | id: 'jasmine-throw-failures', 357 | type: 'checkbox' 358 | }), 359 | createDom('label', { className: 'jasmine-label', 'for': 'jasmine-throw-failures' }, 'stop spec on expectation failure')), 360 | createDom('div', { className: 'jasmine-random-order' }, 361 | createDom('input', { 362 | className: 'jasmine-random', 363 | id: 'jasmine-random-order', 364 | type: 'checkbox' 365 | }), 366 | createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order')), 367 | createDom('div', { className: 'jasmine-hide-disabled' }, 368 | createDom('input', { 369 | className: 'jasmine-disabled', 370 | id: 'jasmine-hide-disabled', 371 | type: 'checkbox' 372 | }), 373 | createDom('label', { className: 'jasmine-label', 'for': 'jasmine-hide-disabled' }, 'hide disabled tests')) 374 | ) 375 | ); 376 | 377 | var failFastCheckbox = optionsMenuDom.querySelector('#jasmine-fail-fast'); 378 | failFastCheckbox.checked = config.failFast; 379 | failFastCheckbox.onclick = function() { 380 | navigateWithNewParam('failFast', !config.failFast); 381 | }; 382 | 383 | var throwCheckbox = optionsMenuDom.querySelector('#jasmine-throw-failures'); 384 | throwCheckbox.checked = config.oneFailurePerSpec; 385 | throwCheckbox.onclick = function() { 386 | navigateWithNewParam('throwFailures', !config.oneFailurePerSpec); 387 | }; 388 | 389 | var randomCheckbox = optionsMenuDom.querySelector('#jasmine-random-order'); 390 | randomCheckbox.checked = config.random; 391 | randomCheckbox.onclick = function() { 392 | navigateWithNewParam('random', !config.random); 393 | }; 394 | 395 | var hideDisabled = optionsMenuDom.querySelector('#jasmine-hide-disabled'); 396 | hideDisabled.checked = config.hideDisabled; 397 | hideDisabled.onclick = function() { 398 | navigateWithNewParam('hideDisabled', !config.hideDisabled); 399 | }; 400 | 401 | var optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), 402 | optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'), 403 | isOpen = /\bjasmine-open\b/; 404 | 405 | optionsTrigger.onclick = function() { 406 | if (isOpen.test(optionsPayload.className)) { 407 | optionsPayload.className = optionsPayload.className.replace(isOpen, ''); 408 | } else { 409 | optionsPayload.className += ' jasmine-open'; 410 | } 411 | }; 412 | 413 | return optionsMenuDom; 414 | } 415 | 416 | function failureDescription(result, suite) { 417 | var wrapper = createDom('div', {className: 'jasmine-description'}, 418 | createDom('a', {title: result.description, href: specHref(result)}, result.description) 419 | ); 420 | var suiteLink; 421 | 422 | while (suite && suite.parent) { 423 | wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild); 424 | suiteLink = createDom('a', {href: suiteHref(suite)}, suite.result.description); 425 | wrapper.insertBefore(suiteLink, wrapper.firstChild); 426 | 427 | suite = suite.parent; 428 | } 429 | 430 | return wrapper; 431 | } 432 | 433 | function suiteHref(suite) { 434 | var els = []; 435 | 436 | while (suite && suite.parent) { 437 | els.unshift(suite.result.description); 438 | suite = suite.parent; 439 | } 440 | 441 | return addToExistingQueryString('spec', els.join(' ')); 442 | } 443 | 444 | function addDeprecationWarnings(result) { 445 | if (result && result.deprecationWarnings) { 446 | for(var i = 0; i < result.deprecationWarnings.length; i++) { 447 | var warning = result.deprecationWarnings[i].message; 448 | if (!j$.util.arrayContains(warning)) { 449 | deprecationWarnings.push(warning); 450 | } 451 | } 452 | } 453 | } 454 | 455 | function find(selector) { 456 | return getContainer().querySelector('.jasmine_html-reporter ' + selector); 457 | } 458 | 459 | function clearPrior() { 460 | // return the reporter 461 | var oldReporter = find(''); 462 | 463 | if(oldReporter) { 464 | getContainer().removeChild(oldReporter); 465 | } 466 | } 467 | 468 | function createDom(type, attrs, childrenVarArgs) { 469 | var el = createElement(type); 470 | 471 | for (var i = 2; i < arguments.length; i++) { 472 | var child = arguments[i]; 473 | 474 | if (typeof child === 'string') { 475 | el.appendChild(createTextNode(child)); 476 | } else { 477 | if (child) { 478 | el.appendChild(child); 479 | } 480 | } 481 | } 482 | 483 | for (var attr in attrs) { 484 | if (attr == 'className') { 485 | el[attr] = attrs[attr]; 486 | } else { 487 | el.setAttribute(attr, attrs[attr]); 488 | } 489 | } 490 | 491 | return el; 492 | } 493 | 494 | function pluralize(singular, count) { 495 | var word = (count == 1 ? singular : singular + 's'); 496 | 497 | return '' + count + ' ' + word; 498 | } 499 | 500 | function specHref(result) { 501 | return addToExistingQueryString('spec', result.fullName); 502 | } 503 | 504 | function seedHref(seed) { 505 | return addToExistingQueryString('seed', seed); 506 | } 507 | 508 | function defaultQueryString(key, value) { 509 | return '?' + key + '=' + value; 510 | } 511 | 512 | function setMenuModeTo(mode) { 513 | htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); 514 | } 515 | 516 | function noExpectations(result) { 517 | return (result.failedExpectations.length + result.passedExpectations.length) === 0 && 518 | result.status === 'passed'; 519 | } 520 | 521 | function hasActiveSpec(resultNode) { 522 | if (resultNode.type == 'spec' && resultNode.result.status != 'excluded') { 523 | return true; 524 | } 525 | 526 | if (resultNode.type == 'suite') { 527 | for (var i = 0, j = resultNode.children.length; i < j; i++) { 528 | if (hasActiveSpec(resultNode.children[i])) { 529 | return true; 530 | } 531 | } 532 | } 533 | } 534 | } 535 | 536 | return HtmlReporter; 537 | }; 538 | 539 | jasmineRequire.HtmlSpecFilter = function() { 540 | function HtmlSpecFilter(options) { 541 | var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); 542 | var filterPattern = new RegExp(filterString); 543 | 544 | this.matches = function(specName) { 545 | return filterPattern.test(specName); 546 | }; 547 | } 548 | 549 | return HtmlSpecFilter; 550 | }; 551 | 552 | jasmineRequire.ResultsNode = function() { 553 | function ResultsNode(result, type, parent) { 554 | this.result = result; 555 | this.type = type; 556 | this.parent = parent; 557 | 558 | this.children = []; 559 | 560 | this.addChild = function(result, type) { 561 | this.children.push(new ResultsNode(result, type, this)); 562 | }; 563 | 564 | this.last = function() { 565 | return this.children[this.children.length - 1]; 566 | }; 567 | 568 | this.updateResult = function(result) { 569 | this.result = result; 570 | }; 571 | } 572 | 573 | return ResultsNode; 574 | }; 575 | 576 | jasmineRequire.QueryString = function() { 577 | function QueryString(options) { 578 | 579 | this.navigateWithNewParam = function(key, value) { 580 | options.getWindowLocation().search = this.fullStringWithNewParam(key, value); 581 | }; 582 | 583 | this.fullStringWithNewParam = function(key, value) { 584 | var paramMap = queryStringToParamMap(); 585 | paramMap[key] = value; 586 | return toQueryString(paramMap); 587 | }; 588 | 589 | this.getParam = function(key) { 590 | return queryStringToParamMap()[key]; 591 | }; 592 | 593 | return this; 594 | 595 | function toQueryString(paramMap) { 596 | var qStrPairs = []; 597 | for (var prop in paramMap) { 598 | qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])); 599 | } 600 | return '?' + qStrPairs.join('&'); 601 | } 602 | 603 | function queryStringToParamMap() { 604 | var paramStr = options.getWindowLocation().search.substring(1), 605 | params = [], 606 | paramMap = {}; 607 | 608 | if (paramStr.length > 0) { 609 | params = paramStr.split('&'); 610 | for (var i = 0; i < params.length; i++) { 611 | var p = params[i].split('='); 612 | var value = decodeURIComponent(p[1]); 613 | if (value === 'true' || value === 'false') { 614 | value = JSON.parse(value); 615 | } 616 | paramMap[decodeURIComponent(p[0])] = value; 617 | } 618 | } 619 | 620 | return paramMap; 621 | } 622 | 623 | } 624 | 625 | return QueryString; 626 | }; 627 | -------------------------------------------------------------------------------- /jasmine/lib/jasmine-3.3.0/jasmine.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | body { overflow-y: scroll; } 3 | 4 | .jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; } 5 | .jasmine_html-reporter a { text-decoration: none; } 6 | .jasmine_html-reporter a:hover { text-decoration: underline; } 7 | .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; } 8 | .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; } 9 | .jasmine_html-reporter .jasmine-banner { position: relative; } 10 | .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; } 11 | .jasmine_html-reporter .jasmine-banner .jasmine-version { margin-left: 14px; position: relative; top: 6px; } 12 | .jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } 13 | .jasmine_html-reporter .jasmine-version { color: #aaa; } 14 | .jasmine_html-reporter .jasmine-banner { margin-top: 14px; } 15 | .jasmine_html-reporter .jasmine-duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; } 16 | .jasmine_html-reporter .jasmine-symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } 17 | .jasmine_html-reporter .jasmine-symbol-summary li { display: inline-block; height: 10px; width: 14px; font-size: 16px; } 18 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { font-size: 14px; } 19 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { color: #007069; content: "•"; } 20 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { line-height: 9px; } 21 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { color: #ca3a11; content: "×"; font-weight: bold; margin-left: -1px; } 22 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded { font-size: 14px; } 23 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded:before { color: #bababa; content: "•"; } 24 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded-no-display { font-size: 14px; display: none; } 25 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { line-height: 17px; } 26 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { color: #ba9d37; content: "*"; } 27 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { font-size: 14px; } 28 | .jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { color: #ba9d37; content: "•"; } 29 | .jasmine_html-reporter .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; } 30 | .jasmine_html-reporter .jasmine-run-options .jasmine-trigger { cursor: pointer; padding: 8px 16px; } 31 | .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; } 32 | .jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { display: block; } 33 | .jasmine_html-reporter .jasmine-bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 34 | .jasmine_html-reporter .jasmine-bar.jasmine-failed, .jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; border-bottom: 1px solid #eee; } 35 | .jasmine_html-reporter .jasmine-bar.jasmine-passed { background-color: #007069; } 36 | .jasmine_html-reporter .jasmine-bar.jasmine-incomplete { background-color: #bababa; } 37 | .jasmine_html-reporter .jasmine-bar.jasmine-skipped { background-color: #bababa; } 38 | .jasmine_html-reporter .jasmine-bar.jasmine-warning { background-color: #ba9d37; color: #333; } 39 | .jasmine_html-reporter .jasmine-bar.jasmine-menu { background-color: #fff; color: #aaa; } 40 | .jasmine_html-reporter .jasmine-bar.jasmine-menu a { color: #333; } 41 | .jasmine_html-reporter .jasmine-bar a { color: white; } 42 | .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; } 43 | .jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine_html-reporter.jasmine-failure-list .jasmine-summary { display: none; } 44 | .jasmine_html-reporter .jasmine-results { margin-top: 14px; } 45 | .jasmine_html-reporter .jasmine-summary { margin-top: 14px; } 46 | .jasmine_html-reporter .jasmine-summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } 47 | .jasmine_html-reporter .jasmine-summary ul.jasmine-suite { margin-top: 7px; margin-bottom: 7px; } 48 | .jasmine_html-reporter .jasmine-summary li.jasmine-passed a { color: #007069; } 49 | .jasmine_html-reporter .jasmine-summary li.jasmine-failed a { color: #ca3a11; } 50 | .jasmine_html-reporter .jasmine-summary li.jasmine-empty a { color: #ba9d37; } 51 | .jasmine_html-reporter .jasmine-summary li.jasmine-pending a { color: #ba9d37; } 52 | .jasmine_html-reporter .jasmine-summary li.jasmine-excluded a { color: #bababa; } 53 | .jasmine_html-reporter .jasmine-specs li.jasmine-passed a:before { content: "• "; } 54 | .jasmine_html-reporter .jasmine-specs li.jasmine-failed a:before { content: "× "; } 55 | .jasmine_html-reporter .jasmine-specs li.jasmine-empty a:before { content: "* "; } 56 | .jasmine_html-reporter .jasmine-specs li.jasmine-pending a:before { content: "• "; } 57 | .jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before { content: "• "; } 58 | .jasmine_html-reporter .jasmine-description + .jasmine-suite { margin-top: 0; } 59 | .jasmine_html-reporter .jasmine-suite { margin-top: 14px; } 60 | .jasmine_html-reporter .jasmine-suite a { color: #333; } 61 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { margin-bottom: 28px; } 62 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { background-color: #ca3a11; color: white; } 63 | .jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { color: white; } 64 | .jasmine_html-reporter .jasmine-result-message { padding-top: 14px; color: #333; white-space: pre-wrap; } 65 | .jasmine_html-reporter .jasmine-result-message span.jasmine-result { display: block; } 66 | .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; } 67 | -------------------------------------------------------------------------------- /jasmine/lib/jasmine-3.3.0/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gocodeup/intro-to-testing-js/7eec62175fc30eede0387edb0444970108c8b31b/jasmine/lib/jasmine-3.3.0/jasmine_favicon.png -------------------------------------------------------------------------------- /jasmine/spec/PlayerSpec.js: -------------------------------------------------------------------------------- 1 | describe("Player", function() { 2 | var player; 3 | var song; 4 | 5 | beforeEach(function() { 6 | player = new Player(); 7 | song = new Song(); 8 | }); 9 | 10 | it("should be able to play a Song", function() { 11 | player.play(song); 12 | expect(player.currentlyPlayingSong).toEqual(song); 13 | 14 | //demonstrates use of custom matcher 15 | expect(player).toBePlaying(song); 16 | }); 17 | 18 | describe("when song has been paused", function() { 19 | beforeEach(function() { 20 | player.play(song); 21 | player.pause(); 22 | }); 23 | 24 | it("should indicate that the song is currently paused", function() { 25 | expect(player.isPlaying).toBeFalsy(); 26 | 27 | // demonstrates use of 'not' with a custom matcher 28 | expect(player).not.toBePlaying(song); 29 | }); 30 | 31 | it("should be possible to resume", function() { 32 | player.resume(); 33 | expect(player.isPlaying).toBeTruthy(); 34 | expect(player.currentlyPlayingSong).toEqual(song); 35 | }); 36 | }); 37 | 38 | // demonstrates use of spies to intercept and test method calls 39 | it("tells the current song if the user has made it a favorite", function() { 40 | spyOn(song, 'persistFavoriteStatus'); 41 | 42 | player.play(song); 43 | player.makeFavorite(); 44 | 45 | expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); 46 | }); 47 | 48 | //demonstrates use of expected exceptions 49 | describe("#resume", function() { 50 | it("should throw an exception if song is already playing", function() { 51 | player.play(song); 52 | 53 | expect(function() { 54 | player.resume(); 55 | }).toThrowError("song is already playing"); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /jasmine/spec/SpecHelper.js: -------------------------------------------------------------------------------- 1 | beforeEach(function () { 2 | jasmine.addMatchers({ 3 | toBePlaying: function () { 4 | return { 5 | compare: function (actual, expected) { 6 | var player = actual; 7 | 8 | return { 9 | pass: player.currentlyPlayingSong === expected && player.isPlaying 10 | }; 11 | } 12 | }; 13 | } 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /jasmine/src/Player.js: -------------------------------------------------------------------------------- 1 | function Player() { 2 | } 3 | Player.prototype.play = function(song) { 4 | this.currentlyPlayingSong = song; 5 | this.isPlaying = true; 6 | }; 7 | 8 | Player.prototype.pause = function() { 9 | this.isPlaying = false; 10 | }; 11 | 12 | Player.prototype.resume = function() { 13 | if (this.isPlaying) { 14 | throw new Error("song is already playing"); 15 | } 16 | 17 | this.isPlaying = true; 18 | }; 19 | 20 | Player.prototype.makeFavorite = function() { 21 | this.currentlyPlayingSong.persistFavoriteStatus(true); 22 | }; -------------------------------------------------------------------------------- /jasmine/src/Song.js: -------------------------------------------------------------------------------- 1 | function Song() { 2 | } 3 | 4 | Song.prototype.persistFavoriteStatus = function(value) { 5 | // something complicated 6 | throw new Error("not yet implemented"); 7 | }; -------------------------------------------------------------------------------- /report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jasmine Spec Runner v3.3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests.js: -------------------------------------------------------------------------------- 1 | // Unit tests for the helloWorld function 2 | describe('helloWorld', function() { 3 | it('should be a defined function', function() { 4 | expect(typeof helloWorld).toBe('function'); 5 | }); 6 | it('should return a string when called', function() { 7 | expect(typeof helloWorld()).toBe("string"); 8 | }); 9 | it('should return the string "Hello, World!" when executed', function() { 10 | expect(helloWorld()).toBe("Hello, World!"); 11 | }); 12 | it("should never return 'undefined' when called", function() { 13 | expect(helloWorld()).not.toBe(undefined); 14 | }); 15 | }); --------------------------------------------------------------------------------