toolkit/content/tests/widgets/test_popupanchor.xul

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:bdacee255caa
1 <?xml version="1.0"?>
2 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
3 <?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
4
5 <window title="Popup Anchor Tests"
6 xmlns:html="http://www.w3.org/1999/xhtml"
7 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
8
9 <panel id="testPanel"
10 type="arrow"
11 animate="false"
12 noautohide="true">
13 </panel>
14
15 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
16
17 <script>
18 <![CDATA[
19 var anchor, panel, arrow;
20
21 function is_close(got, exp, msg) {
22 // on some platforms we see differences of a fraction of a pixel - so
23 // allow any difference of < 1 pixels as being OK.
24 ok(Math.abs(got - exp) < 1, msg + ": " + got + " should be equal(-ish) to " + exp);
25 }
26
27 function isArrowPositionedOn(side, offset) {
28 var arrowRect = arrow.getBoundingClientRect();
29 var arrowMidX = (arrowRect.left + arrowRect.right) / 2;
30 var arrowMidY = (arrowRect.top + arrowRect.bottom) / 2;
31 var panelRect = panel.getBoundingClientRect();
32 var panelMidX = (panelRect.left + panelRect.right) / 2;
33 var panelMidY = (panelRect.top + panelRect.bottom) / 2;
34 // First check the "flip" of the panel is correct. If we are expecting the
35 // arrow to be pointing to the left side of the anchor, the arrow must
36 // also be on the left side of the panel (and vice-versa)
37 // XXX - on OSX, the arrow seems to always be exactly in the center, hence
38 // the 'equals' sign in the "<=" and ">=" comparisons. NFI why though...
39 switch (side) {
40 case "left":
41 ok(arrowMidX <= panelMidX, "arrow should be on the left of the panel");
42 break;
43 case "right":
44 ok(arrowMidX >= panelMidX, "arrow should be on the right of the panel");
45 break;
46 case "top":
47 ok(arrowMidY <= panelMidY, "arrow should be on the top of the panel");
48 break;
49 case "bottom":
50 ok(arrowMidY >= panelMidY, "arrow should be on the bottom of the panel");
51 break;
52 default:
53 ok(false, "invalid position " + where);
54 break;
55 }
56 // Now check the arrow really is pointing where we expect. The middle of
57 // the arrow should be pointing exactly to the left (or right) side of the
58 // anchor rect, +- any offsets.
59 if (offset === null) // special case - explicit 'null' means 'don't check offset'
60 return;
61 offset = offset || 0; // no param means no offset expected.
62 var anchorRect = anchor.getBoundingClientRect();
63 var anchorPos = anchorRect[side];
64 switch (side) {
65 case "left":
66 case "right":
67 is_close(arrowMidX - anchorPos, offset, "arrow should be " + offset + "px from " + side + " side of anchor");
68 is_close(panelRect.top, anchorRect.bottom, "top of panel should be at bottom of anchor");
69 break;
70 case "top":
71 case "bottom":
72 is_close(arrowMidY - anchorPos, offset, "arrow should be " + offset + "px from " + side + " side of anchor");
73 is_close(panelRect.right, anchorRect.left, "right of panel should be left of anchor");
74 break;
75 default:
76 ok(false, "unknown side " + side);
77 break;
78 }
79 }
80
81 function openSlidingPopup(position, callback) {
82 panel.setAttribute("flip", "slide");
83 _openPopup(position, callback);
84 }
85
86 function openPopup(position, callback) {
87 panel.setAttribute("flip", "both");
88 _openPopup(position, callback);
89 }
90
91 function _openPopup(position, callback) {
92 // this is very ugly: the panel CSS sets the arrow's list-style-image based
93 // on the 'side' attribute. If the setting of the 'side' attribute causes
94 // the image to change, we may get the popupshown event before the new
95 // image has loaded - which causes the size of the arrow to be incorrect
96 // for a brief moment - right when we are measuring it!
97 // So we work around this in 2 steps:
98 // * Force the 'side' attribute to a value which causes the CSS to not
99 // specify an image - then when the popup gets shown, the correct image
100 // is set, causing a load() event on the image element.
101 // * Listen to *both* popupshown and the image load event. When both have
102 // fired (the order is indeterminate) we start the test.
103 panel.setAttribute("side", "noside");
104 var numEvents = 0;
105 function onEvent() {
106 if (++numEvents == 2) // after both panel 'popupshown' and image 'load'
107 callback();
108 };
109 panel.addEventListener("popupshown", function popupshown() {
110 panel.removeEventListener("popupshown", popupshown);
111 onEvent();
112 });
113 arrow.addEventListener("load", function imageload() {
114 arrow.removeEventListener("load", imageload);
115 onEvent();
116 });
117 panel.openPopup(anchor, position);
118 }
119
120 var tests = [
121 // A panel with the anchor after_end - the anchor should not move on resize
122 ['simpleResizeHorizontal', 'middle', function(next) {
123 openPopup("after_end", function() {
124 isArrowPositionedOn("right");
125 var origPanelRect = panel.getBoundingClientRect();
126 panel.sizeTo(100, 100);
127 isArrowPositionedOn("right"); // should not have flipped, so still "right"
128 panel.sizeTo(origPanelRect.width, origPanelRect.height);
129 isArrowPositionedOn("right"); // should not have flipped, so still "right"
130 next();
131 });
132 }],
133
134 ['simpleResizeVertical', 'middle', function(next) {
135 openPopup("start_after", function() {
136 isArrowPositionedOn("bottom");
137 var origPanelRect = panel.getBoundingClientRect();
138 panel.sizeTo(100, 100);
139 isArrowPositionedOn("bottom"); // should not have flipped
140 panel.sizeTo(origPanelRect.width, origPanelRect.height);
141 isArrowPositionedOn("bottom"); // should not have flipped
142 next();
143 });
144 }],
145
146 ['flippingResizeHorizontal', 'middle', function(next) {
147 openPopup("after_end", function() {
148 isArrowPositionedOn("right");
149 panel.sizeTo(anchor.getBoundingClientRect().left + 50, 50);
150 isArrowPositionedOn("left"); // check it flipped and has zero offset.
151 next();
152 });
153 }],
154
155 ['flippingResizeVertical', 'middle', function(next) {
156 openPopup("start_after", function() {
157 isArrowPositionedOn("bottom");
158 panel.sizeTo(50, anchor.getBoundingClientRect().top + 50);
159 isArrowPositionedOn("top"); // check it flipped and has zero offset.
160 next();
161 });
162 }],
163
164 ['simpleMoveToAnchorHorizontal', 'middle', function(next) {
165 openPopup("after_end", function() {
166 isArrowPositionedOn("right");
167 panel.moveToAnchor(anchor, "after_end", 20, 0);
168 // the anchor and the panel should have moved 20px right without flipping.
169 isArrowPositionedOn("right", 20);
170 panel.moveToAnchor(anchor, "after_end", -20, 0);
171 // the anchor and the panel should have moved 20px left without flipping.
172 isArrowPositionedOn("right", -20);
173 next();
174 });
175 }],
176
177 ['simpleMoveToAnchorVertical', 'middle', function(next) {
178 openPopup("start_after", function() {
179 isArrowPositionedOn("bottom");
180 panel.moveToAnchor(anchor, "start_after", 0, 20);
181 // the anchor and the panel should have moved 20px down without flipping.
182 isArrowPositionedOn("bottom", 20);
183 panel.moveToAnchor(anchor, "start_after", 0, -20);
184 // the anchor and the panel should have moved 20px up without flipping.
185 isArrowPositionedOn("bottom", -20);
186 next();
187 });
188 }],
189
190 // Do a moveToAnchor that causes the panel to flip horizontally
191 ['flippingMoveToAnchorHorizontal', 'middle', function(next) {
192 var anchorRight = anchor.getBoundingClientRect().right;
193 // Size the panel such that it only just fits from the left-hand side of
194 // the window to the right of the anchor - thus, it will fit when
195 // anchored to the right-hand side of the anchor.
196 panel.sizeTo(anchorRight - 10, 100);
197 openPopup("after_end", function() {
198 isArrowPositionedOn("right");
199 // Ask for it to be anchored 1/2 way between the left edge of the window
200 // and the anchor right - it can't fit with the panel on the left/arrow
201 // on the right, so it must flip (arrow on the left, panel on the right)
202 var offset = Math.floor(-anchorRight / 2);
203 panel.moveToAnchor(anchor, "after_end", offset, 0);
204 isArrowPositionedOn("left", offset); // should have flipped and have the offset.
205 // resize back to original and move to a zero offset - it should flip back.
206 panel.sizeTo(anchorRight - 10, 100);
207 panel.moveToAnchor(anchor, "after_end", 0, 0);
208 isArrowPositionedOn("right"); // should have flipped back and no offset
209 next();
210 });
211 }],
212
213 // Do a moveToAnchor that causes the panel to flip vertically
214 ['flippingMoveToAnchorVertical', 'middle', function(next) {
215 var anchorBottom = anchor.getBoundingClientRect().bottom;
216 // See comments above in flippingMoveToAnchorHorizontal, but read
217 // "top/bottom" instead of "left/right"
218 panel.sizeTo(100, anchorBottom - 10);
219 openPopup("start_after", function() {
220 isArrowPositionedOn("bottom");
221 var offset = Math.floor(-anchorBottom / 2);
222 panel.moveToAnchor(anchor, "start_after", 0, offset);
223 isArrowPositionedOn("top", offset);
224 panel.sizeTo(100, anchorBottom - 10);
225 panel.moveToAnchor(anchor, "start_after", 0, 0);
226 isArrowPositionedOn("bottom");
227 next();
228 });
229 }],
230
231 ['veryWidePanel-after_end', 'middle', function(next) {
232 openSlidingPopup("after_end", function() {
233 var origArrowRect = arrow.getBoundingClientRect();
234 // Now move it such that the arrow can't be at either end of the panel but
235 // instead somewhere in the middle as that is the only way things fit,
236 // meaning the arrow should "slide" down the panel.
237 panel.sizeTo(window.innerWidth - 10, 60);
238 is(panel.getBoundingClientRect().width, window.innerWidth - 10, "width is what we requested.")
239 // the arrow should not have moved.
240 var curArrowRect = arrow.getBoundingClientRect();
241 is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
242 is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
243 next();
244 });
245 }],
246
247 ['veryWidePanel-before_start', 'middle', function(next) {
248 openSlidingPopup("before_start", function() {
249 var origArrowRect = arrow.getBoundingClientRect();
250 // Now size it such that the arrow can't be at either end of the panel but
251 // instead somewhere in the middle as that is the only way things fit.
252 panel.sizeTo(window.innerWidth - 10, 60);
253 is(panel.getBoundingClientRect().width, window.innerWidth - 10, "width is what we requested")
254 // the arrow should not have moved.
255 var curArrowRect = arrow.getBoundingClientRect();
256 is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
257 is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
258 next();
259 });
260 }],
261
262 ['veryTallPanel-start_after', 'middle', function(next) {
263 openSlidingPopup("start_after", function() {
264 var origArrowRect = arrow.getBoundingClientRect();
265 // Now move it such that the arrow can't be at either end of the panel but
266 // instead somewhere in the middle as that is the only way things fit,
267 // meaning the arrow should "slide" down the panel.
268 panel.sizeTo(100, window.innerHeight - 10);
269 is(panel.getBoundingClientRect().height, window.innerHeight - 10, "height is what we requested.")
270 // the arrow should not have moved.
271 var curArrowRect = arrow.getBoundingClientRect();
272 is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
273 is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
274 next();
275 });
276 }],
277
278 ['veryTallPanel-start_before', 'middle', function(next) {
279 openSlidingPopup("start_before", function() {
280 var origArrowRect = arrow.getBoundingClientRect();
281 // Now size it such that the arrow can't be at either end of the panel but
282 // instead somewhere in the middle as that is the only way things fit.
283 panel.sizeTo(100, window.innerHeight - 10);
284 is(panel.getBoundingClientRect().height, window.innerHeight - 10, "height is what we requested")
285 // the arrow should not have moved.
286 var curArrowRect = arrow.getBoundingClientRect();
287 is_close(curArrowRect.left, origArrowRect.left, "arrow should not have moved");
288 is_close(curArrowRect.top, origArrowRect.top, "arrow should not have moved up or down");
289 next();
290 });
291 }],
292
293 // Tests against the anchor at the right-hand side of the window
294 ['afterend', 'right', function(next) {
295 openPopup("after_end", function() {
296 // when we request too far to the right/bottom, the panel gets shrunk
297 // and moved. The amount it is shrunk by is how far it is moved.
298 var panelRect = panel.getBoundingClientRect();
299 // panel was requested 100px wide - calc offset based on actual width.
300 var offset = panelRect.width - 100;
301 isArrowPositionedOn("right", offset);
302 next();
303 });
304 }],
305
306 ['after_start', 'right', function(next) {
307 openPopup("after_start", function() {
308 // See above - we are still too far to the right, but the anchor is
309 // on the other side.
310 var panelRect = panel.getBoundingClientRect();
311 var offset = panelRect.width - 100;
312 isArrowPositionedOn("right", offset);
313 next();
314 });
315 }],
316
317 // Tests against the anchor at the left-hand side of the window
318 ['after_start', 'left', function(next) {
319 openPopup("after_start", function() {
320 var panelRect = panel.getBoundingClientRect();
321 is(panelRect.left, 0, "panel remains within the screen");
322 // not sure how to determine the offset here, so given we have checked
323 // the panel is as left as possible while still being inside the window,
324 // we just don't check the offset.
325 isArrowPositionedOn("left", null);
326 next();
327 });
328 }],
329 ]
330
331 function runTests() {
332 var testIter = Iterator(tests);
333 function runNextTest() {
334 let name, anchorPos, test;
335 try {
336 let index;
337 [index, [name, anchorPos, test]] = testIter.next();
338 } catch (err if err instanceof StopIteration) {
339 // out of tests
340 panel.hidePopup();
341 SimpleTest.finish();
342 return;
343 }
344 SimpleTest.info("sub-test " + anchorPos + "." + name + " starting");
345 // first arrange for the anchor to be where the test requires it.
346 panel.hidePopup();
347 panel.sizeTo(100, 50);
348 // hide all the anchors here, then later we make one of them visible.
349 document.getElementById("anchor-left-wrapper").style.display = "none";
350 document.getElementById("anchor-middle-wrapper").style.display = "none";
351 document.getElementById("anchor-right-wrapper").style.display = "none";
352 switch(anchorPos) {
353 case 'middle':
354 anchor = document.getElementById("anchor-middle");
355 document.getElementById("anchor-middle-wrapper").style.display = "block";
356 break;
357 case 'left':
358 anchor = document.getElementById("anchor-left");
359 document.getElementById("anchor-left-wrapper").style.display = "block";
360 break;
361 case 'right':
362 anchor = document.getElementById("anchor-right");
363 document.getElementById("anchor-right-wrapper").style.display = "block";
364 break;
365 default:
366 SimpleTest.ok(false, "Bad anchorPos: " + anchorPos);
367 runNextTest();
368 return;
369 }
370 try {
371 test(runNextTest);
372 } catch (ex) {
373 SimpleTest.ok(false, "sub-test " + anchorPos + "." + name + " failed: " + ex.toString() + "\n" + ex.stack);
374 runNextTest();
375 }
376 }
377 runNextTest();
378 }
379
380 SimpleTest.waitForExplicitFinish();
381
382 addEventListener("load", function() {
383 // anchor is set by the test runner above
384 panel = document.getElementById("testPanel");
385 arrow = SpecialPowers.wrap(document).getAnonymousElementByAttribute(panel, "anonid", "arrow");
386 // Cancel the arrow panel slide-in transition (bug 767133) so the size and
387 // position are "stable" enough to test without jumping through hoops...
388 arrow.style.transition = "none";
389 runTests();
390 });
391
392 ]]>
393 </script>
394
395 <body xmlns="http://www.w3.org/1999/xhtml">
396 <!-- Our tests assume at least 100px around the anchor on all sides, else the
397 panel may flip when we don't expect it to
398 -->
399 <div id="anchor-middle-wrapper" style="margin: 100px 100px 100px 100px;">
400 <p>The anchor --&gt; <span id="anchor-middle">v</span> &lt;--</p>
401 </div>
402 <div id="anchor-left-wrapper" style="text-align: left; display: none;">
403 <p><span id="anchor-left">v</span> &lt;-- The anchor;</p>
404 </div>
405 <div id="anchor-right-wrapper" style="text-align: right; display: none;">
406 <p>The anchor --&gt; <span id="anchor-right">v</span></p>
407 </div>
408 </body>
409
410 </window>

mercurial