Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,50 +33,24 @@ public void DragEvents()
App.WaitForElement("LabelDragElement");
App.DragAndDrop("LabelDragElement", "DragTarget");

App.WaitForElement("DragStartEventsLabel");
var textAfterDragStart = App.FindElement("DragStartEventsLabel").GetText();

if (string.IsNullOrEmpty(textAfterDragStart))
{
Assert.Fail("Text was expected: Drag start event");
}
else
{
Assert.That(textAfterDragStart, Is.EqualTo("DragStarting"));
}

App.WaitForElement("DragOverEventsLabel");
var textAfterDragOver = App.FindElement("DragOverEventsLabel").GetText();
if (string.IsNullOrEmpty(textAfterDragOver))
{
Assert.Fail("Text was expected: Drag over event");
}
else
{
Assert.That(textAfterDragOver, Is.EqualTo("DragOver"));
}

App.WaitForElement("DragCompletedEventsLabel");
var textAfterDragComplete = App.FindElement("DragCompletedEventsLabel").GetText();
if (string.IsNullOrEmpty(textAfterDragComplete))
{
Assert.Fail("Text was expected: Drag complete event");
}
else
{
Assert.That(textAfterDragComplete, Is.EqualTo("DropCompleted"));
}
AssertEventText("DragStartEventsLabel", "DragStarting");
AssertEventText("DragOverEventsLabel", "DragOver");
AssertEventText("DragCompletedEventsLabel", "DropCompleted");
AssertEventText("DropEventsLabel", "Drop");
}

App.WaitForElement("DropEventsLabel");
var textAfterDrop = App.FindElement("DropEventsLabel").GetText();
if (string.IsNullOrEmpty(textAfterDrop))
{
Assert.Fail("Text was expected: Drop event");
}
else
{
Assert.That(textAfterDrop, Is.EqualTo("Drop"));
}
void AssertEventText(string automationId, string expectedText)
Comment thread
kubaflo marked this conversation as resolved.
{
// Wait for the label's text to become EXACTLY the expected value. A substring wait is
// unreliable here because each label's placeholder (e.g. "DragOverEvents: ") already
// contains the expected event name (e.g. "DragOver"), so a Contains-based wait would pass
// immediately on the placeholder. WaitForTextEqualToElement polls until the text matches
// exactly, so the Assert below fails only on a genuine timeout; no separate GetText re-read
// is needed (it would only re-open a window for transient Appium flakiness).
Assert.That(
App.WaitForTextEqualToElement(automationId, expectedText),
Is.True,
$"Timed out waiting for {automationId} to become '{expectedText}'.");
}

[Test]
Expand All @@ -94,7 +68,7 @@ public void DragAndDropBetweenLayouts()
App.WaitForElement("Green");
App.DragAndDrop("Red", "Green");

App.WaitForElement("DragStartEventsLabel");
App.WaitForTextEqualToElement("DragStartEventsLabel", "DragStarting");
var textAfterDragStart = App.FindElement("DragStartEventsLabel").GetText();

if (string.IsNullOrEmpty(textAfterDragStart))
Expand All @@ -106,7 +80,7 @@ public void DragAndDropBetweenLayouts()
Assert.That(textAfterDragStart, Is.EqualTo("DragStarting"));
}

App.WaitForElement("DragOverEventsLabel");
App.WaitForTextEqualToElement("DragOverEventsLabel", "DragOver");
var textAfterDragOver = App.FindElement("DragOverEventsLabel").GetText();
if (string.IsNullOrEmpty(textAfterDragOver))
{
Expand All @@ -117,7 +91,7 @@ public void DragAndDropBetweenLayouts()
Assert.That(textAfterDragOver, Is.EqualTo("DragOver"));
}

App.WaitForElement("DragCompletedEventsLabel");
App.WaitForTextEqualToElement("DragCompletedEventsLabel", "DropCompleted");
var textAfterDragComplete = App.FindElement("DragCompletedEventsLabel").GetText();
if (string.IsNullOrEmpty(textAfterDragComplete))
{
Expand All @@ -139,7 +113,7 @@ public void DragAndDropBetweenLayouts()
Assert.That(rainbowColorText, Is.EqualTo("RainbowColorsAdd:Red"));
}

App.WaitForElement("DropEventsLabel");
App.WaitForTextEqualToElement("DropEventsLabel", "Drop");
var textAfterDrop = App.FindElement("DropEventsLabel").GetText();
if (string.IsNullOrEmpty(textAfterDrop))
{
Expand Down
32 changes: 28 additions & 4 deletions src/TestUtils/src/UITest.Appium/HelperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,30 +1054,54 @@ public static void WaitForNoElement(
}

public static bool WaitForTextToBePresentInElement(this IApp app, string automationId, string text, TimeSpan? timeout = null)
=> app.WaitForText(automationId, text, s => s.Contains(text, StringComparison.OrdinalIgnoreCase), timeout);

/// <summary>
/// Waits until the element's text is exactly equal to <paramref name="text"/> (ordinal), rather
/// than merely containing it. Use this when the element's placeholder/initial text already
/// contains the expected value as a substring, which would make a Contains-based wait pass
/// prematurely on the placeholder.
/// </summary>
public static bool WaitForTextEqualToElement(this IApp app, string automationId, string text, TimeSpan? timeout = null)
Comment thread
kubaflo marked this conversation as resolved.
=> app.WaitForText(automationId, text, s => string.Equals(s, text, StringComparison.Ordinal), timeout);

/// <summary>
/// Shared polling loop for the text-wait helpers. Repeatedly reads the element's text and
/// returns <see langword="true"/> as soon as <paramref name="matches"/> is satisfied. On
/// timeout it logs the last observed text (and the expected value) so a stalled or
/// placeholder-stuck label is distinguishable from a text-read failure, then returns
/// <see langword="false"/>.
/// </summary>
static bool WaitForText(this IApp app, string automationId, string expected, Func<string, bool> matches, TimeSpan? timeout)
{
timeout ??= DefaultTimeout;
TimeSpan retryFrequency = TimeSpan.FromMilliseconds(500);

DateTime start = DateTime.Now;
string? lastObservedText = null;

while (true)
{
var element = app.FindElements(automationId).FirstOrDefault();

if (element is not null && element.TryGetText(out var s) && s.Contains(text, StringComparison.OrdinalIgnoreCase))
if (element is not null && element.TryGetText(out var s))
{
return true;
lastObservedText = s;
if (matches(s))
{
return true;
}
}

long elapsed = DateTime.Now.Subtract(start).Ticks;
if (elapsed >= timeout.Value.Ticks)
{
Debug.WriteLine($">>>>> {elapsed} ticks elapsed, timeout value is {timeout.Value.Ticks}");
Debug.WriteLine($">>>>> {elapsed} ticks elapsed, timeout value is {timeout.Value.Ticks}; last observed text for '{automationId}' was '{lastObservedText ?? "<unavailable>"}', expected '{expected}'");

return false;
}

Task.Delay(retryFrequency.Milliseconds).Wait();
Task.Delay(retryFrequency).Wait();
}
}

Expand Down
Loading