Skip to content

Commit

Permalink
feat: Add support for viewmatcher (#1293)
Browse files Browse the repository at this point in the history
  • Loading branch information
ccharnkij authored and mykola-mokhnach committed Jan 17, 2020
1 parent 03172d8 commit 3573e23
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 4 deletions.
32 changes: 32 additions & 0 deletions src/main/java/io/appium/java_client/FindsByAndroidViewMatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client;

import org.openqa.selenium.WebElement;

import java.util.List;

public interface FindsByAndroidViewMatcher<T extends WebElement> extends FindsByFluentSelector<T> {

default T findElementByAndroidViewMatcher(String using) {
return findElement(MobileSelector.ANDROID_VIEW_MATCHER.toString(), using);
}

default List<T> findElementsByAndroidViewMatcher(String using) {
return findElements(MobileSelector.ANDROID_VIEW_MATCHER.toString(), using);
}
}
75 changes: 73 additions & 2 deletions src/main/java/io/appium/java_client/MobileBy.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,26 @@ public static By iOSClassChain(final String iOSClassChainString) {

/**
* This locator strategy is only available in Espresso Driver mode.
* @param dataMatcherString is a valid class chain locator string.
* See <a href="https://github.com/appium/appium-espresso-driver/pull/386">
* @param dataMatcherString is a valid json string detailing hamcrest matcher for Espresso onData().
* See <a href="http://appium.io/docs/en/writing-running-appium/android/espresso-datamatcher-selector/">
* the documentation</a> for more details
* @return an instance of {@link io.appium.java_client.MobileBy.ByAndroidDataMatcher}
*/
public static By androidDataMatcher(final String dataMatcherString) {
return new ByAndroidDataMatcher(dataMatcherString);
}

/**
* This locator strategy is only available in Espresso Driver mode.
* @param viewMatcherString is a valid json string detailing hamcrest matcher for Espresso onView().
* See <a href="http://appium.io/docs/en/writing-running-appium/android/espresso-datamatcher-selector/">
* the documentation</a> for more details
* @return an instance of {@link io.appium.java_client.MobileBy.ByAndroidViewMatcher}
*/
public static By androidViewMatcher(final String viewMatcherString) {
return new ByAndroidViewMatcher(viewMatcherString);
}

/**
* This locator strategy is available in XCUITest Driver mode.
* @param iOSNsPredicateString is an an iOS NsPredicate String
Expand Down Expand Up @@ -407,6 +418,66 @@ protected ByAndroidDataMatcher(String locatorString) {
}
}

public static class ByAndroidViewMatcher extends MobileBy implements Serializable {

protected ByAndroidViewMatcher(String locatorString) {
super(MobileSelector.ANDROID_VIEW_MATCHER, locatorString);
}

/**
* {@inheritDoc}
*
* @throws WebDriverException when current session doesn't support the given selector or when
* value of the selector is not consistent.
* @throws IllegalArgumentException when it is impossible to find something on the given
* {@link SearchContext} instance
*/
@SuppressWarnings("unchecked")
@Override public List<WebElement> findElements(SearchContext context) {
Class<?> contextClass = context.getClass();

if (FindsByAndroidViewMatcher.class.isAssignableFrom(contextClass)) {
return FindsByAndroidViewMatcher.class.cast(context)
.findElementsByAndroidViewMatcher(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElements(context);
}

throw formIllegalArgumentException(contextClass, FindsByAndroidViewMatcher.class,
FindsByFluentSelector.class);
}

/**
* {@inheritDoc}
*
* @throws WebDriverException when current session doesn't support the given selector or when
* value of the selector is not consistent.
* @throws IllegalArgumentException when it is impossible to find something on the given
* {@link SearchContext} instance
*/
@Override public WebElement findElement(SearchContext context) {
Class<?> contextClass = context.getClass();

if (FindsByAndroidViewMatcher.class.isAssignableFrom(contextClass)) {
return FindsByAndroidViewMatcher.class.cast(context)
.findElementByAndroidViewMatcher(getLocatorString());
}

if (FindsByFluentSelector.class.isAssignableFrom(contextClass)) {
return super.findElement(context);
}

throw formIllegalArgumentException(contextClass, FindsByAndroidViewMatcher.class,
FindsByFluentSelector.class);
}

@Override public String toString() {
return "By.FindsByAndroidViewMatcher: " + getLocatorString();
}
}

public static class ByIosNsPredicate extends MobileBy implements Serializable {

protected ByIosNsPredicate(String locatorString) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/appium/java_client/MobileSelector.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public enum MobileSelector {
IMAGE("-image"),
ANDROID_VIEWTAG("-android viewtag"),
ANDROID_DATA_MATCHER("-android datamatcher"),
ANDROID_VIEW_MATCHER("-android viewmatcher"),
CUSTOM("-custom");

private final String selector;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.CommandExecutionHelper;
import io.appium.java_client.FindsByAndroidDataMatcher;
import io.appium.java_client.FindsByAndroidViewMatcher;
import io.appium.java_client.FindsByAndroidUIAutomator;
import io.appium.java_client.FindsByAndroidViewTag;
import io.appium.java_client.HasOnScreenKeyboard;
Expand Down Expand Up @@ -62,7 +63,7 @@ public class AndroidDriver<T extends WebElement>
extends AppiumDriver<T>
implements PressesKey, HasNetworkConnection, PushesFiles, StartsActivity,
FindsByAndroidUIAutomator<T>, FindsByAndroidViewTag<T>, FindsByAndroidDataMatcher<T>,
LocksDevice, HasAndroidSettings, HasAndroidDeviceDetails,
FindsByAndroidViewMatcher<T>, LocksDevice, HasAndroidSettings, HasAndroidDeviceDetails,
HasSupportedPerformanceDataType, AuthenticatesByFinger, HasOnScreenKeyboard,
CanRecordScreen, SupportsSpecialEmulatorCommands,
SupportsNetworkStateManagement, ListensToLogcatMessages, HasAndroidClipboard,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@

import io.appium.java_client.CommandExecutionHelper;
import io.appium.java_client.FindsByAndroidDataMatcher;
import io.appium.java_client.FindsByAndroidViewMatcher;
import io.appium.java_client.FindsByAndroidUIAutomator;
import io.appium.java_client.FindsByAndroidViewTag;
import io.appium.java_client.MobileElement;

public class AndroidElement extends MobileElement
implements FindsByAndroidUIAutomator<MobileElement>, FindsByAndroidDataMatcher<MobileElement>,
FindsByAndroidViewTag<MobileElement> {
FindsByAndroidViewMatcher<MobileElement>, FindsByAndroidViewTag<MobileElement> {
/**
* This method replace current text value.
* @param value a new value
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/io/appium/java_client/events/DefaultAspect.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class DefaultAspect {
+ "execution(* org.openqa.selenium.ContextAware.*(..)) || "
+ "execution(* io.appium.java_client.FindsByAccessibilityId.*(..)) || "
+ "execution(* io.appium.java_client.FindsByAndroidUIAutomator.*(..)) || "
+ "execution(* io.appium.java_client.FindsByAndroidDataMatcher.*(..)) || "
+ "execution(* io.appium.java_client.FindsByAndroidViewMatcher.*(..)) || "
+ "execution(* io.appium.java_client.FindsByWindowsAutomation.*(..)) || "
+ "execution(* io.appium.java_client.FindsByIosNSPredicate.*(..)) || "
+ "execution(* org.openqa.selenium.internal.FindsByClassName.*(..)) || "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@
*/
String androidDataMatcher() default "";

/**
* It is a desired view matcher expression.
*
* @return a desired view matcher expression
*/
String androidViewMatcher() default "";

/**
* It is a xpath to the target element.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ enum Strategies {
.androidDataMatcher(getValue(annotation, this));
}
},
BY_VIEW_MATCHER("androidViewMatcher") {
@Override By getBy(Annotation annotation) {
return MobileBy
.androidViewMatcher(getValue(annotation, this));
}
},
BY_NS_PREDICATE("iOSNsPredicate") {
@Override By getBy(Annotation annotation) {
return MobileBy
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client.android;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.appium.java_client.MobileBy;
import org.junit.Test;
import org.openqa.selenium.json.Json;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import static org.junit.Assert.assertNotNull;

public class AndroidDataMatcherTest extends BaseEspressoTest {

@Test
public void testFindByDataMatcher() {
final WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions
.elementToBeClickable(MobileBy.AccessibilityId("Graphics")));
driver.findElement(MobileBy.AccessibilityId("Graphics")).click();

String selector = new Json().toJson(ImmutableMap.of(
"name", "hasEntry",
"args", ImmutableList.of("title", "Sweep")
));

assertNotNull(wait.until(ExpectedConditions
.presenceOfElementLocated(MobileBy.androidDataMatcher(selector))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client.android;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.appium.java_client.MobileBy;
import org.junit.Test;
import org.openqa.selenium.json.Json;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import static org.junit.Assert.assertNotNull;

public class AndroidViewMatcherTest extends BaseEspressoTest {

@Test
public void testFindByViewMatcher() {
String selector = new Json().toJson(ImmutableMap.of(
"name", "withText",
"args", ImmutableList.of("Animation"),
"class", "androidx.test.espresso.matcher.ViewMatchers"
));
final WebDriverWait wait = new WebDriverWait(driver, 10);
assertNotNull(wait.until(ExpectedConditions
.presenceOfElementLocated(MobileBy.androidViewMatcher(selector))));
}
}
67 changes: 67 additions & 0 deletions src/test/java/io/appium/java_client/android/BaseEspressoTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.appium.java_client.android;

import io.appium.java_client.remote.AutomationName;
import io.appium.java_client.remote.MobileCapabilityType;
import io.appium.java_client.service.local.AppiumDriverLocalService;
import io.appium.java_client.service.local.AppiumServerHasNotBeenStartedLocallyException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.openqa.selenium.remote.DesiredCapabilities;

import java.io.File;

public class BaseEspressoTest {

private static AppiumDriverLocalService service;
protected static AndroidDriver<AndroidElement> driver;

/**
* initialization.
*/
@BeforeClass public static void beforeClass() {
service = AppiumDriverLocalService.buildDefaultService();
service.start();

if (service == null || !service.isRunning()) {
throw new AppiumServerHasNotBeenStartedLocallyException(
"An appium server node is not started!");
}

File appDir = new File("src/test/java/io/appium/java_client");
File app = new File(appDir, "ApiDemos-debug.apk");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.ESPRESSO);
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Android Emulator");
capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
capabilities.setCapability("eventTimings", true);
driver = new AndroidDriver<>(service.getUrl(), capabilities);
}

/**
* finishing.
*/
@AfterClass public static void afterClass() {
if (driver != null) {
driver.quit();
}
if (service != null) {
service.stop();
}
}
}

0 comments on commit 3573e23

Please sign in to comment.