diff options
Diffstat (limited to 'test/ngScenario')
| -rw-r--r-- | test/ngScenario/ApplicationSpec.js | 141 | ||||
| -rw-r--r-- | test/ngScenario/DescribeSpec.js | 122 | ||||
| -rw-r--r-- | test/ngScenario/FutureSpec.js | 77 | ||||
| -rw-r--r-- | test/ngScenario/ObjectModelSpec.js | 333 | ||||
| -rw-r--r-- | test/ngScenario/RunnerSpec.js | 116 | ||||
| -rw-r--r-- | test/ngScenario/ScenarioSpec.js | 32 | ||||
| -rw-r--r-- | test/ngScenario/SpecRunnerSpec.js | 177 | ||||
| -rw-r--r-- | test/ngScenario/dslSpec.js | 629 | ||||
| -rw-r--r-- | test/ngScenario/e2e/Runner-compiled.html | 9 | ||||
| -rw-r--r-- | test/ngScenario/e2e/Runner.html | 9 | ||||
| -rw-r--r-- | test/ngScenario/e2e/style.css | 11 | ||||
| -rw-r--r-- | test/ngScenario/e2e/widgets-scenario.js | 67 | ||||
| -rw-r--r-- | test/ngScenario/e2e/widgets.html | 99 | ||||
| -rw-r--r-- | test/ngScenario/jstd-scenario-adapter/AdapterSpecs.js | 322 | ||||
| -rw-r--r-- | test/ngScenario/matchersSpec.js | 51 | ||||
| -rw-r--r-- | test/ngScenario/mocks.js | 33 | ||||
| -rw-r--r-- | test/ngScenario/output/HtmlSpec.js | 126 | ||||
| -rw-r--r-- | test/ngScenario/output/jsonSpec.js | 37 | ||||
| -rw-r--r-- | test/ngScenario/output/objectSpec.js | 40 | ||||
| -rw-r--r-- | test/ngScenario/output/xmlSpec.js | 36 |
20 files changed, 2467 insertions, 0 deletions
diff --git a/test/ngScenario/ApplicationSpec.js b/test/ngScenario/ApplicationSpec.js new file mode 100644 index 00000000..7384ecaa --- /dev/null +++ b/test/ngScenario/ApplicationSpec.js @@ -0,0 +1,141 @@ +'use strict'; + +describe('angular.scenario.Application', function() { + var $window; + var app, frames; + + function callLoadHandlers(app) { + var handlers = app.getFrame_().data('events').load; + expect(handlers).toBeDefined(); + expect(handlers.length).toEqual(1); + handlers[0].handler(); + } + + beforeEach(function() { + frames = _jQuery("<div></div>"); + app = new angular.scenario.Application(frames); + }); + + it('should return new $window and $document after navigate', function() { + var called; + var testWindow, testDocument, counter = 0; + app.getWindow_ = function() { + return {x:counter++, document:{x:counter++}}; + }; + app.navigateTo('http://www.google.com/'); + app.executeAction(function($document, $window) { + testWindow = $window; + testDocument = $document; + }); + app.navigateTo('http://www.google.com/'); + app.executeAction(function($window, $document) { + expect($window).not.toEqual(testWindow); + expect($document).not.toEqual(testDocument); + called = true; + }); + expect(called).toBeTruthy(); + }); + + it('should execute callback with correct arguments', function() { + var called; + var testWindow = {document: {}}; + app.getWindow_ = function() { + return testWindow; + }; + app.executeAction(function($window, $document) { + expect(this).toEqual(app); + expect($document).toEqual(_jQuery($window.document)); + expect($window).toEqual(testWindow); + called = true; + }); + expect(called).toBeTruthy(); + }); + + it('should use a new iframe each time', function() { + app.navigateTo('http://localhost/'); + var frame = app.getFrame_(); + frame.attr('test', true); + app.navigateTo('http://localhost/'); + expect(app.getFrame_().attr('test')).toBeFalsy(); + }); + + it('should call error handler if document not accessible', function() { + var called; + app.getWindow_ = function() { + return {}; + }; + app.navigateTo('http://localhost/', angular.noop, function(error) { + expect(error).toMatch(/Sandbox Error/); + called = true; + }); + callLoadHandlers(app); + expect(called).toBeTruthy(); + }); + + it('should call error handler if navigating to about:blank', function() { + var called; + app.navigateTo('about:blank', angular.noop, function(error) { + expect(error).toMatch(/Sandbox Error/); + called = true; + }); + expect(called).toBeTruthy(); + }); + + it('should remove old iframes', function() { + app.navigateTo('http://localhost/#foo'); + frames.find('iframe')[0].id = 'test'; + + app.navigateTo('http://localhost/#bar'); + var iframes = frames.find('iframe'); + + expect(iframes.length).toEqual(1); + expect(iframes[0].src).toEqual('http://localhost/#bar'); + expect(iframes[0].id).toBeFalsy(); + }); + + it('should URL update description bar', function() { + app.navigateTo('http://localhost/'); + var anchor = frames.find('> h2 a'); + expect(anchor.attr('href')).toEqual('http://localhost/'); + expect(anchor.text()).toEqual('http://localhost/'); + }); + + it('should call onload handler when frame loads', function() { + var called; + app.getWindow_ = function() { + return {document: {}}; + }; + app.navigateTo('http://localhost/', function($window, $document) { + called = true; + }); + callLoadHandlers(app); + expect(called).toBeTruthy(); + }); + + it('should wait for pending requests in executeAction', inject(function($injector, $browser) { + var called, polled; + var handlers = []; + var testWindow = { + document: jqLite('<div class="test-foo" ng-app></div>')[0], + angular: { + element: jqLite, + service: {} + } + }; + $browser.notifyWhenNoOutstandingRequests = function(fn) { + handlers.push(fn); + }; + jqLite(testWindow.document).data('$injector', $injector); + app.getWindow_ = function() { + return testWindow; + }; + app.executeAction(function($window, $document) { + expect($window).toEqual(testWindow); + expect($document).toBeDefined(); + expect($document[0].className).toEqual('test-foo'); + }); + expect(handlers.length).toEqual(1); + handlers[0](); + dealoc(testWindow.document); + })); +}); diff --git a/test/ngScenario/DescribeSpec.js b/test/ngScenario/DescribeSpec.js new file mode 100644 index 00000000..6741ed6d --- /dev/null +++ b/test/ngScenario/DescribeSpec.js @@ -0,0 +1,122 @@ +'use strict'; + +describe('angular.scenario.Describe', function() { + var log; + var root; + + beforeEach(function() { + root = new angular.scenario.Describe(); + + /** + * Simple callback logging system. Use to assert proper order of calls. + */ + log = function(text) { + log.text = log.text + text; + }; + log.fn = function(text) { + return function(done){ + log(text); + (done || angular.noop)(); + }; + }; + log.reset = function() { + log.text = ''; + }; + log.reset(); + }); + + it('should handle basic nested case', function() { + root.describe('A', function() { + this.beforeEach(log.fn('{')); + this.afterEach(log.fn('}')); + this.it('1', log.fn('1')); + this.describe('B', function() { + this.beforeEach(log.fn('(')); + this.afterEach(log.fn(')')); + this.it('2', log.fn('2')); + }); + }); + var specs = root.getSpecs(); + expect(specs.length).toEqual(2); + + expect(specs[0].name).toEqual('2'); + specs[0].before(); + specs[0].body(); + specs[0].after(); + expect(log.text).toEqual('{(2)}'); + + log.reset(); + expect(specs[1].name).toEqual('1'); + specs[1].before(); + specs[1].body(); + specs[1].after(); + expect(log.text).toEqual('{1}'); + }); + + it('should link nested describe blocks with parent and children', function() { + root.describe('A', function() { + this.it('1', angular.noop); + this.describe('B', function() { + this.it('2', angular.noop); + this.describe('C', function() { + this.it('3', angular.noop); + }); + }); + }); + var specs = root.getSpecs(); + expect(specs[2].definition.parent).toEqual(root); + expect(specs[0].definition.parent).toEqual(specs[2].definition.children[0]); + }); + + it('should not process xit and xdescribe', function() { + root.describe('A', function() { + this.xit('1', angular.noop); + this.xdescribe('B', function() { + this.it('2', angular.noop); + this.describe('C', function() { + this.it('3', angular.noop); + }); + }); + }); + var specs = root.getSpecs(); + expect(specs.length).toEqual(0); + }); + + it('should only return iit and ddescribe if present', function() { + root.describe('A', function() { + this.it('1', angular.noop); + this.iit('2', angular.noop); + this.describe('B', function() { + this.it('3', angular.noop); + this.ddescribe('C', function() { + this.it('4', angular.noop); + this.describe('D', function() { + this.it('5', angular.noop); + }); + }); + }); + }); + var specs = root.getSpecs(); + expect(specs.length).toEqual(3); + expect(specs[0].name).toEqual('5'); + expect(specs[1].name).toEqual('4'); + expect(specs[2].name).toEqual('2'); + }); + + it('should create uniqueIds in the tree', function() { + angular.scenario.Describe.id = 0; + var a = new angular.scenario.Describe(); + var b = new angular.scenario.Describe(); + expect(a.id).toNotEqual(b.id); + }); + + it('should create uniqueIds for each spec', function() { + var d = new angular.scenario.Describe(); + d.it('fake', function() {}); + d.it('fake', function() {}); + + expect(d.its[0].id).toBeDefined(); + expect(d.its[1].id).toBeDefined(); + expect(d.its[0].id).not.toEqual(d.its[1].id); + }); +}); diff --git a/test/ngScenario/FutureSpec.js b/test/ngScenario/FutureSpec.js new file mode 100644 index 00000000..2a75f275 --- /dev/null +++ b/test/ngScenario/FutureSpec.js @@ -0,0 +1,77 @@ +'use strict'; + +describe('angular.scenario.Future', function() { + var future; + + it('should set the sane defaults', function() { + var behavior = function() {}; + var future = new angular.scenario.Future('test name', behavior, 'foo'); + expect(future.name).toEqual('test name'); + expect(future.behavior).toEqual(behavior); + expect(future.line).toEqual('foo'); + expect(future.value).toBeUndefined(); + expect(future.fulfilled).toBeFalsy(); + expect(future.parser).toEqual(angular.identity); + }); + + it('should be fulfilled after execution and done callback', function() { + var future = new angular.scenario.Future('test name', function(done) { + done(); + }); + future.execute(angular.noop); + expect(future.fulfilled).toBeTruthy(); + }); + + it('should take callback with (error, result) and forward', function() { + var future = new angular.scenario.Future('test name', function(done) { + done(10, 20); + }); + future.execute(function(error, result) { + expect(error).toEqual(10); + expect(result).toEqual(20); + }); + }); + + it('should use error as value if provided', function() { + var future = new angular.scenario.Future('test name', function(done) { + done(10, 20); + }); + future.execute(angular.noop); + expect(future.value).toEqual(10); + }); + + it('should parse json with fromJson', function() { + var future = new angular.scenario.Future('test name', function(done) { + done(null, "{test: 'foo'}"); + }); + future.fromJson().execute(angular.noop); + expect(future.value).toEqual({test: 'foo'}); + }); + + it('should convert to json with toJson', function() { + var future = new angular.scenario.Future('test name', function(done) { + done(null, {test: 'foo'}); + }); + future.toJson().execute(angular.noop); + expect(future.value).toEqual('{"test":"foo"}'); + }); + + it('should convert with custom parser', function() { + var future = new angular.scenario.Future('test name', function(done) { + done(null, 'foo'); + }); + future.parsedWith(function(value) { + return value.toUpperCase(); + }).execute(angular.noop); + expect(future.value).toEqual('FOO'); + }); + + it('should pass error if parser fails', function() { + var future = new angular.scenario.Future('test name', function(done) { + done(null, '{'); + }); + future.fromJson().execute(function(error, result) { + expect(error).toBeDefined(); + }); + }); +}); diff --git a/test/ngScenario/ObjectModelSpec.js b/test/ngScenario/ObjectModelSpec.js new file mode 100644 index 00000000..e0da628d --- /dev/null +++ b/test/ngScenario/ObjectModelSpec.js @@ -0,0 +1,333 @@ +'use strict'; + +describe('angular.scenario.ObjectModel', function() { + var model; + var runner; + var spec, step; + + function buildSpec(id, name, definitions) { + var spec = { + id: id, + name: name, + definition: { + name: definitions.shift() + } + }; + var currentDef = spec.definition; + + forEach(definitions, function(defName) { + currentDef.parent = { + name: defName + }; + currentDef = currentDef.parent; + }); + + return spec; + } + + function buildStep(name, line) { + return { + name: name || 'test step', + line: function() { return line || ''; } + }; + } + + beforeEach(function() { + spec = buildSpec(1, 'test spec', ['describe 1']); + step = buildStep(); + runner = new angular.scenario.testing.MockRunner(); + model = new angular.scenario.ObjectModel(runner); + }); + + it('should value default empty value', function() { + expect(model.value).toEqual({ + name: '', + children: [] + }); + }); + + it('should add spec and create describe blocks on SpecBegin event', function() { + runner.emit('SpecBegin', buildSpec(1, 'test spec', ['describe 2', 'describe 1'])); + + expect(model.value.children['describe 1']).toBeDefined(); + expect(model.value.children['describe 1'].children['describe 2']).toBeDefined(); + expect(model.value.children['describe 1'].children['describe 2'].specs['test spec']).toBeDefined(); + }); + + it('should set fullDefinitionName on SpecBegin event', function() { + runner.emit('SpecBegin', buildSpec(1, 'fake spec', ['describe 2'])); + var spec = model.getSpec(1); + + expect(spec.fullDefinitionName).toBeDefined(); + expect(spec.fullDefinitionName).toEqual('describe 2'); + }); + + it('should set fullDefinitionName on SpecBegin event (join more names by space)', function() { + runner.emit('SpecBegin', buildSpec(1, 'fake spec', ['describe 2', 'describe 1'])); + var spec = model.getSpec(1); + + expect(spec.fullDefinitionName).toEqual('describe 1 describe 2'); + }); + + it('should add step to spec on StepBegin', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + expect(model.value.children['describe 1'].specs['test spec'].steps.length).toEqual(1); + }); + + it('should update spec timer duration on SpecEnd event', function() { + runner.emit('SpecBegin', spec); + runner.emit('SpecEnd', spec); + + expect(model.value.children['describe 1'].specs['test spec'].duration).toBeDefined(); + }); + + it('should update step timer duration on StepEnd event', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + expect(model.value.children['describe 1'].specs['test spec'].steps[0].duration).toBeDefined(); + }); + + it('should set spec status on SpecEnd to success if no status set', function() { + runner.emit('SpecBegin', spec); + runner.emit('SpecEnd', spec); + + expect(model.value.children['describe 1'].specs['test spec'].status).toEqual('success'); + }); + + it('should set status to error after SpecError', function() { + runner.emit('SpecBegin', spec); + runner.emit('SpecError', spec, 'error'); + + expect(model.value.children['describe 1'].specs['test spec'].status).toEqual('error'); + }); + + it('should set spec status to failure if step fails', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, 'error'); + runner.emit('StepEnd', spec, step); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + expect(model.value.children['describe 1'].specs['test spec'].status).toEqual('failure'); + }); + + it('should set spec status to error if step errors', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepError', spec, step, 'error'); + runner.emit('StepEnd', spec, step); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, 'error'); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + expect(model.value.children['describe 1'].specs['test spec'].status).toEqual('error'); + }); + + describe('events', function() { + var Spec = angular.scenario.ObjectModel.Spec, + Step = angular.scenario.ObjectModel.Step, + callback; + + beforeEach(function() { + callback = jasmine.createSpy('listener'); + }); + + it('should provide method for registering a listener', function() { + expect(model.on).toBeDefined(); + expect(model.on instanceof Function).toBe(true); + }); + + it('should forward SpecBegin event', function() { + model.on('SpecBegin', callback); + runner.emit('SpecBegin', spec); + + expect(callback).toHaveBeenCalled(); + }); + + it('should forward SpecBegin event with ObjectModel.Spec as a param', function() { + model.on('SpecBegin', callback); + runner.emit('SpecBegin', spec); + + expect(callback.mostRecentCall.args[0] instanceof Spec).toBe(true); + expect(callback.mostRecentCall.args[0].name).toEqual(spec.name); + }); + + it('should forward SpecError event', function() { + model.on('SpecError', callback); + runner.emit('SpecBegin', spec); + runner.emit('SpecError', spec, {}); + + expect(callback).toHaveBeenCalled(); + }); + + it('should forward SpecError event with ObjectModel.Spec and error as a params', function() { + var error = {}; + model.on('SpecError', callback); + runner.emit('SpecBegin', spec); + runner.emit('SpecError', spec, error); + + var param = callback.mostRecentCall.args[0]; + expect(param instanceof Spec).toBe(true); + expect(param.name).toEqual(spec.name); + expect(param.status).toEqual('error'); + expect(param.error).toBe(error); + }); + + it('should forward SpecEnd event', function() { + model.on('SpecEnd', callback); + runner.emit('SpecBegin', spec); + runner.emit('SpecEnd', spec); + + expect(callback).toHaveBeenCalled(); + }); + + it('should forward SpecEnd event with ObjectModel.Spec as a param', function() { + model.on('SpecEnd', callback); + runner.emit('SpecBegin', spec); + runner.emit('SpecEnd', spec); + + expect(callback.mostRecentCall.args[0] instanceof Spec).toBe(true); + expect(callback.mostRecentCall.args[0].name).toEqual(spec.name); + }); + + it('should forward StepBegin event', function() { + model.on('StepBegin', callback); + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + + expect(callback).toHaveBeenCalled(); + }); + + it('should forward StepBegin event with Spec and Step as params', function() { + model.on('StepBegin', callback); + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + + var params = callback.mostRecentCall.args; + expect(params[0] instanceof Spec).toBe(true); + expect(params[0].name).toEqual(spec.name); + expect(params[1] instanceof Step).toBe(true); + }); + + it('should forward StepError event', function() { + model.on('StepError', callback); + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepError', spec, step, {}); + + expect(callback).toHaveBeenCalled(); + }); + + it('should forward StepError event with Spec, Step and error as params', function() { + var error = {}; + model.on('StepError', callback); + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepError', spec, step, error); + + var params = callback.mostRecentCall.args; + expect(params[0] instanceof Spec).toBe(true); + expect(params[0].name).toEqual(spec.name); + expect(params[1] instanceof Step).toBe(true); + expect(params[1].status).toEqual('error'); + expect(params[2]).toBe(error); + }); + + it('should forward StepFailure event', function() { + model.on('StepFailure', callback); + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, {}); + + expect(callback).toHaveBeenCalled(); + }); + + it('should forward StepFailure event with Spec, Step and error as params', function() { + var error = {}; + model.on('StepFailure', callback); + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, error); + + var params = callback.mostRecentCall.args; + expect(params[0] instanceof Spec).toBe(true); + expect(params[0].name).toEqual(spec.name); + expect(params[1] instanceof Step).toBe(true); + expect(params[1].status).toEqual('failure'); + expect(params[2]).toBe(error); + }); + + it('should forward StepEnd event', function() { + model.on('StepEnd', callback); + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + + expect(callback).toHaveBeenCalled(); + }); + + it('should forward StepEnd event with Spec and Step as params', function() { + model.on('StepEnd', callback); + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + + var params = callback.mostRecentCall.args; + expect(params[0] instanceof Spec).toBe(true); + expect(params[0].name).toEqual(spec.name); + expect(params[1] instanceof Step).toBe(true); + }); + + it('should forward RunnerEnd event', function() { + model.on('RunnerEnd', callback); + runner.emit('RunnerEnd'); + expect(callback).toHaveBeenCalled(); + }); + + it('should set error of first failure', function() { + var error = 'first-error', + step2 = buildStep(); + + model.on('SpecEnd', function(spec) { + expect(spec.error).toBeDefined(); + expect(spec.error).toBe(error); + }); + + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, error); + runner.emit('StepBegin', spec, step2); + runner.emit('StepFailure', spec, step2, 'second-error'); + runner.emit('SpecEnd', spec); + }); + + it('should set line number of first failure', function() { + var step = buildStep('fake', 'first-line'), + step2 = buildStep('fake2', 'second-line'); + + model.on('SpecEnd', function(spec) { + expect(spec.line).toBeDefined(); + expect(spec.line).toBe('first-line'); + }); + + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, null); + runner.emit('StepBegin', spec, step2); + runner.emit('StepFailure', spec, step2, null); + runner.emit('SpecEnd', spec); + }); + }); +}); diff --git a/test/ngScenario/RunnerSpec.js b/test/ngScenario/RunnerSpec.js new file mode 100644 index 00000000..c4ad6f95 --- /dev/null +++ b/test/ngScenario/RunnerSpec.js @@ -0,0 +1,116 @@ +'use strict'; + +/** + * Mock spec runner. + */ +function MockSpecRunner() {} +MockSpecRunner.prototype.run = function(spec, specDone) { + spec.before.call(this); + spec.body.call(this); + spec.after.call(this); + specDone(); +}; + +MockSpecRunner.prototype.addFuture = function(name, fn, line) { + return {name: name, fn: fn, line: line}; +}; + +describe('angular.scenario.Runner', function() { + var $window; + var runner; + + beforeEach(function() { + // Trick to get the scope out of a DSL statement + angular.scenario.dsl('dslAddFuture', function() { + return function() { + return this.addFuture('future name', angular.noop); + }; + }); + // Trick to get the scope out of a DSL statement + angular.scenario.dsl('dslScope', function() { + var scope = this; + return function() { return scope; }; + }); + // Trick to get the scope out of a DSL statement + angular.scenario.dsl('dslChain', function() { + return function() { + this.chained = 0; + this.chain = function() { this.chained++; return this; }; + return this; + }; + }); + $window = { + location: {} + }; + runner = new angular.scenario.Runner($window); + runner.on('SpecError', angular.mock.rethrow); + runner.on('StepError', angular.mock.rethrow); + }); + + afterEach(function() { + delete angular.scenario.dsl.dslScope; + delete angular.scenario.dsl.dslChain; + }); + + it('should publish the functions in the public API', function() { + angular.forEach(runner.api, function(fn, name) { + var func; + if (name in $window) { + func = $window[name]; + } + expect(angular.isFunction(func)).toBeTruthy(); + }); + }); + + it('should construct valid describe trees with public API', function() { + var before = []; + var after = []; + $window.describe('A', function() { + $window.beforeEach(function() { before.push('A'); }); + $window.afterEach(function() { after.push('A'); }); + $window.it('1', angular.noop); + $window.describe('B', function() { + $window.beforeEach(function() { before.push('B'); }); + $window.afterEach(function() { after.push('B'); }); + $window.it('2', angular.noop); + $window.describe('C', function() { + $window.beforeEach(function() { before.push('C'); }); + $window.afterEach(function() { after.push('C'); }); + $window.it('3', angular.noop); + }); + }); + }); + var specs = runner.rootDescribe.getSpecs(); + specs[0].before(); + specs[0].body(); + specs[0].after(); + expect(before).toEqual(['A', 'B', 'C']); + expect(after).toEqual(['C', 'B', 'A']); + expect(specs[2].definition.parent).toEqual(runner.rootDescribe); + expect(specs[0].definition.parent).toEqual(specs[2].definition.children[0]); + }); + + it('should publish the DSL statements to the $window', function() { + $window.describe('describe', function() { + $window.it('1', function() { + expect($window.dslScope).toBeDefined(); + }); + }); + runner.run(null/*application*/); + }); + + it('should create a new scope for each DSL chain', function() { + $window.describe('describe', function() { + $window.it('1', function() { + var scope = $window.dslScope(); + scope.test = "foo"; + expect($window.dslScope().test).toBeUndefined(); + }); + $window.it('2', function() { + var scope = $window.dslChain().chain().chain(); + expect(scope.chained).toEqual(2); + }); + }); + runner.run(null/*application*/); + }); +}); diff --git a/test/ngScenario/ScenarioSpec.js b/test/ngScenario/ScenarioSpec.js new file mode 100644 index 00000000..cc2efd1e --- /dev/null +++ b/test/ngScenario/ScenarioSpec.js @@ -0,0 +1,32 @@ +'use strict'; + +describe("ScenarioSpec: Compilation", function() { + var element; + + afterEach(function() { + dealoc(element); + }); + + + describe('compilation', function() { + it("should compile dom node and return scope", inject(function($rootScope, $compile) { + var node = jqLite('<div ng-init="a=1">{{b=a+1}}</div>')[0]; + element = $compile(node)($rootScope); + $rootScope.$digest(); + expect($rootScope.a).toEqual(1); + expect($rootScope.b).toEqual(2); + })); + + it("should compile jQuery node and return scope", inject(function($rootScope, $compile) { + element = $compile(jqLite('<div>{{a=123}}</div>'))($rootScope); + $rootScope.$digest(); + expect(jqLite(element).text()).toEqual('123'); + })); + + it("should compile text node and return scope", inject(function($rootScope, $compile) { + element = $compile('<div>{{a=123}}</div>')($rootScope); + $rootScope.$digest(); + expect(jqLite(element).text()).toEqual('123'); + })); + }); +}); diff --git a/test/ngScenario/SpecRunnerSpec.js b/test/ngScenario/SpecRunnerSpec.js new file mode 100644 index 00000000..c104a9b7 --- /dev/null +++ b/test/ngScenario/SpecRunnerSpec.js @@ -0,0 +1,177 @@ +'use strict'; + +/** + * Mock Application + */ +function ApplicationMock($window) { + this.$window = $window; +} +ApplicationMock.prototype = { + executeAction: function(callback) { + callback.call(this.$window, _jQuery(this.$window.document), this.$window); + } +}; + +describe('angular.scenario.SpecRunner', function() { + var $window, $root, log; + var runner; + + function createSpec(name, body) { + return { + name: name, + before: angular.noop, + body: body || angular.noop, + after: angular.noop + }; + } + + beforeEach(inject(function($rootScope) { + log = []; + $window = {}; + $window.setTimeout = function(fn, timeout) { + fn(); + }; + $root = $rootScope; + $root.emit = function(eventName) { + log.push(eventName); + }; + $root.on = function(eventName) { + log.push('Listener Added for ' + eventName); + }; + $root.application = new ApplicationMock($window); + $root.$window = $window; + runner = $root.$new(); + + var Cls = angular.scenario.SpecRunner; + for (var name in Cls.prototype) + runner[name] = angular.bind(runner, Cls.prototype[name]); + + Cls.call(runner); + })); + + it('should bind futures to the spec', function() { + runner.addFuture('test future', function(done) { + this.value = 10; + done(); + }); + runner.futures[0].execute(angular.noop); + expect(runner.value).toEqual(10); + }); + + it('should pass done to future action behavior', function() { + runner.addFutureAction('test future', function($window, $document, done) { + expect(angular.isFunction(done)).toBeTruthy(); + done(10, 20); + }); + runner.futures[0].execute(function(error, result) { + expect(error).toEqual(10); + expect(result).toEqual(20); + }); + }); + + it('should execute spec function and notify UI', function() { + var finished; + var spec = createSpec('test spec', function() { + this.test = 'some value'; + }); + runner.addFuture('test future', function(done) { + done(); + }); + runner.run(spec, function() { + finished = true; + }); + expect(runner.test).toEqual('some value'); + expect(finished).toBeTruthy(); + expect(log).toEqual([ + 'SpecBegin', + 'StepBegin', + 'StepEnd', + 'SpecEnd' + ]); + }); + + it('should execute notify UI on spec setup error', function() { + var finished; + var spec = createSpec('test spec', function() { + throw 'message'; + }); + runner.run(spec, function() { + finished = true; + }); + expect(finished).toBeTruthy(); + expect(log).toEqual([ + 'SpecBegin', + 'SpecError', + 'SpecEnd' + ]); + }); + + it('should execute notify UI on step failure', function() { + var finished; + var spec = createSpec('test spec'); + runner.addFuture('test future', function(done) { + done('failure message'); + }); + runner.run(spec, function() { + finished = true; + }); + expect(finished).toBeTruthy(); + expect(log).toEqual([ + 'SpecBegin', + 'StepBegin', + 'StepFailure', + 'StepEnd', + 'SpecEnd' + ]); + }); + + it('should execute notify UI on step error', function() { + var finished; + var spec = createSpec('test spec', function() { + this.addFuture('test future', function(done) { + throw 'error message'; + }); + }); + runner.run(spec, function() { + finished = true; + }); + expect(finished).toBeTruthy(); + expect(log).toEqual([ + 'SpecBegin', + 'StepBegin', + 'StepError', + 'StepEnd', + 'SpecEnd' + ]); + }); + + it('should run after handlers even if error in body of spec', function() { + var finished, after; + var spec = createSpec('test spec', function() { + this.addFuture('body', function(done) { + throw 'error message'; + }); + }); + spec.after = function() { + this.addFuture('after', function(done) { + after = true; + done(); + }); + }; + runner.run(spec, function() { + finished = true; + }); + expect(finished).toBeTruthy(); + expect(after).toBeTruthy(); + expect(log).toEqual([ + 'SpecBegin', + 'StepBegin', + 'StepError', + 'StepEnd', + 'StepBegin', + 'StepEnd', + 'SpecEnd' + ]); + }); + +}); diff --git a/test/ngScenario/dslSpec.js b/test/ngScenario/dslSpec.js new file mode 100644 index 00000000..0a8ab762 --- /dev/null +++ b/test/ngScenario/dslSpec.js @@ -0,0 +1,629 @@ +'use strict'; + +describe("angular.scenario.dsl", function() { + var element; + var $window, $root; + var eventLog; + + afterEach(function() { + dealoc(element); + }); + + beforeEach(inject(function($injector) { + eventLog = []; + $window = { + document: window.document.body, + angular: new angular.scenario.testing.MockAngular() + }; + jqLite($window.document).data('$injector', $injector).attr('ng-app', '').addClass('html'); + $root = $injector.get('$rootScope'); + $root.emit = function(eventName) { + eventLog.push(eventName); + }; + $root.on = function(eventName) { + eventLog.push('Listener Added for ' + eventName); + }; + $root.futures = []; + $root.futureLog = []; + $root.$window = $window; + $root.addFuture = function(name, fn) { + this.futures.push(name); + fn.call(this, function(error, result) { + $root.futureError = error; + $root.futureResult = result; + $root.futureLog.push(name); + }); + }; + $root.dsl = {}; + angular.forEach(angular.scenario.dsl, function(fn, name) { + $root.dsl[name] = function() { + return fn.call($root).apply($root, arguments); + }; + }); + $root.application = new angular.scenario.Application(jqLite($window.document)); + $root.application.getWindow_ = function() { + return $window; + }; + $root.application.navigateTo = function(url, callback) { + $window.location = url; + callback(); + }; + // Just use the real one since it delegates to this.addFuture + $root.addFutureAction = angular.scenario. + SpecRunner.prototype.addFutureAction; + jqLite($window.document).html(''); + })); + + afterEach(function(){ + jqLite($window.document).removeData('$injector'); + }); + + describe('Pause', function() { + it('should pause until resume to complete', function() { + expect($window.resume).toBeUndefined(); + $root.dsl.pause(); + expect(angular.isFunction($window.resume)).toBeTruthy(); + expect($root.futureLog).toEqual([]); + $window.resume(); + expect($root.futureLog). + toEqual(['pausing for you to resume']); + expect(eventLog).toContain('InteractivePause'); + }); + }); + + describe('Sleep', function() { + beforeEach(function() { + $root.$window.setTimeout = function(fn, value) { + $root.timerValue = value; + fn(); + }; + }); + + it('should sleep for specified seconds', function() { + $root.dsl.sleep(10); + expect($root.timerValue).toEqual(10000); + expect($root.futureResult).toEqual(10000); + }); + }); + + describe('Expect', function() { + it('should chain and execute matcher', function() { + var future = {value: 10}; + var result = $root.dsl.expect(future); + result.toEqual(10); + expect($root.futureError).toBeUndefined(); + expect($root.futureResult).toBeUndefined(); + result = $root.dsl.expect(future); + result.toEqual(20); + expect($root.futureError).toBeDefined(); + }); + }); + + describe('Browser', function() { + describe('Reload', function() { + it('should navigateTo', function() { + $window.location = { + href: '#foo' + }; + $root.dsl.browser().reload(); + expect($root.futureResult).toEqual('#foo'); + expect($window.location).toEqual('#foo'); + }); + }); + + describe('NavigateTo', function() { + it('should allow a string url', function() { + $root.dsl.browser().navigateTo('http://myurl'); + expect($window.location).toEqual('http://myurl'); + expect($root.futureResult).toEqual('http://myurl'); + }); + + it('should allow a future url', function() { + $root.dsl.browser().navigateTo('http://myurl', function() { + return 'http://futureUrl/'; + }); + expect($window.location).toEqual('http://futureUrl/'); + expect($root.futureResult).toEqual('http://futureUrl/'); + }); + + it('should complete if angular is missing from app frame', function() { + delete $window.angular; + $root.dsl.browser().navigateTo('http://myurl'); + expect($window.location).toEqual('http://myurl'); + expect($root.futureResult).toEqual('http://myurl'); + }); + }); + + describe('window', function() { + beforeEach(function() { + $window.location = { + href: 'http://myurl/some/path?foo=10#/bar?x=2', + pathname: '/some/path', + search: '?foo=10', + hash: '#bar?x=2' + }; + }); + + it('should return full URL for href', function() { + $root.dsl.browser().window().href(); + expect($root.futureResult).toEqual($window.location.href); + }); + + it('should return the pathname', function() { + $root.dsl.browser().window().path(); + expect($root.futureResult).toEqual($window.location.pathname); + }); + + it('should return the search part', function() { + $root.dsl.browser().window().search(); + expect($root.futureResult).toEqual($window.location.search); + }); + + it('should return the hash without the #', function() { + $root.dsl.browser().window().hash(); + expect($root.futureResult).toEqual('bar?x=2'); + }); + }); + + describe('location', function() { + beforeEach(function() { + $window.angular.injector = function() { + return { + get: function(serviceId) { + if (serviceId == '$location') { + return { + url: function() {return '/path?search=a#hhh';}, + path: function() {return '/path';}, + search: function() {return {search: 'a'};}, + hash: function() {return 'hhh';} + }; + } + throw new Error('unknown service id ' + serviceId); + } + }; + }; + }); + + it('should return full url', function() { + $root.dsl.browser().location().url(); + expect($root.futureResult).toEqual('/path?search=a#hhh'); + }); + + it('should return the pathname', function() { + $root.dsl.browser().location().path(); + expect($root.futureResult).toEqual('/path'); + }); + + it('should return the query string as an object', function() { + $root.dsl.browser().location().search(); + expect($root.futureResult).toEqual({search: 'a'}); + }); + + it('should return the hash without the #', function() { + $root.dsl.browser().location().hash(); + expect($root.futureResult).toEqual('hhh'); + }); + }); + }); + + describe('Element Finding', function() { + var doc; + beforeEach(inject(function($injector) { + doc = _jQuery($window.document).append('<div class="body"></div>').find('.body'); + })); + + describe('Select', function() { + it('should select single option', function() { + doc.append( + '<select ng-model="test">' + + ' <option value=A>one</option>' + + ' <option value=B selected>two</option>' + + '</select>' + ); + $root.dsl.select('test').option('A'); + expect(doc.find('[ng-model="test"]').val()).toEqual('A'); + }); + + it('should select option by name', function() { + doc.append( + '<select ng-model="test">' + + ' <option value=A>one</option>' + + ' <option value=B selected>two</option>' + + '</select>' + ); + $root.dsl.select('test').option('one'); + expect(doc.find('[ng-model="test"]').val()).toEqual('A'); + }); + + it('should select multiple options', function() { + doc.append( + '<select ng-model="test" multiple>' + + ' <option>A</option>' + + ' <option selected>B</option>' + + ' <option>C</option>' + + '</select>' + ); + $root.dsl.select('test').options('A', 'B'); + expect(doc.find('[ng-model="test"]').val()).toEqual(['A','B']); + }); + + it('should fail to select multiple options on non-multiple select', function() { + doc.append('<select ng-model="test"></select>'); + $root.dsl.select('test').options('A', 'B'); + expect($root.futureError).toMatch(/did not match/); + }); + }); + + describe('Element', function() { + it('should execute click', function() { + var clicked; + // Hash is important, otherwise we actually + // go to a different page and break the runner + doc.append('<a href="#"></a>'); + doc.find('a').click(function() { + clicked = true; + }); + $root.dsl.element('a').click(); + }); + + it('should navigate page if click on anchor', function() { + expect($window.location).not.toEqual('#foo'); + doc.append('<a href="#foo"></a>'); + $root.dsl.element('a').click(); + expect($window.location).toMatch(/#foo$/); + }); + + it('should not navigate if click event was cancelled', function() { + var initLocation = $window.location, + elm = jqLite('<a href="#foo"></a>'); + + doc.append(elm); + elm.bind('click', function(event) { + event.preventDefault(); + }); + + $root.dsl.element('a').click(); + expect($window.location).toBe(initLocation); + dealoc(elm); + }); + + it('should count matching elements', function() { + doc.append('<span></span><span></span>'); + $root.dsl.element('span').count(); + expect($root.futureResult).toEqual(2); + }); + + it('should return count of 0 if no matching elements', function() { + $root.dsl.element('span').count(); + expect($root.futureResult).toEqual(0); + }); + + it('should get attribute', function() { + doc.append('<div id="test" class="foo"></div>'); + $root.dsl.element('#test').attr('class'); + expect($root.futureResult).toEqual('foo'); + }); + + it('should set attribute', function() { + doc.append('<div id="test" class="foo"></div>'); + $root.dsl.element('#test').attr('class', 'bam'); + expect(doc.find('#test').attr('class')).toEqual('bam'); + }); + + it('should get property', function() { + doc.append('<div id="test" class="foo"></div>'); + $root.dsl.element('#test').prop('className'); + expect($root.futureResult).toEqual('foo'); + }); + + it('should set property', function() { + doc.append('<div id="test" class="foo"></div>'); + $root.dsl.element('#test').prop('className', 'bam'); + expect(doc.find('#test').prop('className')).toEqual('bam'); + }); + + it('should get css', function() { + doc.append('<div id="test" style="height: 30px"></div>'); + $root.dsl.element('#test').css('height'); + expect($root.futureResult).toMatch(/30px/); + }); + + it('should set css', function() { + doc.append('<div id="test" style="height: 10px"></div>'); + $root.dsl.element('#test').css('height', '20px'); + expect(doc.find('#test').css('height')).toEqual('20px'); + }); + + it('should add all jQuery key/value methods', function() { + var METHODS = ['css', 'attr']; + var chain = $root.dsl.element('input'); + angular.forEach(METHODS, function(name) { + expect(angular.isFunction(chain[name])).toBeTruthy(); + }); + }); + + it('should get val', function() { + doc.append('<input value="bar">'); + $root.dsl.element('input').val(); + expect($root.futureResult).toEqual('bar'); + }); + + it('should set val', function() { + doc.append('<input value="bar">'); + $root.dsl.element('input').val('baz'); + expect(doc.find('input').val()).toEqual('baz'); + }); + + it('should use correct future name for generated set methods', function() { + doc.append('<input value="bar">'); + $root.dsl.element('input').val(false); + expect($root.futures.pop()).toMatch(/element 'input' set val/); + }); + + it('should use correct future name for generated get methods', function() { + doc.append('<input value="bar">'); + $root.dsl.element('input').val(); + expect($root.futures.pop()).toMatch(/element 'input' val/); + }); + + it('should add all jQuery property methods', function() { + var METHODS = [ + 'val', 'text', 'html', 'height', 'innerHeight', 'outerHeight', 'width', + 'innerWidth', 'outerWidth', 'position', 'scrollLeft', 'scrollTop', 'offset' + ]; + var chain = $root.dsl.element('input'); + angular.forEach(METHODS, function(name) { + expect(angular.isFunction(chain[name])).toBeTruthy(); + }); + }); + + it('should execute custom query', function() { + doc.append('<a id="test" href="http://example.com/myUrl"></a>'); + $root.dsl.element('#test').query(function(elements, done) { + done(null, elements.attr('href')); + }); + expect($root.futureResult).toEqual('http://example.com/myUrl'); + }); + + it('should use the selector as label if none is given', function() { + $root.dsl.element('mySelector'); + expect($root.label).toEqual('mySelector'); + }); + + it('should include the selector in paren when a label is given', function() { + $root.dsl.element('mySelector', 'myLabel'); + expect($root.label).toEqual('myLabel ( mySelector )'); + }); + }); + + describe('Repeater', function() { + var chain; + beforeEach(inject(function($compile, $rootScope) { + element = $compile( + '<ul><li ng-repeat="i in items">{{i.name}} {{i.gender}}</li></ul>')($rootScope); + $rootScope.items = [{name:'misko', gender:'male'}, {name:'felisa', gender:'female'}]; + $rootScope.$apply(); + doc.append(element); + chain = $root.dsl.repeater('ul li'); + })); + + it('should get the row count', function() { + chain.count(); + expect($root.futureResult).toEqual(2); + }); + + it('should return 0 if repeater doesnt match', inject(function($rootScope) { + $rootScope.items = []; + $rootScope.$apply(); + chain.count(); + expect($root.futureResult).toEqual(0); + })); + + it('should get a row of bindings', function() { + chain.row(1); + expect($root.futureResult).toEqual(['felisa', 'female']); + }); + + it('should get a column of bindings', function() { + chain.column('i.gender'); + expect($root.futureResult).toEqual(['male', 'female']); + }); + + it('should use the selector as label if none is given', function() { + expect($root.label).toEqual('ul li'); + }); + + it('should include the selector in paren when a label is given', function() { + $root.dsl.repeater('mySelector', 'myLabel'); + expect($root.label).toEqual('myLabel ( ul li mySelector )'); + }); + }); + + describe('Binding', function() { + var compile; + + beforeEach(inject(function($compile, $rootScope) { + compile = function(html, value) { + element = $compile(html)($rootScope); + doc.append(element); + $rootScope.foo = {bar: value || 'some value'}; + $rootScope.$apply(); + }; + })); + + + it('should select binding in interpolation', function() { + compile('<span>{{ foo.bar }}</span>'); + $root.dsl.binding('foo.bar'); + expect($root.futureResult).toEqual('some value'); + }); + + it('should select binding in multiple interpolations', function() { + compile('<span>{{ foo.bar }}<hr/> {{ true }}</span>'); + $root.dsl.binding('foo.bar'); + expect($root.futureResult).toEqual('some value'); + + $root.dsl.binding('true'); + expect($root.futureResult).toEqual('true'); + }); + + it('should select binding by name', function() { + compile('<span ng-bind=" foo.bar "></span>'); + $root.dsl.binding('foo.bar'); + expect($root.futureResult).toEqual('some value'); + }); + + it('should select binding by regexp', function() { + compile('<span ng-bind="foo.bar">some value</span>'); + $root.dsl.binding(/^foo\..+/); + expect($root.futureResult).toEqual('some value'); + }); + + it('should return innerHTML for all the other elements', function() { + compile('<div ng-bind-html="foo.bar"></div>', 'some <b>value</b>'); + $root.dsl.binding('foo.bar'); + expect($root.futureResult.toLowerCase()).toEqual('some <b>value</b>'); + }); + + it('should select binding in template by name', function() { + compile('<pre ng-bind-template="foo {{foo.bar}} baz"></pre>', 'bar'); + $root.dsl.binding('foo.bar'); + expect($root.futureResult).toEqual('bar'); + }); + + it('should match bindings by substring match', function() { + compile('<pre ng-bind="foo.bar | filter"></pre>', 'binding value'); + $root.dsl.binding('foo . bar'); + expect($root.futureResult).toEqual('binding value'); + }); + + it('should return error if no bindings in document', function() { + $root.dsl.binding('foo.bar'); + expect($root.futureError).toMatch(/did not match/); + }); + + it('should return error if no binding matches', function() { + compile('<span ng-bind="foo">some value</span>'); + $root.dsl.binding('foo.bar'); + expect($root.futureError).toMatch(/did not match/); + }); + }); + + describe('Using', function() { + it('should prefix selector in $document.elements()', function() { + var chain; + doc.append( + '<div id="test1"><input ng-model="test.input" value="something"></div>' + + '<div id="test2"><input ng-model="test.input" value="something"></div>' + ); + chain = $root.dsl.using('div#test2'); + chain.input('test.input').enter('foo'); + var inputs = _jQuery('input[ng-model="test.input"]'); + expect(inputs.first().val()).toEqual('something'); + expect(inputs.last().val()).toEqual('foo'); + }); + + it('should use the selector as label if none is given', function() { + $root.dsl.using('mySelector'); + expect($root.label).toEqual('mySelector'); + }); + + it('should include the selector in paren when a label is given', function() { + $root.dsl.using('mySelector', 'myLabel'); + expect($root.label).toEqual('myLabel ( mySelector )'); + }); + + }); + + describe('Input', function() { + it('should change value in text input', function() { + doc.append('<input ng-model="test.input" value="something">'); + var chain = $root.dsl.input('test.input'); + chain.enter('foo'); + expect(_jQuery('input[ng-model="test.input"]').val()).toEqual('foo'); + }); + + it('should change value in text input in dash form', function() { + doc.append('<input ng-model="test.input" value="something">'); + var chain = $root.dsl.input('test.input'); + chain.enter('foo'); + expect(_jQuery('input[ng-model="test.input"]').val()).toEqual('foo'); + }); + + it('should return error if no input exists', function() { + var chain = $root.dsl.input('test.input'); + chain.enter('foo'); + expect($root.futureError).toMatch(/did not match/); + }); + + it('should toggle checkbox state', function() { + doc.append('<input type="checkbox" ng-model="test.input" checked>'); + expect(_jQuery('input[ng-model="test.input"]'). + prop('checked')).toBe(true); + var chain = $root.dsl.input('test.input'); + chain.check(); + expect(_jQuery('input[ng-model="test.input"]'). + prop('checked')).toBe(false); + $window.angular.reset(); + chain.check(); + expect(_jQuery('input[ng-model="test.input"]'). + prop('checked')).toBe(true); + }); + + it('should return error if checkbox did not match', function() { + var chain = $root.dsl.input('test.input'); + chain.check(); + expect($root.futureError).toMatch(/did not match/); + }); + + it('should select option from radio group', function() { + doc.append( + '<input type="radio" name="r" ng:model="test.input" value="foo">' + + '<input type="radio" name="r" ng:model="test.input" value="bar" checked="checked">' + ); + // HACK! We don't know why this is sometimes false on chrome + _jQuery('input[ng\\:model="test.input"][value="bar"]').prop('checked', true); + expect(_jQuery('input[ng\\:model="test.input"][value="bar"]'). + prop('checked')).toBe(true); + expect(_jQuery('input[ng\\:model="test.input"][value="foo"]'). + prop('checked')).toBe(false); + var chain = $root.dsl.input('test.input'); + chain.select('foo'); + expect(_jQuery('input[ng\\:model="test.input"][value="bar"]'). + prop('checked')).toBe(false); + expect(_jQuery('input[ng\\:model="test.input"][value="foo"]'). + prop('checked')).toBe(true); + }); + + it('should return error if radio button did not match', function() { + var chain = $root.dsl.input('test.input'); + chain.select('foo'); + expect($root.futureError).toMatch(/did not match/); + }); + + describe('val', function() { + it('should return value in text input', function() { + doc.append('<input ng-model="test.input" value="something">'); + $root.dsl.input('test.input').val(); + expect($root.futureResult).toEqual("something"); + }); + }); + }); + + describe('Textarea', function() { + + it('should change value in textarea', function() { + doc.append('<textarea ng-model="test.textarea">something</textarea>'); + var chain = $root.dsl.input('test.textarea'); + chain.enter('foo'); + expect(_jQuery('textarea[ng-model="test.textarea"]').val()).toEqual('foo'); + }); + + it('should return error if no textarea exists', function() { + var chain = $root.dsl.input('test.textarea'); + chain.enter('foo'); + expect($root.futureError).toMatch(/did not match/); + }); + }); + }); +}); diff --git a/test/ngScenario/e2e/Runner-compiled.html b/test/ngScenario/e2e/Runner-compiled.html new file mode 100644 index 00000000..530fef96 --- /dev/null +++ b/test/ngScenario/e2e/Runner-compiled.html @@ -0,0 +1,9 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <script type="text/javascript" src="../../../build/angular-scenario.js" ng-autotest></script> + <script type="text/javascript" src="widgets-scenario.js"></script> + </head> + <body> + </body> +</html> diff --git a/test/ngScenario/e2e/Runner.html b/test/ngScenario/e2e/Runner.html new file mode 100644 index 00000000..1191dc86 --- /dev/null +++ b/test/ngScenario/e2e/Runner.html @@ -0,0 +1,9 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <script type="text/javascript" src="../../../src/scenario/angular-bootstrap.js" ng-autotest></script> + <script type="text/javascript" src="widgets-scenario.js"></script> + </head> + <body> + </body> +</html> diff --git a/test/ngScenario/e2e/style.css b/test/ngScenario/e2e/style.css new file mode 100644 index 00000000..26540bec --- /dev/null +++ b/test/ngScenario/e2e/style.css @@ -0,0 +1,11 @@ +th { + text-align: left; +} + +tr { + border: 1px solid black; +} + +.redbox { + background-color: red; +} diff --git a/test/ngScenario/e2e/widgets-scenario.js b/test/ngScenario/e2e/widgets-scenario.js new file mode 100644 index 00000000..fda9b3c6 --- /dev/null +++ b/test/ngScenario/e2e/widgets-scenario.js @@ -0,0 +1,67 @@ +'use strict'; + +describe('widgets', function() { + it('should verify that basic widgets work', function() { + browser().navigateTo('widgets.html'); + + using('#text-basic-box').input('text.basic').enter('Carlos'); + expect(binding('text.basic')).toEqual('Carlos'); + input('text.basic').enter('Carlos Santana'); + expect(binding('text.basic')).not().toEqual('Carlos Boozer'); + + input('text.password').enter('secret'); + expect(binding('text.password')).toEqual('secret'); + + expect(binding('text.hidden')).toEqual('hiddenValue'); + + expect(binding('gender')).toEqual('male'); + input('gender').select('female'); + expect(using('#gender-box').binding('gender')).toEqual('female'); + + expect(repeater('#repeater-row ul li').count()).toEqual(2); + expect(repeater('#repeater-row ul li').row(1)).toEqual(['adam']); + expect(repeater('#repeater-row ul li').column('name')).toEqual(['misko', 'adam']); + + select('select').option('B'); + expect(binding('select')).toEqual('B'); + + select('multiselect').options('A', 'C'); + expect(binding('multiselect').fromJson()).toEqual(['A', 'C']); + + expect(binding('button').fromJson()).toEqual({'count': 0}); + expect(binding('form').fromJson()).toEqual({'count': 0}); + + element('form a', "'action' link").click(); + expect(binding('button').fromJson()).toEqual({'count': 1}); + + element('input[value="submit input"]', "'submit input' button").click(); + expect(binding('button').fromJson()).toEqual({'count': 2}); + expect(binding('form').fromJson()).toEqual({'count': 1}); + + element('button:contains("submit button")', "'submit button' button").click(); + expect(binding('button').fromJson()).toEqual({'count': 2}); + expect(binding('form').fromJson()).toEqual({'count': 2}); + + element('input[value="button"]', "'button' button").click(); + expect(binding('button').fromJson()).toEqual({'count': 3}); + + element('input[type="image"]', 'form image').click(); + expect(binding('button').fromJson()).toEqual({'count': 4}); + + /** + * Custom value parser for futures. + */ + function checkboxParser(value) { + return angular.fromJson(value.substring(value.indexOf('=')+1)); + } + + input('checkbox.tea').check(); + expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: false, tea: false}); + input('checkbox.coffee').check(); + expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: true, tea: false}); + input('checkbox.tea').check(); + input('checkbox.tea').check(); + input('checkbox.tea').check(); + expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: true, tea: true}); + }); +}); diff --git a/test/ngScenario/e2e/widgets.html b/test/ngScenario/e2e/widgets.html new file mode 100644 index 00000000..f986144e --- /dev/null +++ b/test/ngScenario/e2e/widgets.html @@ -0,0 +1,99 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns:ng="http://angularjs.org"> + <head ng-app> + <link rel="stylesheet" type="text/css" href="style.css"/> + <script type="text/javascript" src="../../../src/angular-bootstrap.js"></script> + </head> + <body ng-init="$window.$scope = this"> + <table> + <tr> + <th width="330">Description</th> + <th>Test</th> + <th>Result</th> + </tr> + <tr><th colspan="3">Input text field</th></tr> + <tr> + <td>basic</td> + <td id="text-basic-box"> + <input type="text" ng-model="text.basic"/> + </td> + <td>text.basic={{text.basic}}</td> + </tr> + <tr> + <td>password</td> + <td><input type="password" ng-model="text.password" /></td> + <td>text.password={{text.password}}</td> + </tr> + <tr> + <td>hidden</td> + <td><input type="hidden" ng-model="text.hidden" value="hiddenValue" /></td> + <td>text.hidden={{text.hidden}}</td> + </tr> + <tr><th colspan="3">Input selection field</th></tr> + <tr id="gender-box"> + <td>radio</td> + <td> + <input type="radio" ng-model="gender" value="female"/> Female <br/> + <input type="radio" ng-model="gender" value="male" checked="checked"/> Male + </td> + <td>gender={{gender}}</td> + </tr> + <tr> + <td>checkbox</td> + <td> + <input type="checkbox" ng-model="checkbox.tea" checked value="on"/> Tea<br/> + <input type="checkbox" ng-model="checkbox.coffee" value="on"/> Coffe + </td> + <td> + <pre>checkbox={{checkbox}}</pre> + </td> + </tr> + <tr> + <td>select</td> + <td> + <select ng-model="select"> + <option>A</option> + <option>B</option> + <option>C</option> + </select> + </td> + <td>select={{select}}</td> + </tr> + <tr> + <td>multiselect</td> + <td> + <select ng-model="multiselect" multiple> + <option>A</option> + <option>B</option> + <option>C</option> + </select> + </td> + <td>multiselect={{multiselect}}</td> + </tr> + <tr><th colspan="3">Buttons</th></tr> + <tr> + <td>ng-change<br/>ng-click</td> + <td ng-init="button.count = 0; form.count = 0;"> + <form ng-submit="form.count = form.count + 1"> + <input type="button" value="button" ng-change="button.count = button.count + 1"/> <br/> + <input type="submit" value="submit input" ng-change="button.count = button.count + 1"/><br/> + <button type="submit">submit button</button> + <input type="image" src="" ng-change="button.count = button.count + 1"/><br/> + <a href="" ng-click="button.count = button.count + 1">action</a> + </form> + </td> + <td>button={{button}} form={{form}}</td> + </tr> + <tr><th colspan="3">Repeaters</th></tr> + <tr id="repeater-row"> + <td>ng-repeat</td> + <td> + <ul> + <li ng-repeat="name in ['misko', 'adam']">{{name}}</li> + </ul> + </td> + <td></td> + </tr> + </table> + </body> + </html> diff --git a/test/ngScenario/jstd-scenario-adapter/AdapterSpecs.js b/test/ngScenario/jstd-scenario-adapter/AdapterSpecs.js new file mode 100644 index 00000000..e360ad4f --- /dev/null +++ b/test/ngScenario/jstd-scenario-adapter/AdapterSpecs.js @@ -0,0 +1,322 @@ +'use strict'; + +describe('jstd-adapter', function() { + var fakeJSTD = { pluginRegistrar: { register: function() {} } }, + originalNavigateTo = angular.scenario.Application.prototype.navigateTo; + + /** + * Reverts hack on angular.scenario.Application.navigateTo + * We should revert this hack after any single call of initScenarioAdapter, + * so that it doesn't influence other tests... + */ + function revertNavigateToHack() { + angular.scenario.Application.prototype.navigateTo = originalNavigateTo; + } + + /** + * Helper for building angular.scenario.ObjectModel.Spec + * @returns {angular.scenario.ObjectModel.Spec} + */ + function buildSpec(status, name, duration, definitions, error, line) { + var spec = new angular.scenario.ObjectModel.Spec( + 'fake-id', name || 'name', definitions || ['desc1', 'desc2']); + spec.duration = duration || 10; + spec.status = status || 'success'; + spec.error = error || ''; + spec.line = line || ''; + + return spec; + } + + /** + * Helper for building angular.scenario.ObjectModel.Spec with error and error line + * @returns {angular.scenario.ObjectModel.Spec} + */ + function buildErrorSpec(error, line, status, name) { + return buildSpec(status || 'error', name, null, null, error, line); + } + + /** + * Helper for building TestConfiguration + * @returns {jstestdriver.TestRunConfiguration} + */ + function buildTestConf(type) { + return new jstestdriver.TestRunConfiguration( + new jstestdriver.TestCaseInfo('Fake test - ' + Math.random(), function() {}, type), null); + } + + /** + * Helper for building SCENARIO TestConfiguration + * @returns {jstestdriver.TestRunConfiguration} + */ + function buildScenarioTestConf() { + return buildTestConf(SCENARIO_TYPE); + } + + describe('initScenarioAdapter', function() { + afterEach(revertNavigateToHack); + + it('should create and register plugin if jstestdriver defined', function() { + spyOn(fakeJSTD.pluginRegistrar, 'register'); + initScenarioAdapter(fakeJSTD); + expect(fakeJSTD.pluginRegistrar.register).toHaveBeenCalled(); + expect(fakeJSTD.pluginRegistrar.register.mostRecentCall.args[0] instanceof JstdPlugin); + }); + + it('should do nothing if jstestdriver not defined', function() { + expect(function() { + initScenarioAdapter(undefined); + }).not.toThrow(); + }); + + it('should set setUpAndRun callback to plugin', function() { + var runFn = jasmine.createSpy('setUpAndRun'); + plugin.runScenario = null; + + initScenarioAdapter(fakeJSTD, runFn); + expect(plugin.runScenario).toBe(runFn); + }); + + describe('navigateTo', function() { + var fakeJSTD = { pluginRegistrar: { register: function() {} } }, + app = new angular.scenario.Application(_jQuery('<div></div>')), + navigateSpy; + + beforeEach(function() { + navigateSpy = spyOn(angular.scenario.Application.prototype, 'navigateTo'); + }); + + it('should add url prefix when jstd defined', function() { + initScenarioAdapter(fakeJSTD, null, {relativeUrlPrefix: '/prefix/'}); + + app.navigateTo('test.html'); + expect(navigateSpy).toHaveBeenCalled(); + expect(navigateSpy.mostRecentCall.args[0]).toEqual('/prefix/test.html'); + }); + + it('should add forward-slash as default url prefix when jstd defined', function() { + initScenarioAdapter(fakeJSTD); + + app.navigateTo('test.html'); + expect(navigateSpy).toHaveBeenCalled(); + expect(navigateSpy.mostRecentCall.args[0]).toEqual('/test.html'); + }); + + it('should not change url when jstd not defined', function() { + initScenarioAdapter(null); + + app.navigateTo('test.html'); + expect(navigateSpy).toHaveBeenCalled(); + expect(navigateSpy.mostRecentCall.args[0]).toEqual('test.html'); + }); + + it('should not change hash url', function() { + initScenarioAdapter(fakeJSTD); + + app.navigateTo('#/index.html/a'); + expect(navigateSpy).toHaveBeenCalled(); + expect(navigateSpy.mostRecentCall.args[0]).toEqual('#/index.html/a'); + }); + + it('should not change absolute url', function() { + initScenarioAdapter(fakeJSTD); + + app.navigateTo('/index.html/a'); + expect(navigateSpy).toHaveBeenCalled(); + expect(navigateSpy.mostRecentCall.args[0]).toEqual('/index.html/a'); + }); + + it('should not change "about:blank" url', function() { + initScenarioAdapter(fakeJSTD); + + app.navigateTo('about:blank'); + expect(navigateSpy).toHaveBeenCalled(); + expect(navigateSpy.mostRecentCall.args[0]).toEqual('about:blank'); + }); + + it('should not change url with domain', function() { + initScenarioAdapter(fakeJSTD); + + app.navigateTo('http://www.google.com'); + expect(navigateSpy).toHaveBeenCalled(); + expect(navigateSpy.mostRecentCall.args[0]).toEqual('http://www.google.com'); + }); + }); + }); + + describe('JstdPlugin', function() { + var p; + + beforeEach(function() { + p = new JstdPlugin(); + }); + + describe('runTestConfiguration', function() { + var initScenarioSpy, onTestSpy, onAllTestsSpy, spec, modelSpec; + + beforeEach(function() { + initScenarioSpy = jasmine.createSpy('initScenarioAndRun'); + onTestSpy = jasmine.createSpy('onOneTest'); + onAllTestsSpy = jasmine.createSpy('onAllTests'); + + p.runScenario = initScenarioSpy; + spec = {id: 'fake', name: 'Spec Name'}; + modelSpec = new angular.scenario.ObjectModel.Spec(spec.id, spec.name); + }); + + it('should ignore non scenario test cases', function() { + expect(p.runTestConfiguration(buildTestConf(), onTestSpy, onAllTestsSpy)).toBe(false); + expect(p.runTestConfiguration(buildTestConf('async'), onTestSpy, onAllTestsSpy)).toBe(false); + expect(initScenarioSpy).not.toHaveBeenCalled(); + expect(onTestSpy).not.toHaveBeenCalled(); + expect(onAllTestsSpy).not.toHaveBeenCalled(); + }); + + it('should return true when scenario test case', function() { + expect(p.runTestConfiguration(buildScenarioTestConf(), onTestSpy, onAllTestsSpy)).toBe(true); + }); + + it('should call initAndRunTests when scenario test case', function() { + p.runTestConfiguration(buildScenarioTestConf(), onTestSpy, onAllTestsSpy); + expect(initScenarioSpy).toHaveBeenCalled(); + }); + }); + + describe('getTestRunsConfigurationFor', function() { + it('should add TestRunConfiguration with SCENARIO_TYPE TestCase', function() { + var configurations = []; + p.getTestRunsConfigurationFor(null, null, configurations); + + expect(configurations.length).toBe(1); + expect(configurations[0] instanceof jstestdriver.TestRunConfiguration).toBe(true); + expect(configurations[0].getTestCaseInfo().getType()).toEqual(SCENARIO_TYPE); + }); + + it('should always return true', function() { + expect(p.getTestRunsConfigurationFor(null, null, [])).toBe(true); + }); + }); + }); + + describe('createTestResultFromSpec', function() { + it('should return jstestdriver.TestResult instance', function() { + expect(createTestResultFromSpec(buildSpec()) instanceof jstestdriver.TestResult).toBe(true); + }); + + it('should set proper test name', function() { + expect(createTestResultFromSpec(buildSpec()).testName).toEqual('name'); + }); + + it('should set duration', function() { + expect(createTestResultFromSpec(buildSpec()).time).toEqual(10); + }); + + it('should set test case - full definition name', function() { + var spec = buildSpec(); + expect(createTestResultFromSpec(spec).testCaseName).toEqual(spec.fullDefinitionName); + }); + + it('should set passed result when success', function() { + expect(createTestResultFromSpec(buildSpec('success')).result) + .toEqual(jstestdriver.TestResult.RESULT.PASSED); + }); + + it('should set error result when error', function() { + expect(createTestResultFromSpec(buildSpec('error')).result) + .toEqual(jstestdriver.TestResult.RESULT.ERROR); + }); + + it('should set failed result when failure', function() { + expect(createTestResultFromSpec(buildSpec('failure')).result) + .toEqual(jstestdriver.TestResult.RESULT.FAILED); + }); + + it('should set error message when error/failure', function() { + expect(createTestResultFromSpec(buildErrorSpec('error-message')).message) + .toEqual('error-message'); + }); + + it('should log line number when error/failure', function() { + expect(createTestResultFromSpec(buildErrorSpec('msg', 'line-number')).log) + .toEqual('line-number'); + }); + }); + + describe('angular.scenario.output.jstd', function() { + var model; + + beforeEach(function() { + var runner = new angular.scenario.testing.MockRunner(), + context = _jQuery("<div></div>"); + + plugin = new JstdPlugin(); + model = new angular.scenario.ObjectModel(runner); + angular.scenario.output.jstd(context, runner, model); + + spyOn(plugin, 'reportEnd'); + spyOn(plugin, 'reportResult'); + }); + + it('should report end of all tests', function() { + model.emit('RunnerEnd'); + expect(plugin.reportEnd).toHaveBeenCalled(); + }); + + it('should report jstestdriver.TestResult', function() { + model.emit('SpecEnd', buildSpec()); + expect(plugin.reportResult).toHaveBeenCalled(); + expect(plugin.reportResult.argsForCall[0][0] instanceof jstestdriver.TestResult).toBe(true); + }); + }); + + // couple of higher level tests (wiring objects together) + describe('HIGHER LEVEL', function() { + var initScenarioSpy, onTestSpy, onAllTestsSpy, model; + + beforeEach(function() { + plugin = new JstdPlugin(); + initScenarioSpy = jasmine.createSpy('initScenarioAndRun'); + onTestSpy = jasmine.createSpy('onOneTest'); + onAllTestsSpy = jasmine.createSpy('onAllTests'); + + var runner = new angular.scenario.testing.MockRunner(), + context = _jQuery("<div></div>"); + + model = new angular.scenario.ObjectModel(runner); + angular.scenario.output.jstd(context, runner, model); + + initScenarioAdapter(fakeJSTD, initScenarioSpy); + plugin.runTestConfiguration(buildScenarioTestConf(), onTestSpy, onAllTestsSpy); + }); + + afterEach(revertNavigateToHack); + + it('should report and of test suite', function() { + model.emit('RunnerEnd'); + expect(onAllTestsSpy).toHaveBeenCalled(); + }); + + it('should report success test result', function() { + model.emit('SpecEnd', buildSpec('success', 'name')); + expect(onTestSpy).toHaveBeenCalled(); + var result = onTestSpy.argsForCall[0][0]; + expect(result instanceof jstestdriver.TestResult).toBe(true); + expect(result.testName).toEqual('name'); + expect(result.result).toEqual(jstestdriver.TestResult.RESULT.PASSED); + }); + + it('should report error test result', function() { + model.emit('SpecEnd', buildSpec('error')); + expect(onTestSpy).toHaveBeenCalled(); + var result = onTestSpy.argsForCall[0][0]; + expect(result.result).toEqual(jstestdriver.TestResult.RESULT.ERROR); + }); + + it('should report failed test result', function() { + model.emit('SpecEnd', buildSpec('failure')); + expect(onTestSpy).toHaveBeenCalled(); + var result = onTestSpy.argsForCall[0][0]; + expect(result.result).toEqual(jstestdriver.TestResult.RESULT.FAILED); + }); + }); +}); diff --git a/test/ngScenario/matchersSpec.js b/test/ngScenario/matchersSpec.js new file mode 100644 index 00000000..7a5217d7 --- /dev/null +++ b/test/ngScenario/matchersSpec.js @@ -0,0 +1,51 @@ +'use strict'; + +describe('angular.scenario.matchers', function () { + var matchers; + + function expectMatcher(value, test) { + delete matchers.error; + delete matchers.future.value; + if (value !== undefined) { + matchers.future.value = value; + } + test(); + expect(matchers.error).toBeUndefined(); + } + + beforeEach(function() { + /** + * Mock up the future system wrapped around matchers. + * + * @see Scenario.js#angular.scenario.matcher + */ + matchers = { + future: { name: 'test' } + }; + matchers.addFuture = function(name, callback) { + callback(function(error) { + matchers.error = error; + }); + }; + angular.extend(matchers, angular.scenario.matcher); + }); + + it('should handle basic matching', function() { + expectMatcher(10, function() { matchers.toEqual(10); }); + expectMatcher('value', function() { matchers.toBeDefined(); }); + expectMatcher([1], function() { matchers.toBeTruthy(); }); + expectMatcher("", function() { matchers.toBeFalsy(); }); + expectMatcher(0, function() { matchers.toBeFalsy(); }); + expectMatcher('foo', function() { matchers.toMatch('.o.'); }); + expectMatcher(null, function() { matchers.toBeNull(); }); + expectMatcher([1, 2, 3], function() { matchers.toContain(2); }); + expectMatcher(3, function() { matchers.toBeLessThan(10); }); + expectMatcher(3, function() { matchers.toBeGreaterThan(-5); }); + }); + + it('should have toHaveClass matcher', function(){ + var e = angular.element('<div class="abc">'); + expect(e).not.toHaveClass('none'); + expect(e).toHaveClass('abc'); + }); +}); diff --git a/test/ngScenario/mocks.js b/test/ngScenario/mocks.js new file mode 100644 index 00000000..e135390f --- /dev/null +++ b/test/ngScenario/mocks.js @@ -0,0 +1,33 @@ +'use strict'; + +angular.scenario.testing = angular.scenario.testing || {}; + +angular.scenario.testing.MockAngular = function() { + this.reset(); + this.element = jqLite; +}; + +angular.scenario.testing.MockAngular.prototype.reset = function() { + this.log = []; +}; + +angular.scenario.testing.MockAngular.prototype.poll = function() { + this.log.push('$brower.poll()'); + return this; +}; + +angular.scenario.testing.MockRunner = function() { + this.listeners = []; +}; + +angular.scenario.testing.MockRunner.prototype.on = function(eventName, fn) { + this.listeners[eventName] = this.listeners[eventName] || []; + this.listeners[eventName].push(fn); +}; + +angular.scenario.testing.MockRunner.prototype.emit = function(eventName) { + var args = Array.prototype.slice.call(arguments, 1); + angular.forEach(this.listeners[eventName] || [], function(fn) { + fn.apply(this, args); + }); +}; diff --git a/test/ngScenario/output/HtmlSpec.js b/test/ngScenario/output/HtmlSpec.js new file mode 100644 index 00000000..442e596d --- /dev/null +++ b/test/ngScenario/output/HtmlSpec.js @@ -0,0 +1,126 @@ +'use strict'; + +describe('angular.scenario.output.html', function() { + var runner, model, spec, step, listeners, ui, context; + + beforeEach(function() { + listeners = []; + spec = { + name: 'test spec', + definition: { + id: 10, + name: 'child', + children: [], + parent: { + id: 20, + name: 'parent', + children: [] + } + } + }; + step = { + name: 'some step', + line: function() { return 'unknown:-1'; } + }; + runner = new angular.scenario.testing.MockRunner(); + model = new angular.scenario.ObjectModel(runner); + context = _jQuery("<div></div>"); + ui = angular.scenario.output.html(context, runner, model); + }); + + it('should create nested describe context', function() { + runner.emit('SpecBegin', spec); + expect(context.find('#describe-20 #describe-10 > h2').text()). + toEqual('describe: child'); + expect(context.find('#describe-20 > h2').text()).toEqual('describe: parent'); + expect(context.find('#describe-10 .tests > li .test-info .test-name').text()). + toEqual('test spec'); + expect(context.find('#describe-10 .tests > li').hasClass('status-pending')). + toBeTruthy(); + }); + + it('should add link on InteractivePause', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('StepBegin', spec, step); + runner.emit('InteractivePause', spec, step); + expect(context.find('.test-actions .test-title:first').text()).toEqual('some step'); + expect(lowercase(context.find('.test-actions .test-title:last').html())).toEqual( + 'paused... <a href="javascript:resume()">resume</a> when ready.' + ); + }); + + it('should update totals when steps complete', function() { + // Failure + for (var i=0; i < 3; ++i) { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, 'error'); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + } + + // Error + runner.emit('SpecBegin', spec); + runner.emit('SpecError', spec, 'error'); + runner.emit('SpecEnd', spec); + + // Error + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepError', spec, step, 'error'); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + // Success + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + expect(parseInt(context.find('#status-legend .status-failure').text(), 10)). + toEqual(3); + expect(parseInt(context.find('#status-legend .status-error').text(), 10)). + toEqual(2); + expect(parseInt(context.find('#status-legend .status-success').text(), 10)). + toEqual(1); + }); + + it('should update timer when test completes', function() { + // Success + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + // Failure + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, 'error'); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + // Error + runner.emit('SpecBegin', spec); + runner.emit('SpecError', spec, 'error'); + runner.emit('SpecEnd', spec); + + context.find('#describe-10 .tests > li .test-info .timer-result'). + each(function(index, timer) { + expect(timer.innerHTML).toMatch(/ms$/); + }); + }); + + it('should include line if provided', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepFailure', spec, step, 'error'); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + + var errorHtml = context.find('#describe-10 .tests li pre').html(); + expect(errorHtml.indexOf('unknown:-1')).toEqual(0); + }); + +}); diff --git a/test/ngScenario/output/jsonSpec.js b/test/ngScenario/output/jsonSpec.js new file mode 100644 index 00000000..06caf91c --- /dev/null +++ b/test/ngScenario/output/jsonSpec.js @@ -0,0 +1,37 @@ +'use strict'; + +describe('angular.scenario.output.json', function() { + var output, context; + var runner, model, $window; + var spec, step; + + beforeEach(function() { + $window = {}; + context = _jQuery('<div></div>'); + runner = new angular.scenario.testing.MockRunner(); + model = new angular.scenario.ObjectModel(runner); + output = angular.scenario.output.json(context, runner, model); + spec = { + name: 'test spec', + definition: { + id: 10, + name: 'describe' + } + }; + step = { + name: 'some step', + line: function() { return 'unknown:-1'; } + }; + }); + + it('should put json in context on RunnerEnd', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + runner.emit('RunnerEnd'); + + expect(angular.fromJson(context.html()).children['describe'] + .specs['test spec'].status).toEqual('success'); + }); +}); diff --git a/test/ngScenario/output/objectSpec.js b/test/ngScenario/output/objectSpec.js new file mode 100644 index 00000000..d92c939d --- /dev/null +++ b/test/ngScenario/output/objectSpec.js @@ -0,0 +1,40 @@ +'use strict'; + +describe('angular.scenario.output.object', function() { + var output; + var runner, model, $window; + var spec, step; + + beforeEach(function() { + $window = {}; + runner = new angular.scenario.testing.MockRunner(); + model = new angular.scenario.ObjectModel(runner); + runner.$window = $window; + output = angular.scenario.output.object(null, runner, model); + spec = { + name: 'test spec', + definition: { + id: 10, + name: 'describe', + children: [] + } + }; + step = { + name: 'some step', + line: function() { return 'unknown:-1'; } + }; + }); + + it('should create a global variable $result', function() { + expect($window.$result).toBeDefined(); + }); + + it('should maintain live state in $result', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + + expect($window.$result.children['describe'] + .specs['test spec'].steps[0].duration).toBeDefined(); + }); +}); diff --git a/test/ngScenario/output/xmlSpec.js b/test/ngScenario/output/xmlSpec.js new file mode 100644 index 00000000..94c3cb5a --- /dev/null +++ b/test/ngScenario/output/xmlSpec.js @@ -0,0 +1,36 @@ +'use strict'; + +describe('angular.scenario.output.json', function() { + var output, context; + var runner, model, $window; + var spec, step; + + beforeEach(function() { + $window = {}; + context = _jQuery('<div></div>'); + runner = new angular.scenario.testing.MockRunner(); + model = new angular.scenario.ObjectModel(runner); + output = angular.scenario.output.xml(context, runner, model); + spec = { + name: 'test spec', + definition: { + id: 10, + name: 'describe' + } + }; + step = { + name: 'some step', + line: function() { return 'unknown:-1'; } + }; + }); + + it('should create XML nodes for object model', function() { + runner.emit('SpecBegin', spec); + runner.emit('StepBegin', spec, step); + runner.emit('StepEnd', spec, step); + runner.emit('SpecEnd', spec); + runner.emit('RunnerEnd'); + expect(context.find('it').attr('status')).toEqual('success'); + expect(context.find('it step').attr('status')).toEqual('success'); + }); +}); |
