chore(module): move files around in preparation for more modules

This commit is contained in:
Misko Hevery
2012-03-23 14:03:24 -07:00
parent 944098a4e0
commit 2430f52bb9
305 changed files with 114 additions and 105 deletions

View File

@@ -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);
}));
});

View File

@@ -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);
});
});

View File

@@ -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();
});
});
});

View File

@@ -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);
});
});
});

View File

@@ -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*/);
});
});

View File

@@ -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');
}));
});
});

View File

@@ -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'
]);
});
});

629
test/ngScenario/dslSpec.js Normal file
View File

@@ -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/);
});
});
});
});

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,11 @@
th {
text-align: left;
}
tr {
border: 1px solid black;
}
.redbox {
background-color: red;
}

View File

@@ -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});
});
});

View File

@@ -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>

View File

@@ -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);
});
});
});

View File

@@ -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');
});
});

33
test/ngScenario/mocks.js Normal file
View File

@@ -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);
});
};

View File

@@ -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);
});
});

View File

@@ -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');
});
});

View File

@@ -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();
});
});

View File

@@ -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');
});
});