Fix browser triggering in scenario to always do native events.

- Also fixed angular.suffix for scenarios
 - refactored click() to browserTrigger()
 - Fixed Rakefile with CSS and jQuery
This commit is contained in:
Misko Hevery 2010-10-19 15:34:58 -07:00
parent e7e894a2e3
commit 01c7abab35
16 changed files with 119 additions and 152 deletions

View file

@ -66,7 +66,7 @@ task :compile_scenario do
File.open('angular-scenario.js', 'w') do |f| File.open('angular-scenario.js', 'w') do |f|
f.write(%x{#{concat}}) f.write(%x{#{concat}})
f.write(gen_css('css/angular.css')) f.write(gen_css('css/angular.css') + "\n")
f.write(gen_css('css/angular-scenario.css')) f.write(gen_css('css/angular-scenario.css'))
end end
end end
@ -234,7 +234,8 @@ def gen_css(cssFile, minify = false)
end end
#escape for js #escape for js
css.gsub! /'/, "\\'" css.gsub! /\\/, "\\\\\\"
css.gsub! /'/, "\\\\'"
css.gsub! /\n/, "\\n" css.gsub! /\n/, "\\n"
return %Q{document.write('<style type="text/css">#{css}</style>');} return %Q{document.write('<style type="text/css">#{css}</style>');}

View file

@ -129,7 +129,7 @@ body {
} }
.test-actions .status-pending .test-title:before { .test-actions .status-pending .test-title:before {
content: '» '; content: '\00bb\00A0';
} }
/** Colors */ /** Colors */

View file

@ -23,6 +23,7 @@ describe('widgets', function() {
select('select').option('B'); select('select').option('B');
expect(binding('select')).toEqual('B'); expect(binding('select')).toEqual('B');
select('multiselect').options('A', 'C'); select('multiselect').options('A', 'C');
expect(binding('multiselect').fromJson()).toEqual(['A', 'C']); expect(binding('multiselect').fromJson()).toEqual(['A', 'C']);

View file

@ -2,7 +2,7 @@
<html xmlns:ng="http://angularjs.org"> <html xmlns:ng="http://angularjs.org">
<head> <head>
<link rel="stylesheet" type="text/css" href="style.css"/> <link rel="stylesheet" type="text/css" href="style.css"/>
<script type="text/javascript" src="../lib/jquery/jquery-1.4.2.js"></script> <script type="text/javascript" src="../libs/jquery/jquery-1.4.2.js"></script>
<script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script> <script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script>
</head> </head>
<body ng:init="$window.$scope = this"> <body ng:init="$window.$scope = this">

View file

@ -146,12 +146,12 @@ function HTML(html) {
if (msie) { if (msie) {
nodeName = function(element) { nodeName = function(element) {
element = element[0] || element; element = element.nodeName ? element : element[0];
return (element.scopeName && element.scopeName != 'HTML' ) ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; return (element.scopeName && element.scopeName != 'HTML' ) ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
}; };
} else { } else {
nodeName = function(element) { nodeName = function(element) {
return (element[0] || element).nodeName; return element.nodeName ? element.nodeName : element[0].nodeName;
}; };
} }

View file

@ -118,17 +118,6 @@ JQLite.prototype = {
}); });
}, },
trigger: function(type) {
if (msie) {
this[0].fireEvent('on' + type);
} else {
var evnt = document.createEvent('MouseEvents'),
element = this[0];
evnt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
element.dispatchEvent(evnt);
}
},
replaceWith: function(replaceNode) { replaceWith: function(replaceNode) {
this[0].parentNode.replaceChild(jqLite(replaceNode)[0], this[0]); this[0].parentNode.replaceChild(jqLite(replaceNode)[0], this[0]);
}, },

View file

@ -101,3 +101,44 @@ function asyncForEach(list, iterator, done) {
} }
loop(); loop();
} }
function browserTrigger(element, type) {
if (!element.nodeName) element = element[0];
if (!type) {
type = {
'text': 'change',
'textarea': 'change',
'hidden': 'change',
'password': 'change',
'button': 'click',
'submit': 'click',
'reset': 'click',
'image': 'click',
'checkbox': 'click',
'radio': 'click',
'select-one': 'change',
'select-multiple': 'change'
}[element.type] || 'click';
}
if (lowercase(nodeName(element)) == 'option') {
element.parentNode.value = element.value;
element = element.parentNode;
type = 'change';
}
if (msie) {
element.fireEvent('on' + type);
} else {
var evnt = document.createEvent('MouseEvents');
evnt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
element.dispatchEvent(evnt);
}
}
_jQuery.fn.trigger = function(type) {
return this.each(function(index, node) {
browserTrigger(node, type);
});
};

