From db87308aa7b58c2af7374825928fb7086f0e9166 Mon Sep 17 00:00:00 2001 From: giamir Date: Fri, 29 Jan 2016 17:16:02 +0000 Subject: [PATCH 01/10] setup environment --- .gitignore | 3 +++ CONTRIBUTING.md | 10 -------- Gruntfile.js | 45 ++++++++++++++++++++++++++++++++ Procfile | 1 + app.js | 16 ++++++++++++ bower.json | 28 ++++++++++++++++++++ common_issues.md | 14 ---------- css/style.css | 0 index.html | 17 ++++++++++++ js/app.js | 1 + package.json | 50 ++++++++++++++++++++++++++++++++++++ tests/e2e/conf.js | 19 ++++++++++++++ tests/e2e/todoListFeature.js | 0 tests/karma.conf.js | 43 +++++++++++++++++++++++++++++++ 14 files changed, 223 insertions(+), 24 deletions(-) create mode 100644 .gitignore delete mode 100644 CONTRIBUTING.md create mode 100644 Gruntfile.js create mode 100644 Procfile create mode 100644 app.js create mode 100644 bower.json delete mode 100644 common_issues.md create mode 100644 css/style.css create mode 100644 index.html create mode 100644 js/app.js create mode 100644 package.json create mode 100644 tests/e2e/conf.js create mode 100644 tests/e2e/todoListFeature.js create mode 100644 tests/karma.conf.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..526efb4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +bower_components +coverage diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 8fdc4773..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,10 +0,0 @@ -# Friday Challenge Submission Guidelines - -Before submitting, please review the requirements/guidelines below. - -* Try to set up [Travis CI](https://travis-ci.org) on your own repo and add a [status badge](http://docs.travis-ci.com/user/status-images/) to your README showing that all tests are passing - make sure it passes our own CI when you submit your PR. -* Make sure you have written your own README that briefly explains your approach to solving the challenge. -* If your code isn't finished, explain in your README how far you got and how you would plan to finish the challenge. -* All code **must** be written test-first - we're looking for 100% test coverage or as near as possible to that figure. -* Ensure you've understood the specification and built the code according to the challenge guidelines. -* Read through [Code Reviews](https://github.com/makersacademy/course/blob/master/pills/code_reviews.md):pill: to understand what we're looking for in your code. diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..00e108e5 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,45 @@ +module.exports = function(grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + express: { + options: { + port:8080, + script: 'app.js' + }, + run: { + } + }, + karma: { + options: { + configFile: './tests/karma.conf.js' + }, + run: { + } + }, + protractor: { + options: { + configFile: './tests/e2e/conf.js' + }, + run: { + } + }, + protractor_webdriver: { + start: { + options: { + path: 'node_modules/protractor/bin/', + command: 'webdriver-manager start' + } + } + } + }); + + grunt.loadNpmTasks('grunt-express-server'); + grunt.loadNpmTasks('grunt-karma'); + grunt.loadNpmTasks('grunt-protractor-runner'); + grunt.loadNpmTasks('grunt-protractor-webdriver'); + + grunt.registerTask('test', ['karma', 'e2e']); + grunt.registerTask('unit', ['karma']); + grunt.registerTask('e2e', ['express', 'protractor_webdriver', 'protractor']); + +}; diff --git a/Procfile b/Procfile new file mode 100644 index 00000000..e1d4131b --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: node app.js diff --git a/app.js b/app.js new file mode 100644 index 00000000..01aebc73 --- /dev/null +++ b/app.js @@ -0,0 +1,16 @@ +var express = require('express'); +// var logger = require('morgan'); +var app = express(); + +app.set('port', (process.env.PORT || 8080)); + +// app.use(logger('dev')); +app.use(express.static(__dirname)); + +app.get('/', function(req, res) { + res.render('index.html'); +}); + +app.listen(app.get('port'), function() { + console.log('Node app is running on port', app.get('port')); +}); diff --git a/bower.json b/bower.json new file mode 100644 index 00000000..2c52294e --- /dev/null +++ b/bower.json @@ -0,0 +1,28 @@ +{ + "name": "github-profiles", + "homepage": "https://github.com/giamir/todo_challenge", + "authors": [ + "giamir " + ], + "description": "Todo list mini front-end application with Angular.js", + "main": "", + "moduleType": [], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "jquery": "~2.2.0", + "bootstrap": "~3.3.6", + "angular": "~1.4.9", + "angular-resource": "~1.4.9" + }, + "devDependencies": { + "angular-mocks": "~1.4.9", + "angular-route": "~1.4.9" + } +} diff --git a/common_issues.md b/common_issues.md deleted file mode 100644 index 2dcbc6da..00000000 --- a/common_issues.md +++ /dev/null @@ -1,14 +0,0 @@ -#Common issues - ToDo List - - * Tests with no expectation - * Tests with multiple expectation - * No README.md - * Incorrect installation instructions in the README - * If using a framework, delete any unnecessary code - * Avoid ```
``` - * No integration tests, or poor test coverage - * Over-reliance on online tutorials - * Not using controllerAs syntax - * not using preferred array syntax for dependency injection (to avoid minify issues) - * make use of available angular directives - e.g. checkboxes - * make use of filters where appropriate diff --git a/css/style.css b/css/style.css new file mode 100644 index 00000000..e69de29b diff --git a/index.html b/index.html new file mode 100644 index 00000000..9d98c785 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + ~Todo List + + + + + + + + + + + + diff --git a/js/app.js b/js/app.js new file mode 100644 index 00000000..148358c7 --- /dev/null +++ b/js/app.js @@ -0,0 +1 @@ +var todoList = angular.module('TodoList', ['ngResource']); diff --git a/package.json b/package.json new file mode 100644 index 00000000..561ab151 --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "todo_challenge", + "version": "1.0.0", + "description": "Todo list mini front-end application with Angular.js", + "main": "app.js", + "directories": { + "doc": "docs" + }, + "scripts": { + "start": "nodemon app.js", + "test": "grunt test", + "postinstall": "bower install" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/giamir/todo_challenge.git" + }, + "author": "Giamir Buoncristiani", + "license": "ISC", + "bugs": { + "url": "https://github.com/giamir/todo_challenge/issues" + }, + "homepage": "https://github.com/giamir/todo_challenge#readme", + "devDependencies": { + "grunt": "^0.4.5", + "grunt-express-server": "^0.5.1", + "grunt-karma": "^0.12.1", + "grunt-protractor-runner": "^3.0.0", + "grunt-protractor-webdriver": "^0.2.5", + "jasmine-core": "^2.4.1", + "jasmine-reporters": "^2.1.1", + "karma": "^0.13.19", + "karma-chrome-launcher": "^0.2.2", + "karma-coverage": "^0.5.3", + "karma-jasmine": "^0.3.6", + "karma-phantomjs-launcher": "^0.2.3", + "karma-spec-reporter": "0.0.23", + "nodemon": "^1.8.1", + "phantomjs": "^1.9.19", + "protractor": "^3.0.0", + "protractor-http-mock": "^0.2.1", + "selenium-standalone": "^4.8.0", + "webdriver-manager": "^8.0.0" + }, + "dependencies": { + "bower": "^1.7.7", + "express": "^4.13.4", + "morgan": "^1.6.1" + } +} diff --git a/tests/e2e/conf.js b/tests/e2e/conf.js new file mode 100644 index 00000000..815262fc --- /dev/null +++ b/tests/e2e/conf.js @@ -0,0 +1,19 @@ +exports.config = { + + seleniumAddress: 'http://localhost:4444/wd/hub', + capabilities: { 'browserName': 'chrome' }, + specs: ['todoListFeature.js'], + + jasmineNodeOpts: { + showColors: true + }, + + onPrepare: function() { + var jasmineReporters = require('jasmine-reporters'); + jasmine.getEnv().addReporter(new jasmineReporters.TerminalReporter({ + verbosity: 3, + color: true, + showStack: true + })); + } +}; diff --git a/tests/e2e/todoListFeature.js b/tests/e2e/todoListFeature.js new file mode 100644 index 00000000..e69de29b diff --git a/tests/karma.conf.js b/tests/karma.conf.js new file mode 100644 index 00000000..782d86e4 --- /dev/null +++ b/tests/karma.conf.js @@ -0,0 +1,43 @@ +module.exports = function(config) { + config.set({ + + basePath: '../', + frameworks: ['jasmine'], + files: [ + 'bower_components/angular/angular.js', + 'bower_components/angular-route/angular-route.js', + 'bower_components/angular-resource/angular-resource.js', + 'bower_components/angular-mocks/angular-mocks.js', + 'js/**/*.js', + 'test/mocks/*.js', + 'test/**/*.spec.js' + ], + + exclude: [], + + preprocessors: { + 'js/**/*.js': ['coverage'] + }, + + reporters: ['spec', 'coverage'], + + port: 9876, + + colors: true, + + logLevel: config.LOG_INFO, + + autoWatch: true, + + // browsers: ['Chrome'], + browsers: ['PhantomJS'], + + singleRun: true, + + coverageReporter: { + type : 'html', + dir : 'coverage/' + } + + }); +}; From 57005b41e3f13056b47d97bc1406f2cd37a62a0f Mon Sep 17 00:00:00 2001 From: giamir Date: Sun, 31 Jan 2016 12:28:24 +0000 Subject: [PATCH 02/10] add HTML skeleton --- .jshintrc | 10 +++++----- css/style.css | 20 ++++++++++++++++++++ index.html | 43 ++++++++++++++++++++++++++++++++++++++++--- js/app.js | 2 +- package.json | 1 + tests/e2e/conf.js | 8 ++------ tests/karma.conf.js | 4 ++-- 7 files changed, 71 insertions(+), 17 deletions(-) diff --git a/.jshintrc b/.jshintrc index cd595a55..6e44d67c 100644 --- a/.jshintrc +++ b/.jshintrc @@ -72,13 +72,13 @@ "couch" : false, // CouchDB "devel" : true, // Development/debugging (alert, confirm, etc) "dojo" : false, // Dojo Toolkit - "jasmine" : false, // Jasmine - "jquery" : false, // jQuery + "jasmine" : true, // Jasmine + "jquery" : true, // jQuery "mocha" : true, // Mocha "mootools" : false, // MooTools - "node" : false, // Node.js + "node" : true, // Node.js "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) - "phantom" : false, // PhantomJS + "phantom" : true, // PhantomJS "prototypejs" : false, // Prototype and Scriptaculous "qunit" : false, // QUnit "rhino" : false, // Rhino @@ -89,5 +89,5 @@ "yui" : false, // Yahoo User Interface // Custom Globals - "globals" : { "toDoList": true, "angular": true } // additional predefined global variables + "globals" : { "toDoList": true, "angular": true, "karma": true } // additional predefined global variables } diff --git a/css/style.css b/css/style.css index e69de29b..652c166d 100644 --- a/css/style.css +++ b/css/style.css @@ -0,0 +1,20 @@ +@media (min-width: 1200px) { + .container{ + max-width: 500px; + } +} + +form[name="insertTask"] { +margin-top: 50px; +} + +form[name="insertTask"] input{ + width: 100%; + margin: 20px 0; + padding: 10px; + font-size: 16px; +} + +.items-result li{ + margin: 10px 0; +} diff --git a/index.html b/index.html index 9d98c785..9855878e 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - ~Todo List + Todo List @@ -11,7 +11,44 @@ - - + +
+
+
+ +
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
+
+ 4 items left +
+ + + +
+ Clear All +
+
diff --git a/js/app.js b/js/app.js index 148358c7..0607b385 100644 --- a/js/app.js +++ b/js/app.js @@ -1 +1 @@ -var todoList = angular.module('TodoList', ['ngResource']); +var toDoList = angular.module('ToDoList', ['ngResource']); diff --git a/package.json b/package.json index 561ab151..7e9c89dd 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "grunt-protractor-webdriver": "^0.2.5", "jasmine-core": "^2.4.1", "jasmine-reporters": "^2.1.1", + "jasmine-spec-reporter": "^2.4.0", "karma": "^0.13.19", "karma-chrome-launcher": "^0.2.2", "karma-coverage": "^0.5.3", diff --git a/tests/e2e/conf.js b/tests/e2e/conf.js index 815262fc..50349e92 100644 --- a/tests/e2e/conf.js +++ b/tests/e2e/conf.js @@ -9,11 +9,7 @@ exports.config = { }, onPrepare: function() { - var jasmineReporters = require('jasmine-reporters'); - jasmine.getEnv().addReporter(new jasmineReporters.TerminalReporter({ - verbosity: 3, - color: true, - showStack: true - })); + var SpecReporter = require('jasmine-spec-reporter'); + jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'all'})); } }; diff --git a/tests/karma.conf.js b/tests/karma.conf.js index 782d86e4..9728d780 100644 --- a/tests/karma.conf.js +++ b/tests/karma.conf.js @@ -9,8 +9,8 @@ module.exports = function(config) { 'bower_components/angular-resource/angular-resource.js', 'bower_components/angular-mocks/angular-mocks.js', 'js/**/*.js', - 'test/mocks/*.js', - 'test/**/*.spec.js' + 'tests/mocks/*.js', + 'tests/**/*.spec.js' ], exclude: [], From 282d809e20289f9a0323a2351038e53a015b73f2 Mon Sep 17 00:00:00 2001 From: giamir Date: Sun, 31 Jan 2016 23:52:20 +0000 Subject: [PATCH 03/10] update README --- .jshintrc | 15 +++- README.md | 72 ++++++++-------- index.html | 47 ++++------ js/toDoListController.js | 45 ++++++++++ js/toDoListStorageFactory.js | 39 +++++++++ tests/e2e/conf.js | 5 +- tests/e2e/todoListFeature.js | 90 +++++++++++++++++++ tests/toDoListController.spec.js | 124 +++++++++++++++++++++++++++ tests/toDoListStorageFactory.spec.js | 69 +++++++++++++++ 9 files changed, 436 insertions(+), 70 deletions(-) create mode 100644 js/toDoListController.js create mode 100644 js/toDoListStorageFactory.js create mode 100644 tests/toDoListController.spec.js create mode 100644 tests/toDoListStorageFactory.spec.js diff --git a/.jshintrc b/.jshintrc index 6e44d67c..a3bbeb73 100644 --- a/.jshintrc +++ b/.jshintrc @@ -88,6 +88,17 @@ "wsh" : false, // Windows Scripting Host "yui" : false, // Yahoo User Interface - // Custom Globals - "globals" : { "toDoList": true, "angular": true, "karma": true } // additional predefined global variables + "globals" : { + "toDoList": true, + "angular": true, + "inject": true, + "protractor": true, + "browser": true, + "by": true, + "element": true, + "it": true, + "describe": true, + "beforeEach": true, + "afterEach": true + } } diff --git a/README.md b/README.md index 29dc834e..1e8bab93 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,9 @@ -# Todo Challenge +Todo Challenge [![Build Status](https://travis-ci.org/giamir/todo_challenge.svg?branch=master)](https://travis-ci.org/giamir/todo_challenge) +=============== +A Todo list as a mini Angular.js front-end application (builded with a TDD approach). -* Deadline: submit completed pull request by 9am on Monday -* You may use whatever level of JavaScript you feel comfortable with - pure JS, jQuery, Angular, or whatever weird and wonderful framework you want to try. Extra points for DogeScript - -Steps -------- - -1. Fill out your learning plan self review for the week: https://github.com/makersacademy/learning_plan -2. Fork this repo, and clone to your local machine -3. Complete the following challenge: - -## Challenge - -![Todo mockup](https://makersacademy.mybalsamiq.com/mockups/2914603.png?key=afabb09aef2901a2732515ae4349c1ec0458294b) - -Build a Todo list as a mini front-end application. You don't have to use a database, the front-end is more important - you can use an appropriate data structure stored somewhere in your JavaScript (this time only!) - -Here are the core user stories: +User Stories +------------ ``` As a forgetful person @@ -30,11 +17,7 @@ So that I have more time to think about other things As a person who actually gets stuff done I want to mark my tasks as done So that I don't do them twice -``` - -Here are some other user stories you may choose to implement: -``` As a person with a lot of tasks I want to be able to filter my tasks by "All", "Active", "Complete" So that I only see the relevant tasks @@ -48,26 +31,41 @@ I want to be able to clear my completed tasks So I never see them again ``` -As you may imagine, implementing a To-do list is very much a solved problem. However, we are mainly interested in seeing how you approach testing and design. We are looking for: +Installation +------------ +You can try the app remotely: +>[https://todolist-giamir.herokuapp.com](https://todolist-giamir.herokuapp.com) -* well written, well structured acceptance and unit tests -* clear and expressive JavaScript -* good HTML5 markup +or install it locally: +``` +$ git clone git@github.com:giamir/todo_challenge.git +$ npm install +``` +Visit: [http://localhost:8080](http://localhost:8080) on your machine. -Don't worry about deployment, and make sure you read the CONTRIBUTING.md when submitting a pull request. +Testing +------------- -## Extensions +### To run tests: +- ```$ npm test``` -* Deploy the app -* Create a persistance layer (e.g. MongoDB), or use LocalStorage or the filesystem through Node -* Make it look purdy (CSS) - try a framework like Bootstrap or Foundation +### To run e2e tests: +- ```$ grunt e2e``` -## CI +### To run unit tests: +- ```$ grunt karma``` -Read the `.travis.yml` if any of the steps below don't make sense! +Technologies +------------- -* Make sure you have set up `npm test` in your `package.json` so that it runs your Karma tests -* Make sure you have your Protractor config file at `e2e/conf.js` -* Make sure `npm start` spins up whatever serves up your app - `http-server`, Sinatra or Node +- Angular.js +- Node.js +- Express +- Karma +- Protractor +- Jasmine +- Grunt -Good luck! +Contributors +------------- +[Giamir Buoncristiani](https://github.com/giamir) diff --git a/index.html b/index.html index 9855878e..13b37ce4 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + Todo List @@ -9,45 +9,34 @@ + + - +
-
- + +
-
    -
  • +
      +
    • - -
    • -
    • - - -
    • -
    • - - +
    - 4 items left -
    - - - + {{ctrl.remainingCount()}} + +
    + + +
    - Clear All + Clear All
