diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/DragAndDropUITests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/DragAndDropUITests.cs
index 60a2ea803955..1b76ffe5dbbc 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/DragAndDropUITests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/DragAndDropUITests.cs
@@ -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)
+ {
+ // 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]
@@ -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))
@@ -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))
{
@@ -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))
{
@@ -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))
{
diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
index 6bbd5cfae4ec..19abd1b3a7e4 100644
--- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
+++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
@@ -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);
+
+ ///
+ /// Waits until the element's text is exactly equal to (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.
+ ///
+ public static bool WaitForTextEqualToElement(this IApp app, string automationId, string text, TimeSpan? timeout = null)
+ => app.WaitForText(automationId, text, s => string.Equals(s, text, StringComparison.Ordinal), timeout);
+
+ ///
+ /// Shared polling loop for the text-wait helpers. Repeatedly reads the element's text and
+ /// returns as soon as 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
+ /// .
+ ///
+ static bool WaitForText(this IApp app, string automationId, string expected, Func 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 ?? ""}', expected '{expected}'");
return false;
}
- Task.Delay(retryFrequency.Milliseconds).Wait();
+ Task.Delay(retryFrequency).Wait();
}
}