Skip to content

Commit 354685e

Browse files
committed
adds screenshots of tests failing & passing for add_attributes function https://vimeo.com/8503138
1 parent 6ec9868 commit 354685e

File tree

3 files changed

+156
-93
lines changed

3 files changed

+156
-93
lines changed

elmish.md

+89-26
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
# `Elm`(_ish_)
22

3-
## (How to Build a Front-end Micro-Framework _From Scratch_)
4-
53
![elmlogo-ish](https://user-images.githubusercontent.com/194400/43213139-b70a4c68-902d-11e8-8162-3c7cb56b6360.png)
64
<!-- the colors are deliberately "a bit off" to emphasize that
75
this is a "inspired by" but really a "poor immitation" of Elm! -->
86

97
`Elm`(_ish_) is an **`Elm`**-_inspired_ `JavaScript` (**ES5**)
10-
fully functional front-end _micro_-framework.[<sup>1</sup>](#notes)
8+
fully functional front-end _micro_-framework from _scratch_.[<sup>1</sup>](#notes)
119

12-
<br /><br />
10+
<br />
1311

1412
## _Why?_
1513

@@ -22,19 +20,19 @@ into a "micro framework" is to: <br />
2220
***simplify*** the Todo List application code
2321
to _just_
2422
["**application logic**"](https://en.wikipedia.org/wiki/Business_logic). <br />
25-
**b)** _demonstrate_ a ***re-useable*** (_fully-tested_)
23+
**b)** _demo_ a ***re-useable*** (_fully-tested_)
2624
"**micro-framework**" that allows us
2725
to _practice_ using The Elm Architecture ("TEA").<br />
2826
**c)** promote the **mindset** of writing **tests _first_**
2927
and **`then`** the _least_ amount of code necessary to pass the test
3028
(_while meeting the acceptance criteria_).
3129

3230
> _**Test** & **Document-Driven Development** is **easy** and it's **easily**
33-
one of the **best habits** to form in your software development career.
31+
one of the **best habits** to form in your software development "career".
3432
This walkthrough shows **how** you can do it **the right way**
3533
from the **start** of a project._
3634

37-
<br /><br />
35+
<br />
3836

3937
## _What?_
4038

@@ -64,7 +62,7 @@ https://github.com/dwyl/learn-elm-architecture-in-javascript/issues <br />
6462
@dwyl is a "safe space" and we are all here to help don't be shy/afraid; <br />
6563
the _more_ questions you ask, the more you are helping yourself and _others_!
6664

67-
<br /><br />
65+
<br />
6866

6967
## _How_?
7068

@@ -131,10 +129,8 @@ The `view` function _invokes_ several "helper" functions
131129
which create HTML ("DOM") elements e.g: `<section>`, `<div>` & `<button>`;
132130
these _can_ (_will_) be generalised (_below_).
133131

134-
135132
Let's start with a couple of "_familiar_" _generic_ functions
136-
(_which we used in the "counter-reset" example_):
137-
`init`, `empty` and `mount`. <br />
133+
(_which we used in the "counter-reset" example_): `empty` and `mount`. <br />
138134

139135
<br />
140136

@@ -283,8 +279,6 @@ function empty(node) {
283279

284280
#### Add `module.exports` statement to "export" the `empty` function
285281

286-
Remember to add a line in the `module.exports` Object at the end of the file:
287-
288282
Adding the function to the `elmish.js` file is a good _start_,
289283
but we need to ***`export`*** it to be able to _invoke_ it in our test. <br />
290284
Add the following code at the end of `examples/todo-list/elmish.js`:
@@ -310,7 +304,6 @@ Boom! our first test is passing!
310304
(_the test has **3 assertions**, that's why Tape says "tests 3. pass 3"_).
311305

312306

313-
314307
### `mount` the App
315308

316309
The `mount` function is the "glue" or "wiring" function that
@@ -590,7 +583,8 @@ The `JSDOC` comment for our `add_attributes` function is:
590583
/**
591584
* add_attributes applies the desired attributes to the desired node.
592585
* Note: this function is "impure" because it "mutates" the node.
593-
* however it is idempotent; the "side effect" is only applied once.
586+
* however it is idempotent; the "side effect" is only applied once
587+
* and no other nodes in the DOM are "affected" (undesirably).
594588
* @param {Array.<String>} attrlist list of attributes to be applied to the node
595589
* @param {Object} node DOM node upon which attribute(s) should be applied
596590
* @example
@@ -617,37 +611,98 @@ test('elmish.add_attributes applies class HTML attribute to a node', function (t
617611
});
618612
```
619613

620-
Given the code in the test above,
621-
take a moment to think of how _you_ would write,
614+
If you (_attempt to_) run this test (_and you **should**_),
615+
you will see something like this:
616+
617+
![image](https://user-images.githubusercontent.com/194400/43414770-af5ee0e4-942b-11e8-9d1c-1cbab3adc136.png)
618+
619+
Test is failing because the `elmish.add_attributes` function does not _exist_.
620+
621+
Go ahead and _create_ the `elmish.add_attributes` function
622+
(_just the function without passing the test_) and _export_ it in `elmish.js`:
623+
```js
624+
/**
625+
* add_attributes applies the desired attributes to the desired node.
626+
* Note: this function is "impure" because it "mutates" the node.
627+
* however it is idempotent; the "side effect" is only applied once
628+
* and no other nodes in the DOM are "affected" (undesirably).
629+
* @param {Array.<String>} attrlist list of attributes to be applied to the node
630+
* @param {Object} node DOM node upon which attribute(s) should be applied
631+
* @example
632+
* // returns node with attributes applied
633+
* div = add_attributes(["class=item", "id=mydiv", "active=true"], div);
634+
*/
635+
function add_attributes (attrlist, node) {
636+
if(attrlist && attrlist.length) {
637+
attrlist.forEach(function (attr) { // apply each prop in array
638+
var a = attr.split('=');
639+
switch(a[0]) {
640+
// code to make test pass goes here ...
641+
default:
642+
break;
643+
}
644+
});
645+
}
646+
return node;
647+
}
648+
// ... at the end of the file, "export" the add_attributes funciton:
649+
if (typeof module !== 'undefined' && module.exports) {
650+
module.exports = {
651+
add_attributes: add_attributes, // export the function so we can test it!
652+
empty: empty,
653+
mount: mount
654+
}
655+
}
656+
```
657+
658+
When you re-run the test you will see something like this:
659+
![image](https://user-images.githubusercontent.com/194400/43416008-ff63d70e-942e-11e8-97ee-6544efb7d43a.png)
660+
The function _exists_ but it does not make the tests pass.
661+
Your _quest_ is to turn this **`0`** into a **`1`**.
662+
663+
Given the **`JSDOC`** comment and _test_ above,
664+
take a moment to think of how _you_ would write
622665
the `add_attributes` function to apply a CSS `class` to an element. <br />
623666
Note: we have _seen_ the code _before_ in the `counter` example.
624667
The difference is this time we want it to be "generic";
625668
we want to apply a CSS `class` to _any_ DOM node.
626669

627670
If you can, make the test _pass_
628671
by writing the `add_attributes` function.
629-
(_don't forget to_ `export` _the function at the end of the file_).
672+
(_don't forget to_ `export` _the function at the bottom of the file_).
630673

631-
If you get "stuck", checkout:
632-
https://github.com/dwyl/learn-elm-architecture-in-javascript/tree/master/examples/todo-list/elmish.js <br />
674+
If you get "stuck", checkout the _complete_ example:
675+
[/examples/todo-list/elmish.js](https://github.com/dwyl/learn-elm-architecture-in-javascript/tree/master/examples/todo-list/elmish.js)
633676

677+
> **Note 1**: it's not "cheating" to look at "the solution",
678+
the whole point of having a step-by-step tutorial
679+
is that you can fill-in any "gaps" if you get "stuck",
680+
but you should only check _after_ making
681+
a good attempt to write the code _yourself_.
682+
<br />
634683

635-
> **Note**: The `add_attributes` function is "impure" as it "mutates"
684+
> **Note 2**: The `add_attributes` function is "impure" as it "mutates"
636685
the target DOM `node`, this is more of a "fact of life" in JavaScript,
637686
and given that the application of attributes
638687
to DOM node(s) is idempotent we aren't "concerned" with "side effects";
639688
the attribute will only be applied _once_ to the node
640689
regardless of how many times the `add_attributes` function is called.
641690
see: https://en.wikipedia.org/wiki/Idempotence
642691

643-
644692
For reference, the Elm HTML Attributes function on Elm package is:
645693
http://package.elm-lang.org/packages/elm-lang/html/2.0.0/Html-Attributes
646694

695+
Once you make the test _pass_ you _should_ see the following in your Terminal:
696+
![image](https://user-images.githubusercontent.com/194400/43416304-d06339da-942f-11e8-9546-06af9c494a45.png)
697+
698+
<!-- Onto the next one! https://vimeo.com/8503138 -->
699+
700+
<br />
701+
647702
#### Input `placeholder` Attribute
648703

649704
The `<input>` form element (_where we create new Todo List items_)
650-
has a helpful `placeholder` _prompting_ us with a question:
705+
has a helpful `placeholder` attribute _prompting_ us with a question:
651706
"_What needs to be done?_"
652707

653708
Add the following test to the `test/elmish.test.js` file: <br />
@@ -666,7 +721,14 @@ test('elmish.add_attributes set placeholder on <input> element', function (t) {
666721
});
667722
```
668723

669-
Write the necessary code to make this test _pass_ in `elmish.js`.
724+
725+
726+
727+
Write the necessary code in the `add_attributes` function of `elmish.js`
728+
to make this test _pass_.
729+
730+
If you get "stuck", checkout the _complete_ example:
731+
[/examples/todo-list/elmish.js](https://github.com/dwyl/learn-elm-architecture-in-javascript/tree/master/examples/todo-list/elmish.js)
670732

671733

672734
#### Input `autofocus`
@@ -771,7 +833,8 @@ test.only('elmish.add_attributes set "for" attribute <label> element', function
771833
});
772834
```
773835

774-
Write the "case" in to make this test _pass_ in `elmish.js`.
836+
Add the "`case`" in the `add_attributes` function's `switch` statement
837+
to make this test _pass_ in `elmish.js`.
775838

776839

777840
#### `<input>` attribute `type`
@@ -1503,7 +1566,7 @@ We are going to make **3 adjustments** to this code
15031566
to use `setItem` and `getItem`,
15041567
but _first_ let's write a ***test*** for the desired outcome!
15051568

1506-
Add the following _test code_ to your `test/elmish.test.js` file: <br />:
1569+
Add the following _test code_ to your `test/elmish.test.js` file: <br />
15071570

15081571
```js
15091572
// Testing localStorage requires a "polyfil" because it's unavailable in JSDOM:

examples/todo-list/elmish.js

+56-56
Original file line numberDiff line numberDiff line change
@@ -35,61 +35,61 @@ function mount(model, update, view, root_element_id) {
3535
view(signal, model, root); // render initial model (once)
3636
localStorage.setItem('elmish_store', JSON.stringify(model)); // save model!
3737
}
38-
//
39-
//
40-
// /**
41-
// * `add_attributes` applies the desired attribute(s) to the specified DOM node.
42-
// * Note: this function is "impure" because it "mutates" the node.
43-
// * however it is idempotent; the "side effect" is only applied once.
44-
// * @param {Array.<String>} attrlist list of attributes to be applied to the node
45-
// * @param {Object} node DOM node upon which attribute(s) should be applied
46-
// * @example
47-
// * // returns node with attributes applied
48-
// * input = add_attributes(["type=checkbox", "id=todo1", "checked=true"], input);
49-
// */
50-
// function add_attributes (attrlist, node) {
51-
// if(attrlist && attrlist.length) {
52-
// attrlist.forEach(function (attr) { // apply all props in array
53-
// var a = attr.split('=');
54-
// switch(a[0]) {
55-
// case 'autofocus':
56-
// node.autofocus = "";
57-
// node.focus();
58-
// break;
59-
// case 'checked':
60-
// node.checked = (a[1] === 'true' ? true : false);
61-
// case 'class':
62-
// node.className = a[1]; // apply CSS classes
63-
// break;
64-
// case 'data-id':
65-
// node.setAttribute('data-id', a[1]); // add data-id e.g: to <li>
66-
// break;
67-
// case 'for':
68-
// node.setAttribute('for', a[1]); // e.g: <label for="toggle-all">
69-
// break;
70-
// case 'href':
71-
// node.href = a[1]; // e.g: <a href="#/active">Active</a>
72-
// break;
73-
// case 'id':
74-
// node.id = a[1]; // apply element id e.g: <input id="toggle-all">
75-
// break;
76-
// case 'placeholder':
77-
// node.placeholder = a[1]; // add placeholder to <input> element
78-
// break;
79-
// case 'style':
80-
// node.setAttribute("style", a[1]); // <div style="display: block;">
81-
// break;
82-
// case 'type':
83-
// node.setAttribute('type', a[1]); // <input id="go" type="checkbox">
84-
// break;
85-
// default:
86-
// break;
87-
// }
88-
// });
89-
// }
90-
// return node;
91-
// }
92-
//
38+
39+
40+
/**
41+
* `add_attributes` applies the desired attribute(s) to the specified DOM node.
42+
* Note: this function is "impure" because it "mutates" the node.
43+
* however it is idempotent; the "side effect" is only applied once.
44+
* @param {Array.<String>} attrlist list of attributes to be applied to the node
45+
* @param {Object} node DOM node upon which attribute(s) should be applied
46+
* @example
47+
* // returns node with attributes applied
48+
* input = add_attributes(["type=checkbox", "id=todo1", "checked=true"], input);
49+
*/
50+
function add_attributes (attrlist, node) {
51+
if(attrlist && attrlist.length) {
52+
attrlist.forEach(function (attr) { // apply all props in array
53+
var a = attr.split('=');
54+
switch(a[0]) {
55+
// case 'autofocus':
56+
// node.autofocus = "";
57+
// node.focus();
58+
// break;
59+
// case 'checked':
60+
// node.checked = (a[1] === 'true' ? true : false);
61+
case 'class':
62+
node.className = a[1]; // apply CSS classes
63+
break;
64+
// case 'data-id':
65+
// node.setAttribute('data-id', a[1]); // add data-id e.g: to <li>
66+
// break;
67+
// case 'for':
68+
// node.setAttribute('for', a[1]); // e.g: <label for="toggle-all">
69+
// break;
70+
// case 'href':
71+
// node.href = a[1]; // e.g: <a href="#/active">Active</a>
72+
// break;
73+
// case 'id':
74+
// node.id = a[1]; // apply element id e.g: <input id="toggle-all">
75+
// break;
76+
// case 'placeholder':
77+
// node.placeholder = a[1]; // add placeholder to <input> element
78+
// break;
79+
// case 'style':
80+
// node.setAttribute("style", a[1]); // <div style="display: block;">
81+
// break;
82+
// case 'type':
83+
// node.setAttribute('type', a[1]); // <input id="go" type="checkbox">
84+
// break;
85+
default:
86+
break;
87+
}
88+
});
89+
}
90+
return node;
91+
}
92+
9393
// /**
9494
// * `append_childnodes` appends an array of HTML elements to a parent DOM node.
9595
// * @param {Array.<Object>} childnodes array of child DOM nodes.
@@ -211,7 +211,7 @@ function mount(model, update, view, root_element_id) {
211211
/* istanbul ignore next */
212212
if (typeof module !== 'undefined' && module.exports) {
213213
module.exports = {
214-
// add_attributes: add_attributes,
214+
add_attributes: add_attributes,
215215
// append_childnodes: append_childnodes,
216216
// a: a,
217217
// button: button,

test/elmish.test.js

+11-11
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,17 @@ test('elmish.mount app expect state to be Zero', function (t) {
6363
// t.end();
6464
// });
6565
//
66-
// test('elmish.add_attributes applies HTML class attribute to el', function (t) {
67-
// const root = document.getElementById(id);
68-
// let div = document.createElement('div');
69-
// div.id = 'divid';
70-
// div = elmish.add_attributes(["class=apptastic"], div);
71-
// root.appendChild(div);
72-
// // test the div has the desired class:
73-
// const nodes = document.getElementsByClassName('apptastic');
74-
// t.equal(nodes.length, 1, "<div> has 'apptastic' CSS class applied");
75-
// t.end();
76-
// });
66+
test('elmish.add_attributes applies HTML class attribute to el', function (t) {
67+
const root = document.getElementById(id);
68+
let div = document.createElement('div');
69+
div.id = 'divid';
70+
div = elmish.add_attributes(["class=apptastic"], div);
71+
root.appendChild(div);
72+
// test the div has the desired class:
73+
const nodes = document.getElementsByClassName('apptastic');
74+
t.equal(nodes.length, 1, "<div> has 'apptastic' CSS class applied");
75+
t.end();
76+
});
7777
//
7878
// test('elmish.add_attributes applies id HTML attribute to a node', function (t) {
7979
// const root = document.getElementById(id);

0 commit comments

Comments
 (0)