MOBILE-4061 behat: Treat async calls
parent
9ce31948ad
commit
ca87b084d2
|
@ -94,7 +94,7 @@ class behat_app extends behat_app_helper {
|
|||
public function i_wait_the_app_to_restart() {
|
||||
// Wait window to reload.
|
||||
$this->spin(function() {
|
||||
$result = $this->evaluate_script("return !window.behat;");
|
||||
$result = $this->js("return !window.behat;");
|
||||
|
||||
if (!$result) {
|
||||
throw new DriverException('Window is not reloading properly.');
|
||||
|
@ -121,7 +121,7 @@ class behat_app extends behat_app_helper {
|
|||
$containerName = json_encode($containerName);
|
||||
|
||||
$this->spin(function() use ($not, $locator, $containerName) {
|
||||
$result = $this->evaluate_script("return window.behat.find($locator, $containerName);");
|
||||
$result = $this->js("return window.behat.find($locator, $containerName);");
|
||||
|
||||
if ($not && $result === 'OK') {
|
||||
throw new DriverException('Error, found an item that should not be found');
|
||||
|
@ -147,7 +147,7 @@ class behat_app extends behat_app_helper {
|
|||
$locator = $this->parse_element_locator($locator);
|
||||
|
||||
$this->spin(function() use ($locator) {
|
||||
$result = $this->evaluate_script("return window.behat.scrollTo($locator);");
|
||||
$result = $this->js("return window.behat.scrollTo($locator);");
|
||||
|
||||
if ($result !== 'OK') {
|
||||
throw new DriverException('Error finding item - ' . $result);
|
||||
|
@ -170,7 +170,7 @@ class behat_app extends behat_app_helper {
|
|||
*/
|
||||
public function i_load_more_items_in_the_app(bool $not = false) {
|
||||
$this->spin(function() use ($not) {
|
||||
$result = $this->evaluate_async_script('return window.behat.loadMoreItems();');
|
||||
$result = $this->js('return await window.behat.loadMoreItems();');
|
||||
|
||||
if ($not && $result !== 'ERROR: All items are already loaded.') {
|
||||
throw new DriverException('It should not have been possible to load more items');
|
||||
|
@ -195,7 +195,7 @@ class behat_app extends behat_app_helper {
|
|||
public function i_swipe_in_the_app(string $direction) {
|
||||
$method = 'swipe' . ucwords($direction);
|
||||
|
||||
$this->evaluate_script("window.behat.getAngularInstance('ion-content', 'CoreSwipeNavigationDirective').$method()");
|
||||
$this->js("window.behat.getAngularInstance('ion-content', 'CoreSwipeNavigationDirective').$method()");
|
||||
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
|
@ -214,7 +214,7 @@ class behat_app extends behat_app_helper {
|
|||
$locator = $this->parse_element_locator($locator);
|
||||
|
||||
$this->spin(function() use ($locator, $not) {
|
||||
$result = $this->evaluate_script("return window.behat.isSelected($locator);");
|
||||
$result = $this->js("return window.behat.isSelected($locator);");
|
||||
|
||||
switch ($result) {
|
||||
case 'YES':
|
||||
|
@ -318,7 +318,7 @@ class behat_app extends behat_app_helper {
|
|||
$this->login($username);
|
||||
}
|
||||
|
||||
$mycoursesfound = $this->evaluate_script("return window.behat.find({ text: 'My courses', selector: 'ion-tab-button'});");
|
||||
$mycoursesfound = $this->js("return window.behat.find({ text: 'My courses', selector: 'ion-tab-button'});");
|
||||
|
||||
if ($mycoursesfound !== 'OK') {
|
||||
// My courses not present enter from Dashboard.
|
||||
|
@ -370,7 +370,7 @@ class behat_app extends behat_app_helper {
|
|||
*/
|
||||
public function i_press_the_standard_button_in_the_app(string $button) {
|
||||
$this->spin(function() use ($button) {
|
||||
$result = $this->evaluate_script("return window.behat.pressStandard('$button');");
|
||||
$result = $this->js("return await window.behat.pressStandard('$button');");
|
||||
|
||||
if ($result !== 'OK') {
|
||||
throw new DriverException('Error pressing standard button - ' . $result);
|
||||
|
@ -408,7 +408,7 @@ class behat_app extends behat_app_helper {
|
|||
],
|
||||
]);
|
||||
|
||||
$this->evaluate_script("window.behat.notificationClicked($notification)");
|
||||
$this->js("window.behat.notificationClicked($notification)");
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
|
@ -494,7 +494,7 @@ class behat_app extends behat_app_helper {
|
|||
*/
|
||||
public function i_close_the_popup_in_the_app() {
|
||||
$this->spin(function() {
|
||||
$result = $this->evaluate_script("return window.behat.closePopup();");
|
||||
$result = $this->js("return window.behat.closePopup();");
|
||||
|
||||
if ($result !== 'OK') {
|
||||
throw new DriverException('Error closing popup - ' . $result);
|
||||
|
@ -532,7 +532,7 @@ class behat_app extends behat_app_helper {
|
|||
$locator = $this->parse_element_locator($locator);
|
||||
|
||||
$this->spin(function() use ($locator) {
|
||||
$result = $this->evaluate_script("return window.behat.press($locator);");
|
||||
$result = $this->js("return await window.behat.press($locator);");
|
||||
|
||||
if ($result !== 'OK') {
|
||||
throw new DriverException('Error pressing item - ' . $result);
|
||||
|
@ -562,14 +562,14 @@ class behat_app extends behat_app_helper {
|
|||
|
||||
$this->spin(function() use ($selectedtext, $selected, $locator) {
|
||||
// Don't do anything if the item is already in the expected state.
|
||||
$result = $this->evaluate_script("return window.behat.isSelected($locator);");
|
||||
$result = $this->js("return window.behat.isSelected($locator);");
|
||||
|
||||
if ($result === $selected) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Press item.
|
||||
$result = $this->evaluate_script("return window.behat.press($locator);");
|
||||
$result = $this->js("return await window.behat.press($locator);");
|
||||
|
||||
if ($result !== 'OK') {
|
||||
throw new DriverException('Error pressing item - ' . $result);
|
||||
|
@ -578,7 +578,7 @@ class behat_app extends behat_app_helper {
|
|||
// Check that it worked as expected.
|
||||
$this->wait_for_pending_js();
|
||||
|
||||
$result = $this->evaluate_script("return window.behat.isSelected($locator);");
|
||||
$result = $this->js("return window.behat.isSelected($locator);");
|
||||
|
||||
switch ($result) {
|
||||
case 'YES':
|
||||
|
@ -612,7 +612,7 @@ class behat_app extends behat_app_helper {
|
|||
$value = addslashes_js($value);
|
||||
|
||||
$this->spin(function() use ($field, $value) {
|
||||
$result = $this->evaluate_script("return window.behat.setField(\"$field\", \"$value\");");
|
||||
$result = $this->js("return await window.behat.setField(\"$field\", \"$value\");");
|
||||
|
||||
if ($result !== 'OK') {
|
||||
throw new DriverException('Error setting field - ' . $result);
|
||||
|
@ -651,7 +651,7 @@ class behat_app extends behat_app_helper {
|
|||
*/
|
||||
public function the_header_should_be_in_the_app(string $text) {
|
||||
$this->spin(function() use ($text) {
|
||||
$result = $this->evaluate_script('return window.behat.getHeader();');
|
||||
$result = $this->js('return window.behat.getHeader();');
|
||||
|
||||
if (substr($result, 0, 3) !== 'OK:') {
|
||||
throw new DriverException('Error getting header - ' . $result);
|
||||
|
@ -732,7 +732,7 @@ class behat_app extends behat_app_helper {
|
|||
* @When I run cron tasks in the app
|
||||
*/
|
||||
public function i_run_cron_tasks_in_the_app() {
|
||||
$this->evaluate_script('window.behat.forceSyncExecution()');
|
||||
$this->js('await window.behat.forceSyncExecution()');
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
|
@ -742,7 +742,7 @@ class behat_app extends behat_app_helper {
|
|||
* @When I wait loading to finish in the app
|
||||
*/
|
||||
public function i_wait_loading_to_finish_in_the_app() {
|
||||
$this->evaluate_script('window.behat.waitLoadingToFinish()');
|
||||
$this->js('await window.behat.waitLoadingToFinish()');
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
|
@ -764,7 +764,7 @@ class behat_app extends behat_app_helper {
|
|||
$this->getSession()->switchToWindow($names[1]);
|
||||
}
|
||||
|
||||
$this->evaluate_script('window.close();');
|
||||
$this->js('window.close();');
|
||||
$this->getSession()->switchToWindow($names[0]);
|
||||
}
|
||||
|
||||
|
@ -776,7 +776,7 @@ class behat_app extends behat_app_helper {
|
|||
* @throws DriverException If the navigator.online mode is not available
|
||||
*/
|
||||
public function i_switch_offline_mode(string $offline) {
|
||||
$this->evaluate_script("window.behat.network.setForceOffline($offline);");
|
||||
$this->js("window.behat.network.setForceOffline($offline);");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -318,7 +318,7 @@ class behat_app_helper extends behat_base {
|
|||
$initOptions->skipOnBoarding = $options['skiponboarding'] ?? true;
|
||||
$initOptions->configOverrides = $this->appconfig;
|
||||
|
||||
$this->evaluate_script('window.behatInit(' . json_encode($initOptions) . ');');
|
||||
$this->js('window.behatInit(' . json_encode($initOptions) . ');');
|
||||
} catch (Exception $error) {
|
||||
throw new DriverException('Moodle App not running or not running on Automated mode.');
|
||||
}
|
||||
|
@ -434,19 +434,27 @@ class behat_app_helper extends behat_base {
|
|||
}
|
||||
|
||||
/**
|
||||
* Evaluate a script that returns a Promise.
|
||||
* Evaluate and execute scripts checking for promises if needed.
|
||||
*
|
||||
* @param string $script
|
||||
* @return mixed Resolved promise result.
|
||||
*/
|
||||
protected function evaluate_async_script(string $script) {
|
||||
$script = preg_replace('/^return\s+/', '', $script);
|
||||
$script = preg_replace('/;$/', '', $script);
|
||||
protected function js(string $script) {
|
||||
$scriptnoreturn = preg_replace('/^return\s+/', '', $script);
|
||||
$scriptnoreturn = preg_replace('/;$/', '', $scriptnoreturn);
|
||||
|
||||
if (!preg_match('/^await\s+/', $scriptnoreturn)) {
|
||||
// No async.
|
||||
return $this->evaluate_script($script);
|
||||
}
|
||||
|
||||
$script = preg_replace('/^await\s+/', '', $scriptnoreturn);
|
||||
|
||||
$start = microtime(true);
|
||||
$promisevariable = 'PROMISE_RESULT_' . time();
|
||||
$timeout = self::get_timeout();
|
||||
$timeout = self::get_extended_timeout();
|
||||
|
||||
$this->evaluate_script("Promise.resolve($script)
|
||||
$res = $this->evaluate_script("Promise.resolve($script)
|
||||
.then(result => window.$promisevariable = result)
|
||||
.catch(error => window.$promisevariable = 'Async code rejected: ' + error?.message);");
|
||||
|
||||
|
@ -455,6 +463,7 @@ class behat_app_helper extends behat_base {
|
|||
throw new DriverException("Async script not resolved after $timeout seconds");
|
||||
}
|
||||
|
||||
// 0.1 seconds.
|
||||
usleep(100000);
|
||||
} while (!$this->evaluate_script("return '$promisevariable' in window;"));
|
||||
|
||||
|
@ -514,7 +523,7 @@ class behat_app_helper extends behat_base {
|
|||
$successXPath = '//page-core-mainmenu';
|
||||
}
|
||||
|
||||
$this->handle_url_and_wait_page_to_load($url, $successXPath);
|
||||
$this->handle_url($url, $successXPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -529,7 +538,7 @@ class behat_app_helper extends behat_base {
|
|||
$urlscheme = $this->get_mobile_url_scheme();
|
||||
$url = "$urlscheme://link=" . urlencode($CFG->behat_wwwroot.$path);
|
||||
|
||||
$this->handle_url_and_wait_page_to_load($url);
|
||||
$this->handle_url($url);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -538,11 +547,13 @@ class behat_app_helper extends behat_base {
|
|||
* @param string $customurl To navigate.
|
||||
* @param string $successXPath The XPath of the element to lookat after navigation.
|
||||
*/
|
||||
protected function handle_url_and_wait_page_to_load(string $customurl, string $successXPath = '') {
|
||||
protected function handle_url(string $customurl, string $successXPath = '') {
|
||||
// Instead of using evaluate_async_script, we wait for the path to load.
|
||||
$this->evaluate_script("return window.behat.handleCustomURL('$customurl')");
|
||||
$result = $this->js("return await window.behat.handleCustomURL('$customurl');");
|
||||
|
||||
$this->wait_for_pending_js();
|
||||
if ($result !== 'OK') {
|
||||
throw new DriverException('Error handling url - ' . $result);
|
||||
}
|
||||
|
||||
if (!empty($successXPath)) {
|
||||
// Wait until the page appears.
|
||||
|
@ -554,10 +565,9 @@ class behat_app_helper extends behat_base {
|
|||
}
|
||||
throw new DriverException('Moodle App custom URL page not loaded');
|
||||
}, false, 30);
|
||||
|
||||
// Wait for JS to finish as well.
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { NgZone } from '@singletons';
|
||||
import { TestsBehatBlocking } from './behat-blocking';
|
||||
import { TestBehatElementLocator } from './behat-runtime';
|
||||
|
||||
// Containers that block containers behind them.
|
||||
|
@ -447,21 +447,23 @@ export class TestsBehatDomUtils {
|
|||
|
||||
element.scrollIntoView(false);
|
||||
|
||||
return new Promise<DOMRect>((resolve): void => {
|
||||
requestAnimationFrame(() => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const promise = new CorePromisedValue<DOMRect>();
|
||||
|
||||
if (initialRect.y !== rect.y) {
|
||||
setTimeout(() => {
|
||||
resolve(rect);
|
||||
}, 300);
|
||||
requestAnimationFrame(() => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
return;
|
||||
}
|
||||
if (initialRect.y !== rect.y) {
|
||||
setTimeout(() => {
|
||||
promise.resolve(rect);
|
||||
}, 300);
|
||||
|
||||
resolve(rect);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
promise.resolve(rect);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -471,7 +473,7 @@ export class TestsBehatDomUtils {
|
|||
*/
|
||||
static async pressElement(element: HTMLElement): Promise<void> {
|
||||
await NgZone.run(async () => {
|
||||
const blockKey = TestsBehatBlocking.block();
|
||||
const promise = new CorePromisedValue<void>();
|
||||
|
||||
// Events don't bubble up across Shadow DOM boundaries, and some buttons
|
||||
// may not work without doing this.
|
||||
|
@ -501,8 +503,10 @@ export class TestsBehatDomUtils {
|
|||
element.dispatchEvent(new MouseEvent('mouseup', eventOptions));
|
||||
element.click();
|
||||
|
||||
TestsBehatBlocking.unblock(blockKey);
|
||||
promise.resolve();
|
||||
}, 300);
|
||||
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -514,7 +518,7 @@ export class TestsBehatDomUtils {
|
|||
*/
|
||||
static async setElementValue(element: HTMLInputElement | HTMLElement, value: string): Promise<void> {
|
||||
await NgZone.run(async () => {
|
||||
const blockKey = TestsBehatBlocking.block();
|
||||
const promise = new CorePromisedValue<void>();
|
||||
|
||||
// Functions to get/set value depending on field type.
|
||||
const setValue = (text: string) => {
|
||||
|
@ -569,7 +573,9 @@ export class TestsBehatDomUtils {
|
|||
element.dispatchEvent(event);
|
||||
}
|
||||
|
||||
TestsBehatBlocking.unblock(blockKey);
|
||||
promise.resolve();
|
||||
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -83,8 +83,6 @@ export class TestsBehatRuntime {
|
|||
* @return OK if successful, or ERROR: followed by message.
|
||||
*/
|
||||
static async handleCustomURL(url: string): Promise<string> {
|
||||
const blockKey = TestsBehatBlocking.block();
|
||||
|
||||
try {
|
||||
await NgZone.run(async () => {
|
||||
await CoreCustomURLSchemes.handleCustomURL(url);
|
||||
|
@ -93,8 +91,6 @@ export class TestsBehatRuntime {
|
|||
return 'OK';
|
||||
} catch (error) {
|
||||
return 'ERROR: ' + error.message;
|
||||
} finally {
|
||||
TestsBehatBlocking.unblock(blockKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,15 +119,9 @@ export class TestsBehatRuntime {
|
|||
* @return Promise resolved if all handlers are executed successfully, rejected otherwise.
|
||||
*/
|
||||
static async forceSyncExecution(): Promise<void> {
|
||||
const blockKey = TestsBehatBlocking.block();
|
||||
|
||||
try {
|
||||
await NgZone.run(async () => {
|
||||
await CoreCronDelegate.forceSyncExecution();
|
||||
});
|
||||
} finally {
|
||||
TestsBehatBlocking.unblock(blockKey);
|
||||
}
|
||||
await NgZone.run(async () => {
|
||||
await CoreCronDelegate.forceSyncExecution();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,20 +130,13 @@ export class TestsBehatRuntime {
|
|||
* @return Promise resolved when all components have been rendered.
|
||||
*/
|
||||
static async waitLoadingToFinish(): Promise<void> {
|
||||
const blockKey = TestsBehatBlocking.block();
|
||||
|
||||
await NgZone.run(async () => {
|
||||
try {
|
||||
const elements = Array.from(document.body.querySelectorAll<HTMLElement>('core-loading'))
|
||||
.filter((element) => CoreDom.isElementVisible(element));
|
||||
const elements = Array.from(document.body.querySelectorAll<HTMLElement>('core-loading'))
|
||||
.filter((element) => CoreDom.isElementVisible(element));
|
||||
|
||||
await Promise.all(elements.map(element =>
|
||||
CoreComponentsRegistry.waitComponentReady(element, CoreLoadingComponent)));
|
||||
} finally {
|
||||
TestsBehatBlocking.unblock(blockKey);
|
||||
}
|
||||
await Promise.all(elements.map(element =>
|
||||
CoreComponentsRegistry.waitComponentReady(element, CoreLoadingComponent)));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,7 +145,7 @@ export class TestsBehatRuntime {
|
|||
* @param button Type of button to press.
|
||||
* @return OK if successful, or ERROR: followed by message.
|
||||
*/
|
||||
static pressStandard(button: string): string {
|
||||
static async pressStandard(button: string): Promise<string> {
|
||||
this.log('Action - Click standard button: ' + button);
|
||||
|
||||
// Find button
|
||||
|
@ -194,7 +177,7 @@ export class TestsBehatRuntime {
|
|||
}
|
||||
|
||||
// Click button
|
||||
TestsBehatDomUtils.pressElement(foundButton);
|
||||
await TestsBehatDomUtils.pressElement(foundButton);
|
||||
|
||||
return 'OK';
|
||||
}
|
||||
|
@ -348,7 +331,7 @@ export class TestsBehatRuntime {
|
|||
* @param locator Element locator.
|
||||
* @return OK if successful, or ERROR: followed by message
|
||||
*/
|
||||
static press(locator: TestBehatElementLocator): string {
|
||||
static async press(locator: TestBehatElementLocator): Promise<string> {
|
||||
this.log('Action - Press', locator);
|
||||
|
||||
try {
|
||||
|
@ -358,7 +341,7 @@ export class TestsBehatRuntime {
|
|||
return 'ERROR: No element matches locator to press.';
|
||||
}
|
||||
|
||||
TestsBehatDomUtils.pressElement(found);
|
||||
await TestsBehatDomUtils.pressElement(found);
|
||||
|
||||
return 'OK';
|
||||
} catch (error) {
|
||||
|
@ -397,7 +380,7 @@ export class TestsBehatRuntime {
|
|||
* @param value New value
|
||||
* @return OK or ERROR: followed by message
|
||||
*/
|
||||
static setField(field: string, value: string): string {
|
||||
static async setField(field: string, value: string): Promise<string> {
|
||||
this.log('Action - Set field ' + field + ' to: ' + value);
|
||||
|
||||
const found: HTMLElement | HTMLInputElement = TestsBehatDomUtils.findElementBasedOnText(
|
||||
|
@ -408,7 +391,7 @@ export class TestsBehatRuntime {
|
|||
return 'ERROR: No element matches field to set.';
|
||||
}
|
||||
|
||||
TestsBehatDomUtils.setElementValue(found, value);
|
||||
await TestsBehatDomUtils.setElementValue(found, value);
|
||||
|
||||
return 'OK';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue