Bug 1484636 - Revive and enable all the mozmill tests in calendar/test/mozmill; r=Fallen
authorGeoff Lankow <geoff@darktrojan.net>
Fri, 14 Sep 2018 11:40:11 +1200
changeset 33149 63459259f1e91e62aeaaf674f90dc122a57bcad1
parent 33148 760fa56f28063ce0a99d8b2767ff78a9933c52be
child 33150 86b75ada0d6a76d60996891036f246e05efabfa6
push id387
push userclokep@gmail.com
push dateMon, 10 Dec 2018 21:30:47 +0000
reviewersFallen
bugs1484636
Bug 1484636 - Revive and enable all the mozmill tests in calendar/test/mozmill; r=Fallen
calendar/lightning/content/lightning-item-iframe.xul
calendar/test/mozmill/.eslintrc.js
calendar/test/mozmill/eventDialog/testEventDialog.js
calendar/test/mozmill/eventDialog/testEventDialogModificationPrompt.js
calendar/test/mozmill/eventDialog/testUTF8.js
calendar/test/mozmill/mozmilltests.list
calendar/test/mozmill/recurrenceRotated/testAnnualRecurrence.js
calendar/test/mozmill/recurrenceRotated/testBiweeklyRecurrence.js
calendar/test/mozmill/recurrenceRotated/testDailyRecurrence.js
calendar/test/mozmill/recurrenceRotated/testLastDayOfMonthRecurrence.js
calendar/test/mozmill/recurrenceRotated/testWeeklyNRecurrence.js
calendar/test/mozmill/recurrenceRotated/testWeeklyUntilRecurrence.js
calendar/test/mozmill/recurrenceRotated/testWeeklyWithExceptionRecurrence.js
calendar/test/mozmill/shared-modules/test-calendar-utils.js
calendar/test/mozmill/shared-modules/test-timezone-utils.js
calendar/test/mozmill/testAlarmDefaultValue.js
calendar/test/mozmill/testTimezones.js
calendar/test/mozmill/timezoneTests/test1.js
calendar/test/mozmill/timezoneTests/test10.js
calendar/test/mozmill/timezoneTests/test2.js
calendar/test/mozmill/timezoneTests/test3.js
calendar/test/mozmill/timezoneTests/test4.js
calendar/test/mozmill/timezoneTests/test5.js
calendar/test/mozmill/timezoneTests/test6.js
calendar/test/mozmill/timezoneTests/test7.js
calendar/test/mozmill/timezoneTests/test8.js
calendar/test/mozmill/timezoneTests/test9.js
calendar/test/mozmill/views/testDayView.js
calendar/test/mozmill/views/testMonthView.js
calendar/test/mozmill/views/testMultiweekView.js
calendar/test/mozmill/views/testTaskView.js
calendar/test/mozmill/views/testWeekView.js
mail/test/mozmill/runtest.py
mail/test/resources/mozmill/mozmill/extension/content/modules/controller.js
mail/test/resources/mozmill/mozmill/extension/content/modules/frame.js
--- a/calendar/lightning/content/lightning-item-iframe.xul
+++ b/calendar/lightning/content/lightning-item-iframe.xul
@@ -580,17 +580,17 @@
                    flex="1">
           <tabpanel id="event-grid-tabpanel-description">
             <textbox id="item-description"
                      disable-on-readonly="true"
                      flex="1"
                      multiline="true"
                      rows="12"/>
           </tabpanel>
-          <tabpanel id="event-grid-tabpanel-attachements">
+          <tabpanel id="event-grid-tabpanel-attachments">
             <vbox flex="1">
               <richlistbox id="attachment-link"
                            seltype="single"
                            context="attachment-popup"
                            rows="3"
                            flex="1"
                            disable-on-readonly="true"
                            onkeypress="attachmentLinkKeyPress(event)"
--- a/calendar/test/mozmill/.eslintrc.js
+++ b/calendar/test/mozmill/.eslintrc.js
@@ -3,17 +3,27 @@
 module.exports = {
     globals: {
         elementslib: true,
         controller: true,
         mozmill: true,
         utils: true,
         require: true,
         exports: true,
-        module: true
+        module: true,
+        registeredFunctions: true,
+        collector: true,
+        persisted: true,
+
+        lookup: true,
+        eid: true,
+        xpath: true,
+        sleep: true,
+        getEventBoxPath: true,
+        lookupEventBox: true,
     },
     rules: {
         // Allow mozmill test methods to be used without warning
         "no-unused-vars": [2, {
             vars: "all",
             args: "none",
             varsIgnorePattern: "(MODULE_NAME|MODULE_REQUIRES|RELATIVE_ROOT|setupModule|installInto|teardownTest|^test[A-Z_].*)"
         }]
--- a/calendar/test/mozmill/eventDialog/testEventDialog.js
+++ b/calendar/test/mozmill/eventDialog/testEventDialog.js
@@ -1,63 +1,61 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils", "window-helpers"];
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { cal } = ChromeUtils.import("resource://calendar/modules/calUtils.jsm", null);
+
 var plan_for_modal_dialog, wait_for_modal_dialog;
 var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var handleAddingAttachment, acceptSendingNotificationMail, handleOccurrencePrompt;
+var handleAddingAttachment, handleOccurrencePrompt;
+var goToDate, setData;
 var CALENDARNAME, TIMEOUT_MODAL_DIALOG;
 
-var utils = require("../shared-modules/utils");
-
 var eventTitle = "Event";
 var eventLocation = "Location";
 var eventDescription = "Event Description";
 var eventAttendee = "foo@bar.com";
 var eventUrl = "http://mozilla.org";
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({ plan_for_modal_dialog, wait_for_modal_dialog } =
         collector.getModule("window-helpers"));
     ({
         helpersForController,
         invokeEventDialog,
         createCalendar,
         deleteCalendars,
         handleAddingAttachment,
-        acceptSendingNotificationMail,
         handleOccurrencePrompt,
+        goToDate,
+        setData,
         CALENDARNAME,
         TIMEOUT_MODAL_DIALOG
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
 function testEventDialog() {
+    let dateFormatter = cal.getDateFormatter();
     // paths
     let monthView = `
         /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
         id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
         id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
         id("month-view")
     `;
-    let miniMonth = `
-        /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
-        id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
-        id("ltnSidebar")/id("minimonth-pane")/{"align":"center"}/
-        id("calMinimonthBox")/id("calMinimonth")/
-    `;
     let eventDialog = `
         /id("calendar-event-dialog-inner")/id("event-grid")/id("event-grid-rows")/
     `;
 
     let eventBox = `
         ${monthView}/anon({"anonid":"mainbox"})/anon({"anonid":"monthgrid"})/
         anon({"anonid":"monthgridrows"})/[rowNumber]/[columnNumber]/
         {"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}/
@@ -65,132 +63,102 @@ function testEventDialog() {
         {"class":"calendar-event-selection"}/anon({"anonid":"eventbox"})/
         {"class":"calendar-event-details"}
     `;
 
     // open month view
     controller.click(eid("calendar-tab-button"));
     controller.waitThenClick(eid("calendar-month-view-button"));
 
-    // pick year
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"yearcell"})
-    `));
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"years-popup"})/
-        [0]/{"value":"2009"}
-    `));
-
-    // pick month
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"monthheader"})
-    `));
-
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"months-popup"})/
-        [0]/{"index":"0"}
-    `));
-
-    // pick day
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-calendar"})/[1]/{"value":"1"}
-    `));
+    goToDate(controller, 2009, 1, 1);
     sleep();
 
     // create new event
     let now = new Date();
     let hour = now.getHours();
     let startHour = hour == 23 ? hour : (hour + 1) % 24;
-    let ampm = "";
-    // check that the start time is correct
-    // next full hour except last hour hour of the day
-    if (now.toLocaleTimeString().match(/AM|PM/)) {
-        ampm = (hour >= 12 ? " PM" : " AM");
-        startHour = startHour % 12;
-        if (startHour == 0) {
-            startHour = 12;
-        }
-    }
-    let startTime = startHour + ":00" + ampm;
-    let endTime = ((startHour + 1) % 24) + ":00" + ampm;
+
+    let nextHour = cal.dtz.now();
+    nextHour.resetTo(2009, 0, 1, startHour, 0, 0, cal.dtz.floating);
+    let startTime = dateFormatter.formatTime(nextHour);
+    nextHour.resetTo(2009, 0, 1, (startHour + 1) % 24, 0, 0, cal.dtz.floating);
+    let endTime = dateFormatter.formatTime(nextHour);
 
     controller.mainMenu.click("#ltnNewEvent");
     invokeEventDialog(controller, null, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
+        let { lookup: iframeLookup, eid: iframeId } = helpersForController(iframe);
 
-        let startTimeInput = eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
+        let timeInput = `
             anon({"anonid":"hbox"})/anon({"anonid":"time-picker"})/
-            anon({"class":"timepicker-box-class"})/id("timepicker-text")/
-            anon({"class":"menulist-editable-box moz-input-box"})/
+            anon({"class":"timepicker-box-class"})/
+            anon({"class":"timepicker-text-class"})/anon({"flex":"1"})/
             anon({"anonid":"input"})
+        `;
+        let startTimeInput = iframeLookup(`
+            /id("calendar-event-dialog-inner")/id("event-grid")/id("event-grid-rows")/
+            id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("event-starttime")/${timeInput}
         `);
+
         event.waitForElement(startTimeInput);
         event.assertValue(startTimeInput, startTime);
 
         // check selected calendar
-        event.assertNode(eventlookup(`
-            ${eventDialog}/id("event-grid-category-color-row")/
-            id("event-grid-category-box")/id("item-calendar")/[0]/
-            {"selected":"true","label":"${CALENDARNAME}"}
-        `));
+        event.assertValue(iframeId("item-calendar"), CALENDARNAME);
 
         // fill in name, location, description
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), eventTitle);
-
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-location-row")/id("item-location")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), eventLocation);
-
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-description-row")/
-            id("item-description")/anon({"anonid":"moz-input-box"})/
-            anon({"anonid":"input"})
-        `), eventDescription);
-
-        // set category
-        let categories = utils.getProperty("chrome://calendar/locale/categories.properties", "categories2");
-        let category = categories.split(",")[4]; // pick 4th value in a comma-separated list
-        event.select(eventid("item-categories"), null, category);
-
-        // repeat daily
-        event.click(eventid("repeat-daily-menuitem"));
-
-        // add reminder
-        event.click(eventid("reminder-5minutes-menuitem"));
+        setData(event, iframe, {
+            title: eventTitle,
+            location: eventLocation,
+            description: eventDescription,
+            category: "Clients",
+            repeat: "daily"
+        });
+        event.click(iframeId("item-alarm"));
+        event.click(iframeId("reminder-5minutes-menuitem"));
+        event.waitFor(() => iframeId("item-alarm").getNode().label == "5 minutes before");
+        iframeId("item-alarm-menupopup").getNode().hidePopup();
 
         // add an attendee and verify added
+        event.click(iframeId("event-grid-tab-attendees"));
+
         plan_for_modal_dialog("Calendar:EventDialog:Attendees", handleAttendees);
-        event.click(eventid("button-attendees"));
+        event.click(eventid("options-attendees-menuitem"));
         wait_for_modal_dialog("Calendar:EventDialog:Recurrence", TIMEOUT_MODAL_DIALOG);
-        event.assertValue(eventid("attendee-list"), eventAttendee);
+        event.assertNode(iframeLookup(`
+            ${eventDialog}/id("event-grid-tabbox")/id("event-grid-tabpanels")/
+            id("event-grid-tabpanel-attendees")/[0]/[1]/id("item-attendees-box")/
+            {"class":"item-attendees-row"}/{"class":"item-attendees-cell"}/
+            {"class":"item-attendees-cell-label","value":"${eventAttendee}"}
+        `));
+        event.click(iframeId("notify-attendees-checkbox"));
+        event.waitFor(() => !iframeId("notify-attendees-checkbox").getNode().checked);
 
         // make it private and verify label visible
-        event.click(eventid("button-privacy"));
+        let toolbarbutton = eventid("button-privacy");
+        let rect = toolbarbutton.getNode().getBoundingClientRect();
+        event.click(toolbarbutton, rect.width - 5, 5);
         event.click(eventid("event-privacy-private-menuitem"));
-        let label = eventid("status-privacy-private-box");
-        event.assertJS(event.window.getComputedStyle(label.getNode()).getPropertyValue("visibility") == "visible");
+        event.waitFor(() => !eventid("status-privacy-private-box").getNode().hasAttribute("collapsed"));
+        eventid("event-privacy-menupopup").getNode().hidePopup();
 
         // add attachment and verify added
+        event.click(iframeId("event-grid-tab-attachments"));
+
         handleAddingAttachment(event, eventUrl);
         event.click(eventid("button-url"));
-        event.assertNode(eventlookup(`
-            ${eventDialog}/id("event-grid-attachment-row")/
-            id("attachment-link")/{"label":"mozilla.org"}
+        wait_for_modal_dialog("commonDialog");
+        event.assertNode(iframeLookup(`
+            ${eventDialog}/id("event-grid-tabbox")/id("event-grid-tabpanels")/
+            id("event-grid-tabpanel-attachments")/{"flex":"1"}/
+            id("attachment-link")/[0]/{"value":"mozilla.org"}
         `));
 
         // save
-        acceptSendingNotificationMail(event);
         event.click(eventid("button-saveandclose"));
     });
 
     // catch and dismiss alarm
     controller.waitFor(() => mozmill.utils.getWindows("Calendar:AlarmWindow").length > 0);
     let alarm = new mozmill.controller.MozMillController(mozmill.utils.getWindows("Calendar:AlarmWindow")[0]);
     let { lookup: alarmlookup } = helpersForController(alarm);
 
@@ -215,18 +183,18 @@ function testEventDialog() {
     controller.assertNode(lookup(
         eventBox.replace("rowNumber", "0").replace("columnNumber", "6")
     ));
     checkIcon(eventBox, "0", "6");
     checkTooltip(monthView, "0", "6", "3", startTime, endTime);
 
     // 31st of January is Saturday so there's four more full rows to check
     let date = 4;
-    for (row = 1; row < 5; row++) {
-        for (col = 0; col < 7; col++) {
+    for (let row = 1; row < 5; row++) {
+        for (let col = 0; col < 7; col++) {
             controller.assertNode(lookup(
                 eventBox.replace("rowNumber", row).replace("columnNumber", col)
             ));
             checkIcon(eventBox, row, col);
             checkTooltip(monthView, row, col, date, startTime, endTime);
             date++;
         }
     }
@@ -244,18 +212,18 @@ function testEventDialog() {
     // verify all others still exist
     controller.assertNode(lookup(
         eventBox.replace("rowNumber", "0").replace("columnNumber", "4")
     ));
     controller.assertNode(lookup(
         eventBox.replace("rowNumber", "0").replace("columnNumber", "6")
     ));
 
-    for (row = 1; row < 5; row++) {
-        for (col = 0; col < 7; col++) {
+    for (let row = 1; row < 5; row++) {
+        for (let col = 0; col < 7; col++) {
             controller.assertNode(lookup(
                 eventBox.replace("rowNumber", row).replace("columnNumber", col)
             ));
         }
     }
 
     // delete series by deleting 3rd January and confirming to delete all
     controller.click(lookup(
@@ -270,75 +238,78 @@ function testEventDialog() {
     ));
     controller.assertNodeNotExist(lookup(
         eventBox.replace("rowNumber", "0").replace("columnNumber", "5")
     ));
     controller.assertNodeNotExist(lookup(
         eventBox.replace("rowNumber", "0").replace("columnNumber", "6")
     ));
 
-    for (row = 1; row < 5; row++) {
-        for (col = 0; col < 7; col++) {
+    for (let row = 1; row < 5; row++) {
+        for (let col = 0; col < 7; col++) {
             controller.assertNodeNotExist(lookup(
                 eventBox.replace("rowNumber", row).replace("columnNumber", col)
             ));
         }
     }
 }
 
 function handleAttendees(attendees) {
     let { lookup: attendeeslookup } = helpersForController(attendees);
 
     let input = attendeeslookup(`
-        /id("calendar-event-dialog-attendees-v2")/[6]/[0]/id("attendees-list")/
-        anon({"anonid":"listbox"})/[1]/[1]/anon({"anonid":"input"})/
-        {"anonid":"moz-input-box"}/anon({"anonid":"input"})
+        /id("calendar-event-dialog-attendees-v2")/{"flex":"1"}/
+        id("attendees-container")/id("attendees-list")/[1]/[2]/[0]
     `);
     attendees.waitForElement(input);
     attendees.type(input, eventAttendee);
     attendees.click(attendeeslookup(`
         /id("calendar-event-dialog-attendees-v2")/anon({"anonid":"buttons"})/
         {"dlgtype":"accept"}
     `));
 }
 
 function checkIcon(eventBox, row, col) {
     let icon = lookup((`
         ${eventBox}/anon({"anonid":"category-box-stack"})/
-        anon({"align": "right"})/anon({"class":"alarm-icons-box"})/
+        anon({"align": "center"})/anon({"class":"alarm-icons-box"})/
         anon({"class": "reminder-icon"})
     `).replace("rowNumber", row).replace("columnNumber", col));
 
     controller.assertJS(icon.getNode().getAttribute("value") == "DISPLAY");
 }
 
 function checkTooltip(monthView, row, col, date, startTime, endTime) {
-    controller.mouseOver(lookup((`
+    controller.mouseOver(lookup(`
         ${monthView}/anon({"anonid":"mainbox"})/anon({"anonid":"monthgrid"})/
-        anon({"anonid":"monthgridrows"})/[rowNumber]/[columnNumber]/
+        anon({"anonid":"monthgridrows"})/[${row}]/[${col}]/
         {"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}
-    `).replace("rowNumber", row).replace("columnNumber", col)));
+    `));
 
     // check title
     let eventName = lookup(`
         /id("messengerWindow")/id("calendar-popupset")/id("itemTooltip")/
         {"class":"tooltipBox"}/{"class":"tooltipHeaderGrid"}/[1]/[0]/[1]
     `);
-    controller.assertJS(eventName.getNode().textContent == eventTitle);
+    controller.waitFor(() => eventName.getNode().textContent == eventTitle);
 
     // check date and time
-    // date-time string contains strings formatted in operating system language
-    // so check numeric values only
     let dateTime = lookup(`
         /id("messengerWindow")/id("calendar-popupset")/id("itemTooltip")/
         {"class":"tooltipBox"}/{"class":"tooltipHeaderGrid"}/[1]/[2]/[1]
-    `).getNode().textContent;
+    `);
+
+    let formatter = new Services.intl.DateTimeFormat(undefined, { dateStyle: "full" });
+    let startDate = formatter.format(new Date(2009, 0, date));
 
-    controller.assertJS(
-        dateTime.includes(date) &&
-        dateTime.includes(startTime) &&
-        dateTime.includes(endTime)
-    );
+    controller.waitFor(() => {
+        let text = dateTime.getNode().textContent;
+        dump(`${text}\n`);
+        return text.includes(`${startDate} ${startTime} – `);
+    });
+
+    // This could be on the next day if it is 00:00.
+    controller.assertJS(() => dateTime.getNode().textContent.endsWith(endTime));
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/eventDialog/testEventDialogModificationPrompt.js
+++ b/calendar/test/mozmill/eventDialog/testEventDialogModificationPrompt.js
@@ -4,19 +4,16 @@
 
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
 var helpersForController, invokeEventDialog, createCalendar;
 var deleteCalendars, switchToView, goToDate, setData;
 var CALENDARNAME, EVENT_BOX, CANVAS_BOX;
 
-var modalDialog = require("../shared-modules/modal-dialog");
-var prefs = require("../shared-modules/prefs");
-
 var savePromptAppeared = false;
 var { date1, date2, date3, data, newlines } = setupData();
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
         helpersForController,
         invokeEventDialog,
@@ -28,19 +25,16 @@ function setupModule(module) {
         CALENDARNAME,
         EVENT_BOX,
         CANVAS_BOX,
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
-    let categories = prefs.preferences.getPref("calendar.categories.names", "string").split(",");
-    data[0].category = categories[0];
-    data[1].category = categories[1];
 }
 
 // Test that closing an event dialog with no changes does not prompt for save
 function testEventDialogModificationPrompt() {
     controller.click(eid("calendar-tab-button"));
     switchToView(controller, "day");
     goToDate(controller, 2009, 1, 1);
 
@@ -54,67 +48,52 @@ function testEventDialogModificationProm
 
         // save
         event.click(eventid("button-saveandclose"));
     });
 
     eventbox = lookupEventBox("day", EVENT_BOX, null, 1, 8, '/{"tooltip":"itemTooltip"}');
     invokeEventDialog(controller, eventbox, (event, iframe) => {
         // open, but change nothing
-        let dialog = new modalDialog.modalDialog(event.window);
-        dialog.start(handleSavePrompt);
-
         // escape the event window, there should be no prompt to save event
         event.keypress(null, "VK_ESCAPE", {});
-        sleep();
-        dialog.stop();
     });
 
     // open
     eventbox = lookupEventBox("day", EVENT_BOX, null, 1, 8, '/{"tooltip":"itemTooltip"}');
     invokeEventDialog(controller, eventbox, (event, iframe) => {
         // change all values
         setData(event, iframe, data[1]);
 
         // edit all values back to original
         setData(event, iframe, data[0]);
 
-        // this is set up after data entry because otherwise it tries to handle
-        // attachment dialog
-        dialog = new modalDialog.modalDialog(event.window);
-        dialog.start(handleSavePrompt);
-
         // escape the event window, there should be no prompt to save event
         event.keypress(null, "VK_ESCAPE", {});
-        sleep();
-        dialog.stop();
     });
 
     // delete event
-    controller.click(lookupEventBox("day", EVENT_BOX, null, 1, 8));
+    controller.click(lookupEventBox("day", EVENT_BOX, null, 1, 8, '/{"tooltip":"itemTooltip"}'));
     controller.keypress(eid("day-view"), "VK_DELETE", {});
     controller.waitForElementNotPresent(lookupEventBox("day", EVENT_BOX, null, 1, 8));
 
     for (let i = 0; i < newlines.length; i++) {
         // test set i
-        eventbox = lookupEventBox("day", EVENT_BOX, null, 1, 8, '/{"tooltip":"itemTooltip"}');
+        eventbox = lookupEventBox("day", CANVAS_BOX, null, 1, 8);
         invokeEventDialog(controller, eventbox, (event, iframe) => {
+            let { eid: eventid } = helpersForController(event);
             setData(event, iframe, newlines[i]);
             event.click(eventid("button-saveandclose"));
         });
 
         // open and close
         eventbox = lookupEventBox("day", EVENT_BOX, null, 1, 8, '/{"tooltip":"itemTooltip"}');
         invokeEventDialog(controller, eventbox, (event, iframe) => {
             setData(event, iframe, newlines[i]);
-            dialog = new modalDialog.modalDialog(event.window);
-            dialog.start(handleSavePrompt);
             event.keypress(null, "VK_ESCAPE", {});
-            sleep();
-            dialog.stop();
         });
 
         // delete it
         // XXX somehow the event is selected at this point, this didn't use to
         // be the case and can't be reproduced manually
         controller.keypress(eid("day-view"), "VK_DELETE", {});
         controller.waitForElementNotPresent(lookupEventBox("day", EVENT_BOX, null, 1, 8));
     }
@@ -122,66 +101,53 @@ function testEventDialogModificationProm
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
     if (savePromptAppeared) {
         controller.assertJS('"Prompt appeared" == "Prompt didn\'t appear."');
     }
 }
 
-function handleSavePrompt(controller) {
-    // unexpected prompt, thus the test has already failed
-    // can't trigger a failure though, because the following click wouldn't be executed
-    // so remembering it
-    savePromptAppered = true;
-    // application close is blocked without it
-    controller.click(lookup(`
-        /id("commonDialog")/anon({"anonid":"buttons"})/{"dlgtype":"extra1"}
-    `));
-}
-
 function setupData() {
     return {
         date1: new Date(2009, 0, 1, 8, 0),
         date2: new Date(2009, 0, 2, 9, 0),
         date3: new Date(2009, 0, 3, 10, 0),
         data: [{
             title: "title1",
             location: "location1",
+            category: "Anniversary",
             description: "description1",
             allday: false,
             startdate: date1,
             starttime: date1,
             enddate: date2,
             endtime: date2,
             repeat: "none",
-            reminder: 0,
             priority: "normal",
             privacy: "public",
             status: "confirmed",
             freebusy: "busy",
             timezone: true,
-            attachment: { add: "http://mozilla.org" }
         }, {
             title: "title2",
             location: "location2",
+            category: "Birthday",
             description: "description2",
             allday: true,
             startdate: date2,
             starttime: date2,
             enddate: date3,
             endtime: date3,
             repeat: "daily",
-            reminder: 2,
             priority: "high",
             privacy: "private",
             status: "tentative",
             freebusy: "free",
             timezone: true,
-            attachment: { "delete": "mozilla.org" }
         }],
         newlines: [
             { title: "title", description: "  test spaces  " },
             { title: "title", description: "\ntest newline\n" },
             { title: "title", description: "\rtest \\r\r" },
             { title: "title", description: "\r\ntest \\r\\n\r\n" },
             { title: "title", description: "\ttest \\t\t" }
         ]
--- a/calendar/test/mozmill/eventDialog/testUTF8.js
+++ b/calendar/test/mozmill/eventDialog/testUTF8.js
@@ -1,97 +1,83 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
 var helpersForController, invokeEventDialog, createCalendar;
-var deleteCalendars, switchToView;
+var deleteCalendars, switchToView, setData;
 var EVENT_BOX, CANVAS_BOX;
 
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 
 var UTF8STRING = " 💣 💥  ☣  ";
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
         helpersForController,
         invokeEventDialog,
         createCalendar,
         deleteCalendars,
         switchToView,
+        setData,
         EVENT_BOX,
         CANVAS_BOX
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, UTF8STRING);
     Preferences.set("calendar.categories.names", UTF8STRING);
 }
 
 function testUTF8() {
-    let eventDialog = '/id("calendar-event-dialog-inner")/id("event-grid")/id("event-grid-rows")';
-
     controller.click(eid("calendar-tab-button"));
     switchToView(controller, "day");
 
     // create new event
     let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, 8);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
+        let { lookup: iframeLookup, eid: iframeId } = helpersForController(iframe);
 
         // fill in name, location, description
-        let titleTextBox = eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
+        setData(event, iframe, { title: UTF8STRING, location: UTF8STRING, description: UTF8STRING });
+
+        let menuitem = iframeLookup(`
+            /id("calendar-event-dialog-inner")/id("event-grid")/
+            id("event-grid-rows")/id("event-grid-category-color-row")/
+            id("event-grid-category-box")/id("item-categories")/
+            id("item-categories-popup")/[2]
         `);
-        event.waitForElement(titleTextBox);
-        event.type(titleTextBox, UTF8STRING);
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-location-row")/id("item-location")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), UTF8STRING);
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-description-row")/id("item-description")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), UTF8STRING);
 
-        // select category
-        event.select(eventid("item-categories"), null, UTF8STRING);
+        event.click(iframeId("item-categories"));
+        menuitem.getNode().click();
+        menuitem.getNode().setAttribute("checked", "true"); // When in doubt, cheat.
+        event.waitFor(() => iframeId("item-categories").getNode().label == UTF8STRING);
+        iframeId("item-categories-popup").getNode().hidePopup();
 
         // save
         event.click(eventid("button-saveandclose"));
     });
 
     // open
     let eventPath = `/{"tooltip":"itemTooltip","calendar":"${UTF8STRING.toLowerCase()}"}`;
     eventBox = lookupEventBox("day", EVENT_BOX, null, 1, 8, eventPath);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: iframeId } = helpersForController(iframe);
 
         // check values
