MOBILE-4496 reminders: Improve notification tests
The previous implementation was too flaky because it relied on test execution timemain
parent
da976a08a7
commit
208ec01b6c
|
@ -1055,29 +1055,83 @@ 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.
|
* 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 string $title Notification title
|
||||||
* @param bool $not Whether assert that the notification was not found
|
* @param bool $not Whether assert that the notification was not found
|
||||||
*/
|
*/
|
||||||
public function notification_present_in_the_app(string $title, bool $not = false) {
|
public function notification_present_in_the_app(string $title, bool $not = false) {
|
||||||
$result = $this->runtime_js("notificationIsPresentWithText($title)");
|
$this->spin(function() use ($not, $title) {
|
||||||
|
$result = $this->runtime_js("notificationIsPresentWithText($title)");
|
||||||
|
|
||||||
if ($not && $result === 'YES') {
|
if ($not && $result === 'YES') {
|
||||||
throw new ExpectationException("Notification is present", $this->getSession()->getDriver());
|
throw new ExpectationException("Notification is present", $this->getSession()->getDriver());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$not && $result === 'NO') {
|
||||||
|
throw new ExpectationException("Notification is not present", $this->getSession()->getDriver());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($result !== 'YES' && $result !== 'NO') {
|
||||||
|
throw new DriverException('Error checking notification - ' . $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
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';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$not && $result === 'NO') {
|
$this->spin(function() use ($not, $title, $date) {
|
||||||
throw new ExpectationException("Notification is not present", $this->getSession()->getDriver());
|
$result = $this->runtime_js("notificationIsScheduledWithText($title, $date)");
|
||||||
}
|
|
||||||
|
|
||||||
if ($result !== 'YES' && $result !== 'NO') {
|
if ($not && $result === 'YES') {
|
||||||
throw new DriverException('Error checking notification - ' . $result);
|
throw new ExpectationException("Notification is scheduled", $this->getSession()->getDriver());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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.
|
* Sets timeout for next nofitication.
|
||||||
*/
|
*/
|
||||||
|
@ -104,36 +115,7 @@ export class LocalNotificationsMock extends LocalNotifications {
|
||||||
|
|
||||||
const notificationTime = nextNotification.trigger?.at?.getTime() || 0;
|
const notificationTime = nextNotification.trigger?.at?.getTime() || 0;
|
||||||
if (notificationTime === 0 || notificationTime <= dateNow) {
|
if (notificationTime === 0 || notificationTime <= dateNow) {
|
||||||
const body = Array.isArray(nextNotification.text) ? nextNotification.text.join() : nextNotification.text;
|
this.sendNotification(nextNotification);
|
||||||
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.scheduledNotifications.shift();
|
this.scheduledNotifications.shift();
|
||||||
this.triggerNextNotification();
|
this.triggerNextNotification();
|
||||||
} else {
|
} 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
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -62,20 +62,21 @@ Feature: Set a new reminder on activity
|
||||||
And I press "Custom..." in the app
|
And I press "Custom..." in the app
|
||||||
Then I should find "Custom reminder" in the app
|
Then I should find "Custom reminder" in the app
|
||||||
When I set the following fields to these values in the app:
|
When I set the following fields to these values in the app:
|
||||||
| Value | 69 |
|
| Value | 40 |
|
||||||
| Units | minutes |
|
| Units | minutes |
|
||||||
And I press "Set reminder" in the app
|
And I press "Set reminder" in the app
|
||||||
Then I should find "Reminder set for" in the app
|
Then I should find "Reminder set for" in the app
|
||||||
When I wait "50" seconds
|
And a notification with title "Due: Assignment 01" should be scheduled 40 minutes before the "Assignment 01" assignment due date in the app
|
||||||
Then a notification with title "Due: Assignment 01" is present in the app
|
When I flush pending notifications in the app
|
||||||
And I close a notification with title "Due: Assignment 01" in the app
|
Then a notification with title "Due: Assignment 01" should be present in the app
|
||||||
|
|
||||||
# Set and check reminder is cancelled
|
# 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
|
And I press "Custom..." in the app
|
||||||
Then I should find "Custom reminder" in the app
|
Then I should find "Custom reminder" in the app
|
||||||
When I set the following fields to these values in the app:
|
When I set the following fields to these values in the app:
|
||||||
| Value | 68 |
|
| Value | 20 |
|
||||||
| Units | minutes |
|
| Units | minutes |
|
||||||
And I press "Set reminder" in the app
|
And I press "Set reminder" in the app
|
||||||
Then I should find "Reminder set for" 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
|
Then I should find "Reminder set for" in the app
|
||||||
When I press "Delete reminder" in the app
|
When I press "Delete reminder" in the app
|
||||||
Then I should find "Reminder deleted" in the app
|
Then I should find "Reminder deleted" in the app
|
||||||
When I wait "50" seconds
|
But a notification with title "Due: Assignment 01" should not be scheduled in the app
|
||||||
Then a notification with title "Due: Assignment 01" is not present in the app
|
And a notification with title "Due: Assignment 01" should not be present in the app
|
||||||
|
|
||||||
Scenario: Check toast is correct
|
Scenario: Check toast is correct
|
||||||
Given I entered the assign activity "Assignment 02" on course "Course 1" as "student1" in the app
|
Given I entered the assign activity "Assignment 02" on course "Course 1" as "student1" in the app
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { CoreSites, CoreSitesProvider } from '@services/sites';
|
||||||
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
|
import { CoreNavigator, CoreNavigatorService } from '@services/navigator';
|
||||||
import { CoreSwipeNavigationDirective } from '@directives/swipe-navigation';
|
import { CoreSwipeNavigationDirective } from '@directives/swipe-navigation';
|
||||||
import { Swiper } from 'swiper';
|
import { Swiper } from 'swiper';
|
||||||
|
import { LocalNotificationsMock } from '@features/emulator/services/local-notifications';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Behat runtime servive with public API.
|
* Behat runtime servive with public API.
|
||||||
|
@ -585,6 +586,13 @@ export class TestingBehatRuntimeService {
|
||||||
console.log('BEHAT: ' + nowFormatted, ...args); // eslint-disable-line no-console
|
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.
|
* Check a notification is present.
|
||||||
*
|
*
|
||||||
|
@ -608,6 +616,23 @@ export class TestingBehatRuntimeService {
|
||||||
return (await LocalNotifications.isPresent(notification.id)) ? 'YES' : 'NO';
|
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.
|
* Close notification.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue