MOBILE-4061 behat: Always use the runtime to communicate with the app

main
Pau Ferrer Ocaña 2022-06-14 12:29:59 +02:00
parent f69e7971be
commit ef574e7e63
6 changed files with 83 additions and 77 deletions

View File

@ -408,7 +408,7 @@ class behat_app extends behat_app_helper {
], ],
]); ]);
$this->evaluate_script("return window.pushNotifications.notificationClicked($notification)"); $this->evaluate_script("window.behat.notificationClicked($notification)");
$this->wait_for_pending_js(); $this->wait_for_pending_js();
} }
@ -717,25 +717,8 @@ class behat_app extends behat_app_helper {
* @When I run cron tasks in the app * @When I run cron tasks in the app
*/ */
public function i_run_cron_tasks_in_the_app() { public function i_run_cron_tasks_in_the_app() {
$session = $this->getSession(); $this->evaluate_script('window.behat.forceSyncExecution()');
$this->wait_for_pending_js();
// Force cron tasks execution and wait until they are completed.
$operationid = random_string();
$session->executeScript(
"cronProvider.forceSyncExecution().then(() => { window['behat_{$operationid}_completed'] = true; });"
);
$this->spin(
function() use ($session, $operationid) {
return $session->evaluateScript("window['behat_{$operationid}_completed'] || false");
},
false,
60,
new ExpectationException('Forced cron tasks in the app took too long to complete', $session)
);
// Trigger Angular change detection.
$this->trigger_angular_change_detection();
} }
/** /**
@ -744,28 +727,8 @@ class behat_app extends behat_app_helper {
* @When I wait loading to finish in the app * @When I wait loading to finish in the app
*/ */
public function i_wait_loading_to_finish_in_the_app() { public function i_wait_loading_to_finish_in_the_app() {
$session = $this->getSession(); $this->evaluate_script('window.behat.waitLoadingToFinish()');
$this->wait_for_pending_js();
$this->spin(
function() use ($session) {
$this->trigger_angular_change_detection();
$nodes = $this->find_all('css', 'core-loading ion-spinner');
foreach ($nodes as $node) {
if (!$node->isVisible()) {
continue;
}
return false;
}
return true;
},
false,
60,
new ExpectationException('"Loading took too long to complete', $session)
);
} }
/** /**
@ -786,7 +749,7 @@ class behat_app extends behat_app_helper {
$this->getSession()->switchToWindow($names[1]); $this->getSession()->switchToWindow($names[1]);
} }
$this->execute_script('window.close();'); $this->evaluate_script('window.close();');
$this->getSession()->switchToWindow($names[0]); $this->getSession()->switchToWindow($names[0]);
} }
@ -798,7 +761,7 @@ class behat_app extends behat_app_helper {
* @throws DriverException If the navigator.online mode is not available * @throws DriverException If the navigator.online mode is not available
*/ */
public function i_switch_offline_mode(string $offline) { public function i_switch_offline_mode(string $offline) {
$this->execute_script("appProvider.setForceOffline($offline);"); $this->evaluate_script("window.behat.network.setForceOffline($offline);");
} }
} }

View File

