Skip to content

Commit 4d4c7cc

Browse files
committed
Fix CSS selector support for numeric IDs and escape sequences
1 parent 3cd26e6 commit 4d4c7cc

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

src/FlaUI.WebDriver/Controllers/FindElementsController.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ private async Task<ActionResult> FindElementFrom(Func<AutomationElement> startNo
6060
{
6161
element = await Wait.Until(() => startNode().FindFirstByXPath(findElementRequest.Value), element => element != null, session.ImplicitWaitTimeout);
6262
}
63+
else if (findElementRequest.Using == "css selector")
64+
{
65+
var (strategy, value) = ParseCssSelector(findElementRequest.Value);
66+
var condition = _conditionParser.ParseCondition(session.Automation.ConditionFactory, strategy, value);
67+
element = await Wait.Until(() => startNode().FindFirstDescendant(condition), element => element != null, session.ImplicitWaitTimeout);
68+
}
6369
else
6470
{
6571
var condition = _conditionParser.ParseCondition(session.Automation.ConditionFactory, findElementRequest.Using, findElementRequest.Value);
@@ -71,7 +77,7 @@ private async Task<ActionResult> FindElementFrom(Func<AutomationElement> startNo
7177
return NoSuchElement(findElementRequest);
7278
}
7379

74-
var knownElement = session.GetOrAddKnownElement(element);
80+
var knownElement = session.GetOrAddKnownElement(element);
7581
return await Task.FromResult(WebDriverResult.Success(new FindElementResponse
7682
{
7783
ElementReference = knownElement.ElementReference,
@@ -85,6 +91,12 @@ private async Task<ActionResult> FindElementsFrom(Func<AutomationElement> startN
8591
{
8692
elements = await Wait.Until(() => startNode().FindAllByXPath(findElementRequest.Value), elements => elements.Length > 0, session.ImplicitWaitTimeout);
8793
}
94+
else if (findElementRequest.Using == "css selector")
95+
{
96+
var (strategy, value) = ParseCssSelector(findElementRequest.Value);
97+
var condition = _conditionParser.ParseCondition(session.Automation.ConditionFactory, strategy, value);
98+
elements = await Wait.Until(() => startNode().FindAllDescendants(condition), elements => elements.Length > 0, session.ImplicitWaitTimeout);
99+
}
88100
else
89101
{
90102
var condition = _conditionParser.ParseCondition(session.Automation.ConditionFactory, findElementRequest.Using, findElementRequest.Value);
@@ -140,5 +152,47 @@ private Session GetSession(string sessionId)
140152
session.SetLastCommandTimeToNow();
141153
return session;
142154
}
155+
156+
private (string strategy, string value) ParseCssSelector(string cssSelector)
157+
{
158+
if (cssSelector.StartsWith("#"))
159+
{
160+
return ("id", cssSelector.Substring(1));
161+
}
162+
if (cssSelector.StartsWith("."))
163+
{
164+
return ("class name", cssSelector.Substring(1));
165+
}
166+
167+
var nameMatch = Regex.Match(cssSelector, @"\*?\[name\s*=\s*""(.+?)""\]", RegexOptions.IgnoreCase);
168+
if (nameMatch.Success)
169+
{
170+
return ("name", UnescapeCssValue(nameMatch.Groups[1].Value));
171+
}
172+
173+
var idMatch = Regex.Match(cssSelector, @"\*?\[id\s*=\s*""(.+?)""\]", RegexOptions.IgnoreCase);
174+
if (idMatch.Success)
175+
{
176+
return ("id", UnescapeCssValue(idMatch.Groups[1].Value));
177+
}
178+
179+
var classMatch = Regex.Match(cssSelector, @"\*?\[class\s*=\s*""(.+?)""\]", RegexOptions.IgnoreCase);
180+
if (classMatch.Success)
181+
{
182+
return ("class name", UnescapeCssValue(classMatch.Groups[1].Value));
183+
}
184+
185+
return ("name", cssSelector);
186+
}
187+
188+
private string UnescapeCssValue(string cssValue)
189+
{
190+
var result = Regex.Replace(cssValue, @"\\([0-9a-fA-F]{1,6})\s?", m =>
191+
((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString());
192+
193+
result = Regex.Replace(result, @"\\(.)", "$1");
194+
195+
return result;
196+
}
143197
}
144198
}

src/FlaUI.WebDriver/Services/ConditionParser.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public PropertyCondition ParseCondition(ConditionFactory conditionFactory, strin
4444
{
4545
switch (@using)
4646
{
47+
case "id":
4748
case "accessibility id":
4849
return conditionFactory.ByAutomationId(value);
4950
case "name":

0 commit comments

Comments
 (0)