Skip to content

Commit

Permalink
Feat: Add support for Android Espresso's ViewMatcher (#447)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Dick authored Jan 12, 2021
1 parent 8561e7c commit e75c1eb
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/Appium.Net/Appium/Android/AndroidDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ public IReadOnlyCollection<W> FindElementsByAndroidDataMatcher(string selector)

#endregion IFindByAndroidDataMatcher Members

#region IFindByAndroidViewMatcher Members

public W FindElementByAndroidViewMatcher(string selector) =>
FindElement(MobileSelector.AndroidViewMatcher, selector);

public IReadOnlyCollection<W> FindElementsByAndroidViewMatcher(string selector) =>
ConvertToExtendedWebElementCollection<W>(FindElements(MobileSelector.AndroidViewMatcher, selector));

#endregion IFindByAndroidViewMatcher Members

public void StartActivity(string appPackage, string appActivity, string appWaitPackage = "",
string appWaitActivity = "", bool stopApp = true) =>
AndroidCommandExecutionHelper.StartActivity(this, appPackage, appActivity, appWaitPackage, appWaitActivity,
Expand Down
10 changes: 10 additions & 0 deletions src/Appium.Net/Appium/Android/AndroidElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ public IReadOnlyCollection<AppiumWebElement> FindElementsByAndroidDataMatcher(st

#endregion IFindByAndroidDataMatcher Members

#region IFindByAndroidViewMatcher Members

public AppiumWebElement FindElementByAndroidViewMatcher(string selector) =>
FindElement(MobileSelector.AndroidViewMatcher, selector);

public IReadOnlyCollection<AppiumWebElement> FindElementsByAndroidViewMatcher(string selector) =>
FindElements(MobileSelector.AndroidViewMatcher, selector);

#endregion IFindByAndroidViewMatcher Members

public void ReplaceValue(string value) => AndroidCommandExecutionHelper.ReplaceValue(this, Id, value);
}
}
1 change: 1 addition & 0 deletions src/Appium.Net/Appium/Enums/MobileSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class MobileSelector
public static readonly string Accessibility = "accessibility id";
public static readonly string AndroidUIAutomator = "-android uiautomator";
public static readonly string AndroidDataMatcher = "-android datamatcher";
public static readonly string AndroidViewMatcher = "-android viewmatcher";
public static readonly string iOSAutomatoion = "-ios uiautomation";
public static readonly string iOSPredicateString = "-ios predicate string";
public static readonly string iOSClassChain = "-ios class chain";
Expand Down
47 changes: 47 additions & 0 deletions src/Appium.Net/Appium/Interfaces/IFindByAndroidViewMatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//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.

using System.Collections.Generic;

namespace OpenQA.Selenium.Appium.Interfaces
{
public interface IFindByAndroidViewMatcher<out W> : IFindsByFluentSelector<W> where W : IWebElement
{
/// <summary>
/// Finds the first element in the page that matches the Android Espresso's View Matcher selector supplied
/// </summary>
/// <param name="selector">Selector for the element.</param>
/// <returns>IWebElement object so that you can interact that object</returns>
/// <example>
/// <code>
/// IWebDriver driver = new RemoteWebDriver(new DriverOptions());
/// IWebElement elem = driver.FindElementByAndroidViewMatcher('elements()'))
/// </code>
/// </example>
W FindElementByAndroidViewMatcher(string selector);

/// <summary>
/// Finds a list of elements that match the Android Espresso's View Matcher selector supplied
/// </summary>
/// <param name="selector">Selector for the elements.</param>
/// <returns>ReadOnlyCollection of IWebElement object so that you can interact with those objects</returns>
/// <example>
/// <code>
/// IWebDriver driver = new RemoteWebDriver(new FirefoxOptions());
/// ReadOnlyCollection<![CDATA[<IWebElement>]]> elem = driver.FindElementsByAndroidViewMatcher(elements())
/// </code>
/// </example>
IReadOnlyCollection<W> FindElementsByAndroidViewMatcher(string selector);
}
}
41 changes: 41 additions & 0 deletions src/Appium.Net/Appium/MobileBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ public static By AndroidUIAutomator(IUiAutomatorStatementBuilder selector) =>
/// <returns></returns>
public static By AndroidDataMatcher(string selector) => new ByAndroidDataMatcher(selector);

/// <summary>
/// This method creates a <see cref="OpenQA.Selenium.By"/> strategy
/// that searches for elements using Espresso's View Matcher.
/// <see cref="https://developer.android.com/training/testing/espresso/basics#finding-view"/>
/// </summary>
/// <param name="selector">The selector to use in finding the element.</param>
/// <returns></returns>
public static By AndroidViewMatcher(string selector) => new ByAndroidViewMatcher(selector);

/// <summary>
/// This method creates a <see cref="OpenQA.Selenium.By"/> strategy
/// that searches for elements using iOS UI automation.
Expand Down Expand Up @@ -232,6 +241,38 @@ public override string ToString() =>
$"ByAndroidDataMatcher({selector})";
}

/// <summary>
/// Finds element when the Espresso's View Matcher selector has the specified value.
/// <see cref="https://developer.android.com/training/testing/espresso/basics#finding-view"/>
/// </summary>
public class ByAndroidViewMatcher : MobileBy
{
/// <summary>
/// Initializes a new instance of the <see cref="ByAndroidViewMatcher"/> class.
/// </summary>
/// <param name="selector">The selector to use in finding the element.</param>
public ByAndroidViewMatcher(string selector) : base(selector, MobileSelector.AndroidViewMatcher)
{
}

public override IWebElement FindElement(ISearchContext context)
{
if (context is IFindByAndroidViewMatcher<IWebElement> finder)
return finder.FindElementByAndroidViewMatcher(selector);
return base.FindElement(context);
}

public override ReadOnlyCollection<IWebElement> FindElements(ISearchContext context)
{
if (context is IFindByAndroidViewMatcher<IWebElement> finder)
return finder.FindElementsByAndroidViewMatcher(selector).ToList().AsReadOnly();
return base.FindElements(context);
}

public override string ToString() =>
$"ByAndroidViewMatcher({selector})";
}

/// <summary>
/// Finds element when the Ios UIAutomation selector has the specified value.
/// <see cref="https://developer.apple.com/library/tvos/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UIAutomation.html"/>
Expand Down
21 changes: 21 additions & 0 deletions test/integration/Android/ElementTestEspresso.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ public void FindByAndroidDataMatcherTest()
1);
}

[Test]
public void FindByAndroidViewMatcherTest()
{
const string selectorData = @"{
'name':'withText',
'args':[{
'name':'containsString',
'args':['Preference']
}
}]";

By byAndroidViewMatcher = new ByAndroidViewMatcher(selectorData);

Assert.AreNotEqual(
_driver.FindElementById("android:id/list").FindElement(byAndroidViewMatcher).Text,
null);
Assert.GreaterOrEqual(
_driver.FindElementById("android:id/list").FindElements(byAndroidViewMatcher).Count,
1);
}

[OneTimeTearDown]
public void AfterAll()
{
Expand Down

0 comments on commit e75c1eb

Please sign in to comment.