@ -318,7 +318,7 @@ class behat_app_helper extends behat_base {
$initOptions->skipOnBoarding = $options['skiponboarding'] ?? true; $initOptions->skipOnBoarding = $options['skiponboarding'] ?? true;
$initOptions->configOverrides = $this->appconfig; $initOptions->configOverrides = $this->appconfig;
$this->execute_script('window.behatInit(' . json_encode($initOptions) . ');'); $this->evaluate_script('window.behatInit(' . json_encode($initOptions) . ');');
} catch (Exception $error) { } catch (Exception $error) {
throw new DriverException('Moodle App not running or not running on Automated mode.'); throw new DriverException('Moodle App not running or not running on Automated mode.');
} }
@ -433,14 +433,6 @@ class behat_app_helper extends behat_base {
} }
} }
/**
* Trigger Angular change detection.
*/
protected function trigger_angular_change_detection() {
$this->getSession()->executeScript('ngZone.run(() => {});');
}
/** /**
* Evaluate a script that returns a Promise. * Evaluate a script that returns a Promise.
* *

View File

@ -431,7 +431,7 @@ export class CorePushNotificationsProvider {
/** /**
* Function called when a push notification is clicked. Redirect the user to the right state. * Function called when a push notification is clicked. Redirect the user to the right state.
* *
* @param notification Notification. * @param data Notification data.
* @return Promise resolved when done. * @return Promise resolved when done.
*/ */
async notificationClicked(data: CorePushNotificationsNotificationBasicData): Promise<void> { async notificationClicked(data: CorePushNotificationsNotificationBasicData): Promise<void> {

View File

@ -12,35 +12,15 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { ApplicationRef, NgZone as NgZoneService } from '@angular/core'; import { CoreAppProvider } from '@services/app';
import { CorePushNotifications, CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications';
import { CoreApp, CoreAppProvider } from '@services/app';
import { CoreConfig, CoreConfigProvider } from '@services/config';
import { CoreCronDelegate, CoreCronDelegateService } from '@services/cron';
import { CoreDB, CoreDbProvider } from '@services/db'; import { CoreDB, CoreDbProvider } from '@services/db';
import { CoreCustomURLSchemes, CoreCustomURLSchemesProvider } from '@services/urlschemes';
import { Application, NgZone } from '@singletons';
type AutomatedTestsWindow = Window & { type AutomatedTestsWindow = Window & {
appRef?: ApplicationRef;
appProvider?: CoreAppProvider;
dbProvider?: CoreDbProvider; dbProvider?: CoreDbProvider;
configProvider?: CoreConfigProvider;
cronProvider?: CoreCronDelegateService;
ngZone?: NgZoneService;
pushNotifications?: CorePushNotificationsProvider;
urlSchemes?: CoreCustomURLSchemesProvider;
}; };
function initializeAutomatedTestsWindow(window: AutomatedTestsWindow) { function initializeAutomatedTestsWindow(window: AutomatedTestsWindow) {
window.appRef = Application.instance;
window.appProvider = CoreApp.instance;
window.dbProvider = CoreDB.instance; window.dbProvider = CoreDB.instance;
window.configProvider = CoreConfig.instance;
window.cronProvider = CoreCronDelegate.instance;
window.ngZone = NgZone.instance;
window.pushNotifications = CorePushNotifications.instance;
window.urlSchemes = CoreCustomURLSchemes.instance;
} }
export default function(): void { export default function(): void {

View File

@ -468,7 +468,7 @@ export class TestsBehatDomUtils {
* @param element Element to press. * @param element Element to press.
*/ */
static async pressElement(element: HTMLElement): Promise<void> { static async pressElement(element: HTMLElement): Promise<void> {
NgZone.run(async () => { await NgZone.run(async () => {
const blockKey = TestsBehatBlocking.block(); const blockKey = TestsBehatBlocking.block();
// Events don't bubble up across Shadow DOM boundaries, and some buttons // Events don't bubble up across Shadow DOM boundaries, and some buttons
@ -511,7 +511,7 @@ export class TestsBehatDomUtils {
* @param value Value to be set. * @param value Value to be set.
*/ */
static async setElementValue(element: HTMLElement, value: string): Promise<void> { static async setElementValue(element: HTMLElement, value: string): Promise<void> {
NgZone.run(async () => { await NgZone.run(async () => {
const blockKey = TestsBehatBlocking.block(); const blockKey = TestsBehatBlocking.block();
// Functions to get/set value depending on field type. // Functions to get/set value depending on field type.

View File

@ -19,6 +19,15 @@ import { CoreLoginHelperProvider } from '@features/login/services/login-helper';
import { CoreConfig } from '@services/config'; import { CoreConfig } from '@services/config';
import { EnvironmentConfig } from '@/types/config'; import { EnvironmentConfig } from '@/types/config';
import { NgZone } from '@singletons'; import { NgZone } from '@singletons';
import { CoreNetwork } from '@services/network';
import {
CorePushNotifications,
CorePushNotificationsNotificationBasicData,
} from '@features/pushnotifications/services/pushnotifications';
import { CoreCronDelegate } from '@services/cron';
import { CoreLoadingComponent } from '@components/loading/loading';
import { CoreComponentsRegistry } from '@singletons/components-registry';
import { CoreDom } from '@singletons/dom';
/** /**
* Behat runtime servive with public API. * Behat runtime servive with public API.
@ -46,6 +55,10 @@ export class TestsBehatRuntime {
scrollTo: TestsBehatRuntime.scrollTo, scrollTo: TestsBehatRuntime.scrollTo,
setField: TestsBehatRuntime.setField, setField: TestsBehatRuntime.setField,
handleCustomURL: TestsBehatRuntime.handleCustomURL, handleCustomURL: TestsBehatRuntime.handleCustomURL,
notificationClicked: TestsBehatRuntime.notificationClicked,
forceSyncExecution: TestsBehatRuntime.forceSyncExecution,
waitLoadingToFinish: TestsBehatRuntime.waitLoadingToFinish,
network: CoreNetwork.instance,
}; };
if (!options) { if (!options) {
@ -85,6 +98,64 @@ export class TestsBehatRuntime {
} }
} }
/**
* Function called when a push notification is clicked. Redirect the user to the right state.
*
* @param data Notification data.
* @return Promise resolved when done.
*/
static async notificationClicked(data: CorePushNotificationsNotificationBasicData): Promise<void> {
const blockKey = TestsBehatBlocking.block();
try {
await NgZone.run(async () => {
await CorePushNotifications.notificationClicked(data);
});
} finally {
TestsBehatBlocking.unblock(blockKey);
}
}
/**
* Force execution of synchronization cron tasks without waiting for the scheduled time.
* Please notice that some tasks may not be executed depending on the network connection and sync settings.
*
* @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);
}
}
/**
* Wait all controlled components to be rendered.
*
* @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));
await Promise.all(elements.map(element =>
CoreComponentsRegistry.waitComponentReady(element, CoreLoadingComponent)));
} finally {
TestsBehatBlocking.unblock(blockKey);
}
});
}
/** /**
* Function to find and click an app standard button. * Function to find and click an app standard button.
* *