-        titleTextBox = eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `);
-        event.waitForElement(titleTextBox);
-        event.assertValue(titleTextBox, UTF8STRING);
-        event.assertValue(eventlookup(`
-            ${eventDialog}/id("event-grid-location-row")/id("item-location")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), UTF8STRING);
-        event.assertValue(eventlookup(`
-            ${eventDialog}/id("event-grid-description-row")/id("item-description")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), UTF8STRING);
-        event.assertValue(eventid("item-categories"), UTF8STRING);
+        event.assertValue(iframeId("item-title"), UTF8STRING);
+        event.assertValue(iframeId("item-location"), UTF8STRING);
+        event.assertValue(iframeId("item-description"), UTF8STRING);
+        event.assertJS(() => iframeId("item-categories").getNode().querySelector(`menuitem[label="${UTF8STRING}"][checked]`));
 
         // escape the event window
         event.keypress(null, "VK_ESCAPE", {});
     });
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, UTF8STRING);
--- a/calendar/test/mozmill/mozmilltests.list
+++ b/calendar/test/mozmill/mozmilltests.list
@@ -1,6 +1,9 @@
 cal-recurrence
 invitations
+recurrenceRotated
 testAlarmDefaultValue.js
 testBasicFunctionality.js
 testLocalICS.js
+testTimezones.js
 testTodayPane.js
+views
--- a/calendar/test/mozmill/recurrenceRotated/testAnnualRecurrence.js
+++ b/calendar/test/mozmill/recurrenceRotated/testAnnualRecurrence.js
@@ -1,96 +1,99 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+var MODULE_NAME = "testAnnualRecurrence";
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
-var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var switchToView, goToDate, handleOccurrencePrompt;
-var ALLDAY, CALENDARNAME;
+var CALENDARNAME, EVENTPATH, ALLDAY;
+var helpersForController, handleOccurrencePrompt, switchToView, goToDate;
+var invokeEventDialog, deleteCalendars, createCalendar, menulistSelect;
 
-var STARTYEAR = 1950;
-var EPOCH = 1970;
+const STARTYEAR = 1950;
+const EPOCH = 1970;
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
+        CALENDARNAME,
+        EVENTPATH,
+        ALLDAY,
         helpersForController,
-        invokeEventDialog,
-        createCalendar,
-        deleteCalendars,
+        handleOccurrencePrompt,
         switchToView,
         goToDate,
-        handleOccurrencePrompt,
-        ALLDAY,
-        CALENDARNAME
+        invokeEventDialog,
+        deleteCalendars,
+        createCalendar,
+        menulistSelect
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
 function testAnnualRecurrence() {
-    let EVENTPATH = `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`;
     controller.click(eid("calendar-tab-button"));
+    sleep();
+
     switchToView(controller, "day");
     goToDate(controller, STARTYEAR, 1, 1);
 
     // rotate view
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "horizontal");
 
     // create yearly recurring all-day event
     let eventBox = lookupEventBox("day", ALLDAY, null, 1, null);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
 
-        event.select(eventid("item-repeat"), null, null, "yearly");
-        event.click(eventid("button-save"));
+        menulistSelect(eventid("item-repeat"), "yearly", event);
+        event.click(eventid("button-saveandclose"));
     });
 
     let checkYears = [STARTYEAR, STARTYEAR + 1, EPOCH - 1, EPOCH, EPOCH + 1];
-    let box = "";
     for (let year of checkYears) {
         goToDate(controller, year, 1, 1);
         let date = new Date(year, 0, 1);
         let column = date.getDay() + 1;
 
         // day view
         switchToView(controller, "day");
-        controller.assertNode(
+        controller.waitForElement(
             lookupEventBox("day", ALLDAY, null, 1, null, EVENTPATH)
         );
 
         // week view
         switchToView(controller, "week");
-        controller.assertNode(
+        controller.waitForElement(
             lookupEventBox("week", ALLDAY, null, column, null, EVENTPATH)
         );
 
         // multiweek view
         switchToView(controller, "multiweek");
-        controller.assertNode(
+        controller.waitForElement(
             lookupEventBox("multiweek", ALLDAY, 1, column, null, EVENTPATH)
         );
 
         // month view
         switchToView(controller, "month");
-        controller.assertNode(
+        controller.waitForElement(
             lookupEventBox("month", ALLDAY, 1, column, null, EVENTPATH)
         );
     }
 
     // delete event
     goToDate(controller, checkYears[0], 1, 1);
     switchToView(controller, "day");
-    box = getEventBoxPath("day", ALLDAY, null, 1, null) + EVENTPATH;
+    let box = getEventBoxPath("day", ALLDAY, null, 1, null) + EVENTPATH;
     controller.click(lookup(box));
     handleOccurrencePrompt(controller, eid("day-view"), "delete", true, false);
     controller.waitForElementNotPresent(lookup(box));
 
     // reset view
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "vertical");
 }
--- a/calendar/test/mozmill/recurrenceRotated/testBiweeklyRecurrence.js
+++ b/calendar/test/mozmill/recurrenceRotated/testBiweeklyRecurrence.js
@@ -1,36 +1,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+var MODULE_NAME = "testBiweeklyRecurrence";
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
-var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var switchToView, goToDate, viewForward, handleOccurrencePrompt;
-var CALENDARNAME, EVENT_BOX, CANVAS_BOX;
+var CALENDARNAME, EVENTPATH, EVENT_BOX, CANVAS_BOX;
+var helpersForController, handleOccurrencePrompt, switchToView, goToDate;
+var invokeEventDialog, viewForward, createCalendar, deleteCalendars, menulistSelect;
 
-var HOUR = 8;
-var EVENTPATH = `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`;
+const HOUR = 8;
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
+        CALENDARNAME,
+        EVENTPATH,
+        EVENT_BOX,
+        CANVAS_BOX,
         helpersForController,
-        invokeEventDialog,
-        createCalendar,
-        deleteCalendars,
+        handleOccurrencePrompt,
         switchToView,
         goToDate,
+        invokeEventDialog,
         viewForward,
-        handleOccurrencePrompt,
-        CALENDARNAME,
-        EVENT_BOX,
-        CANVAS_BOX
+        deleteCalendars,
+        createCalendar,
+        menulistSelect
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
 function testBiweeklyRecurrence() {
@@ -42,73 +44,76 @@ function testBiweeklyRecurrence() {
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "horizontal");
 
     // create biweekly event
     let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, HOUR);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
 
-        event.waitForElement(eventid("item-repeat"));
-        event.select(eventid("item-repeat"), null, null, "bi.weekly");
+        menulistSelect(eventid("item-repeat"), "bi.weekly", event);
         event.click(eventid("button-saveandclose"));
     });
 
     // check day view
     for (let i = 0; i < 4; i++) {
-        controller.assertNode(lookupEventBox("day", EVENT_BOX, null, 1, HOUR, EVENTPATH));
+        controller.waitForElement(
+            lookupEventBox("day", EVENT_BOX, null, 1, HOUR, EVENTPATH)
+        );
         viewForward(controller, 14);
     }
 
     // check week view
     switchToView(controller, "week");
     goToDate(controller, 2009, 1, 31);
 
     for (let i = 0; i < 4; i++) {
-        controller.assertNode(lookupEventBox("week", EVENT_BOX, null, 7, HOUR, EVENTPATH));
+        controller.waitForElement(
+            lookupEventBox("week", EVENT_BOX, null, 7, HOUR, EVENTPATH)
+        );
         viewForward(controller, 2);
     }
 
     // check multiweek view
     switchToView(controller, "multiweek");
     goToDate(controller, 2009, 1, 31);
 
     // always two occurrences in view, 1st and 3rd or 2nd and 4th week
     for (let i = 0; i < 5; i++) {
-        controller.assertNode(
+        controller.waitForElement(
             lookupEventBox("multiweek", EVENT_BOX, i % 2 + 1, 7, null, EVENTPATH)
         );
         controller.assertNode(
-            getEventBoxPath("multiweek", EVENT_BOX, i % 2 + 3, 7, null, EVENTPATH)
+            lookupEventBox("multiweek", EVENT_BOX, i % 2 + 3, 7, null, EVENTPATH)
         );
         viewForward(controller, 1);
     }
 
     // check month view
     switchToView(controller, "month");
     goToDate(controller, 2009, 1, 31);
 
     // January
-    controller.assertNode(lookupEventBox("month", EVENT_BOX, 5, 7, null, EVENTPATH));
+    controller.waitForElement(lookupEventBox("month", EVENT_BOX, 5, 7, null, EVENTPATH));
     viewForward(controller, 1);
 
     // February
-    controller.assertNode(lookupEventBox("month", EVENT_BOX, 2, 7, null, EVENTPATH));
+    controller.waitForElement(lookupEventBox("month", EVENT_BOX, 2, 7, null, EVENTPATH));
     controller.assertNode(lookupEventBox("month", EVENT_BOX, 4, 7, null, EVENTPATH));
     viewForward(controller, 1);
 
     // March
-    controller.assertNode(lookupEventBox("month", EVENT_BOX, 2, 7, null, EVENTPATH));
+    controller.waitForElement(lookupEventBox("month", EVENT_BOX, 2, 7, null, EVENTPATH));
     controller.assertNode(lookupEventBox("month", EVENT_BOX, 4, 7, null, EVENTPATH));
 
     // delete event
-    let box = getEventBoxPath("month", EVENT_BOX, 4, 7, null) + EVENTPATH;
-    controller.click(lookup(box));
+    let box = lookupEventBox("month", EVENT_BOX, 4, 7, null, EVENTPATH);
+    controller.click(box);
     handleOccurrencePrompt(controller, eid("month-view"), "delete", true, false);
-    controller.waitForElementNotPresent(lookup(box));
+    controller.waitForElementNotPresent(box);
 
     // reset view
     switchToView(controller, "day");
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "vertical");
 }
 
 function teardownTest(module) {
--- a/calendar/test/mozmill/recurrenceRotated/testDailyRecurrence.js
+++ b/calendar/test/mozmill/recurrenceRotated/testDailyRecurrence.js
@@ -1,37 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+var MODULE_NAME = "testDailyRecurrence";
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
+var CALENDARNAME, EVENTPATH, EVENT_BOX, CANVAS_BOX;
 var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
 var switchToView, goToDate, viewForward, viewBack, handleOccurrencePrompt;
-var CALENDARNAME, EVENT_BOX, CANVAS_BOX;
+var menulistSelect;
 
-var HOUR = 8;
-var EVENTPATH = `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`;
+const HOUR = 8;
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
+        CALENDARNAME,
+        EVENTPATH,
+        EVENT_BOX,
+        CANVAS_BOX,
         helpersForController,
-        invokeEventDialog,
-        createCalendar,
-        deleteCalendars,
+        handleOccurrencePrompt,
         switchToView,
         goToDate,
+        invokeEventDialog,
         viewForward,
         viewBack,
-        handleOccurrencePrompt,
-        CALENDARNAME,
-        EVENT_BOX,
-        CANVAS_BOX
+        deleteCalendars,
+        createCalendar,
+        menulistSelect
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
 function testDailyRecurrence() {
@@ -43,66 +46,73 @@ function testDailyRecurrence() {
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "horizontal");
 
     // create daily event
     let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, HOUR);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
 
-        event.waitForElement(eventid("item-repeat"));
-        event.select(eventid("item-repeat"), null, null, "daily");
+        menulistSelect(eventid("item-repeat"), "daily", event);
         event.click(eventid("button-saveandclose"));
     });
 
     // check day view for 7 days
     let daybox = getEventBoxPath("day", EVENT_BOX, null, 1, HOUR, null) + EVENTPATH;
     controller.waitForElement(lookup(daybox));
 
     for (let day = 1; day <= 7; day++) {
-        controller.assertNode(lookup(daybox));
+        controller.waitForElement(lookup(daybox));
         viewForward(controller, 1);
     }
 
     // check week view for 2 weeks
     switchToView(controller, "week");
     goToDate(controller, 2009, 1, 1);
 
     for (let day = 5; day <= 7; day++) {
-        controller.assertNode(lookupEventBox("week", EVENT_BOX, 1, day, HOUR, EVENTPATH));
+        controller.waitForElement(
+            lookupEventBox("week", EVENT_BOX, 1, day, HOUR, EVENTPATH)
+        );
     }
 
     viewForward(controller, 1);
 
     for (let day = 1; day <= 7; day++) {
-        controller.assertNode(lookupEventBox("week", EVENT_BOX, 2, day, HOUR, EVENTPATH));
+        controller.waitForElement(
+            lookupEventBox("week", EVENT_BOX, 2, day, HOUR, EVENTPATH)
+        );
     }
 
     // check multiweek view for 4 weeks
     switchToView(controller, "multiweek");
     goToDate(controller, 2009, 1, 1);
 
     for (let day = 5; day <= 7; day++) {
-        controller.assertNode(lookupEventBox("multiweek", EVENT_BOX, 1, day, HOUR, EVENTPATH));
+        controller.waitForElement(
+            lookupEventBox("multiweek", EVENT_BOX, 1, day, HOUR, EVENTPATH)
+        );
     }
 
     for (let week = 2; week <= 4; week++) {
         for (let day = 1; day <= 7; day++) {
-            controller.assertNode(
+            controller.waitForElement(
                 lookupEventBox("multiweek", EVENT_BOX, week, day, HOUR, EVENTPATH)
             );
         }
     }
 
     // check month view for all 5 weeks
     switchToView(controller, "month");
     goToDate(controller, 2009, 1, 1);
 
     for (let day = 5; day <= 7; day++) {
-        controller.assertNode(lookupEventBox("month", EVENT_BOX, 1, day, null, EVENTPATH));
+        controller.waitForElement(
+            lookupEventBox("month", EVENT_BOX, 1, day, null, EVENTPATH)
+        );
     }
 
     for (let week = 2; week <= 5; week++) {
         for (let day = 1; day <= 7; day++) {
             controller.assertNode(
                 lookupEventBox("month", EVENT_BOX, week, day, null, EVENTPATH)
             );
         }
@@ -132,76 +142,72 @@ function testDailyRecurrence() {
     );
 
     // go to previous day to edit event to occur only on weekdays
     viewBack(controller, 1);
 
     eventBox = lookupEventBox("day", EVENT_BOX, null, 1, HOUR, EVENTPATH);
     handleOccurrencePrompt(controller, eventBox, "modify", true, false);
     invokeEventDialog(controller, null, (event, iframe) => {
-        let { eid: eventid } = helpersForController(event);
+        let { eid: eventid, sleep: eventsleep } = helpersForController(event);
 
-        event.waitForElement(eventid("item-repeat"));
-        event.select(eventid("item-repeat"), null, null, "every.weekday");
+        menulistSelect(eventid("item-repeat"), "every.weekday", event);
+        eventsleep();
         event.click(eventid("button-saveandclose"));
     });
 
     // check day view for 7 days
     let day = getEventBoxPath("day", EVENT_BOX, null, 1, null) + EVENTPATH;
-    let dates = [[2009, 1, 3], [2009, 1, 4]];
+    let dates = [
+        [2009, 1, 3],
+        [2009, 1, 4]
+    ];
     for (let [y, m, d] of dates) {
         goToDate(controller, y, m, d);
         controller.assertNodeNotExist(lookup(day));
     }
 
     // check week view for 2 weeks
     switchToView(controller, "week");
     goToDate(controller, 2009, 1, 1);
 
     for (let i = 0; i <= 1; i++) {
-        controller.assertNodeNotExist(
+        controller.waitForElementNotPresent(
             lookupEventBox("week", EVENT_BOX, null, 1, null, EVENTPATH)
         );
         controller.assertNodeNotExist(
             lookupEventBox("week", EVENT_BOX, null, 7, null, EVENTPATH)
         );
-
         viewForward(controller, 1);
     }
 
     // check multiweek view for 4 weeks
     switchToView(controller, "multiweek");
     goToDate(controller, 2009, 1, 1);
 
     for (let i = 1; i <= 4; i++) {
-        controller.assertNodeNotExist(
+        controller.waitForElementNotPresent(
             lookupEventBox("multiweek", EVENT_BOX, i, 1, null, EVENTPATH)
         );
-
         controller.assertNodeNotExist(
             lookupEventBox("multiweek", EVENT_BOX, i, 7, null, EVENTPATH)
         );
-
-        viewForward(controller, 1);
     }
 
     // check month view for all 5 weeks
     switchToView(controller, "month");
     goToDate(controller, 2009, 1, 1);
 
     for (let i = 1; i <= 5; i++) {
-        controller.assertNodeNotExist(
+        controller.waitForElementNotPresent(
             lookupEventBox("month", EVENT_BOX, i, 1, null, EVENTPATH)
         );
-
         controller.assertNodeNotExist(
             lookupEventBox("month", EVENT_BOX, i, 7, null, EVENTPATH)
         );
-
-        viewForward(controller, 1);
     }
 
     // delete event
     day = getEventBoxPath("month", EVENT_BOX, 1, 5, null) + EVENTPATH;
     controller.click(lookup(day));
     handleOccurrencePrompt(controller, eid("month-view"), "delete", true, false);
     controller.waitForElementNotPresent(lookup(day));
 
--- a/calendar/test/mozmill/recurrenceRotated/testLastDayOfMonthRecurrence.js
+++ b/calendar/test/mozmill/recurrenceRotated/testLastDayOfMonthRecurrence.js
@@ -1,147 +1,150 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+var MODULE_NAME = "testLastDayOfMonthRecurrence";
 var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils"];
+var MODULE_REQUIRES = ["calendar-utils", "window-helpers"];
 
-var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var switchToView, goToDate, handleOccurrencePrompt;
-var CALENDARNAME, EVENT_BOX, CANVAS_BOX;
+var TIMEOUT_MODAL_DIALOG, CALENDARNAME, EVENTPATH, EVENT_BOX;
+var CANVAS_BOX, REC_DLG_ACCEPT;
+var helpersForController, handleOccurrencePrompt, switchToView, goToDate;
+var invokeEventDialog, deleteCalendars, createCalendar, menulistSelect;
+var plan_for_modal_dialog, wait_for_modal_dialog;
 
-var modalDialog = require("../shared-modules/modal-dialog");
-
-var HOUR = 8;
-var EVENTPATH = `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`;
+const HOUR = 8;
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
+        TIMEOUT_MODAL_DIALOG,
+        CALENDARNAME,
+        EVENTPATH,
+        EVENT_BOX,
+        CANVAS_BOX,
+        REC_DLG_ACCEPT,
         helpersForController,
         invokeEventDialog,
         createCalendar,
         deleteCalendars,
         switchToView,
         goToDate,
         handleOccurrencePrompt,
-        CALENDARNAME,
-        EVENT_BOX,
-        CANVAS_BOX
+        menulistSelect
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
+    ({ plan_for_modal_dialog, wait_for_modal_dialog } =
+        collector.getModule("window-helpers")
+    );
+
     createCalendar(controller, CALENDARNAME);
 }
 
 function testLastDayOfMonthRecurrence() {
     controller.click(eid("calendar-tab-button"));
     switchToView(controller, "day");
     goToDate(controller, 2008, 1, 31); // start with a leap year
 
     // rotate view
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "horizontal");
 
     // create monthly recurring event
-    let eventBox = lookupEventBox(controller, "day", CANVAS_BOX, null, 1, HOUR);
+    let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, HOUR);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
 
-        let dialog = new modalDialog.modalDialog(event.window);
-        dialog.start(setRecurrence);
-        event.waitForElement(eventid("item-repeat"));
-        event.select(eventid("item-repeat"), null, null, "custom");
+        plan_for_modal_dialog("Calendar:EventDialog:Recurrence", setRecurrence);
+        menulistSelect(eventid("item-repeat"), "custom", event);
+        wait_for_modal_dialog("Calendar:EventDialog:Recurrence", TIMEOUT_MODAL_DIALOG);
 
         event.click(eventid("button-saveandclose"));
     });
 
-    //                      date      correct row in month view
-    //                   vvvvvvvvvvv  v
+    // data tuple: [year, month, day, row in month view]
+    // note: month starts here with 1 for January
     let checkingData = [[2008, 1, 31, 5],
                         [2008, 2, 29, 5],
                         [2008, 3, 31, 6],
                         [2008, 4, 30, 5],
                         [2008, 5, 31, 5],
                         [2008, 6, 30, 5],
                         [2008, 7, 31, 5],
                         [2008, 8, 31, 6],
                         [2008, 9, 30, 5],
                         [2008, 10, 31, 5],
                         [2008, 11, 30, 6],
                         [2008, 12, 31, 5],
                         [2009, 1, 31, 5],
                         [2009, 2, 28, 4],
                         [2009, 3, 31, 5]];
-    let box = "";
-
     // check all dates
     for (let [y, m, d, correctRow] of checkingData) {
+        let date = new Date(y, m - 1, d);
+        let column = date.getDay() + 1;
+
         goToDate(controller, y, m, d);
 
         // day view
         switchToView(controller, "day");
         controller.waitForElement(
-            lookupEventBox(controller, "day", EVENT_BOX, null, 1, HOUR, EVENTPATH)
+            lookupEventBox("day", EVENT_BOX, null, 1, HOUR, EVENTPATH)
         );
 
         // week view
         switchToView(controller, "week");
-        let date = new Date(y, m - 1, d);
-        let column = date.getDay() + 1;
         controller.waitForElement(
-            lookupEventBox(controller, "week", EVENT_BOX, null, column, HOUR, EVENTPATH)
+            lookupEventBox("week", EVENT_BOX, null, column, HOUR, EVENTPATH)
         );
 
         // multiweek view
         switchToView(controller, "multiweek");
-        controller.assertNode(
-            lookupEventBox(controller, "multiweek", EVENT_BOX, 1, column, null, EVENTPATH)
+        controller.waitForElement(
+            lookupEventBox("multiweek", EVENT_BOX, 1, column, null, EVENTPATH)
         );
 
         // month view
         switchToView(controller, "month");
-        controller.assertNode(
-            lookupEventBox(controller, "month", EVENT_BOX, correctRow, column, null, EVENTPATH)
+        controller.waitForElement(
+            lookupEventBox("month", EVENT_BOX, correctRow, column, null, EVENTPATH)
         );
     }
 
     // delete event
     goToDate(controller, checkingData[0][0], checkingData[0][1], checkingData[0][2]);
     switchToView(controller, "day");
-
-    box = getEventBoxPath(controller, "day", EVENT_BOX, null, 1, HOUR) + EVENTPATH;
+    let box = getEventBoxPath("day", EVENT_BOX, null, 1, HOUR) + EVENTPATH;
     controller.waitThenClick(lookup(box));
     handleOccurrencePrompt(controller, eid("day-view"), "delete", true, false);
     controller.waitForElementNotPresent(lookup(box));
 
     // reset view
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "vertical");
 }
 
 function setRecurrence(recurrence) {
-    let { lookup: reclookup, eid: recid, sleep: recsleep } = helpersForController(recurrence);
+    let {
+        sleep: recsleep,
+        lookup: reclookup,
+        eid: recid
+    } = helpersForController(recurrence);
 
     // monthly
-    recsleep();
-    recurrence.select(recid("period-list"), null, null, "2");
+    menulistSelect(recid("period-list"), "2", recurrence);
 
     // last day of month
-    recurrence.click(recid("montly-period-relative-date-radio"));
-    recsleep();
-    recurrence.select(recid("monthly-ordinal"), null, null, "-1");
-    recsleep();
-    recurrence.select(recid("monthly-weekday"), null, null, "-1");
+    recurrence.radio(recid("montly-period-relative-date-radio"));
+    menulistSelect(recid("monthly-ordinal"), "-1", recurrence);
+    menulistSelect(recid("monthly-weekday"), "-1", recurrence);
     recsleep();
 
     // close dialog
-    recurrence.click(reclookup(`
-        /id("calendar-event-dialog-recurrence")/anon({"anonid":"buttons"})/
-        {"dlgtype":"accept"}
-    `));
+    recurrence.click(reclookup(REC_DLG_ACCEPT));
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/recurrenceRotated/testWeeklyNRecurrence.js
+++ b/calendar/test/mozmill/recurrenceRotated/testWeeklyNRecurrence.js
@@ -1,43 +1,53 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+var MODULE_NAME = "testWeeklyNRecurrence";
 var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils"];
+var MODULE_REQUIRES = ["calendar-utils", "window-helpers"];
+
+var { cal } = ChromeUtils.import("resource://calendar/modules/calUtils.jsm", null);
 
-var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var switchToView, goToDate, viewForward, handleOccurrencePrompt;
-var CALENDARNAME, EVENT_BOX, CANVAS_BOX;
+var TIMEOUT_MODAL_DIALOG, CALENDARNAME, EVENTPATH, EVENT_BOX, CANVAS_BOX;
+var REC_DLG_ACCEPT, REC_DLG_DAYS;
+var helpersForController, handleOccurrencePrompt, switchToView, goToDate;
+var invokeEventDialog, viewForward, deleteCalendars, createCalendar, menulistSelect;
+var plan_for_modal_dialog, wait_for_modal_dialog;
 
-var modalDialog = require("../shared-modules/modal-dialog");
-var utils = require("../shared-modules/utils");
-
-var HOUR = 8;
-var EVENTPATH = `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`;
+const HOUR = 8;
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
+        TIMEOUT_MODAL_DIALOG,
+        CALENDARNAME,
+        EVENTPATH,
+        EVENT_BOX,
+        CANVAS_BOX,
+        REC_DLG_ACCEPT,
+        REC_DLG_DAYS,
         helpersForController,
-        invokeEventDialog,
-        createCalendar,
-        deleteCalendars,
+        handleOccurrencePrompt,
         switchToView,
         goToDate,
+        invokeEventDialog,
         viewForward,
-        handleOccurrencePrompt,
-        CALENDARNAME,
-        EVENT_BOX,
-        CANVAS_BOX
+        deleteCalendars,
+        createCalendar,
+        menulistSelect
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
+    ({ plan_for_modal_dialog, wait_for_modal_dialog } =
+        collector.getModule("window-helpers")
+    );
+
     createCalendar(controller, CALENDARNAME);
 }
 
 function testWeeklyNRecurrence() {
     controller.click(eid("calendar-tab-button"));
     switchToView(controller, "day");
     goToDate(controller, 2009, 1, 5);
 
@@ -45,129 +55,129 @@ function testWeeklyNRecurrence() {
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "horizontal");
 
     // create weekly recurring event
     let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, HOUR);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
 
-        let dialog = new modalDialog.modalDialog(event.window);
-        dialog.start(setRecurrence);
+        plan_for_modal_dialog("Calendar:EventDialog:Recurrence", setRecurrence);
         event.waitForElement(eventid("item-repeat"));
-        event.select(eventid("item-repeat"), null, null, "custom");
+        menulistSelect(eventid("item-repeat"), "custom", event);
+        wait_for_modal_dialog("Calendar:EventDialog:Recurrence", TIMEOUT_MODAL_DIALOG);
 
         event.click(eventid("button-saveandclose"));
     });
 
     // check day view
-
+    let box = getEventBoxPath("day", EVENT_BOX, undefined, 1, HOUR) + EVENTPATH;
     // Monday, Tuesday, Wednesday, Thursday
     for (let i = 0; i < 4; i++) {
-        controller.assertNode(lookupEventBox("day", EVENT_BOX, null, 1, HOUR, EVENTPATH));
+        controller.waitForElement(lookup(box));
         viewForward(controller, 1);
     }
 
-    // Saturday
+    // Not Friday
+    sleep();
+    controller.assertNodeNotExist(lookup(box));
     viewForward(controller, 1);
-    controller.assertNodeNotExist(lookupEventBox("day", EVENT_BOX, null, 1, HOUR, EVENTPATH));
+
+    // Not Saturday as only 4 occurrences are set.
+    sleep();
+    controller.assertNodeNotExist(lookup(box));
 
     // check week view
     switchToView(controller, "week");
 
     // Monday, Tuesday, Wednesday, Thursday
     for (let i = 2; i < 6; i++) {
-        controller.waitForElement(lookupEventBox("week", EVENT_BOX, null, i, HOUR, EVENTPATH));
+        controller.waitForElement(
+            lookupEventBox("week", EVENT_BOX, null, i, HOUR, EVENTPATH)
+        );
     }
 
     // Saturday
-    controller.assertNodeNotExist(lookupEventBox("week", EVENT_BOX, null, 7, HOUR, EVENTPATH));
+    controller.assertNodeNotExist(
+        lookupEventBox("week", EVENT_BOX, null, 7, HOUR, EVENTPATH)
+    );
 
     // check multiweek view
     switchToView(controller, "multiweek");
     checkMultiWeekView("multiweek");
 
     // check month view
     switchToView(controller, "month");
     checkMultiWeekView("month");
 
     // delete event
-    let box = getEventBoxPath("month", EVENT_BOX, 2, 2, HOUR) + EVENTPATH;
+    box = getEventBoxPath("month", EVENT_BOX, 2, 2, HOUR) + EVENTPATH;
     controller.click(lookup(box));
     handleOccurrencePrompt(controller, eid("month-view"), "delete", true, false);
     controller.waitForElementNotPresent(lookup(box));
 
     // reset view
     switchToView(controller, "day");
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "vertical");
 }
 
 function setRecurrence(recurrence) {
-    let { lookup: reclookup, eid: recid, sleep: recsleep } = helpersForController(recurrence);
+    let {
+        sleep: recsleep,
+        lookup: reclookup,
+        eid: recid,
+    } = helpersForController(recurrence);
 
     // weekly
     recurrence.waitForElement(recid("period-list"));
-    recurrence.select(recid("period-list"), null, null, "1");
+    menulistSelect(recid("period-list"), "1", recurrence);
     recsleep();
 
-    let mon = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.2.Mmm");
-    let tue = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.3.Mmm");
-    let wed = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.4.Mmm");
-    let thu = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.5.Mmm");
-    let sat = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.7.Mmm");
+    let mon = cal.l10n.getDateFmtString("day.2.Mmm");
+    let tue = cal.l10n.getDateFmtString("day.3.Mmm");
+    let wed = cal.l10n.getDateFmtString("day.4.Mmm");
+    let thu = cal.l10n.getDateFmtString("day.5.Mmm");
+    let sat = cal.l10n.getDateFmtString("day.7.Mmm");
 
-    let days = `
-        /id("calendar-event-dialog-recurrence")/id("recurrence-pattern-groupbox")/
-        id("recurrence-pattern-grid")/id("recurrence-pattern-rows")/
-        id("recurrence-pattern-period-row")/id("period-deck")/
-        id("period-deck-weekly-box")/[1]/id("daypicker-weekday")/
-        anon({"anonid":"mainbox"})
-    `;
-
-    // starting from Monday so it should be checked
-    recurrence.assertChecked(reclookup(`${days}/{"label":"${mon}"}`));
+    // starting from Monday so it should be checked. We have to wait a little,
+    // because the checkedstate is set in background by JS.
+    recurrence.waitFor(() => {
+        return recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${mon}"}`));
+    }, 30000);
     // check Tuesday, Wednesday, Thursday and Saturday too
-    recurrence.click(reclookup(`${days}/{"label":"${tue}"}`));
-    recurrence.click(reclookup(`${days}/{"label":"${wed}"}`));
-    recurrence.click(reclookup(`${days}/{"label":"${thu}"}`));
-    recurrence.click(reclookup(`${days}/{"label":"${sat}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${tue}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${wed}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${thu}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${sat}"}`));
 
     // set number of occurrences
     recurrence.click(recid("recurrence-range-for"));
-    let input = `
-        /id("calendar-event-dialog-recurrence")/id("recurrence-range-groupbox")/[1]/
-        id("recurrence-duration")/id("recurrence-range-count-box")/
-        id("repeat-ntimes-count")/
-        anon({"class":"textbox-input-box numberbox-input-box"})/
-        anon({"anonid":"input"})
-    `;
-    // replace previous number
-    recurrence.keypress(reclookup(input), "a", { ctrlKey: true });
-    recurrence.type(reclookup(input), "4");
+    let ntimesField = recid("repeat-ntimes-count");
+    ntimesField.getNode().value = "4";
 
     // close dialog
-    recurrence.click(reclookup(`
-        /id("calendar-event-dialog-recurrence")/anon({"anonid":"buttons"})/
-        {"dlgtype":"accept"}
-    `));
+    recurrence.click(reclookup(REC_DLG_ACCEPT));
 }
 
 function checkMultiWeekView(view) {
-    let week = 1;
+    // make sure, the view has time to load
+    sleep();
 
-    // in month view event starts from 2nd row
-    if (view == "month") {
-        week++;
-    }
+    // In month view event starts from 2nd row
+    let week = view == "month" ? 2 : 1;
 
     // Monday, Tuesday, Wednesday, Thursday
     for (let i = 2; i < 6; i++) {
-        controller.assertNode(lookupEventBox(view, EVENT_BOX, week, i, null, EVENTPATH));
+        controller.assertNode(
+            lookupEventBox(view, EVENT_BOX, week, i, null, EVENTPATH)
+        );
     }
 
     // Saturday
-    controller.assertNodeNotExist(lookupEventBox(view, EVENT_BOX, week, 7, null, EVENTPATH));
+    controller.assertNodeNotExist(
+        getEventBoxPath(view, EVENT_BOX, week, 7, null, EVENTPATH)
+    );
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/recurrenceRotated/testWeeklyUntilRecurrence.js
+++ b/calendar/test/mozmill/recurrenceRotated/testWeeklyUntilRecurrence.js
@@ -1,235 +1,227 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+var MODULE_NAME = "testWeeklyUntilRecurrence";
 var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils"];
+var MODULE_REQUIRES = ["calendar-utils", "window-helpers"];
 
 var { cal } = ChromeUtils.import("resource://calendar/modules/calUtils.jsm", null);
 
-var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var switchToView, goToDate, viewForward, handleOccurrencePrompt;
-var CALENDARNAME, EVENT_BOX, CANVAS_BOX;
+var SHORT_SLEEP, TIMEOUT_MODAL_DIALOG, CALENDARNAME, EVENTPATH, EVENT_BOX;
+var CANVAS_BOX, REC_DLG_DAYS, REC_DLG_ACCEPT, REC_DLG_UNTIL_INPUT;
+var helpersForController, handleOccurrencePrompt, switchToView, goToDate;
+var invokeEventDialog, viewForward, deleteCalendars, createCalendar, menulistSelect;
+var plan_for_modal_dialog, wait_for_modal_dialog;
 
-var modalDialog = require("../shared-modules/modal-dialog");
-var utils = require("../shared-modules/utils");
-
-var ENDDATE = new Date(2009, 0, 26); // last Monday in month
-var HOUR = 8;
-var EVENTPATH = `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`;
+const ENDDATE = new Date(2009, 0, 26); // last Monday in month
+const HOUR = 8;
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
+        SHORT_SLEEP,
+        TIMEOUT_MODAL_DIALOG,
+        CALENDARNAME,
+        EVENTPATH,
+        EVENT_BOX,
+        CANVAS_BOX,
+        REC_DLG_DAYS,
+        REC_DLG_ACCEPT,
+        REC_DLG_UNTIL_INPUT,
         helpersForController,
-        invokeEventDialog,
-        createCalendar,
-        deleteCalendars,
+        handleOccurrencePrompt,
         switchToView,
         goToDate,
+        invokeEventDialog,
         viewForward,
-        handleOccurrencePrompt,
-        CALENDARNAME,
-        EVENT_BOX,
-        CANVAS_BOX
+        deleteCalendars,
+        createCalendar,
+        menulistSelect
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
+    ({ plan_for_modal_dialog, wait_for_modal_dialog } =
+        collector.getModule("window-helpers")
+    );
+
     createCalendar(controller, CALENDARNAME);
 }
 
 function testWeeklyUntilRecurrence() {
     controller.click(eid("calendar-tab-button"));
     switchToView(controller, "day");
     goToDate(controller, 2009, 1, 5); // Monday
 
     // rotate view
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "horizontal");
 
     // create weekly recurring event
-    let eventBox = lookupEventBox(controller, "day", CANVAS_BOX, null, 1, HOUR);
+    let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, HOUR);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
 
-        let dialog = new modalDialog.modalDialog(event.window);
-        dialog.start(setRecurrence);
+        plan_for_modal_dialog("Calendar:EventDialog:Recurrence", setRecurrence);
         event.waitForElement(eventid("item-repeat"));
-        event.select(eventid("item-repeat"), null, null, "custom");
+        menulistSelect(eventid("item-repeat"), "custom", event);
+        wait_for_modal_dialog("Calendar:EventDialog:Recurrence", TIMEOUT_MODAL_DIALOG);
 
         event.click(eventid("button-saveandclose"));
     });
 
-    let box = getEventBoxPath(controller, "day", EVENT_BOX, null, 1, HOUR) + EVENTPATH;
+    let box = getEventBoxPath("day", EVENT_BOX, null, 1, HOUR) + EVENTPATH;
 
     // check day view
     for (let week = 0; week < 3; week++) {
         // Monday
-        controller.assertNode(lookup(box));
+        controller.waitForElement(lookup(box));
         viewForward(controller, 2);
 
         // Wednesday
-        controller.assertNode(lookup(box));
+        controller.waitForElement(lookup(box));
         viewForward(controller, 2);
 
         // Friday
-        controller.assertNode(lookup(box));
+        controller.waitForElement(lookup(box));
         viewForward(controller, 3);
     }
 
     // Monday, last occurrence
