Kim Borup
2019-03-14 c44749539a519773a8f1a08842db797e4c6576ba
commit | author | age
92e460 1 # Revenge Of The Automated Testing
43f2f2 2
d2fa3e 3 > The purpose of this exercise is to develop and validate a new feature using Test Driven Development (TDD); and to promote the assured feature through the pipeline.
f6d2bd 4
d2fa3e 5 <!-- ![comic-header](../images/exercise3/comic-header.png)
62a054 6
d2fa3e 7 [Image Source](https://cdn-images-1.medium.com/max/1600/1*wF_fSCH-gLYfMbkwb3gR2w.png) -->
62a054 8
b76e74 9 ## Introduction to TDD.
D 10
d2fa3e 11 <!-- > _Here is a brief introduction of TDD and why we use it._ -->
b76e74 12
D 13 **Test Driven Development (TDD)** is a software development process that relies on the repetition of a very short development cycle. Requirements are turned into test cases, where the software is developed to pass the tests. In other words, it creates a safety net that serves to keep the developer's problems/bugs at bay while enabling the developer to refactor efficiently. This is opposed to software development that allows software to be added that is not proven to meet requirements.
14
d2fa3e 15 The TDD cycle can be illustrated with the following diagram:
RS 16
c92d13 17 ![TDD-diagram](../images/exercise3/TDD_Lifecycle.png)
b76e74 18
1173e5 19 ### The TDD Cycle
b76e74 20
D 21 1. `Write a test` -
e43fd2 22 In TDD a new feature begins by writing a test. Write a test that clearly defines a function or one that provides an improvement to an existing function. It's important the developer clearly understands the feature's specification and requirements, or the feature could be wrong from the get-go.
b76e74 23
D 24 2. `Test Fails` -
1173e5 25 When a test is first implemented it is expected to fail. This failure validates the test is working correctly as the feature is yet to be implemented.
b76e74 26
D 27 3. `Write code to make test pass` -
28 This step involves implementing the feature to pass the failed test. Code written at this stage may be inelegant and still pass the test, however this is acceptable as TDD is a recursive cycle which includes code refactoring.
29
30 4. `Code Passes tests` -
31 If all tests pass, the developer can be confident that the new code meets the test requirements.
32
33 5. `Refactor` -
82cb19 34 The refactoring step will allow the developer to clean up their code without changing its behaviour. Not changing the behaviour should ensure the tests still pass. The process of refactoring can include; removal of duplication, renaming of object, class, module, variable and method names to clearly represent their current purpose and use, decoupling of functionality and increasing code cohesion.
b76e74 35
D 36 6. `Repeat` -
37 Starting with another new test, the cycle is then repeated to push forward the functionality. The size of the steps should always be small, with as few as 1 to 10 edits between each test run. If new code does not rapidly satisfy a new test, or other tests fail unexpectedly, the programmer should undo or revert in preference to excessive debugging.
38
39 ### Testing Bananalogy
1173e5 40 Explanation of Mocha and JS test syntax through Bananalogy! Imagine for a moment; we're not building software but creating a bowl of fruit. To create a `Bunch of Bananas` component for our fruit bowl we could start with our tests as shown below.
d2fa3e 41
b76e74 42 ![bdd-bananas](../images/exercise3/bdd-bananas.png)
d2fa3e 43
b76e74 44   * `describe` is used to group tests together. The string `"a bunch of ripe bananas"` is for human reading and allows you to identify tests.
D 45   * `it` is a statement that contains a test. It should contain an assertion such as `expect` or `should`. It follows the syntax of `describe` where the string passed in identifies the statement.
f6d2bd 46
D 47 ---
43f2f2 48
D 49 ## Learning Outcomes
f6d2bd 50
43f2f2 51 As a learner you will be able to
f6d2bd 52
5285f8 53 * Understand the why behind TDD
95ba2a 54 * Implement a feature using TDD for front end and backend
5285f8 55 * Write end to end tests for the feature and run them in CI
43f2f2 56
D 57 ## Tools and Frameworks
f6d2bd 58
d2fa3e 59 <!-- > Name of tool - short description and link to docs or website -->
43f2f2 60
5285f8 61 1.  [Jest](https://facebook.github.io/jest/) - Zero configuration testing platform
D 62 Jest is used by Facebook to test all JavaScript code including React applications. One of Jest's philosophies is to provide an integrated "zero-configuration" experience. We observed that when engineers are provided with ready-to-use tools, they end up writing more tests, which in turn results in more stable and healthy code bases.
63 1.  [Vue Test Utils](https://vue-test-utils.vuejs.org/en/) - Vue Test Utils is the official unit testing utility library for Vue.js.
64 1.  [Nightwatch.js](http://nightwatchjs.org/) - Nightwatch.js is an easy to use Node.js based End-to-End (E2E) testing solution for browser based apps and websites. It uses the powerful W3C WebDriver API to perform commands and assertions on DOM elements.
65 1.  [Mocha](https://mochajs.org/) - Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases. Hosted on GitHub.
1173e5 66 1.  [Sinon](http://sinonjs.org/) - Standalone test spies, stubs and mocks for JavaScript.
5285f8 67 Works with any unit testing framework.
43f2f2 68
D 69 ## Big Picture
664135 70 > From the previous exercise; we created a simple pipeline. We will now flesh it out with some testing to add gates to our pathway to production.
f6d2bd 71
664135 72 ![big-picture](../images/big-picture/big-picture-3.jpg)
43f2f2 73
f6d2bd 74 ---
43f2f2 75
D 76 ## 10,000 Ft View
77
8894bf 78 > The goal of this exercise is to add a new component to the application using TDD to create and validate its behaviour. The User story we have been given is as follows:
43f2f2 79
1173e5 80 *As a doer I want to mark todos as important so that I can keep track of and complete high priority todos first*
f6d2bd 81
5285f8 82 _Acceptance Criteria_
D 83 - [ ] should be doable with a single click
84 - [ ] should add a red flag against the todo when marked important
85 - [ ] should remove the red colour flag on the flag when important removed
86 - [ ] should not affect existing todos
87
88 _On page load:_
89 - [ ] should display existing todos that are not marked important
90 - [ ] should display existing todos that are marked important with an red flag
43f2f2 91
D 92 ## Step by Step Instructions
d2fa3e 93 <!-- > This is a fairly structured guide with references to exact filenames and sections of text to be added. -->
43f2f2 94
f6d2bd 95 ### Part 1 - Tests in our Pipeline
95ba2a 96 > _In this part we will get familiar with the layout of our tests. We will also improve the pipeline created already by adding some unit tests for the front end & backend along with some end to end tests (e2e) to validate the full solution_
43f2f2 97
d2fa3e 98 #### 1a - Todo List Front End Unit tests
95ba2a 99 > In this exercise we will execute our test for the front end locally. Once verified we will add them to Jenkins.
bc2216 100
664135 101 1. Before linking our automated testing to the pipeline we'll first ensure the tests run locally. Change to the `todolist-fe` directory and run `test`.
91cc51 102 ```bash
fad576 103 cd todolist-fe
D 104 ```
105 ```bash
106 npm run test
91cc51 107 ```
1173e5 108 <p class="tip" >
60bca4 109 `test` is an alias used that runs `vue-cli-service test` from the scripts object in `package.json`
91cc51 110 </p>
d2fa3e 111
b76e74 112 ![screenshot-scripts](../images/exercise3/screenshot-scripts.png)
D 113
95ba2a 114 2. This command will run all `*spec.js` files. Our test files are stored in the following places. There are 12 front end test files stored in these directories: `todolist-fe/tests/unit/vue-components/*` & `todolist-fe/tests/unit/javascript/*`
bc2216 115
d2fa3e 116 3. You should see an output similar to the following. The above command has run a test suite for every `*.spec.js` file. The table generated in the terminal shows the code coverage. We're going to be focusing on the unit tests for now.
RS 117
b76e74 118 ![test-run-locally](../images/exercise3/test-run-locally.png)
91cc51 119
d2fa3e 120 4. Repeat the same process for `todolist-api` and verify that all the tests run. If you have an ExpressJS server already running from previous exercise; you should kill it before running the tests. The `mocha` test suite will launch a dev server for running the tests. There are 2 API test files: `todolist-api/server/api/todo/todo.spec.js` & `todolist-api/server/mocks/mock-routes.spec.js` for our API and the Mocks server. Remember to start the `mongo` container before running the tests
RS 121 ```bash
015fee 122 cd todolist-api
d2fa3e 123 ```
91cc51 124 ```bash
015fee 125 npm run mongo:start
fad576 126 ```
D 127 ```bash
128 npm run test
91cc51 129 ```
6e5072 130
d2fa3e 131 > NOTE: On Windows systems, tests will fail because Mocha is unable to find the `*.spec.js` files. Edit the `package.json` file, and remove the single quotes around `server/**/*.spec.js` in the `test` line as follows:
RS 132
133 ```bash
134 "test": "node_modules/.bin/nyc node_modules/.bin/mocha server/**/*.spec.js --exit"
135 ```
136
137 5. Navigate to your instance of Jenkins at `https://jenkins-<YOUR_NAME>-ci-cd.<APPS_URL>`.
91cc51 138 Click on `dev-todolist-fe-build` and then click the `configure` button on the left-hand side.
d2fa3e 139
b76e74 140 ![jenkins-configure-job](../images/exercise3/jenkins-configure-job.png)
91cc51 141
d2fa3e 142 6. Scroll to the `Build` part of the configuration page and add `npm run test` below `npm install`.
RS 143
b76e74 144 ![jenkins-build-step](../images/exercise3/jenkins-build-step.png)
91cc51 145
d2fa3e 146 7. Scroll to the `Post-build Actions` section and click `Add post-build action`. Select `Publish xUnit test result report` (Jenkins might place this at the top of the `Post-build Actions` list).
RS 147
b76e74 148 ![xunit-action](../images/exercise3/xunit-action.png)
6e5072 149
d2fa3e 150 8. Click the `Add` button under `Publish xUnit test result report` and select `JUnit`. In the pattern field enter `test-report.xml`. In the `Failed Tests Thresholds`  input box enter 0 under `Red Ball Total`. It should look a little something like this:
RS 151
b76e74 152 ![post-build-actions](../images/exercise3/post-build-actions.png)
6e5072 153
d2fa3e 154 9. Click `Save` at the bottom to save the changes. Run the `dev-todolist-fe-build` job and verify that this passes and the `build` and `bake` jobs are both triggered.
6e5072 155
d2fa3e 156 <!-- #### (Optional) Failing the tests
da5391 157 > _If you're not very confident in your technical ability and want don't want to do the TDD lab; feel free to just do this next section. If you are confident jump on to the e2e testing section below_
0d4d53 158
8eeb96 159 2. We're now going to deliberately fail a test to ensure that `bake` and `deploy` jobs aren't triggered if any tests fail. Open the `todolist-fe` source code in your favourite editor. Open `ListOfTodos.spec.js` in `/tests/unit/vue-components` and head to `line 39`. Add `not.` before `toHaveBeenCalled()` to fail the test.
b76e74 160 ![change-test-to-fail](../images/exercise3/change-test-to-fail.png)
6e5072 161
f56999 162 2. Push this to Gitlab and run the build job.
6e5072 163 ```bash
fad576 164 git add .
D 165 ```
166 ```bash
167 git commit -m "TEST - failing build with tests"
168 ```
169 ```bash
170 git push
6e5072 171 ```
A 172
1173e5 173 2. Rerun the `dev-todolist-fe-build` job. It should have failed and not run any other jobs.
b76e74 174 ![jenkins-with-failing-build](../images/exercise3/jenkins-with-failing-build.png)
6e5072 175
a83842 176 2. You can examine the test results on the jobs home page. Drill down into the tests to see which failed and other useful stats
D 177 ![fe-test-fails](../images/exercise3/fe-test-fails.png)
178
d2fa3e 179 2. Undo the changes you made to the `ListOfTodos.spec.js` file, commit your code and rerun the build. -->
530bd4 180
d2fa3e 181 #### 1b - End to End Tests (e2e)
3c8f9c 182 > _Unit tests are a great way to get immediate feedback as part of testing an application. End to end tests that drive user behaviour are another amazing way to ensure an application is behaving as expected._
bc2216 183
d2fa3e 184 In this part of the exercise, we will add a new stage to our pipeline called `dev-todolist-fe-e2e` that will run after the deploy has been completed. End to end tests will use `Nightwatch.js` to orchestrate a selenium webdriver instance that controls the web browser; in this case Google Chrome!
8529b9 185
d2fa3e 186 1. Let's start by checking that our tests execute locally. On the terminal move to the `todolist-fe` folder. Our end to end tests are stored in `tests/e2e/specs/`. The vuejs cli uses Nightwatch.js and comes pre-configured to run tests against Google Chrome. We have created some additional configuration in the root of the project `nightwatch.config.js` to run headless in CI mode on Jenkins.
8529b9 187 ```bash
fad576 188 cd todolist-fe
8529b9 189 ```
D 190
191 2. Run the tests locally by executing the following. This should start the dev server and run the test. You may see the browser pop up and close while tests execute.
192 ```bash
fad576 193 npm run e2e
8529b9 194 ```
d2fa3e 195
e0761f 196 ![local-e2e](../images/exercise3/local-e2e.png)
8529b9 197
d2fa3e 198 > NOTE: On Windows systems, you will see the firewall pop-up and ask permission to allow access. Click allow access to continue.
8529b9 199
d2fa3e 200
RS 201 3. With tests executing locally; let's add them to our Jenkins pipeline. To do this; we'll create a new job and connect it up to our `todolist-fe` jobs. Open Jenkins and create a `New Item` called `dev-todolist-fe-e2e`. Make this Job `Freestyle`.
202
203 4. On the configuration page (under the general tab); Set the Label for the job to run on as `jenkins-slave-npm`. Check the box marking the build parameterised and add a String parameter of `BUILD_TAG` as done before
204
e0761f 205 ![e2e-general](../images/exercise3/e2e-general.png)
bc2216 206
d2fa3e 207 5. On the Source Code Management tab; set the source code to git and add the url to your `todolist-fe` app. Set the branch to `refs/tags/${BUILD_TAG}`
RS 208
e0761f 209 ![e2e-git](../images/exercise3/e2e-git.png)
D 210
d2fa3e 211 6. Set `Color ANSI Console Output` on the `Build Environment` section
e0761f 212
d2fa3e 213 7. On the Build section; add a build step to execute shell and fill in the following substituting `<YOUR_NAME>` accordingly:
e0761f 214 ```bash
530a25 215 export E2E_TEST_ROUTE=todolist-fe-<YOUR_NAME>-dev.<APPS_URL>
4acca2 216 npm install
D 217 npm run e2e:ci
e0761f 218 ```
D 219 ![e2e-steps](../images/exercise3/e2e-steps.png)
220
d2fa3e 221 8. Add a Post Build action to `Publish Junit test result report`. Add `reports/e2e/specs/*.xml` to the report location and save the configuration to be brought back to the Job's homepage.
RS 222
e0761f 223 ![e2e-post-build](../images/exercise3/e2e-post-build.png)
D 224
d2fa3e 225 9. We want to connect the e2e job we just created to our dev pipleline by editing the post build actions on `dev-todolist-fe-deploy` job. Open the `dev-todolist-fe-deploy` job and hit `configure`. In the `Post-build actions` section of this job add a `Trigger parameterised build` on other jobs. Set the `Projects to build` to be `dev-todolist-fe-e2e`. Add a Parameter and set the it to the `Current build parameters`. Save the settings.
RS 226
7f49cd 227 ![e2e-trigger](../images/exercise3/e2e-trigger.png)
D 228
d2fa3e 229 10. Run the pipeline from the beginning to see the e2e tests executed after deployment.
bc2216 230
5285f8 231 ### Part 2 - TodoList new feature
bc2216 232 > _In this exercise we will introduce a new feature to create an important flag on the todos. In order to be able to build and test our feature we will use TDD_
D 233
5285f8 234 *As a doer I want to mark todos as important so that I can keep track of and complete high prirority todos first*
bc2216 235
5285f8 236 _Acceptance Criteria_
D 237 - [ ] should be doable with a single click
238 - [ ] should add a red flag against the todo when marked important
239 - [ ] should remove the red colour flag on the flag when important removed
240 - [ ] should not affect existing todos
bc2216 241
5285f8 242 _On page load:_
D 243 - [ ] should display existing todos that are not marked important
244 - [ ] should display existing todos that are marked important with an red flag
bc2216 245
d4f1fa 246 #### 2a - Create todolist-api tests
5285f8 247 > Using [Mocha](https://mochajs.org/) as our test runner; we will now write some tests for backend functionality to persist our important-flag. The changes required to the backend are minimal but we will use TDD to create our test first, then implement the functionality.
f6d2bd 248
d2fa3e 249 1.  Create a new branch in your `todolist-api` app for our feature and push it to the remote
RS 250 ```bash
251 cd todolist-api
252 ```
bc2216 253 ```bash
fad576 254 git checkout -b feature/important-flag
D 255 ```
256 ```bash
257 git push -u origin feature/important-flag
43f2f2 258 ```
D 259
d2fa3e 260 2.  Navigate to the `server/api/todo/todo.spec.js` file. This contains all of the existing todo list api tests. These are broken down into simple `describe("api definition", function(){})` blocks which is BDD speak for how the component being tested should behave. Inside of each `it("should do something ", function(){})` statements we use some snappy language to illustrate the expected behaviour of the test. For example a `GET` request of the api is described and tested for the return to be of type Array as follows.
RS 261
f6d2bd 262 ```javascript
D 263 describe("GET /api/todos", function() {
264     it("should respond with JSON array", function(done) {
265         request(app)
266         .get("/api/todos")
267         .expect(200)
268         .expect("Content-Type", /json/)
269         .end(function(err, res) {
270             if (err) return done(err);
271             // Test goes here
272             res.body.should.be.instanceof(Array);
273             done();
274         });
5285f8 275       });
f6d2bd 276 });
D 277 ```
278 where:
3c9a48 279   - `describe` is used to group tests together into a collection asserting some feature; for example the get all todos api.
D 280   - `it` is an individual test statement and should contain an `expect` or a `should` statement asserting behaviour of the API under test.
281   - `request` is a library for making http calls to the api.
282   - `.expect(200)` asserts the HTTP Return Code
283   - `res.body.should.be.instanceof(Array);` is the actual test call
284   - `done();` tells the test runner that `mocha` has finished execution. This is needed as the http calls are asynchronous.
43f2f2 285
1173e5 286 3.  With this knowledge; let's implement our test for the `important` flag. We expect the front end to introduce a new property on each `todo` that gets passed to the backend called `important`. The API will need to handle this new property and pass it into the mongodb. Let's begin implementing this functionality by writing our test case. Navigate to the `PUT /api/todos` section of the `server/api/todo/todo.spec.js` test file (which should be at the bottom)
d2fa3e 287
3c9a48 288 ![todo-api-tests](../images/exercise3/todo-api-tests.png)
f6d2bd 289
d2fa3e 290 4. Before writing our test; let's first make sure all the existing tests are passing. Ensure that your MongoDB container is running before running the tests (using the `npm run mongo:*` scripts)
RS 291
5285f8 292 ```bash
fad576 293 npm run test
f6d2bd 294 ```
D 295
d2fa3e 296 5. With all the tests passing; let's add our new one. For ease of completing this exercise a template of a new test has been written at the very end of the file (just below the `  // Exercise 3 test case!` comment). A PUT request responds in our API with the data that it has just updated. So provided that MongoDB accepted the change, the API will respond with an object that has the `important` property on it. To write our test; edit the template test by completing the following:
7fdd6e 297     * Edit the `it("should ...")` to describe the important flag we're testing
f6d2bd 298     * Edit the `.send()` to include `important: true` property
3c9a48 299     * Edit the `.expect()` to be `.expect(200)`
f6d2bd 300     * Add a new test assertion to check that `res.body.important` is `true` below the `// YOUR TEST GO HERE` line.
d2fa3e 301
f6d2bd 302 ```javascript
3c9a48 303 // Exercise 3 test case!
5285f8 304 it("should mark todo as important and persist it", function(done) {
f6d2bd 305     request(app)
D 306       .put("/api/todos/" + todoId)
5285f8 307       .send({
D 308         title: "LOVE endpoint/server side testing!",
309         completed: true,
310         important: true
311       })
f6d2bd 312       .expect(200)
D 313       .expect("Content-Type", /json/)
314       .end(function(err, res) {
5285f8 315           if (err) return done(err);
D 316           res.body.should.have.property("_id");
317           res.body.title.should.equal("LOVE endpoint/server side testing!");
318           // YOUR TEST GO HERE
319           res.body.important.should.equal(true);
320           done();
f6d2bd 321       });
5285f8 322 });
f6d2bd 323 ```
D 324
d2fa3e 325 6.  Run your test. It should fail.
5285f8 326 ```bash
fad576 327 npm run test
f6d2bd 328 ```
d2fa3e 329
f6d2bd 330 ![fail-mocha](../images/exercise3/fail-mocha.png)
D 331
d2fa3e 332 7.  With our test now failing; let's implement the feature. This is quite a simple change - we first need to update the `server/api/todo/todo.model.js`. Add an additional property on the schema called `important` and make its type Boolean.
RS 333
f6d2bd 334 ```javascript
D 335 const TodoSchema = new Schema({
5285f8 336   title: String,
d2fa3e 337   completed: Boolean,  
5285f8 338   important: Boolean
f6d2bd 339 });
D 340 ```
7fdd6e 341
d2fa3e 342 8. Next we need to update the `server/config/seed.js` file so that the pre-generated todos have an important property. Add `important: false` below `completed: *` for each object. Don't forget to add a comma at the end of the `completed: *` line.
RS 343
7fdd6e 344 ![api-add-seed-important](../images/exercise3/api-add-seed-important.png)
f6d2bd 345
d2fa3e 346 9.  With your changes to the Database schema updated; re-run your tests. The tests should pass.
5285f8 347 ```bash
fad576 348 npm run test
f6d2bd 349 ```
D 350
d2fa3e 351 10.  Commit your code to the `feature/important-flag` branch and then merge onto the `develop` branch as follows
RS 352 <!-- <p class="tip">
f6d2bd 353 NOTE - At this point in a residency we would peer review the code before pushing it to develop or master branch!
d2fa3e 354 </p> -->
5285f8 355 ```bash
fad576 356 git add .
D 357 ```
358 ```bash
359 git commit -m "ADD backend schema updates"
360 ```
361 ```bash
362 git push
363 ```
364 ```bash
365 git checkout develop
366 ```
367 ```bash
368 git merge feature/important-flag
369 ```
370 ```bash
371 git push
f6d2bd 372 ```
D 373
d2fa3e 374 <!-- 3.  After pushing your changes; start back up the `todolist-api` app on a new terminal session
44888e 375 ```bash
fad576 376 npm run start
d2fa3e 377 ``` -->
44888e 378
95ba2a 379 #### 2b - Create todolist-fe tests
1173e5 380 > Using [Jest](https://facebook.github.io/jest/) as our test runner and the `vue-test-utils` library for managing our vue components; we will now write some tests for front end functionality to persist our important-flag. The changes required to the front end are quite large but we will use TDD to create our test first, then implement the functionality.
f6d2bd 381
e43fd2 382 Our TodoList App uses `vuex` to manage the state of the app's todos and `axios` HTTP library to connect to the backend. `Vuex` is an opinionated framework for managing application state and has some key design features you will need to know to continue with the exercise.
f6d2bd 383
8894bf 384 In `vuex` the application state is managed by a `store`. The `store` houses all the todos we have retrieved from the backend as well as the `getter` methods for our array of `todos`. In order to make changes to the store, we could call the store directly and update each todo item but as earlier said; vuex is an opinionated module with its own way of updating the store. It is bad practice to call the store directly.
5285f8 385
e43fd2 386 There are two parts of the lifecycle to updating the store, the `actions` & `mutations`. When the user clicks a todo to mark it as complete; the `actions` are called. An action could involve a call to the backend or some pre-processing of the data. Once this is done, the change is committed to the store by calling the `mutation` function. A store should only ever be manipulated through a mutation function. Calling the mutation will then update the todo object in the app's local store for rendering in the view.
5285f8 387
D 388 For example; when marking a todo as done in the UI, the following flow occurs
44888e 389   * The `TodoItem.vue` calls the `markTodoDone()` function which dispatches an event to the store.
D 390   * This calls the `updateTodo()` function in the `actions.js` file
391   * The action will update the backend db (calling our `todolist-api`) with our updated todo object.
392   * The action will commit the change to the store by calling the mutation method `MARK_TODO_COMPLETED`
393   * The `MARK_TODO_COMPLETED` will directly access the store object and update it with the new state value
394   * The `ListOfTodos.vue` component is watching the store for changes and when something gets updated it re-renders the `TodoItem.vue`.
395
396 The implementation of our `important` flag will follow this same flow.
5285f8 397
d2fa3e 398 1. Let's implement our feature by first creating a branch. Our new feature, important flag will behave in the same way as the `MARK_TODO_COMPLETED`. Create a new branch in your `todolist-fe` app for our feature and push it to the remote
RS 399
400 ```bash
401 cd todolist-fe
402 ```
f6d2bd 403 ```bash
fad576 404 git checkout -b feature/important-flag
D 405 ```
406 ```bash
407 git push -u origin feature/important-flag
f6d2bd 408 ```
bc2216 409
d2fa3e 410 2. Let's get our tests running by executing a `--watch` on our tests. This will keep re-running our tests everytime there is a file change. It is handy to have this running in a new terminal session.
5285f8 411 ```bash
fad576 412 npm run test -- --watch
5285f8 413 ```
D 414
c516ed 415 > NOTE: You may see an `ENOSPC` error on Linux systems like the following:
RS 416 ```bash
417 ERROR jest exited with code 1.
418 npm ERR! code ELIFECYCLE
419 npm ERR! errno 1
420 npm ERR! todolist-fe@1.0.0 test: `vue-cli-service test "--watch"`
421 npm ERR! Exit status 1
422 ```
423 To fix this error, run the following command:
424 ```bash
425 echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
426 ```
427
44888e 428 3. All the tests should be passing when we begin. If `No tests found related to files changed since last commit` is on show; hit `a` on the terminal to re-run `all` tests.
d2fa3e 429
44888e 430 ![rerun-all](../images/exercise3/rerun-all.png)
D 431
d2fa3e 432 4. There are three places we will add new tests to validate our function behaves as expected against the acceptance criteria from the Feature Story supplied to us. We will need to write tests for our `TodoItem.vue` to handle having a red flag and that it is clickable. Our app is going to need to persist the changes in the backend so we'll want to make changes to our `actions.js` and `mutations.js` to keep the api and local copy of the store in sync. Let's start with our `TodoItem.vue` component. Open the `tests/unit/vue-components/TodoItem.spec.js` file. This has been templated with some example test to correspond with our A/Cs for speed of doing the exercise. Find the describe block for our important flag tests. It is setup already with a `beforeEach()` hook for test setup.
RS 433
5285f8 434 ![important-flag-before](../images/exercise3/important-flag-before.png)
D 435
d2fa3e 436 5. Each of our test cases has its skeleton in place already for example the `TodoItem.vue` component takes a property of `todos` when rendering. This setup is already done for each of our tests so all we have to do is fill in our assertions.
RS 437
5285f8 438 ![todoitem-skeleton-tests](../images/exercise3/todoitem-skeleton-tests.png)
D 439
d2fa3e 440 6. Let's implement the first test `it("should render a button with important flag"`. This test will assert if the button is present on the page and it contains the `.important-flag` CSS class. To implement this; add the `expect` statement as follows below the `// TODO - test goes here!` comment.  
RS 441
5285f8 442 ```javascript
D 443   it("should render a button with important flag", () => {
444     const wrapper = mount(TodoItem, {
445       propsData: { todoItem: importantTodo }
446     });
447     // TODO - test goes here!
448     expect(wrapper.find(".important-flag").exists()).toBe(true);
449   });
450 ```
451
d2fa3e 452 7. Save the file. Observe that the test case has started failing because we have not yet implemented the feature!
RS 453
5285f8 454 ![todoitem-fail-test](../images/exercise3/todoitem-fail-test.png)
D 455
d2fa3e 456 8. With a basic assertion in place, let's continue on to the next few tests. We want the important flag to be red when an item in the todolist is marked accordingly. Conversely we want it to be not red when false. Let's create a check for `.red-flag` CSS property to be present when important is true and not when false. Complete the `expect` statements in your test file as shown below for both tests.
RS 457
5285f8 458 ```javascript
D 459   it("should set the colour to red when true", () => {
460     const wrapper = mount(TodoItem, {
461       propsData: { todoItem: importantTodo }
462     });
463     // TODO - test goes here!
464     expect(wrapper.find(".red-flag").exists()).toBe(true);
465   });
466   it("should set the colour to not red when false", () => {
467     importantTodo.important = false;
468     const wrapper = mount(TodoItem, {
469       propsData: { todoItem: importantTodo }
470     });
471     // TODO - test goes here!
472     expect(wrapper.find(".red-flag").exists()).toBe(false);
473   });
474 ```
475
d2fa3e 476 9. Finally, we want to make the flag clickable and for it to call a function to update the state. The final test in the `TodoItem.spec.js` we want to create should simulate this behaviour. Implement the `it("call markImportant when clicked", () ` test by first simulating the click of our important-flag and asserting the function `markImportant()` to write is executed.
RS 477
5285f8 478 ```javascript
baa8d9 479   it("call markImportant when clicked", () => {
5285f8 480     const wrapper = mount(TodoItem, {
D 481       methods,
482       propsData: { todoItem: importantTodo }
483     });
484     // TODO - test goes here!
485     const input = wrapper.find(".important-flag");
486     input.trigger("click");
487     expect(methods.markImportant).toHaveBeenCalled();
488   });
489 ```
490
d2fa3e 491 10. With our tests written for the feature's UI component, let's implement our code to pass the tests. Explore the `src/components/TodoItem.vue`. Each vue file is broken down into 3 sections
RS 492
5285f8 493     * The `<template></template>` contains the HTML of our component. This could include references to other Components also
D 494     * The `<script></script>` contains the JavaScript of our component and is essentially the logic for our component. It defines things like `properties`, `methods` and other `components`
495     * The `<style></style>` contains the encapsulated CSS of our component
44888e 496
d2fa3e 497 11. Underneath the `</md-list-item>` tag, let's add a new md-button. Add an `.important-flag` class on the `md-button` and put the svg of the flag provided inside it.
RS 498
5285f8 499 ```html
D 500     </md-list-item>
b5d705 501     <!-- TODO - SVG for use in Exercise3 -->
5285f8 502     <md-button class="important-flag">
D 503         <svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" ><path d="M0 0h24v24H0z" fill="none"/><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/></svg>
504     </md-button>
505 ```
506
d2fa3e 507 12. We should now see the first of our failing tests has started to pass. Running the app locally (using `npm run serve`) should show the flag appear in the UI. It is clickable but won't fire any events and the colour is not red as per our requirement. Let's continue to implement the colour change for the flag. On our `<svg/>` tag, add some logic to bind the css to the property of a `todo.important` by adding ` :class="{'red-flag': todoItem.important}"  `. This logic will apply the CSS class when `todo.important`  is true.
RS 508
5285f8 509 ```html
D 510 <md-button class="important-flag">
511     <svg :class="{'red-flag': todoItem.important}"  height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" ><path d="M0 0h24v24H0z" fill="none"/><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/></svg>
512 </md-button>
513 ```
514
d2fa3e 515 13. More tests should now be passing. Let's wire the click of the flag to an event in Javascript. In the methods section of the `<script></script>` tags in the Vue file, implement the `markImportant()`. We want to wire this to the action to updateTodo, just like we have in the `markCompleted()` call above it. We also need to pass an additional property to this method called `important`
RS 516
5285f8 517 ```javascript
D 518     markImportant() {
b5d705 519       // TODO - FILL THIS OUT IN THE EXERCISE
5285f8 520       this.$store.dispatch("updateTodo", {id: this.todoItem._id, important: true});
D 521       console.info("INFO - Mark todo as important ", this.todoItem.important);
c44749 522     }
5285f8 523 ```
D 524
d2fa3e 525 14. Let's connect the click button in the DOM to the Javascript function we've just created. In the template, add a click handler to the md-button to call the function `markImportant()` by adding ` @click="markImportant()"` to the `<md-button>` tag
RS 526
5285f8 527 ```html
b5d705 528     <!-- TODO - SVG for use in Exercise3 -->
5285f8 529     <md-button class="important-flag" @click="markImportant()">
D 530         <svg :class="{'red-flag': todoItem.important}"  height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" ><path d="M0 0h24v24H0z" fill="none"/><path d="M14.4 6L14 4H5v17h2v-7h5.6l.4 2h7V6z"/></svg>
531     </md-button>
532 ```
7fdd6e 533
c44749 534 15. Finally - we need to make it so that when a new todo item is created it will have an important property. Head to `src/store/actions.js` and add `important: false`  below `completed: false` in the `addTodo(){}` action.
d2fa3e 535
RS 536 ![fe-add-actions-important](../images/exercise3/fe-add-actions-important.jpg)
7fdd6e 537
5285f8 538
d2fa3e 539 16. The previously failing tests should have started to pass now. With this work done, let's commit our code. On the terminal, run
5285f8 540 ```bash
fad576 541 git add .
D 542 ```
543 ```bash
544 git commit -m "Implementing the todoitem flag"
545 ```
546 ```bash
547 git push
5285f8 548 ```
d2fa3e 549 17. Start our local development server to test the changes 
5285f8 550
d2fa3e 551 ```bash
RS 552 npm run serve
553 ```
f34abc 554
d2fa3e 555 18. Open our local todolist app (http://localhost:8080/#/todo). If we try to use our important flag, we should see it's still not behaving as expected; this is because we're not updating the state of the app in response to the click event.
RS 556
557 19. We need to implement the `actions` and `mutations` for our feature. Let's start with the tests. Open the `tests/unit/javascript/actions.spec.js` and navigate to the bottom of the file. Our action should should commit the `MARK_TODO_IMPORTANT` to the mutations. Scroll to the end of the test file and implement the skeleton test by adding `expect(commit.firstCall.args[0]).toBe("MARK_TODO_IMPORTANT");` as the assertion.
558
5285f8 559 ```javascript
D 560   it("should call MARK_TODO_IMPORTANT", done => {
561     const commit = sinon.spy();
562     state.todos = todos;
563     actions.updateTodo({ commit, state }, { id: 1, important: true }).then(() => {
564         // TODO - test goes here!
565         expect(commit.firstCall.args[0]).toBe("MARK_TODO_IMPORTANT");
566         done();
567     });
568   });
569 ```
570
d2fa3e 571 20. We should now have more failing tests, let's fix this by adding the call from our action to the mutation method. Open the `src/store/actions.js` file and scroll to the bottom to the `updateTodo()` method. Complete the if block by adding `commit("MARK_TODO_IMPORTANT", i);` as shown below.
RS 572
5285f8 573 ```javascript
D 574 updateTodo({ commit, state }, { id, important }) {
575     let i = state.todos.findIndex(todo => todo._id === id);
576     if (important) {
7fdd6e 577         // TODO - add commit important here!
5285f8 578         commit("MARK_TODO_IMPORTANT", i);
D 579     } else {
580         commit("MARK_TODO_COMPLETED", i);
581     }
582 ```
583
d2fa3e 584 21. Finally, let's implement the `mutation` for our feature. Again, starting with the tests... Open the `tests/unit/javascript/mutations.spec.js` to find our skeleton tests at the bottom of the file. Our mutation method is responsible for toggling the todo's `important` property between `true` and `false`. Let's implement the tests for this functionality by setting important to be true and calling the method expecting the inverse. Then let's set it to false and call the method expecting the inverse. Add the expectations below the `// TODO - test goes here!` comment as done previously.
RS 585
5285f8 586 ```javascript
D 587   it("it should MARK_TODO_IMPORTANT as false", () => {
588     state.todos = importantTodos;
589     // TODO - test goes here!
590     mutations.MARK_TODO_IMPORTANT(state, 0);
591     expect(state.todos[0].important).toBe(false);
592   });
593
594   it("it should MARK_TODO_IMPORTANT as true", () => {
595     state.todos = importantTodos;
596     // TODO - test goes here!
597     state.todos[0].important = false;
598     mutations.MARK_TODO_IMPORTANT(state, 0);
599     expect(state.todos[0].important).toBe(true);
600   });
601 ```
602
d2fa3e 603 22. With our tests running and failing, let's implement the feature to their spec. Open the `src/store/mutations.js` and add another function called `MARK_TODO_IMPORTANT` below the `MARK_TODO_COMPLETED` to toggle `todo.important` between true and false. NOTE - add a `,` at the end of the `MARK_TODO_COMPLETED(){}` function call.
RS 604
5285f8 605 ```javascript
D 606   MARK_TODO_IMPORTANT(state, index) {
607     console.log("INFO - MARK_TODO_IMPORTANT");
608     state.todos[index].important = !state.todos[index].important;
609   }
610 ```
d2fa3e 611
f34abc 612 ![mark-todo-important](../images/exercise3/mark-todo-important.png)
5285f8 613
d2fa3e 614 23. All our tests should now be passing. On the watch tab where they are running, hit `a` to re-run all tests and update any snapshots with `u` if needed.
5285f8 615
d2fa3e 616 24. With all our tests now passing, let's commit our code. On the terminal, run
RS 617
5285f8 618 ```bash
fad576 619 git add .
D 620 ```
621 ```bash
622 git commit -m "Implementing the store and actions"
623 ```
624 ```bash
625 git push
5285f8 626 ```
D 627
d2fa3e 628 25. Before running a build in Jenkins, let's add our tests and code to the develop branch. Ask your neighbour to review your code changes and if they approve; merge them to Develop! (If you're feeling adventurous - raise a PR through GitLab and have someone on your desk peer review it!)
RS 629
630 <!-- <p class="tip">
5285f8 631 NOTE - At this point in a residency we would peer review the code before pushing it to develop or master branch!
d2fa3e 632 </p> -->
RS 633
5285f8 634 ```bash
fad576 635 git checkout develop
D 636 ```
637 ```bash
638 git merge feature/important-flag
639 ```
640 ```bash
641 git push --all
5285f8 642 ```
D 643
d2fa3e 644 26. Run a build in Jenkins. If all things were successful; our application should be deployed as expected! Validate the flag is working as expected. Invoke the route URL for the application from the `*-dev` namespace in the OCP web console.
RS 645
f34abc 646 ![todolist-important](../images/exercise3/todolist-important.png)
bc2216 647
d4f1fa 648 #### 2c - Create todolist e2e tests
bc2216 649
51f8a9 650 > Using [Nightwatch.js](http://nightwatchjs.org/) We will write a reasonably simple e2e test to test the functionality of the feature we just implemented.
6b43e4 651
d2fa3e 652 1.  Firstly we need to create an e2e spec test file in the correct place.
6b43e4 653
T 654 ```bash
655 touch tests/e2e/specs/importantFlag.js
656 ```
657
d2fa3e 658 > NOTE: Windows users should create this new file using a text editor.
RS 659
660 2.  Open this new file in your code editor and set out the initial blank template for an e2e test as below:
661
6b43e4 662     ![if-e2e-step1](../images/exercise3/if-e2e-step1.png)
T 663
664 3.  Now get the test to access the todos page and wait for it to load. The url can be taken from `process.env.VUE_DEV_SERVER_URL`
d2fa3e 665
6b43e4 666     ![if-e2e-step2](../images/exercise3/if-e2e-step2.png)
T 667
d2fa3e 668 4.  Write code to do the following:
RS 669     * Click the clear all button and then enter a value in the textbox to create a new item. 
670     * Assert that an 'important flag' exists (the button to set important)
671     * Assert that a red flag does not exist. 
672     * Click the important flag and check whether the flag has turned red.
673
674 5. You will need to reference the clear all button from the test code. We will therefore have to go to `src/components/XofYItems.vue` and add `id="clear-all"` to the clear all button.
675
676     <!-- ![if-e2e-step3](../images/exercise3/if-e2e-step3.png) -->
6b43e4 677     ![if-e2e-step3a](../images/exercise3/if-e2e-step3a.png)
T 678
d2fa3e 679 6.  Write the following test code. The pauses allow for the body of the page to render the todo list before exercising the test code:
015fee 680 ```javascript
RS 681 module.exports = {
682     "Testing important flag setting": browser => {
683       browser
684         .url(process.env.VUE_DEV_SERVER_URL + '/#/todo')
685         .waitForElementVisible("body", 5000)
686         .pause(5000)
687         .click('#clear-all')
688         .pause(2000)
689         .setValue('input',['set a todo',browser.Keys.ENTER])
690         .pause(2000)
691         .assert.elementPresent(".important-flag")
692         .assert.elementNotPresent(".red-flag")
693         .click('.important-flag')
694         .end();
695     }
696   };
697 ```
d2fa3e 698     <!-- ![if-e2e-step4](../images/exercise3/if-e2e-step4.png) -->
4c6b6d 699     
RS 700     <!-- ![if-e2e-step4](../images/exercise3/e2e-code-listing-full.jpg) -->
701
702 7. Your final E2E test should look like the following:
703
d2fa3e 704     ![if-e2e-step4](../images/exercise3/e2e-code-listing-full.jpg)
RS 705
4c6b6d 706 8.  At this point we should have a working e2e test. We can run this by using `npm run e2e`. When all tests pass, we can push up these changes.
6b43e4 707
T 708 ```bash
709 git add .
710 ```
711
712 ```bash
713 git commit -m "Implementing e2e tests"
714 ```
715
716 ```bash
717 git push
718 ```
43f2f2 719
f6d2bd 720 ---
43f2f2 721
d2fa3e 722 <!-- ## Extension Tasks
f6d2bd 723
43f2f2 724 > _Ideas for go-getters. Advanced topic for doers to get on with if they finish early. These will usually not have a solution and are provided for additional scope._
D 725
3c8f9c 726 * Add Config maps to inject DB creds to the app
D 727 * Create a blue/green deploy based on the success of running e2e tests against either blue or green and flopping load over to new endpoint when successful.
728 Advanced
729 * Add Auth0 support to your FE app.
43f2f2 730
D 731 ## Additional Reading
f6d2bd 732
43f2f2 733 > List of links or other reading that might be of use / reference for the exercise
D 734
735 ## Slide links
f6d2bd 736
01c4da 737 - [Intro](https://docs.google.com/presentation/d/18W0GoBTwRGpgbOyHf2ZnL_OIWU_DvsoR14H0_t5jisA)
RH 738 - [Wrap-up](https://docs.google.com/presentation/d/1uIYHC57POSaVD6XNZGhdetABiSnlP1TztYTgglMP-DA)
d2fa3e 739 - [All Material](https://drive.google.com/drive/folders/1xVaQukmwwmyJSDN0zOkghZX7yb-0freT) -->