#filter substitution package @ANDROID_PACKAGE_NAME@.tests; import com.jayway.android.robotium.solo.Solo; import @ANDROID_PACKAGE_NAME@.*; import android.app.Activity; import android.app.Instrumentation; import android.content.ContentResolver; import android.content.ContentValues; import android.content.ContentUris; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.AssetManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.SystemClock; import android.test.ActivityInstrumentationTestCase2; import android.util.DisplayMetrics; import android.view.inputmethod.InputMethodManager; import android.view.View; import android.widget.Button; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; /** * A convenient base class suitable for most Robocop tests. */ abstract class BaseTest extends ActivityInstrumentationTestCase2 { public static final int TEST_MOCHITEST = 0; public static final int TEST_TALOS = 1; private static final String TARGET_PACKAGE_ID = "org.mozilla.gecko"; private static final String LAUNCH_ACTIVITY_FULL_CLASSNAME="@ANDROID_PACKAGE_NAME@.App"; private static final int VERIFY_URL_TIMEOUT = 2000; private static final int MAX_LIST_ATTEMPTS = 3; private static final int MAX_WAIT_ENABLED_TEXT_MS = 10000; public static final int MAX_WAIT_MS = 3000; private static Class mLauncherActivityClass; private Activity mActivity; protected Solo mSolo; protected Driver mDriver; protected Assert mAsserter; protected Actions mActions; protected String mBaseUrl; protected String mRawBaseUrl; private String mLogFile; protected String mProfile; public Device mDevice; protected void blockForGeckoReady() { try { Actions.EventExpecter geckoReadyExpector = mActions.expectGeckoEvent("Gecko:Ready"); ClassLoader classLoader = getActivity().getClassLoader(); Class appsCls = classLoader.loadClass("org.mozilla.gecko.GeckoThread"); Class launchStateCls = classLoader.loadClass("org.mozilla.gecko.GeckoThread$LaunchState"); Method checkLaunchState = appsCls.getMethod("checkLaunchState", launchStateCls); Object states[] = launchStateCls.getEnumConstants(); Boolean ret = (Boolean)checkLaunchState.invoke(null, states[3]); if (!ret.booleanValue()) { geckoReadyExpector.blockForEvent(); } geckoReadyExpector.unregisterListener(); } catch (Exception e) { mAsserter.dumpLog("Exception in blockForGeckoReady", e); } } static { try { mLauncherActivityClass = (Class)Class.forName(LAUNCH_ACTIVITY_FULL_CLASSNAME); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public BaseTest() { super(TARGET_PACKAGE_ID, mLauncherActivityClass); } protected abstract int getTestType(); @Override protected void setUp() throws Exception { // Load config file from root path (setup by python script) String rootPath = FennecInstrumentationTestRunner.getArguments().getString("deviceroot"); String configFile = FennecNativeDriver.getFile(rootPath + "/robotium.config"); HashMap config = FennecNativeDriver.convertTextToTable(configFile); // Create the intent to be used with all the important arguments. Intent i = new Intent(Intent.ACTION_MAIN); mProfile = (String)config.get("profile"); i.putExtra("args", "-no-remote -profile " + mProfile); String envString = (String)config.get("envvars"); if (envString != "") { String[] envStrings = envString.split(","); for (int iter = 0; iter < envStrings.length; iter++) { i.putExtra("env" + iter, envStrings[iter]); } } // Start the activity setActivityIntent(i); mActivity = getActivity(); mLogFile = (String)config.get("logfile"); mBaseUrl = ((String)config.get("host")).replaceAll("(/$)", ""); mRawBaseUrl = ((String)config.get("rawhost")).replaceAll("(/$)", ""); // Initialize the asserter if (getTestType() == TEST_TALOS) { mAsserter = new FennecTalosAssert(); } else { mAsserter = new FennecMochitestAssert(); } mAsserter.setLogFile(mLogFile); mAsserter.setTestName(this.getClass().getName()); // Set up Robotium.solo and Driver objects mSolo = new Solo(getInstrumentation(), mActivity); mDriver = new FennecNativeDriver(mActivity, mSolo, rootPath); mActions = new FennecNativeActions(mActivity, mSolo, getInstrumentation(), mAsserter); mDevice = new Device(); } @Override protected void runTest() throws Throwable { try { super.runTest(); } catch (Throwable t) { // save screenshot -- written to /mnt/sdcard/Robotium-Screenshots // as .jpg mSolo.takeScreenshot("robocop-screenshot"); if (mAsserter != null) { mAsserter.dumpLog("Exception caught during test!", t); mAsserter.ok(false, "Exception caught", t.toString()); } // re-throw to continue bail-out throw t; } } @Override public void tearDown() throws Exception { try { mAsserter.endTest(); mSolo.finishOpenedActivities(); } catch (Throwable e) { e.printStackTrace(); } super.tearDown(); } public void assertMatches(String value, String regex, String name) { if (value == null) { mAsserter.ok(false, name, "Expected /" + regex + "/, got null"); return; } mAsserter.ok(value.matches(regex), name, "Expected /" + regex +"/, got \"" + value + "\""); } /** * Click on the specified element and return the resulting activity. * @return The created activity, or null if the element cannot be clicked. */ protected final Activity getActivityFromClick(Element element) { Instrumentation inst = getInstrumentation(); Instrumentation.ActivityMonitor monitor = inst.addMonitor((String)null, null, false); boolean clicked = element.click(); if (!clicked) { mAsserter.ok(clicked != false, "checking that awesome bar clicked", "awesome bar was clicked"); return null; } inst.waitForMonitor(monitor); // Give the activity time to render itself and initialize views // before continuing, so that views are created before access // attempts are made. Again, waitForIdleSync was used here // previously, but replaced with a sleep to avoid hangs. // TODO: Investigate and document why these pauses are required. mSolo.sleep(2000); return mSolo.getCurrentActivity(); } /** * Click on the awesome bar element and return the resulting activity. * @return The created activity, or null if the awesome bar cannot be clicked. */ private Activity mAwesomeActivity; protected final Activity clickOnAwesomeBar() { mAwesomeActivity = null; boolean success = waitForTest(new BooleanTest() { @Override public boolean test() { Element awesomebar = mDriver.findElement(mActivity, "browser_toolbar"); if (awesomebar != null) { mAwesomeActivity = getActivityFromClick(awesomebar); if (mAwesomeActivity == null) { mAsserter.dumpLog("failed to click on awesome bar!"); } return mSolo.waitForText("History", 1, MAX_WAIT_MS); } return false; } }, MAX_WAIT_MS*5); if (!success) mAsserter.dumpLog("failed to find History"); return mAwesomeActivity; } protected final void enterUrl(String url) { Activity awesomeBarActivity = clickOnAwesomeBar(); Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text"); mActions.sendKeys(url); String urlbarText = null; if (urlbar != null) { urlbarText = urlbar.getText(); } mAsserter.is(urlbarText, url, "Awesomebar URL typed properly"); } protected final void hitEnterAndWait() { Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded"); mActions.sendSpecialKey(Actions.SpecialKey.ENTER); // wait for screen to load contentEventExpecter.blockForEvent(); contentEventExpecter.unregisterListener(); } /** * Load url using the awesome bar UI and sending key strokes. * * This method waits synchronously for the DOMContentLoaded * message from Gecko before returning. */ protected final void loadUrl(String url) { enterUrl(url); hitEnterAndWait(); } /** * Load url using reflection and the internal * org.mozilla.gecko.Tabs API. * * This method does not wait for any confirmation from Gecko before * returning. */ protected final void loadUrlInTab(final String url) { try { ClassLoader classLoader = getActivity().getClassLoader(); Class tabsClass = classLoader.loadClass("org.mozilla.gecko.Tabs"); Method getInstance = tabsClass.getMethod("getInstance"); Method loadUrlInTab = tabsClass.getMethod("loadUrlInTab", String.class); Object tabs = getInstance.invoke(null); loadUrlInTab.invoke(tabs, new Object[] { url }); } catch (Exception e) { mAsserter.dumpLog("Exception in loadUrlInTab", e); throw new RuntimeException(e); } } public final void verifyUrl(String url) { Activity awesomeBarActivity = clickOnAwesomeBar(); Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text"); String urlbarText = null; if (urlbar != null) { // wait for a short time for the expected text, in case there is a delay // in updating the view waitForTest(new VerifyUrlTest(urlbar, url), VERIFY_URL_TIMEOUT); urlbarText = urlbar.getText(); } mAsserter.is(urlbarText, url, "Awesomebar URL stayed the same"); } class VerifyUrlTest implements BooleanTest { private Element mUrlbar; private String mUrl; public VerifyUrlTest(Element urlbar, String url) { mUrlbar = urlbar; mUrl = url; } @Override public boolean test() { String urlbarText = mUrlbar.getText(); if (urlbarText.equals(mUrl)) { return true; } return false; } } protected final String getAbsoluteUrl(String url) { return mBaseUrl + "/" + url.replaceAll("(^/)", ""); } protected final String getAbsoluteRawUrl(String url) { return mRawBaseUrl + "/" + url.replaceAll("(^/)", ""); } protected final boolean waitForTest(BooleanTest t, int timeout) { long end = SystemClock.uptimeMillis() + timeout; while (SystemClock.uptimeMillis() < end) { if (t.test()) { return true; } mSolo.sleep(100); } // log out wait failure for diagnostic purposes only; // a failed wait may be normal and does not necessarily // warrant a test assertion/failure mAsserter.dumpLog("waitForTest timeout after "+timeout+" ms"); return false; } protected interface BooleanTest { public boolean test(); } @SuppressWarnings({"unchecked", "non-varargs"}) public void SqliteCompare(String dbName, String sqlCommand, ContentValues[] cvs) { File profile = new File(mProfile); String dbPath = new File(profile, dbName).getPath(); Cursor c = mActions.querySql(dbPath, sqlCommand); SqliteCompare(c, cvs); } private boolean CursorMatches(Cursor c, String[] columns, ContentValues cv) { for (int i = 0; i < columns.length; i++) { String column = columns[i]; if (cv.containsKey(column)) { mAsserter.info("Comparing", "Column values for: " + column); Object value = cv.get(column); if (value == null) { if (!c.isNull(i)) { return false; } } else { if (c.isNull(i) || !value.toString().equals(c.getString(i))) { return false; } } } } return true; } @SuppressWarnings({"unchecked", "non-varargs"}) public void SqliteCompare(Cursor c, ContentValues[] cvs) { mAsserter.is(c.getCount(), cvs.length, "List is correct length"); if (c.moveToFirst()) { do { boolean found = false; for (int i = 0; !found && i < cvs.length; i++) { if (CursorMatches(c, cvs[i])) { found = true; } } mAsserter.is(found, true, "Password was found"); } while(c.moveToNext()); } } public boolean CursorMatches(Cursor c, ContentValues cv) { for (int i = 0; i < c.getColumnCount(); i++) { String column = c.getColumnName(i); if (cv.containsKey(column)) { mAsserter.info("Comparing", "Column values for: " + column); Object value = cv.get(column); if (value == null) { if (!c.isNull(i)) { return false; } } else { if (c.isNull(i) || !value.toString().equals(c.getString(i))) { return false; } } } } return true; } public InputStream getAsset(String filename) throws IOException { AssetManager assets = getInstrumentation().getContext().getAssets(); return assets.open(filename); } public boolean waitForText(String text) { boolean rc = mSolo.waitForText(text); if (!rc) { // log out failed wait for diagnostic purposes only; // waitForText failures are sometimes expected/normal mAsserter.dumpLog("waitForText timeout on "+text); } return rc; } public boolean waitForEnabledText(String text) { final String testText = text; boolean rc = waitForTest(new BooleanTest() { @Override public boolean test() { // Solo.getText() could be used here, except that it sometimes // hits an assertion when the requested text is not found. ArrayList views = mSolo.getCurrentViews(); for (View view : views) { if (view instanceof TextView) { TextView tv = (TextView)view; String viewText = tv.getText().toString(); if (tv.isEnabled() && viewText != null && viewText.matches(testText)) { return true; } } } return false; } }, MAX_WAIT_ENABLED_TEXT_MS); if (!rc) { // log out failed wait for diagnostic purposes only; // failures are sometimes expected/normal mAsserter.dumpLog("waitForEnabledText timeout on "+text); } return rc; } /** * Select from Menu > "Settings" >
*/ 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); } waitForEnabledText(itemName); mSolo.clickOnText(itemName); } public final void selectMenuItem(String menuItemName) { // build the item name ready to be used String itemName = "^" + menuItemName + "$"; mActions.sendSpecialKey(Actions.SpecialKey.MENU); if (waitForText(itemName)) { mSolo.clickOnText(itemName); } else { if (mSolo.searchText("(^More$|^Tools$)")) { mSolo.clickOnText("(^More$|^Tools$)"); } waitForText(itemName); mSolo.clickOnText(itemName); } } public final void verifyPageTitle(String title) { Element awesomebar = mDriver.findElement(getActivity(), "awesome_bar_title"); String pageTitle = null; if (awesomebar != null) { // Wait for the title to make sure it has been displayed in case the view // does not update fast enough waitForTest(new VerifyTitle(awesomebar, title), MAX_WAIT_MS); pageTitle = awesomebar.getText(); } mAsserter.is(pageTitle, title, "Page title is correct"); } class VerifyTitle implements BooleanTest { private Element mAwesomebar; private String mTitle; public VerifyTitle(Element awesomebar, String title) { mAwesomebar = awesomebar; mTitle = title; } @Override public boolean test() { String pageTitle = mAwesomebar.getText(); if (pageTitle.equals(mTitle)) { return true; } return false; } } public final void verifyTabCount(int expectedTabCount) { Activity activity = getActivity(); Element tabCount = mDriver.findElement(activity, "tabs_counter"); String tabCountText = tabCount.getText(); int tabCountInt = Integer.parseInt(tabCountText); mAsserter.is(tabCountInt, expectedTabCount, "The correct number of tabs are opened"); } // Used to perform clicks on pop-up buttons without having to close the virtual keyboard public void clickOnButton(String label) { final Button button = mSolo.getButton(label); try { runTestOnUiThread(new Runnable() { @Override public void run() { button.performClick(); } }); } catch (Throwable throwable) { mAsserter.ok(false, "Unable to click the button","Was unable to click button "); } } // Used to hide/show the virtual keyboard public void toggleVKB() { InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE); imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); } protected boolean isBookmark(String[] bookmarks, String aValue) { for (int i = 0; i < bookmarks.length; i++) { if (bookmarks[i].equals(aValue)) return true; } return false; } private ListView getAwesomeList(String waitText, int expectedChildCount, String clickText, String tagName, String callerName) { ArrayList views; ListView tabView = null; int childCount = 0; for (int i = 0; i < MAX_LIST_ATTEMPTS; i++) { tabView = null; childCount = 0; Activity awesomeBarActivity = clickOnAwesomeBar(); mSolo.clickOnText(clickText); if (waitForText(waitText)) { views = mSolo.getCurrentListViews(); for (ListView view : views) { if (view.getTag().equals(tagName)) { tabView = view; ListAdapter adapter = view.getAdapter(); if (adapter != null) { childCount = adapter.getCount(); } else { childCount = 0; } if (expectedChildCount < 0 || expectedChildCount == childCount) { return view; } } } } } if (tabView == null) { mAsserter.dumpLog(callerName+" did not find ListView"); } else if (expectedChildCount >= 0) { mAsserter.dumpLog(callerName+" found ListView with "+childCount+" children"); } return null; } /** * Click on the awesome bar, click on the Top Sites tab, and return * the ListView for the All Pages ("Top Sites") tab. */ protected ListView getAllPagesList(String waitText, int expectedChildCount) { return getAwesomeList(waitText, expectedChildCount, "Top Sites", "allPages", "getAllPagesList"); } protected ListView getAllPagesList(String waitText) { return getAllPagesList(waitText, -1); } /** * Click on the awesome bar, click on the Bookmarks tab, and return * the ListView for the Bookmarks tab. */ protected ListView getBookmarksList(String waitText, int expectedChildCount) { return getAwesomeList(waitText, expectedChildCount, "Bookmarks", "bookmarks", "getBookmarksList"); } protected ListView getBookmarksList(String waitText) { return getBookmarksList(waitText, -1); } /** * Click on the awesome bar, click on the History tab, and return * the ListView for the History tab. */ protected ListView getHistoryList(String waitText, int expectedChildCount) { return getAwesomeList(waitText, expectedChildCount, "History", "history", "getHistoryList"); } protected ListView getHistoryList(String waitText) { return getHistoryList(waitText, -1); } public long addOrUpdateBookmark(String title, String url, boolean bookmarklet) { ContentResolver resolver = getActivity().getContentResolver(); Uri bookmarksUri = Uri.parse("content://@ANDROID_PACKAGE_NAME@.db.browser/bookmarks"); bookmarksUri = bookmarksUri.buildUpon().appendQueryParameter("profile", "default").build(); long folderId = -1; Cursor c = null; // NOTE: this is hardcoded to toolbar/mobile and triggered from bookmarklet because the existing // testcases were written this way String location = "toolbar"; if (bookmarklet) { location = "mobile"; } try { c = resolver.query(bookmarksUri, new String[] { "_id" }, "guid = ?", new String[] { location }, null); if (c.moveToFirst()) { folderId = c.getLong(c.getColumnIndexOrThrow("_id")); } } finally { if (c != null) { c.close(); } } ContentValues values = new ContentValues(); values.put("title", title); values.put("url", url); values.put("parent", folderId); long now = System.currentTimeMillis(); values.put("modified", now); if (!bookmarklet) { values.put("type", 1); values.put("guid", url); values.put("position", 10); values.put("created", now); } int updated = resolver.update(bookmarksUri, values, "url = ?", new String[] { url }); if (updated == 0) { Uri uri = resolver.insert(bookmarksUri, values); mAsserter.ok(true, "Inserted at: ", uri.toString()); return ContentUris.parseId(uri); } return ContentUris.parseId(bookmarksUri); } public void deleteBookmark(String title) { ContentResolver resolver = getActivity().getContentResolver(); Uri uri = Uri.parse("content://@ANDROID_PACKAGE_NAME@.db.browser/bookmarks"); uri = uri.buildUpon().appendQueryParameter("profile", "default") .appendQueryParameter("sync", "true").build(); resolver.delete(uri, "title = ?", new String[] { title }); } public void addTab(String url) { Element tabs = null; Element addTab = null; Activity activity = getActivity(); tabs = mDriver.findElement(activity, "tabs"); addTab = mDriver.findElement(activity, "add_tab"); final int addTabId = addTab.getId(); mAsserter.ok(tabs.click(), "checking that tabs clicked", "tabs element clicked"); // wait for addTab to appear (this is usually immediate) boolean success = waitForTest(new BooleanTest() { @Override public boolean test() { View addTabView = getActivity().findViewById(addTabId); if (addTabView == null) { return false; } return true; } }, MAX_WAIT_MS); mAsserter.ok(success, "waiting for add tab view", "add tab view available"); mAsserter.ok(addTab.click(), "checking that add_tab clicked", "add_tab element clicked"); // must pause before sending keys, until awesome bar is displayed; waiting for known text is simple waitForText("History"); // cannot use loadUrl(): getText fails because we are using a different urlbar mActions.sendKeys(url); hitEnterAndWait(); } public final void runOnUiThreadSync(Runnable runnable) { RobocopUtils.runOnUiThreadSync(mActivity, runnable); } /** * This method will edit the bookmark with index = bookmarkIndex from the list of bookmarks * For the field index: * fieldIndex = 1 - the Bookmark name * fieldIndex = 2 - the Bookmark url * fieldIndex = 3 - the Bookmark keyword */ public void editBookmark(int bookmarkIndex, int fieldIndex, String addedText, ListView list) { // Open the Edit Bookmark context menu View child; mSolo.clickOnText("Bookmarks"); child = list.getChildAt(bookmarkIndex); mAsserter.ok(child != null, "edit item can be retrieved", child != null ? child.toString() : "null!"); waitForText("Switch to tab"); mSolo.clickLongOnView(child); waitForText("Share"); mSolo.clickOnText("Edit"); waitForText("Edit Bookmark"); // Clear the Field mSolo.clearEditText(fieldIndex); // Enter the new text mSolo.clickOnEditText(fieldIndex); mActions.sendKeys(addedText); mSolo.clickOnText("OK"); waitForText("Bookmark updated"); } public boolean checkBookmarkEdit(int bookmarkIndex, String addedText, ListView list) { Device mDevice = new Device(); // Open the Edit Bookmark context menu View child; mSolo.clickOnText("Bookmarks"); child = list.getChildAt(bookmarkIndex); mAsserter.ok(child != null, "check item can be retrieved", child != null ? child.toString() : "null!"); waitForText("Switch to tab"); mSolo.clickLongOnView(child); waitForText("Share"); mSolo.clickOnText("Edit"); waitForText("Edit Bookmark"); // Check if the new text was added if (mSolo.searchText(addedText)) { clickOnButton("Cancel"); waitForText("about:home"); return true; } else { clickOnButton("Cancel"); waitForText("about:home"); return false; } } class Device { public final String version; // 2.x or 3.x or 4.x public String type; // "tablet" or "phone" public final int width; public final int height; public Device() { // Determine device version int sdk = Build.VERSION.SDK_INT; if (sdk < Build.VERSION_CODES.HONEYCOMB) { version = "2.x"; } else { if (sdk > Build.VERSION_CODES.HONEYCOMB_MR2) { version = "4.x"; } else { version = "3.x"; } } // Determine with and height DisplayMetrics dm = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); height = dm.heightPixels; width = dm.widthPixels; // Determine device type type = "phone"; try { ClassLoader classLoader = getActivity().getClassLoader(); Class appsCls = classLoader.loadClass("org.mozilla.gecko.GeckoAppShell"); Method isTabletMethod = appsCls.getMethod("isTablet", (Class[]) null); boolean isTablet = (Boolean)isTabletMethod.invoke(null); if (isTablet) { type = "tablet"; } } catch (Exception e) { mAsserter.dumpLog("Exception in detectDevice", e); } } public void rotate() { if (getActivity().getRequestedOrientation () == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { mSolo.setActivityOrientation(Solo.PORTRAIT); } else { mSolo.setActivityOrientation(Solo.LANDSCAPE); } } } class Navigation { private String devType; private String osVersion; public Navigation(Device mDevice) { devType = mDevice.type; osVersion = mDevice.version; } public void back() { if (devType.equals("tablet")) { Element backBtn = mDriver.findElement(getActivity(), "back"); backBtn.click(); } else { mActions.sendSpecialKey(Actions.SpecialKey.BACK); } } public void forward() { if (devType.equals("tablet")) { Element fwdBtn = mDriver.findElement(getActivity(), "forward"); fwdBtn.click(); } else { mActions.sendSpecialKey(Actions.SpecialKey.MENU); waitForText("^New Tab$"); if (!osVersion.equals("2.x")) { Element fwdBtn = mDriver.findElement(getActivity(), "forward"); fwdBtn.click(); } else { mSolo.clickOnText("^Forward$"); } } } public void reload() { if (devType.equals("tablet")) { Element reloadBtn = mDriver.findElement(getActivity(), "reload"); reloadBtn.click(); } else { mActions.sendSpecialKey(Actions.SpecialKey.MENU); waitForText("^New Tab$"); if (!osVersion.equals("2.x")) { Element reloadBtn = mDriver.findElement(getActivity(), "reload"); reloadBtn.click(); } else { mSolo.clickOnText("^Reload$"); } } } public void bookmark() { if (devType.equals("tablet")) { if (!osVersion.equals("4.x")){ Element bookmarkBtn = mDriver.findElement(getActivity(), "bookmark"); bookmarkBtn.click(); } else { mActions.sendSpecialKey(Actions.SpecialKey.MENU); mSolo.waitForText("^New Tab$"); mSolo.clickOnText("^Bookmark$"); } } else { mActions.sendSpecialKey(Actions.SpecialKey.MENU); mSolo.waitForText("^New Tab$"); if (!osVersion.equals("2.x")) { Element bookmarkBtn = mDriver.findElement(getActivity(), "bookmark"); bookmarkBtn.click(); } else { mSolo.clickOnText("^Bookmark$"); } } } } }