-    controller.assertNode(lookup(box));
+    controller.waitForElement(lookup(box));
     viewForward(controller, 2);
 
     // Wednesday
-    controller.assertNodeNotExist(lookup(box));
-    viewForward(controller, 2);
+    controller.waitForElementNotPresent(lookup(box));
 
     // check week view
     switchToView(controller, "week");
     goToDate(controller, 2009, 1, 5);
     for (let week = 0; week < 3; week++) {
         // Monday
-        controller.assertNode(
-            lookupEventBox(controller, "week", EVENT_BOX, null, 2, HOUR, EVENTPATH)
+        controller.waitForElement(
+            lookupEventBox("week", EVENT_BOX, null, 2, HOUR, EVENTPATH)
         );
 
-        controller.assertNode(
-            lookupEventBox(controller, "week", EVENT_BOX, null, 4, HOUR, EVENTPATH)
+        // Wednesday
+        controller.waitForElement(
+            lookupEventBox("week", EVENT_BOX, null, 4, HOUR, EVENTPATH)
         );
 
         // Friday
-        controller.assertNode(
-            lookupEventBox(controller, "week", EVENT_BOX, null, 6, HOUR, EVENTPATH)
+        controller.waitForElement(
+            lookupEventBox("week", EVENT_BOX, null, 6, HOUR, EVENTPATH)
         );
 
         viewForward(controller, 1);
     }
 
     // Monday, last occurrence
-    controller.assertNode(
-        lookupEventBox(controller, "week", EVENT_BOX, null, 2, HOUR, EVENTPATH)
+    controller.waitForElement(
+        lookupEventBox("week", EVENT_BOX, null, 2, HOUR, EVENTPATH)
     );
-
     // Wednesday
     controller.assertNodeNotExist(
-        lookupEventBox(controller, "week", EVENT_BOX, null, 4, HOUR, EVENTPATH)
+        lookupEventBox("week", EVENT_BOX, null, 4, HOUR, EVENTPATH)
     );
 
     // check multiweek view
     switchToView(controller, "multiweek");
     goToDate(controller, 2009, 1, 5);
     checkMultiWeekView("multiweek");
 
     // check month view
     switchToView(controller, "month");
     goToDate(controller, 2009, 1, 5);
     checkMultiWeekView("month");
 
     // delete event
-    box = getEventBoxPath(controller, "month", EVENT_BOX, 2, 2, null) + EVENTPATH;
+    box = getEventBoxPath("month", EVENT_BOX, 2, 2, null) + EVENTPATH;
     controller.click(lookup(box));
     handleOccurrencePrompt(controller, eid("month-view"), "delete", true, false);
     controller.waitForElementNotPresent(lookup(box));
 
     // reset view
     switchToView(controller, "day");
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "vertical");
 }
 
 function setRecurrence(recurrence) {
-    let { lookup: reclookup, eid: recid } = helpersForController(recurrence);
+    let { sleep: recsleep, lookup: reclookup, eid: recid } =
+        helpersForController(recurrence);
 
     // weekly
     recurrence.waitForElement(recid("period-list"));
-    recurrence.select(recid("period-list"), null, null, "1");
-    recurrence.sleep(sleep);
+    menulistSelect(recid("period-list"), "1", recurrence);
 
-    let mon = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.2.Mmm");
-    let wed = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.4.Mmm");
-    let fri = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.6.Mmm");
+    let mon = cal.l10n.getDateFmtString("day.2.Mmm");
+    let wed = cal.l10n.getDateFmtString("day.4.Mmm");
+    let fri = cal.l10n.getDateFmtString("day.6.Mmm");
 
-    let days = `
-        /id("calendar-event-dialog-recurrence")/id("recurrence-pattern-groupbox")/
-        id("recurrence-pattern-grid")/id("recurrence-pattern-rows")/
-        id("recurrence-pattern-period-row")/id("period-deck")/
-        id("period-deck-weekly-box")/[1]/id("daypicker-weekday")/
-        anon({"anonid":"mainbox"})
-    `;
-
+    // starting from Monday so it should be checked. We have to wait a little,
+    // because the checkedstate is set in background by JS.
+    recurrence.waitFor(() => {
+        return recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${mon}"}`));
+    }, 30000);
     // starting from Monday so it should be checked
-    recurrence.assertChecked(reclookup(`${days}/{"label":"${mon}"}`));
-
+    recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${mon}"}`));
     // check Wednesday and Friday too
-    recurrence.click(reclookup(`${days}/{"label":"${wed}"}`));
-    recurrence.click(reclookup(`${days}/{"label":"${fri}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${wed}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${fri}"}`));
 
     // set until date
-    recurrence.click(recid("recurrence-range-until"));
-    let input = `
-        /id("calendar-event-dialog-recurrence")/id("recurrence-range-groupbox")/[1]/
-        id("recurrence-duration")/id("recurrence-range-until-box")/
-        id("repeat-until-date")/anon({"class":"datepicker-box-class"})/
-        {"class":"datepicker-text-class"}/
-        anon({"class":"menulist-editable-box textbox-input-box"})/
-        anon({"anonid":"input"})
-    `;
+    recurrence.radio(recid("recurrence-range-until"));
 
     // delete previous date
-    recurrence.keypress(reclookup(input), "a", { ctrlKey: true });
-    recurrence.keypress(reclookup(input), "VK_DELETE", {});
+    let untilInput = reclookup(REC_DLG_UNTIL_INPUT);
+    recurrence.keypress(untilInput, "a", { accelKey: true });
+    recurrence.keypress(untilInput, "VK_DELETE", {});
 
     let dateFormatter = cal.getDateFormatter();
 
     let endDateString = dateFormatter.formatDateShort(cal.dtz.jsDateToDateTime(ENDDATE, cal.dtz.floating));
+    recsleep(SHORT_SLEEP);
+    recurrence.type(untilInput, endDateString);
 
-    recurrence.type(reclookup(input), endDateString);
+    recsleep(SHORT_SLEEP);
+    // Move focus to ensure the date is selected
+    recurrence.keypress(untilInput, "VK_TAB", {});
 
     // close dialog
-    recurrence.click(reclookup(`
-        /id("calendar-event-dialog-recurrence")/anon({"anonid":"buttons"})/
-        {"dlgtype":"accept"}
-    `));
+    recurrence.click(reclookup(REC_DLG_ACCEPT));
 }
 
 function checkMultiWeekView(view) {
-    let startWeek = 1;
-
-    // in month view event starts from 2nd row
-    if (view == "month") {
-        startWeek++;
-    }
+    let startWeek = view == "month" ? 2 : 1;
 
     for (let week = startWeek; week < startWeek + 3; week++) {
         // Monday
-        controller.assertNode(
-            lookupEventBox(controller, view, EVENT_BOX, week, 2, null, EVENTPATH)
+        controller.waitForElement(
+            lookupEventBox(view, EVENT_BOX, week, 2, null, EVENTPATH)
         );
-
         // Wednesday
         controller.assertNode(
-            lookupEventBox(controller, view, EVENT_BOX, week, 4, null, EVENTPATH)
+            lookupEventBox(view, EVENT_BOX, week, 4, null, EVENTPATH)
         );
-
         // Friday
         controller.assertNode(
-            lookupEventBox(controller, view, EVENT_BOX, week, 6, null, EVENTPATH)
+            lookupEventBox(view, EVENT_BOX, week, 6, null, EVENTPATH)
         );
     }
 
     // Monday, last occurrence
     controller.assertNode(
-        lookupEventBox(controller, view, EVENT_BOX, startWeek + 3, 2, null, EVENTPATH)
+        lookupEventBox(view, EVENT_BOX, startWeek + 3, 2, null, EVENTPATH)
     );
 
     // Wednesday
     controller.assertNodeNotExist(
-        lookupEventBox(controller, view, EVENT_BOX, startWeek + 3, 4, null, EVENTPATH)
+        lookupEventBox(view, EVENT_BOX, startWeek + 3, 4, null, EVENTPATH)
     );
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/recurrenceRotated/testWeeklyWithExceptionRecurrence.js
+++ b/calendar/test/mozmill/recurrenceRotated/testWeeklyWithExceptionRecurrence.js
@@ -1,46 +1,56 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+var MODULE_NAME = "testWeeklyWithExceptionRecurrence";
 var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils"];
+var MODULE_REQUIRES = ["calendar-utils", "window-helpers"];
+
+var TIMEOUT_MODAL_DIALOG, CALENDARNAME, EVENTPATH, EVENT_BOX;
+var CANVAS_BOX, REC_DLG_ACCEPT, REC_DLG_DAYS;
+var helpersForController, handleOccurrencePrompt, switchToView, goToDate;
+var invokeEventDialog, viewForward, deleteCalendars, createCalendar, setData;
+var menulistSelect;
+var plan_for_modal_dialog, wait_for_modal_dialog;
 
 var { cal } = ChromeUtils.import("resource://calendar/modules/calUtils.jsm", null);
 
-var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var switchToView, goToDate, handleOccurrencePrompt;
-var CALENDARNAME, EVENT_BOX, CANVAS_BOX;
-
-var modalDialog = require("../shared-modules/modal-dialog");
-var utils = require("../shared-modules/utils");
-
-var HOUR = 8;
-var STARTDATE = new Date(2009, 0, 6);
-var EVENTPATH = `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`;
+const HOUR = 8;
+const STARTDATE = new Date(2009, 0, 6);
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
-        helpersForController,
-        invokeEventDialog,
-        createCalendar,
-        deleteCalendars,
-        switchToView,
-        viewForward,
-        goToDate,
-        handleOccurrencePrompt,
+        TIMEOUT_MODAL_DIALOG,
         CALENDARNAME,
         EVENT_BOX,
-        CANVAS_BOX
+        CANVAS_BOX,
+        EVENTPATH,
+        REC_DLG_ACCEPT,
+        REC_DLG_DAYS,
+        helpersForController,
+        handleOccurrencePrompt,
+        switchToView,
+        goToDate,
+        invokeEventDialog,
+        viewForward,
+        deleteCalendars,
+        createCalendar,
+        setData,
+        menulistSelect
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
+    ({ plan_for_modal_dialog, wait_for_modal_dialog } =
+        collector.getModule("window-helpers")
+    );
+
     createCalendar(controller, CALENDARNAME);
 }
 
 function testWeeklyWithExceptionRecurrence() {
     controller.click(eid("calendar-tab-button"));
     switchToView(controller, "day");
     goToDate(controller, 2009, 1, 5);
 
@@ -48,277 +58,247 @@ function testWeeklyWithExceptionRecurren
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "horizontal");
 
     // create weekly recurring event
     let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, HOUR);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
 
-        let dialog = new modalDialog.modalDialog(event.window);
-        dialog.start(setRecurrence);
         event.waitForElement(eventid("item-repeat"));
-        event.select(eventid("item-repeat"), null, null, "custom");
+        plan_for_modal_dialog("Calendar:EventDialog:Recurrence", setRecurrence);
+        menulistSelect(eventid("item-repeat"), "custom", event);
+        wait_for_modal_dialog("Calendar:EventDialog:Recurrence", TIMEOUT_MODAL_DIALOG);
 
         event.click(eventid("button-saveandclose"));
     });
 
     // move 5th January occurrence to 6th January
     eventBox = lookupEventBox("day", EVENT_BOX, null, 1, HOUR, EVENTPATH);
     handleOccurrencePrompt(controller, eventBox, "modify", false, false);
     invokeEventDialog(controller, null, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
 
-        let startDateInput = eventlookup(`
-            /id("calendar-event-dialog")/id("event-grid")/id("event-grid-rows")/
-            id("event-grid-startdate-row")/id("event-grid-startdate-picker-box")/
-            id("event-starttime")/anon({"anonid":"hbox"})/
-            anon({"anonid":"date-picker"})/anon({"class":"datepicker-box-class"})/
-            {"class":"datepicker-text-class"}/
-            anon({"class":"menulist-editable-box textbox-input-box"})/
-            anon({"anonid":"input"})
-        `);
-        let endDateInput = eventlookup(`
-            /id("calendar-event-dialog")/id("event-grid")/id("event-grid-rows")/
-            id("event-grid-enddate-row")/[1]/id("event-grid-enddate-picker-box")/
-            id("event-endtime")/anon({"anonid":"hbox"})/
-            anon({"anonid":"date-picker"})/anon({"class":"datepicker-box-class"})/
-            {"class":"datepicker-text-class"}/
-            anon({"class":"menulist-editable-box textbox-input-box"})/
-            anon({"anonid":"input"})
-        `);
-
-        event.keypress(startDateInput, "a", { ctrlKey: true });
-
-        let dateFormatter = cal.getDateFormatter();
-
-        let startDateString = dateFormatter.formatDateShort(cal.dtz.jsDateToDateTime(STARTDATE, cal.dtz.floating));
-        event.type(startDateInput, startDateString);
-        // applies startdate change
-        event.click(endDateInput);
-
+        setData(event, iframe, { startdate: STARTDATE, enddate: STARTDATE });
         event.click(eventid("button-saveandclose"));
     });
 
     // change recurrence rule
     goToDate(controller, 2009, 1, 7);
     eventBox = lookupEventBox("day", EVENT_BOX, null, 1, HOUR, EVENTPATH);
     handleOccurrencePrompt(controller, eventBox, "modify", true, false);
     invokeEventDialog(controller, null, (event, iframe) => {
         let { eid: eventid } = helpersForController(event);
+        let { lookup: iframelookup } = helpersForController(iframe);
 
-        dialog = new modalDialog.modalDialog(event.window);
-        dialog.start(changeRecurrence);
         event.waitForElement(eventid("item-repeat"));
-        event.select(eventid("item-repeat"), null, null, "custom");
+        plan_for_modal_dialog("Calendar:EventDialog:Recurrence", changeRecurrence);
+        event.click(iframelookup(`
+            /id("calendar-event-dialog-inner")/id("event-grid")/
+            id("event-grid-rows")/id("event-grid-recurrence-row")/
+            id("event-grid-recurrence-picker-box")/id("repeat-deck")/
+            id("repeat-details")/[0]
+        `));
+        wait_for_modal_dialog("Calendar:EventDialog:Recurrence", TIMEOUT_MODAL_DIALOG);
 
         event.click(eventid("button-saveandclose"));
     });
 
     // check two weeks
     // day view
     switchToView(controller, "day");
     let path = getEventBoxPath("day", EVENT_BOX, null, 1, HOUR) + EVENTPATH;
 
     goToDate(controller, 2009, 1, 5);
-    controller.assertNodeNotExist(lookup(path));
+    controller.waitForElementNotPresent(lookup(path));
 
     viewForward(controller, 1);
     let tuesPath = `
         /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
         id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
         id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
         id("day-view")/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
         anon({"anonid":"daybox"})/[0]/anon({"anonid":"boxstack"})/
         anon({"anonid":"topbox"})/{"flex":"1"}/{"flex":"1"}/[eventIndex]
     `;
 
     // assert exactly two
-    controller.assertNode(lookup(tuesPath.replace("eventIndex", "0") + EVENTPATH));
+    controller.waitForElement(lookup(tuesPath.replace("eventIndex", "0") + EVENTPATH));
     controller.assertNode(lookup(tuesPath.replace("eventIndex", "1") + EVENTPATH));
     controller.assertNodeNotExist(lookup(tuesPath.replace("eventIndex", "2") + EVENTPATH));
 
     viewForward(controller, 1);
-    controller.assertNode(lookup(path));
+    controller.waitForElement(lookup(path));
     viewForward(controller, 1);
-    controller.assertNodeNotExist(lookup(path));
+    controller.waitForElementNotPresent(lookup(path));
     viewForward(controller, 1);
-    controller.assertNode(lookup(path));
+    controller.waitForElement(lookup(path));
     viewForward(controller, 1);
-    controller.assertNodeNotExist(lookup(path));
+    controller.waitForElementNotPresent(lookup(path));
     viewForward(controller, 1);
-    controller.assertNodeNotExist(lookup(path));
+    controller.waitForElementNotPresent(lookup(path));
 
     // next week
     viewForward(controller, 1);
-    controller.assertNode(lookup(path));
+    controller.waitForElement(lookup(path));
     viewForward(controller, 1);
-    controller.assertNode(lookup(path));
+    controller.waitForElement(lookup(path));
     viewForward(controller, 1);
-    controller.assertNode(lookup(path));
+    controller.waitForElement(lookup(path));
     viewForward(controller, 1);
-    controller.assertNodeNotExist(lookup(path));
+    controller.waitForElementNotPresent(lookup(path));
     viewForward(controller, 1);
-    controller.assertNode(lookup(path));
+    controller.waitForElement(lookup(path));
     viewForward(controller, 1);
-    controller.assertNodeNotExist(lookup(path));
+    controller.waitForElementNotPresent(lookup(path));
 
     // week view
     switchToView(controller, "week");
     goToDate(controller, 2009, 1, 5);
 
-    path = getEventBoxPath("week", EVENT_BOX, null, 2, HOUR) + EVENTPATH;
-    controller.assertNodeNotExist(lookup(path));
-
     tuesPath = `
         /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
         id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
         id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
         id("week-view")/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
         anon({"anonid":"daybox"})/[dayIndex]/anon({"anonid":"boxstack"})/
         anon({"anonid":"topbox"})/{"flex":"1"}/{"flex":"1"}/[eventIndex]
     `;
+
     // assert exactly two
-    controller.assertNode(
-        lookup(tuesPath.replace("dayIndex", "2").replace("eventIndex", "0") + EVENTPATH)
-    );
-    controller.assertNode(
-        lookup(tuesPath.replace("dayIndex", "2").replace("eventIndex", "1") + EVENTPATH)
-    );
-    controller.assertNodeNotExist(
-        lookup(tuesPath.replace("dayIndex", "2").replace("eventIndex", "2") + EVENTPATH)
-    );
+    controller.waitForElement(lookup(
+        tuesPath.replace("dayIndex", "2").replace("eventIndex", "0") + EVENTPATH
+    ));
+    controller.assertNode(lookup(
+        tuesPath.replace("dayIndex", "2").replace("eventIndex", "1") + EVENTPATH
+    ));
+    controller.assertNodeNotExist(lookup(
+        tuesPath.replace("dayIndex", "2").replace("eventIndex", "2") + EVENTPATH
+    ));
 
+    // wait for the last occurrence because this appears latest.
+    controller.waitForElement(lookupEventBox("week", EVENT_BOX, null, 6, HOUR));
+    controller.assertNodeNotExist(lookupEventBox("week", EVENT_BOX, null, 1, HOUR));
+    controller.assertNodeNotExist(lookupEventBox("week", EVENT_BOX, null, 2, HOUR));
     controller.assertNode(lookupEventBox("week", EVENT_BOX, null, 4, HOUR));
     controller.assertNodeNotExist(lookupEventBox("week", EVENT_BOX, null, 5, HOUR));
-    controller.assertNode(lookupEventBox("week", EVENT_BOX, null, 6, HOUR));
     controller.assertNodeNotExist(lookupEventBox("week", EVENT_BOX, null, 7, HOUR));
 
     viewForward(controller, 1);
+    controller.waitForElement(lookupEventBox("week", EVENT_BOX, null, 6, HOUR));
     controller.assertNodeNotExist(lookupEventBox("week", EVENT_BOX, null, 1, HOUR));
     controller.assertNode(lookupEventBox("week", EVENT_BOX, null, 2, HOUR));
     controller.assertNode(lookupEventBox("week", EVENT_BOX, null, 3, HOUR));
     controller.assertNode(lookupEventBox("week", EVENT_BOX, null, 4, HOUR));
     controller.assertNodeNotExist(lookupEventBox("week", EVENT_BOX, null, 5, HOUR));
-    controller.assertNode(lookupEventBox("week", EVENT_BOX, null, 6, HOUR));
     controller.assertNodeNotExist(lookupEventBox("week", EVENT_BOX, null, 7, HOUR));
 
     // multiweek view
     switchToView(controller, "multiweek");
     goToDate(controller, 2009, 1, 5);
     checkMultiWeekView("multiweek");
 
     // month view
     switchToView(controller, "month");
     checkMultiWeekView("month");
 
     // delete event
     switchToView(controller, "day");
     goToDate(controller, 2009, 1, 12);
     path = getEventBoxPath("day", EVENT_BOX, null, 1, HOUR) + EVENTPATH;
