diff --git a/Jenkinsfile b/Jenkinsfile index 7ebc9f440b7..e37ce872fd2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -11,11 +11,11 @@ def matrix = [ 'target': 'i686-linux-android', 'test': true, ], - 'cliqz-alpha':[ + /*'cliqz-alpha':[ 'bundleid': 'com.cliqz.browser.alpha', 'target': 'arm-linux-androideabi', 'test': false, - ], + ],*/ 'ghostery-alpha':[ 'bundleid': 'com.ghostery.android.alpha', 'target': 'arm-linux-androideabi', @@ -35,14 +35,14 @@ def build(Map m){ def apk = "" def testsFolder = "cliqz-mobile-tests" setupTestInstance( - test, - "ami-6c24fc11", - "1", - "t2.medium", - "android_ci_genymotion", - "sg-5bbf173f", - "subnet-341ff61f", - "us-east-1" + test, // Boolean value for Running Tests + "ami-6c24fc11", // Amazon AWS AMI ID + "1", // Count, Number of Instances + "t2.medium", // Instance Size + "android_ci_genymotion", // RSA Key + "sg-5bbf173f", // Secutiry Group ID of AWS + "subnet-341ff61f", // Subnet ID for the instance + "us-east-1" // AWS Region ) { try { stage('Checkout') { @@ -55,10 +55,14 @@ def build(Map m){ image.pull() image.inside { stage('Build Cliqz React Native') { - cliqz.buildCliqzReactNative("cliqz") + cliqz.buildCliqzReactNative("cliqz") // Pass the Folder Name for the React Native SRC } stage("Build APK: ${flavorname}") { - apk = cliqz.buildBrowser("${androidtarget}", "${flavorname}", "ci") + apk = cliqz.buildBrowser( + "${androidtarget}", // Target for the build + "${flavorname}", // Name of the Flavor + "ci" // Type of the Build (CI, Nightly or Release) + ) archiveArtifacts allowEmptyArchive: true, artifacts: "build/${apk}" } } @@ -78,12 +82,22 @@ def build(Map m){ "FLAVOR=${flavorname}", "appPackage=${bundleid}" ]) { - stage('Genymotion ADB Connect') { - genymotion.connectGenyInstance('da5f91e6-e1ca-4aac-94ea-352b6769228b') + stage('Set Genymotion Resolution and Connect') { + genymotion.genySetPhoneResolution('da5f91e6-e1ca-4aac-94ea-352b6769228b') // Pass the Credentials ID for the AWS API Key + genymotion.connectGenyInstance('da5f91e6-e1ca-4aac-94ea-352b6769228b') // Pass the Credentials ID for the AWS API Key + } + stage("Run UIA2 Tests & Upload Results: ${flavorname}"){ + timeout(10){ + cliqz.runUITests("${flavorname}") // Pass the Flavor Name for running the UIAutomator Tests + } } - stage("Run Tests & Upload Results: ${flavorname}") { + stage("Run Appium Tests & Upload Results: ${flavorname}") { timeout(60) { - cliqz.runAppiumTests("${testsFolder}", "${flavorname}", "${apk}") + cliqz.runAppiumTests( + "${testsFolder}", // Path to the Folder where the Tests are cloned. + "${flavorname}", // Flavor Name for running the UIAutomator Tests + "${apk}" // Path to the APK used for testing. + ) } } } @@ -95,7 +109,7 @@ def build(Map m){ error 'Something Failed ! Check Logs above.' } finally { stage('Clean Up') { - utils.cleanUp("${testsFolder}") + utils.cleanUp("${testsFolder}") // Path to the Folder where the Tests are cloned. This will delete the Tests Folder. } } } diff --git a/Jenkinsfile.docker b/Jenkinsfile.docker index e0906d21f0f..ea0fcacb377 100644 --- a/Jenkinsfile.docker +++ b/Jenkinsfile.docker @@ -10,7 +10,7 @@ node('us-east-1 && ubuntu && docker && !gpu') { } def baseImageName = "browser-f/android:${dockerTag}" - + stage('Build Create Docker Image') { docker.withRegistry('https://141047255820.dkr.ecr.us-east-1.amazonaws.com'){ def baseImage = docker.build(baseImageName, '--build-arg USER=`whoami` --build-arg UID=`id -u` --build-arg GID=`id -g` .') diff --git a/mozconfigs/jenkins.mozconfig b/mozconfigs/jenkins.mozconfig index da083e4a215..3b2d614ebed 100644 --- a/mozconfigs/jenkins.mozconfig +++ b/mozconfigs/jenkins.mozconfig @@ -1,6 +1,6 @@ # Build Firefox for Android: # Add MOZILLA_OFFICIAL flag to proguard the artifacts -MOZILLA_OFFICIAL=1 +# MOZILLA_OFFICIAL=1 # Android Targets: 'i686-linux-android' or 'arm-linux-androideabi' ac_add_options --enable-application=mobile/android @@ -8,6 +8,7 @@ ac_add_options --target=$ANDROID_TARGET # With the following Android SDK: ac_add_options --with-android-sdk=$ANDROID_HOME +ac_add_options --with-android-min-sdk=21 # Allow artifact builds: ac_add_options --enable-artifact-builds diff --git a/mozilla-release/build.gradle b/mozilla-release/build.gradle index 31d4ac267e2..26a78f8ffed 100644 --- a/mozilla-release/build.gradle +++ b/mozilla-release/build.gradle @@ -52,6 +52,7 @@ buildscript { url repository } } + google() } ext.kotlin_version = '1.2.41' diff --git a/mozilla-release/mobile/android/app/build.gradle b/mozilla-release/mobile/android/app/build.gradle index 3413a5cb46c..a6cfa70b6bb 100644 --- a/mozilla-release/mobile/android/app/build.gradle +++ b/mozilla-release/mobile/android/app/build.gradle @@ -47,11 +47,11 @@ android { ndk { abiFilters mozconfig.substs.ANDROID_CPU_ARCH.toString() } + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + // testInstrumentationRunner 'org.mozilla.gecko.FennecInstrumentationTestRunner' + // Used by Robolectric based tests; see TestRunner. /* Cliqz end */ applicationId mozconfig.substs.ANDROID_PACKAGE_NAME - testApplicationId 'org.mozilla.roboexample.test' - testInstrumentationRunner 'org.mozilla.gecko.FennecInstrumentationTestRunner' - // Used by Robolectric based tests; see TestRunner. buildConfigField 'String', 'BUILD_DIR', "\"${project.buildDir}\"" multiDexEnabled true vectorDrawables.useSupportLibrary = true @@ -214,7 +214,11 @@ android { } androidTest { - java { + /* Cliqz Start :: Cliqz Android Tests */ + java { + srcDir "${topsrcdir}/mobile/android/tests/cliqz/java" + } + /*java { srcDir "${topsrcdir}/mobile/android/tests/browser/robocop/src" // Bug 1229149 tracks pushing this into a :services Gradle project. srcDir "${topsrcdir}/mobile/android/services/src/androidTest/java" @@ -226,6 +230,7 @@ android { assets { srcDir "${topsrcdir}/mobile/android/tests/browser/robocop/assets" } + /o Cliqz End */ } } @@ -289,7 +294,9 @@ dependencies { implementation project(path: ':thirdparty') testImplementation 'junit:junit:4.12' + /* Cliqz Start o/ testImplementation 'org.robolectric:robolectric:3.8' + /o Cliqz End */ testImplementation 'org.simpleframework:simple-http:6.0.1' testImplementation 'org.mockito:mockito-core:1.10.19' @@ -299,6 +306,9 @@ dependencies { /*Cliqz Start*/ //Library for control center donut - chart implementation 'com.github.PhilJay:MPAndroidChart:v3.0.2' + //UIAutomator2 Dependencies + androidTestImplementation 'com.android.support.test:testing-support-lib:0.1' + androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3' /*Cliqz End*/ } @@ -608,3 +618,21 @@ if (mozconfig.substs.MOZ_JAVA_CODE_COVERAGE) { } } } +/* Cliqz Start */ +// Grant permissions to disable Animations. +task grantAnimationPermissions(type: Exec, dependsOn: 'installWithGeckoBinariesDebug') { + group = 'Verification tasks' + description = 'Grant permissions for testing.' + def appPackage = mozconfig.substs.ANDROID_PACKAGE_NAME + def androidHome = mozconfig.substs.ANDROID_SDK_ROOT + commandLine "$androidHome/platform-tools/adb shell pm grant $appPackage android.permission.SET_ANIMATION_SCALE".split(" ") +} + +afterEvaluate { + tasks.each { task -> + if (task.name.contains('connectedWithGeckoBinariesDebugAndroidTest')) { + task.dependsOn grantAnimationPermissions + } + } +} +/* Cliqz End */ \ No newline at end of file diff --git a/mozilla-release/mobile/android/app/src/androidTest/AndroidManifest.xml b/mozilla-release/mobile/android/app/src/androidTest/AndroidManifest.xml index 88610f2f091..04e7c0dd911 100644 --- a/mozilla-release/mobile/android/app/src/androidTest/AndroidManifest.xml +++ b/mozilla-release/mobile/android/app/src/androidTest/AndroidManifest.xml @@ -1,16 +1,17 @@ + + - @@ -18,8 +19,12 @@ android:name="org.mozilla.gecko.FennecInstrumentationTestRunner" android:targetPackage="${ANDROID_PACKAGE_NAME}" /> + + android:label="Instrumentation Test"> + diff --git a/mozilla-release/mobile/android/base/AndroidManifest.xml.in b/mozilla-release/mobile/android/base/AndroidManifest.xml.in index aa3065137c1..da2954ad3ed 100644 --- a/mozilla-release/mobile/android/base/AndroidManifest.xml.in +++ b/mozilla-release/mobile/android/base/AndroidManifest.xml.in @@ -10,6 +10,7 @@ #ifdef MOZ_ANDROID_SHARED_ID android:sharedUserId="@MOZ_ANDROID_SHARED_ID@" #endif + #endif +#ifndef MOZILLA_OFFICIAL + + +#endif + #ifdef MOZ_WEBRTC diff --git a/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/CliqzRunner.java b/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/CliqzRunner.java new file mode 100644 index 00000000000..3dfddfae5aa --- /dev/null +++ b/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/CliqzRunner.java @@ -0,0 +1,87 @@ +package com.cliqz.mobile.test; + +import android.util.Log; + +import org.junit.Ignore; +import org.junit.internal.AssumptionViolatedException; +import org.junit.internal.runners.model.EachTestNotifier; +import org.junit.runner.Description; +import org.junit.runner.notification.RunNotifier; +import org.junit.runner.notification.StoppedByUserException; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; + +/** + * Copyright © Cliqz 2019 + */ +public class CliqzRunner extends BlockJUnit4ClassRunner { + + private static final int RETRY_COUNT = 2; + + public CliqzRunner(Class clazz) throws InitializationError { + super(clazz); + } + + @Override + public void run(final RunNotifier notifier) { + final EachTestNotifier testNotifier = new EachTestNotifier(notifier, getDescription()); + Statement statement = classBlock(notifier); + try { + statement.evaluate(); + } catch (AssumptionViolatedException ave) { + testNotifier.fireTestIgnored(); + } catch (StoppedByUserException sbue) { + throw sbue; + } catch (Throwable t) { + Log.w("AUTOBOTS", "Retry class: " + getDescription().getDisplayName()); + retry(testNotifier, statement, t, getDescription()); + } + } + + @Override + protected void runChild(final FrameworkMethod method, RunNotifier notifier) { + final Description description = describeChild(method); + if (method.getAnnotation(Ignore.class) != null) { + notifier.fireTestIgnored(description); + } else { + runTest(methodBlock(method), description, notifier); + } + } + + private void runTest(Statement statement, Description description, RunNotifier notifier) { + final EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description); + eachNotifier.fireTestStarted(); + try { + statement.evaluate(); + } catch (AssumptionViolatedException e) { + eachNotifier.addFailedAssumption(e); + } catch (Throwable e) { + Log.w("AUTOBOTS", "Retry test: " + description.getDisplayName()); + retry(eachNotifier, statement, e, description); + } finally { + eachNotifier.fireTestFinished(); + } + } + + private void retry(EachTestNotifier notifier, Statement statement, Throwable currentThrowable, Description info) { + int failedAttempts = 0; + Throwable caughtThrowable = currentThrowable; + while (RETRY_COUNT > failedAttempts) { + try { + notifier.fireTestIgnored(); + notifier.fireTestStarted(); + Log.w("AUTOBOTS", "Retry attempt " + (failedAttempts + 1) + " for " + info.getDisplayName()); + statement.evaluate(); + notifier.fireTestFinished(); + return; + } catch (Throwable t) { + failedAttempts++; + caughtThrowable = t; + } + } + Log.e("AUTOBOTS", caughtThrowable.getMessage()); + notifier.addFailure(caughtThrowable); + } +} \ No newline at end of file diff --git a/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/driver/ElementData.java b/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/driver/ElementData.java new file mode 100644 index 00000000000..070daddf3a6 --- /dev/null +++ b/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/driver/ElementData.java @@ -0,0 +1,41 @@ +package com.cliqz.mobile.test.driver; + +import android.support.test.uiautomator.UiObject2; +import android.util.Log; + +/** + * Copyright © Cliqz 2019 + */ +public class ElementData { + + private UiObject2 element; + private boolean exists; + private boolean checked; + private boolean enabled; + private boolean focused; + private boolean selected; + private String text; + private String description; + + + public ElementData(UiObject2 elem) { + element = elem; + try { + exists = element.getResourceName()!=null; + text = element.getText(); + description = element.getContentDescription(); + checked = element.isChecked(); + enabled = element.isEnabled(); + focused = element.isFocused(); + selected = element.isSelected(); + } catch (Exception e) { + Log.w("AUTOBOTS", e.toString()); + } + } + + public boolean hasChanged() { + ElementData tempData = new ElementData(element); + return this != tempData; + } + +} diff --git a/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/driver/NativeDriver.java b/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/driver/NativeDriver.java new file mode 100644 index 00000000000..6fca0de7adb --- /dev/null +++ b/mozilla-release/mobile/android/tests/cliqz/java/com/cliqz/mobile/test/driver/NativeDriver.java @@ -0,0 +1,388 @@ +package com.cliqz.mobile.test.driver; + +import android.app.Instrumentation; +import android.content.Context; +import android.content.Intent; +import android.os.Environment; +import android.os.IBinder; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.Until; +import android.util.Log; + +import org.mozilla.gecko.BuildConfig; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.fail; + +/** + * Copyright © Cliqz 2019 + */ +public class NativeDriver { + + // *** Driver Settings **** + private final static int IMPLICIT_WAIT = 5000; + private final static int POLL_INTERVAL = 50; + + // *** Driver Actors *** + private final UiDevice deviceDriver; + public final Context instrumentedContext; + public final Context targetContext; + private final Intent intent; + private final Instrumentation instrumentation; + public static final String APP_PACKAGE = BuildConfig.APPLICATION_ID; + + // *** Constructor and Class Methods *** + public NativeDriver() throws NullPointerException { + instrumentation = InstrumentationRegistry.getInstrumentation(); + deviceDriver = UiDevice.getInstance(instrumentation); + instrumentedContext = instrumentation.getContext(); + targetContext = instrumentation.getTargetContext(); + intent = instrumentedContext.getPackageManager().getLaunchIntentForPackage(APP_PACKAGE); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + } + + public void launchActivity() { + instrumentedContext.startActivity(intent); + waitForDriverReady(); + deviceDriver.wait(Until.hasObject(By.depth(3)), IMPLICIT_WAIT); + } + + public void waitForDriverReady() { + deviceDriver.waitForIdle(3000); + } + + public void disableAnimations() { + try { + Class windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub"); + Method asInterface = windowManagerStubClazz.getDeclaredMethod("asInterface", IBinder.class); + Class serviceManagerClazz = Class.forName("android.os.ServiceManager"); + Method getService = serviceManagerClazz.getDeclaredMethod("getService", String.class); + Class windowManagerClazz = Class.forName("android.view.IWindowManager"); + Method setAnimationScales = windowManagerClazz.getDeclaredMethod("setAnimationScales", float[].class); + Method getAnimationScales = windowManagerClazz.getDeclaredMethod("getAnimationScales"); + + IBinder windowManagerBinder = (IBinder) getService.invoke(null, "window"); + Object windowManagerObj = asInterface.invoke(null, windowManagerBinder); + float[] currentScales = (float[]) getAnimationScales.invoke(windowManagerObj); + for (int i = 0; i < currentScales.length; i++) { + currentScales[i] = 0.0f; + } + setAnimationScales.invoke(windowManagerObj, new Object[]{currentScales}); + } catch (Exception e) { + logErr("SystemAnimations: Could not disable animation."); + } + } + + // *** Utility Methods *** + public void sleep(long seconds) { + try { + Thread.sleep(seconds * 1000); + } catch (InterruptedException ie) { + logErr(ie.getMessage()); + } + } + + public void log(String msg) { + Log.d("AUTOBOTS", msg); + } + + public void logErr(String msg) { + Log.e("AUTOBOTS", msg); + } + + public void logWarn(String msg) { + Log.w("AUTOBOTS", msg); + } + + private String deviceDirectory(String dirName) { + String mDir = String.format( + "%s/%s", Environment.getExternalStorageDirectory().getPath(), dirName); + File scrDir = new File(mDir); + if (!scrDir.exists()) { + scrDir.mkdir(); + } + return mDir; + } + + public void takeScreenshot(String name) { + log("Take Screenshot: " + name + ".png"); + String mDir = deviceDirectory("test-screenshots"); + deviceDriver.takeScreenshot(new File(String.format("%s/%s.%s", mDir, name, "png"))); + } + + public void waitForChange(ElementData elem, long timeout) { + final long startTime = System.currentTimeMillis(); + while (!elem.hasChanged()) { + try { + Thread.sleep(25); + } catch (InterruptedException ie) { + logWarn(ie.getMessage()); + } + if (System.currentTimeMillis() - timeout > startTime) { + throw new Error("Element is still same after timeout."); + } + } + } + + public void waitForChange(ElementData elem) { + waitForChange(elem, IMPLICIT_WAIT); + } + + public void clickAndWait(UiObject2 clickElement, int waitTimeInSecs) { + clickElement.click(); + sleep(waitTimeInSecs); + } + + public void clickAndWait(UiObject2 clickElement, UiObject2 changeElement) { + ElementData tempData = new ElementData(changeElement); + clickElement.click(); + waitForChange(tempData); + } + + public void clickAndWait(UiObject2 element) { + clickAndWait(element, element); + } + + public void typeAndWait(UiObject2 element, String text, int waitTimeInSecs) { + element.setText(text); + sleep(waitTimeInSecs); + } + + public void typeAndWait(UiObject2 element, String text) { + ElementData tempData = new ElementData(element); + element.setText(text); + sleep(1); + waitForChange(tempData); + } + + // *** Element Methods *** + public List getElements(BySelector selector, + long timeout, + boolean failIfNotFound) { + waitForDriverReady(); + List elemList = new ArrayList(); + final long startTime = System.currentTimeMillis(); + while (elemList.isEmpty()) { + elemList = deviceDriver.findObjects(selector); + try { + Thread.sleep(POLL_INTERVAL); + } catch (InterruptedException ie) { + logErr(ie.getMessage()); + } + if (System.currentTimeMillis() - timeout > startTime) { + if (failIfNotFound) { + throw new Error("Element not Found: " + selector.toString()); + } + else { + logWarn("Element not found: " + selector.toString()); + break; + } + } + } + return elemList; + } + + public List getElements(BySelector selector, + boolean failIfNotFound) { + return getElements(selector, + IMPLICIT_WAIT, + failIfNotFound); + } + + public List getElements(BySelector selector, + long timeout) { + return getElements(selector, + timeout, + true); + } + + public List getElements(BySelector selector) { + return getElements(selector, + IMPLICIT_WAIT, + true); + } + + public UiObject2 getElement(BySelector selector, + long timeout, + boolean failIfNotFound) { + return getElements(selector, timeout, failIfNotFound).get(0); + } + + public UiObject2 getElement(BySelector selector, + boolean failIfNotFound) { + return getElements(selector, failIfNotFound).get(0); + } + + public UiObject2 getElement(BySelector selector, + long timeout) { + return getElements(selector, timeout).get(0); + } + + public UiObject2 getElement(BySelector selector) { + return getElements(selector).get(0); + } + + public List getElements(UiObject2 baseElement, + BySelector selector, + long timeout, + boolean failIfNotFound) { + waitForDriverReady(); + List elemList = new ArrayList(); + long curTime = System.currentTimeMillis(); + while (elemList.isEmpty()) { + elemList = baseElement.findObjects(selector); + try { + Thread.sleep(POLL_INTERVAL); + } catch (InterruptedException ie) { + logErr(ie.getMessage()); + } + if (System.currentTimeMillis() - timeout > curTime) { + if (failIfNotFound) { + throw new Error("Element not Found: " + selector.toString()); + } + else { + logWarn("Element not found: " + selector.toString()); + } + } + } + return elemList; + } + + public List getElements(UiObject2 baseElement, + BySelector selector, + long timeout) { + return getElements(baseElement, + selector, + timeout, + true); + } + + public List getElements(UiObject2 baseElement, + BySelector selector) { + return getElements(baseElement, + selector, + IMPLICIT_WAIT, + true); + } + + + public List getElements(UiObject2 baseElement, + BySelector selector, + boolean failIfNotFound) { + return getElements(baseElement, + selector, + IMPLICIT_WAIT, + failIfNotFound); + } + + public UiObject2 getElement(UiObject2 baseElement, + BySelector selector, + long timeout, + boolean failIfNotFound) { + return getElements(baseElement, selector, timeout, failIfNotFound).get(0); + } + + public UiObject2 getElement(UiObject2 baseElement, + BySelector selector, + long timeout) { + return getElements(baseElement, selector, timeout).get(0); + } + + public UiObject2 getElement(UiObject2 baseElement, + BySelector selector, + boolean failIfNotFound) { + return getElements(baseElement, selector, failIfNotFound).get(0); + } + + public UiObject2 getElement(UiObject2 baseElement, + BySelector selector) { + return getElements(baseElement, selector).get(0); + } + + // *** Action Methods *** + public void pageSwipe(Direction dir, int count) { + int scrWidth = deviceDriver.getDisplayWidth(); + int scrHeight = deviceDriver.getDisplayHeight(); + for (int i=0; i getPageViewButtonList() { + return driver.getElements(getPageViewButtonBar(), PAGE_VIEW_BUTTONS); + } + + private UiObject2 getPageViewButton(BySelector selector) { + return driver.getElement(getPageViewButtonBar(), selector); + } + + private UiObject2 getTopSitesAndNewsView() { + return driver.getElement(TOP_SITES_NEWS_VIEW); + } + + private UiObject2 getTopSitesSection() { + return driver.getElement(getTopSitesAndNewsView(), TOP_SITES_VIEW); + } + + private UiObject2 getTopSitesEmptyText() { + return driver.getElement(getTopSitesSection(), EMPTY_TOP_SITES_ELEMENT); + } + + private UiObject2 getNewsSection() { + return driver.getElement(getTopSitesAndNewsView(), NEWS_VIEW); + } + + private UiObject2 getNewsToggleButton() { + return driver.getElement(getNewsSection(), NEWS_SECTION_TOGGLE); + } + + private List getNewsListElements() { + final List newsList = getNewsSection().getChildren(); + newsList.remove(0); + return newsList; + } + + private UiObject2 getNewsIcon(UiObject2 newsItem) { + return driver.getElement(newsItem, NEWS_ITEM_ICON, 500); + } + + private UiObject2 getNewsTitle(UiObject2 newsItem) { + return driver.getElement(newsItem, NEWS_ITEM_TITLE, 500); + } + + private UiObject2 getNewsUrl(UiObject2 newsItem) { + return driver.getElement(newsItem, NEWS_ITEM_URL, 500); + } + + private List> getNewsListData() { + List> newsData = new ArrayList<>(); + boolean found = true; + int count = 0; + while(found && (count<=5)) { + found = false; + List newsElemList = getNewsListElements(); + if(newsElemList.size() == 0){ + throw new Error("News Section is Empty !"); + } + for (int i = 0; i < newsElemList.size(); i++) { + Map data = new HashMap<>(); + try { + driver.log(getNewsTitle(newsElemList.get(i)).getText()); + data.put("title", getNewsTitle(newsElemList.get(i)).getText()); + data.put("url", getNewsUrl(newsElemList.get(i)).getText()); + data.put("icon", (getNewsIcon(newsElemList.get(i)) != null) ? "true" : "false"); + if (!newsData.contains(data)) { + newsData.add(data); + found = true; + } + } catch(Error e) { + driver.logWarn(e.toString()); + found = true; + } + } + getNewsSection().swipe(Direction.UP, 0.5f); + count++; + } + getNewsSection().swipe(Direction.DOWN, 1.0f); + return newsData; + } + + // *** Test Methods *** + @Test + public void testUrlBar() { + UiObject2 urlBarElem = getUrlBar(); + Assert.assertTrue( + "Check that the Url Bar exists.", + driver.exists(URL_BAR) + ); + Assert.assertEquals( + "Check if the Url Bar Text is correct.", + URL_BAR_TEXT, + urlBarElem.getText() + ); + driver.clickAndWait(urlBarElem); + final UiObject2 urlBarEditElem = getUrlBarEdit(); + final UiObject2 urlBarEditCancelElem = getUrlBarEditCancel(); + Assert.assertTrue( + "Check that the Url Bar Edit exists.", + driver.exists(URL_BAR_EDIT) + ); + Assert.assertTrue( + "Check that the Url Bar Cancel exists.", + driver.exists(URL_BAR_EDIT_CANCEL) + ); + Assert.assertFalse( + "Check that the Url Bar, Ghosty, Tabs and Menu Buttons do not exist.", + driver.exists(URL_BAR, GHOSTY_ICON, TABS_BUTTON, MENU_BUTTON) + ); + driver.typeAndWait(urlBarEditElem, "Hello"); + Assert.assertEquals( + "Check that the URL Bar Edit Text changes to 'Hello'.", + "Hello.com", + urlBarEditElem.getText().replace("/", "") + ); + driver.clickAndWait(urlBarEditCancelElem); + urlBarElem = getUrlBar(); + Assert.assertNotSame( + "Check that the URL Bar Edit Text is NOT 'Hello'.", + "Hello.com", + urlBarElem.getText().replace("/", "") + ); + Assert.assertEquals( + "Check if the Url Bar Text is correct.", + URL_BAR_TEXT, + urlBarElem.getText() + ); + Assert.assertFalse( + "Check that the Url Bar Edit no longer exists.", + driver.exists(URL_BAR_EDIT) + ); + Assert.assertTrue( + "Check that the Url Bar, Ghosty, Tabs and Menu Buttons exist.", + driver.exists(URL_BAR, GHOSTY_ICON, TABS_BUTTON, MENU_BUTTON) + ); + driver.log("Url Bar is working as intended."); + } + + @Test + public void testGhosty() { + final UiObject2 ghostyElem = getGhostyIcon(); + Assert.assertTrue( + "Check that the Ghosty Icon exists.", + driver.exists(GHOSTY_ICON) + ); + Assert.assertEquals( + "Check if the Ghosty Text is correct.", + GHOSTY_TEXT, + ghostyElem.getContentDescription() + ); + } + + @Test + public void testTabsButton() { + Assert.assertTrue( + "Check that the Tabs Button exists.", + driver.exists(TABS_BUTTON) + ); + Assert.assertEquals( + "Check if the Tabs Count is 1.", + 1, + getTabsCount() + ); + } + + @Test + public void testMenuButton() { + final UiObject2 menuButtonElem = getMenuButton(); + Assert.assertTrue( + "Check that the Menu Button exists.", + driver.exists(MENU_BUTTON) + ); + Assert.assertEquals( + "Check if the Menu Button Text is correct.", + MENU_BUTTON_TEXT, + menuButtonElem.getContentDescription() + ); + } + + @Test + public void testFreshTabViewButtons() { + final List buttonList = getPageViewButtonList(); + Assert.assertEquals( + "Check if Number of Buttons on the TabBar is Correct.", + buttonList.size(), + PAGE_VIEW_BUTTON_COUNT + ); + Assert.assertNotNull( + "Check that TopSites Button Exist.", + getPageViewButton(TOP_SITES_BUTTON) + ); + Assert.assertNotNull( + "Check that History Button Exist.", + getPageViewButton(HISTORY_BUTTON) + ); + Assert.assertNotNull( + "Check that MyOfferz Button Exist.", + getPageViewButton(OFFERZ_BUTTON) + ); + Assert.assertNotNull( + "Check that Favorites Button Exist.", + getPageViewButton(FAVS_BUTTON) + ); + } + + @Test + public void testTopSitesSectionEmpty() { + final UiObject2 emptyTextElement = getTopSitesEmptyText(); + Assert.assertNotNull( + "Check that a Text is shown if Top Sites are empty.", + emptyTextElement + ); + Assert.assertEquals( + "Check that the Text shown for Empty Top Sites is correct.", + EMPTY_TOP_SITE_TEXT, + emptyTextElement.getText() + ); + } + + @Test + public void testNewsSection() { + Assert.assertEquals( + "Check if News Section is Collapsed.", + "Show", + getNewsToggleButton().getText() + ); + driver.clickAndWait(getNewsToggleButton()); + Assert.assertEquals( + "Check if News Section is Expanded and text is changed to Hide.", + "Hide", + getNewsToggleButton().getText() + ); + driver.clickAndWait(getNewsToggleButton()); + Assert.assertEquals( + "Check if News Section is Collapsed Again.", + "Show", + getNewsToggleButton().getText() + ); + driver.clickAndWait(getNewsToggleButton(), 1); + List> newsData = getNewsListData(); + Assert.assertNotNull( + "Check that the News List is not Null", + newsData + ); + for(int i=0; i getPageText() { + return driver.getElements(getOnBoardingPager(), ONBOARDING_LABEL); + } + + private UiObject2 getStartBrowsingButton() { + return driver.getElement(START_BROWSING_BUTTON); + } + + private UiObject2 getTabDotsMarkerView() { + return driver.getElement(TAB_DOTS_MARKER_VIEW); + } + + private List getTabDotsMarkers() { + return driver.getElements(getTabDotsMarkerView(), TAB_DOTS_MARKER); + } + + /* Page 1 */ + private UiObject2 getTelemetryCheckBox() { + return driver.getElement(getOnBoardingPager(), TELEMETRY_CHECK_BOX); + } + + /* Page 2 */ + private List getTrackerRadioButtons() { + return driver.getElements(getOnBoardingPager(), TRACKER_RADIO_BUTTON); + } + + private boolean checkRadioButtonsStatus(List radioButtons, boolean... statuses) { + if (radioButtons.size() != statuses.length) { + return false; + } + + final UiObject2[] array = new UiObject2[radioButtons.size()]; + radioButtons.toArray(array); + for (int i = 0; i < statuses.length; i++) { + if (array[i].isChecked() != statuses[i]) { + return false; + } + } + return true; + } + + // *** Common Assert Methods *** + private void assertIntroImage() { + final UiObject2 introImageElem = getIntroImage(); + Assert.assertTrue( + "Check if an Image is present.", + driver.exists(INTRO_IMAGE) + ); + Assert.assertEquals( + "Check if the Image Text is '" + ONBOARDING_IMAGE_TEXT + "'.", + ONBOARDING_IMAGE_TEXT, + introImageElem.getContentDescription() + ); + } + + private void assertFooterElems(int pageNumber) { + final UiObject2 startBrowsingButton = getStartBrowsingButton(); + final List tabDotsMarkersElems = getTabDotsMarkers(); + Assert.assertTrue( + "Check that '" + ONBOARDING_START_BUTTON_TEXT + "' Button is present.", + driver.exists(START_BROWSING_BUTTON) + ); + Assert.assertTrue( + "Check that '" + ONBOARDING_START_BUTTON_TEXT + "' Button is Clickable", + startBrowsingButton.isClickable() + ); + Assert.assertEquals( + "Check that '" + ONBOARDING_START_BUTTON_TEXT + "' Button is Clickable", + ONBOARDING_START_BUTTON_TEXT, + startBrowsingButton.getText() + ); + Assert.assertTrue( + "Check that the there is a Tabs Dots View.", + driver.exists(TAB_DOTS_MARKER_VIEW) + ); + Assert.assertEquals( + "Check that there are correct amount of dots.", + ONBOARDING_PAGES, + tabDotsMarkersElems.size() + ); + Assert.assertTrue( + "Check that the Page No: "+Integer.toString(pageNumber)+" is selected.", + tabDotsMarkersElems.get(pageNumber-1).isSelected() + ); + } + + // *** Test Methods *** + @Test + public void testOnboardingPage1() { + final List page1TextElems = getPageText(); + final UiObject2 telemetryCheckBoxElem = getTelemetryCheckBox(); + // Test Page + assertIntroImage(); + Assert.assertEquals( + "Check if the Text Label 1 is '" + ONBOARDING_PAGE_1_TEXT[0] + "'.", + ONBOARDING_PAGE_1_TEXT[0], + page1TextElems.get(0).getText() + ); + Assert.assertTrue( + "Check if the Text Label 2 contains '" + ONBOARDING_PAGE_1_TEXT[1] + "'.", + page1TextElems.get(1).getText().contains(ONBOARDING_PAGE_1_TEXT[1]) + ); + Assert.assertTrue( + "Check if the Text Label 3 contains '" + ONBOARDING_PAGE_1_TEXT[2] + "'.", + page1TextElems.get(2).getText().contains(ONBOARDING_PAGE_1_TEXT[2]) + ); + Assert.assertTrue( + "Check if a CheckBox for Telemetry Exists.", + telemetryCheckBoxElem.isEnabled() + ); + Assert.assertEquals( + "Check if the CheckBox is " + + (ONBOARDING_TELEMETRY_DEFAULT ? "checked" : "unchecked") + + " by Default.", + ONBOARDING_TELEMETRY_DEFAULT, + telemetryCheckBoxElem.isChecked() + ); + // Enable Telemetry + driver.clickAndWait(getTelemetryCheckBox()); + Assert.assertEquals( + "Check if the CheckBox is now " + + (!ONBOARDING_TELEMETRY_DEFAULT ? "checked" : "unchecked") + ".", + !ONBOARDING_TELEMETRY_DEFAULT, + getTelemetryCheckBox().isChecked() + ); + // TODO:: Check if the 'Send Data' in Settings is enabled. + // Disable Telemetry + driver.clickAndWait(getTelemetryCheckBox()); + Assert.assertEquals( + "Check if the CheckBox is now " + + (ONBOARDING_TELEMETRY_DEFAULT ? "checked" : "unchecked"), + ONBOARDING_TELEMETRY_DEFAULT, + getTelemetryCheckBox().isChecked() + ); + // TODO:: Check if the 'Send Data' in Settings is disabled. + assertFooterElems(1); + driver.log("Onboarding Page 1 is as Expected."); + } + + @Test + public void testOnboardingPage2() { + // Swipe to Page 2 + int scrollCount = 1; + while (!getTabDotsMarkers().get(1).isSelected() && scrollCount < 5) { + driver.pageSwipe(Direction.LEFT); + scrollCount++; + } + final List page2TextElems = getPageText(); + final List trackerRadioElems = getTrackerRadioButtons(); + // Test Page + assertIntroImage(); + Assert.assertEquals( + "Check if the Text Label 1 is '" + ONBOARDING_PAGE_2_TEXT[0] + "'.", + ONBOARDING_PAGE_2_TEXT[0], + page2TextElems.get(0).getText() + ); + Assert.assertTrue( + "Check if the Text Label 2 contains '" + ONBOARDING_PAGE_2_TEXT[1] + "'.", + page2TextElems.get(1).getText().contains(ONBOARDING_PAGE_2_TEXT[1]) + ); + Assert.assertEquals( + "Check that there are correct number of options for tracker blocking.", + ONBOARDING_TRACKER_OPTIONS, + trackerRadioElems.size() + ); + Assert.assertEquals( + "Check that the 1st button is for '" + ONBOARDING_TRACKER_TEXT[0] + "'.", + ONBOARDING_TRACKER_TEXT[0], + trackerRadioElems.get(0).getText() + ); + Assert.assertEquals( + "Check that the 2nd button is for '" + ONBOARDING_TRACKER_TEXT[1] + "'.", + ONBOARDING_TRACKER_TEXT[1], + trackerRadioElems.get(1).getText() + ); + Assert.assertEquals( + "Check that the 3rd button is for '" + ONBOARDING_TRACKER_TEXT[2] + "'.", + ONBOARDING_TRACKER_TEXT[2], + trackerRadioElems.get(2).getText() + ); + Assert.assertTrue( + "Check that the default option is selected.", + checkRadioButtonsStatus(trackerRadioElems, ONBOARDING_TRACKER_DEFAULT)); + for (int i=0; i page3TextElems = getPageText(); + // Test Page + assertIntroImage(); + Assert.assertEquals( + "Check if the Text Label 1 is '" + ONBOARDING_PAGE_3_TEXT[0] + "'.", + ONBOARDING_PAGE_3_TEXT[0], + page3TextElems.get(0).getText() + ); + Assert.assertTrue( + "Check if the Text Label 2 contains '" + ONBOARDING_PAGE_3_TEXT[1] + "'.", + page3TextElems.get(1).getText().contains(ONBOARDING_PAGE_3_TEXT[1]) + ); + assertFooterElems(3); + driver.log("Onboarding Page 3 is as Expected."); + } + + @Test + public void testOnboardingPage4() { + // Swipe to Page 3 + int scrollCount = 1; + while (!getTabDotsMarkers().get(3).isSelected() && scrollCount < 5) { + driver.pageSwipe(Direction.LEFT); + scrollCount++; + } + final List page4TextElems = getPageText(); + // Test Page + assertIntroImage(); + Assert.assertEquals( + "Check if the Text Label 1 is '" + ONBOARDING_PAGE_4_TEXT[0] + "'.", + ONBOARDING_PAGE_4_TEXT[0], + page4TextElems.get(0).getText() + ); + Assert.assertTrue( + "Check if the Text Label 2 contains '" + ONBOARDING_PAGE_4_TEXT[1] + "'.", + page4TextElems.get(1).getText().contains(ONBOARDING_PAGE_4_TEXT[1]) + ); + assertFooterElems(4); + driver.log("Onboarding Page 4 is as Expected."); + } + +}