MOBILE-4496 reminders: Improve notification tests

The previous implementation was too flaky because it relied on test execution time
main
Noel De Martin 2024-01-29 14:51:20 +01:00
parent da976a08a7
commit 208ec01b6c
4 changed files with 148 additions and 49 deletions

View File

@ -1055,14 +1055,24 @@ class behat_app extends behat_app_helper {
}
/**
* Send pending notifications.
*
* @Then /^I flush pending notifications in the app$/
*/
public function i_flush_notifications() {
$this->runtime_js("flushNotifications()");
}
/**
* Check if a notification has been triggered and is present.
*
* @Then /^a notification with title (".+") is( not)? present in the app$/
* @Then /^a notification with title (".+") should( not)? be present in the app$/
* @param string $title Notification title
* @param bool $not Whether assert that the notification was not found
*/
public function notification_present_in_the_app(string $title, bool $not = false) {
$this->spin(function() use ($not, $title) {
$result = $this->runtime_js("notificationIsPresentWithText($title)");
if ($not && $result === 'YES') {
@ -1078,6 +1088,50 @@ class behat_app extends behat_app_helper {
}
return true;
});
}
/**
* Check if a notification has been scheduled.
*
* @Then /^a notification with title (".+") should( not)? be scheduled(?: (\d+) minutes before the "(.+)" assignment due date)? in the app$/
* @param string $title Notification title
* @param bool $not Whether assert that the notification was not scheduled
* @param int $minutes Minutes before the assignment at which the notification was scheduled
* @param string $assignment Assignment for which the notification was scheduled
*/
public function notification_scheduled_in_the_app(string $title, bool $not = false, ?int $minutes = null, ?string $assignment = null) {
if (!is_null($minutes)) {
global $DB;
$assign = $DB->get_record('assign', ['name' => $assignment]);
if (!$assign) {
throw new ExpectationException("Couldn't find '$assignment' assignment", $this->getSession()->getDriver());
}
$date = ($assign->duedate - $minutes * 60) * 1000;
} else {
$date = 'undefined';
}
$this->spin(function() use ($not, $title, $date) {
$result = $this->runtime_js("notificationIsScheduledWithText($title, $date)");
if ($not && $result === 'YES') {
throw new ExpectationException("Notification is scheduled", $this->getSession()->getDriver());
}
if (!$not && $result === 'NO') {
throw new ExpectationException("Notification is not scheduled", $this->getSession()->getDriver());
}
if ($result !== 'YES' && $result !== 'NO') {
throw new DriverException('Error checking scheduled notification - ' . $result);
}
return true;
});
}
/**

View File

@ -67,6 +67,17 @@ export class LocalNotificationsMock extends LocalNotifications {
});
}
/**
* Flush pending notifications.
*/
flush(): void {
for (const notification of this.scheduledNotifications) {
this.sendNotification(notification);
}
this.scheduledNotifications = [];
}
/**
* Sets timeout for next nofitication.
*/
@ -104,36 +115,7 @@ export class LocalNotificationsMock extends LocalNotifications {
const notificationTime = nextNotification.trigger?.at?.getTime() || 0;
if (notificationTime === 0 || notificationTime <= dateNow) {
const body = Array.isArray(nextNotification.text) ? nextNotification.text.join() : nextNotification.text;
const notification = new Notification(nextNotification.title || '', {
body,
data: nextNotification.data,
icon: nextNotification.icon,
requireInteraction: true,
tag: nextNotification.data?.component,
});
this.triggeredNotifications.push(nextNotification);
this.observables.trigger.next(nextNotification);
notification.addEventListener('click', () => {
this.observables.click.next(nextNotification);
notification.close();
if (nextNotification.id) {
delete(this.presentNotifications[nextNotification.id]);
}
});
if (nextNotification.id) {
this.presentNotifications[nextNotification.id] = notification;
notification.addEventListener('close', () => {
delete(this.presentNotifications[nextNotification.id ?? 0]);
});
}
this.sendNotification(nextNotification);
this.scheduledNotifications.shift();
this.triggerNextNotification();
} else {
@ -141,6 +123,43 @@ export class LocalNotificationsMock extends LocalNotifications {
}
}
/**
* Send notification.
*
* @param localNotification Notification.
*/
protected sendNotification(localNotification: ILocalNotification): void {
const body = Array.isArray(localNotification.text) ? localNotification.text.join() : localNotification.text;
const notification = new Notification(localNotification.title || '', {
body,
data: localNotification.data,
icon: localNotification.icon,
requireInteraction: true,
tag: localNotification.data?.component,
});
this.triggeredNotifications.push(localNotification);
this.observables.trigger.next(localNotification);
notification.addEventListener('click', () => {
this.observables.click.next(localNotification);
notification.close();
if (localNotification.id) {
delete(this.presentNotifications[localNotification.id]);
}
});
if (localNotification.id) {
this.presentNotifications[localNotification.id] = notification;
notification.addEventListener('close', () => {
delete(this.presentNotifications[localNotification.id ?? 0]);
});
}
}
/**
* @inheritdoc
*/

View File

@ -62,20 +62,21 @@ Feature: Set a new reminder on activity
And I press "Custom..." in the app
Then I should find "Custom reminder" in the app
When I set the following fields to these values in the app:
| Value | 69 |
| Value | 40 |
| Units | minutes |
And I press "Set reminder" in the app
Then I should find "Reminder set for" in the app
When I wait "50" seconds
Then a notification with title "Due: Assignment 01" is present in the app
And I close a notification with title "Due: Assignment 01" in the app
And a notification with title "Due: Assignment 01" should be scheduled 40 minutes before the "Assignment 01" assignment due date in the app
When I flush pending notifications in the app
Then a notification with title "Due: Assignment 01" should be present in the app
# Set and check reminder is cancelled
When I press "Set a reminder for \"Assignment 01\" (Due)" in the app
When I close a notification with title "Due: Assignment 01" in the app
And I press "Set a reminder for \"Assignment 01\" (Due)" in the app
And I press "Custom..." in the app
Then I should find "Custom reminder" in the app
When I set the following fields to these values in the app:
| Value | 68 |
| Value | 20 |
| Units | minutes |
And I press "Set reminder" in the app
Then I should find "Reminder set for" in the app
@ -83,8 +84,8 @@ Feature: Set a new reminder on activity
Then I should find "Reminder set for" in the app
When I press "Delete reminder" in the app
Then I should find "Reminder deleted" in the app
When I wait "50" seconds
Then a notification with title "Due: Assignment 01" is not present in the app
But a notification with title "Due: Assignment 01" should not be scheduled in the app
And a notification with title "Due: Assignment 01" should not be present in the app
Scenario: Check toast is correct
Given I entered the assign activity "Assignment 02" on course "Course 1" as "student1" in the app

View File

@ -30,6 +30,7 @@ import { CoreSites, CoreSitesProvider } from '@services/sites';
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
import { CoreSwipeNavigationDirective } from '@directives/swipe-navigation';
import { Swiper } from 'swiper';
import { LocalNotificationsMock } from '@features/emulator/services/local-notifications';
/**
* Behat runtime servive with public API.
@ -585,6 +586,13 @@ export class TestingBehatRuntimeService {
console.log('BEHAT: ' + nowFormatted, ...args); // eslint-disable-line no-console
}
/**
* Flush pending notifications.
*/
flushNotifications(): void {
(LocalNotifications as unknown as LocalNotificationsMock).flush();
}
/**
* Check a notification is present.
*
@ -608,6 +616,23 @@ export class TestingBehatRuntimeService {
return (await LocalNotifications.isPresent(notification.id)) ? 'YES' : 'NO';
}
/**
* Check a notification is scheduled.
*
* @param title Title of the notification
* @param date Scheduled notification date.
* @returns YES or NO: depending on the result.
*/
async notificationIsScheduledWithText(title: string, date?: number): Promise<string> {
const notifications = await LocalNotifications.getAllScheduled();
const notification = notifications.find(
(notification) => notification.title?.includes(title) && (!date || notification.trigger?.at?.getTime() === date),
);
return notification ? 'YES' : 'NO';
}
/**
* Close notification.
*