-
     controller.click(lookup(path));
     handleOccurrencePrompt(controller, eid("day-view"), "delete", true, false);
     controller.waitForElementNotPresent(lookup(path));
 
     // reset view
     controller.mainMenu.click("#ltnViewRotated");
     controller.waitFor(() => eid("day-view").getNode().orient == "vertical");
 }
 
 function setRecurrence(recurrence) {
-    let { sleep: recsleep, lookup: reclookup, eid: recid } = helpersForController(recurrence);
+    let { lookup: reclookup, eid: recid } = helpersForController(recurrence);
 
     // weekly
-    recurrence.waitForElement(recid("period-list"));
-    recurrence.select(recid("period-list"), null, null, "1");
-    recsleep();
+    menulistSelect(recid("period-list"), "1", recurrence);
 
-    let mon = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.2.Mmm");
-    let wed = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.4.Mmm");
-    let fri = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.6.Mmm");
+    let mon = cal.l10n.getDateFmtString("day.2.Mmm");
+    let wed = cal.l10n.getDateFmtString("day.4.Mmm");
+    let fri = cal.l10n.getDateFmtString("day.6.Mmm");
 
-    let days = `
-        /id("calendar-event-dialog-recurrence")/id("recurrence-pattern-groupbox")/
-        id("recurrence-pattern-grid")/id("recurrence-pattern-rows")/
-        id("recurrence-pattern-period-row")/id("period-deck")/
-        id("period-deck-weekly-box")/[1]/id("daypicker-weekday")/
-        anon({"anonid":"mainbox"})
-    `;
-
-    // starting from Monday so it should be checked
-    recurrence.assertChecked(reclookup(`${days}/{"label":"${mon}"}`));
+    // starting from Monday so it should be checked. We have to wait a little,
+    // because the checkedstate is set in background by JS.
+    recurrence.waitFor(() => {
+        return recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${mon}"}`));
+    }, 10000);
     // check Wednesday and Friday too
-    recurrence.click(reclookup(`${days}/{"label":"${wed}"}`));
-    recurrence.click(reclookup(`${days}/{"label":"${fri}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${wed}"}`));
+    recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${wed}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${fri}"}`));
+    recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${fri}"}`));
 
     // close dialog
-    recurrence.click(reclookup(`
-        /id("calendar-event-dialog-recurrence")/anon({"anonid":"buttons"})/
-        {"dlgtype":"accept"}
-    `));
+    recurrence.click(reclookup(REC_DLG_ACCEPT));
 }
 
 function changeRecurrence(recurrence) {
-    let { sleep: recsleep, lookup: reclookup, eid: recid } = helpersForController(recurrence);
+    let { lookup: reclookup, eid: recid } = helpersForController(recurrence);
 
     // weekly
-    recurrence.waitForElement(recid("period-list"));
-    recurrence.select(recid("period-list"), null, null, "1");
-    recsleep();
+    menulistSelect(recid("period-list"), "1", recurrence);
 
-    let mon = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.2.Mmm");
-    let tue = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.3.Mmm");
-    let wed = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.4.Mmm");
-    let fri = utils.getProperty("chrome://calendar/locale/dateFormat.properties", "day.6.Mmm");
-
-    let days = `
-        /id("calendar-event-dialog-recurrence")/id("recurrence-pattern-groupbox")/
-        id("recurrence-pattern-grid")/id("recurrence-pattern-rows")/
-        id("recurrence-pattern-period-row")/id("period-deck")/
-        id("period-deck-weekly-box")/[1]/id("daypicker-weekday")/
-        anon({"anonid":"mainbox"})/
-     `;
+    let mon = cal.l10n.getDateFmtString("day.2.Mmm");
+    let tue = cal.l10n.getDateFmtString("day.3.Mmm");
+    let wed = cal.l10n.getDateFmtString("day.4.Mmm");
+    let fri = cal.l10n.getDateFmtString("day.6.Mmm");
 
     // check old rule
-    recurrence.assertChecked(reclookup(`${days}/{"label":"${mon}"}`));
-    recurrence.assertChecked(reclookup(`${days}/{"label":"${wed}"}`));
-    recurrence.assertChecked(reclookup(`${days}/{"label":"${fri}"}`));
+    // starting from Monday so it should be checked. We have to wait a little,
+    // because the checkedstate is set in background by JS.
+    recurrence.waitFor(() => {
+        return recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${mon}"}`));
+    }, 10000);
+    recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${wed}"}`));
+    recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${fri}"}`));
 
     // check Tuesday
-    recurrence.click(reclookup(`${days}/{"label":"${tue}"}`));
+    recurrence.click(reclookup(`${REC_DLG_DAYS}/{"label":"${tue}"}`));
+    recurrence.assertChecked(reclookup(`${REC_DLG_DAYS}/{"label":"${tue}"}`));
 
     // close dialog
-    recurrence.click(reclookup(`
-        /id("calendar-event-dialog-recurrence")/anon({"anonid":"buttons"})/
-        {"dlgtype":"accept"}
-    `));
+    recurrence.click(reclookup(REC_DLG_ACCEPT));
 }
 
 function checkMultiWeekView(view) {
     let startWeek = view == "multiweek" ? 1 : 2;
+    let assertNodeLookup = (...args) => {
+        return controller.assertNode(lookupEventBox(...args));
+    };
+    let assertNodeNotExistLookup = (...args) => {
+        return controller.assertNodeNotExist(lookupEventBox(...args));
+    };
 
-    controller.assertNodeNotExist(
-        lookupEventBox(view, EVENT_BOX, startWeek, 2, HOUR, EVENTPATH)
-    );
-
+    // wait for the first items, then check the ones not to be present
     // assert exactly two
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 3, HOUR, "/[0]"));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 3, HOUR, "/[1]"));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 3, HOUR, "/[2]"));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 4, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 5, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 6, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 7, HOUR, EVENTPATH));
+    controller.waitForElement(
+        lookupEventBox(view, EVENT_BOX, startWeek, 3, HOUR, "/[0]")
+    );
+    assertNodeLookup(view, EVENT_BOX, startWeek, 3, HOUR, "/[1]");
+    assertNodeNotExistLookup(view, EVENT_BOX, startWeek, 3, HOUR, "/[2]");
+    // Then check no item on the 5th.
+    assertNodeNotExistLookup(view, EVENT_BOX, startWeek, 2, HOUR, EVENTPATH);
+    assertNodeNotExistLookup(view, EVENT_BOX, startWeek, 3, HOUR, "/[2]");
+    assertNodeLookup(view, EVENT_BOX, startWeek, 4, HOUR, EVENTPATH);
+    assertNodeNotExistLookup(view, EVENT_BOX, startWeek, 5, HOUR, EVENTPATH);
+    assertNodeLookup(view, EVENT_BOX, startWeek, 6, HOUR, EVENTPATH);
+    assertNodeNotExistLookup(view, EVENT_BOX, startWeek, 7, HOUR, EVENTPATH);
 
