Skip to content

Commit 77546af

Browse files
committed
add *failing* test for DELETE an item #59
1 parent a47b24c commit 77546af

File tree

5 files changed

+134
-28
lines changed

5 files changed

+134
-28
lines changed

Diff for: examples/counter-basic/index.html

+15-15
Original file line numberDiff line numberDiff line change
@@ -95,22 +95,22 @@
9595
<script src="../counter-basic-test/test.js"></script> <!-- load test.js last -->
9696

9797
<div onclick="doubleclick(this, function(){alert('single')}, function(){alert('double')})">click me</div>
98-
<script>
99-
function doubleclick(el, onsingle, ondouble) {
100-
if (el.getAttribute("data-dblclick") == null) {
101-
el.setAttribute("data-dblclick", 1);
102-
setTimeout(function () {
103-
if (el.getAttribute("data-dblclick") == 1) {
104-
onsingle();
105-
}
106-
el.removeAttribute("data-dblclick");
107-
}, 300);
108-
} else {
109-
el.removeAttribute("data-dblclick");
110-
ondouble();
111-
}
98+
<script>
99+
function doubleclick(el, onsingle, ondouble) {
100+
if (el.getAttribute("data-dblclick") == null) {
101+
el.setAttribute("data-dblclick", 1);
102+
setTimeout(function () {
103+
if (el.getAttribute("data-dblclick") == 1) {
104+
onsingle();
112105
}
113-
</script>
106+
el.removeAttribute("data-dblclick");
107+
}, 300);
108+
} else {
109+
el.removeAttribute("data-dblclick");
110+
ondouble();
111+
}
112+
}
113+
</script>
114114

115115
</body>
116116
</html>

Diff for: examples/todo-list/elmish.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function mount (model, update, view, root_element_id, subscriptions) {
3131
root.appendChild(view(mod, sig)) // render view based on model & signal
3232
}
3333

34-
function signal(action, data) { // signal function takes action
34+
function signal(action, data, model) { // signal function takes action
3535
return function callback() { // and returns callback
3636
model = JSON.parse(localStorage.getItem(store_name)) //|| model;
3737
var updatedModel = update(action, model, data); // update model for the action

Diff for: examples/todo-list/todo-app.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ var initial_model = {
1313
/**
1414
* `update` transforms the `model` based on the `action`.
1515
* @param {String} action - the desired action to perform on the model.
16-
* @param {Object} model - the App's (current) model (or "state").
1716
* @param {String} data - the data we want to "apply" to the item.
17+
* @param {Object} model - the App's (current) model (or "state").
1818
* @return {Object} updated_model - the transformed model.
1919
*/
2020
function update(action, model, data) {
21+
console.log(arguments)
22+
console.log(' - - - - - - - - - - - ');
2123
var new_model = JSON.parse(JSON.stringify(model)) // "clone" the model
2224
switch(action) { // and an action (String) runs a switch
2325
case 'ADD':
@@ -51,6 +53,9 @@ function update(action, model, data) {
5153
item.done = new_model.all_done;
5254
});
5355
break;
56+
// case 'DELETE':
57+
// console.log('DELETE', data)
58+
// break;
5459
default: // if action unrecognised or undefined,
5560
return model; // return model unmodified
5661
} // see: https://softwareengineering.stackexchange.com/a/201786/211301
@@ -66,12 +71,14 @@ function update(action, model, data) {
6671
* + `<button class="destroy">` lets people "delete" a todo item.
6772
* see: https://github.com/dwyl/learn-elm-architecture-in-javascript/issues/52
6873
* @param {Object} item the todo item object
74+
* @param {Object} model - the App's (current) model (or "state").
75+
* @param {Function} singal - the Elm Architicture "dispacher" which will run
6976
* @return {Object} <li> DOM Tree which is nested in the <ul>.
7077
* @example
7178
* // returns <li> DOM element with <div>, <input>. <label> & <button> nested
7279
* var DOM = render_item({id: 1, title: "Build Todo List App", done: false});
7380
*/
74-
function render_item (item, signal) {
81+
function render_item (item, model, signal) {
7582
return (
7683
li([
7784
"data-id=" + item.id,
@@ -87,7 +94,7 @@ function render_item (item, signal) {
8794
],
8895
[]), // <input> does not have any nested elements
8996
label([], [text(item.title)]),
90-
button(["class=destroy"])
97+
button(["class=destroy", ]) // signal('DELETE', 'blah!', model)])
9198
]) // </div>
9299
]) // </li>
93100
)
@@ -97,6 +104,7 @@ function render_item (item, signal) {
97104
* `render_main` renders the `<section class="main">` of the Todo List App
98105
* which contains all the "main" controls and the `<ul>` with the todo items.
99106
* @param {Object} model - the App's (current) model (or "state").
107+
* @param {Function} singal - the Elm Architicture "dispacher" which will run
100108
* @return {Object} <section> DOM Tree which containing the todo list <ul>, etc.
101109
*/
102110
function render_main (model, signal) {
@@ -114,8 +122,9 @@ function render_main (model, signal) {
114122
label(["for=toggle-all"], [ text("Mark all as complete") ]),
115123
ul(["class=todo-list"],
116124
(model.todos && model.todos.length > 0) ?
117-
model.todos.map(function (item) { return render_item(item, signal) })
118-
: null
125+
model.todos.map(function (item) {
126+
return render_item(item, model, signal)
127+
}) : null
119128
) // </ul>
120129
]) // </section>
121130
)
@@ -126,6 +135,7 @@ function render_main (model, signal) {
126135
* which contains count of items to (still) to be done and a `<ul>` "menu"
127136
* with links to filter which todo items appear in the list view.
128137
* @param {Object} model - the App's (current) model (or "state").
138+
* @param {Function} singal - the Elm Architicture "dispacher" which will run
129139
* @return {Object} <section> DOM Tree which containing the <footer> element.
130140
* @example
131141
* // returns <footer> DOM element with other DOM elements nested:
@@ -183,6 +193,7 @@ function render_footer (model, signal) {
183193
* which contains count of items to (still) to be done and a `<ul>` "menu"
184194
* with links to filter which todo items appear in the list view.
185195
* @param {Object} model - the App's (current) model (or "state").
196+
* @param {Function} singal - the Elm Architicture "dispacher" which will run
186197
* @return {Object} <section> DOM Tree which containing all other DOM elements.
187198
* @example
188199
* // returns <section class="todo-app"> DOM element with other DOM els nested:

Diff for: test/todo-app.test.js

+14-6
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ test('render_item HTML for a single Todo Item', function (t) {
6767
hash: '#/' // the "route" to display
6868
};
6969
// render the ONE todo list item:
70-
document.getElementById(id).appendChild(app.render_item(model.todos[0]))
70+
document.getElementById(id).appendChild(
71+
app.render_item(model.todos[0]),
72+
)
7173

7274
const done = document.querySelectorAll('.completed')[0].textContent;
7375
t.equal(done, 'Learn Elm Architecture', 'Done: Learn "TEA"');
@@ -307,7 +309,7 @@ test('3. Mark all as completed ("TOGGLE_ALL")', function (t) {
307309
t.end();
308310
});
309311

310-
test('4. Item, should allow me to mark items as complete', function (t) {
312+
test('4. DELETE an item', function (t) {
311313
elmish.empty(document.getElementById(id));
312314
localStorage.removeItem('elmish_' + id);
313315
const model = {
@@ -337,7 +339,7 @@ test('4. Item, should allow me to mark items as complete', function (t) {
337339
t.end();
338340
});
339341

340-
test.skip('4.1 Item, should allow me to edit an item ("EDIT")' , function (t) {
342+
test.only('4.1 DELETE item by clicking <button class="destroy">', function (t) {
341343
elmish.empty(document.getElementById(id));
342344
localStorage.removeItem('elmish_' + id);
343345
const model = {
@@ -348,10 +350,16 @@ test.skip('4.1 Item, should allow me to edit an item ("EDIT")' , function (t) {
348350
};
349351
// render the view and append it to the DOM inside the `test-app` node:
350352
elmish.mount(model, app.update, app.view, id, app.subscriptions);
353+
// const todo_count = ;
354+
t.equal(document.querySelectorAll('.destroy').length, 1, "one destroy button")
355+
351356
const item = document.getElementById('0')
352357
t.equal(item.textContent, model.todos[0].title, 'Item contained in model.');
353-
// confirm that the todo item is NOT done (done=false):
354-
355-
358+
// DELETE the item by clicking on the <button class="destroy">:
359+
const button = item.querySelectorAll('button.destroy')[0];
360+
button.click()
361+
// confirm that there is no loger a <button class="destroy">
362+
t.equal(document.querySelectorAll('button.destroy').length, 0,
363+
'there is no loger a <button class="destroy"> as the only item was DELETEd')
356364
t.end();
357365
});

Diff for: todo-list.md

+88-1
Original file line numberDiff line numberDiff line change
@@ -1781,13 +1781,100 @@ function render_item (item, signal) {
17811781
```
17821782

17831783

1784-
17851784
#### 4.1 `DELETE` an Item
17861785

17871786
```
17881787
should show the remove button on hover
17891788
```
17901789

1790+
##### Acceptance Criteria
1791+
1792+
+ [ ] should show the `<button class="destroy">`
1793+
on hover (over the item) ... thankfully the TodoMVC CSS
1794+
handles this for us, we just need our `view`
1795+
to render the `<button>`
1796+
+ [ ] Clicking/tapping the `<button class="destroy">`
1797+
sends the `signal('DELETE', todo.id, model)`
1798+
+ [ ] The `DELETE` update case receives the `todo.id`
1799+
and removes it from the `model.todos` Array.
1800+
1801+
In order to `DELETE` an item from the `model.todos` Array,
1802+
we need to "_supply_" the `model` when invoking the `signal`
1803+
function. This means we need to _extend_ the `signal` function's
1804+
parameters to include the `model` as an _optional_ argument.
1805+
1806+
In the `elmish.js` file, locate the `mount` function
1807+
and add a `model` parameter to the `signal` function definition.
1808+
Thus it should change from:
1809+
```js
1810+
function signal(action, data) { // signal function takes action
1811+
return function callback() { // and returns callback
1812+
model = JSON.parse(localStorage.getItem(store_name)) //|| model;
1813+
var updatedModel = update(action, model, data); // update model for the action
1814+
render(updatedModel, signal, ROOT);
1815+
};
1816+
};
1817+
```
1818+
1819+
To:
1820+
```js
1821+
function signal(action, data, model) { // signal function takes action
1822+
return function callback() { // and returns callback
1823+
model = JSON.parse(localStorage.getItem(store_name)) //|| model;
1824+
var updatedModel = update(action, model, data); // update model for the action
1825+
render(updatedModel, signal, ROOT);
1826+
};
1827+
};
1828+
```
1829+
1830+
Thankfully, we have a "bank" (or "suite") of tests that cover
1831+
all of our existing functionality,
1832+
so when we make a change like this
1833+
we can _confirm_ that all _existing_ functionality
1834+
still works as expected.
1835+
1836+
Run the tests: `npm test` and ensure they all still pass
1837+
after making the change to the `signal` function. (_they should!_)
1838+
1839+
![tests-still-passing](https://user-images.githubusercontent.com/194400/44953303-a109ce00-ae93-11e8-8f0c-c271832df3a5.png)
1840+
1841+
1842+
##### `DELETE` Item _Test_
1843+
1844+
Append following test code to your `test/todo-app.test.js` file:
1845+
1846+
```js
1847+
test.only('4.1 DELETE item by clicking <button class="destroy">', function (t) {
1848+
elmish.empty(document.getElementById(id));
1849+
localStorage.removeItem('elmish_' + id);
1850+
const model = {
1851+
todos: [
1852+
{ id: 0, title: "Make something people want.", done: false }
1853+
],
1854+
hash: '#/' // the "route" to display
1855+
};
1856+
// render the view and append it to the DOM inside the `test-app` node:
1857+
elmish.mount(model, app.update, app.view, id, app.subscriptions);
1858+
// const todo_count = ;
1859+
t.equal(document.querySelectorAll('.destroy').length, 1, "one destroy button")
1860+
1861+
const item = document.getElementById('0')
1862+
t.equal(item.textContent, model.todos[0].title, 'Item contained in model.');
1863+
// DELETE the item by clicking on the <button class="destroy">:
1864+
const button = item.querySelectorAll('button.destroy')[0];
1865+
button.click()
1866+
// confirm that there is no loger a <button class="destroy">
1867+
t.equal(document.querySelectorAll('button.destroy').length, 0,
1868+
'there is no loger a <button class="destroy"> as the only item was DELETEd')
1869+
t.end();
1870+
});
1871+
```
1872+
1873+
If you run the tests `node test/todo-app.test.js`
1874+
you should now see:
1875+
![delete-test-one-assertion-failing](https://user-images.githubusercontent.com/194400/44953479-21313300-ae96-11e8-971a-51757702bacc.png)
1876+
1877+
17911878

17921879
<!--
17931880

0 commit comments

Comments
 (0)