donal
2018-05-09 58480f18403a3e74f9afa99d15c8a89ffaeeec2a
commit | author | age
15c296 1 # Revenge Of The Automated Testing
5a16fd 2
867471 3 > The purpose of this exercise is to develop and validate a new feature using TDD; and to promote the assured feature through the pipeline.
01245c 4
9e07af 5 ![comic-header](../images/exercise3/comic-header.png)
RH 6
7 [Image Source](https://cdn-images-1.medium.com/max/1600/1*wF_fSCH-gLYfMbkwb3gR2w.png)
8
aea5c5 9 ## Introduction to TDD.
D 10
11 > _Here is a brief introduction of TDD and why we use it._
12
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
15 The TDD cycle can be illustrated with the following diagram;
16 ![TDD-diagram](../images/exercise3/TDD-lifecycle.jpg)
17
18 ### The TDD Cycle 
19
20 1. `Write a test` -
21 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 features specification and requirements, or the feature could be wrong from the get-go. 
22
23 2. `Test Fails` -
24 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. 
25
26 3. `Write code to make test pass` -
27 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.
28
29 4. `Code Passes tests` -
30 If all tests pass, the developer can be confident that the new code meets the test requirements.
31
32 5. `Refactor` -
f23c14 33 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.
aea5c5 34
D 35 6. `Repeat` -
36 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.
37
38 ### Testing Bananalogy
39 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.
40 ![bdd-bananas](../images/exercise3/bdd-bananas.png)
41   * `describe` is used to group tests together. The string `"a bunch of ripe bananas"` is for human reading and allows you to identify tests.
42   * `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.
01245c 43
D 44 ---
5a16fd 45
D 46 ## Learning Outcomes
01245c 47
5a16fd 48 As a learner you will be able to
01245c 49
142de4 50 * Understand the why behind TDD
936424 51 * Implement a feature using TDD for front end and backend
142de4 52 * Write end to end tests for the feature and run them in CI
5a16fd 53
D 54 ## Tools and Frameworks
01245c 55
5a16fd 56 > Name of tool - short description and link to docs or website
D 57
142de4 58 1.  [Jest](https://facebook.github.io/jest/) - Zero configuration testing platform
D 59 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.
60 1.  [Vue Test Utils](https://vue-test-utils.vuejs.org/en/) - Vue Test Utils is the official unit testing utility library for Vue.js.
61 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.
62 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.
63 1.  [Sinon](http://sinonjs.org/) - Standalone test spies, stubs and mocks for JavaScript. 
64 Works with any unit testing framework.
5a16fd 65
D 66 ## Big Picture
01245c 67
5a16fd 68 This exercise begins cluster containing blah blah
D 69
01245c 70 ---
5a16fd 71
D 72 ## 10,000 Ft View
73
142de4 74 > The goal of this exercise is to add a new component to the application using TDD to create and validate it's behaviour. The User story we have been given is as follows:
5a16fd 75
142de4 76 *As a doer I want to mark todos as important so that I can keep track of and complete high prirority todos first*
01245c 77
142de4 78 _Acceptance Criteria_
D 79 - [ ] should be doable with a single click
80 - [ ] should add a red flag against the todo when marked important
81 - [ ] should remove the red colour flag on the flag when important removed
82 - [ ] should not affect existing todos
83
84 _On page load:_
85 - [ ] should display existing todos that are not marked important
86 - [ ] should display existing todos that are marked important with an red flag
5a16fd 87
D 88 ## Step by Step Instructions
3050f4 89 > This is a fairly structured guide with references to exact filenames and sections of text to be added.
5a16fd 90
01245c 91 ### Part 1 - Tests in our Pipeline
936424 92 > _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_
5a16fd 93
752f2a 94 #### 1a - FE Unit tests
936424 95 > In this exercise we will execute our test for the front end locally. Once verified we will add them to Jenkins.
d1565e 96
655873 97 2. 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`.
3050f4 98 ```bash
58480f 99 cd todolist-fe
D 100 ```
101 ```bash
102 npm run test
3050f4 103 ```
A 104 <p class="tip" > 
8fc8df 105 `test` is an alias used that runs `vue-cli-service test` from the scripts object in `package.json`
3050f4 106 </p>
aea5c5 107 ![screenshot-scripts](../images/exercise3/screenshot-scripts.png)
D 108
936424 109 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/*`
d1565e 110
3050f4 111 2. 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.
aea5c5 112 ![test-run-locally](../images/exercise3/test-run-locally.png)
3050f4 113
204a7b 114 2. 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.
3050f4 115 ```bash
58480f 116 cd todolist-api
D 117 ```
118 ```bash
119 npm run test
3050f4 120 ```
b81187 121
a5f8e5 122 2. Navigate to your instance of jenkins at `https://jenkins-<YOUR_NAME>-ci-cd.apps.somedomain.com/`. 
3050f4 123 Click on `dev-todolist-fe-build` and then click the `configure` button on the left-hand side.
aea5c5 124 ![jenkins-configure-job](../images/exercise3/jenkins-configure-job.png)
3050f4 125
140a4a 126 2. Scroll to the `Build` part of the configuration page and add `npm run test` below `npm install`.
aea5c5 127 ![jenkins-build-step](../images/exercise3/jenkins-build-step.png)
3050f4 128
e1ba4f 129 2. 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).
aea5c5 130 ![xunit-action](../images/exercise3/xunit-action.png)
b81187 131
A 132 2. 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:
aea5c5 133 ![post-build-actions](../images/exercise3/post-build-actions.png)
b81187 134
0976ea 135 2. Click `save` or `apply` 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.
b81187 136
d82125 137 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.
aea5c5 138 ![change-test-to-fail](../images/exercise3/change-test-to-fail.png)
b81187 139
0976ea 140 2. Push this to Gitlab and run the build job.
b81187 141 ```bash
58480f 142 git add .
D 143 ```
144 ```bash
145 git commit -m "TEST - failing build with tests"
146 ```
147 ```bash
148 git push
b81187 149 ```
A 150
e1ba4f 151 2. Rerun the `dev-todolist-fe-build` job. It should have failed and not run any other jobs. 
aea5c5 152 ![jenkins-with-failing-build](../images/exercise3/jenkins-with-failing-build.png)
b81187 153
5da0fd 154 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 155 ![fe-test-fails](../images/exercise3/fe-test-fails.png)
156
ff561b 157 2. Undo the changes you made to the `ListOfTodos.spec.js` file, commit your code and rerun the build. This should trigger a full `build --> bake --> deploy` of `todolist-fe`.
e1ba4f 158
752f2a 159 #### 1b - BE Unit tests
e1ba4f 160 > In this exercise we will execute our test for the backend locally. Once verified we will add them to Jenkins and add a mongodb to Jenkins for running tests there.
ff561b 161
867471 162 2. We're now going to do the same for the api. However, in order to run our API tests in CI; we need there to be a MongoDB available for testing. In our `enablement-ci-cd` repo; checkout the mongo branch as shown below to bring in the template and params. The mongodb template we're using is the same as the one for our `todolist-fe` created in previous exercise.
55df0e 163 ```bash
58480f 164 git checkout exercise3/mongodb params/mongodb templates/mongodb.yml
55df0e 165 ```
ff561b 166
55df0e 167 2. Open `enablement-ci-cd` in your favourite editor. Edit the `inventory/host_vars/ci-cd-tooling.yml` to include a new object for our mongodb  as shown below. This item can be added below the jenkins slave in the `ci-cd-builds` section.
D 168 ```yaml
169   - name: "jenkins-mongodb"
170     namespace: "{{ ci_cd_namespace }}"
171     template: "{{ playbook_dir }}/templates/mongodb.yml"
172     params: "{{ playbook_dir }}/params/mongodb"
173     tags:
174     - mongodb
175 ```
176 ![jenkins-mongo](../images/exercise3/jenkins-mongo.png)
177
178 2. Git commit your updates to the inventory to git for traceability.
179 ```bash
58480f 180 git add .
D 181 ```
182 ```bash
183 git commit -m "ADD - mongodb for use in the pipeline"
184 ```
185 ```bash
186 git push
55df0e 187 ```
D 188
189 2. Apply this change as done previously using ansible. The deployment can be validated by going to your `<YOUR_NAME>-ci-cd` namespace! 
190 ```bash
58480f 191 ansible-playbook apply.yml -e target=tools \
55df0e 192   -i inventory/ \
D 193   -e "filter_tags=mongodb"
194 ```
195 ![ocp-mongo](../images/exercise3/ocp-mongo.png)
196
197 2. With the mongodb in place Open up Jenkins and head to the `configure` panel of the `dev-todolist-api-build` job. 
198  
7a3088 199 2. Add `npm run test:ci` above `npm run build:ci`.
aea5c5 200 ![api-build-step](../images/exercise3/api-build-step.png)
ff561b 201
227301 202 2. Scroll to the `Post-build Actions` section and click `Add post-build action`. Select `Publish xUnit test result report`.
ff561b 203
e1ba4f 204 2. Click the `Add` button under `Publish xUnit test result report` and select `JUnit`. In the pattern field enter `reports/server/mocha/test-results.xml`. In the `Failed Tests Thresholds`  input box enter 0 under `Red Ball Total`. It should look a little something like this. Save your changes:
aea5c5 205 ![api-post-build](../images/exercise3/api-post-build.png)
ff561b 206
A 207 2. We're now going to deliberately fail a test again to ensure that `bake` and `deploy` jobs aren't triggered if any tests fail. Go to `todo.spec.js` in `/server/api/todo` and head to `line 35`. Replace `false` with `true`. 
aea5c5 208 ![api-fail-build](../images/exercise3/api-fail-build.png)
ff561b 209
0976ea 210 2. Push this to Gitlab and run the build job.
ff561b 211 ```bash
58480f 212 git add .
D 213 ```
214 ```bash
215 git commit -m "TEST - failing build with tests"
216 ```
217 ```bash
218 git push
ff561b 219 ```
A 220
d82125 221 2. If successful this will fail the build and not run the `bake` or `deploy` jobs! Undo your changes and move on to the next section.
0976ea 222
A 223 <p class="tip">
224   NOTE - Don't forget to undo the changes that you made to your tests!
225 </p>
ff561b 226
752f2a 227 #### 1c - End to End tests (e2e)
cad21a 228 > _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._
d1565e 229
f9d113 230 In this 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 Nightwatchjs to orchestrate a selenium webdriver instance that controls the web browser; in this case Chrome!
D 231
232 2. Let's start by checking 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 and comes pre-configured to run tests against Chrome. We have created some additional configuration in the root of the project `nightwatch.config.js` to run headless in CI mode on Jenkins.
233 ```bash
58480f 234 cd todolist-fe
f9d113 235 ```
D 236
237 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.
238 ```bash
58480f 239 npm run e2e
f9d113 240 ```
4ce512 241 ![local-e2e](../images/exercise3/local-e2e.png)
f9d113 242
4ce512 243 2. 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`.
f9d113 244
e1ba4f 245 2. 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
4ce512 246 ![e2e-general](../images/exercise3/e2e-general.png)
d1565e 247
4ce512 248 2. 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}`
D 249 ![e2e-git](../images/exercise3/e2e-git.png)
250
e1ba4f 251 2. Set `Color ANSI Console Output` on the `Build Environment` section
4ce512 252
e1ba4f 253 2. On the Build section; add a build step to execute shell and fill in the following substituting `<YOUR_NAME>` and `somedomain` accordingly:
4ce512 254 ```bash
8bf642 255 export E2E_TEST_ROUTE=todolist-fe-<YOUR_NAME>-dev.apps.somedomain.com
7a3088 256 npm install
D 257 npm run e2e:ci
4ce512 258 ```
D 259 ![e2e-steps](../images/exercise3/e2e-steps.png)
260
e1ba4f 261 2. 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.
4ce512 262 ![e2e-post-build](../images/exercise3/e2e-post-build.png)
D 263
e1ba4f 264 2. 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.
a47ec2 265 ![e2e-trigger](../images/exercise3/e2e-trigger.png)
D 266
b62fab 267 2. Run the pipeline from the beginning to see the tests executed (two executions will show tests scores on the graph!).
d1565e 268
142de4 269 ### Part 2 - TodoList new feature
d1565e 270 > _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 271
142de4 272 *As a doer I want to mark todos as important so that I can keep track of and complete high prirority todos first*
d1565e 273
142de4 274 _Acceptance Criteria_
D 275 - [ ] should be doable with a single click
276 - [ ] should add a red flag against the todo when marked important
277 - [ ] should remove the red colour flag on the flag when important removed
278 - [ ] should not affect existing todos
d1565e 279
142de4 280 _On page load:_
D 281 - [ ] should display existing todos that are not marked important
282 - [ ] should display existing todos that are marked important with an red flag
d1565e 283
752f2a 284 #### 2a - Create todolist-api tests
142de4 285 > 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.
01245c 286
D 287 3.  Create a new branch in your `todolist-api` app for our feature and push it to the remote
d1565e 288 ```bash
58480f 289 git checkout -b feature/important-flag
D 290 ```
291 ```bash
292 git push -u origin feature/important-flag
5a16fd 293 ```
D 294
01245c 295 3.  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.
D 296 ```javascript
297 describe("GET /api/todos", function() {
298     it("should respond with JSON array", function(done) {
299         request(app)
300         .get("/api/todos")
301         .expect(200)
302         .expect("Content-Type", /json/)
303         .end(function(err, res) {
304             if (err) return done(err);
305             // Test goes here
306             res.body.should.be.instanceof(Array);
307             done();
308         });
142de4 309       });
01245c 310 });
D 311 ```
312 where:
775c6b 313   - `describe` is used to group tests together into a collection asserting some feature; for example the get all todos api.
D 314   - `it` is an individual test statement and should contain an `expect` or a `should` statement asserting behaviour of the API under test.
315   - `request` is a library for making http calls to the api.
316   - `.expect(200)` asserts the HTTP Return Code
317   - `res.body.should.be.instanceof(Array);` is the actual test call
318   - `done();` tells the test runner that `mocha` has finished execution. This is needed as the http calls are asynchronous.
5a16fd 319
936424 320 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) 
775c6b 321 ![todo-api-tests](../images/exercise3/todo-api-tests.png)
01245c 322
775c6b 323 3. Before writing our test; let's first make sure all the existing tests are passing.
142de4 324 ```bash
58480f 325 npm run test
01245c 326 ```
D 327
775c6b 328 3. 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:
9f3a7a 329     * Edit the `it("should ...")` to describe the important flag we're testing
01245c 330     * Edit the `.send()` to include `important: true` property
775c6b 331     * Edit the `.expect()` to be `.expect(200)`
01245c 332     * Add a new test assertion to check that `res.body.important` is `true` below the `// YOUR TEST GO HERE` line.
D 333 ```javascript
775c6b 334 // Exercise 3 test case!
142de4 335 it("should mark todo as important and persist it", function(done) {
01245c 336     request(app)
D 337       .put("/api/todos/" + todoId)
142de4 338       .send({
D 339         title: "LOVE endpoint/server side testing!",
340         completed: true,
341         important: true
342       })
01245c 343       .expect(200)
D 344       .expect("Content-Type", /json/)
345       .end(function(err, res) {
142de4 346           if (err) return done(err);
D 347           res.body.should.have.property("_id");
348           res.body.title.should.equal("LOVE endpoint/server side testing!");
349           // YOUR TEST GO HERE
350           res.body.important.should.equal(true);
351           done();
01245c 352       });
142de4 353 });
01245c 354 ```
D 355
142de4 356 3.  Run your test. It should fail.
D 357 ```bash
58480f 358 npm run test
01245c 359 ```
D 360 ![fail-mocha](../images/exercise3/fail-mocha.png)
361
9f3a7a 362 3.  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.
01245c 363 ```javascript
D 364 const TodoSchema = new Schema({
142de4 365   title: String,
D 366   completed: Boolean,
367   important: Boolean
01245c 368 });
D 369 ```
9f3a7a 370
A 371 3. Next we need to update the `server/config/seed.js` file so that the pre-generated todos have an important propety. Add `important: true` below `completed: *` for each object. Don't forget to add a comma at the end of the `completed: *` line. 
372 ![api-add-seed-important](../images/exercise3/api-add-seed-important.png)
01245c 373
142de4 374 3.  With your changes to the Database schema updated; re-run your tests.
D 375 ```bash
58480f 376 npm run test
01245c 377 ```
D 378
142de4 379 3.  Commit your code to the `feature/important-flag` branch and then merge onto the `develop` branch as follows
01245c 380 <p class="tip">
D 381 NOTE - At this point in a residency we would peer review the code before pushing it to develop or master branch!
382 </p>
142de4 383 ```bash
58480f 384 git add .
D 385 ```
386 ```bash
387 git commit -m "ADD backend schema updates"
388 ```
389 ```bash
390 git push
391 ```
392 ```bash
393 git checkout develop
394 ```
395 ```bash
396 git merge feature/important-flag
397 ```
398 ```bash
399 git push
01245c 400 ```
D 401
1329f3 402 3.  After pushing your changes; start back up the `todolist-api` app on a new terminal session
D 403 ```bash
58480f 404 npm run start
1329f3 405 ```
D 406
936424 407 #### 2b - Create todolist-fe tests
RH 408 > 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. 
01245c 409
142de4 410 Our TodoList App uses `vuex` to manage the state of the apps' 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. 
01245c 411
142de4 412 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 it's own way of updating the store. It is bad practice to call the store directly. 
D 413
414 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 apps local store for rendering in the view.
415
416 For example; when marking a todo as done in the UI, the following flow occurs
1329f3 417   * The `TodoItem.vue` calls the `markTodoDone()` function which dispatches an event to the store.
D 418   * This calls the `updateTodo()` function in the `actions.js` file
419   * The action will update the backend db (calling our `todolist-api`) with our updated todo object.
420   * The action will commit the change to the store by calling the mutation method `MARK_TODO_COMPLETED`
421   * The `MARK_TODO_COMPLETED` will directly access the store object and update it with the new state value
422   * The `ListOfTodos.vue` component is watching the store for changes and when something gets updated it re-renders the `TodoItem.vue`.
423
424 The implementation of our `important` flag will follow this same flow.
142de4 425
D 426 3. 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
01245c 427 ```bash
58480f 428 git checkout -b feature/important-flag
D 429 ```
430 ```bash
431 git push -u origin feature/important-flag
01245c 432 ```
d1565e 433
1329f3 434 3. 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 hany to have this running in a new terminal session.
142de4 435 ```bash
58480f 436 npm run test -- --watch
142de4 437 ```
D 438
1329f3 439 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.
D 440 ![rerun-all](../images/exercise3/rerun-all.png)
441
867471 442 3. 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.
142de4 443 ![important-flag-before](../images/exercise3/important-flag-before.png)
D 444
445 3. Each of our test cases has it's 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.
446 ![todoitem-skeleton-tests](../images/exercise3/todoitem-skeleton-tests.png)
447
1329f3 448 3. 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.  
142de4 449 ```javascript
D 450   it("should render a button with important flag", () => {
451     const wrapper = mount(TodoItem, {
452       propsData: { todoItem: importantTodo }
453     });
454     // TODO - test goes here!
455     expect(wrapper.find(".important-flag").exists()).toBe(true);
456   });
457 ```
458
459 3. Save the file and we should see in our test watch the test case has started failing because we have not yet implemented the feature!
460 ![todoitem-fail-test](../images/exercise3/todoitem-fail-test.png)
461
9f3a7a 462 3. 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.
142de4 463 ```javascript
D 464   it("should set the colour to red when true", () => {
465     const wrapper = mount(TodoItem, {
466       propsData: { todoItem: importantTodo }
467     });
468     // TODO - test goes here!
469     expect(wrapper.find(".red-flag").exists()).toBe(true);
470   });
471   it("should set the colour to not red when false", () => {
472     importantTodo.important = false;
473     const wrapper = mount(TodoItem, {
474       propsData: { todoItem: importantTodo }
475     });
476     // TODO - test goes here!
477     expect(wrapper.find(".red-flag").exists()).toBe(false);
478   });
479 ```
480
7d4ccd 481 3. 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 makImportant when clicked", () ` test by first simulating the click of our important-flag and asserting the function `markImportant()` to write is executed.
142de4 482 ```javascript
D 483   it("call makImportant when clicked", () => {
484     const wrapper = mount(TodoItem, {
485       methods,
486       propsData: { todoItem: importantTodo }
487     });
488     // TODO - test goes here!
489     const input = wrapper.find(".important-flag");
490     input.trigger("click");
491     expect(methods.markImportant).toHaveBeenCalled();
492   });
493 ```
494
1329f3 495 3. 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
142de4 496     * The `<template></template>` contains the HTML of our component. This could include references to other Components also
D 497     * 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`
498     * The `<style></style>` contains the encapsulated CSS of our component
1329f3 499
D 500 3. Underneath the `</md-list-item>` tag, let's add a new md-button. Add a `.important-flag` class on the `md-button` and put the svg of the flag provided inside it.
142de4 501 ```html
D 502     </md-list-item>
867471 503     <!-- TODO - SVG for use in Exercise3 -->
142de4 504     <md-button class="important-flag">
D 505         <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>
506     </md-button>
507 ```
508
509 3. 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.
510 ```html
511 <md-button class="important-flag">
512     <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>
513 </md-button>
514 ```
515
9f3a7a 516 3. 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 and additional property to this method call `important`
142de4 517 ```javascript
D 518     markImportant() {
867471 519       // TODO - FILL THIS OUT IN THE EXERCISE
142de4 520       this.$store.dispatch("updateTodo", {id: this.todoItem._id, important: true});
D 521       console.info("INFO - Mark todo as important ", this.todoItem.important);
522     },
523 ```
524
9f3a7a 525 3. 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 
142de4 526 ```html
867471 527     <!-- TODO - SVG for use in Exercise3 -->
142de4 528     <md-button class="important-flag" @click="markImportant()">
D 529         <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>
530     </md-button>
531 ```
9f3a7a 532
A 533 3. Finally - we need to make it so that when a new todo item is created it will have an important property. Head to `store/actions.js` and add `important: false`  below `completed: false` in the `addTodo(){}` action.
534 ![fe-add-actions-important](../images/exercise3/fe-add-actions-important.png)
535
142de4 536
D 537 3. The previously failing tests should have started to pass now. With this work done, let's commit our code. On the terminal, run 
538 ```bash
58480f 539 git add .
D 540 ```
541 ```bash
542 git commit -m "Implementing the todoitem flag"
543 ```
544 ```bash
545 git push
142de4 546 ```
D 547
3ee499 548 3. 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.
D 549
550 3. 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.
142de4 551 ```javascript
D 552   it("should call MARK_TODO_IMPORTANT", done => {
553     const commit = sinon.spy();
554     state.todos = todos;
555     actions.updateTodo({ commit, state }, { id: 1, important: true }).then(() => {
556         // TODO - test goes here!
557         expect(commit.firstCall.args[0]).toBe("MARK_TODO_IMPORTANT");
558         done();
559     });
560   });
561 ```
562
7d4ccd 563 3. 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.
142de4 564 ```javascript
D 565 updateTodo({ commit, state }, { id, important }) {
566     let i = state.todos.findIndex(todo => todo._id === id);
567     if (important) {
9f3a7a 568         // TODO - add commit important here!
142de4 569         commit("MARK_TODO_IMPORTANT", i);
D 570     } else {
571         commit("MARK_TODO_COMPLETED", i);
572     }
573 ```
574
e1ba4f 575 3. 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.
142de4 576 ```javascript
D 577   it("it should MARK_TODO_IMPORTANT as false", () => {
578     state.todos = importantTodos;
579     // TODO - test goes here!
580     mutations.MARK_TODO_IMPORTANT(state, 0);
581     expect(state.todos[0].important).toBe(false);
582   });
583
584   it("it should MARK_TODO_IMPORTANT as true", () => {
585     state.todos = importantTodos;
586     // TODO - test goes here!
587     state.todos[0].important = false;
588     mutations.MARK_TODO_IMPORTANT(state, 0);
589     expect(state.todos[0].important).toBe(true);
590   });
591 ```
592
e1ba4f 593 3. 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.
142de4 594 ```javascript
D 595   MARK_TODO_IMPORTANT(state, index) {
596     console.log("INFO - MARK_TODO_IMPORTANT");
597     state.todos[index].important = !state.todos[index].important;
598   }
599 ```
3ee499 600 ![mark-todo-important](../images/exercise3/mark-todo-important.png)
142de4 601
e1ba4f 602 3. 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.
142de4 603
D 604 3. With all our tests now passing, let's commit our code. On the terminal, run
605 ```bash
58480f 606 git add .
D 607 ```
608 ```bash
609 git commit -m "Implementing the store and actions"
610 ```
611 ```bash
612 git push
142de4 613 ```
D 614
5965fd 615 3. 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!)
142de4 616 <p class="tip">
D 617 NOTE - At this point in a residency we would peer review the code before pushing it to develop or master branch!
618 </p>
619 ```bash
58480f 620 git checkout develop
D 621 ```
622 ```bash
623 git merge feature/important-flag
624 ```
625 ```bash
626 git push --all
142de4 627 ```
D 628
3ee499 629 3. Run a build in Jenkins. If all things were successful; our application should be deployed as expected! Validate the flag is working as expected.
D 630 ![todolist-important](../images/exercise3/todolist-important.png)
d1565e 631
752f2a 632 #### 2c - Create todolist e2e tests
d1565e 633
4ce512 634 3. TODO !!
5a16fd 635
01245c 636 ---
5a16fd 637
D 638 ## Extension Tasks
01245c 639
5a16fd 640 > _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 641
cad21a 642 * Add Config maps to inject DB creds to the app
D 643 * 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.
644 Advanced
645 * Add Auth0 support to your FE app.
5a16fd 646
D 647 ## Additional Reading
01245c 648
5a16fd 649 > List of links or other reading that might be of use / reference for the exercise
D 650
651 ## Slide links
01245c 652
4f0295 653 - [Intro](https://docs.google.com/presentation/d/18W0GoBTwRGpgbOyHf2ZnL_OIWU_DvsoR14H0_t5jisA)
RH 654 - [Wrap-up](https://docs.google.com/presentation/d/1uIYHC57POSaVD6XNZGhdetABiSnlP1TztYTgglMP-DA)
655 - [All Material](https://drive.google.com/drive/folders/1xVaQukmwwmyJSDN0zOkghZX7yb-0freT)