-    startWeek++;
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 1, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 2, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 3, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 4, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 5, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 6, HOUR, EVENTPATH));
-    controller.assertNode(lookupEventBox(view, EVENT_BOX, startWeek, 7, HOUR, EVENTPATH));
+    assertNodeNotExistLookup(view, EVENT_BOX, startWeek + 1, 1, HOUR, EVENTPATH);
+    assertNodeLookup(view, EVENT_BOX, startWeek + 1, 2, HOUR, EVENTPATH);
+    assertNodeLookup(view, EVENT_BOX, startWeek + 1, 3, HOUR, EVENTPATH);
+    assertNodeLookup(view, EVENT_BOX, startWeek + 1, 4, HOUR, EVENTPATH);
+    assertNodeNotExistLookup(view, EVENT_BOX, startWeek + 1, 5, HOUR, EVENTPATH);
+    assertNodeLookup(view, EVENT_BOX, startWeek + 1, 6, HOUR, EVENTPATH);
+    assertNodeNotExistLookup(view, EVENT_BOX, startWeek + 1, 7, HOUR, EVENTPATH);
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/shared-modules/test-calendar-utils.js
+++ b/calendar/test/mozmill/shared-modules/test-calendar-utils.js
@@ -152,18 +152,16 @@ function handleAddingAttachment(controll
     plan_for_modal_dialog("commonDialog", (attachment) => {
         let { lookup: cdlglookup, eid: cdlgid } = helpersForController(attachment);
         attachment.waitForElement(cdlgid("loginTextbox"));
         cdlgid("loginTextbox").getNode().value = url;
         attachment.click(cdlglookup(`
             /id("commonDialog")/anon({"anonid":"buttons"})/{"dlgtype":"accept"}
         `));
     });
-
-    wait_for_modal_dialog("commonDialog");
 }
 
 /**
  * open and click the appropriate button on the recurrence-Prompt Dialog
  *
  * @param controller      Mozmill window controller
  * @param element         Mozmill element which will open the dialog
  * @param mode            action to exec on element (delete OR modify)
@@ -595,17 +593,16 @@ function findEventsInNode(node, eventNod
  *                          description - event/task description
  *                          category - category label
  *                          calendar - calendar the item should be in
  *                          allday - boolean value
  *                          startdate - Date object
  *                          starttime - Date object
  *                          enddate - Date object
  *                          endtime - Date object
- *                          timezone - false for local, true for set timezone
  *                          repeat - reccurrence value, one of none/daily/weekly/
  *                                   every.weekday/bi.weekly/
  *                                   monthly/yearly
  *                                   (custom is not supported)
  *                          reminder - reminder option index (custom not supp.)
  *                          priority - none/low/normal/high
  *                          privacy - public/confidential/private
  *                          status - none/tentative/confirmed/canceled for events
@@ -664,22 +661,16 @@ function setData(dialog, iframe, data) {
     let endTimeInput = iframeLookup(`
         ${innerFrame}/id("event-grid-enddate-row")/[1]/
         id("event-grid-enddate-picker-box")/id("${endId}")/${timeInput}
     `);
     let completedDateInput = iframeLookup(`
         ${innerFrame}/id("event-grid-todo-status-row")/
         id("event-grid-todo-status-picker-box")/id("completed-date-picker")/${dateInput}
     `);
-    let percentCompleteInput = iframeLookup(`
-        ${innerFrame}/id("event-grid-todo-status-row")/
-        id("event-grid-todo-status-picker-box")/id("percent-complete-textbox")/
-        anon({"class":"textbox-input-box numberbox-input-box"})/
-        anon({"anonid":"input"})
-    `);
     let dateFormatter = cal.getDateFormatter();
     // wait for input elements' values to be populated
     sleep();
 
     // title
     if (data.title != undefined) {
         // we need to set directly here in case there is already a title.
         // accelKey+a won't work in all OS
@@ -704,23 +695,16 @@ function setData(dialog, iframe, data) {
         menulistSelect(iframeId("item-calendar"), data.calendar, dialog);
     }
 
     // all-day
     if (data.allday != undefined && isEvent) {
         dialog.check(iframeId("event-all-day"), data.allday);
     }
 
-    // timezone
-    if (data.timezone != undefined) {
-        let menuitem = iframeId("options-timezones-menuitem");
-        menuitem.getNode().setAttribute("checked", data.timezone);
-        dialog.click(menuitem);
-    }
-
     // startdate
     if (data.startdate != undefined && data.startdate.constructor.name == "Date") {
         let startdate = dateFormatter.formatDateShort(cal.dtz.jsDateToDateTime(data.startdate, cal.dtz.floating));
 
         if (!isEvent) {
             dialog.check(iframeId("todo-has-entrydate"), true);
         }
         dialog.keypress(startDateInput, "a", { accelKey: true });
@@ -759,20 +743,17 @@ function setData(dialog, iframe, data) {
     // TODO: menulistSelect does not work here, because menuitems have no value.
     // will be fixed with Bug 984044
     if (data.reminder != undefined) {
         menulistSelect(iframeId("item-alarm"), data.reminder, dialog);
     }
 
     // description
     if (data.description != undefined) {
-        let descField = iframeLookup(`
-            ${innerFrame}/id("event-grid-description-row")/id("item-description")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `);
+        let descField = iframeId("item-description");
         descField.getNode().value = data.description;
     }
 
     // priority
     if (data.priority != undefined) {
         dialog.mainMenu.click(`#options-priority-${data.priority}-label`);
     }
 
@@ -801,31 +782,32 @@ function setData(dialog, iframe, data) {
         }
     }
 
     // percent complete
     if (data.percent != undefined &&
         (currentStatus == "NEEDS-ACTION" ||
          currentStatus == "IN-PROCESS" ||
          currentStatus == "COMPLETED")) {
-        percentCompleteInput.getNode().value = data.percent;
+        iframeId("percent-complete-textbox").getNode().value = data.percent;
     }
 
     // free/busy
     if (data.freebusy != undefined) {
         dialog.mainMenu.click(`#options-freebusy-${data.freebusy}-menuitem`);
     }
 
     // attachment
     // TODO: Needs fixing,
     // will be fixed with Bug 984044
     if (data.attachment != undefined) {
         if (data.attachment.add != undefined) {
             handleAddingAttachment(dialog, data.attachment.add);
             dialog.click(eid("button-url"));
+            wait_for_modal_dialog("commonDialog");
         }
         if (data.attachment.delete != undefined) {
             dialog.click(iframeLookup(`
                 ${innerFrame}/id("event-grid-attachment-row")/id("attachment-link")/
                 {"label":"${data.attachment.delete}"}
             `));
             dialog.keypress(iframeId("attachment-link"), "VK_DELETE", {});
         }
deleted file mode 100755
--- a/calendar/test/mozmill/shared-modules/test-timezone-utils.js
+++ /dev/null
@@ -1,128 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var MODULE_NAME = "timezone-utils";
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils"];
-
-ChromeUtils.import("resource://gre/modules/Preferences.jsm");
-
-var DATES = [
-    [2009, 1, 1], [2009, 4, 2], [2009, 4, 16], [2009, 4, 30],
-    [2009, 7, 2], [2009, 10, 15], [2009, 10, 29], [2009, 11, 5]
-];
-
-var TIMEZONES = [
-    "America/St_Johns", "America/Caracas", "America/Phoenix", "America/Los_Angeles",
-    "America/Argentina/Buenos_Aires", "Europe/Paris", "Asia/Kathmandu", "Australia/Adelaide"
-];
-
-var helpersForController, goToDate, viewForward, viewBack, findEventsInNode;
-
-function setupModule() {
-    ({ helpersForController, goToDate, viewForward, viewBack, findEventsInNode } =
-        collector.getModule("calendar-utils"));
-}
-
-function installInto(module) {
-    // copy constants into module
-    module.DATES = DATES;
-    module.TIMEZONES = TIMEZONES;
-
-    // Now copy helper functions
-    module.switchAppTimezone = switchAppTimezone;
-    module.verify = verify;
-}
-
-
-function switchAppTimezone(timezone) {
-    // change directly as Mac has different Lookup & XPath than Windows & Linux, bug 536605
-    Preferences.set("calendar.timezone.local", timezone);
-}
-
-function verify(controller, dates, timezones, times) {
-    function* datetimes() {
-        for (let idx = 0; idx < dates.length; idx++) {
-            yield [dates[idx][0], dates[idx][1], dates[idx][2], times[idx]];
-        }
-    }
-
-    let { lookup } = helpersForController(controller);
-
-    let dayView = `
-        /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
-        id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
-        id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
-        id("day-view")
-    `;
-    let dayStack = `
-        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"daybox"})/[0]/anon({"anonid":"boxstack"})/
-        anon({"anonid":"topbox"})/{"flex":"1"}
-    `;
-    let timeLine = `
-        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"timebar"})/anon({"anonid":"topbox"})
-    `;
-    let allowedDifference = 3;
-
-    /* Event box' time can't be deduced from it's position in                    ----------------
-       xul element tree because for each event a box is laid over whole day and  |___spacer_____|
-       a spacer is added to push the event to it's correct location.             |__event_box___|
-       But timeline can be used to retrieve the position of a particular hour    |day continues |
-       on screen and it can be compared against the position of the event.       ----------------
-    */
-
-    for (let [selectedYear, selectedMonth, selectedDay, selectedTime] of datetimes()) {
-        goToDate(controller, selectedYear, selectedMonth, selectedDay);
-
-        // find event with timezone tz
-        for (let tzIdx = 0; tzIdx < timezones.length; tzIdx++) {
-            let [correctHour, minutes, day] = selectedTime[tzIdx];
-            let found = false;
-
-            let timeNode = lookup(`${timeLine}/[${correctHour}]`).getNode();
-            let timeY = timeNode.boxObject.y + timeNode.boxObject.height * (minutes / 60);
-
-            let stackNode;
-            let eventNodes = [];
-
-            // same day
-            if (day == undefined) {
-                stackNode = lookup(dayStack).getNode();
-            }
-
-            // following day
-            if (day != undefined && day == 1) {
-                viewForward(controller, 1);
-                stackNode = lookup(dayStack).getNode();
-            }
-
-            // previous day
-            if (day != undefined && day == -1) {
-                viewBack(controller, 1);
-                stackNode = lookup(dayStack).getNode();
-            }
-
-            findEventsInNode(stackNode, eventNodes);
-
-            for (let node of eventNodes) {
-                if (Math.abs(timeY - node.boxObject.y) < allowedDifference &&
-                    timezones[tzIdx] == node.mOccurrence.title) {
-                    found = true;
-                    break;
-                }
-            }
-
-            if (day != undefined && day == 1) {
-                viewBack(controller, 1);
-            }
-
-            if (day != undefined && day == -1) {
-                viewForward(controller, 1);
-            }
-            controller.assertJS(found);
-        }
-    }
-}
--- a/calendar/test/mozmill/testAlarmDefaultValue.js
+++ b/calendar/test/mozmill/testAlarmDefaultValue.js
@@ -64,17 +64,17 @@ function testDefaultAlarms() {
 
     // Create New Task
     controller.click(eid("newMsgButton-task-menuitem"));
     invokeEventDialog(controller, null, (task, iframe) => {
         let { xpath: taskpath, eid: taskid } = helpersForController(task);
 
         // Check if the "custom" item was selected
         task.assertDOMProperty(taskid("item-alarm"), "value", "custom");
-        reminderDetailsVisible = taskpath(`
+        let reminderDetailsVisible = taskpath(`
             //*[@id="reminder-details"]/
             *[local-name()="label" and (not(@hidden) or @hidden="false")]
         `);
         task.assertDOMProperty(reminderDetailsVisible, "value", expectedTaskReminder);
 
         // Close the task dialog
         task.window.close();
     });
rename from calendar/test/mozmill/timezoneTests/test2.js
rename to calendar/test/mozmill/testTimezones.js
--- a/calendar/test/mozmill/timezoneTests/test2.js
+++ b/calendar/test/mozmill/testTimezones.js
@@ -1,89 +1,339 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
+var MODULE_NAME = "testTimezones";
+var RELATIVE_ROOT = "./shared-modules";
+var MODULE_REQUIRES = ["calendar-utils"];
 
 var helpersForController, invokeEventDialog, switchToView, goToDate, setData;
-var CANVAS_BOX;
-var switchAppTimezone, TIMEZONES;
+var findEventsInNode, viewForward, viewBack, CANVAS_BOX, TIMEOUT_MODAL_DIALOG;
+var plan_for_modal_dialog, wait_for_modal_dialog;
 
-var modalDialog = require("../shared-modules/modal-dialog");
+var DATES = [
+    [2009, 1, 1], [2009, 4, 2], [2009, 4, 16], [2009, 4, 30],
+    [2009, 7, 2], [2009, 10, 15], [2009, 10, 29], [2009, 11, 5]
+];
 
-var times = [[4, 30], [4, 30], [3, 0], [3, 0], [9, 0], [14, 0], [19, 45], [1, 30]];
+var TIMEZONES = [
+    "America/St_Johns", "America/Caracas", "America/Phoenix", "America/Los_Angeles",
+    "America/Argentina/Buenos_Aires", "Europe/Paris", "Asia/Kathmandu", "Australia/Adelaide"
+];
+
+ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
         helpersForController,
         invokeEventDialog,
         switchToView,
         goToDate,
         setData,
-        CANVAS_BOX
+        findEventsInNode,
+        viewForward,
+        viewBack,
+        CANVAS_BOX,
+        TIMEOUT_MODAL_DIALOG
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
-    ({ switchAppTimezone, TIMEZONES } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
+
+    ({ plan_for_modal_dialog, wait_for_modal_dialog } = collector.getModule("window-helpers"));
+}
+
+function testTimezones1_SetGMT() {
+    Preferences.set("calendar.timezone.local", "Europe/London");
 }
 
 function testTimezones2_CreateEvents() {
     controller.click(eid("calendar-tab-button"));
     switchToView(controller, "day");
     goToDate(controller, 2009, 1, 1);
 
-    // create daily recurring events in all TIMEZONES
+    // create weekly recurring events in all TIMEZONES
+    let times = [[4, 30], [4, 30], [3, 0], [3, 0], [9, 0], [14, 0], [19, 45], [1, 30]];
     let time = new Date();
     for (let i = 0; i < TIMEZONES.length; i++) {
-        let eventBox = lookupEventBox(controller, "day", CANVAS_BOX, null, 1, i + 8);
+        let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, i + 8);
         invokeEventDialog(controller, eventBox, (event, iframe) => {
             time.setHours(times[i][0]);
             time.setMinutes(times[i][1]);
 
             // set timezone
             setTimezone(event, TIMEZONES[i]);
 
             // set title and repeat
-            setData(event, { title: TIMEZONES[i], repeat: "weekly", starttime: time });
+            setData(event, iframe, { title: TIMEZONES[i], repeat: "weekly", starttime: time });
 
             // save
+            let { eid: eventid } = helpersForController(event);
             event.click(eventid("button-saveandclose"));
         });
     }
 }
 
+function testTimezones3_checkStJohns() {
+    Preferences.set("calendar.timezone.local", "America/St_Johns");
+    let times = [
+        [[4, 30], [5, 30], [6, 30], [7, 30], [8, 30], [9, 30], [10, 30], [11, 30]],
+        [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [12, 30]],
+        [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [13, 30]],
+        [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [13, 30]],
+        [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [13, 30]],
+        [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [12, 30]],
+        [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [10, 30], [11, 30], [12, 30]],
+        [[4, 30], [5, 30], [6, 30], [7, 30], [8, 30], [9, 30], [10, 30], [11, 30]]
+    ];
+    controller.click(eid("calendar-tab-button"));
+    switchToView(controller, "day");
+    goToDate(controller, 2009, 1, 1);
+
+    verify(controller, DATES, TIMEZONES, times);
+}
+
+function testTimezones4_checkCaracas() {
+    Preferences.set("calendar.timezone.local", "America/Caracas");
+    // This is actually incorrect. Venezuela shifted clocks forward 30 minutes
+    // in 2016, but our code doesn't handle historical timezones.
+    let times = [
+        [[4, 0], [5, 0], [6, 0], [7, 0], [8, 0], [9, 0], [10, 0], [11, 0]],
+        [[3, 0], [5, 0], [6, 0], [6, 0], [8, 0], [8, 0], [10, 0], [11, 0]],
+        [[3, 0], [5, 0], [6, 0], [6, 0], [8, 0], [8, 0], [10, 0], [12, 0]],
+        [[3, 0], [5, 0], [6, 0], [6, 0], [8, 0], [8, 0], [10, 0], [12, 0]],
+        [[3, 0], [5, 0], [6, 0], [6, 0], [8, 0], [8, 0], [10, 0], [12, 0]],
+        [[3, 0], [5, 0], [6, 0], [6, 0], [8, 0], [8, 0], [10, 0], [11, 0]],
+        [[3, 0], [5, 0], [6, 0], [6, 0], [8, 0], [9, 0], [10, 0], [11, 0]],
+        [[4, 0], [5, 0], [6, 0], [7, 0], [8, 0], [9, 0], [10, 0], [11, 0]]
+    ];
+    controller.click(eid("calendar-tab-button"));
+    switchToView(controller, "day");
+    goToDate(controller, 2009, 1, 1);
+
+    verify(controller, DATES, TIMEZONES, times);
+}
+
+function testTimezones5_checkPhoenix() {
+    Preferences.set("calendar.timezone.local", "America/Phoenix");
+    let times = [
+        [[1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [8, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [8, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [6, 0], [7, 0], [8, 0]],
+        [[1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0]]
+    ];
+    controller.click(eid("calendar-tab-button"));
+    switchToView(controller, "day");
+    goToDate(controller, 2009, 1, 1);
+
+    verify(controller, DATES, TIMEZONES, times);
+}
+
+function testTimezones6_checkLosAngeles() {
+    Preferences.set("calendar.timezone.local", "America/Los_Angeles");
+    let times = [
+        [[0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [8, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [8, 0]],
+        [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [6, 0], [7, 0], [8, 0]],
+        [[0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0]]
+    ];
+    controller.click(eid("calendar-tab-button"));
+    switchToView(controller, "day");
+    goToDate(controller, 2009, 1, 1);
+
+    verify(controller, DATES, TIMEZONES, times);
+}
+
+function testTimezones7_checkBuenosAires() {
+    Preferences.set("calendar.timezone.local", "America/Argentina/Buenos_Aires");
+    let times = [
+        [[5, 0], [6, 0], [7, 0], [8, 0], [9, 0], [10, 0], [11, 0], [12, 0]],
+        [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [12, 0]],
+        [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [13, 0]],
+        [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [13, 0]],
+        [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [13, 0]],
+        [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [12, 0]],
+        [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [10, 0], [11, 0], [12, 0]],
+        [[5, 0], [6, 0], [7, 0], [8, 0], [9, 0], [10, 0], [11, 0], [12, 0]]
+    ];
+    controller.click(eid("calendar-tab-button"));
+    switchToView(controller, "day");
+    goToDate(controller, 2009, 1, 1);
+
+    verify(controller, DATES, TIMEZONES, times);
+}
+
+function testTimezones8_checkParis() {
+    Preferences.set("calendar.timezone.local", "Europe/Paris");
+    let times = [
+        [[9, 0], [10, 0], [11, 0], [12, 0], [13, 0], [14, 0], [15, 0], [16, 0]],
+        [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [17, 0]],
+        [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [18, 0]],
+        [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [18, 0]],
+        [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [18, 0]],
+        [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [17, 0]],
+        [[8, 0], [10, 0], [11, 0], [11, 0], [13, 0], [14, 0], [15, 0], [16, 0]],
+        [[9, 0], [10, 0], [11, 0], [12, 0], [13, 0], [14, 0], [15, 0], [16, 0]]
+    ];
+    controller.click(eid("calendar-tab-button"));
+    switchToView(controller, "day");
+    goToDate(controller, 2009, 1, 1);
+
+    verify(controller, DATES, TIMEZONES, times);
+}
+
+function testTimezones9_checkKathmandu() {
+    Preferences.set("calendar.timezone.local", "Asia/Kathmandu");
+    let times = [
+        [[13, 45], [14, 45], [15, 45], [16, 45], [17, 45], [18, 45], [19, 45], [20, 45]],
+        [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [20, 45]],
+        [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [21, 45]],
+        [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [21, 45]],
+        [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [21, 45]],
+        [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [20, 45]],
+        [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [18, 45], [19, 45], [20, 45]],
+        [[13, 45], [14, 45], [15, 45], [16, 45], [17, 45], [18, 45], [19, 45], [20, 45]]
+    ];
+    controller.click(eid("calendar-tab-button"));
+    switchToView(controller, "day");
+    goToDate(controller, 2009, 1, 1);
+
+    verify(controller, DATES, TIMEZONES, times);
+}
+
+function testTimezones10_checkAdelaide() {
+    Preferences.set("calendar.timezone.local", "Australia/Adelaide");
+    let times = [
+        [[18, 30], [19, 30], [20, 30], [21, 30], [22, 30], [23, 30], [0, 30, +1], [1, 30, +1]],
+        [[17, 30], [19, 30], [20, 30], [20, 30], [22, 30], [22, 30], [0, 30, +1], [1, 30, +1]],
+        [[16, 30], [18, 30], [19, 30], [19, 30], [21, 30], [21, 30], [23, 30], [1, 30, +1]],
+        [[16, 30], [18, 30], [19, 30], [19, 30], [21, 30], [21, 30], [23, 30], [1, 30, +1]],
+        [[16, 30], [18, 30], [19, 30], [19, 30], [21, 30], [21, 30], [23, 30], [1, 30, +1]],
+        [[17, 30], [19, 30], [20, 30], [20, 30], [22, 30], [22, 30], [0, 30, +1], [1, 30, +1]],
+        [[17, 30], [19, 30], [20, 30], [20, 30], [22, 30], [23, 30], [0, 30, +1], [1, 30, +1]],
+        [[18, 30], [19, 30], [20, 30], [21, 30], [22, 30], [23, 30], [0, 30, +1], [1, 30, +1]]
+    ];
+    controller.click(eid("calendar-tab-button"));
+    switchToView(controller, "day");
+    goToDate(controller, 2009, 1, 1);
+
+    verify(controller, DATES, TIMEZONES, times);
+}
+
 function teardownTest(module) {
-    switchAppTimezone(TIMEZONES[0]);
 }
 
 function setTimezone(event, timezone) {
     let { eid: eventid } = helpersForController(event);
 
-    // for some reason setting checked is needed, no other menuitem with checkbox needs it
-    let menuitem = eventid("options-TIMEZONES-menuitem");
-    event.waitForElement(menuitem);
-    menuitem.getNode().setAttribute("checked", "true");
-    event.click(menuitem);
+    if (eventid("timezone-starttime").getNode().collapsed) {
+        let menuitem = eventid("options-timezones-menuitem");
+        event.click(menuitem);
+    }
 
-    let modal = new modalDialog.modalDialog(event.window);
-    modal.start(eventCallback.bind(null, timezone));
+    plan_for_modal_dialog("Calendar:EventDialog:Timezone", eventCallback.bind(null, timezone));
     event.waitForElement(eventid("timezone-starttime"));
     event.click(eventid("timezone-starttime"));
+    event.click(eventid("timezone-starttime"));
+    event.waitForElement(eventid("timezone-custom-menuitem"));
+    event.click(eventid("timezone-custom-menuitem"));
+    wait_for_modal_dialog("Calendar:EventDialog:Timezone", TIMEOUT_MODAL_DIALOG);
 }
 
 function eventCallback(zone, tzcontroller) {
     let { lookup: tzlookup, xpath: tzpath } = helpersForController(tzcontroller);
 
     let item = tzpath(`
         /*[name()='dialog']/*[name()='menulist'][1]/*[name()='menupopup'][1]/
         *[@value='${zone}']
     `);
     tzcontroller.waitForElement(item);
     tzcontroller.click(item);
     tzcontroller.click(tzlookup(`
         /id("calendar-event-dialog-timezone")/anon({"anonid":"buttons"})/
         {"dlgtype":"accept"}
     `));
 }
+
+function verify(controller, dates, timezones, times) {
+    function* datetimes() {
+        for (let idx = 0; idx < dates.length; idx++) {
+            yield [dates[idx][0], dates[idx][1], dates[idx][2], times[idx]];
+        }
+    }
+
+    let { lookup } = helpersForController(controller);
+
+    let dayView = `
+        /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
+        id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
+        id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
+        id("day-view")
+    `;
+    let dayStack = `
+        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
+        anon({"anonid":"daybox"})/[0]/anon({"anonid":"boxstack"})/
+        anon({"anonid":"topbox"})/{"flex":"1"}
+    `;
+    let timeLine = `
+        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
+        anon({"anonid":"timebar"})/anon({"anonid":"topbox"})
+    `;
+    let allowedDifference = 3;
+
+    /* Event box' time can't be deduced from it's position in                    ----------------
+       xul element tree because for each event a box is laid over whole day and  |___spacer_____|
+       a spacer is added to push the event to it's correct location.             |__event_box___|
+       But timeline can be used to retrieve the position of a particular hour    |day continues |
+       on screen and it can be compared against the position of the event.       ----------------
+    */
+
+    for (let [selectedYear, selectedMonth, selectedDay, selectedTime] of datetimes()) {
+        goToDate(controller, selectedYear, selectedMonth, selectedDay);
+
+        // find event with timezone tz
+        for (let tzIdx = 0; tzIdx < timezones.length; tzIdx++) {
+            let [correctHour, minutes, day] = selectedTime[tzIdx];
+
+            let timeNode = lookup(`${timeLine}/[${correctHour}]`).getNode();
+            let timeY = timeNode.boxObject.y + timeNode.boxObject.height * (minutes / 60);
+
+            let eventNodes = [];
+
+            // following day
+            if (day == 1) {
+                viewForward(controller, 1);
+            } else if (day == -1) {
+                viewBack(controller, 1);
+            }
+
+            let stackNode = lookup(dayStack);
+            controller.waitForElement(stackNode);
+            stackNode = stackNode.getNode();
+
+            findEventsInNode(stackNode, eventNodes);
+            eventNodes = eventNodes.filter(node => node.mOccurrence.title == timezones[tzIdx])
+                                   .map(node => node.boxObject.y);
+
+            dump(`Looking for ${timezones[tzIdx]} at ${timeY}: found `);
+            dump(eventNodes.join(", ") + "\n");
+
+            if (day != undefined && day == 1) {
+                viewBack(controller, 1);
+            }
+
+            if (day != undefined && day == -1) {
+                viewForward(controller, 1);
+            }
+
+            controller.assertJS(() => eventNodes.some(node => Math.abs(timeY - node) < allowedDifference));
+        }
+    }
+}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test1.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var createCalendar, CALENDARNAME;
-var switchAppTimezone;
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-    ({ createCalendar, CALENDARNAME } = collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-    ({ switchAppTimezone } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-    createCalendar(controller, CALENDARNAME);
-}
-
-function testTimezones1_SetGMT() {
-    switchAppTimezone("Europe/London");
-}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test10.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var helpersForController, switchToView, goToDate, deleteCalendars, CALENDARNAME;
-var verify, DATES, TIMEZONES;
-
-var { preferences } = require("../shared-modules/prefs");
-
-/* rows - dates
-   columns - correct time for each event */
-var times = [
-    [[18, 30], [19, 30], [20, 30], [21, 30], [22, 30], [23, 30], [0, 30, +1], [1, 30, +1]],
-    [[17, 30], [19, 30], [20, 30], [20, 30], [22, 30], [22, 30], [0, 30, +1], [1, 30, +1]],
-    [[16, 30], [18, 30], [19, 30], [19, 30], [21, 30], [21, 30], [23, 30], [1, 30, +1]],
-    [[16, 30], [18, 30], [19, 30], [19, 30], [21, 30], [21, 30], [23, 30], [1, 30, +1]],
-    [[16, 30], [18, 30], [19, 30], [19, 30], [21, 30], [21, 30], [23, 30], [1, 30, +1]],
-    [[17, 30], [19, 30], [20, 30], [20, 30], [22, 30], [22, 30], [0, 30, +1], [1, 30, +1]],
-    [[17, 30], [19, 30], [20, 30], [20, 30], [22, 30], [23, 30], [0, 30, +1], [1, 30, +1]],
-    [[18, 30], [19, 30], [20, 30], [21, 30], [22, 30], [23, 30], [0, 30, +1], [1, 30, +1]]
-];
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-    ({ helpersForController, switchToView, goToDate, deleteCalendars, CALENDARNAME } =
-        collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-    Object.assign(module, helpersForController(controller));
-
-    ({ verify, DATES, TIMEZONES } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-}
-
-function testTimezones10_checkAdelaide() {
-    controller.click(eid("calendar-tab-button"));
-    switchToView(controller, "day");
-    goToDate(controller, 2009, 1, 1);
-
-    verify(controller, dates, DATES, TIMEZONES, times);
-}
-
-function teardownTest(module) {
-    preferences.clearUserPref("calendar.timezone.local");
-    deleteCalendars(controller, CALENDARNAME);
-}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test3.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var helpersForController, switchToView, goToDate;
-var switchAppTimezone, verify, DATES, TIMEZONES;
-
-/* rows - dates
-   columns - correct time for each event */
-var times = [
-    [[4, 30], [5, 30], [6, 30], [7, 30], [8, 30], [9, 30], [10, 30], [11, 30]],
-    [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [12, 30]],
-    [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [13, 30]],
-    [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [13, 30]],
-    [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [13, 30]],
-    [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [9, 30], [11, 30], [12, 30]],
-    [[4, 30], [6, 30], [7, 30], [7, 30], [9, 30], [10, 30], [11, 30], [12, 30]],
-    [[4, 30], [5, 30], [6, 30], [7, 30], [8, 30], [9, 30], [10, 30], [11, 30]]
-];
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-
-    ({ helpersForController, switchToView, goToDate } =
-        collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-
-    Object.assign(module, helpersForController(controller));
-    ({ switchAppTimezone, verify, DATES, TIMEZONES } =
-        collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-}
-
-function testTimezones3_checkStJohns() {
-    controller.click(eid("calendar-tab-button"));
-    switchToView(controller, "day");
-    goToDate(controller, 2009, 1, 1);
-
-    verify(controller, DATES, TIMEZONES, times);
-}
-
-function teardownTest(module) {
-    switchAppTimezone(TIMEZONES[1]);
-}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test4.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var helpersForController, switchToView, goToDate;
-var switchAppTimezone, verify, DATES, TIMEZONES;
-
-/* rows - dates
-   columns - correct time for each event */
-var times = [
-    [[3, 30], [4, 30], [5, 30], [6, 30], [7, 30], [8, 30], [9, 30], [10, 30]],
-    [[2, 30], [4, 30], [5, 30], [5, 30], [7, 30], [7, 30], [9, 30], [10, 30]],
-    [[2, 30], [4, 30], [5, 30], [5, 30], [7, 30], [7, 30], [9, 30], [11, 30]],
-    [[2, 30], [4, 30], [5, 30], [5, 30], [7, 30], [7, 30], [9, 30], [11, 30]],
-    [[2, 30], [4, 30], [5, 30], [5, 30], [7, 30], [7, 30], [9, 30], [11, 30]],
-    [[2, 30], [4, 30], [5, 30], [5, 30], [7, 30], [7, 30], [9, 30], [10, 30]],
-    [[2, 30], [4, 30], [5, 30], [5, 30], [7, 30], [8, 30], [9, 30], [10, 30]],
-    [[3, 30], [4, 30], [5, 30], [6, 30], [7, 30], [8, 30], [9, 30], [10, 30]]
-];
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-
-    ({ helpersForController, switchToView, goToDate } =
-        collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-    Object.assign(module, helpersForController(controller));
-
-    ({ switchAppTimezone, verify, DATES, TIMEZONES } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-}
-
-function testTimezones4_checkCaracas() {
-    controller.click(eid("calendar-tab-button"));
-    switchToView(controller, "day");
-    goToDate(controller, 2009, 1, 1);
-
-    verify(controller, DATES, TIMEZONES, times);
-}
-
-function teardownTest(module) {
-    switchAppTimezone(TIMEZONES[2]);
-}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test5.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var helpersForController, switchToView, goToDate;
-var switchAppTimezone, verify, DATES, TIMEZONES;
-
-/* rows - dates
-   columns - correct time for each event */
-var times = [
-    [[1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [8, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [8, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [6, 0], [7, 0], [8, 0]],
-    [[1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0]]
-];
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-    ({ helpersForController, switchToView, goToDate } =
-        collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-    Object.assign(module, helpersForController(controller));
-    ({ switchAppTimezone, verify, DATES, TIMEZONES } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-}
-
-function testTimezones5_checkPhoenix() {
-    controller.click(eid("calendar-tab-button"));
-    switchToView(controller, "day");
-    goToDate(controller, 2009, 1, 1);
-
-    verify(controller, DATES, TIMEZONES, times);
-}
-
-function teardownTest(module) {
-    switchAppTimezone(TIMEZONES[3]);
-}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test6.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var helpersForController, switchToView, goToDate;
-var switchAppTimezone, verify, DATES, TIMEZONES;
-
-/* rows - dates
-   columns - correct time for each event */
-var times = [
-    [[0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [8, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [9, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [5, 0], [7, 0], [8, 0]],
-    [[0, 0], [2, 0], [3, 0], [3, 0], [5, 0], [6, 0], [7, 0], [8, 0]],
-    [[0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0]]
-];
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-    ({ helpersForController, switchToView, goToDate } =
-        collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-    Object.assign(module, helpersForController(controller));
-    ({ switchAppTimezone, verify, DATES, TIMEZONES } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-}
-
-function testTimezones6_checkLosAngeles() {
-    controller.click(eid("calendar-tab-button"));
-    switchToView(controller, "day");
-    goToDate(controller, 2009, 1, 1);
-
-    verify(controller, DATES, TIMEZONES, times);
-}
-
-function teardownTest(module) {
-    switchAppTimezone(TIMEZONES[4]);
-}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test7.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var helpersForController, switchToView, goToDate;
-var switchAppTimezone, verify, DATES, TIMEZONES;
-
-/* rows - dates
-   columns - correct time for each event */
-var times = [
-    [[5, 0], [6, 0], [7, 0], [8, 0], [9, 0], [10, 0], [11, 0], [12, 0]],
-    [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [12, 0]],
-    [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [13, 0]],
-    [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [13, 0]],
-    [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [13, 0]],
-    [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [9, 0], [11, 0], [12, 0]],
-    [[4, 0], [6, 0], [7, 0], [7, 0], [9, 0], [10, 0], [11, 0], [12, 0]],
-    [[5, 0], [6, 0], [7, 0], [8, 0], [9, 0], [10, 0], [11, 0], [12, 0]]
-];
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-    ({ helpersForController, switchToView, goToDate } =
-        collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-    Object.assign(module, helpersForController(controller));
-    ({ switchAppTimezone, verify, DATES, TIMEZONES } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-}
-
-function testTimezones7_checkBuenosAires() {
-    controller.click(eid("calendar-tab-button"));
-    switchToView(controller, "day");
-    goToDate(controller, 2009, 1, 1);
-
-    verify(controller, DATES, TIMEZONES, times);
-}
-
-function teardownTest(module) {
-    switchAppTimezone(TIMEZONES[5]);
-}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test8.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var helpersForController, switchToView, goToDate;
-var switchAppTimezone, verify, DATES, TIMEZONES;
-
-/* rows - dates
-   columns - correct time for each event */
-var times = [
-    [[9, 0], [10, 0], [11, 0], [12, 0], [13, 0], [14, 0], [15, 0], [16, 0]],
-    [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [17, 0]],
-    [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [18, 0]],
-    [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [18, 0]],
-    [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [18, 0]],
-    [[9, 0], [11, 0], [12, 0], [12, 0], [14, 0], [14, 0], [16, 0], [17, 0]],
-    [[8, 0], [10, 0], [11, 0], [11, 0], [13, 0], [14, 0], [15, 0], [16, 0]],
-    [[9, 0], [10, 0], [11, 0], [12, 0], [13, 0], [14, 0], [15, 0], [16, 0]]
-];
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-    ({ helpersForController, switchToView, goToDate } =
-        collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-    Object.assign(module, helpersForController(controller));
-    ({ switchAppTimezone, verify, DATES, TIMEZONES } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-}
-
-function testTimezones8_checkParis() {
-    controller.click(eid("calendar-tab-button"));
-    switchToView(controller, "day");
-    goToDate(controller, 2009, 1, 1);
-
-    verify(controller, DATES, TIMEZONES, times);
-}
-
-function teardownTest(module) {
-    switchAppTimezone(TIMEZONES[6]);
-}
deleted file mode 100755
--- a/calendar/test/mozmill/timezoneTests/test9.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var RELATIVE_ROOT = "../shared-modules";
-var MODULE_REQUIRES = ["calendar-utils", "timezone-utils"];
-
-var helpersForController, switchToView, goToDate;
-var switchAppTimezone, verify, DATES, TIMEZONES;
-
-/* rows - dates
-   columns - correct time for each event */
-var times = [
-    [[13, 45], [14, 45], [15, 45], [16, 45], [17, 45], [18, 45], [19, 45], [20, 45]],
-    [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [20, 45]],
-    [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [21, 45]],
-    [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [21, 45]],
-    [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [21, 45]],
-    [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [17, 45], [19, 45], [20, 45]],
-    [[12, 45], [14, 45], [15, 45], [15, 45], [17, 45], [18, 45], [19, 45], [20, 45]],
-    [[13, 45], [14, 45], [15, 45], [16, 45], [17, 45], [18, 45], [19, 45], [20, 45]]
-];
-
-function setupModule(module) {
-    controller = mozmill.getMail3PaneController();
-    ({ helpersForController, switchToView, goToDate } =
-        collector.getModule("calendar-utils"));
-    collector.getModule("calendar-utils").setupModule();
-    Object.assign(module, helpersForController(controller));
-    ({ switchAppTimezone, verify, DATES, TIMEZONES } = collector.getModule("timezone-utils"));
-    collector.getModule("timezone-utils").setupModule();
-}
-
-function testTimezones9_checkKathmandu() {
-    controller.click(eid("calendar-tab-button"));
-    switchToView(controller, "day");
-    goToDate(controller, 2009, 1, 1);
-
-    verify(controller, DATES, TIMEZONES, times);
-}
-
-function teardownTest(module) {
-    switchAppTimezone(TIMEZONES[7]);
-}
--- a/calendar/test/mozmill/views/testDayView.js
+++ b/calendar/test/mozmill/views/testDayView.js
@@ -3,188 +3,142 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
 var { cal } = ChromeUtils.import("resource://calendar/modules/calUtils.jsm", null);
 
 var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var CALENDARNAME;
+var goToDate, setData, lookupEventBox;
+var CALENDARNAME, CANVAS_BOX, EVENT_BOX;
 
 var TITLE1 = "Day View Event";
 var TITLE2 = "Day View Event Changed";
 var DESC = "Day View Event Description";
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
         helpersForController,
         invokeEventDialog,
         createCalendar,
         deleteCalendars,
-        CALENDARNAME
+        goToDate,
+        setData,
+        lookupEventBox,
+        CALENDARNAME,
+        CANVAS_BOX,
+        EVENT_BOX
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
 function testDayView() {
     let dateFormatter = cal.getDateFormatter();
     // paths
-    let miniMonth = `
-        /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
-        id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
-        id("ltnSidebar")/id("minimonth-pane")/{"align":"center"}/
-        id("calMinimonthBox")/id("calMinimonth")
-    `;
     let dayView = `
         /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
         id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
         id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
         id("day-view")
     `;
-    let day = lookup(`
-        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"labelbox"})/
-        anon({"anonid":"labeldaybox"})/{"flex":"1"}
-    `);
-    let eventDialog = '/id("calendar-event-dialog")/id("event-grid")/id("event-grid-rows")/';
 
     // open day view
     controller.click(eid("calendar-tab-button"));
     controller.waitThenClick(eid("calendar-day-view-button"));
 
-    // pick year
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"yearcell"})
-    `));
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"years-popup"})/[0]/
-        {"value":"2009"}
-    `));
-
-    // pick month
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"monthheader"})
-    `));
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"months-popup"})/[0]/
-        {"index":"0"}
-    `));
-
-    // pick day
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-calendar"})/[1]/{"value":"1"}
-    `));
+    goToDate(controller, 2009, 1, 1);
 
     // verify date in view
+    let day = lookup(`
+        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"labelbox"})/
+        anon({"anonid":"labeldaybox"})/{"flex":"1"}
+    `);
     controller.waitFor(() => day.getNode().mDate.icalString == "20090101");
 
     // create event at 8 AM
-    let eventBox = lookup(`
-        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"daybox"})/{"class":"calendar-event-column-even"}/
-        anon({"anonid":"boxstack"})/anon({"anonid":"bgbox"})/[8]')
-    `);
-
+    let eventBox = lookupEventBox("day", CANVAS_BOX, null, 1, 8);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
+        let { lookup: iframeLookup } = helpersForController(iframe);
 
-        // check that the start time is correct
-        let startTimeInput = eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
+        let innerFrame = '/id("calendar-event-dialog-inner")/id("event-grid")/id("event-grid-rows")/';
+        let dateInput = `
+            anon({"class":"datepicker-box-class"})/{"class":"datepicker-text-class"}/
+            anon({"class":"menulist-editable-box textbox-input-box"})/
+            anon({"anonid":"input"})
+        `;
+        let timeInput = `
             anon({"anonid":"hbox"})/anon({"anonid":"time-picker"})/
             anon({"class":"timepicker-box-class"})/
             anon({"class":"timepicker-text-class"})/anon({"flex":"1"})/
-            anon({"anonid":"input"})'
+            anon({"anonid":"input"})
+        `;
+        let startId = "event-starttime";
+
+        let startTimeInput = iframeLookup(`
+            ${innerFrame}/id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("${startId}")/${timeInput}
         `);
+        let startDateInput = iframeLookup(`
+            ${innerFrame}/id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("${startId}")/
+            anon({"anonid":"hbox"})/anon({"anonid":"date-picker"})/${dateInput}
+        `);
+
+        // check that the start time is correct
+        let someDate = cal.createDateTime();
+        someDate.resetTo(2009, 0, 1, 8, 0, 0, cal.dtz.floating);
         event.waitForElement(startTimeInput);
-        event.assertValue(startTimeInput, "8:00");
-        let someTime = cal.createDateTime();
-        someTime.resetTo(2009, 1, 1);
-        let date = dateFormatter.formatDateShort(someDate);
-        event.assertValue(eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
-            anon({"anonid":"hbox"})/anon({"anonid":"date-picker"})/
-            anon({"flex":"1","id":"hbox","class":"datepicker-box-class"})/
-            {"class":"datepicker-text-class"}/
-            anon({"class":"menulist-editable-box textbox-input-box"})/
-            anon({"anonid":"input"})
-        `), date);
+        event.assertValue(startTimeInput, dateFormatter.formatTime(someDate));
+        event.assertValue(startDateInput, dateFormatter.formatDateShort(someDate));
 
         // fill in title, description and calendar
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), TITLE1);
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-description-row")/id("item-description")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), DESC);
-        event.select(eventid("item-calendar"), null, CALENDARNAME);
+        setData(event, iframe, { title: TITLE1, description: DESC, calendar: CALENDARNAME });
 
         // save
         event.click(eventid("button-saveandclose"));
     });
 
     // if it was created successfully, it can be opened
