From 75b2f278edce288090730824e2a408c17bca58e7 Mon Sep 17 00:00:00 2001 From: gaelazzo Date: Wed, 30 Apr 2014 08:55:48 +0200 Subject: [PATCH 01/17] Update ngMidwayTester.js --- src/ngMidwayTester.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 08216b6..0cd8568 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -217,9 +217,15 @@ * @method visit */ visit : function(path, callback) { - this.rootScope().__view_status = ++$viewCounter; + var scope = this.rootScope(), + that = this; + scope.__view_status = ++$viewCounter; this.until(function() { - return parseInt($terminalElement.attr('status')) >= $viewCounter; + if (that.inject('$route').current.scope === undefined) { + //console.log('still no scope! this sometimes happens and causes promblems '); + return false; + } + return parseInt($terminalElement.attr('status')) >= $viewCounter && scope.$$phase == null; }, callback || noop); var $location = this.inject('$location'); @@ -245,7 +251,17 @@ }, delay); $timers.push(timer); }, - + /** + * An helper function useful when waiting for a page transition not caused from visit() + */ + stabilize: function (callback) { + this.rootScope().__view_status = ++$viewCounter; + var myScope= this.rootScope(); + this.until(function () { + return parseInt($terminalElement.attr('status')) >= $viewCounter && myScope.$$phase == null; + }, callback); + }, + /** * Removes the $rootElement and clears the module from the page * From ca98dc4431822629831570c71959ccf3a3d92591 Mon Sep 17 00:00:00 2001 From: gaelazzo Date: Wed, 30 Apr 2014 09:16:33 +0200 Subject: [PATCH 02/17] midway testHelper Some test helper functions. --- src/testHelper.js | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/testHelper.js diff --git a/src/testHelper.js b/src/testHelper.js new file mode 100644 index 0000000..20fb191 --- /dev/null +++ b/src/testHelper.js @@ -0,0 +1,72 @@ +'use strict'; +function TestHelper(ngMidWayTester) { + this.tester = ngMidWayTester; +}; + +/** +* get current controller +**/ +TestHelper.prototype.controller = function () { + return this.tester.inject('$route').current.controller; +}; + +/** +* get actual scope, useful if page has been switched. It requires some element with ng-model on page to work. +**/ +TestHelper.prototype.scope = function () { + return angular.element($('[ng-model]')).scope(); +}; + +/** +* get actual html of page. I found that ngMidWayTester.htmlElement does not work after page transitions +**/ +TestHelper.prototype.html = function () { + return document.body.outerHTML; +}; + +/* a necessary helper function to properly wait fo page transitions */ +TestHelper.prototype.wait = function (done) { + var that = this, + $timeout = that.tester.inject('$timeout'); + $timeout(function () { + that.tester.digest(); + that.tester.stabilize(function () { + $timeout(done); + }); + }); +}; + + +/* some helper functions, I provide them just as case-example for wait function */ +TestHelper.prototype.login = function (done) { + var that = this; + that.tester.visit('/login', function () { + var scope = that.scope(); + scope.user = {username: 'user', password: 'pwd'}; + scope.submit(); + that.wait(done); + }); +}; + +/* try to logout from wherever you are */ +TestHelper.prototype.logout = function (done) { + var that = this; + that.tester.visit('/', function () { + if (that.tester.path() === '/login') { + done(); + return; + } + var scope = that.scope(); + if (scope === null || scope === undefined) { + //may be he was already not logged in, let's try with viewScope()... + scope = that.tester.viewScope(); + } + if (scope === null || scope === undefined) { + console.log('Page:' + that.tester.path() + ' ctrl ' + that.controller() + ': no scope found'); + done(); + return; + } + scope.logout(); + that.wait(done); + }); +}; From 5831b1f6a83b9188a409df1fea7956e5374000d0 Mon Sep 17 00:00:00 2001 From: gaelazzo Date: Wed, 30 Apr 2014 09:31:00 +0200 Subject: [PATCH 03/17] Delete testHelper.js --- src/testHelper.js | 72 ----------------------------------------------- 1 file changed, 72 deletions(-) delete mode 100644 src/testHelper.js diff --git a/src/testHelper.js b/src/testHelper.js deleted file mode 100644 index 20fb191..0000000 --- a/src/testHelper.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict'; -function TestHelper(ngMidWayTester) { - this.tester = ngMidWayTester; -}; - -/** -* get current controller -**/ -TestHelper.prototype.controller = function () { - return this.tester.inject('$route').current.controller; -}; - -/** -* get actual scope, useful if page has been switched. It requires some element with ng-model on page to work. -**/ -TestHelper.prototype.scope = function () { - return angular.element($('[ng-model]')).scope(); -}; - -/** -* get actual html of page. I found that ngMidWayTester.htmlElement does not work after page transitions -**/ -TestHelper.prototype.html = function () { - return document.body.outerHTML; -}; - -/* a necessary helper function to properly wait fo page transitions */ -TestHelper.prototype.wait = function (done) { - var that = this, - $timeout = that.tester.inject('$timeout'); - $timeout(function () { - that.tester.digest(); - that.tester.stabilize(function () { - $timeout(done); - }); - }); -}; - - -/* some helper functions, I provide them just as case-example for wait function */ -TestHelper.prototype.login = function (done) { - var that = this; - that.tester.visit('/login', function () { - var scope = that.scope(); - scope.user = {username: 'user', password: 'pwd'}; - scope.submit(); - that.wait(done); - }); -}; - -/* try to logout from wherever you are */ -TestHelper.prototype.logout = function (done) { - var that = this; - that.tester.visit('/', function () { - if (that.tester.path() === '/login') { - done(); - return; - } - var scope = that.scope(); - if (scope === null || scope === undefined) { - //may be he was already not logged in, let's try with viewScope()... - scope = that.tester.viewScope(); - } - if (scope === null || scope === undefined) { - console.log('Page:' + that.tester.path() + ' ctrl ' + that.controller() + ': no scope found'); - done(); - return; - } - scope.logout(); - that.wait(done); - }); -}; From 427925bcf8338c0d420c33ed426efefbb517db42 Mon Sep 17 00:00:00 2001 From: gaelazzo Date: Wed, 30 Apr 2014 09:42:14 +0200 Subject: [PATCH 04/17] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 222c0d9..fef18ae 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "karma-firefox-launcher": "~0.1.0", "karma-html2js-preprocessor": "~0.1.0", "karma-jasmine": "~0.1.3", - "karma-requirejs": "~0.1.0", + "karma-requirejs": "~0.2.0", "karma-coffee-preprocessor": "~0.1.0", "karma-phantomjs-launcher": "~0.1.0", "karma": "~0.10.2", From 87579ef2b72feeb3ef6fae9813f903362547f3d9 Mon Sep 17 00:00:00 2001 From: gaelazzo Date: Wed, 30 Apr 2014 09:48:21 +0200 Subject: [PATCH 05/17] Update ngMidwayTester.js --- src/ngMidwayTester.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 0cd8568..3151642 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -221,7 +221,8 @@ that = this; scope.__view_status = ++$viewCounter; this.until(function() { - if (that.inject('$route').current.scope === undefined) { + if (that.inject('$route').current === undefined || + that.inject('$route').current.scope === undefined) { //console.log('still no scope! this sometimes happens and causes promblems '); return false; } From db2f66857e385be3540a6bd17ef703cfcef4fdf8 Mon Sep 17 00:00:00 2001 From: gaelazzo Date: Wed, 30 Apr 2014 09:52:24 +0200 Subject: [PATCH 06/17] Update ngMidwayTester.js --- src/ngMidwayTester.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 3151642..9d421f1 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -221,7 +221,7 @@ that = this; scope.__view_status = ++$viewCounter; this.until(function() { - if (that.inject('$route').current === undefined || + if (that.inject('$route').current !== undefined && that.inject('$route').current.scope === undefined) { //console.log('still no scope! this sometimes happens and causes promblems '); return false; From 06006a58264a475384bdacda8506a6cb3408e543 Mon Sep 17 00:00:00 2001 From: gaelazzo Date: Thu, 1 May 2014 07:45:22 +0200 Subject: [PATCH 07/17] Update ngMidwayTester.js --- src/ngMidwayTester.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 9d421f1..50805d7 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -217,19 +217,8 @@ * @method visit */ visit : function(path, callback) { - var scope = this.rootScope(), - that = this; - scope.__view_status = ++$viewCounter; - this.until(function() { - if (that.inject('$route').current !== undefined && - that.inject('$route').current.scope === undefined) { - //console.log('still no scope! this sometimes happens and causes promblems '); - return false; - } - return parseInt($terminalElement.attr('status')) >= $viewCounter && scope.$$phase == null; - }, callback || noop); - var $location = this.inject('$location'); + stabilize(callback || noop); this.apply(function() { $location.path(path); }); @@ -256,10 +245,16 @@ * An helper function useful when waiting for a page transition not caused from visit() */ stabilize: function (callback) { - this.rootScope().__view_status = ++$viewCounter; - var myScope= this.rootScope(); + var scope= this.rootScope(), + that = this; + scope.__view_status = ++$viewCounter; this.until(function () { - return parseInt($terminalElement.attr('status')) >= $viewCounter && myScope.$$phase == null; + if (that.inject('$route').current !== undefined && + that.inject('$route').current.scope === undefined) { + //console.log('still no scope! this sometimes happens and causes promblems '); + return false; + } + return parseInt($terminalElement.attr('status')) >= $viewCounter && scope.$$phase === null; }, callback); }, From 22141ac7e5808349409e2d9b0a5958b653813e43 Mon Sep 17 00:00:00 2001 From: gaelazzo Date: Thu, 1 May 2014 07:49:55 +0200 Subject: [PATCH 08/17] Update ngMidwayTester.js --- src/ngMidwayTester.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 50805d7..8c07701 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -218,7 +218,7 @@ */ visit : function(path, callback) { var $location = this.inject('$location'); - stabilize(callback || noop); + this.stabilize(callback || noop); this.apply(function() { $location.path(path); }); From 4cb578f72a775b11baadf7d9ae6415ee54892335 Mon Sep 17 00:00:00 2001 From: Gaetano Date: Sun, 8 Feb 2015 12:12:57 +0100 Subject: [PATCH 09/17] t --- src/ngMidwayTester.js | 51 +++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 8c07701..090a961 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -1,6 +1,6 @@ /** - * Creates an instance of the midway tester on the specified module. - * + * Creates an instance of the midway tester on the specified module. + * * @class ngMidwayTester * @constructor * @param moduleName the AngularJS module that you wish to test @@ -137,7 +137,7 @@ * @return {Object} The scope of the current view element */ viewScope : function() { - return this.viewElement().scope(); + return this.viewElement().scope()|| this.rootScope(); }, /** @@ -169,7 +169,16 @@ * @param {Object} [scope=$rootScope] The scope object which will be used for the compilation */ digest : function(scope) { - (scope || this.rootScope()).$digest(); + var s = scope || this.rootScope(); + if(!s.$$phase) { + //console.log('routed to digest'); + s.$digest(); + } + else { + //$timeout = this.inject('$timeout'); + //console.log('skipped digest'); + //$timeout(this.digest); + } }, /** @@ -218,7 +227,18 @@ */ visit : function(path, callback) { var $location = this.inject('$location'); - this.stabilize(callback || noop); + + //this.stabilize(callback || noop); + //this.apply(function() { + // $location.path(path); + //}); + + this.rootScope().__view_status = ++$viewCounter; + this.until(function() { + return parseInt($terminalElement.attr('status')) >= $viewCounter; + }, callback || noop); + + var $location = this.inject('$location'); this.apply(function() { $location.path(path); }); @@ -228,19 +248,20 @@ * Keeps checking an expression until it returns a truthy value and then runs the provided callback * * @param {function} exp The given function to poll - * @param {function} callback The given callback to fire once the exp function returns a truthy value + * @param {function} callback The given callback to fire once the exp function returns a truthy value * @method until */ until : function(exp, callback) { - var timer, delay = 50; + var timer, delay = 30; timer = setInterval(function() { if(exp()) { clearTimeout(timer); callback(); } - }, delay); + }, delay); $timers.push(timer); }, + /** * An helper function useful when waiting for a page transition not caused from visit() */ @@ -249,15 +270,17 @@ that = this; scope.__view_status = ++$viewCounter; this.until(function () { - if (that.inject('$route').current !== undefined && - that.inject('$route').current.scope === undefined) { - //console.log('still no scope! this sometimes happens and causes promblems '); - return false; - } + scope= that.rootScope(); + //console.log('route.current = '+JSON.stringify(that.inject('$route').current.$scope)); + //if (that.inject('$route').current !== undefined && + // that.inject('$route').current.$scope === undefined) { + // console.log('still no scope! this sometimes happens and causes problems '); + // return false; + //} return parseInt($terminalElement.attr('status')) >= $viewCounter && scope.$$phase === null; }, callback); }, - + /** * Removes the $rootElement and clears the module from the page * From 12fc132f3c52444dae88a15209b792f99df55b4d Mon Sep 17 00:00:00 2001 From: Gaetano Date: Sun, 8 Feb 2015 17:50:43 +0100 Subject: [PATCH 10/17] updated versions --- package.json | 26 +++++++++++++------------- test/karma.conf.js | 20 +++++++++++++++++++- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index fef18ae..b61e71a 100644 --- a/package.json +++ b/package.json @@ -9,25 +9,25 @@ "test": "grunt travis" }, "devDependencies": { - "bower": "latest", + "bower": "~1.3.12", "yuidocjs": "~0.3.45", - "grunt": "~0.4.1", - "grunt-contrib-connect": "~0.5.0", - "grunt-shell": "~0.5.0", + "grunt": "^0.4.5", + "grunt-contrib-connect": "~0.9.0", + "grunt-shell": "^1.1.1", "grunt-open": "~0.2.2", "karma-script-launcher": "~0.1.0", - "karma-chrome-launcher": "~0.1.0", - "karma-firefox-launcher": "~0.1.0", + "karma-chrome-launcher": "~0.1.7", + "karma-firefox-launcher": "~0.1.3", "karma-html2js-preprocessor": "~0.1.0", - "karma-jasmine": "~0.1.3", - "karma-requirejs": "~0.2.0", + "karma-jasmine": "~0.3.5", + "karma-requirejs": "~0.2.1", "karma-coffee-preprocessor": "~0.1.0", - "karma-phantomjs-launcher": "~0.1.0", - "karma": "~0.10.2", - "grunt-karma": "~0.6.2", - "mocha": "~1.13.0", + "karma-phantomjs-launcher": "^0.1.4", + "karma": "^0.12.31", + "grunt-karma": "^0.10.1", + "mocha": "^2.1.0", "karma-mocha": "~0.1.0", - "chai": "~1.8.1", + "chai": "^1.10.0", "karma-coverage": "~0.1.0" } } diff --git a/test/karma.conf.js b/test/karma.conf.js index 8f08d49..7ad85b5 100644 --- a/test/karma.conf.js +++ b/test/karma.conf.js @@ -11,9 +11,27 @@ module.exports = function(config) { ], singleRun: true, frameworks: ['mocha'], - browsers: ['Chrome'], + + // Start these browsers, currently available: + // - Chrome + // - ChromeCanary + // - Firefox + // - Opera + // - Safari (only Mac) + // - PhantomJS + // - IE (only Windows) + browsers: ['PhantomJS'], proxies: { '/': 'http://localhost:8844/' } + , + + // Which plugins to enable + plugins: [ + 'karma-phantomjs-launcher', + 'karma-jasmine', + 'karma-mocha' + //'karma-junit-reporter' + ], }); }; From d49602664aa5ed48a985327b0727b6e58fe711d2 Mon Sep 17 00:00:00 2001 From: Gaetano Date: Sun, 8 Feb 2015 18:27:12 +0100 Subject: [PATCH 11/17] updated ignore list --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 107a9ca..5aceae6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ libpeerconnection.log npm-debug.log .agignore /coverage +.idea From 01d99c7ea61567ac521adb46ecdfc1de44dacf11 Mon Sep 17 00:00:00 2001 From: Gaetano Date: Sun, 8 Feb 2015 18:29:04 +0100 Subject: [PATCH 12/17] updated gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 5aceae6..9e5b91b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ npm-debug.log .agignore /coverage .idea +/.idea/jsLibraryMappings.xml +/.idea/misc.xml +/.idea/libraries/ngMidwayTester_node_modules.xml From 4e99ded3aec9b9742c93ca4d0643f9cbf9dc8fba Mon Sep 17 00:00:00 2001 From: Gaetano Lazzo Date: Mon, 14 Sep 2015 09:34:26 +0200 Subject: [PATCH 13/17] Updated to new angular --- Gruntfile.js | 2 +- bower.json | 4 ++-- src/ngMidwayTester.js | 42 +++++++++++++++++++++++---------- test/karma.conf.js | 5 ++-- test/spec/custom-view.html | 2 +- test/spec/ngMidwayTesterSpec.js | 2 +- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0856f51..65baf7a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -18,7 +18,7 @@ module.exports = function(grunt) { }, bower_install: { command: 'node ./node_modules/bower/bin/bower install' - }, + } }, connect: { diff --git a/bower.json b/bower.json index fe537cc..6860f7e 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "ngMidwayTester", "devDependencies": { - "angularjs": "http://code.angularjs.org/1.2.0-rc.3/angular.js", - "angularjs-route": "http://code.angularjs.org/1.2.0-rc.3/angular-route.js" + "angular": "1.4.4", + "angular-route": "1.4.4" } } diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 090a961..8561446 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -38,6 +38,8 @@ $delegate.path = function(path) { if(path) { _path = path; + //console.log('$rootScope.$broadcast("$locationChangeSuccess", ', path,');'); + $rootScope.$broadcast('$locationChangeStart', path); $rootScope.$broadcast('$locationChangeSuccess', path); return this; } @@ -225,23 +227,35 @@ * @param {function} [callback] The given callback to fire once the view has been fully loaded * @method visit */ - visit : function(path, callback) { - var $location = this.inject('$location'); - - //this.stabilize(callback || noop); - //this.apply(function() { - // $location.path(path); - //}); - - this.rootScope().__view_status = ++$viewCounter; - this.until(function() { - return parseInt($terminalElement.attr('status')) >= $viewCounter; - }, callback || noop); + visit : function(path, callback, noView) { + this.delayed(callback,noView)(); var $location = this.inject('$location'); this.apply(function() { $location.path(path); }); + + }, + + delayed: function(callback,noView){ + var that=this; + if (noView === undefined) { + noView=false; + } + return function(){ + that.rootScope().__view_status = ++$viewCounter; + console.log('rootScope augmented'); + that.until(function(){ + if (!noView) { + if (!that.viewScope()) { + console.log('no viewScope'); + return false; + } + } + console.log('viewScope is here'); + return parseInt($terminalElement.attr('status')) >= $viewCounter; + },callback || noop); + } }, /** @@ -252,10 +266,12 @@ * @method until */ until : function(exp, callback) { - var timer, delay = 30; + var timer, delay = 50; timer = setInterval(function() { + console.log('evaluating exp...'); if(exp()) { clearTimeout(timer); + console.log('calling callback'); callback(); } }, delay); diff --git a/test/karma.conf.js b/test/karma.conf.js index 7ad85b5..0ca54db 100644 --- a/test/karma.conf.js +++ b/test/karma.conf.js @@ -1,10 +1,11 @@ module.exports = function(config) { config.set({ + reporters: ['dots'], //dots progress basePath : '../', files : [ './node_modules/chai/chai.js', - './bower_components/angularjs/index.js', - './bower_components/angularjs-route/index.js', + './bower_components/angular/angular.js', + './bower_components/angular-route/angular-route.js', './src/ngMidwayTester.js', './test/lib/chai.js', './test/spec/ngMidwayTesterSpec.js' diff --git a/test/spec/custom-view.html b/test/spec/custom-view.html index 3ecf9f1..10ed5ca 100644 --- a/test/spec/custom-view.html +++ b/test/spec/custom-view.html @@ -1,5 +1,5 @@

Hello

How's it going?

-
+
diff --git a/test/spec/ngMidwayTesterSpec.js b/test/spec/ngMidwayTesterSpec.js index f88b9c2..3abd925 100644 --- a/test/spec/ngMidwayTesterSpec.js +++ b/test/spec/ngMidwayTesterSpec.js @@ -164,7 +164,7 @@ describe('ngMidwayTester', function() { tester.visit('/', function() { expect(tester.path()).to.equal('/'); done(); - }); + },true); }); it('should update the when by the time the callback is called', function(done) { From b0416bc7562b33dc22db9229a971651de38eb03a Mon Sep 17 00:00:00 2001 From: Gaetano Lazzo Date: Mon, 14 Sep 2015 09:51:15 +0200 Subject: [PATCH 14/17] created new version Created a new official version --- package.json | 4 ++-- src/ngMidwayTester.js | 30 +++--------------------------- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index b61e71a..23800e9 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "ng-midway-tester", - "version": "2.0.5", + "version": "2.0.6", "repository": { "type": "git", - "url": "git@github.com:yearofmoo/ngMidwayTester.git" + "url": "https://github.com/gaelazzo/ngMidwayTester" }, "scripts": { "test": "grunt travis" diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 8561446..b3587f5 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -70,7 +70,7 @@ $viewContainer = view.parent(); } else { - $viewContainer = angular.element('
'); + $viewContainer = angular.element('
'); $rootElement.append($viewContainer); } @@ -139,7 +139,7 @@ * @return {Object} The scope of the current view element */ viewScope : function() { - return this.viewElement().scope()|| this.rootScope(); + return this.viewElement().scope(); }, /** @@ -173,7 +173,6 @@ digest : function(scope) { var s = scope || this.rootScope(); if(!s.$$phase) { - //console.log('routed to digest'); s.$digest(); } else { @@ -244,15 +243,12 @@ } return function(){ that.rootScope().__view_status = ++$viewCounter; - console.log('rootScope augmented'); that.until(function(){ if (!noView) { if (!that.viewScope()) { - console.log('no viewScope'); return false; } } - console.log('viewScope is here'); return parseInt($terminalElement.attr('status')) >= $viewCounter; },callback || noop); } @@ -266,36 +262,16 @@ * @method until */ until : function(exp, callback) { - var timer, delay = 50; + var timer, delay = 30; timer = setInterval(function() { - console.log('evaluating exp...'); if(exp()) { clearTimeout(timer); - console.log('calling callback'); callback(); } }, delay); $timers.push(timer); }, - /** - * An helper function useful when waiting for a page transition not caused from visit() - */ - stabilize: function (callback) { - var scope= this.rootScope(), - that = this; - scope.__view_status = ++$viewCounter; - this.until(function () { - scope= that.rootScope(); - //console.log('route.current = '+JSON.stringify(that.inject('$route').current.$scope)); - //if (that.inject('$route').current !== undefined && - // that.inject('$route').current.$scope === undefined) { - // console.log('still no scope! this sometimes happens and causes problems '); - // return false; - //} - return parseInt($terminalElement.attr('status')) >= $viewCounter && scope.$$phase === null; - }, callback); - }, /** * Removes the $rootElement and clears the module from the page From 46e982ea136cea4958046585134a015581322a4f Mon Sep 17 00:00:00 2001 From: Gaetano Lazzo Date: Sat, 19 Sep 2015 16:54:09 +0200 Subject: [PATCH 15/17] Some minor improvements. Added a deferred function called when the ngMidway module is configured. --- src/ngMidwayTester.js | 68 +++++++++++++++++++++++---------- test/spec/ngMidwayTesterSpec.js | 37 ++++++++++++++++++ 2 files changed, 85 insertions(+), 20 deletions(-) diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index b3587f5..9be6f6d 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -12,7 +12,9 @@ * @param {Object} [config.mockLocationPaths=true] Whether or not to fake the URL change in the browser address bar * @return {Object} An instance of the midway tester */ -;var ngMidwayTester = function(moduleName, options) { + + +;var ngMidwayTester = function(moduleName, options, deferred) { options = options || {}; var doc = options.document || document; @@ -21,6 +23,7 @@ var mockLocationPaths = options.mockLocationPaths == null ? true : !!options.mockLocationPaths; + var $rootElement = angular.element(doc.createElement('div')), $timers = [], $viewContainer, @@ -28,8 +31,17 @@ $viewCounter = 0; var viewSelector = 'ng-view, [ng-view], .ng-view, [x-ng-view], [data-ng-view]'; + var $injector; + - var midwayModule = angular.module('ngMidway', []); + + var midwayModule = angular.module('ngMidway', []) + .config( + ['$injector', function($i) { + $injector = $i; + if (deferred) deferred.resolve(); + }] + ); if(mockLocationPaths) { midwayModule.config(function($provide) { @@ -70,16 +82,16 @@ $viewContainer = view.parent(); } else { - $viewContainer = angular.element('
'); + $viewContainer = angular.element('
'); $rootElement.append($viewContainer); } $terminalElement = angular.element('
'); $rootElement.append($terminalElement); - var $injector = angular.bootstrap($rootElement, ['ng','ngMidway',moduleName]); + $injector = angular.bootstrap($rootElement, ['ng','ngMidway',moduleName]); var $rootModule = angular.module(moduleName); - angular.element(doc.body).append($rootElement); + angular.element(doc.body).prepend($rootElement); return { /** @@ -171,14 +183,15 @@ * @param {Object} [scope=$rootScope] The scope object which will be used for the compilation */ digest : function(scope) { + //(scope || this.rootScope()).$digest(); var s = scope || this.rootScope(); if(!s.$$phase) { s.$digest(); } else { - //$timeout = this.inject('$timeout'); + var $timeout = this.inject('$timeout'); //console.log('skipped digest'); - //$timeout(this.digest); + $timeout(this.digest); } }, @@ -190,7 +203,7 @@ * @param {Object} [scope=$rootScope] scope The scope object which the apply process will be run on */ apply : function(fn, scope) { - scope = scope || this.inject('$rootScope'); + scope = scope || scope || this.inject('$rootScope'); //$rootScope scope.$$phase ? fn() : scope.$apply(fn); }, @@ -227,7 +240,7 @@ * @method visit */ visit : function(path, callback, noView) { - this.delayed(callback,noView)(); + this.delayed(callback, noView)(); var $location = this.inject('$location'); this.apply(function() { @@ -236,24 +249,35 @@ }, - delayed: function(callback,noView){ + delayed: function(callback, noView){ var that=this; + if (callback.$$$isDelayed) { + return callback; //just to be sure a function is never double delayed + } if (noView === undefined) { noView=false; } - return function(){ + + return function() { + this.$$$isDelayed=true; //just to be sure a function is never double delayed + var args = Array.prototype.slice.call(arguments); + var myFun= function(){ + callback.apply(null,args); + }; that.rootScope().__view_status = ++$viewCounter; - that.until(function(){ + that.until(function () { if (!noView) { if (!that.viewScope()) { return false; } + if(that.viewScope().$$phase){ + return false; + } } return parseInt($terminalElement.attr('status')) >= $viewCounter; - },callback || noop); + }, myFun || noop); } }, - /** * Keeps checking an expression until it returns a truthy value and then runs the provided callback * @@ -262,12 +286,16 @@ * @method until */ until : function(exp, callback) { + var timer, delay = 30; timer = setInterval(function() { if(exp()) { - clearTimeout(timer); + clearInterval(timer); callback(); } + else { + active = true; + } }, delay); $timers.push(timer); }, @@ -280,13 +308,13 @@ */ destroy : function() { angular.forEach($timers, function(timer) { - clearTimeout(timer); + clearInterval(timer); }); - var body = angular.element(document.body); - body.removeData(); - $rootElement.remove(); - this.rootScope().$destroy(); + var body = angular.element(document.body); + body.removeData(); + $rootElement.remove(); + this.rootScope().$destroy(); } }; }; diff --git a/test/spec/ngMidwayTesterSpec.js b/test/spec/ngMidwayTesterSpec.js index 3abd925..7f70ecc 100644 --- a/test/spec/ngMidwayTesterSpec.js +++ b/test/spec/ngMidwayTesterSpec.js @@ -263,4 +263,41 @@ describe('ngMidwayTester', function() { tester3.destroy(); }); }); + + describe ('System functions', function(){ + + beforeEach(function() { + example = angular.module(appName, []) + .factory('factory', function() { + return function() { + return 'hello'; + } + }) + .controller('HomeCtrl', function($scope, factory) { + $scope.factory = factory; + }); + + tester = ngMidwayTester(appName, { + template : '' + + '' + + '
' + + '

Help Desk

' + + '
' + + '
' + + '
' + + '
' + }); + newScope = tester.rootScope().$new(); + }); + + + it ('calling setTimeout should call a function after some time', function(done){ + var s = function(){ + expect(s).to.exist(); + clearTimeout(s); + done(); + }; + setTimeout(s,1000); + }); + }) }); From 181b8d7df74e287164ee5ea4601d0881d96fcc79 Mon Sep 17 00:00:00 2001 From: Gaetano Lazzo Date: Sat, 31 Oct 2015 18:05:06 +0000 Subject: [PATCH 16/17] changed router to ui-router and tested --- bower.json | 7 +- package.json | 24 +- src/ngMidwayTester.js | 631 +++++++++++++++++--------------- test/karma.conf.js | 14 +- test/lib/chai.js | 1 - test/spec/custom-view.html | 2 +- test/spec/custom-view2.html | 8 + test/spec/custom-view3.html | 7 + test/spec/custom-view4.html | 6 + test/spec/ngMidwayTesterSpec.js | 351 ++++++++++++------ 10 files changed, 622 insertions(+), 429 deletions(-) delete mode 100644 test/lib/chai.js create mode 100644 test/spec/custom-view2.html create mode 100644 test/spec/custom-view3.html create mode 100644 test/spec/custom-view4.html diff --git a/bower.json b/bower.json index 6860f7e..14be849 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,10 @@ { "name": "ngMidwayTester", "devDependencies": { - "angular": "1.4.4", - "angular-route": "1.4.4" + "angular": "1.4.7", + "angular-ui-router": "0.2.15" + }, + "dependencies": { + "jasmine-core": "jasmine#~2.3.4" } } diff --git a/package.json b/package.json index 23800e9..66d85b8 100644 --- a/package.json +++ b/package.json @@ -15,19 +15,25 @@ "grunt-contrib-connect": "~0.9.0", "grunt-shell": "^1.1.1", "grunt-open": "~0.2.2", - "karma-script-launcher": "~0.1.0", - "karma-chrome-launcher": "~0.1.7", - "karma-firefox-launcher": "~0.1.3", + "karma": "~0.12.37", + "karma-chrome-launcher": "~0.2.0", + "karma-coffee-preprocessor": "~0.2.1", + "karma-firefox-launcher": "~0.1.6", "karma-html2js-preprocessor": "~0.1.0", - "karma-jasmine": "~0.3.5", - "karma-requirejs": "~0.2.1", - "karma-coffee-preprocessor": "~0.1.0", - "karma-phantomjs-launcher": "^0.1.4", - "karma": "^0.12.31", - "grunt-karma": "^0.10.1", + "karma-jade-preprocessor": "0.0.11", + "karma-jasmine": "~0.3.6", + "karma-ng-html2js-preprocessor": "~0.1.0", + "karma-ng-jade2js-preprocessor": "^0.2.1", + "karma-ng-scenario": "~0.1.0", + "karma-phantomjs-launcher": "^0.2.1", + "karma-requirejs": "~0.2.2", + "karma-script-launcher": "~0.1.0", + "load-grunt-tasks": "~3.1.0", + "grunt-karma": "~0.11.1", "mocha": "^2.1.0", "karma-mocha": "~0.1.0", "chai": "^1.10.0", "karma-coverage": "~0.1.0" } + } diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 9be6f6d..0b03307 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -14,307 +14,342 @@ */ -;var ngMidwayTester = function(moduleName, options, deferred) { - - options = options || {}; - var doc = options.document || document; - var wind = options.window || window; - var noop = angular.noop; - - var mockLocationPaths = options.mockLocationPaths == null ? true : !!options.mockLocationPaths; - - - var $rootElement = angular.element(doc.createElement('div')), - $timers = [], - $viewContainer, - $terminalElement, - $viewCounter = 0; - - var viewSelector = 'ng-view, [ng-view], .ng-view, [x-ng-view], [data-ng-view]'; - var $injector; - - - - var midwayModule = angular.module('ngMidway', []) - .config( - ['$injector', function($i) { - $injector = $i; - if (deferred) deferred.resolve(); - }] - ); - - if(mockLocationPaths) { - midwayModule.config(function($provide) { - $provide.decorator('$location', ['$delegate', '$rootScope', function($delegate, $rootScope) { - var _path = $delegate.path(); - $delegate.path = function(path) { - if(path) { - _path = path; - //console.log('$rootScope.$broadcast("$locationChangeSuccess", ', path,');'); - $rootScope.$broadcast('$locationChangeStart', path); - $rootScope.$broadcast('$locationChangeSuccess', path); - return this; - } - else { - return _path; - } - }; - return $delegate; - }]); - }); - } - - if(options.templateUrl) { - var request = new XMLHttpRequest(); - request.open('GET', options.templateUrl, false); - request.send(null); - - if (request.status != 200) { - throw new Error('ngMidwayTester: Unable to download template file'); +; +var ngMidwayTester = function (moduleName, options, deferred) { + + options = options || {}; + var doc = options.document || document; + var wind = options.window || window; + var noop = angular.noop; + + var mockLocationPaths = options.mockLocationPaths == null ? true : !!options.mockLocationPaths; + + + var $rootElement = angular.element(doc.createElement('div')), + $timers = [], + $viewContainer, + $terminalElement, + $viewCounter = 0; + + var viewSelector = 'ui-view, [ui-view], .ui-view, [x-ui-view], [data-ui-view]'; + var $injector; + + + var midwayModule = angular.module('ngMidway', []) + .config( + ['$injector', function ($i) { + $injector = $i; + if (deferred) deferred.resolve(); + }] + ); + + + + if (mockLocationPaths) { + midwayModule.config(function ($provide) { + $provide.decorator('$location', ['$delegate', '$rootScope', function ($delegate, $rootScope) { + var _path = $delegate.path(); + $delegate.path = function (path) { + if (path) { + _path = path; + //console.log('$rootScope.$broadcast("$locationChangeSuccess", ', path,');'); + $rootScope.$broadcast('$locationChangeStart', path); + $rootScope.$broadcast('$locationChangeSuccess', path); + return this; + } + else { + return _path; + } + }; + return $delegate; + }]); + }); } + var notifyOnLocationChangeSuccess =[]; - options.template = request.responseText; - } - - if(options.template) { - $rootElement.html(options.template); - var view = angular.element($rootElement[0].querySelector(viewSelector)); - $viewContainer = view.parent(); - } - else { - $viewContainer = angular.element('
'); - $rootElement.append($viewContainer); - } - - $terminalElement = angular.element('
'); - $rootElement.append($terminalElement); - - $injector = angular.bootstrap($rootElement, ['ng','ngMidway',moduleName]); - var $rootModule = angular.module(moduleName); - angular.element(doc.body).prepend($rootElement); - - return { - /** - * @method module - * @return {Object} Returns the module container object acquired from angular.module(moduleName) - */ - module : function() { - return $rootModule; - }, - - /** - * Attaches the $rootElement module to the provided body element - * @param {Element} [body=document.body] The element that will be used as the parent (defaults to document.body) - * @method attach - */ - attach : function(body) { - angular.element(body || doc.body).append($rootElement); - }, - - /** - * Attaches the $rootElement module to the provided body element - * @method controller - * @param {String} name The name of the controller - * @param {Object} [locals] A key/value map of all the injectable services for when the controller is instantiated - * @return {Object} The instance of the controller - */ - controller : function(name, locals) { - return this.inject('$controller')(name, locals); - }, - - /** - * @method rootScope - * @return {Object} The $rootScope object of the module - */ - rootScope : function() { - return this.inject('$rootScope'); - }, - - /** - * @method rootElement - * @return {Object} The $rootElement object of the module - */ - rootElement : function() { - return $rootElement; - }, - - /** - * @method viewElement - * @return {Element} The current element that has ng-view attached to it - */ - viewElement : function() { - return angular.element($viewContainer[0].querySelector(viewSelector)); - }, - - /** - * @method viewElement - * @return {Object} The scope of the current view element - */ - viewScope : function() { - return this.viewElement().scope(); - }, - - /** - * Runs $scope.$evalAsync() on the provided scope - * @param {function} fn The function to be provided to evalAsync - * @param {Object} [scope=$rootScope] The scope object which will be used for the eval call - * @method evalAsync - */ - evalAsync : function(fn, scope) { - (scope || this.rootScope()).$evalAsync(fn); - }, - - /** - * Compiles and links the given HTML - * - * @method compile - * @param {String|Element} html the html or element node which will be compiled - * @param {Object} [scope=$rootScope] The scope object which will be linked to the compile - * @return {Element} The element node which which is the result of the compilation - */ - compile : function(html, scope) { - return this.inject('$compile')(html)(scope || this.rootScope()); - }, - - /** - * Performs a digest operation on the given scope - * - * @method digest - * @param {Object} [scope=$rootScope] The scope object which will be used for the compilation - */ - digest : function(scope) { - //(scope || this.rootScope()).$digest(); - var s = scope || this.rootScope(); - if(!s.$$phase) { - s.$digest(); - } - else { - var $timeout = this.inject('$timeout'); - //console.log('skipped digest'); - $timeout(this.digest); - } - }, - - /** - * Performs an apply operation on the given scope - * - * @method apply - * @param {function} fn The callback function which will be used in the apply digest - * @param {Object} [scope=$rootScope] scope The scope object which the apply process will be run on - */ - apply : function(fn, scope) { - scope = scope || scope || this.inject('$rootScope'); //$rootScope - scope.$$phase ? fn() : scope.$apply(fn); - }, - - /* - * @method inject - * @param {String} item The name of the service which will be fetched - * @return {Object} The service fetched from the injection call - */ - inject : function(item) { - return $injector.get(item); - }, - - /** - * @method injector - * @return {Object} Returns the AngularJS $injector service - */ - injector : function() { - return $injector; - }, - - /** - * @method path - * @return {String} Returns the path of the current route - */ - path : function() { - return this.inject('$location').path(); - }, - - /** - * Changes the current route of the page and then fires the callback when the page has loaded - * - * @param {String} path The given path that the current route will be changed to - * @param {function} [callback] The given callback to fire once the view has been fully loaded - * @method visit - */ - visit : function(path, callback, noView) { - this.delayed(callback, noView)(); - - var $location = this.inject('$location'); - this.apply(function() { - $location.path(path); - }); - - }, - - delayed: function(callback, noView){ - var that=this; - if (callback.$$$isDelayed) { - return callback; //just to be sure a function is never double delayed - } - if (noView === undefined) { - noView=false; - } - - return function() { - this.$$$isDelayed=true; //just to be sure a function is never double delayed - var args = Array.prototype.slice.call(arguments); - var myFun= function(){ - callback.apply(null,args); - }; - that.rootScope().__view_status = ++$viewCounter; - that.until(function () { - if (!noView) { - if (!that.viewScope()) { - return false; + + if (options.templateUrl) { + var request = new XMLHttpRequest(); + request.open('GET', options.templateUrl, false); + request.send(null); + + if (request.status != 200) { + throw new Error('ngMidwayTester: Unable to download template file'); + } + + options.template = request.responseText; + } + + if (options.template) { + $rootElement.html(options.template); + var view = angular.element($rootElement[0].querySelector(viewSelector)); + $viewContainer = view.parent(); + } + else { + $viewContainer = angular.element('
'); + $rootElement.append($viewContainer); + } + + $terminalElement = angular.element('
'); + $rootElement.append($terminalElement); + + $injector = angular.bootstrap($rootElement, ['ng', 'ngMidway', moduleName]); + var $rootModule = angular.module(moduleName); + + + + angular.element(doc.body).prepend($rootElement); + + + + return { + + + /** + * @method module + * @return {Object} Returns the module container object acquired from angular.module(moduleName) + */ + module: function () { + return $rootModule; + }, + + /** + * Attaches the $rootElement module to the provided body element + * @param {Element} [body=document.body] The element that will be used as the parent (defaults to document.body) + * @method attach + */ + attach: function (body) { + angular.element(body || doc.body).append($rootElement); + }, + + /** + * Attaches the $rootElement module to the provided body element + * @method controller + * @param {String} name The name of the controller + * @param {Object} [locals] A key/value map of all the injectable services for when the controller is instantiated + * @return {Object} The instance of the controller + */ + controller: function (name, locals) { + return this.inject('$controller')(name, locals); + }, + + + /** + * @method rootScope + * @return {Object} The $rootScope object of the module + */ + rootScope: function () { + return this.inject('$rootScope'); + }, + + /** + * @method rootElement + * @return {Object} The $rootElement object of the module + */ + rootElement: function () { + return $rootElement; + }, + + /** + * @method viewElement + * @param {string|array} [viewName] if array, the view is searched recursively + * @return {Element} The current element that has ng-view attached to it + */ + viewElement: function (viewName) { + if (!viewName) return angular.element($viewContainer[0].querySelector(viewSelector)); + if (typeof viewName === 'string') { + return angular.element($viewContainer[0].querySelector('[ui-view="' + viewName + '"]')); } - if(that.viewScope().$$phase){ - return false; + var curr = $viewContainer; + for (var i = 0; i < viewName.length; i++) { + //console.log('searching:', viewName[i]); + if (!curr) return curr; + curr = angular.element(curr[0].querySelector('[ui-view="' + viewName[i] + '"]')); } - } - return parseInt($terminalElement.attr('status')) >= $viewCounter; - }, myFun || noop); - } - }, - /** - * Keeps checking an expression until it returns a truthy value and then runs the provided callback - * - * @param {function} exp The given function to poll - * @param {function} callback The given callback to fire once the exp function returns a truthy value - * @method until - */ - until : function(exp, callback) { - - var timer, delay = 30; - timer = setInterval(function() { - if(exp()) { - clearInterval(timer); - callback(); - } - else { - active = true; + return curr; + }, + + /** + * @method viewElement + * @return {Object} The scope of the current view element + */ + viewScope: function (viewName) { + return this.viewElement(viewName).scope(); + }, + + /** + * Runs $scope.$evalAsync() on the provided scope + * @param {function} fn The function to be provided to evalAsync + * @param {Object} [scope=$rootScope] The scope object which will be used for the eval call + * @method evalAsync + */ + evalAsync: function (fn, scope) { + (scope || this.rootScope()).$evalAsync(fn); + }, + + /** + * Compiles and links the given HTML + * + * @method compile + * @param {String|Element} html the html or element node which will be compiled + * @param {Object} [scope=$rootScope] The scope object which will be linked to the compile + * @return {Element} The element node which which is the result of the compilation + */ + compile: function (html, scope) { + return this.inject('$compile')(html)(scope || this.rootScope()); + }, + + /** + * Performs a digest operation on the given scope + * + * @method digest + * @param {Object} [scope=$rootScope] The scope object which will be used for the compilation + */ + digest: function (scope) { + //(scope || this.rootScope()).$digest(); + var s = scope || this.rootScope(); + if (!s.$$phase) { + s.$digest(); + } + else { + var $timeout = this.inject('$timeout'); + //console.log('skipped digest'); + $timeout(this.digest); + } + }, + + /** + * Performs an apply operation on the given scope + * + * @method apply + * @param {function} fn The callback function which will be used in the apply digest + * @param {Object} [scope=$rootScope] scope The scope object which the apply process will be run on + */ + apply: function (fn, scope) { + scope = scope || scope || this.inject('$rootScope'); //$rootScope + scope.$$phase ? fn() : scope.$apply(fn); + }, + + /* + * @method inject + * @param {String} item The name of the service which will be fetched + * @return {Object} The service fetched from the injection call + */ + inject: function (item) { + return $injector.get(item); + }, + + /** + * @method injector + * @return {Object} Returns the AngularJS $injector service + */ + injector: function () { + return $injector; + }, + + /** + * @method path + * @return {String} Returns the path of the current route + */ + path: function () { + return this.inject('$location').path(); + }, + + state: function () { + return this.inject('$state'); + }, + + currentState: function () { + return this.inject('$state').$current; + }, + + + /** + * Changes the current route of the page and then fires the callback when the page has loaded + * + * @param {String} path The given path that the current route will be changed to + * @param {function} [callback] The given callback to fire once the view has been fully loaded + * @param {bool} [noView] when false, does not wait for viewScope() to be available and ready + * @method visit + */ + visit: function (path, callback, noView) { + var that= this,q; + this.delayed(callback, noView)(); + + var $location = this.inject('$location'); + this.apply(function () { + + $location.path(path); + }); + + }, + + delayed: function (callback, noView) { + var that = this; + if (callback.$$$isDelayed) { + return callback; //just to be sure a function is never double delayed + } + if (noView === undefined) { + noView = false; + } + + return function () { + this.$$$isDelayed = true; //just to be sure a function is never double delayed + var args = Array.prototype.slice.call(arguments); + var myFun = function () { + callback.apply(null, args); + }; + that.rootScope().__view_status = ++$viewCounter; + that.until(function () { + if (!noView) { + if (!that.viewScope()) { + return false; + } + if (that.viewScope().$$phase) { + return false; + } + } + return parseInt($terminalElement.attr('status')) >= $viewCounter; + }, myFun || noop); + } + }, + /** + * Keeps checking an expression until it returns a truthy value and then runs the provided callback + * + * @param {function} exp The given function to poll + * @param {function} callback The given callback to fire once the exp function returns a truthy value + * @method until + */ + until: function (exp, callback) { + + var timer, delay = 30; + timer = setInterval(function () { + if (exp()) { + clearInterval(timer); + callback(); + } + else { + active = true; + } + }, delay); + $timers.push(timer); + }, + + + /** + * Removes the $rootElement and clears the module from the page + * + * @method destroy + */ + destroy: function () { + angular.forEach($timers, function (timer) { + clearInterval(timer); + }); + + var body = angular.element(document.body); + body.removeData(); + $rootElement.remove(); + this.rootScope().$destroy(); } - }, delay); - $timers.push(timer); - }, - - - /** - * Removes the $rootElement and clears the module from the page - * - * @method destroy - */ - destroy : function() { - angular.forEach($timers, function(timer) { - clearInterval(timer); - }); - - var body = angular.element(document.body); - body.removeData(); - $rootElement.remove(); - this.rootScope().$destroy(); - } - }; + }; }; diff --git a/test/karma.conf.js b/test/karma.conf.js index 0ca54db..6ce3bc4 100644 --- a/test/karma.conf.js +++ b/test/karma.conf.js @@ -5,12 +5,11 @@ module.exports = function(config) { files : [ './node_modules/chai/chai.js', './bower_components/angular/angular.js', - './bower_components/angular-route/angular-route.js', + './bower_components/angular-ui-router/release/angular-ui-router.min.js', './src/ngMidwayTester.js', - './test/lib/chai.js', - './test/spec/ngMidwayTesterSpec.js' + './test/spec/*.js' ], - singleRun: true, + logLevel : config.LOG_INFO, frameworks: ['mocha'], // Start these browsers, currently available: @@ -30,9 +29,8 @@ module.exports = function(config) { // Which plugins to enable plugins: [ 'karma-phantomjs-launcher', - 'karma-jasmine', - 'karma-mocha' - //'karma-junit-reporter' - ], + 'karma-mocha' + // 'karma-jasmine' + ] }); }; diff --git a/test/lib/chai.js b/test/lib/chai.js deleted file mode 100644 index 35da615..0000000 --- a/test/lib/chai.js +++ /dev/null @@ -1 +0,0 @@ -var expect = chai.expect; diff --git a/test/spec/custom-view.html b/test/spec/custom-view.html index 10ed5ca..441999b 100644 --- a/test/spec/custom-view.html +++ b/test/spec/custom-view.html @@ -1,5 +1,5 @@

Hello

How's it going?

-
+
diff --git a/test/spec/custom-view2.html b/test/spec/custom-view2.html new file mode 100644 index 0000000..eaf0dae --- /dev/null +++ b/test/spec/custom-view2.html @@ -0,0 +1,8 @@ +
+

Hello

+

Nice view

+

{{dog.page}}

+
+ +
+
diff --git a/test/spec/custom-view3.html b/test/spec/custom-view3.html new file mode 100644 index 0000000..89d493c --- /dev/null +++ b/test/spec/custom-view3.html @@ -0,0 +1,7 @@ +
+

View3

+

Sub-Nested view

+
+ +
+
diff --git a/test/spec/custom-view4.html b/test/spec/custom-view4.html new file mode 100644 index 0000000..688b8fa --- /dev/null +++ b/test/spec/custom-view4.html @@ -0,0 +1,6 @@ +
+

Bad view

+
this is old
+ +
this is old, too
+
\ No newline at end of file diff --git a/test/spec/ngMidwayTesterSpec.js b/test/spec/ngMidwayTesterSpec.js index 7f70ecc..806ed64 100644 --- a/test/spec/ngMidwayTesterSpec.js +++ b/test/spec/ngMidwayTesterSpec.js @@ -1,3 +1,5 @@ +var expect = chai.expect; + describe('ngMidwayTester', function() { var tester, @@ -40,86 +42,211 @@ describe('ngMidwayTester', function() { }); describe('template options', function() { - it('should use a custom index.html template string', function(done) { - var example = angular.module(appName, ['ngRoute']) - .run(function($rootScope) { - $rootScope.value = 'true'; - }) - .config(function($routeProvider) { - $routeProvider.when('/path2', { - controller: function($scope) { - $scope.page = 'two'; - }, - template : 'two' + it('should use a custom index.html template string', function (done) { + var example = angular.module(appName, ['ui.router']) + .run(function ($rootScope) { + $rootScope.value = 'true'; + }) + .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $stateProvider + .state('path2', { + url: '/path2', + controller: function ($scope) { + this.woof = 'hello'; + }, + controllerAs: 'dog', + template: 'two {{dog.woof}}' + }) + }); + + + tester = ngMidwayTester(appName, { + template: '

title

' + + '
' + }); + expect(tester.module()).to.equal(example); + expect(tester.rootScope().value).to.equal('true'); + + tester.visit('/path2', function () { + expect(tester.path()).to.equal('/path2'); + expect(tester.viewElement().text()).to.contain('two'); + expect(tester.viewElement().text()).to.contain('hello'); + expect(tester.currentState().controllerAs).to.equal('dog'); + done(); }); - }); - - tester = ngMidwayTester(appName, { - template : '

title

' + - '
' }); - expect(tester.module()).to.equal(example); - expect(tester.rootScope().value).to.equal('true'); - tester.visit('/path2', function() { - expect(tester.path()).to.equal('/path2'); - expect(tester.viewElement().text()).to.contain('two'); - done(); + it('should use a custom index.html template file', function (done) { + var example = angular.module(appName, ['ui.router']) + .run(function ($rootScope) { + $rootScope.value = 'true'; + }) + .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $stateProvider + .state('path-10', { + url: '/path-10', + controller: function ($scope) { + $scope.page = 'ten'; + }, + template: 'ten' + }) + }); + + + tester = ngMidwayTester(appName, { + templateUrl: './test/spec/custom-view.html' + }); + + expect(tester.module()).to.equal(example); + expect(tester.rootScope().value).to.equal('true'); + + tester.visit('/path-10', function () { + expect(tester.path()).to.equal('/path-10'); + var html = tester.rootElement().html(); + expect(html).to.contain('
'); + expect(html).to.contain('ten'); + done(); + }); }); - }); - it('should use a custom index.html template file', function(done) { - var example = angular.module(appName, ['ngRoute']) - .run(function($rootScope) { - $rootScope.value = 'true'; - }) - .config(function($routeProvider) { - $routeProvider.when('/path-10', { - controller: function($scope) { - $scope.page = 'ten'; - }, - template : 'ten' + + + it('should use a custom index.html template file with nested views', function (done) { + var example = angular.module(appName, ['ui.router']) + .run(function ($rootScope) { + $rootScope.value = 'true'; + }) + .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $stateProvider + .state('nested', { + url: '/nested', + controller: function ($scope) { + $scope.page = 'ten'; + }, + views:{ + 'main':{ + template:'MainContent' + }, + 'secondary':{ + template:'SecondaryContent' + } + } + }) + }); + + + tester = ngMidwayTester(appName, { + templateUrl: './test/spec/custom-view2.html' }); - }); - tester = ngMidwayTester(appName, { - templateUrl : './test/spec/custom-view.html' - }); + expect(tester.module()).to.equal(example); + expect(tester.rootScope().value).to.equal('true'); - expect(tester.module()).to.equal(example); - expect(tester.rootScope().value).to.equal('true'); + tester.visit('/nested', function () { - tester.visit('/path-10', function() { - expect(tester.path()).to.equal('/path-10'); - var html = tester.rootElement().html(); - expect(html).to.contain('
'); - expect(html).to.contain('ten'); - done(); + expect(tester.path()).to.equal('/nested'); + var html = tester.rootElement().html(); + expect(html).to.contain('Nice view'); + expect(html).to.contain('MainContent'); + done(); + }); }); - }); - it('should throw an error when a file downloaded from templateUrl is not found', function() { - var example = angular.module(appName, ['ngRoute']) - .run(function($rootScope) { - $rootScope.value = 'true'; - }) - .config(function($routeProvider) { - $routeProvider.when('/path-10', { - controller: function($scope) { - $scope.page = 'ten'; - }, - template : 'ten' + it('should get data from nested views', function (done) { + var example = angular.module(appName, ['ui.router']) + .run(function ($rootScope) { + $rootScope.value = 'true'; + }) + .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $stateProvider + .state('nested', { + url: '/nested', + controller: function ($scope) { + this.page = 'ten'; + }, + controllerAs:'cat', + views: { + 'main': { + template: 'MainContent' + }, + 'secondary': { + templateUrl: './test/spec/custom-view4.html' + }, + 'main3@nested': { + template: 'hot content' + }, + 'secondary3@nested': { + templateUrl: './test/spec/custom-view3.html' + }, + 'blue@nested': { + template: 'I like blue' + }, + 'red@nested': { + template: 'I also like red' + } + + + } + }) + }); + + + tester = ngMidwayTester(appName, { + templateUrl: './test/spec/custom-view2.html' }); - }); - var fn = function() { - tester = ngMidwayTester(appName, { - templateUrl : '../../some-file.html' - }); - }; + expect(tester.module()).to.equal(example); + expect(tester.rootScope().value).to.equal('true'); - expect(fn).to.throw('ngMidwayTester: Unable to download template file'); - }); + tester.visit('/nested', function () { + + + + + + expect(tester.path()).to.equal('/nested'); + var htmlMain = tester.viewElement('main').html(); + expect(htmlMain).to.contain('MainContent'); + var htmlSecondary = tester.viewElement('secondary').html(); + expect(htmlSecondary).to.contain('Bad view'); + var htmlSecondaryMain = tester.viewElement(['secondary', 'main3']).html(); + expect(htmlSecondaryMain).to.contain('hot content'); + var htmlBlueSecondaryMain = tester.viewElement(['secondary', 'secondary3', 'blue']).html(); + expect(htmlBlueSecondaryMain).to.contain('I like blue'); + var htmlRedSecondaryMain = tester.viewElement(['secondary', 'secondary3', 'red']).html(); + expect(htmlRedSecondaryMain).to.contain('I also like red'); + done(); + }); + + + }); + + + it('should throw an error when a file downloaded from templateUrl is not found', function () { + var example = angular.module(appName, ['ui.router']) + .run(function ($rootScope) { + $rootScope.value = 'true'; + }) + .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $stateProvider + .state('path-10', { + url: '/path-10', + controller: function ($scope) { + $scope.page = 'ten'; + }, + template: 'ten' + }) + }); + + + var fn = function () { + tester = ngMidwayTester(appName, { + templateUrl: '../../some-file.html' + }); + }; + + expect(fn).to.throw('ngMidwayTester: Unable to download template file'); + }); }); describe('scope', function() { @@ -154,53 +281,57 @@ describe('ngMidwayTester', function() { }); describe('routing', function() { - it('should change the path', function(done) { - var example = angular.module(appName, ['ngRoute']) - .run(function($rootScope) { - $rootScope.value = 'true'; - }); - - tester = ngMidwayTester(appName, true); - tester.visit('/', function() { - expect(tester.path()).to.equal('/'); - done(); - },true); - }); + it('should change the path', function (done) { + var example = angular.module(appName, ['ui.router']) + .run(function ($rootScope) { + $rootScope.value = 'true'; + }); + + tester = ngMidwayTester(appName, true); + tester.visit('/', function () { + expect(tester.path()).to.equal('/'); + done(); + }, true); + }); - it('should update the when by the time the callback is called', function(done) { - var example = angular.module(appName, ['ngRoute']) - .config(function($routeProvider) { - $routeProvider.when('/path', { - controller: function($scope) { - $scope.page = 'one'; - }, - template : '...' - }); - $routeProvider.when('/path2', { - controller: function($scope) { - $scope.page = 'two'; - }, - template : '===' + it('should update the when by the time the callback is called', function (done) { + var example = angular.module(appName, ['ui.router']) + .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $stateProvider + .state('path', { + url: '/path', + controller: function ($scope) { + $scope.page = 'one'; + }, + template: '...' + }) + .state('path2', { + url: '/path2', + controller: function ($scope) { + $scope.page = 'two'; + }, + template: '===' + }) + }); + + + tester = ngMidwayTester(appName, true); + tester.attach(); + + tester.visit('/path', function () { + expect(tester.path()).to.equal('/path'); + expect(tester.rootElement().text()).to.equal('...'); + expect(tester.viewScope().page).to.equal('one'); + + tester.visit('/path2', function () { + expect(tester.path()).to.equal('/path2'); + expect(tester.rootElement().text()).to.equal('==='); + expect(tester.viewScope().page).to.equal('two'); + + done(); + }); }); - }); - - tester = ngMidwayTester(appName, true); - tester.attach(); - - tester.visit('/path', function() { - expect(tester.path()).to.equal('/path'); - expect(tester.rootElement().text()).to.equal('...'); - expect(tester.viewScope().page).to.equal('one'); - - tester.visit('/path2', function() { - expect(tester.path()).to.equal('/path2'); - expect(tester.rootElement().text()).to.equal('==='); - expect(tester.viewScope().page).to.equal('two'); - - done(); - }); }); - }); }); describe('controllers', function() { From 5713e9764905f7fb23ee8af256236786fa5242d2 Mon Sep 17 00:00:00 2001 From: Gaetano Lazzo Date: Mon, 2 Nov 2015 09:55:57 +0000 Subject: [PATCH 17/17] Corrected test and added utility functions to the tester --- src/ngMidwayTester.js | 107 +++++++++++++++++++++++---- test/spec/custom-view2.html | 13 ++-- test/spec/ngMidwayTesterSpec.js | 127 ++++++++++++++++++++------------ 3 files changed, 178 insertions(+), 69 deletions(-) diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 0b03307..8af260b 100644 --- a/src/ngMidwayTester.js +++ b/src/ngMidwayTester.js @@ -266,16 +266,10 @@ var ngMidwayTester = function (moduleName, options, deferred) { /** * Changes the current route of the page and then fires the callback when the page has loaded - * - * @param {String} path The given path that the current route will be changed to - * @param {function} [callback] The given callback to fire once the view has been fully loaded - * @param {bool} [noView] when false, does not wait for viewScope() to be available and ready * @method visit + * @param {String} path The given path that the current route will be changed to */ - visit: function (path, callback, noView) { - var that= this,q; - this.delayed(callback, noView)(); - + visit: function (path) { var $location = this.inject('$location'); this.apply(function () { @@ -284,21 +278,102 @@ var ngMidwayTester = function (moduleName, options, deferred) { }, - delayed: function (callback, noView) { + /** + * @method timeOut + * Calls a method after a angular timeout + * @param fn + */ + callFnOnTimeOut: function(fn, args){ + var $timeout = this.inject('$timeout'); + return function() { + $timeout(function () { + fn.apply(null, args); + }) + } + }, + + /** + * Transforms a function into a function that will be called after a view will be available + * @method waitForViewElement + * @param {function} [fn] The given callback to fire once the view has been fully loaded + * @param {bool} [noView] when false, does not wait for viewScope() to be available and ready + * @returns {*} + */ + waitForViewScopeCondition: function (fn, viewName, condition){ + var that = this; + if (fn.$$$isViewDelayed) { + return fn; //just to be sure a function is never double delayed + } + return function () { + this.$$$isViewDelayed = true; //just to be sure a function is never double delayed + var args = Array.prototype.slice.call(arguments); + var myFun = that.callFnOnTimeOut(fn,args); + that.until(function () { + //console.log(that.viewElement(viewName)); + if (!that.viewElement(viewName))return false; + if (!that.viewElement(viewName).scope) return false; + var scope = that.viewElement(viewName).scope(); + //console.log(scope); + return condition(scope); + }, myFun || noop); + } + }, + + waitForControllerInViewScope: function (fn, viewName, controllerName){ + return this.waitForViewScopeCondition(fn, viewName, function(scope){ + if (!scope) return false; + if (!scope[controllerName])return false; + return (scope[controllerName].constructor instanceof Function); + }) + }, + + + /** + * Transforms a function into a function that will be called after a view will be available + * @method waitForViewElement + * @param {function} [fn] The given callback to fire once the view has been fully loaded + * @param {bool} [noView] when false, does not wait for viewScope() to be available and ready + * @returns {*} + */ + waitForViewCondition: function (fn, viewName, condition){ var that = this; - if (callback.$$$isDelayed) { - return callback; //just to be sure a function is never double delayed + if (fn.$$$isViewDelayed) { + return fn; //just to be sure a function is never double delayed + } + return function () { + this.$$$isViewDelayed = true; //just to be sure a function is never double delayed + var args = Array.prototype.slice.call(arguments); + var myFun = that.callFnOnTimeOut(fn,args); + that.until(function () { + var view=that.viewElement(viewName); + if (!view)return false; + //console.log(scope); + return condition(view); + }, myFun || noop); + } + }, + + + /** + * Transforms a function into a function that will be called after a complete digest will have happened + * @method waitForDigest + * @param {function} [fn] The given callback to fire once the view has been fully loaded + * @param {bool} [noView] when false, does not wait for viewScope() to be available and ready + * @returns {*} + */ + waitForDigest: function (fn, noView) { + var that = this; + if (fn.$$$isDigestDelayed) { + return fn; //just to be sure a function is never double delayed } if (noView === undefined) { noView = false; } return function () { - this.$$$isDelayed = true; //just to be sure a function is never double delayed + this.$$$isDigestDelayed = true; //just to be sure a function is never double delayed var args = Array.prototype.slice.call(arguments); - var myFun = function () { - callback.apply(null, args); - }; + var myFun = that.callFnOnTimeOut(fn,args); that.rootScope().__view_status = ++$viewCounter; that.until(function () { if (!noView) { @@ -322,7 +397,7 @@ var ngMidwayTester = function (moduleName, options, deferred) { */ until: function (exp, callback) { - var timer, delay = 30; + var timer, delay = 0; timer = setInterval(function () { if (exp()) { clearInterval(timer); diff --git a/test/spec/custom-view2.html b/test/spec/custom-view2.html index eaf0dae..62bf4ec 100644 --- a/test/spec/custom-view2.html +++ b/test/spec/custom-view2.html @@ -1,8 +1,9 @@
-

Hello

-

Nice view

-

{{dog.page}}

-
- -
+
+

Hello

+

Nice view

+

{{dog.page}}

+
+
+
diff --git a/test/spec/ngMidwayTesterSpec.js b/test/spec/ngMidwayTesterSpec.js index 806ed64..bb79f54 100644 --- a/test/spec/ngMidwayTesterSpec.js +++ b/test/spec/ngMidwayTesterSpec.js @@ -67,13 +67,14 @@ describe('ngMidwayTester', function() { expect(tester.module()).to.equal(example); expect(tester.rootScope().value).to.equal('true'); - tester.visit('/path2', function () { + tester.visit('/path2'); + tester.waitForControllerInViewScope(function () { expect(tester.path()).to.equal('/path2'); expect(tester.viewElement().text()).to.contain('two'); expect(tester.viewElement().text()).to.contain('hello'); expect(tester.currentState().controllerAs).to.equal('dog'); done(); - }); + },null,'dog')(); }); it('should use a custom index.html template file', function (done) { @@ -91,8 +92,6 @@ describe('ngMidwayTester', function() { template: 'ten' }) }); - - tester = ngMidwayTester(appName, { templateUrl: './test/spec/custom-view.html' }); @@ -100,34 +99,44 @@ describe('ngMidwayTester', function() { expect(tester.module()).to.equal(example); expect(tester.rootScope().value).to.equal('true'); - tester.visit('/path-10', function () { + tester.visit('/path-10'); + tester.waitForViewScopeCondition(function () { expect(tester.path()).to.equal('/path-10'); var html = tester.rootElement().html(); expect(html).to.contain('
'); expect(html).to.contain('ten'); + var viewEl = tester.viewElement().html(); + expect(viewEl).to.contain('ten'); done(); - }); + },null, + function(scope){ + return scope.page; + })(); }); - - it('should use a custom index.html template file with nested views', function (done) { var example = angular.module(appName, ['ui.router']) .run(function ($rootScope) { $rootScope.value = 'true'; }) .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $urlRouterProvider.otherwise("/nested"); + $locationProvider.html5Mode(false).hashPrefix('!'); $stateProvider .state('nested', { url: '/nested', - controller: function ($scope) { - $scope.page = 'ten'; - }, + views:{ - 'main':{ + '':{ + controller: function ($scope) { + this.page = 'ten'; + }, + controllerAs:'dog' + }, + 'main@nested':{ template:'MainContent' }, - 'secondary':{ + 'secondary@nested':{ template:'SecondaryContent' } } @@ -142,14 +151,16 @@ describe('ngMidwayTester', function() { expect(tester.module()).to.equal(example); expect(tester.rootScope().value).to.equal('true'); - tester.visit('/nested', function () { - + tester.visit('/nested'); + tester.waitForViewScopeCondition(function () { expect(tester.path()).to.equal('/nested'); - var html = tester.rootElement().html(); + var html = tester.viewElement().html(); expect(html).to.contain('Nice view'); expect(html).to.contain('MainContent'); done(); - }); + },null,function(scope){ + return scope.dog && scope.dog.page; + })(); }); it('should get data from nested views', function (done) { @@ -158,18 +169,22 @@ describe('ngMidwayTester', function() { $rootScope.value = 'true'; }) .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $urlRouterProvider.otherwise("/nested"); + $locationProvider.html5Mode(false).hashPrefix('!'); $stateProvider .state('nested', { url: '/nested', - controller: function ($scope) { - this.page = 'ten'; - }, - controllerAs:'cat', views: { - 'main': { + '':{ + controller: function ($scope) { + this.page = 'ten'; + }, + controllerAs:'cat' + }, + 'main@nested': { template: 'MainContent' }, - 'secondary': { + 'secondary@nested': { templateUrl: './test/spec/custom-view4.html' }, 'main3@nested': { @@ -184,8 +199,6 @@ describe('ngMidwayTester', function() { 'red@nested': { template: 'I also like red' } - - } }) }); @@ -198,12 +211,8 @@ describe('ngMidwayTester', function() { expect(tester.module()).to.equal(example); expect(tester.rootScope().value).to.equal('true'); - tester.visit('/nested', function () { - - - - - + tester.visit('/nested'); + tester.waitForControllerInViewScope(function () { expect(tester.path()).to.equal('/nested'); var htmlMain = tester.viewElement('main').html(); expect(htmlMain).to.contain('MainContent'); @@ -216,7 +225,7 @@ describe('ngMidwayTester', function() { var htmlRedSecondaryMain = tester.viewElement(['secondary', 'secondary3', 'red']).html(); expect(htmlRedSecondaryMain).to.contain('I also like red'); done(); - }); + },null,'cat')(); }); @@ -228,6 +237,9 @@ describe('ngMidwayTester', function() { $rootScope.value = 'true'; }) .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $urlRouterProvider.otherwise("/path-10"); + $locationProvider.html5Mode(false).hashPrefix('!'); + $stateProvider .state('path-10', { url: '/path-10', @@ -288,29 +300,43 @@ describe('ngMidwayTester', function() { }); tester = ngMidwayTester(appName, true); - tester.visit('/', function () { + tester.visit('/'); + tester.waitForDigest(function () { expect(tester.path()).to.equal('/'); done(); - }, true); + })(); }); it('should update the when by the time the callback is called', function (done) { var example = angular.module(appName, ['ui.router']) .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $urlRouterProvider.otherwise("/path"); + $locationProvider.html5Mode(false).hashPrefix('!'); + $stateProvider .state('path', { url: '/path', - controller: function ($scope) { - $scope.page = 'one'; - }, - template: '...' + views:{ + '':{ + controller: function ($scope) { + $scope.page = 'one'; + }, + template: '...' + } + } + }) .state('path2', { url: '/path2', - controller: function ($scope) { - $scope.page = 'two'; - }, - template: '===' + views: { + '':{ + controller: function ($scope) { + $scope.page = 'two'; + }, + template: '===' + } + } + }) }); @@ -318,19 +344,26 @@ describe('ngMidwayTester', function() { tester = ngMidwayTester(appName, true); tester.attach(); - tester.visit('/path', function () { + tester.visit('/path'); + var f1, f2; + f1 = tester.waitForViewScopeCondition(function(){ expect(tester.path()).to.equal('/path'); expect(tester.rootElement().text()).to.equal('...'); expect(tester.viewScope().page).to.equal('one'); - - tester.visit('/path2', function () { + tester.visit('/path2'); + f2(); + },null,function(scope){ + return scope.page && scope.page==='one'; + }); + f2 = tester.waitForViewScopeCondition(function () { expect(tester.path()).to.equal('/path2'); expect(tester.rootElement().text()).to.equal('==='); expect(tester.viewScope().page).to.equal('two'); - done(); - }); + }, null,function(scope){ + return scope.page && scope.page==='two'; }); + f1(); }); });