fixes IE related failures, and form submit event handling in ie

This commit is contained in:
Misko Hevery 2010-10-26 15:35:58 -07:00
parent 40d7e66f40
commit 9c0225512c
14 changed files with 134 additions and 89 deletions

View file

@ -1,5 +1,5 @@
/** /**
* @fileOverview Very simple personal log demo application to demostrate angular functionality, * @fileOverview Very simple personal log demo application to demonstrate angular functionality,
* especially: * especially:
* - the MVC model * - the MVC model
* - testability of controllers * - testability of controllers
@ -46,7 +46,7 @@ function LogCtrl($cookieStore) {
logs.push(log); logs.push(log);
$cookieStore.put(LOGS, logs); $cookieStore.put(LOGS, logs);
self.newMsg = ''; self.newMsg = '';
} };
/** /**
@ -56,16 +56,16 @@ function LogCtrl($cookieStore) {
this.rmLog = function(msgIdx) { this.rmLog = function(msgIdx) {
logs.splice(msgIdx,1); logs.splice(msgIdx,1);
$cookieStore.put(LOGS, logs); $cookieStore.put(LOGS, logs);
} };
/** /**
* Persistently removes all logs. * Persistently removes all logs.
*/ */
this.rmLogs = function() { this.rmLogs = function() {
logs.splice(0); logs.splice(0, logs.length);
$cookieStore.remove(LOGS); $cookieStore.remove(LOGS);
} };
} }
//inject //inject

View file

@ -27,12 +27,22 @@ describe('widgets', function() {
expect(binding('multiselect').fromJson()).toEqual(['A', 'C']); expect(binding('multiselect').fromJson()).toEqual(['A', 'C']);
expect(binding('button').fromJson()).toEqual({'count': 0}); expect(binding('button').fromJson()).toEqual({'count': 0});
expect(binding('form').fromJson()).toEqual({'count': 0});
element('form a').click(); element('form a').click();
expect(binding('button').fromJson()).toEqual({'count': 1}); expect(binding('button').fromJson()).toEqual({'count': 1});
element('input[value="submit"]').click();
element('input[value="submit input"]').click();
expect(binding('button').fromJson()).toEqual({'count': 2}); expect(binding('button').fromJson()).toEqual({'count': 2});
expect(binding('form').fromJson()).toEqual({'count': 1});
element('button:contains("submit button")').click();
expect(binding('button').fromJson()).toEqual({'count': 2});
expect(binding('form').fromJson()).toEqual({'count': 2});
element('input[value="button"]').click(); element('input[value="button"]').click();
expect(binding('button').fromJson()).toEqual({'count': 3}); expect(binding('button').fromJson()).toEqual({'count': 3});
element('input[type="image"]').click(); element('input[type="image"]').click();
expect(binding('button').fromJson()).toEqual({'count': 4}); expect(binding('button').fromJson()).toEqual({'count': 4});

View file

@ -73,15 +73,16 @@
<tr><th colspan="3">Buttons</th></tr> <tr><th colspan="3">Buttons</th></tr>
<tr> <tr>
<td>ng:change<br/>ng:click</td> <td>ng:change<br/>ng:click</td>
<td ng:init="button.count = 0"> <td ng:init="button.count = 0; form.count = 0;">
<form> <form ng:submit="form.count = form.count + 1">
<input type="button" value="button" ng:change="button.count = button.count + 1"/> <br/> <input type="button" value="button" ng:change="button.count = button.count + 1"/> <br/>
<input type="submit" value="submit" 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/> <input type="image" src="" ng:change="button.count = button.count + 1"/><br/>
<a href="" ng:click="button.count = button.count + 1">action</a> <a href="" ng:click="button.count = button.count + 1">action</a>
</form> </form>
</td> </td>
<td>button={{button}}</td> <td>button={{button}} form={{form}}</td>
</tr> </tr>
<tr><th colspan="3">Repeaters</th></tr> <tr><th colspan="3">Repeaters</th></tr>
<tr id="repeater-row"> <tr id="repeater-row">

View file

@ -246,6 +246,15 @@ function browserTrigger(element, type) {
break; break;
} }
element.fireEvent('on' + type); element.fireEvent('on' + type);
if (lowercase(element.type) == 'submit') {
while(element) {
if (lowercase(element.nodeName) == 'form') {
element.fireEvent('onsubmit');
break;
}
element = element.parentNode;
}
}
} else { } else {
var evnt = document.createEvent('MouseEvents'); var evnt = document.createEvent('MouseEvents');
evnt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element); evnt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);