-    eventBox = lookup(`
-        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"daybox"})/{"class":"calendar-event-column-even"}/
-        anon({"anonid":"boxstack"})/anon({"anonid":"topbox"})/{"flex":"1"}/
-        {"flex":"1"}/[0]/
-        {"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}
-    `);
+    eventBox = lookupEventBox(
+        "day", EVENT_BOX, null, 1, 8,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    );
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
 
         // change title and save changes
-        let titleTextBox = eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `);
-        event.waitForElement(titleTextBox);
-        event.type(titleTextBox, TITLE2);
+        setData(event, iframe, { title: TITLE2 });
         event.click(eventid("button-saveandclose"));
     });
 
     // check if name was saved
-    let eventName = lookup(`
-        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"daybox"})/{"class":"calendar-event-column-even"}/
-        anon({"anonid":"boxstack"})/anon({"anonid":"topbox"})/{"flex":"1"}/
-        {"flex":"1"}/{"flex":"1"}/
-        {"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}/
+    let eventName = lookupEventBox(
+        "day", EVENT_BOX, null, 1, 8,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}/
         anon({"flex":"1"})/anon({"anonid":"event-container"})/
         {"class":"calendar-event-selection"}/anon({"anonid":"eventbox"})/
-        {"class":"calendar-event-details"}/anon({"anonid":"event-name"})
-    `);
+        {"class":"calendar-event-details"}/anon({"flex":"1"})/
+        anon({"anonid":"event-name"})`
+    );
     controller.waitForElement(eventName);
     controller.assertJSProperty(eventName, "textContent", TITLE2);
 
     // delete event
-    controller.click(lookup(`
-        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"daybox"})/{"class":"calendar-event-column-even"}/
-        anon({"anonid":"boxstack"})/anon({"anonid":"topbox"})/{"flex":"1"}/
-        {"flex":"1"}/{"flex":"1"}/
-        {"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}
-    `));
+    controller.click(lookupEventBox(
+        "day", EVENT_BOX, null, 1, 8,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    ));
     controller.keypress(eid("day-view"), "VK_DELETE", {});
-    controller.waitForElementNotPresent(lookup(`
-        ${dayView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"daybox"})/{"class":"calendar-event-column-even"}/
-        anon({"anonid":"boxstack"})/anon({"anonid":"topbox"})/{"flex":"1"}/
-        {"flex":"1"}/{"flex":"1"}/
-        {"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}
-    `));
+    controller.waitForElementNotPresent(lookupEventBox(
+        "day", EVENT_BOX, null, 1, 8,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    ));
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/views/testMonthView.js
+++ b/calendar/test/mozmill/views/testMonthView.js
@@ -3,177 +3,145 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
 var { cal } = ChromeUtils.import("resource://calendar/modules/calUtils.jsm", null);
 
 var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var CALENDARNAME;
+var goToDate, setData, lookupEventBox;
+var CALENDARNAME, CANVAS_BOX, EVENT_BOX;
 
 var TITLE1 = "Month View Event";
 var TITLE2 = "Month View Event Changed";
 var DESC = "Month View Event Description";
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
         helpersForController,
         invokeEventDialog,
         createCalendar,
         deleteCalendars,
-        CALENDARNAME
+        goToDate,
+        setData,
+        lookupEventBox,
+        CALENDARNAME,
+        CANVAS_BOX,
+        EVENT_BOX
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
 function testMonthView() {
     let dateFormatter = cal.getDateFormatter();
     // paths
-    let miniMonth = `
-        /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
-        id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
-        id("ltnSidebar")/id("minimonth-pane")/{"align":"center"}/
-        id("calMinimonthBox")/id("calMinimonth")
-    `;
     let monthView = `
         /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
         id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
         id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
         id("month-view")
     `;
-    let eventDialog = '/id("calendar-event-dialog")/id("event-grid")/id("event-grid-rows")/';
-    let eventBox = `
-        ${monthView}/anon({"anonid":"mainbox"})/anon({"anonid":"monthgrid"})/
-        anon({"anonid":"monthgridrows"})/[0]/{"selected":"true"}/
-        {"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}/
-        anon({"flex":"1"})/[0]/anon({"anonid":"event-container"})/
-        {"class":"calendar-event-selection"}/anon({"anonid":"eventbox"})/
-        {"class":"calendar-event-details"}
-    `;
 
     controller.click(eid("calendar-tab-button"));
     controller.waitThenClick(eid("calendar-month-view-button"));
 
-    // pick year
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"yearcell"})
-    `));
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"years-popup"})/[0]/
-        {"value":"2009"}
-    `));
-
-    // pick month
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"monthheader"})
-    `));
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"months-popup"})/[0]/
-        {"index":"0"}
-    `));
-
-    // pick day
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-calendar"})/[1]/{"value":"1"}
-    `));
+    goToDate(controller, 2009, 1, 1);
 
     // verify date
     let day = lookup(`
         ${monthView}/anon({"anonid":"mainbox"})/anon({"anonid":"monthgrid"})/
         anon({"anonid":"monthgridrows"})/[0]/{"selected":"true"}
     `);
     controller.waitFor(() => day.getNode().mDate.icalString == "20090101");
 
     // create event
     // Thursday of 2009-01-01 should be the selected box in the first row with default settings
     let hour = new Date().getHours(); // remember time at click
-    eventBox = lookup(`
-        ${monthView}/anon({"anonid":"mainbox"})/anon({"anonid":"monthgrid"})/
-        anon({"anonid":"monthgridrows"})/[0]/{"selected":"true"}/
-        anon({"anonid":"day-items"})
-    `);
+    let eventBox = lookupEventBox("month", CANVAS_BOX, 1, 5);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
+        let { lookup: iframeLookup } = helpersForController(iframe);
 
-        // check that the start time is correct
-        // next full hour except last hour hour of the day
-        let nextHour = hour == 23 ? hour : (hour + 1) % 24;
-        let startTime = nextHour + ":00"; // next full hour
-        let startTimeInput = eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
+        let innerFrame = '/id("calendar-event-dialog-inner")/id("event-grid")/id("event-grid-rows")/';
+        let dateInput = `
+            anon({"class":"datepicker-box-class"})/{"class":"datepicker-text-class"}/
+            anon({"class":"menulist-editable-box textbox-input-box"})/
+            anon({"anonid":"input"})
+        `;
+        let timeInput = `
             anon({"anonid":"hbox"})/anon({"anonid":"time-picker"})/
             anon({"class":"timepicker-box-class"})/
             anon({"class":"timepicker-text-class"})/anon({"flex":"1"})/
             anon({"anonid":"input"})
-        `);
-        event.waitForElement(startTimeInput);
-        event.assertValue(startTimeInput, startTime);
+        `;
+        let startId = "event-starttime";
 
-        let someTime = cal.dtz.now();
-        someTime.resetTo(2009, 1, 1);
-        let date = dateFormatter.formatDateShort(someDate);
-        event.assertValue(eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
-            anon({"anonid":"hbox"})/anon({"anonid":"date-picker"})/
-            anon({"flex":"1","id":"hbox","class":"datepicker-box-class"})/
-            {"class":"datepicker-text-class"}/
-            anon({"class":"menulist-editable-box textbox-input-box"})/
-            anon({"anonid":"input"})
-        `), date);
+        let startTimeInput = iframeLookup(`
+            ${innerFrame}/id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("${startId}")/${timeInput}
+        `);
+        let startDateInput = iframeLookup(`
+            ${innerFrame}/id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("${startId}")/
+            anon({"anonid":"hbox"})/anon({"anonid":"date-picker"})/${dateInput}
+        `);
+
+        // check that the start time is correct
+        // next full hour except last hour hour of the day
+        let nextHour = hour == 23 ? hour : (hour + 1) % 24;
+        let someDate = cal.dtz.now();
+        someDate.resetTo(2009, 0, 1, nextHour, 0, 0, cal.dtz.floating);
+        event.waitForElement(startTimeInput);
+        event.assertValue(startTimeInput, dateFormatter.formatTime(someDate));
+        event.assertValue(startDateInput, dateFormatter.formatDateShort(someDate));
 
         // fill in title, description and calendar
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})'),
-        `), TITLE1);
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-description-row")/id("item-description")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), DESC);
-        event.click(eventid("item-calendar"));
-        event.click(eventlookup(`
-            ${eventDialog}/id("event-grid-category-color-row")/
-            id("event-grid-category-box")/id("item-calendar")/[0]/
-            {"label":"${CALENDARNAME}"}'
-        `));
+        setData(event, iframe, { title: TITLE1, description: DESC, calendar: CALENDARNAME });
 
         // save
         event.click(eventid("button-saveandclose"));
     });
 
     // if it was created successfully, it can be opened
+    eventBox = lookupEventBox(
+        "month", EVENT_BOX, 1, 5, null,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    );
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
 
         // change title and save changes
-        let titleTextBox = eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})'
-        `);
-        event.waitForElement(titleTextBox);
-        event.type(titleTextBox, TITLE2);
+        setData(event, iframe, { title: TITLE2 });
         event.click(eventid("button-saveandclose"));
     });
 
     // check if name was saved
-    let eventName = lookup(eventBox + '/{"flex":"1"}/anon({"anonid":"event-name"})');
+    let eventName = lookupEventBox(
+        "month", EVENT_BOX, 1, 5, null,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}/
+        anon({"flex":"1"})/[0]/anon({"anonid":"event-container"})/
+        {"class":"calendar-event-selection"}/anon({"anonid":"eventbox"})/
+        {"class":"calendar-event-details"}/{"flex":"1"}/anon({"anonid":"event-name"})`
+    );
 
     controller.waitForElement(eventName);
     controller.assertValue(eventName, TITLE2);
 
     // delete event
-    controller.click(lookup(eventBox));
+    controller.click(lookupEventBox(
+        "month", EVENT_BOX, 1, 5, null,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    ));
     controller.keypress(eid("month-view"), "VK_DELETE", {});
-    controller.waitForElementNotPresent(lookup(eventBox));
+    controller.waitForElementNotPresent(lookupEventBox(
+        "month", EVENT_BOX, 1, 5, null,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    ));
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/views/testMultiweekView.js
+++ b/calendar/test/mozmill/views/testMultiweekView.js
@@ -3,175 +3,145 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
 var { cal } = ChromeUtils.import("resource://calendar/modules/calUtils.jsm", null);
 
 var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var CALENDARNAME;
+var goToDate, setData, lookupEventBox;
+var CALENDARNAME, CANVAS_BOX, EVENT_BOX;
 
 var TITLE1 = "Multiweek View Event";
 var TITLE2 = "Multiweek View Event Changed";
 var DESC = "Multiweek View Event Description";
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
     ({
         helpersForController,
         invokeEventDialog,
         createCalendar,
         deleteCalendars,
-        CALENDARNAME
+        goToDate,
+        setData,
+        lookupEventBox,
+        CALENDARNAME,
+        CANVAS_BOX,
+        EVENT_BOX
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
 function testMultiWeekView() {
     let dateFormatter = cal.getDateFormatter();
     // paths
-    let miniMonth = `
-        /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
-        id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
-        id("ltnSidebar")/id("minimonth-pane")/{"align":"center"}/
-        id("calMinimonthBox")/id("calMinimonth")/
-    `;
     let multiWeekView = `
         /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
         id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
         id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
         id("multiweek-view")/
     `;
-    let eventDialog = '/id("calendar-event-dialog")/id("event-grid")/id("event-grid-rows")/';
-    let eventBox = `
-        ${multiWeekView}/anon({"anonid":"mainbox"})/anon({"anonid":"monthgrid"})/
-        anon({"anonid":"monthgridrows"})/[0]/{"selected":"true"}/
-        {"tooltip":"itemTooltip","calendar":"' + CALENDARNAME.toLowerCase() + '"}/
-        anon({"flex":"1"})/[0]/anon({"anonid":"event-container"})/
-        {"class":"calendar-event-selection"}/anon({"anonid":"eventbox"})/
-        {"class":"calendar-event-details"}
-    `;
 
     controller.click(eid("calendar-tab-button"));
     controller.waitThenClick(eid("calendar-multiweek-view-button"));
 
-    // pick year
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"yearcell"})
-    `));
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"years-popup"})/[0]/
-        {"value":"2009"}
-    `));
-
-    // pick month
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"monthheader"})
-    `));
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"months-popup"})/[0]/
-        {"index":"0"}
-    `));
-
-    // pick day
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-calendar"})/[1]/{"value":"1"}
-    `));
+    goToDate(controller, 2009, 1, 1);
 
     // verify date
     let day = lookup(`
         ${multiWeekView}/anon({"anonid":"mainbox"})/anon({"anonid":"monthgrid"})/
         anon({"anonid":"monthgridrows"})/[0]/{"selected":"true"}
     `);
     controller.waitFor(() => day.getNode().mDate.icalString == "20090101");
 
     // create event
     // Thursday of 2009-01-01 should be the selected box in the first row with default settings
     let hour = new Date().getHours(); // remember time at click
-    eventBox = lookup(`
-        ${multiWeekView}/anon({"anonid":"mainbox"})/anon({"anonid":"monthgrid"})/
-        anon({"anonid":"monthgridrows"})/[0]/{"selected":"true"}/
-        anon({"anonid":"day-items"})
-    `);
+    let eventBox = lookupEventBox("multiweek", CANVAS_BOX, 1, 5);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
+        let { lookup: iframeLookup } = helpersForController(iframe);
 
-        // check that the start time is correct
-        // next full hour except last hour hour of the day
-        let nextHour = (hour == 23 ? hour : (hour + 1) % 24);
-        let startTime = nextHour + ":00";
-        let startTimeInput = eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
+        let innerFrame = '/id("calendar-event-dialog-inner")/id("event-grid")/id("event-grid-rows")/';
+        let dateInput = `
+            anon({"class":"datepicker-box-class"})/{"class":"datepicker-text-class"}/
+            anon({"class":"menulist-editable-box textbox-input-box"})/
+            anon({"anonid":"input"})
+        `;
+        let timeInput = `
             anon({"anonid":"hbox"})/anon({"anonid":"time-picker"})/
             anon({"class":"timepicker-box-class"})/
             anon({"class":"timepicker-text-class"})/anon({"flex":"1"})/
             anon({"anonid":"input"})
+        `;
+        let startId = "event-starttime";
+
+        let startTimeInput = iframeLookup(`
+            ${innerFrame}/id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("${startId}")/${timeInput}
         `);
+        let startDateInput = iframeLookup(`
+            ${innerFrame}/id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("${startId}")/
+            anon({"anonid":"hbox"})/anon({"anonid":"date-picker"})/${dateInput}
+        `);
+
+        // check that the start time is correct
+        // next full hour except last hour hour of the day
+        let nextHour = hour == 23 ? hour : (hour + 1) % 24;
+        let someDate = cal.dtz.now();
+        someDate.resetTo(2009, 0, 1, nextHour, 0, 0, cal.dtz.floating);
         event.waitForElement(startTimeInput);
-        event.assertValue(startTimeInput, startTime);
-        let someTime = cal.createDateTime();
-        someTime.resetTo(2009, 1, 1);
-        let date = dateFormatter.formatDateShort(someDate);
-        event.assertValue(eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
-            anon({"anonid":"hbox"})/anon({"anonid":"date-picker"})/
-            anon({"flex":"1","id":"hbox","class":"datepicker-box-class"})/
-            {"class":"datepicker-text-class"}/
-            anon({"class":"menulist-editable-box textbox-input-box"})/
-            anon({"anonid":"input"})
-        `), date);
+        event.assertValue(startTimeInput, dateFormatter.formatTime(someDate));
+        event.assertValue(startDateInput, dateFormatter.formatDateShort(someDate));
 
         // fill in title, description and calendar
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), TITLE1);
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-description-row")/id("item-description")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), DESC);
-        event.click(eventid("item-calendar"));
-        event.click(eventlookup(`
-            ${eventDialog}/id("event-grid-category-color-row")/
-            id("event-grid-category-box")/id("item-calendar")/[0]/
-            {"label":"${CALENDARNAME}"}
-        `));
+        setData(event, iframe, { title: TITLE1, description: DESC, calendar: CALENDARNAME });
 
         // save
         event.click(eventid("button-saveandclose"));
     });
 
     // if it was created successfully, it can be opened
