aboutsummaryrefslogtreecommitdiffstats
path: root/test/ngScenario
diff options
context:
space:
mode:
Diffstat (limited to 'test/ngScenario')
-rw-r--r--test/ngScenario/ApplicationSpec.js141
-rw-r--r--test/ngScenario/DescribeSpec.js122
-rw-r--r--test/ngScenario/FutureSpec.js77
-rw-r--r--test/ngScenario/ObjectModelSpec.js333
-rw-r--r--test/ngScenario/RunnerSpec.js116
-rw-r--r--test/ngScenario/ScenarioSpec.js32
-rw-r--r--test/ngScenario/SpecRunnerSpec.js177
-rw-r--r--test/ngScenario/dslSpec.js629
-rw-r--r--test/ngScenario/e2e/Runner-compiled.html9
-rw-r--r--test/ngScenario/e2e/Runner.html9
-rw-r--r--test/ngScenario/e2e/style.css11
-rw-r--r--test/ngScenario/e2e/widgets-scenario.js67
-rw-r--r--test/ngScenario/e2e/widgets.html99
-rw-r--r--test/ngScenario/jstd-scenario-adapter/AdapterSpecs.js322
-rw-r--r--test/ngScenario/matchersSpec.js51
-rw-r--r--test/ngScenario/mocks.js33
-rw-r--r--test/ngScenario/output/HtmlSpec.js126
-rw-r--r--test/ngScenario/output/jsonSpec.js37
-rw-r--r--test/ngScenario/output/objectSpec.js40
-rw-r--r--test/ngScenario/output/xmlSpec.js36
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');
+ });
+});