diff --git a/mobile/android/base/tests/BaseTest.java.in b/mobile/android/base/tests/BaseTest.java.in index 8004342e293e..dccd11fd0c79 100644 --- a/mobile/android/base/tests/BaseTest.java.in +++ b/mobile/android/base/tests/BaseTest.java.in @@ -56,6 +56,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { private String mLogFile; protected String mProfile; public Device mDevice; + private boolean menuSettingsTimedOut = false; protected void blockForGeckoReady() { try { @@ -401,6 +402,9 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { return rc; } + /** + * Wait for to be visible and also be enabled/clickable. + */ public boolean waitForEnabledText(String text) { final String testText = text; boolean rc = waitForTest(new BooleanTest() { @@ -436,15 +440,11 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { public void selectSettingsItem(String section, String item) { String itemName = "^" + item + "$"; selectMenuItem("Settings"); - // On tablets, settings are arranged in a hierarchy; if the item is not - // in the first ("General") section, the section must be selected first. - if (mDevice.type.equals("tablet") && - section != null && - !section.equals("General")) { - String sectionName = "^" + section + "$"; - waitForEnabledText(sectionName); - mSolo.clickOnText(sectionName); - } + String sectionName = "^" + section + "$"; + // Click on section name. + waitForEnabledText(sectionName); + mSolo.clickOnText(sectionName); + // Click on item. waitForEnabledText(itemName); mSolo.clickOnText(itemName); } @@ -453,9 +453,12 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { // build the item name ready to be used String itemName = "^" + menuItemName + "$"; mActions.sendSpecialKey(Actions.SpecialKey.MENU); - if (waitForText(itemName)) { + if (!menuSettingsTimedOut && waitForText(itemName)) { mSolo.clickOnText(itemName); } else { + // Record that we've timed out waiting for "Settings" on the top menu + // screen so we can skip the timeout step next time. + menuSettingsTimedOut = true; if (mSolo.searchText("(^More$|^Tools$)")) { mSolo.clickOnText("(^More$|^Tools$)"); } diff --git a/mobile/android/base/tests/robocop.ini b/mobile/android/base/tests/robocop.ini index eee826237f21..3d7479c86fac 100644 --- a/mobile/android/base/tests/robocop.ini +++ b/mobile/android/base/tests/robocop.ini @@ -32,7 +32,7 @@ [testTabHistory] [testShareLink] [testClearPrivateData] -# [testSettingsMenuItems] # see bug 843947 +[testSettingsMenuItems] [testSystemPages] # [testPermissions] # see bug 757475 [testJarReader] diff --git a/mobile/android/base/tests/testAboutPage.java.in b/mobile/android/base/tests/testAboutPage.java.in index 2d01ea35d0b0..e2f5a133fe81 100644 --- a/mobile/android/base/tests/testAboutPage.java.in +++ b/mobile/android/base/tests/testAboutPage.java.in @@ -32,7 +32,7 @@ public class testAboutPage extends PixelTest { Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added"); Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); - selectSettingsItem("General", "About (Fennec|Nightly|Aurora|Firefox|Firefox Beta)"); + selectSettingsItem("Mozilla", "About (Fennec|Nightly|Aurora|Firefox|Firefox Beta)"); // Wait for the new tab and page to load tabEventExpecter.blockForEvent(); diff --git a/mobile/android/base/tests/testClearPrivateData.java.in b/mobile/android/base/tests/testClearPrivateData.java.in index eaec399b8221..cca60831c348 100644 --- a/mobile/android/base/tests/testClearPrivateData.java.in +++ b/mobile/android/base/tests/testClearPrivateData.java.in @@ -30,7 +30,7 @@ public class testClearPrivateData extends PixelTest { waitForText("Browser Blank Page 01"); // Clearing private data - selectSettingsItem("Privacy & Security", "Clear private data"); + selectSettingsItem("Privacy", "Clear private data"); mAsserter.ok(mSolo.searchButton("Clear data"),"checking clear button","clear button exists"); mSolo.clickOnButton("Clear data"); // TODO: extra long wait here for bug 837274 diff --git a/mobile/android/base/tests/testImportFromAndroid.java.in b/mobile/android/base/tests/testImportFromAndroid.java.in index e44cecad8a49..91e0ca4307ec 100644 --- a/mobile/android/base/tests/testImportFromAndroid.java.in +++ b/mobile/android/base/tests/testImportFromAndroid.java.in @@ -134,9 +134,7 @@ public class testImportFromAndroid extends PixelTest { private void importDataFromAndroid() { waitForText("Enter Search or Address"); - selectMenuItem("Settings"); - waitForText("General"); - mSolo.clickOnText("Import from Android"); + selectSettingsItem("Customize", "Import from Android"); // Wait for the Import form Android popup to be opened. It has the same title as the option so waiting for the "Cancel" button waitForText("Cancel"); @@ -145,9 +143,16 @@ public class testImportFromAndroid extends PixelTest { mSolo.clickOnButton("Import"); // Import has finished. Waiting to get back to the Settings Menu and looking for the Import&Export subsection - waitForText("Import & Export"); + if ("phone".equals(mDevice.type)) { + // Phones don't have headers like tablets, so we need to pop up one more level. + waitForText("Import from Android"); + mActions.sendSpecialKey(Actions.SpecialKey.BACK); + } + waitForText("Settings"); mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Exit Settings - waitForText("Enter Search or Address"); // Make sure we are not in the Settings Menu anymore + // Make sure the settings menu has been closed. + mAsserter.ok(mSolo.waitForText("Enter Search or Address"), "Waiting for search bar", "Search bar found"); + } public ArrayList getAndroidUrls(String data) { diff --git a/mobile/android/base/tests/testMasterPassword.java.in b/mobile/android/base/tests/testMasterPassword.java.in index 1f0685b420fd..e0fe583511d2 100644 --- a/mobile/android/base/tests/testMasterPassword.java.in +++ b/mobile/android/base/tests/testMasterPassword.java.in @@ -28,13 +28,7 @@ public class testMasterPassword extends PixelTest { public void enableMasterPassword(String password, String badPassword) { // Look for the 'Settings' menu if this device/OS uses it - selectMenuItem("Settings"); - if (dev.type.equals("tablet")) { - waitForText("Privacy & Security"); - mSolo.clickOnText("Privacy & Security"); - } - waitForText("^Use master password$"); - mSolo.clickOnText("^Use master password$"); + selectSettingsItem("Privacy", "Use master password"); waitForText("^Create Master Password$"); // Verify that the OK button is not activated until both fields are filled @@ -76,25 +70,25 @@ public class testMasterPassword extends PixelTest { mSolo.clickOnButton("OK"); // Verify that the Master Password was set - mSolo.searchText("Privacy & Security"); + mSolo.searchText("Privacy"); mAsserter.ok(mSolo.waitForText("^Use master password$"), "Checking if Use master password is present", "Use master password is present"); mSolo.clickOnText("^Use master password$"); mAsserter.ok(mSolo.waitForText("Remove Master Password"), "Checking if the password is enabled", "The password is enabled"); clickOnButton("Cancel"); // Go back to settings menu - waitForText("^Settings$"); + + if ("phone".equals(mDevice.type)) { + // Phones don't have headers like tablets, so we need to pop up one more level. + waitForText("Use master password"); + mActions.sendSpecialKey(Actions.SpecialKey.BACK); + } + waitForText("Settings"); mActions.sendSpecialKey(Actions.SpecialKey.BACK);// Close the Settings Menu } public void disableMasterPassword(String password, String badPassword) { // Look for the 'Settings' menu if this device/OS uses it - selectMenuItem("Settings"); - if (dev.type.equals("tablet")) { - waitForText("Privacy & Security"); - mSolo.clickOnText("Privacy & Security"); - } - waitForText("^Use master password$"); - mSolo.clickOnText("^Use master password$"); + selectSettingsItem("Privacy", "Use master password"); waitForText("^Remove Master Password$"); // Verify that the OK button is not activated if the password field is empty @@ -115,7 +109,7 @@ public class testMasterPassword extends PixelTest { mSolo.clickOnButton("OK"); // Verify that the Master Password was disabled - mSolo.searchText("Privacy & Security"); + mSolo.searchText("Privacy"); mAsserter.ok(mSolo.waitForText("^Use master password$"), "Checking if Use master password is present", "Use master password is present"); mSolo.clickOnText("^Use master password$"); mAsserter.ok(waitForText("^Create Master Password$"), "Checking if the password is disabled", "The password is disabled"); @@ -125,7 +119,7 @@ public class testMasterPassword extends PixelTest { public void editPasswordField(int i, String password) { mSolo.clickOnEditText(i); mActions.sendKeys(password); - mActions.sendSpecialKey(Actions.SpecialKey.BACK); // Close the VKB + toggleVKB(); // Don't use BACK; this will close the password dialog on devices with hardware keyboard. } public void noDoorhangerDisplayed(String LOGIN_URL) { @@ -151,15 +145,8 @@ public class testMasterPassword extends PixelTest { public void clearPrivateData() { // Look for the 'Settings' menu if this device/OS uses it - selectMenuItem("Settings"); - if (dev.type.equals("tablet")) { - waitForText("Privacy & Security"); - mSolo.clickOnText("Privacy & Security"); - } + selectSettingsItem("Privacy", "Clear private data"); - // Clear private data - waitForText("^Clear private data$"); - mSolo.clickOnText("^Clear private data$"); waitForText("Browsing & download history"); // Make sure the Clear private data pop-up is displayed Actions.EventExpecter clearPrivateDataEventExpecter = mActions.expectGeckoEvent("Sanitize:Finished"); if (mSolo.searchText("Clear data") && !mSolo.searchText("Cookies")) { @@ -181,9 +168,16 @@ public class testMasterPassword extends PixelTest { mSolo.clickOnText("^Use master password$"); mAsserter.ok(mSolo.searchText("^Remove Master Password$"), "Checking if the master password was disabled by clearing private data", "The master password is not disabled by clearing private data"); clickOnButton("Cancel"); // Close the Master Password menu - waitForText("^Settings$"); + + if ("phone".equals(mDevice.type)) { + // Phones don't have headers like tablets, so we need to pop up one more level. + waitForText("Use master password"); + mActions.sendSpecialKey(Actions.SpecialKey.BACK); + } + waitForText("Settings"); mActions.sendSpecialKey(Actions.SpecialKey.BACK);// Close the Settings Menu - waitForText("Browser Blank Page 01"); // Make sure the Settings Menu has been closed + // Make sure the settings menu has been closed. + mAsserter.ok(mSolo.waitForText("Browser Blank Page 01"), "Waiting for blank browser page after exiting settings", "Blank browser page present"); } public void verifyLoginPage(String password, String badPassword) { @@ -212,8 +206,7 @@ public class testMasterPassword extends PixelTest { // Verify that the Master Password is triggered once per session noDoorhangerDisplayed(LOGIN_URL);// Check that the doorhanger isn't displayed - } - else { + } else { clearPrivateData(); doorhangerDisplayed(LOGIN_URL);// Check that the doorhanger is displayed mAsserter.ok(mSolo.waitForText("Don't save"), "Checking if Don't save option is present again", "Don't save option is present again"); @@ -221,7 +214,8 @@ public class testMasterPassword extends PixelTest { doorhangerDisplayed(LOGIN_URL);// Check that the doorhanger is displayed again mAsserter.ok(mSolo.waitForText("Don't save"), "Checking if Don't save option is present again", "Don't save option is present again"); mSolo.clickOnText("Don't save"); - waitForText("Browser Blank Page 01"); + // Make sure the settings menu has been closed. + mAsserter.ok(mSolo.waitForText("Browser Blank Page 01"), "Waiting for blank browser page after exiting settings", "Blank browser page present"); } } } diff --git a/mobile/android/base/tests/testSettingsMenuItems.java.in b/mobile/android/base/tests/testSettingsMenuItems.java.in index c6be036aa865..fc0165a791f7 100644 --- a/mobile/android/base/tests/testSettingsMenuItems.java.in +++ b/mobile/android/base/tests/testSettingsMenuItems.java.in @@ -3,12 +3,60 @@ package @ANDROID_PACKAGE_NAME@.tests; import @ANDROID_PACKAGE_NAME@.*; -/** This patch tests the Sections present in the Settings Menu and the +import java.util.Map; +import java.util.Map.Entry; +import java.util.HashMap; + +/** This patch tests the Sections present in the Settings Menu and the * default values for them */ public class testSettingsMenuItems extends PixelTest { int midWidth; int midHeight; + String BRAND_NAME = "(Fennec|Nightly|Aurora|Firefox|Firefox Beta)"; + + // The following String[][] (arrays) match the menu hierarchy for each section. + // Each String[] (array) represents the menu items/choices in the following order: + // + // itemTitle { defaultValue [options] } + // + // where defaultValue is optional, and there can be multiple options. + // + // This test assumes menu items are in order (scrolling down for off-screen items). + String[][] OPTIONS_CUSTOMIZE = { + { "Import from Android", "", "Bookmarks", "History", "Import" }, + { "Show search suggestions" }, + { "Automatic updates", "Only over Wi-Fi", "Enabled", "Only over Wi-Fi", "Disabled" }, + }; + + String[][] OPTIONS_DISPLAY = { + { "Text size" }, + { "Double tap to reflow text" }, + { "Title bar", "Show page title", "Show page title", "Show page address" }, + { "Character encoding", "Don't show menu", "Show menu", "Don't show menu" }, + { "Plugins", "Tap to play", "Enabled", "Tap to play", "Disabled" }, + }; + + String[][] OPTIONS_PRIVACY = { + { "Tracking", "Do not tell sites anything about my tracking preferences", "Tell sites that I do not want to be tracked", "Tell sites that I want to be tracked", "Do not tell sites anything about my tracking preferences" }, + { "Cookies", "Enabled", "Enabled, excluding 3rd party", "Disabled" }, + { "Remember passwords" }, + { "Use master password" }, + { "Clear private data", "", "Browsing & download history", "Downloaded files", "Form & search history", "Cookies & active logins", "Saved passwords", "Cache", "Offline website data", "Site settings", "Clear data" }, + }; + + String[][] OPTIONS_MOZILLA = { + { "About " + BRAND_NAME }, + { "FAQs" }, + { "Give feedback" }, + { "Show product announcements" }, + { "Data choices" }, + { "Telemetry", "Shares performance, usage, hardware and customization data about your browser with Mozilla to help us make " + BRAND_NAME + " better" }, + { "Crash Reporter", BRAND_NAME + " submits crash reports to help Mozilla make your browser more stable and secure" }, + { "Mozilla location services", "Help improve geolocation services for the Open Web by letting " + BRAND_NAME + " collect and send anonymous cellular tower data" }, + { BRAND_NAME + " Health Report", "Shares data with Mozilla about your browser health and helps you understand your browser performance" }, + { "View my Health Report" }, + }; @Override protected int getTestType() { @@ -20,22 +68,23 @@ public class testSettingsMenuItems extends PixelTest { midWidth = mDriver.getGeckoWidth()/2; midHeight = mDriver.getGeckoHeight()/2; - // In this First Section I've declared the strings that contain the Menu sections - - // The first string contains menu sections that do not have multiple option - String menuItems [] = { "General", "About", "Sync", "Content", "Pinch to reflow text", "Privacy & Security", "Tell sites not to track me", "Remember passwords", "Use master password", "Show search suggestions", "Show product announcements", "Import & Export"}; - // This string contains every menu section that have multiple selection options followed by the option that is selected by default - String defaultSectionsValues [] = { "Automatic updates", "Only over Wi-Fi", "Character encoding", "Don't show menu","Plugins", "Tap to play", "Text size", "Tiny", "Cookies", "Enable"}; - - // This string contains multiple selection options and every option available for each of them - String multipleSectionsOptions[][] = { - { "Automatic updates", "Enable", "Only over Wi-Fi", "Disable"}, - { "Character encoding", "Show menu", "Don't show menu"}, - { "Plugins", "Enable", "Tap to play", "Disable"}, - { "Cookies", "Enable", "Enabled, excluding 3rd party", "Disable"}, - { "Clear private data", "Browsing & download history", "Form & search history", "Cookies & active logins", "Saved passwords", "Cache", "Offline website data", "Site preferences"}, - { "Import from Android", "Bookmarks", "History"} - }; + /* + * This settingsMenuItems Map provides the Settings hierarchy to test. + * + * The keys are the top-level settings categories, and the values are the + * array of menu items contained within each category. + * + * Each menu item is itself an array as follows: + * - item title + * - default string value of item (optional) + * - string values of options that are displayed once clicked (optional). + */ + Map settingsMenuItems = new HashMap(); + // Add items for each category. + settingsMenuItems.put("Customize", OPTIONS_CUSTOMIZE); + settingsMenuItems.put("Display", OPTIONS_DISPLAY); + settingsMenuItems.put("Privacy", OPTIONS_PRIVACY); + settingsMenuItems.put("Mozilla", OPTIONS_MOZILLA); selectMenuItem("Settings"); @@ -46,44 +95,93 @@ public class testSettingsMenuItems extends PixelTest { waitForText("Enter Search"); // Waiting for page title to appear to be sure that is fully loaded before opening the menu selectMenuItem("Settings"); - checkMenuSections(menuItems); - mSolo.scrollUp(); // Scrolling up the page to have in view the top sections - mSolo.scrollUp(); // Repeat scroll up because the top sections were not reached + checkForSync(mDevice); - checkMenuSections(defaultSectionsValues); - mSolo.scrollUp(); // Scrolling up the page to have in view the top sections again - - checkMenuSubSections(multipleSectionsOptions); + checkMenuHierarchy(settingsMenuItems); } - public void checkMenuSections(String menuItems []) { - for (String item:menuItems) { - mAsserter.ok(mSolo.waitForText(item), "Waiting for " + item + " option", "The " + item + " option is present"); + + /** + * Check for Sync in settings. + * + * Sync location is a top level menu item on phones, but is under "Customize" on tablets. + * + */ + public void checkForSync(Device device) { + if (device.type.equals("tablet")) { + // Select "Customize" from settings. + String customizeString = "^Customize$"; + waitForEnabledText(customizeString); + mSolo.clickOnText(customizeString); } + mAsserter.ok(mSolo.waitForText("Sync"), "Waiting for Sync option", "The Sync option is present"); } - // This verifies the multiple option sections sub menus - public void checkMenuSubSections(String multipleSectionsOptions [][]) { - int i,j = 0; - for (i = 0; i <= multipleSectionsOptions [j].length; i++) { - String item = multipleSectionsOptions [i][j]; - mAsserter.ok(mSolo.waitForText(item), "Waiting for " + item + " option", "The " + item + " option is present"); - mSolo.clickOnText(item); - for (j = 1; j < multipleSectionsOptions [i].length; j++) { - String subMenu = multipleSectionsOptions [i][j]; - mAsserter.ok(mSolo.waitForText(subMenu), "Waiting for " + subMenu + " option", "The " + subMenu + " option is present"); - // When reaching the specified sub menu section, for phone devices you have to scroll down the list in order to check all the options - if (subMenu.equals("Offline website data")) { - Device mDevice = new Device(); - if (mDevice.type.equals("phone")) { - MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); - meh.dragSync(midWidth, midHeight+100, midWidth, midHeight-100); - } + public void checkMenuHierarchy(Map settingsMap) { + // Check the items within each category. + for (Entry e : settingsMap.entrySet()) { + String section = "^" + e.getKey() + "$"; + String[][] sectionItems = e.getValue(); + + waitForEnabledText(section); + mSolo.clickOnText(section); + + // Check each item of the section. + for (String[] item : sectionItems) { + int itemLen = item.length; + + // Each item must at least have a title. + mAsserter.ok(item.length > 0, "Section-item", "Each item must at least have a title"); + + // Check item title. + String itemTitle = "^" + item[0] + "$"; + if (!waitForText(itemTitle)) { + // If we don't see the item, scroll down once in case it's off-screen. + scrollDown(); + } + mAsserter.ok(mSolo.waitForText(itemTitle), "Waiting for settings item " + itemTitle + " in section " + section, + "The " + itemTitle + " option is present in section " + section); + // Check item default, if it exists. + if (itemLen > 1) { + String itemDefault = "^" + item[1] + "$"; + mAsserter.ok(mSolo.waitForText(itemDefault), "Waiting for settings item default " + itemDefault + + " in section " + section, + "The " + itemDefault + " default is present in section " + section); + } + // Check item choices, if they exist. + if (itemLen > 2) { + waitForEnabledText(itemTitle); + mSolo.clickOnText(itemTitle); + for (int i = 2; i < itemLen; i++) { + String itemChoice = "^" + item[i] + "$"; + if (!waitForText(itemChoice)) { + // If we don't see the item, scroll down once in case it's off-screen. + scrollDown(); + } + mAsserter.ok(mSolo.waitForText(itemChoice), "Waiting for settings item choice " + itemChoice + + " in section " + section, + "The " + itemChoice + " choice is present in section " + section); + } + // Leave submenu after checking. + waitForText("^Cancel$"); + mSolo.clickOnText("^Cancel$"); } } - waitForText("^Cancel$"); - mSolo.clickOnText("^Cancel$"); - j = 0; + // Navigate back a screen if on a phone. + if (mDevice.type.equals("phone")) { + // Click back to return to previous menu. Tablets shouldn't do this because they use headers and fragments. + mActions.sendSpecialKey(Actions.SpecialKey.BACK); + } } } + + /** + * Hacky way to scroll down. + * + * solo.scroll* does not work in dialogs. + */ + private void scrollDown() { + MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + meh.dragSync(midWidth, midHeight+100, midWidth, midHeight-100); + } } diff --git a/mobile/android/base/tests/testSystemPages.java.in b/mobile/android/base/tests/testSystemPages.java.in index fcbf025676e6..620a7a6227eb 100644 --- a/mobile/android/base/tests/testSystemPages.java.in +++ b/mobile/android/base/tests/testSystemPages.java.in @@ -81,7 +81,7 @@ public class testSystemPages extends PixelTest { } else { // Make sure the about: page was loaded without opening a new tab and verify the page Url paintExpecter = mActions.expectPaint(); // Set up listener to catch the page load - selectSettingsItem("General", "About (Fennec|Nightly|Aurora|Firefox|Firefox Beta)"); + selectSettingsItem("Mozilla", "About (Fennec|Nightly|Aurora|Firefox|Firefox Beta)"); waitForPaint(paintExpecter); // Waiting for the page to load paintExpecter.unregisterListener();