diff --git a/package.json b/package.json index a87fa31..b9383bc 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "", "main": "src/server.js", "scripts": { + "start:fe:dev": "webpack-dev-server --hot --inline --content-base static", "test": "tape -r babel-register 'src/**/*spec.js'" }, "babel": { @@ -19,7 +20,10 @@ "babel-preset-stage-0": "^6.5.0" }, "devDependencies": { + "babel-loader": "^6.2.10", "babel-register": "^6.14.0", - "tape": "^4.6.0" + "tape": "^4.6.0", + "webpack": "^1.14.0", + "webpack-dev-server": "^1.16.2" } } diff --git a/src/Todo.js b/src/Todo.js new file mode 100644 index 0000000..e69de29 diff --git a/src/Todo.spec.js b/src/Todo.spec.js new file mode 100644 index 0000000..379735a --- /dev/null +++ b/src/Todo.spec.js @@ -0,0 +1,59 @@ +import test from 'tape'; +import Todo from './Todo'; + +test( 'Todo', t => { + let actual, expected, todo; + let testTodo = { + id: 'test', + title: 'Test', + complete: true, + }; + + todo = Todo( testTodo ); // use new if using a constructor + + actual = todo.getId(); + expected = testTodo.id; + t.equal( actual, expected, 'with object, should store the id' ); + + actual = todo.getTitle(); + expected = testTodo.title; + t.equal( actual, expected, 'with object, should store the title' ); + + actual = todo.isComplete(); + expected = testTodo.complete; + t.equal( actual, expected, 'with object, should store the completion' ); + + todo = Todo( 'Test' ); // use new if using a constructor + + actual = typeof todo.getId(); + expected = 'string'; + t.equal( actual, expected, 'with title, should generate an id' ); + + actual = todo.getTitle(); + expected = 'Test'; + t.equal( actual, expected, 'with title, should store the title' ); + + actual = todo.isComplete(); + expected = false; + t.equal( actual, expected, 'with title, should default to not complete' ); + + todo = Todo( 'Test' ); // use new if using a constructor + todo.toggleComplete(); + actual = todo.isComplete(); + expected = true; + t.equal( actual, expected, 'toggleComplete should complete uncompleted todos' ); + + todo.toggleComplete(); + actual = todo.isComplete(); + expected = false; + t.equal( actual, expected, 'toggleComplete should uncomplete completed todos' ); + + todo = Todo( 'Test' ); // use new if using a constructor + todo.setTitle( 'New' ); + actual = todo.getTitle(); + expected = 'New'; + t.equal( actual, expected, 'setTitle should change the title' ); + + t.end(); +}); + diff --git a/src/TodoApp.js b/src/TodoApp.js new file mode 100644 index 0000000..e69de29 diff --git a/src/TodoApp.spec.js b/src/TodoApp.spec.js new file mode 100644 index 0000000..67a0c8c --- /dev/null +++ b/src/TodoApp.spec.js @@ -0,0 +1,70 @@ +import test from 'tape'; +import TodoApp from './TodoApp'; +import Todo from './Todo'; + +test( 'TodoApp', t => { + let actual, expected, app; + + // app = TodoApp(); // use new if using a constructor + + actual = app.isFiltered(); + expected = false; + t.deepEqual( actual, expected, 'should begin without a filter' ); + + actual = app.getTodos(); + expected = []; + t.deepEqual( actual, expected, 'should begin an empty set of todos' ); + + app.addTodo( 'Test' ); + + actual = app.getTodos().length; + expected = 1; + t.equal( actual, expected, 'addTodo should add a todo' ); + + let [ todo ] = app.getTodos(); + actual = typeof todo.getTitle; + actual = 'function'; + t.ok( actual, 'addTodo should add todos as Todo instances' ); + + actual = todo.getTitle(); + expected = 'Test'; + t.equal( actual, expected, 'addTodo should set the title of the todo to that passed' ); + + app.toggleFilter(); + todo.toggleComplete(); + actual = app.getTodos().length; + expected = 0; + t.equal( actual, expected, 'should use filter to hide todos' ); + + app.toggleFilter(); + actual = app.getTodos().length; + expected = 1; + t.equal( actual, expected, 'should show all todos when filter removed' ); + + app.rmTodo( todo.getId() ); + actual = app.getTodos().length; + expected = 0; + t.equal( actual, expected, 'rmTodo should remove a todo by its id' ); + + app.setTodos([ Todo( '1' ), Todo( '2' ) ]); // use new if using a constructor + actual = app.getTodos().length; + expected = 2; + t.equal( actual, expected, 'setTodos should replace all todos witht those provided' ); + + app = TodoApp(); // use new if using a constructor + app.addTodo({ id: '1', complete: false, title: 'One' }); + app.addTodo({ id: '2', complete: false, title: 'Two' }); + + app.toggleComplete( '1' ); + let [ todo1, todo2 ] = app.getTodos(); + t.equal( todo1.isComplete(), true, 'toggleComplete should toggle the status of the todo' ); + t.equal( todo2.isComplete(), false, 'toggleComplete should not toggle other todos' ); + + app.setTitle( '2', 'New' ); + [ todo1, todo2 ] = app.getTodos(); + t.equal( todo1.getTitle(), 'One', 'setTitle should not change other todos' ); + t.equal( todo2.getTitle(), 'New', 'setTitle should change the title of the todo' ); + + t.end(); +}); + diff --git a/src/index.js b/src/index.js index c1b4585..6928fa7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,41 +1,59 @@ -export function double ( x ) { - return x * 2; -} - -export function doubleXTimes ( x, num ) { - let result = x; - - // i++ === i = i + 1 - // what is the difference between using let and var? - for ( let i = 0; i < num; i++ ) { - // result = result * 2; - result = double( result ); // internal implementation detail - } - - return result; -} - -export const doubleEach = arr => arr.map( x => double( x ) ); - -// export function doubleEach ( arr ) { -// // let result = []; - -// return arr.map( x => double( x ) ); -// // return arr.map( function ( x ) { -// // return double ( x ); -// // }); - -// // arr.forEach( x => result.push( double( x ) ) ); -// // arr.forEach( function ( x, i ) { -// // // result.push( double( x ) ); -// // result.push( double( arr[ i ] ) ); -// // }); - -// // for ( let i = 0; i < arr.length; i++ ) { -// // // result.push( arr[i] * 2 ); -// // result.push( double( arr[i] ) ); -// // } - -// // return result; -// } - +function onReady () { + const addTodoForm = document.getElementById( 'addTodoForm' ); + const todoList = document.getElementById( 'todoList' ); + const newTodoText = document.getElementById( 'newTodoText' ); + + // create todo object array + var todos = [ + { + title: "item1", + complete: false + }, { + title: "item2", + complete: false + }, { + title: "item3", + complete: true + } + ]; + + function renderTheUi ( todos ) { + for (i=0; i < todos.length; i++) { + var newLi = document.createElement("li"); + if (todos[i].complete) { + newLi.classList.add( "todo--complete" ); + } + newLi.textContent = todos[i].title; + todoList.appendChild(newLi).addEventListener( 'click', function() { + this.classList.toggle( 'todo--complete' ); + }); + } + } + + function createNewTodo ( title ) { + todos.push({ title: title, complete: false }); + // re-render + renderTheUi( todos ); + } // end createNewTodo function + + // handle form submission and adding new todo items + addTodoForm.addEventListener( 'submit', function(event) { + event.preventDefault(); + var title = newTodoText.value; + + // add it to the todos array + createNewTodo(title); + + // reset the input form value + newTodoText.value = ''; + }); + + renderTheUi( todos ); +} // end onReady() + +if ( document.readyState !== 'loading' ) { + onReady(); +} else { + document.addEventListener( 'DOMContentLoaded', onReady ); +} + diff --git a/src/spec.js b/src/spec.js index 7a37b51..e69de29 100644 --- a/src/spec.js +++ b/src/spec.js @@ -1,35 +0,0 @@ -import test from 'tape'; -import { double, doubleXTimes, doubleEach } from './index'; - -test( 'double fn', function ( test ) { - // GWT: Given-When-Then - const actual = double( 2 ); - let expected = 4; - - test.equal( actual, expected, 'should double the number' ); - - test.end(); -}); - -test( 'doubleXTimes', function ( test ) { - const actual = doubleXTimes( 2, 4 ); - const expected = 32; - - test.equal( actual, expected, 'should double the number four times' ); - - test.end(); -}); - -test( 'doubleEach', function ( test ) { - const actual = doubleEach([ 0, 1, 2, 3, 4 ]); - const expected = [ 0, 2, 4, 6, 8 ]; - - // { one: 1 } !== { one: 1 } - // { one: { two: 2 } } !== { one: { two: 2 } } - // but they are deeply equal - - test.deepEqual( actual, expected, 'should double the number four times' ); - - test.end(); -}); - diff --git a/src/spy.js b/src/spy.js new file mode 100644 index 0000000..061dd8a --- /dev/null +++ b/src/spy.js @@ -0,0 +1,46 @@ +export const spyOn = ( target, method ) => { + let spy = { + calls: [], + + reset () { + this.calls = []; + }, + }; + + const oldMethod = target[ method ]; + target[ method ] = ( ...args ) => { + spy.calls.push({ + args: args, + }); + + return oldMethod.apply( target, args ); + }; + + return spy; +}; + +export const createSpy = oldMethod => { + let spy; + + spy = function ( ...args ) { + spy.calls.push({ + args: args, + }); + + spy.called = true; + + if ( oldMethod ) { + return oldMethod( ...args ); + } + }; + + spy.reset = () => { + spy.calls = []; + spy.called = false; + }; + + spy.reset(); + + return spy; +}; + diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..f17bfce --- /dev/null +++ b/static/index.html @@ -0,0 +1,6 @@ +      +    Todo App           

The Todo App!

+   
                     
+    +    +      diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..13561ff --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,31 @@ +var path = require( 'path' ); +var webpack = require( 'webpack' ); + +module.exports = { + devtool: 'source-map', + + entry: './src', + + output: { + path: path.join( __dirname, 'dist' ), + filename: 'bundle.js', + publicPath: '/static/', + }, + + plugins: [ + new webpack.HotModuleReplacementPlugin(), + new webpack.optimize.OccurrenceOrderPlugin(), + new webpack.optimize.UglifyJsPlugin({ sourceMap: true }), + ], + + module: { + loaders: [ + { + test: /\.js$/, + loaders: [ 'babel-loader' ], + include: path.join( __dirname, 'src' ), + }, + ], + } +}; +