+    eventBox = lookupEventBox(
+        "multiweek", EVENT_BOX, 1, 5, null,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    );
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
 
         // change title and save changes
-        let titmeTextBox = eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `);
-        event.waitForElement(titmeTextBox);
-        event.type(titmeTextBox, TITLE2);
+        setData(event, iframe, { title: TITLE2 });
         event.click(eventid("button-saveandclose"));
     });
 
     // check if name was saved
-    let eventName = lookup(eventBox + '/{"flex":"1"}/anon({"anonid":"event-name"})');
+    let eventName = lookupEventBox(
+        "multiweek", EVENT_BOX, 1, 5, null,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}/
+        anon({"flex":"1"})/[0]/anon({"anonid":"event-container"})/
+        {"class":"calendar-event-selection"}/anon({"anonid":"eventbox"})/
+        {"class":"calendar-event-details"}/{"flex":"1"}/anon({"anonid":"event-name"})`
+    );
+
     controller.waitForElement(eventName);
     controller.assertValue(eventName, TITLE2);
 
     // delete event
-    controller.click(lookup(eventBox));
+    controller.click(lookupEventBox(
+        "multiweek", EVENT_BOX, 1, 5, null,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    ));
     controller.keypress(eid("multiweek-view"), "VK_DELETE", {});
-    controller.waitForElementNotPresent(lookup(eventBox));
+    controller.waitForElementNotPresent(lookupEventBox(
+        "multiweek", EVENT_BOX, 1, 5, null,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    ));
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/views/testTaskView.js
+++ b/calendar/test/mozmill/views/testTaskView.js
@@ -1,32 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
 var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
+var setData;
 var CALENDARNAME;
 
-var utils = require("../shared-modules/utils");
-
 var TITLE = "Task";
 var DESCRIPTION = "1. Do A\n2. Do B";
 var percentComplete = "50";
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
-
     ({
         helpersForController,
         invokeEventDialog,
         createCalendar,
         deleteCalendars,
+        setData,
         CALENDARNAME
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
@@ -34,17 +33,16 @@ function setupModule(module) {
 // checked
 function testTaskView() {
     // paths
     let taskView = `
         /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
         id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
         id("calendarDisplayDeck")/id("calendar-task-box")/
     `;
-    let taskDialog = '/id("calendar-task-dialog")/id("event-grid")/id("event-grid-rows")';
     let treeChildren = `
         ${taskView}/[1]/id("calendar-task-tree")/
         anon({"anonid":"calendar-task-tree"})/{"tooltip":"taskTreeTooltip"}
     `;
     let taskTree = taskView + '[1]/id("calendar-task-tree")';
     let toolTip = '/id("messengerWindow")/id("calendar-popupset")/id("taskTreeTooltip")';
     let toolTipGrid = toolTip + '/{"class":"tooltipBox"}/{"class":"tooltipHeaderGrid"}/';
 
@@ -66,21 +64,21 @@ function testTaskView() {
         }
     }
 
     let taskTreeNode = lookup(taskTree).getNode();
     let countBefore = taskTreeNode.mTaskArray.length;
 
     // add task
     controller.type(lookup(`
-        ${taskView}/id("task-addition-box")/id("view-task-edit-field")/
+        ${taskView}/id("task-addition-box")/[0]/id("view-task-edit-field")/
         anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
     `), TITLE);
     controller.keypress(lookup(`
-        ${taskView}/id("task-addition-box")/id("view-task-edit-field")/
+        ${taskView}/id("task-addition-box")/[0]/id("view-task-edit-field")/
         anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
     `), "VK_RETURN", {});
 
     // verify added
     let countAfter;
     controller.waitFor(() => {
         countAfter = taskTreeNode.mTaskArray.length;
         return countBefore + 1 == countAfter;
@@ -89,60 +87,38 @@ function testTaskView() {
     // last added task is automatically selected so verify detail window data
     controller.assertJSProperty(eid("calendar-task-details-title"), "textContent", TITLE);
 
     // open added task
     // doubleclick on completion checkbox is ignored as opening action, so don't click at immediate
     // left where the checkbox is located
     controller.doubleClick(lookup(treeChildren), 50, 0);
     invokeEventDialog(controller, null, (task, iframe) => {
-        let { lookup: tasklookup, eid: taskid } = helpersForController(task);
+        let { eid: taskid } = helpersForController(task);
+        let { eid: iframeId } = helpersForController(iframe);
 
         // verify calendar
-        task.waitForElement(tasklookup(`
-            ${taskDialog}/id("event-grid-category-color-row")/
-            id("event-grid-category-box")/id("item-calendar")/[0]/
-            {"selected":"true","label":"${CALENDARNAME}"}
-        `));
-
-        // add description, mark needs action and add percent complete
-        task.type(tasklookup(`
-            ${taskDialog}/id("event-grid-description-row")/id("item-description")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), DESCRIPTION);
-        task.click(taskid("todo-status-needsaction-menuitem"));
+        controller.assertValue(iframeId("item-calendar"), CALENDARNAME);
 
-        // delete default 0 percent complete
-        task.keypress(tasklookup(`
-            ${taskDialog}/id("event-grid-todo-status-row")/
-            id("event-grid-todo-status-picker-box")/
-            id("percent-complete-textbox")/
-            anon({"class":"textbox-input-box numberbox-input-box"})/
-            anon({"anonid":"input"})
-        `), "VK_DELETE", {});
-        task.type(tasklookup(`
-            ${taskDialog}/id("event-grid-todo-status-row")/
-            id("event-grid-todo-status-picker-box")/
-            id("percent-complete-textbox")/
-            anon({"class":"textbox-input-box numberbox-input-box"})/
-            anon({"anonid":"input"})
-        `), percentComplete);
+        setData(task, iframe, { status: "needs-action", percent: percentComplete, description: DESCRIPTION });
 
         // save
         task.click(taskid("button-saveandclose"));
     });
 
     // verify description and status in details pane
     controller.assertValue(lookup(`
         ${taskView}/{"flex":"1"}/id("calendar-task-details-container")/
         id("calendar-task-details-description")/
         anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
     `), DESCRIPTION);
-    let status = utils.getProperty("chrome://calendar/locale/calendar.properties", "taskDetailsStatusNeedsAction");
-    controller.assertValue(eid("calendar-task-details-status"), status);
+    controller.assertValue(eid("calendar-task-details-status"), "Needs Action");
+
+    // This is a hack.
+    taskTreeNode.getTaskAtRow(0).calendar.setProperty("capabilities.priority.supported", true);
 
     // set high priority and verify it in detail pane
     controller.click(eid("task-actions-priority"));
     sleep();
     controller.click(eid("priority-1-menuitem"));
     sleep();
     let priorityNode = eid("calendar-task-details-priority-high");
     controller.assertNotDOMProperty(priorityNode, "hidden");
@@ -151,34 +127,35 @@ function testTaskView() {
     let toolTipNode = lookup(toolTip).getNode();
     toolTipNode.ownerGlobal.showToolTip(toolTipNode, taskTreeNode.getTaskAtRow(0));
 
     let toolTipName = lookup(toolTipGrid + "[1]/[0]/[1]");
     let toolTipCalendar = lookup(toolTipGrid + "[1]/[1]/[1]");
     let toolTipPriority = lookup(toolTipGrid + "[1]/[2]/[1]");
     let toolTipStatus = lookup(toolTipGrid + "[1]/[3]/[1]");
     let toolTipComplete = lookup(toolTipGrid + "[1]/[4]/[1]");
-    let priority = utils.getProperty("chrome://calendar/locale/calendar.properties", "highPriority");
 
     controller.assertJSProperty(toolTipName, "textContent", TITLE);
     controller.assertJSProperty(toolTipCalendar, "textContent", CALENDARNAME);
-    controller.assertJSProperty(toolTipPriority, "textContent", priority);
-    controller.assertJS(toolTipStatus.getNode().textContent.toLowerCase() == status.toLowerCase());
+    controller.assertJSProperty(toolTipPriority, "textContent", "High");
+    controller.assertJSProperty(toolTipStatus, "textContent", "Needs Action");
     controller.assertJSProperty(toolTipComplete, "textContent", percentComplete + "%");
 
     // mark completed, verify
     controller.click(eid("task-actions-markcompleted"));
     sleep();
 
-    status = utils.getProperty("chrome://calendar/locale/calendar.properties", "taskDetailsStatusCompleted");
     toolTipNode.ownerGlobal.showToolTip(toolTipNode, taskTreeNode.getTaskAtRow(0));
-    controller.assertJS(toolTipStatus.getNode().textContent.toLowerCase() == status.toLowerCase());
+    controller.assertJSProperty(toolTipStatus, "textContent", "Completed");
 
     // delete task, verify
     controller.click(eid("task-context-menu-delete"));
     controller.click(eid("calendar-delete-task-button"));
-    let countAfterDelete = taskTreeNode.mTaskArray.length;
-    controller.assertJS(countAfter - 1 == countAfterDelete);
+    let countAfterDelete;
+    controller.waitFor(() => {
+        countAfterDelete = taskTreeNode.mTaskArray.length;
+        return countAfter - 1 == countAfterDelete;
+    });
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/calendar/test/mozmill/views/testWeekView.js
+++ b/calendar/test/mozmill/views/testWeekView.js
@@ -3,170 +3,142 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var RELATIVE_ROOT = "../shared-modules";
 var MODULE_REQUIRES = ["calendar-utils"];
 
 var { cal } = ChromeUtils.import("resource://calendar/modules/calUtils.jsm", null);
 
 var helpersForController, invokeEventDialog, createCalendar, deleteCalendars;
-var CALENDARNAME;
+var goToDate, setData, lookupEventBox;
+var CALENDARNAME, CANVAS_BOX, EVENT_BOX;
 
 var TITLE1 = "Week View Event";
 var TITLE2 = "Week View Event Changed";
 var DESC = "Week View Event Description";
 
 function setupModule(module) {
     controller = mozmill.getMail3PaneController();
-
     ({
         helpersForController,
         invokeEventDialog,
         createCalendar,
         deleteCalendars,
-        CALENDARNAME
+        goToDate,
+        setData,
+        lookupEventBox,
+        CALENDARNAME,
+        CANVAS_BOX,
+        EVENT_BOX
     } = collector.getModule("calendar-utils"));
     collector.getModule("calendar-utils").setupModule();
     Object.assign(module, helpersForController(controller));
 
     createCalendar(controller, CALENDARNAME);
 }
 
 function testWeekView() {
     let dateFormatter = cal.getDateFormatter();
     // paths
-    let miniMonth = `
-        /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
-        id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
-        id("ltnSidebar")/id("minimonth-pane")/{"align":"center"}/
-        id("calMinimonthBox")/id("calMinimonth")/
-    `;
     let weekView = `
         /id("messengerWindow")/id("tabmail-container")/id("tabmail")/
         id("tabpanelcontainer")/id("calendarTabPanel")/id("calendarContent")/
         id("calendarDisplayDeck")/id("calendar-view-box")/id("view-deck")/
-        id("week-view")/;
-    `;
-    let eventDialog = `
-        /id("calendar-event-dialog")/id("event-grid")/id("event-grid-rows")/
-    `;
-    let eventBox = `
-        ${weekView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"daybox"})/[4]/anon({"anonid":"boxstack"})/
-        anon({"anonid":"topbox"})/{"flex":"1"}/{"flex":"1"}/{"flex":"1"}/
-        {"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}
+        id("week-view")/
     `;
 
     controller.click(eid("calendar-tab-button"));
     controller.waitThenClick(eid("calendar-week-view-button"));
 
-    // pick year
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"yearcell"})
-    `));
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"years-popup"})/[0]/
-        {"value":"2009"}
-    `));
-
-    // pick month
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/anon({"anonid":"monthheader"})
-    `));
-    controller.click(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-header"})/
-        anon({"anonid":"minmonth-popupset"})/anon({"anonid":"months-popup"})/[0]/
-        {"index":"0"}'
-    `));
-
-    // pick day
-    controller.waitThenClick(lookup(`
-        ${miniMonth}/anon({"anonid":"minimonth-calendar"})/[1]/{"value":"1"}
-    `));
+    goToDate(controller, 2009, 1, 1);
 
     // verify date
     let day = lookup(`
-        ${weekView}/anon({"anonid":"mainbox"})/anon({"anonid":"labelbox"})/
-        anon({"anonid":"labeldaybox"})/{"selected":"true"}
+        ${weekView}/anon({"anonid":"mainbox"})/anon({"anonid":"headerbox"})/
+        anon({"anonid":"headerdaybox"})/{"selected":"true"}
     `);
     controller.waitFor(() => day.getNode().mDate.icalString == "20090101");
 
     // create event at 8 AM
     // Thursday of 2009-01-01 is 4th with default settings
-    eventBox = lookup(`
-        ${weekView}/anon({"anonid":"mainbox"})/anon({"anonid":"scrollbox"})/
-        anon({"anonid":"daybox"})/[4]/anon({"anonid":"boxstack"})/
-        anon({"anonid":"bgbox"})/[8]'), 1, 1
-    `);
+    let eventBox = lookupEventBox("week", CANVAS_BOX, null, 5, 8);
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
+        let { lookup: iframeLookup } = helpersForController(iframe);
 
-        // check that the start time is correct
-        let startTimeInput = eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
+        let innerFrame = '/id("calendar-event-dialog-inner")/id("event-grid")/id("event-grid-rows")/';
+        let dateInput = `
+            anon({"class":"datepicker-box-class"})/{"class":"datepicker-text-class"}/
+            anon({"class":"menulist-editable-box textbox-input-box"})/
+            anon({"anonid":"input"})
+        `;
+        let timeInput = `
             anon({"anonid":"hbox"})/anon({"anonid":"time-picker"})/
             anon({"class":"timepicker-box-class"})/
             anon({"class":"timepicker-text-class"})/anon({"flex":"1"})/
-            anon({"anonid":"input"})'
+            anon({"anonid":"input"})
+        `;
+        let startId = "event-starttime";
+
+        let startTimeInput = iframeLookup(`
+            ${innerFrame}/id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("${startId}")/${timeInput}
         `);
+        let startDateInput = iframeLookup(`
+            ${innerFrame}/id("event-grid-startdate-row")/
+            id("event-grid-startdate-picker-box")/id("${startId}")/
+            anon({"anonid":"hbox"})/anon({"anonid":"date-picker"})/${dateInput}
+        `);
+
+        // check that the start time is correct
         event.waitForElement(startTimeInput);
-        event.assertValue(startTimeInput, "8:00");
-        let someTime = cal.createDateTime();
-        someTime.resetTo(2009, 1, 1);
-        let date = dateFormatter.formatDateShort(someDate);
-        event.assertValue(eventlookup(`
-            ${eventDialog}/id("event-grid-startdate-row")/
-            id("event-grid-startdate-picker-box")/id("event-starttime")/
-            anon({"anonid":"hbox"})/anon({"anonid":"date-picker"})/
-            anon({"flex":"1","id":"hbox","class":"datepicker-box-class"})/
-            {"class":"datepicker-text-class"}/
-            anon({"class":"menulist-editable-box textbox-input-box"})/
-            anon({"anonid":"input"})
-        `), date);
+        let someDate = cal.createDateTime();
+        someDate.resetTo(2009, 0, 1, 8, 0, 0, cal.dtz.floating);
+        event.assertValue(startTimeInput, dateFormatter.formatTime(someDate));
+        event.assertValue(startDateInput, dateFormatter.formatDateShort(someDate));
 
         // fill in title, description and calendar
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), TITLE1);
-        event.type(eventlookup(`
-            ${eventDialog}/id("event-grid-description-row")/id("item-description")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `), DESC);
-        event.select(eventid("item-calendar"), null, CALENDARNAME);
+        setData(event, iframe, { title: TITLE1, description: DESC, calendar: CALENDARNAME });
 
         // save
         event.click(eventid("button-saveandclose"));
     });
 
+    // if it was created successfully, it can be opened
+    eventBox = lookupEventBox(
+        "week", EVENT_BOX, null, 5, 8,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    );
     invokeEventDialog(controller, eventBox, (event, iframe) => {
-        let { lookup: eventlookup, eid: eventid } = helpersForController(event);
+        let { eid: eventid } = helpersForController(event);
 
         // change title and save changes
-        let titleTextBox = eventlookup(`
-            ${eventDialog}/id("event-grid-title-row")/id("item-title")/
-            anon({"anonid":"moz-input-box"})/anon({"anonid":"input"})
-        `);
-        event.waitForElement(titleTextBox);
-        event.type(titleTextBox, TITLE2);
+        setData(event, iframe, { title: TITLE2 });
         event.click(eventid("button-saveandclose"));
     });
 
     // check if name was saved
-    let eventName = lookup(`
-        ${eventBox}//anon({"flex":"1"})/anon({"anonid":"event-container"})/
+    let eventName = lookupEventBox(
+        "week", EVENT_BOX, null, 5, 8,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}/
+        anon({"flex":"1"})/anon({"anonid":"event-container"})/
         {"class":"calendar-event-selection"}/anon({"anonid":"eventbox"})/
-        {"class":"calendar-event-details"}/anon({"anonid":"event-name"})'
-    `);
+        {"class":"calendar-event-details"}/anon({"flex":"1"})/
+        anon({"anonid":"event-name"})`
+    );
     controller.waitForElement(eventName);
     controller.assertJSProperty(eventName, "textContent", TITLE2);
 
     // delete event
-    controller.click(lookup(eventBox));
+    controller.click(lookupEventBox(
+        "week", EVENT_BOX, null, 5, 8,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    ));
     controller.keypress(eid("week-view"), "VK_DELETE", {});
-    controller.waitForElementNotPresent(lookup(eventBox));
+    controller.waitForElementNotPresent(lookupEventBox(
+        "week", EVENT_BOX, null, 5, 8,
+        `/{"tooltip":"itemTooltip","calendar":"${CALENDARNAME.toLowerCase()}"}`
+    ));
 }
 
 function teardownTest(module) {
     deleteCalendars(controller, CALENDARNAME);
 }
--- a/mail/test/mozmill/runtest.py
+++ b/mail/test/mozmill/runtest.py
@@ -444,17 +444,17 @@ def logFailure(obj):
 mozmill.LoggerListener.cases['mozmill.fail'] = logFailure
 
 
 def prettifyFilename(path, tail_segs_desired=1):
     parts = path.split('/')
     return '/'.join(parts[-tail_segs_desired:])
 
 def prettyPrintException(e):
-    print '  EXCEPTION:', e.get('message', 'no message!')
+    print '  EXCEPTION:', e.get('message', 'no message!').encode('utf-8')
     print '    at:', prettifyFilename(e.get('fileName', 'nonesuch')), 'line', e.get('lineNumber', 0)
     if 'stack' in e:
         for line in e['stack'].splitlines():
             if not line:
                 continue
             if line[0] == "(":
                 funcname = None
             elif line[0] == "@":
--- a/mail/test/resources/mozmill/mozmill/extension/content/modules/controller.js
+++ b/mail/test/resources/mozmill/mozmill/extension/content/modules/controller.js
@@ -762,17 +762,17 @@ MozMillController.prototype.fireEvent = 
 MozMillController.prototype.startUserShutdown = function (timeout, restart) {
   // 0 = default shutdown, 1 = user shutdown, 2 = user restart
   this.fireEvent('userShutdown', restart ? 2 : 1);
   this.window.setTimeout(this.fireEvent, timeout, 'userShutdown', 0);
 }
 
 /* Select the specified option and trigger the relevant events of the element.*/
 MozMillController.prototype.select = function (el, indx, option, value) {
-  element = el.getNode();
+  var element = el.getNode();
   if (!element){
     throw new Error("Could not find element " + el.getInfo());
     return false;
   }
 
   //if we have a select drop down
   if (element.localName.toLowerCase() == "select"){
     var item = null;
@@ -812,17 +812,17 @@ MozMillController.prototype.select = fun
       return true;
     } catch (ex) {
       throw new Error("No item selected for element " + el.getInfo());
      return false;
    }
   }
   //if we have a xul menulist select accordingly
   else if (element.localName.toLowerCase() == "menulist") {
-    ownerDoc = element.ownerDocument;
+    var ownerDoc = element.ownerDocument;
     // Unwrap the XUL element's XPCNativeWrapper
     if (element.namespaceURI.toLowerCase() == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul") {
       element = utils.unwrapNode(element);
     }
 
     var item = null;
 
     if (indx != undefined) {
--- a/mail/test/resources/mozmill/mozmill/extension/content/modules/frame.js
+++ b/mail/test/resources/mozmill/mozmill/extension/content/modules/frame.js
@@ -105,17 +105,17 @@ var loadFile = function(path, collector)
   module.persisted = persisted;
   module.Cc = Cc;
   module.Ci = Ci;
   module.Cu = Cu;
   module.require = function (mod) {
     var loader = new securableModule.Loader({
       rootPaths: [ios.newFileURI(file.parent).spec],
       defaultPrincipal: "system",
-      globals : { mozmill, elementslib, persistsed, Cc, Ci, Cu }
+      globals : { mozmill, elementslib, persisted, Cc, Ci, Cu }
     });
     if (modules != undefined) {
         loader.modules = modules;
     }
     var retval = loader.require(mod);
     modules = loader.modules;
     return retval;
   }