diff --git a/.gitignore b/.gitignore index 107a9ca..9e5b91b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ libpeerconnection.log npm-debug.log .agignore /coverage +.idea +/.idea/jsLibraryMappings.xml +/.idea/misc.xml +/.idea/libraries/ngMidwayTester_node_modules.xml 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..14be849 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,10 @@ { "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.7", + "angular-ui-router": "0.2.15" + }, + "dependencies": { + "jasmine-core": "jasmine#~2.3.4" } } diff --git a/package.json b/package.json index 222c0d9..66d85b8 100644 --- a/package.json +++ b/package.json @@ -1,33 +1,39 @@ { "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" }, "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": "~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.1.3", - "karma-requirejs": "~0.1.0", - "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-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.8.1", + "chai": "^1.10.0", "karma-coverage": "~0.1.0" } + } diff --git a/src/ngMidwayTester.js b/src/ngMidwayTester.js index 08216b6..8af260b 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 @@ -12,254 +12,419 @@ * @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) { - - 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 midwayModule = angular.module('ngMidway', []); - - 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; - $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); - - var $injector = angular.bootstrap($rootElement, ['ng','ngMidway',moduleName]); - var $rootModule = angular.module(moduleName); - angular.element(doc.body).append($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(); - }, - - /** - * 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 || this.inject('$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) { - 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); - }); - }, - - /** - * 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 = 50; - timer = setInterval(function() { - if(exp()) { - clearTimeout(timer); - callback(); + + 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'); } - }, delay); - $timers.push(timer); - }, - - /** - * Removes the $rootElement and clears the module from the page - * - * @method destroy - */ - destroy : function() { - angular.forEach($timers, function(timer) { - clearTimeout(timer); - }); - - var body = angular.element(document.body); - body.removeData(); - $rootElement.remove(); - this.rootScope().$destroy(); + + 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 + '"]')); + } + 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 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 + * @method visit + * @param {String} path The given path that the current route will be changed to + */ + visit: function (path) { + var $location = this.inject('$location'); + this.apply(function () { + + $location.path(path); + }); + + }, + + /** + * @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 (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.$$$isDigestDelayed = 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.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 = 0; + 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(); + } + }; }; diff --git a/test/karma.conf.js b/test/karma.conf.js index 8f08d49..6ce3bc4 100644 --- a/test/karma.conf.js +++ b/test/karma.conf.js @@ -1,19 +1,36 @@ 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-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'], - 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-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 3ecf9f1..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..62bf4ec --- /dev/null +++ b/test/spec/custom-view2.html @@ -0,0 +1,9 @@ +
+
+

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 f88b9c2..bb79f54 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,223 @@ 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

' + + '
' }); - }); - - tester = ngMidwayTester(appName, { - template : '

title

' + - '
' + expect(tester.module()).to.equal(example); + expect(tester.rootScope().value).to.equal('true'); + + 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')(); }); - 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, ['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', 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' }); - }); - 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'); + 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; + })(); }); - expect(tester.module()).to.equal(example); - expect(tester.rootScope().value).to.equal('true'); + 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', + + views:{ + '':{ + controller: function ($scope) { + this.page = 'ten'; + }, + controllerAs:'dog' + }, + 'main@nested':{ + template:'MainContent' + }, + 'secondary@nested':{ + template:'SecondaryContent' + } + } + }) + }); + + + tester = ngMidwayTester(appName, { + templateUrl: './test/spec/custom-view2.html' + }); - 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.module()).to.equal(example); + expect(tester.rootScope().value).to.equal('true'); + + tester.visit('/nested'); + tester.waitForViewScopeCondition(function () { + expect(tester.path()).to.equal('/nested'); + 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 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) { + $urlRouterProvider.otherwise("/nested"); + $locationProvider.html5Mode(false).hashPrefix('!'); + $stateProvider + .state('nested', { + url: '/nested', + views: { + '':{ + controller: function ($scope) { + this.page = 'ten'; + }, + controllerAs:'cat' + }, + 'main@nested': { + template: 'MainContent' + }, + 'secondary@nested': { + 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'); + + tester.visit('/nested'); + tester.waitForControllerInViewScope(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(); + },null,'cat')(); - expect(fn).to.throw('ngMidwayTester: Unable to download template file'); - }); + + }); + + + 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) { + $urlRouterProvider.otherwise("/path-10"); + $locationProvider.html5Mode(false).hashPrefix('!'); + + $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 +293,78 @@ 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(); + 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('/'); + tester.waitForDigest(function () { + expect(tester.path()).to.equal('/'); + done(); + })(); }); - }); - 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 : '...' + 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', + views:{ + '':{ + controller: function ($scope) { + $scope.page = 'one'; + }, + template: '...' + } + } + + }) + .state('path2', { + url: '/path2', + views: { + '':{ + controller: function ($scope) { + $scope.page = 'two'; + }, + template: '===' + } + } + + }) + }); + + + tester = ngMidwayTester(appName, true); + tester.attach(); + + 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'); + f2(); + },null,function(scope){ + return scope.page && scope.page==='one'; }); - $routeProvider.when('/path2', { - controller: function($scope) { - $scope.page = 'two'; - }, - template : '===' + 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'; }); - }); - - 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(); - }); + f1(); }); - }); }); describe('controllers', function() { @@ -263,4 +427,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); + }); + }) });