Skip to main content
Version: Next

Mocking User Notifications

Detox supports mocking user notifications.

Note: The mocking mechanism will not mimic the UI of a user notification. Instead, it will only simulate a user interaction with the notification - namely, the "opening" of it (equivalent to a user’s tap/swipe on it in the notification center).

Mocking App Launch With a Notification

launchApp() with custom parameters (i.e. userNotification) will trigger the mocking mechanism.

await device.launchApp({newInstance: true, userNotification: notification});

Example

describe('Launch with push notification', () => {
it('should handle the notification', async () => {
await device.launchApp({
newInstance: true,
userNotification: userNotificationPushTrigger,
});
await expect(element(by.text('From push'))).toBeVisible();
});
});

Mocking Notification Reception on a Running App

Use the sendUserNotification() method to send notification to running app. Notifications can be sent to an active or a background app.

Note: while the name sendUserNotification() is not very idiomatic on Android, as notification data is not "sent" to apps (rather, it is bundled into an Activity/Service launch Intent as Intent-extras), this unified API is used, for the time being, for both platforms equivalently. With plans of a more extensive support for Android, we estimate it would be deprecated when the time comes.

await device.sendUserNotification(notification);

Example:

describe('Foreground user notifications', () => {
it('should handle the local notification from inside the app', async () => {
await device.launchApp();
await device.sendUserNotification(localNotification);
await expect(element(by.text('from local notification'))).toBeVisible();
});
});

Notification JSON Format

User notifications are passed as JSON objects to Detox. The JSON object needs to provide some required data, but can also provide an additional, optional payload.

Mind the major difference here between the two platforms. While on iOS many types of data fields are applicable, Android is very loosely defined - with support for just free-form user data in the payload field.

KeyRequiredValue TypePlatformDescription
triggerYesObjectiOSThe conditions that trigger the delivery of the notification. See the Triggers section below.
titleNoStringiOSA short description of the reason for the alert.
subtitleNoStringiOSA secondary description of the reason for the alert.
bodyNoStringiOSThe body of the notification.
badgeNoIntegeriOSThe number to display as the app’s icon badge.
payloadiOS: No
Android: Yes
ObjectiOS & AndroidAn object of custom information associated with the notification.
Android: see full description below
categoryNoStringiOSThe identifier of the app-defined category object.
content-availableNoIntegeriOSInclude this key with a value of 1 to configure a silent notification.
user-textNoStringiOSThe text response provided by the user.
action-identifierNoStringiOSThe identifier for the action that the user selected.

Triggers (iOS-only)

Triggers are objects representing the trigger.

KeyRequiredValue TypeDescription
typeYesStringThe conditions that trigger the delivery of the notification. See the Trigger Types section below.
repeatsNoBooleanIndicates whether the event repeats. Only used for calendar, timeInterval and location trigger types.
timeIntervalYes for timeInterval trigger typeNumberThe time interval used to create the trigger.
date-componentsYes for calendar trigger typeObjectThe date components used to construct this object. See the Date Components section below.
regionYes for location trigger typeObjectThe region used to determine when the notification is sent. See the Region section below.
Trigger Types

There are four types of triggers supported by Detox at this time:

  • push
  • calendar
  • timeInterval
  • location

For convenience, these trigger types are provided as constants in DetoxConstants:

const DetoxConstants = require('detox/index').DetoxConstants;

const userNotification = {
"trigger": {
"type": DetoxConstants.userNotificationTriggers.push
},
// ...
}

Date Components (iOS-only)

KeyRequiredValue TypeDescription
eraNoIntegerThe number of era units for the receiver.
yearNoIntegerThe number of year units for the receiver.
monthNoIntegerThe number of month units for the receiver.
dayNoIntegerThe number of day units for the receiver.
hourNoIntegerThe number of hour units for the receiver.
minuteNoIntegerThe number of minute units for the receiver.
secondNoIntegerThe number of second units for the receiver.
weekdayNoIntegerThe number of the weekday unit for the receiver.
weekdayOrdinalNoIntegerThe ordinal number of weekday units for the receiver.
quarterNoIntegerThe number of quarters for the receiver.
weekOfMonthNoIntegerThe week number of the month for the receiver.
leapMonthNoBooleanIndicates whether the month is a leap month.

Region (iOS-only)

KeyRequiredValue TypeDescription
centerYesObjectThe center point of the geographic area. See the Coordinate section below.
radiusYesNumberThe radius (measured in meters) that defines the geographic area’s outer boundary.
notifyOnEntryNoBooleanIndicates that notifications are generated upon entry into the region.
notifyOnExitNoBooleanIndicates that notifications are generated upon exit from the region.

Coordinate (iOS-only)

KeyRequiredValue TypeDescription
latitudeYesNumberThe latitude in degrees. Positive values indicate latitudes north of the equator. Negative values indicate latitudes south of the equator.
longitudeYesNumberThe longitude in degrees. Measurements are relative to the zero meridian, with positive values extending east of the meridian and negative values extending west of the meridian.

Payload

On Android, the content will be available via the activity’s getIntent() API, inside the intent’s extra bundle. Under some limitations, that includes data-cascading so as to provide comprehensive support for JavaScript’s advanced object-hierarchy capabilities as much as possible. As an example, consider this payload:

const userNotification = {
payload: {
userData: 'userDataValue',
userDataNum: 111.2,
userDataFlag: true,
userDataArray: ['rock', 'paper', 'scissors'],
userDataObj: {
cascadedKey: 'cascadedValue'
},
},
};

The outcome on the native side will be such that all of these conditions evaluate to true:

activity.getIntent().getStringExtra("userData") == "userDataValue";
activity.getIntent().getDoubleExtra("userDataNum") == 111.2;
activity.getIntent().getBooleanExtra("userDataFlag") == true;
activity.getIntent().getStringArrayExtra("userDataArray")[0] == "rock";
activity.getIntent().getBundleExtra("userDataObj").getString("cascadedKey") == "cascadedValue";
Handling at Runtime

Note that on Android, data delivered through an intent at runtime, is typically received in your activity’s onNewIntent callback. Be sure to consider what should be done in order to handle this type of a use case in your app: Namely, that setIntent() should be called in order for the data to be later available in your app through getIntent(), as explained earlier.

This isn’t related to Detox in particular, and is set here simply to help you consider all the use cases in your app so that tests coverage would be optimal.

Examples

  1. Calendar Trigger
  2. Location Trigger
  3. Time Interval Trigger
  4. Push Trigger