View file

@ -89,26 +89,6 @@ angular.scenario.SpecRunner.prototype.addFutureAction = function(name, behavior)
}; };
} }
result.trigger = function(type) {
result.each(function(index, node) {
var element = $window.angular.element(node);
//TODO(esprehn): HACK!!! Something is broken in angular event dispatching
// and if the real jQuery is used we need to set the attribtue after too
if (angular.isDefined(element.selector)) {
if (type === 'click' && node.nodeName.toLowerCase() === 'input') {
element.attr('checked', !element.attr('checked'));
}
}
//TODO(esprehn): HACK!! See above comment.
element.trigger(type);
if (angular.isDefined(element.selector)) {
if (type === 'click' && node.nodeName.toLowerCase() === 'input') {
element.attr('checked', !element.attr('checked'));
}
}
});
};
return result; return result;
}); });

View file

@ -22,3 +22,4 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
(function(window, document, previousOnLoad){ (function(window, document, previousOnLoad){
var _jQuery = window.jQuery.noConflict(true);

View file

@ -4,22 +4,19 @@
try { try {
if (previousOnLoad) previousOnLoad(); if (previousOnLoad) previousOnLoad();
} catch(e) {} } catch(e) {}
jQuery(document.body).append( _jQuery(document.body).append(
'<div id="runner"></div>' + '<div id="runner"></div>' +
'<div id="frame"></div>' '<div id="frame"></div>'
); );
var frame = jQuery('#frame'); var frame = _jQuery('#frame');
var runner = jQuery('#runner'); var runner = _jQuery('#runner');
var application = new angular.scenario.Application(frame); var application = new angular.scenario.Application(frame);
var ui = new angular.scenario.ui.Html(runner); var ui = new angular.scenario.ui.Html(runner);
$scenario.run(ui, application, function(error) { $scenario.run(ui, application, angular.scenario.SpecRunner, function(error) {
frame.remove(); frame.remove();
if (error) { if (error) {
if (window.console) { if (window.console) {
console.log(error); console.log(error.stack || error);
if (error.stack) {
console.log(error.stack);
}
} else { } else {
// Do something for IE // Do something for IE
alert(error); alert(error);

View file

@ -46,6 +46,11 @@
addCSS("../../css/angular-scenario.css"); addCSS("../../css/angular-scenario.css");
addScript("../../lib/jquery/jquery-1.4.2.js"); addScript("../../lib/jquery/jquery-1.4.2.js");
document.write(
'<script type="text/javascript">' +
'var _jQuery = jQuery.noConflict(true);' +
'</script>'
);
addScript("../angular-bootstrap.js"); addScript("../angular-bootstrap.js");
addScript("Scenario.js"); addScript("Scenario.js");
@ -61,7 +66,6 @@
// Create the runner (which also sets up the global API) // Create the runner (which also sets up the global API)
document.write( document.write(
'<script type="text/javascript">' + '<script type="text/javascript">' +
'var _jQuery = jQuery.noConflict(true);' +
'var $scenario = new angular.scenario.Runner(window);' + 'var $scenario = new angular.scenario.Runner(window);' +
'</script>' '</script>'
); );

View file

@ -3,5 +3,5 @@ if [[ $tests = "" ]]; then
tests="all" tests="all"
fi fi
java -jar lib/jstestdriver/JsTestDriver.jar --tests "$tests" #java -jar lib/jstestdriver/JsTestDriver.jar --tests "$tests"
#java -jar lib/jstestdriver/JsTestDriver.jar --tests "$tests" --config jsTestDriver-jquery.conf java -jar lib/jstestdriver/JsTestDriver.jar --tests "$tests" --config jsTestDriver-jquery.conf

View file

@ -589,13 +589,13 @@ BinderTest.prototype.testItShouldSelectTheCorrectRadioBox = function() {
var female = jqLite(c.node[0].childNodes[0]); var female = jqLite(c.node[0].childNodes[0]);
var male = jqLite(c.node[0].childNodes[1]); var male = jqLite(c.node[0].childNodes[1]);
click(female); browserTrigger(female);
assertEquals("female", c.scope.sex); assertEquals("female", c.scope.sex);
assertEquals(true, female[0].checked); assertEquals(true, female[0].checked);
assertEquals(false, male[0].checked); assertEquals(false, male[0].checked);
assertEquals("female", female.val()); assertEquals("female", female.val());
click(male); browserTrigger(male);
assertEquals("male", c.scope.sex); assertEquals("male", c.scope.sex);
assertEquals(false, female[0].checked); assertEquals(false, female[0].checked);
assertEquals(true, male[0].checked); assertEquals(true, male[0].checked);

View file

@ -10,24 +10,6 @@ AngularMock.prototype.reset = function() {
this.log = []; this.log = [];
}; };
AngularMock.prototype.element = function(node) {
this.log.push('element(' + node.nodeName.toLowerCase() + ')');
var mock = this;
return {
selector: '',
attr: function(name, value) {
mock.log.push('attr(' + name + (angular.isDefined(value) ? ',' + value : '') + ')');
return _jQuery.fn.attr.apply(_jQuery(node), arguments);
},
trigger: function(type) {
mock.log.push('element().trigger(' + type + ')');
//TODO(esprehn): See the HACK!! in the SpecRunner. This avoids
// triggering the second part of the hack in tests
delete this.selector;
}
};
};
AngularMock.prototype.$browser = function() { AngularMock.prototype.$browser = function() {
this.log.push('$brower()'); this.log.push('$brower()');
return this; return this;
@ -281,8 +263,6 @@ describe("angular.scenario.dsl", function() {
); );
chain = $root.dsl.using('div#test2'); chain = $root.dsl.using('div#test2');
chain.input('test.input').enter('foo'); chain.input('test.input').enter('foo');
expect($window.angular.log).toContain('element(input)');
expect($window.angular.log).toContain('element().trigger(change)');
var inputs = _jQuery('input[name="test.input"]'); var inputs = _jQuery('input[name="test.input"]');
expect(inputs.first().val()).toEqual('something'); expect(inputs.first().val()).toEqual('something');
expect(inputs.last().val()).toEqual('foo'); expect(inputs.last().val()).toEqual('foo');
@ -294,8 +274,6 @@ describe("angular.scenario.dsl", function() {
doc.append('<input name="test.input" value="something">'); doc.append('<input name="test.input" value="something">');
var chain = $root.dsl.input('test.input'); var chain = $root.dsl.input('test.input');
chain.enter('foo'); chain.enter('foo');
expect($window.angular.log).toContain('element(input)');
expect($window.angular.log).toContain('element().trigger(change)');
expect(_jQuery('input[name="test.input"]').val()).toEqual('foo'); expect(_jQuery('input[name="test.input"]').val()).toEqual('foo');
}); });
@ -311,14 +289,10 @@ describe("angular.scenario.dsl", function() {
attr('checked')).toBeTruthy(); attr('checked')).toBeTruthy();
var chain = $root.dsl.input('test.input'); var chain = $root.dsl.input('test.input');
chain.check(); chain.check();
expect($window.angular.log).toContain('element(input)');
expect($window.angular.log).toContain('element().trigger(click)');
expect(_jQuery('input[name="test.input"]'). expect(_jQuery('input[name="test.input"]').
attr('checked')).toBeFalsy(); attr('checked')).toBeFalsy();
$window.angular.reset(); $window.angular.reset();
chain.check(); chain.check();
expect($window.angular.log).toContain('element(input)');
expect($window.angular.log).toContain('element().trigger(click)');
expect(_jQuery('input[name="test.input"]'). expect(_jQuery('input[name="test.input"]').
attr('checked')).toBeTruthy(); attr('checked')).toBeTruthy();
}); });
@ -342,8 +316,6 @@ describe("angular.scenario.dsl", function() {
attr('checked')).toBeFalsy(); attr('checked')).toBeFalsy();
var chain = $root.dsl.input('test.input'); var chain = $root.dsl.input('test.input');
chain.select('foo'); chain.select('foo');
expect($window.angular.log).toContain('element(input)');
expect($window.angular.log).toContain('element().trigger(click)');
expect(_jQuery('input[name="0@test.input"][value="bar"]'). expect(_jQuery('input[name="0@test.input"][value="bar"]').
attr('checked')).toBeFalsy(); attr('checked')).toBeFalsy();
expect(_jQuery('input[name="0@test.input"][value="foo"]'). expect(_jQuery('input[name="0@test.input"][value="foo"]').

View file

@ -189,25 +189,6 @@ function assertThrows(error, fn){
log = noop; log = noop;
error = noop; error = noop;
function click(element) {
element = jqLite(element);
var type = lowercase(element.attr('type'));
var name = lowercase(nodeName(element));
if (msie) {
if (name == 'input') {
if (type == 'radio' || type == 'checkbox') {
element[0].checked = ! element[0].checked;
}
}
}
if (name == 'option') {
element.parent().val(element.val());
JQLite.prototype.trigger.call(element.parent(), 'change');
} else {
JQLite.prototype.trigger.call(element, 'click');
}
}
function rethrow(e) { function rethrow(e) {
if(e) { if(e) {
throw e; throw e;

View file

@ -128,10 +128,10 @@ describe("widget", function(){
it('should support type="checkbox"', function(){ it('should support type="checkbox"', function(){
compile('<input type="checkBox" name="checkbox" checked ng:change="action = true"/>'); compile('<input type="checkBox" name="checkbox" checked ng:change="action = true"/>');
expect(scope.checkbox).toEqual(true); expect(scope.checkbox).toEqual(true);
click(element); browserTrigger(element);
expect(scope.checkbox).toEqual(false); expect(scope.checkbox).toEqual(false);
expect(scope.action).toEqual(true); expect(scope.action).toEqual(true);
click(element); browserTrigger(element);
expect(scope.checkbox).toEqual(true); expect(scope.checkbox).toEqual(true);
}); });
@ -151,7 +151,7 @@ describe("widget", function(){
expect(scope.state).toEqual("Worked"); expect(scope.state).toEqual("Worked");
expect(scope.$element[0].checked).toEqual(true); expect(scope.$element[0].checked).toEqual(true);
click(scope.$element); browserTrigger(scope.$element);
expect(scope.state).toEqual("Failed"); expect(scope.state).toEqual("Failed");
expect(scope.$element[0].checked).toEqual(false); expect(scope.$element[0].checked).toEqual(false);
@ -278,13 +278,13 @@ describe("widget", function(){
it('should call ng:change on button click', function(){ it('should call ng:change on button click', function(){
compile('<input type="button" value="Click Me" ng:change="clicked = true"/>'); compile('<input type="button" value="Click Me" ng:change="clicked = true"/>');
click(element); browserTrigger(element);
expect(scope.$get('clicked')).toEqual(true); expect(scope.$get('clicked')).toEqual(true);
}); });
it('should support button alias', function(){ it('should support button alias', function(){
compile('<button ng:change="clicked = true">Click Me</button>'); compile('<button ng:change="clicked = true">Click Me</button>');
click(element); browserTrigger(element);
expect(scope.$get('clicked')).toEqual(true); expect(scope.$get('clicked')).toEqual(true);
}); });
@ -310,7 +310,7 @@ describe("widget", function(){
expect(b.checked).toEqual(true); expect(b.checked).toEqual(true);
expect(scope.clicked).not.toBeDefined(); expect(scope.clicked).not.toBeDefined();
click(a); browserTrigger(a);
expect(scope.chose).toEqual('A'); expect(scope.chose).toEqual('A');
expect(scope.clicked).toEqual(1); expect(scope.clicked).toEqual(1);
}); });
@ -363,7 +363,7 @@ describe("widget", function(){
// childNodes[0] is repeater comment // childNodes[0] is repeater comment
expect(scope.selection).toEqual(undefined); expect(scope.selection).toEqual(undefined);
click(element[0].childNodes[2]); browserTrigger(element[0].childNodes[2], 'change');
expect(scope.selection).toEqual(1); expect(scope.selection).toEqual(1);
scope.selection = 2; scope.selection = 2;
@ -423,7 +423,7 @@ describe("widget", function(){
it('should report error on ng:change exception', function(){ it('should report error on ng:change exception', function(){
compile('<button ng:change="a-2=x">click</button>'); compile('<button ng:change="a-2=x">click</button>');
click(element); browserTrigger(element);
expect(element.hasClass('ng-exception')).toBeTruthy(); expect(element.hasClass('ng-exception')).toBeTruthy();
}); });
}); });