View file

@ -3,10 +3,11 @@
*/ */
angular.scenario.output('xml', function(context, runner) { angular.scenario.output('xml', function(context, runner) {
var model = new angular.scenario.ObjectModel(runner); var model = new angular.scenario.ObjectModel(runner);
var $ = function(args) {return new context.init(args);};
runner.on('RunnerEnd', function() { runner.on('RunnerEnd', function() {
context.append('<scenario></scenario>'); var scenario = $('<scenario></scenario>');
serializeXml(context.find('> scenario'), model.value); context.append(scenario);
serializeXml(scenario, model.value);
}); });
/** /**
@ -17,30 +18,31 @@ angular.scenario.output('xml', function(context, runner) {
*/ */
function serializeXml(context, tree) { function serializeXml(context, tree) {
angular.foreach(tree.children, function(child) { angular.foreach(tree.children, function(child) {
context.append('<describe></describe>'); var describeContext = $('<describe></describe>');
var describeContext = context.find('> describe:last');
describeContext.attr('id', child.id); describeContext.attr('id', child.id);
describeContext.attr('name', child.name); describeContext.attr('name', child.name);
context.append(describeContext);
serializeXml(describeContext, child); serializeXml(describeContext, child);
}); });
context.append('<its></its>'); var its = $('<its></its>');
context = context.find('> its'); context.append(its);
angular.foreach(tree.specs, function(spec) { angular.foreach(tree.specs, function(spec) {
context.append('<it></it>') var it = $('<it></it>');
var specContext = context.find('> it:last'); it.attr('id', spec.id);
specContext.attr('id', spec.id); it.attr('name', spec.name);
specContext.attr('name', spec.name); it.attr('duration', spec.duration);
specContext.attr('duration', spec.duration); it.attr('status', spec.status);
specContext.attr('status', spec.status); its.append(it);
angular.foreach(spec.steps, function(step) { angular.foreach(spec.steps, function(step) {
specContext.append('<step></step>'); var stepContext = $('<step></step>');
var stepContext = specContext.find('> step:last');
stepContext.attr('name', step.name); stepContext.attr('name', step.name);
stepContext.attr('duration', step.duration); stepContext.attr('duration', step.duration);
stepContext.attr('status', step.status); stepContext.attr('status', step.status);
it.append(stepContext);
if (step.error) { if (step.error) {
stepContext.append('<error></error'); var error = $('<error></error');
stepContext.find('error').text(formatException(step.error)); stepContext.append(error);
error.text(formatException(stepContext.error));
} }
}); });
}); });

View file

@ -203,10 +203,6 @@ function inputWidget(events, modelAccessor, viewAccessor, initFn) {
lastValue = model.get(); lastValue = model.get();
scope.$tryEval(action, element); scope.$tryEval(action, element);
scope.$root.$eval(); scope.$root.$eval();
// if we have noop initFn than we are just a button,
// therefore we want to prevent default action
if(initFn == noop)
event.preventDefault();
}); });
} }
function updateView(){ function updateView(){

View file

@ -206,7 +206,7 @@ describe("directives", function(){
describe('ng:submit', function() { describe('ng:submit', function() {
it('should get called on form submit', function() { it('should get called on form submit', function() {
var scope = compile('<form action="" ng:submit="submitted = true">' + var scope = compile('<form action="" ng:submit="submitted = true">' +
'<input id="submit" type="submit"/>' + '<input type="submit"/>' +
'</form>'); '</form>');
scope.$eval(); scope.$eval();
expect(scope.submitted).not.toBeDefined(); expect(scope.submitted).not.toBeDefined();

View file

@ -39,56 +39,80 @@
<script type="text/javascript" src="../src/scenario/SpecRunner.js"></script> <script type="text/javascript" src="../src/scenario/SpecRunner.js"></script>
<script type="text/javascript" src="../src/scenario/dsl.js"></script> <script type="text/javascript" src="../src/scenario/dsl.js"></script>
<script type="text/javascript" src="../src/scenario/matchers.js"></script> <script type="text/javascript" src="../src/scenario/matchers.js"></script>
<script type="text/javascript" src="../src/scenario/ObjectModel.js"></script>
<script type="text/javascript" src="../src/scenario/output/Html.js"></script>
<script type="text/javascript" src="../src/scenario/output/Object.js"></script>
<script type="text/javascript" src="../src/scenario/output/Json.js"></script>
<script type="text/javascript" src="../src/scenario/output/Xml.js"></script>
<script type="text/javascript" src="angular-mocks.js"></script> <script type="text/javascript" src="angular-mocks.js"></script>
<script type="text/javascript" src="../test/scenario/mocks.js"></script>
<script type="text/javascript" src="testabilityPatch.js"></script> <script type="text/javascript" src="testabilityPatch.js"></script>
<!-- include spec files here... --> <!-- include spec files here... -->
<script type="text/javascript"> <script type="text/javascript">
describe('manual', function(){ describe('manual', function(){
var scope; var compile, model, element;
function compile(html, initialScope, parent) { beforeEach(function() {
var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget); var compiler = new Compiler(angularTextMarkup, angularAttrMarkup, angularDirective, angularWidget);
var element = self.element = jqLite(html); compile = function(html) {
scope = compiler.compile(element)(element); element = jqLite(html);
model = compiler.compile(element)(element);
if (parent) parent.append(element); model.$init();
return model;
extend(scope, initialScope); };
scope.$init();
return {node:element, scope:scope};
};
it('should debug', function(){
var x = compile(
'<select name="selection" ng:format="number">' +
'<option value="{{$index}}" ng:repeat="name in [\'A\', \'B\', \'C\']">{{name}}</option>' +
'</select>');
expect(scope.selection).toEqual(undefined);
browserTrigger(element[0].childNodes[2], 'change');
expect(scope.selection).toEqual(1);
scope.selection = 2;
scope.$eval();
expect(element[0].childNodes[3].selected).toEqual(true);
}); });
it('should reproduce', function(){ it('should get called on form submit', function() {
var select = $('<select name=""><option>a</option></select>'); var scope = compile('<form action="" ng:submit="submitted = true">' +
var log = ''; '<input type="submit"/>' +
select.bind('change', function(){ log += 'change;'; }); '</form>');
select.bind('click', function(){ log += 'click;'; }); scope.$eval();
select.bind('keyup', function(){ log += 'keyup;'; }); expect(scope.submitted).not.toBeDefined();
select[0].attachEvent('onchange', function(){ log += 'CHANGE;';});
select[0].fireEvent('onfocusout'); browserTrigger(element.children()[0]);
select[0].fireEvent('onchange'); expect(scope.submitted).toEqual(true);
select[0].fireEvent('onclick');
select[0].fireEvent('onkeyup');
expect(log).toEqual('CHANGE;change;click;keyup;');
}); });
}); });
describe('angular.scenario.output.json', function() {
var output, context;
var runner, $window;
var spec, step;
beforeEach(function() {
$window = {};
context = _jQuery('<div>text</div>');
$(document.body).append(context);
runner = new angular.scenario.testing.MockRunner();
output = angular.scenario.output.xml(context, runner);
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(_jQuery(context).find('it').attr('status')).toEqual('success');
expect(_jQuery(context).find('it step').attr('status')).toEqual('success');
});
});
</script> </script>
</head> </head>
@ -96,7 +120,10 @@ describe('manual', function(){
<script type="text/javascript"> <script type="text/javascript">
jasmine.getEnv().addReporter(new jasmine.TrivialReporter()); jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
jasmine.getEnv().execute(); function run(){
jasmine.getEnv().execute();
}
run();
</script> </script>
</body> </body>

View file

@ -88,7 +88,7 @@ describe('angular.scenario.Application', function() {
var testWindow = { var testWindow = {
document: _jQuery('<div class="test-foo"></div>'), document: _jQuery('<div class="test-foo"></div>'),
angular: { angular: {
service: {}, service: {}
} }
}; };
testWindow.angular.service.$browser = function() { testWindow.angular.service.$browser = function() {

View file

@ -177,7 +177,7 @@ describe("angular.scenario.dsl", function() {
expect($window.location).not.toEqual('#foo'); expect($window.location).not.toEqual('#foo');
doc.append('<a href="#foo"></a>'); doc.append('<a href="#foo"></a>');
$root.dsl.element('a').click(); $root.dsl.element('a').click();
expect($window.location).toEqual('#foo'); expect($window.location).toMatch(/#foo$/);
}); });
it('should count matching elements', function() { it('should count matching elements', function() {

View file

@ -19,7 +19,7 @@ describe('angular.scenario.output.html', function() {
}; };
step = { step = {
name: 'some step', name: 'some step',
line: function() { return 'unknown:-1'; }, line: function() { return 'unknown:-1'; }
}; };
runner = new angular.scenario.testing.MockRunner(); runner = new angular.scenario.testing.MockRunner();
context = _jQuery("<div></div>"); context = _jQuery("<div></div>");
@ -44,7 +44,7 @@ describe('angular.scenario.output.html', function() {
runner.emit('StepBegin', spec, step); runner.emit('StepBegin', spec, step);
runner.emit('InteractiveWait', spec, step); runner.emit('InteractiveWait', spec, step);
expect(context.find('.test-actions .test-title:first').text()).toEqual('some step'); expect(context.find('.test-actions .test-title:first').text()).toEqual('some step');
expect(context.find('.test-actions .test-title:last').html()).toEqual( expect(lowercase(context.find('.test-actions .test-title:last').html())).toEqual(
'waiting for you to <a href="javascript:resume()">resume</a>.' 'waiting for you to <a href="javascript:resume()">resume</a>.'
); );
}); });

View file

@ -12,12 +12,12 @@ describe('angular.scenario.output.json', function() {
name: 'test spec', name: 'test spec',
definition: { definition: {
id: 10, id: 10,
name: 'describe', name: 'describe'
} }
}; };
step = { step = {
name: 'some step', name: 'some step',
line: function() { return 'unknown:-1'; }, line: function() { return 'unknown:-1'; }
}; };
}); });

View file

@ -18,7 +18,7 @@ describe('angular.scenario.output.object', function() {
}; };
step = { step = {
name: 'some step', name: 'some step',
line: function() { return 'unknown:-1'; }, line: function() { return 'unknown:-1'; }
}; };
}); });

View file

@ -12,12 +12,12 @@ describe('angular.scenario.output.json', function() {
name: 'test spec', name: 'test spec',
definition: { definition: {
id: 10, id: 10,
name: 'describe', name: 'describe'
} }
}; };
step = { step = {
name: 'some step', name: 'some step',
line: function() { return 'unknown:-1'; }, line: function() { return 'unknown:-1'; }
}; };
}); });
@ -27,7 +27,7 @@ describe('angular.scenario.output.json', function() {
runner.emit('StepEnd', spec, step); runner.emit('StepEnd', spec, step);
runner.emit('SpecEnd', spec); runner.emit('SpecEnd', spec);
runner.emit('RunnerEnd'); runner.emit('RunnerEnd');
expect(_jQuery(context).find('it').attr('status')).toEqual('success'); expect(context.find('it').attr('status')).toEqual('success');
expect(_jQuery(context).find('it step').attr('status')).toEqual('success'); expect(context.find('it step').attr('status')).toEqual('success');
}); });
}); });