Skip to content

Commit e49f10e

Browse files
authored
Merge pull request #4164 from albertgasset/MOBILE-4616
MOBILE-4616 behat: Fix flaky tests
2 parents dd2ffd0 + a837c9c commit e49f10e

File tree

4 files changed

+51
-7
lines changed

4 files changed

+51
-7
lines changed

local_moodleappbehat/tests/behat/behat_app_helper.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -738,25 +738,36 @@ protected function resize_app_window(int $width = 500, int $height = 720) {
738738
* This function is similar to the arg_time_to_string transformation, but it allows the time to be a sub-text of the string.
739739
*
740740
* @param string $text
741-
* @return string Transformed text.
741+
* @return string|string[] Transformed text.
742742
*/
743-
protected function transform_time_to_string(string $text): string {
743+
protected function transform_time_to_string(string $text): string|array {
744744
if (!preg_match('/##(.*)##/', $text, $matches)) {
745745
// No time found, return the original text.
746746
return $text;
747747
}
748748

749749
$timepassed = explode('##', $matches[1]);
750+
$basetime = time();
750751

751752
// If not a valid time string, then just return what was passed.
752-
if ((($timestamp = strtotime($timepassed[0])) === false)) {
753+
if ((($timestamp = strtotime($timepassed[0], $basetime)) === false)) {
753754
return $text;
754755
}
755756

756757
$count = count($timepassed);
757758
if ($count === 2) {
758759
// If timestamp with specified strftime format, then return formatted date string.
759-
return str_replace($matches[0], userdate($timestamp, $timepassed[1]), $text);
760+
$result = [str_replace($matches[0], userdate($timestamp, $timepassed[1]), $text)];
761+
762+
// If it's a relative date, allow a difference of 1 minute for the base time used to calculate the timestampt.
763+
if ($timestamp !== strtotime($timepassed[0], 0)) {
764+
$timestamp = strtotime($timepassed[0], $basetime - 60);
765+
$result[] = str_replace($matches[0], userdate($timestamp, $timepassed[1]), $text);
766+
}
767+
768+
$result = array_unique($result);
769+
770+
return count($result) == 1 ? $result[0] : $result;
760771
} else if ($count === 1) {
761772
return str_replace($matches[0], $timestamp, $text);
762773
} else {

src/testing/services/behat-blocking.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,18 @@ export class TestingBehatBlockingService {
139139
this.unblock(key);
140140
}
141141

142+
/**
143+
* Adds a pending key to the array, and remove it after some time.
144+
*
145+
* @param milliseconds Number of milliseconds to wait before the key is removed.
146+
* @returns Promise resolved after the time has passed.
147+
*/
148+
async wait(milliseconds: number): Promise<void> {
149+
const key = this.block();
150+
await CoreWait.wait(milliseconds);
151+
this.unblock(key);
152+
}
153+
142154
/**
143155
* It would be really beautiful if you could detect CSS transitions and animations, that would
144156
* cover almost everything, but sadly there is no way to do this because the transitionstart

src/testing/services/behat-dom.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,13 @@ export class TestingBehatDomUtilsService {
124124
*/
125125
protected findElementsBasedOnTextWithinWithExact(
126126
container: HTMLElement,
127-
text: string,
127+
text: string | string[],
128128
options: TestingBehatFindOptions,
129129
): ElementsWithExact[] {
130+
if (Array.isArray(text)) {
131+
return text.map((text) => this.findElementsBasedOnTextWithinWithExact(container, text, options)).flat();
132+
}
133+
130134
// Escape double quotes to prevent breaking the query selector.
131135
const escapedText = text.replace(/"/g, '\\"');
132136
const attributesSelector = `[aria-label*="${escapedText}"], a[title*="${escapedText}"], ` +
@@ -266,7 +270,7 @@ export class TestingBehatDomUtilsService {
266270
*/
267271
protected findElementsBasedOnTextWithin(
268272
container: HTMLElement,
269-
text: string,
273+
text: string | string[],
270274
options: TestingBehatFindOptions,
271275
): HTMLElement[] {
272276
const elements = this.findElementsBasedOnTextWithinWithExact(container, text, options);
@@ -495,6 +499,17 @@ export class TestingBehatDomUtilsService {
495499
locator: TestingBehatElementLocator,
496500
options: TestingBehatFindOptions = {},
497501
): HTMLElement | undefined {
502+
if (Array.isArray(locator.text)) {
503+
for (const text of locator.text) {
504+
const element = this.findElementBasedOnText({ ...locator, text });
505+
if (element) {
506+
return element;
507+
}
508+
}
509+
510+
return undefined;
511+
}
512+
498513
// Remove extra spaces.
499514
const treatedText = locator.text.trim().replace(/\s\s+/g, ' ');
500515
if (treatedText !== locator.text) {

src/testing/services/behat-runtime.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@ export class TestingBehatRuntimeService {
219219
// Click button
220220
await TestingBehatDomUtils.pressElement(foundButton);
221221

222+
// Block Behat for at least 500ms, WS calls or DOM changes might not begin immediately.
223+
TestingBehatBlocking.wait(500);
224+
222225
return 'OK';
223226
}
224227

@@ -446,6 +449,9 @@ export class TestingBehatRuntimeService {
446449

447450
await TestingBehatDomUtils.pressElement(found);
448451

452+
// Block Behat for at least 500ms, WS calls or DOM changes might not begin immediately.
453+
TestingBehatBlocking.wait(500);
454+
449455
return 'OK';
450456
} catch (error) {
451457
return 'ERROR: ' + error.message;
@@ -803,7 +809,7 @@ export type TestingBehatFindOptions = {
803809
};
804810

805811
export type TestingBehatElementLocator = {
806-
text: string;
812+
text: string | string[];
807813
within?: TestingBehatElementLocator;
808814
near?: TestingBehatElementLocator;
809815
selector?: string;

0 commit comments

Comments
 (0)