diff --git a/mobile/chrome/tests/Makefile.in b/mobile/chrome/tests/Makefile.in index 382df88a0c77..5af0d4c74463 100644 --- a/mobile/chrome/tests/Makefile.in +++ b/mobile/chrome/tests/Makefile.in @@ -68,6 +68,8 @@ _BROWSER_FILES = \ browser_select.js \ browser_sessionstore.js \ browser_tabs.js \ + browser_tapping.js \ + browser_tap_content.html \ browser_viewport_00.html \ browser_viewport_01.html \ browser_viewport_02.html \ diff --git a/mobile/chrome/tests/browser_tap_content.html b/mobile/chrome/tests/browser_tap_content.html new file mode 100644 index 000000000000..95fe8d3c5063 --- /dev/null +++ b/mobile/chrome/tests/browser_tap_content.html @@ -0,0 +1,22 @@ + +Browser Tap Page 01 + +

Here are some images and links. We use them to test context menu (long taps)

+ +
+
A plain link
+ A blank page - nothing interesting +
+ +
+
A plain image
+ +
+ +
+
A nested image inside a link
+ +
+ + + diff --git a/mobile/chrome/tests/browser_tapping.js b/mobile/chrome/tests/browser_tapping.js new file mode 100644 index 000000000000..1432bc3784bc --- /dev/null +++ b/mobile/chrome/tests/browser_tapping.js @@ -0,0 +1,276 @@ +/* + * Testing the tapping interactions: + * single tap, double tap & long tap + */ + +let testURL = "chrome://mochikit/content/browser/mobile/chrome/browser_tap_content.html"; + +let gTests = []; +let gCurrentTest = null; +let gCurrentTab; + +let gEvents = []; +function dumpEvents(aEvent) { + gEvents.push(aEvent.type); +} + +function clearEvents() { + gEvents = []; +} + +function checkEvents(aEvents) { + if (aEvents.length != gEvents.length) { + dump("---- event check: failed length (" + aEvents.length + " != " + gEvents.length + ")\n"); + dump("---- expected: [" + aEvents.join(",") + "] actual: [" + gEvents.join(",") + "]\n"); + return false; + } + + for (let i=0; i 0) { + gCurrentTest = gTests.shift(); + info(gCurrentTest.desc); + gCurrentTest.run(); + } + else { + window.removeEventListener("TapSingle", dumpEvents, true); + window.removeEventListener("TapDouble", dumpEvents, true); + window.removeEventListener("TapLong", dumpEvents, true); + + Browser.closeTab(gCurrentTab); + + finish(); + } +} + +//------------------------------------------------------------------------------ +// Case: Test the double tap behavior +gTests.push({ + desc: "Test the double tap behavior", + + run: function() { + let browser = gCurrentTab.browser; + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + + // Should fire "TapSingle" + // XXX not working? WTF? + info("Test good single tap"); + clearEvents(); + EventUtils.synthesizeMouse(browser, width / 2, height / 2, {}); + todo(checkEvents(["TapSingle"]), "Fired a good single tap"); + clearEvents(); + + setTimeout(function() { gCurrentTest.doubleTapTest(); }, 500); + }, + + doubleTapTest: function() { + let browser = gCurrentTab.browser; + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + + // Should fire "TapDouble" + info("Test good double tap"); + clearEvents(); + EventUtils.synthesizeMouse(browser, width / 2, height / 2, {}); + EventUtils.synthesizeMouse(browser, width / 2, height / 2, {}); + ok(checkEvents(["TapDouble"]), "Fired a good double tap"); + clearEvents(); + + setTimeout(function() { gCurrentTest.doubleTapFailTest(); }, 500); + }, + + doubleTapFailTest: function() { + let browser = gCurrentTab.browser; + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + + // Should fire "TapSingle", "TapSingle" + info("Test two single taps in different locations"); + clearEvents(); + EventUtils.synthesizeMouse(browser, width / 4, height / 4, {}); + EventUtils.synthesizeMouse(browser, width * 3 / 4, height * 3 / 4, {}); + ok(checkEvents(["TapSingle", "TapSingle"]), "Fired two single taps in different places, not a double tap"); + clearEvents(); + + setTimeout(function() { gCurrentTest.tapPanTest(); }, 500); + }, + + tapPanTest: function() { + let browser = gCurrentTab.browser; + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + + info("Test a pan - non-tap event"); + clearEvents(); + EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" }); + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" }); + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" }); + ok(checkEvents([]), "Fired a pan which should be seen as a non event"); + clearEvents(); + + setTimeout(function() { gCurrentTest.longTapFailTest(); }, 500); + }, + + longTapFailTest: function() { + let browser = gCurrentTab.browser; + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + + info("Test a long pan - non-tap event"); + clearEvents(); + EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" }); + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" }); + setTimeout(function() { + EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" }); + ok(checkEvents([]), "Fired a pan + delay which should be seen as a non-event"); + clearEvents(); + + gCurrentTest.longTapPassTest(); + }, 500); + }, + + longTapPassTest: function() { + let browser = gCurrentTab.browser; + let width = browser.getBoundingClientRect().width; + let height = browser.getBoundingClientRect().height; + + info("Test a good long pan"); + clearEvents(); + EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" }); + setTimeout(function() { + EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mouseup" }); + ok(checkEvents(["TapLong"]), "Fired a good long tap"); + clearEvents(); + + gCurrentTest.contextPlainLinkTest(); + }, 500); + }, + + contextPlainLinkTest: function() { + let browser = gCurrentTab.browser; + browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages); + + let link = browser.contentDocument.getElementById("link-single"); + let linkRect = link.getBoundingClientRect(); + + clearContextTypes(); + EventUtils.synthesizeMouseForContent(link, 1, 1, { type: "mousedown" }, window); + setTimeout(function() { + EventUtils.synthesizeMouseForContent(link, 1, 1, { type: "mouseup" }, window); + ok(checkContextTypes(["link","link-saveable"]), "Plain link context types"); + clearContextTypes(); + + gCurrentTest.contextPlainImageTest(); + }, 500); + }, + + contextPlainImageTest: function() { + let browser = gCurrentTab.browser; + browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages); + + let img = browser.contentDocument.getElementById("img-single"); + let imgRect = img.getBoundingClientRect(); + + clearContextTypes(); + EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mousedown" }, window); + setTimeout(function() { + EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mouseup" }, window); + ok(checkContextTypes(["image","image-shareable","image-loaded"]), "Plain image context types"); + clearContextTypes(); + + gCurrentTest.contextNestedImageTest(); + }, 500); + }, + + contextNestedImageTest: function() { + let browser = gCurrentTab.browser; + browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages); + + let img = browser.contentDocument.getElementById("img-nested"); + let imgRect = img.getBoundingClientRect(); + + clearContextTypes(); + EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mousedown" }, window); + setTimeout(function() { + EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mouseup" }, window); + ok(checkContextTypes(["link","link-saveable","image","image-shareable","image-loaded"]), "Nested image context types"); + clearContextTypes(); + + gCurrentTest.lastTest(); + }, 500); + }, + + lastTest: function() { + gCurrentTab.browser.messageManager.removeMessageListener("Browser:ContextMenu", dumpMessages); + + runNextTest(); + } +}); + diff --git a/mobile/chrome/tests/head.js b/mobile/chrome/tests/head.js index c16f12c4632d..18d9505aab43 100644 --- a/mobile/chrome/tests/head.js +++ b/mobile/chrome/tests/head.js @@ -1,6 +1,9 @@ /*============================================================================= Common Helpers functions =============================================================================*/ + +// Wait for a condition and call a supplied callback if condition is met within +// alloted time. If condition is not met, cause a hard failure, stopping the test. function waitFor(callback, test, timeout) { if (test()) { callback(); @@ -13,6 +16,21 @@ function waitFor(callback, test, timeout) { setTimeout(waitFor, 50, callback, test, timeout); }; +// Wait for a condition and call a supplied callback if condition is met within +// alloted time. If condition is not met, continue anyway. Use this helper if the +// callback will test for the outcome, but not stop the entire test. +function waitForAndContinue(callback, test, timeout) { + if (test()) { + callback(); + return; + } + + timeout = timeout || Date.now(); + if (Date.now() - timeout > 1000) + callback(); + setTimeout(waitFor, 50, callback, test, timeout); +}; + function makeURI(spec) { return Services.io.newURI(spec, null, null); };