Adding unit tests to make sure .scrollTop() and .scrollLeft() work cross-frame.
[jquery.git] / test / unit / offset.js
1 module("offset");
2
3 testoffset("absolute"/* in iframe */, function($, iframe) {
4         expect(4);
5         
6         var doc = iframe.document, tests;
7         
8         // force a scroll value on the main window
9         // this insures that the results will be wrong
10         // if the offset method is using the scroll offset
11         // of the parent window
12         var forceScroll = jQuery('<div>', { width: 2000, height: 2000 }).appendTo('body');
13         window.scrollTo(1, 1);
14         
15         // get offset
16         tests = [
17                 { id: '#absolute-1', top: 1, left: 1 }
18         ];
19         jQuery.each( tests, function() {
20                 equals( jQuery( this.id, doc ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
21                 equals( jQuery( this.id, doc ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
22         });
23
24
25         // get position
26         tests = [
27                 { id: '#absolute-1', top: 0, left: 0 }
28         ];
29         jQuery.each( tests, function() {
30                 equals( jQuery( this.id, doc ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
31                 equals( jQuery( this.id, doc ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
32         });
33         
34         forceScroll.remove();
35 });
36
37 testoffset("absolute", function( jQuery ) {
38         expect(144);
39         
40         // get offset tests
41         var tests = [
42                 { id: '#absolute-1',     top:  1, left:  1 }, 
43                 { id: '#absolute-1-1',   top:  5, left:  5 },
44                 { id: '#absolute-1-1-1', top:  9, left:  9 },
45                 { id: '#absolute-2',     top: 20, left: 20 }
46         ];
47         jQuery.each( tests, function() {
48                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
49                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
50         });
51         
52         
53         // get position
54         tests = [
55                 { id: '#absolute-1',     top:  0, left:  0 },
56                 { id: '#absolute-1-1',   top:  1, left:  1 },
57                 { id: '#absolute-1-1-1', top:  1, left:  1 },
58                 { id: '#absolute-2',     top: 19, left: 19 }
59         ];
60         jQuery.each( tests, function() {
61                 equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
62                 equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
63         });
64         
65         
66         // set offset
67         tests = [
68                 { id: '#absolute-2',     top: 30, left: 30 },
69                 { id: '#absolute-2',     top: 10, left: 10 },
70                 { id: '#absolute-2',     top: -1, left: -1 },
71                 { id: '#absolute-2',     top: 19, left: 19 },
72                 { id: '#absolute-1-1-1', top: 15, left: 15 },
73                 { id: '#absolute-1-1-1', top:  5, left:  5 },
74                 { id: '#absolute-1-1-1', top: -1, left: -1 },
75                 { id: '#absolute-1-1-1', top:  9, left:  9 },
76                 { id: '#absolute-1-1',   top: 10, left: 10 },
77                 { id: '#absolute-1-1',   top:  0, left:  0 },
78                 { id: '#absolute-1-1',   top: -1, left: -1 },
79                 { id: '#absolute-1-1',   top:  5, left:  5 },
80                 { id: '#absolute-1',     top:  2, left:  2 },
81                 { id: '#absolute-1',     top:  0, left:  0 },
82                 { id: '#absolute-1',     top: -1, left: -1 },
83                 { id: '#absolute-1',     top:  1, left:  1 }
84         ];
85         jQuery.each( tests, function() {
86                 jQuery( this.id ).offset({ top: this.top, left: this.left });
87                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
88                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
89                 
90                 var top = this.top, left = this.left;
91                 
92                 jQuery( this.id ).offset(function(i, val){
93                         equals( val.top, top, "Verify incoming top position." );
94                         equals( val.left, left, "Verify incoming top position." );
95                         return { top: top + 1, left: left + 1 };
96                 });
97                 equals( jQuery( this.id ).offset().top,  this.top + 1,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
98                 equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
99                 
100                 jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
101                         jQuery( this ).css({
102                                 top:  props.top  + 1,
103                                 left: props.left + 1
104                         });
105                 }});
106                 equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + ", using: fn })" );
107                 equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + ", using: fn })" );
108         });
109 });
110
111 testoffset("relative", function( jQuery ) {
112         expect(60);
113         
114         // IE is collapsing the top margin of 1px
115         var ie = jQuery.browser.msie && parseInt( jQuery.browser.version ) < 8;
116         
117         // get offset
118         var tests = [
119                 { id: '#relative-1',   top: ie ?   6 :   7, left:  7 },
120                 { id: '#relative-1-1', top: ie ?  13 :  15, left: 15 },
121                 { id: '#relative-2',   top: ie ? 141 : 142, left: 27 }
122         ];
123         jQuery.each( tests, function() {
124                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
125                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
126         });
127         
128         
129         // get position
130         tests = [
131                 { id: '#relative-1',   top: ie ?   5 :   6, left:  6 },
132                 { id: '#relative-1-1', top: ie ?   4 :   5, left:  5 },
133                 { id: '#relative-2',   top: ie ? 140 : 141, left: 26 }
134         ];
135         jQuery.each( tests, function() {
136                 equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
137                 equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
138         });
139         
140         
141         // set offset
142         tests = [
143                 { id: '#relative-2',   top: 200, left:  50 },
144                 { id: '#relative-2',   top: 100, left:  10 },
145                 { id: '#relative-2',   top:  -5, left:  -5 },
146                 { id: '#relative-2',   top: 142, left:  27 },
147                 { id: '#relative-1-1', top: 100, left: 100 },
148                 { id: '#relative-1-1', top:   5, left:   5 },
149                 { id: '#relative-1-1', top:  -1, left:  -1 },
150                 { id: '#relative-1-1', top:  15, left:  15 },
151                 { id: '#relative-1',   top: 100, left: 100 },
152                 { id: '#relative-1',   top:   0, left:   0 },
153                 { id: '#relative-1',   top:  -1, left:  -1 },
154                 { id: '#relative-1',   top:   7, left:   7 }
155         ];
156         jQuery.each( tests, function() {
157                 jQuery( this.id ).offset({ top: this.top, left: this.left });
158                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
159                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
160                 
161                 jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
162                         jQuery( this ).css({
163                                 top:  props.top  + 1,
164                                 left: props.left + 1
165                         });
166                 }});
167                 equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + ", using: fn })" );
168                 equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + ", using: fn })" );
169         });
170 });
171
172 testoffset("static", function( jQuery ) {
173         expect(80);
174         
175         // IE is collapsing the top margin of 1px
176         var ie = jQuery.browser.msie && parseInt( jQuery.browser.version ) < 8;
177         
178         // get offset
179         var tests = [
180                 { id: '#static-1',     top: ie ?   6 :   7, left:  7 },
181                 { id: '#static-1-1',   top: ie ?  13 :  15, left: 15 },
182                 { id: '#static-1-1-1', top: ie ?  20 :  23, left: 23 },
183                 { id: '#static-2',     top: ie ? 121 : 122, left:  7 }
184         ];
185         jQuery.each( tests, function() {
186                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
187                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
188         });
189         
190         
191         // get position
192         tests = [
193                 { id: '#static-1',     top: ie ?   5 :   6, left:  6 },
194                 { id: '#static-1-1',   top: ie ?  12 :  14, left: 14 },
195                 { id: '#static-1-1-1', top: ie ?  19 :  22, left: 22 },
196                 { id: '#static-2',     top: ie ? 120 : 121, left:  6 }
197         ];
198         jQuery.each( tests, function() {
199                 equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.top  + "').position().top" );
200                 equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.left +"').position().left" );
201         });
202         
203         
204         // set offset
205         tests = [
206                 { id: '#static-2',     top: 200, left: 200 },
207                 { id: '#static-2',     top: 100, left: 100 },
208                 { id: '#static-2',     top:  -2, left:  -2 },
209                 { id: '#static-2',     top: 121, left:   6 },
210                 { id: '#static-1-1-1', top:  50, left:  50 },
211                 { id: '#static-1-1-1', top:  10, left:  10 },
212                 { id: '#static-1-1-1', top:  -1, left:  -1 },
213                 { id: '#static-1-1-1', top:  22, left:  22 },
214                 { id: '#static-1-1',   top:  25, left:  25 },
215                 { id: '#static-1-1',   top:  10, left:  10 },
216                 { id: '#static-1-1',   top:  -3, left:  -3 },
217                 { id: '#static-1-1',   top:  14, left:  14 },
218                 { id: '#static-1',     top:  30, left:  30 },
219                 { id: '#static-1',     top:   2, left:   2 },
220                 { id: '#static-1',     top:  -2, left:  -2 },
221                 { id: '#static-1',     top:   7, left:   7 }
222         ];
223         jQuery.each( tests, function() {
224                 jQuery( this.id ).offset({ top: this.top, left: this.left });
225                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
226                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
227                 
228                 jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
229                         jQuery( this ).css({
230                                 top:  props.top  + 1,
231                                 left: props.left + 1
232                         });
233                 }});
234                 equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + ", using: fn })" );
235                 equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + ", using: fn })" );
236         });
237 });
238
239 testoffset("fixed", function( jQuery ) {
240         expect(28);
241         
242         jQuery.offset.initialize();
243         
244         var tests = [
245                 { id: '#fixed-1', top: 1001, left: 1001 },
246                 { id: '#fixed-2', top: 1021, left: 1021 }
247         ];
248         jQuery.each( tests, function() {
249                 if ( jQuery.offset.supportsFixedPosition ) {
250                         equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
251                         equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
252                 } else {
253                         // need to have same number of assertions
254                         ok( true, 'Fixed position is not supported' );
255                         ok( true, 'Fixed position is not supported' );
256                 }
257         });
258         
259         tests = [
260                 { id: '#fixed-1', top: 100, left: 100 },
261                 { id: '#fixed-1', top:   0, left:   0 },
262                 { id: '#fixed-1', top:  -4, left:  -4 },
263                 { id: '#fixed-2', top: 200, left: 200 },
264                 { id: '#fixed-2', top:   0, left:   0 },
265                 { id: '#fixed-2', top:  -5, left:  -5 }
266         ];
267         
268         jQuery.each( tests, function() {
269                 if ( jQuery.offset.supportsFixedPosition ) {
270                         jQuery( this.id ).offset({ top: this.top, left: this.left });
271                         equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
272                         equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
273                 
274                         jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
275                                 jQuery( this ).css({
276                                         top:  props.top  + 1,
277                                         left: props.left + 1
278                                 });
279                         }});
280                         equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + ", using: fn })" );
281                         equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + ", using: fn })" );
282                 } else {
283                         // need to have same number of assertions
284                         ok( true, 'Fixed position is not supported' );
285                         ok( true, 'Fixed position is not supported' );
286                         ok( true, 'Fixed position is not supported' );
287                         ok( true, 'Fixed position is not supported' );
288                 }
289         });
290 });
291
292 testoffset("table", function( jQuery ) {
293         expect(4);
294         
295         equals( jQuery('#table-1').offset().top, 6, "jQuery('#table-1').offset().top" );
296         equals( jQuery('#table-1').offset().left, 6, "jQuery('#table-1').offset().left" );
297         
298         equals( jQuery('#th-1').offset().top, 10, "jQuery('#th-1').offset().top" );
299         equals( jQuery('#th-1').offset().left, 10, "jQuery('#th-1').offset().left" );
300 });
301
302 testoffset("scroll", function( jQuery, win ) {
303         expect(16);
304         
305         var ie = jQuery.browser.msie && parseInt( jQuery.browser.version ) < 8;
306         
307         // IE is collapsing the top margin of 1px
308         equals( jQuery('#scroll-1').offset().top, ie ? 6 : 7, "jQuery('#scroll-1').offset().top" );
309         equals( jQuery('#scroll-1').offset().left, 7, "jQuery('#scroll-1').offset().left" );
310         
311         // IE is collapsing the top margin of 1px
312         equals( jQuery('#scroll-1-1').offset().top, ie ? 9 : 11, "jQuery('#scroll-1-1').offset().top" );
313         equals( jQuery('#scroll-1-1').offset().left, 11, "jQuery('#scroll-1-1').offset().left" );
314         
315         
316         // scroll offset tests .scrollTop/Left
317         equals( jQuery('#scroll-1').scrollTop(), 5, "jQuery('#scroll-1').scrollTop()" );
318         equals( jQuery('#scroll-1').scrollLeft(), 5, "jQuery('#scroll-1').scrollLeft()" );
319         
320         equals( jQuery('#scroll-1-1').scrollTop(), 0, "jQuery('#scroll-1-1').scrollTop()" );
321         equals( jQuery('#scroll-1-1').scrollLeft(), 0, "jQuery('#scroll-1-1').scrollLeft()" );
322         
323         // equals( jQuery('body').scrollTop(), 0, "jQuery('body').scrollTop()" );
324         // equals( jQuery('body').scrollLeft(), 0, "jQuery('body').scrollTop()" );
325         
326         win.name = "test";
327         
328         equals( jQuery(win).scrollTop(), 1000, "jQuery(window).scrollTop()" );
329         equals( jQuery(win).scrollLeft(), 1000, "jQuery(window).scrollLeft()" );
330         
331         equals( jQuery(win.document).scrollTop(), 1000, "jQuery(document).scrollTop()" );
332         equals( jQuery(win.document).scrollLeft(), 1000, "jQuery(document).scrollLeft()" );
333         
334         // test jQuery using parent window/document
335         // jQuery reference here is in the iframe
336         window.scrollTo(0,0);
337         equals( jQuery(window).scrollTop(), 0, "jQuery(window).scrollTop() other window" );
338         equals( jQuery(window).scrollLeft(), 0, "jQuery(window).scrollLeft() other window" );
339         equals( jQuery(document).scrollTop(), 0, "jQuery(window).scrollTop() other document" );
340         equals( jQuery(document).scrollLeft(), 0, "jQuery(window).scrollLeft() other document" );
341 });
342
343 testoffset("body", function( jQuery ) {
344         expect(2);
345         
346         equals( jQuery('body').offset().top, 1, "jQuery('#body').offset().top" );
347         equals( jQuery('body').offset().left, 1, "jQuery('#body').offset().left" );
348 });
349
350 test("Chaining offset(coords) returns jQuery object", function() {
351         expect(2);
352         var coords = { top:  1, left:  1 };
353         equals( jQuery("#absolute-1").offset(coords).selector, "#absolute-1", "offset(coords) returns jQuery object" );
354         equals( jQuery("#non-existent").offset(coords).selector, "#non-existent", "offset(coords) with empty jQuery set returns jQuery object" );
355 });
356
357 test("offsetParent", function(){
358         expect(11);
359
360         var body = jQuery("body").offsetParent();
361         equals( body.length, 1, "Only one offsetParent found." );
362         equals( body[0], document.body, "The body is its own offsetParent." );
363
364         var header = jQuery("#qunit-header").offsetParent();
365         equals( header.length, 1, "Only one offsetParent found." );
366         equals( header[0], document.body, "The body is the offsetParent." );
367
368         var div = jQuery("#nothiddendivchild").offsetParent();
369         equals( div.length, 1, "Only one offsetParent found." );
370         equals( div[0], document.body, "The body is the offsetParent." );
371
372         jQuery("#nothiddendiv").css("position", "relative");
373
374         div = jQuery("#nothiddendivchild").offsetParent();
375         equals( div.length, 1, "Only one offsetParent found." );
376         equals( div[0], jQuery("#nothiddendiv")[0], "The div is the offsetParent." );
377
378         div = jQuery("body, #nothiddendivchild").offsetParent();
379         equals( div.length, 2, "Two offsetParent found." );
380         equals( div[0], document.body, "The body is the offsetParent." );
381         equals( div[1], jQuery("#nothiddendiv")[0], "The div is the offsetParent." );
382 });
383
384 function testoffset(name, fn) {
385         
386         test(name, function() {
387                 // pause execution for now
388                 stop();
389                 
390                 // load fixture in iframe
391                 var iframe = loadFixture(),
392                         win = iframe.contentWindow,
393                         interval = setInterval( function() {
394                                 if ( win && win.jQuery && win.jQuery.isReady ) {
395                                         clearInterval( interval );
396                                         // continue
397                                         start();
398                                         // call actual tests passing the correct jQuery isntance to use
399                                         fn.call( this, win.jQuery, win );
400                                         document.body.removeChild( iframe );
401                                         iframe = null;
402                                 }
403                         }, 15 );
404         });
405         
406         function loadFixture() {
407                 var src = './data/offset/' + name + '.html?' + parseInt( Math.random()*1000 ),
408                         iframe = jQuery('<iframe />').css({
409                                 width: 500, height: 500, position: 'absolute', top: -600, left: -600, visiblity: 'hidden'
410                         }).appendTo('body')[0];
411                 iframe.contentWindow.location = src;
412                 return iframe;
413         }
414 }