diff --git a/js/toDoListController.js b/js/toDoListController.js new file mode 100644 index 00000000..e27707c9 --- /dev/null +++ b/js/toDoListController.js @@ -0,0 +1,45 @@ +toDoList.controller('ToDoListController', ['ToDoListStorage', function(ToDoListStorage) { + var self = this; + self.newItem = undefined; + self.items = ToDoListStorage.items; + self.filteredItems = self.items; + + self.addItem = function() { + var newItem = { + title: self.newItem.trim(), + completed: false + }; + if (!newItem.title) { return; } + ToDoListStorage.insert(newItem) + .then(function success() { + self.newItem = undefined; + }); + }; + + self.toggleCompleted = function (item, completed) { + if (angular.isDefined(completed)) { item.completed = completed; } + ToDoListStorage.put(item, self.items.indexOf(item)) + }; + + self.removeItem = function (item) { + ToDoListStorage.delete(item); + }; + + self.filter = function(type) { + self.filteredItems = self.items.filter(function(item) { + if (type === 'active') { return (!item.completed); } + if (type === 'completed') { return (item.completed); } + return item; + }); + } + + self.remainingCount = function() { + return self.items.filter(function(item) { + return (!item.completed); + }).length; + }; + + self.clearCompletedItems = function () { + ToDoListStorage.clearCompleted(); + }; +}]); diff --git a/js/toDoListStorageFactory.js b/js/toDoListStorageFactory.js new file mode 100644 index 00000000..ea53f4b3 --- /dev/null +++ b/js/toDoListStorageFactory.js @@ -0,0 +1,39 @@ +toDoList.factory('ToDoListStorage', ['$q', function($q) { + + var store = { + items: [], + + clearCompleted: function () { + var deferred = $q.defer(); + var incompleteItems = store.items.filter(function (item) { + return !item.completed; + }); + angular.copy(incompleteItems, store.items); + deferred.resolve(store.items); + return deferred.promise; + }, + + delete: function (item) { + var deferred = $q.defer(); + store.items.splice(store.items.indexOf(item), 1); + deferred.resolve(store.items); + return deferred.promise; + }, + + insert: function (item) { + var deferred = $q.defer(); + store.items.push(item); + deferred.resolve(store.items); + return deferred.promise; + }, + + put: function (item, index) { + var deferred = $q.defer(); + store.items[index] = item; + deferred.resolve(store.items); + return deferred.promise; + } + }; + + return store; +}]); diff --git a/tests/e2e/conf.js b/tests/e2e/conf.js index 50349e92..f3adc4cb 100644 --- a/tests/e2e/conf.js +++ b/tests/e2e/conf.js @@ -2,10 +2,11 @@ exports.config = { seleniumAddress: 'http://localhost:4444/wd/hub', capabilities: { 'browserName': 'chrome' }, - specs: ['todoListFeature.js'], + specs: ['toDoListFeature.js'], jasmineNodeOpts: { - showColors: true + showColors: true, + print: function() {} }, onPrepare: function() { diff --git a/tests/e2e/todoListFeature.js b/tests/e2e/todoListFeature.js index e69de29b..c786f05f 100644 --- a/tests/e2e/todoListFeature.js +++ b/tests/e2e/todoListFeature.js @@ -0,0 +1,90 @@ +describe('Todo List', function() { + + var newItemBox = element(by.model('ctrl.newItem')); + + beforeEach(function() { + browser.get('http://localhost:8080'); + }); + + it('has a title', function() { + expect(browser.getTitle()).toEqual('Todo List'); + }); + + describe('adding a new item', function() { + beforeEach(function() { + newItemBox.sendKeys('cleaning'); + newItemBox.sendKeys(protractor.Key.ENTER); + }); + it('display the item in the list', function() { + expect(element(by.binding('item.title')).getText()).toEqual('cleaning'); + }); + it('display the item as not completed', function() { + expect(element(by.model('item.completed')).isSelected()).toBe(false); + }); + it('increment the remaining items counter', function() { + expect(element(by.binding('ctrl.remainingCount()')).getText()) + .toEqual('1'); + }); + }); + + describe('marking an item as completed', function() { + beforeEach(function() { + newItemBox.sendKeys('cleaning'); + newItemBox.sendKeys(protractor.Key.ENTER); + element(by.model('item.completed')).click(); + }); + it('display the item as completed', function() { + expect(element(by.model('item.completed')).isSelected()).toBe(true); + }); + it('decrement the remaining items counter', function() { + expect(element(by.binding('ctrl.remainingCount()')).getText()) + .toEqual('0'); + }); + }); + + describe('removing an item', function() { + it('no longer display the item in the list', function() { + newItemBox.sendKeys('cleaning'); + newItemBox.sendKeys(protractor.Key.ENTER); + element(by.css('a.remove')).click(); + expect(element(by.binding('item.title')).isPresent()).toBe(false); + }); + }); + + describe('filtering items', function() { + var filteredItems = element.all(by.repeater('item in ctrl.filteredItems')); + beforeEach(function() { + newItemBox.sendKeys('cleaning'); + newItemBox.sendKeys(protractor.Key.ENTER); + element(by.model('item.completed')).click(); + newItemBox.sendKeys('mopping'); + newItemBox.sendKeys(protractor.Key.ENTER); + }); + it('display all the items when the "all" filter is clicked', function() { + element(by.css('#all')).click(); + expect(filteredItems.count()).toEqual(2); + }); + it('display only completed items when the "completed" filter is clicked', function() { + element(by.css('#completed')).click(); + expect(filteredItems.count()).toEqual(1); + expect(element(by.binding('item.title')).getText()).toEqual('cleaning'); + }); + it('display only not completed items when the "active" filter is clicked', function() { + element(by.css('#active')).click(); + expect(filteredItems.count()).toEqual(1); + expect(element(by.binding('item.title')).getText()).toEqual('mopping'); + }); + }); + + describe('clearing completed items', function() { + it('no longer display not completed items in the list', function() { + newItemBox.sendKeys('cleaning'); + newItemBox.sendKeys(protractor.Key.ENTER); + element(by.model('item.completed')).click(); + newItemBox.sendKeys('mopping'); + newItemBox.sendKeys(protractor.Key.ENTER); + element(by.css('a.clear-all')).click(); + expect(element(by.binding('item.title')).getText()).toEqual('mopping'); + }); + }); +}); diff --git a/tests/toDoListController.spec.js b/tests/toDoListController.spec.js new file mode 100644 index 00000000..91daf179 --- /dev/null +++ b/tests/toDoListController.spec.js @@ -0,0 +1,124 @@ +describe('ToDoListController', function() { + var ctrl; + var scope; + + beforeEach(module('ToDoList')); + + beforeEach(inject(function($controller, $rootScope) { + ctrl = $controller('ToDoListController'); + scope = $rootScope; + })); + + it('initializes with an empty new item', function() { + expect(ctrl.newItem).toBeUndefined(); + }); + it('initializes with filtered items equal to items', function() { + expect(ctrl.filteredItems).toEqual(ctrl.items); + }); + + describe('#addItem', function() { + it('saves the item with a title', function() { + ctrl.newItem = 'cleaning'; + ctrl.addItem(); + scope.$digest(); + expect(ctrl.items[0].title) + .toEqual('cleaning'); + }); + it('saves the item as not completed', function() { + ctrl.newItem = 'cleaning'; + ctrl.addItem(); + scope.$digest(); + expect(ctrl.items[0].completed) + .toBe(false); + }); + }); + + describe('#toggleCompleted', function() { + it('updates the item as completed', function() { + ctrl.newItem = 'cleaning'; + ctrl.addItem(); + ctrl.toggleCompleted(ctrl.items[0], true) + scope.$digest(); + expect(ctrl.items[0].completed) + .toBe(true); + }); + }); + + describe('#removeItem', function() { + it('deletes the item', function() { + ctrl.newItem = 'cleaning'; + ctrl.addItem(); + ctrl.removeItem(ctrl.items[0]); + scope.$digest(); + expect(ctrl.items) + .not.toContain({'title': 'cleaning', 'completed': true}); + }); + }); + + describe('#filter', function() { + beforeEach(function() { + ctrl.newItem = 'cleaning'; + ctrl.addItem(); + ctrl.newItem = 'mopping'; + ctrl.addItem(); + ctrl.toggleCompleted(ctrl.items[0], true); + }); + describe('called with no arguments', function() { + it('sets filtered items equal to all items', function() { + ctrl.filter(); + scope.$digest(); + expect(ctrl.filteredItems).toEqual(ctrl.items); + }); + }); + describe('called with active', function() { + it('sets filtered items equal to items not completed yet', function() { + ctrl.filter('active'); + scope.$digest(); + expect(ctrl.filteredItems) + .toContain({'title': 'mopping', 'completed': false}); + expect(ctrl.filteredItems) + .not.toContain({'title': 'cleaning', 'completed': true}); + }); + }); + describe('called with completed', function() { + it('sets filtered items equal to items already completed', function() { + ctrl.filter('completed'); + scope.$digest(); + expect(ctrl.filteredItems) + .toContain({'title': 'cleaning', 'completed': true}); + expect(ctrl.filteredItems) + .not.toContain({'title': 'mopping', 'completed': false}); + }); + }); + }); + + describe('#remainingCount', function() { + it('returns the number of not completed items', function() { + expect(ctrl.remainingCount()).toEqual(0); + ctrl.newItem = 'cleaning'; + ctrl.addItem(); + scope.$digest(); + expect(ctrl.remainingCount()).toEqual(1); + }); + }); + + describe('#clearCompletedItems', function() { + beforeEach(function() { + ctrl.newItem = 'cleaning'; + ctrl.addItem(); + ctrl.newItem = 'mopping'; + ctrl.addItem(); + }); + it('does not delete not completed items', function() { + ctrl.clearCompletedItems(); + scope.$digest(); + expect(ctrl.items.length).toEqual(2); + }); + it('deletes all the completed items', function() { + ctrl.toggleCompleted(ctrl.items[0], true); + ctrl.clearCompletedItems(); + scope.$digest(); + expect(ctrl.items.length).toEqual(1); + }); + }); +}); diff --git a/tests/toDoListStorageFactory.spec.js b/tests/toDoListStorageFactory.spec.js new file mode 100644 index 00000000..de7685bb --- /dev/null +++ b/tests/toDoListStorageFactory.spec.js @@ -0,0 +1,69 @@ +describe('Factory: ToDoListStorage', function() { + beforeEach(module('ToDoList')); + + var store; + + var item1 = { + title: 'cleaning', + completed: false + }; + var item2 = { + title: 'mopping', + completed: true + }; + var item3 = { + title: 'washing', + completed: true + }; + + beforeEach(inject(function(ToDoListStorage) { + store = ToDoListStorage; + })); + + describe('#insert', function() { + it('stores the item in the list', function() { + store.insert(item1) + .then(function(response) { + expect(response.last).toEqual(item1); + }); + }); + }); + + describe('#put', function() { + beforeEach(function() { + store.insert(item1); + }); + it('replaces a particular item of the list by index', function() { + store.put(item2, 0) + .then(function(response) { + expect(response.last).toEqual(item2); + }); + }); + }); + + describe('#delete', function() { + beforeEach(function() { + store.insert(item1); + }); + it('deletes a particular item of the list', function() { + store.delete(item1) + .then(function(response) { + expect(response.count).toEqual(0); + }); + }); + }); + + describe('#clearCompleted', function() { + beforeEach(function() { + store.insert(item1); + store.insert(item2); + store.insert(item3); + }); + it('deletes all the completed items of the list', function() { + store.delete(item1) + .then(function(response) { + expect(response).toEqual(item1); + }); + }); + }); +}); From 8c61104b82c8cbd8438e1800102873bfe8234ca2 Mon Sep 17 00:00:00 2001 From: giamir Date: Mon, 1 Feb 2016 00:02:57 +0000 Subject: [PATCH 04/10] edit travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fd8e40aa..f5fcc8d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ script: - npm test --single-run - - node_modules/.bin/protractor test/e2e/conf.js --browser=firefox + - node_modules/.bin/protractor tests/e2e/conf.js --browser=firefox language: node_js node_js: - - "0.10" + - "5.5.0" before_script: - bower install - "export DISPLAY=:99.0" From a0241cdd99c4831d57e614a329710a9d01b90af7 Mon Sep 17 00:00:00 2001 From: giamir Date: Mon, 1 Feb 2016 00:17:18 +0000 Subject: [PATCH 05/10] edit travis.yml 2 --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f5fcc8d7..0a97545c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,11 @@ script: - - npm test --single-run + - npm test - node_modules/.bin/protractor tests/e2e/conf.js --browser=firefox language: node_js node_js: - "5.5.0" +before_install: npm install -g grunt-cli +install: npm install before_script: - bower install - "export DISPLAY=:99.0" From c9595a33549d6cd86d8e7286430c29224cb84504 Mon Sep 17 00:00:00 2001 From: giamir Date: Mon, 1 Feb 2016 00:28:53 +0000 Subject: [PATCH 06/10] edit travis.yml 3 --- .travis.yml | 3 --- tests/e2e/conf.js | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0a97545c..9f18acc8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ script: - npm test - - node_modules/.bin/protractor tests/e2e/conf.js --browser=firefox language: node_js node_js: - "5.5.0" @@ -11,6 +10,4 @@ before_script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" - webdriver-manager update - - webdriver-manager start > /dev/null & - - npm start > /dev/null & - sleep 3 diff --git a/tests/e2e/conf.js b/tests/e2e/conf.js index f3adc4cb..722a6efe 100644 --- a/tests/e2e/conf.js +++ b/tests/e2e/conf.js @@ -2,7 +2,7 @@ exports.config = { seleniumAddress: 'http://localhost:4444/wd/hub', capabilities: { 'browserName': 'chrome' }, - specs: ['toDoListFeature.js'], + specs: ['*Feature.js'], jasmineNodeOpts: { showColors: true, From 76cb83748824859a2e109e12de2f1ec4531dfc25 Mon Sep 17 00:00:00 2001 From: giamir Date: Mon, 1 Feb 2016 00:39:28 +0000 Subject: [PATCH 07/10] edit travis.yml 4 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9f18acc8..ea3c7875 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ script: - npm test language: node_js node_js: - - "5.5.0" + - "0.10" before_install: npm install -g grunt-cli install: npm install before_script: From 8e7585c25be9ae5f42b22bd38ad999a2df4e8663 Mon Sep 17 00:00:00 2001 From: giamir Date: Mon, 1 Feb 2016 00:47:38 +0000 Subject: [PATCH 08/10] edit travis.yml 5 --- tests/e2e/conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/conf.js b/tests/e2e/conf.js index 722a6efe..9fff185d 100644 --- a/tests/e2e/conf.js +++ b/tests/e2e/conf.js @@ -1,7 +1,7 @@ exports.config = { seleniumAddress: 'http://localhost:4444/wd/hub', - capabilities: { 'browserName': 'chrome' }, + capabilities: { 'browserName': 'firefox' }, specs: ['*Feature.js'], jasmineNodeOpts: { From a060de109651475e5f3fd2d0529f4b53aa24a685 Mon Sep 17 00:00:00 2001 From: giamir Date: Mon, 1 Feb 2016 00:49:56 +0000 Subject: [PATCH 09/10] edit travis.yml 6 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ea3c7875..9f18acc8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ script: - npm test language: node_js node_js: - - "0.10" + - "5.5.0" before_install: npm install -g grunt-cli install: npm install before_script: From 83acae294ea83e7c5dd543f9fc6af3b2aa6439c5 Mon Sep 17 00:00:00 2001 From: giamir Date: Mon, 1 Feb 2016 01:08:29 +0000 Subject: [PATCH 10/10] edit jshintrc --- .jshintrc | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/.jshintrc b/.jshintrc index a3bbeb73..28ace6e4 100644 --- a/.jshintrc +++ b/.jshintrc @@ -88,17 +88,5 @@ "wsh" : false, // Windows Scripting Host "yui" : false, // Yahoo User Interface - "globals" : { - "toDoList": true, - "angular": true, - "inject": true, - "protractor": true, - "browser": true, - "by": true, - "element": true, - "it": true, - "describe": true, - "beforeEach": true, - "afterEach": true - } + "globals" : { "toDoList": true, "angular": true, "browser": true, "element": true, "expect": true, "by": true, "exports": true, "module": true, "inject": true, "protractor": true } }