Commit c569ebcc authored by d4vidi's avatar d4vidi

Move notification ID to JS, add testing

parent 12dff6de
...@@ -16,10 +16,6 @@ android { ...@@ -16,10 +16,6 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
testOptions {
unitTests.returnDefaultValues = true
}
} }
dependencies { dependencies {
...@@ -30,4 +26,5 @@ dependencies { ...@@ -30,4 +26,5 @@ dependencies {
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.+' testCompile 'org.mockito:mockito-core:2.+'
testCompile "org.robolectric:robolectric:3.1.4"
} }
...@@ -70,22 +70,15 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule implements ...@@ -70,22 +70,15 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule implements
} }
@ReactMethod @ReactMethod
public void postLocalNotification(ReadableMap notificationPropsMap, final Promise promise) { public void postLocalNotification(ReadableMap notificationPropsMap, int notificationId) {
Log.d(LOGTAG, "Native method invocation: postLocalNotification"); Log.d(LOGTAG, "Native method invocation: postLocalNotification");
Object result = null;
try {
final Bundle notificationProps = Arguments.toBundle(notificationPropsMap); final Bundle notificationProps = Arguments.toBundle(notificationPropsMap);
final IPushNotification pushNotification = PushNotification.get(getReactApplicationContext().getApplicationContext(), notificationProps, ReactAppLifecycleFacade.get()); final IPushNotification pushNotification = PushNotification.get(getReactApplicationContext().getApplicationContext(), notificationProps, ReactAppLifecycleFacade.get());
int id = pushNotification.onPostRequest(); pushNotification.onPostRequest(notificationId);
result = id;
} finally {
promise.resolve(result);
}
} }
@ReactMethod @ReactMethod
public void removeLocalNotification(int notificationId) { public void cancelLocalNotification(int notificationId) {
IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext()); IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext());
notificationsDrawer.onNotificationClear(notificationId); notificationsDrawer.onNotificationClear(notificationId);
} }
......
...@@ -20,9 +20,11 @@ public interface IPushNotification { ...@@ -20,9 +20,11 @@ public interface IPushNotification {
/** /**
* Handle a request to post this notification. * Handle a request to post this notification.
* @return ID to optionally use for notification deletion. *
* @param notificationId (optional) The specific ID to associated with the notification.
* @return The ID effectively assigned to the notification (Auto-assigned if not specified as a parameter).
*/ */
int onPostRequest(); int onPostRequest(Integer notificationId);
PushNotificationProps asProps(); PushNotificationProps asProps();
} }
...@@ -7,13 +7,7 @@ import android.content.Context; ...@@ -7,13 +7,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.wix.reactnativenotifications.core.AppLaunchHelper; import com.wix.reactnativenotifications.core.AppLaunchHelper;
import com.wix.reactnativenotifications.core.AppLifecycleFacade; import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.AppLifecycleFacade.AppVisibilityListener; import com.wix.reactnativenotifications.core.AppLifecycleFacade.AppVisibilityListener;
...@@ -66,7 +60,7 @@ public class PushNotification implements IPushNotification { ...@@ -66,7 +60,7 @@ public class PushNotification implements IPushNotification {
@Override @Override
public void onReceived() throws InvalidNotificationException { public void onReceived() throws InvalidNotificationException {
postNotification(); postNotification(null);
notifyReceivedToJS(); notifyReceivedToJS();
} }
...@@ -76,8 +70,8 @@ public class PushNotification implements IPushNotification { ...@@ -76,8 +70,8 @@ public class PushNotification implements IPushNotification {
} }
@Override @Override
public int onPostRequest() { public int onPostRequest(Integer notificationId) {
return postNotification(); return postNotification(notificationId);
} }
@Override @Override
...@@ -85,10 +79,10 @@ public class PushNotification implements IPushNotification { ...@@ -85,10 +79,10 @@ public class PushNotification implements IPushNotification {
return mNotificationProps.copy(); return mNotificationProps.copy();
} }
protected int postNotification() { protected int postNotification(Integer notificationId) {
final PendingIntent pendingIntent = getCTAPendingIntent(); final PendingIntent pendingIntent = getCTAPendingIntent();
final Notification notification = buildNotification(pendingIntent); final Notification notification = buildNotification(pendingIntent);
return postNotification(notification); return postNotification(notification, notificationId);
} }
protected void digestNotification() { protected void digestNotification() {
...@@ -153,8 +147,8 @@ public class PushNotification implements IPushNotification { ...@@ -153,8 +147,8 @@ public class PushNotification implements IPushNotification {
.setAutoCancel(true); .setAutoCancel(true);
} }
protected int postNotification(Notification notification) { protected int postNotification(Notification notification, Integer notificationId) {
int id = createNotificationId(notification); int id = notificationId != null ? notificationId : createNotificationId(notification);
postNotification(id, notification); postNotification(id, notification);
return id; return id;
} }
......
...@@ -15,7 +15,9 @@ import org.junit.Test; ...@@ -15,7 +15,9 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.robolectric.RobolectricTestRunner;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
...@@ -23,9 +25,12 @@ import static org.mockito.Mockito.never; ...@@ -23,9 +25,12 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.Silent.class) @RunWith(RobolectricTestRunner.class)
public class PushNotificationTest { public class PushNotificationTest {
private static final String NOTIFICATION_OPENED_EVENT_NAME = "notificationOpened";
private static final String NOTIFICATION_RECEIVED_EVENT_NAME = "notificationReceived";
@Mock private ReactContext mReactContext; @Mock private ReactContext mReactContext;
@Mock private Context mContext; @Mock private Context mContext;
...@@ -37,6 +42,8 @@ public class PushNotificationTest { ...@@ -37,6 +42,8 @@ public class PushNotificationTest {
@Before @Before
public void setup() throws Exception { public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
when(mDefaultBundle.getString(eq("title"))).thenReturn("Notification-title"); when(mDefaultBundle.getString(eq("title"))).thenReturn("Notification-title");
when(mDefaultBundle.getString(eq("body"))).thenReturn("Notification-body"); when(mDefaultBundle.getString(eq("body"))).thenReturn("Notification-body");
when(mDefaultBundle.clone()).thenReturn(mDefaultBundle); when(mDefaultBundle.clone()).thenReturn(mDefaultBundle);
...@@ -88,7 +95,7 @@ public class PushNotificationTest { ...@@ -88,7 +95,7 @@ public class PushNotificationTest {
// Assert // Assert
verify(mReactContextAdapter).sendEventToJS(eq("notificationOpened"), eq(mDefaultBundle), eq(mContext)); verify(mReactContextAdapter).sendEventToJS(eq(NOTIFICATION_OPENED_EVENT_NAME), eq(mDefaultBundle), eq(mContext));
} }
@Test @Test
...@@ -100,7 +107,15 @@ public class PushNotificationTest { ...@@ -100,7 +107,15 @@ public class PushNotificationTest {
uut.onOpened(); uut.onOpened();
verify(mContext, never()).startActivity(any(Intent.class)); verify(mContext, never()).startActivity(any(Intent.class));
verify(mReactContextAdapter).sendEventToJS(eq("notificationOpened"), eq(mDefaultBundle), eq(mContext)); verify(mReactContextAdapter).sendEventToJS(eq(NOTIFICATION_OPENED_EVENT_NAME), eq(mDefaultBundle), eq(mContext));
}
@Test
public void onPostRequest_withValidDataButNoId_postNotifAndNotifyJS() throws Exception {
final PushNotification uut = createUUT();
uut.onPostRequest(null);
verify(mReactContextAdapter).sendEventToJS(eq(NOTIFICATION_RECEIVED_EVENT_NAME), eq(mDefaultBundle), eq(mContext));
} }
protected PushNotification createUUT() { protected PushNotification createUUT() {
......
...@@ -24,11 +24,6 @@ ...@@ -24,11 +24,6 @@
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".ChildActivity"
android:label="Child Activity">
<meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.wix.reactnativenotifications.app.MainActivity"/>
</activity>
</application> </application>
......
...@@ -5,7 +5,8 @@ import { ...@@ -5,7 +5,8 @@ import {
AppRegistry, AppRegistry,
StyleSheet, StyleSheet,
Text, Text,
View View,
TouchableHighlight
} from 'react-native'; } from 'react-native';
import {NotificationsAndroid, PendingNotifications} from 'react-native-notifications'; import {NotificationsAndroid, PendingNotifications} from 'react-native-notifications';
...@@ -42,7 +43,7 @@ const styles = StyleSheet.create({ ...@@ -42,7 +43,7 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
}, },
titleText: { titleText: {
fontSize: 22, fontSize: 24,
textAlign: 'center', textAlign: 'center',
margin: 10, margin: 10,
}, },
...@@ -51,6 +52,19 @@ const styles = StyleSheet.create({ ...@@ -51,6 +52,19 @@ const styles = StyleSheet.create({
textAlign: 'center', textAlign: 'center',
margin: 10, margin: 10,
}, },
mainButtonText: {
fontSize: 25,
fontStyle: 'italic',
fontWeight: 'bold',
textAlign: 'center',
margin: 10,
},
plainButtonText: {
fontSize: 18,
fontStyle: 'italic',
textAlign: 'center',
margin: 10,
},
}); });
class MainComponent extends Component { class MainComponent extends Component {
...@@ -58,6 +72,9 @@ class MainComponent extends Component { ...@@ -58,6 +72,9 @@ class MainComponent extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.onPostNotification = this.onPostNotification.bind(this);
this.onCancelNotification = this.onCancelNotification.bind(this);
this.state = { this.state = {
elapsed: 0, elapsed: 0,
lastNotification: undefined lastNotification: undefined
...@@ -84,6 +101,17 @@ class MainComponent extends Component { ...@@ -84,6 +101,17 @@ class MainComponent extends Component {
this.setState({elapsed: this.state.elapsed + 1}); this.setState({elapsed: this.state.elapsed + 1});
} }
onPostNotification() {
this.lastNotificationId = NotificationsAndroid.localNotification({title: "Local notification", body: "This notification was generated by the app!"});
}
onCancelNotification() {
if (this.lastNotificationId) {
NotificationsAndroid.cancelLocalNotification(this.lastNotificationId);
this.lastNotificationId = undefined;
}
}
render() { render() {
return ( return (
<View style={styles.container}> <View style={styles.container}>
...@@ -91,6 +119,13 @@ class MainComponent extends Component { ...@@ -91,6 +119,13 @@ class MainComponent extends Component {
<Text style={styles.bodyText}>{this.state.initialNotification ? 'Opened from notification' : ''}</Text> <Text style={styles.bodyText}>{this.state.initialNotification ? 'Opened from notification' : ''}</Text>
<Text style={styles.bodyText}>Last notification: {this.state.lastNotification ? '\n'+this.state.lastNotification.body + ` (opened at ''${this.state.notificationRxTime})` : "N/A"}</Text> <Text style={styles.bodyText}>Last notification: {this.state.lastNotification ? '\n'+this.state.lastNotification.body + ` (opened at ''${this.state.notificationRxTime})` : "N/A"}</Text>
<Text style={styles.bodyText}>Time elapsed: {this.state.elapsed}</Text> <Text style={styles.bodyText}>Time elapsed: {this.state.elapsed}</Text>
<Text>{"\n\n"}</Text>
<TouchableHighlight onPress={() => this.onPostNotification()}>
<Text style={styles.mainButtonText}>Try Me!</Text>
</TouchableHighlight>
<TouchableHighlight onPress={() => this.onCancelNotification()}>
<Text style={styles.plainButtonText}>Undo last</Text>
</TouchableHighlight>
</View> </View>
) )
} }
......
...@@ -44,6 +44,16 @@ export class NotificationsAndroid { ...@@ -44,6 +44,16 @@ export class NotificationsAndroid {
static refreshToken() { static refreshToken() {
RNNotifications.refreshToken(); RNNotifications.refreshToken();
} }
static localNotification(notification: Object) {
const id = Date.now() | 0; // Bitwise-OR forces value onto a 32bit limit
RNNotifications.postLocalNotification(notification, id);
return id;
}
static cancelLocalNotification(id) {
RNNotifications.cancelLocalNotification(id);
}
} }
export class PendingNotifications { export class PendingNotifications {
......
...@@ -3,16 +3,20 @@ let expect = require("chai").use(require("sinon-chai")).expect; ...@@ -3,16 +3,20 @@ let expect = require("chai").use(require("sinon-chai")).expect;
import proxyquire from "proxyquire"; import proxyquire from "proxyquire";
import sinon from "sinon"; import sinon from "sinon";
describe("Notifications-Android", () => { describe("Notifications-Android > ", () => {
proxyquire.noCallThru(); proxyquire.noCallThru();
let refreshTokenStub; let refreshTokenStub;
let getInitialNotificationStub; let getInitialNotificationStub;
let postLocalNotificationStub;
let cancelLocalNotificationStub;
let deviceEventEmitterListenerStub; let deviceEventEmitterListenerStub;
let libUnderTest; let libUnderTest;
beforeEach(() => { beforeEach(() => {
refreshTokenStub = sinon.stub(); refreshTokenStub = sinon.stub();
getInitialNotificationStub = sinon.stub(); getInitialNotificationStub = sinon.stub();
postLocalNotificationStub = sinon.stub();
cancelLocalNotificationStub = sinon.stub();
deviceEventEmitterListenerStub = sinon.stub(); deviceEventEmitterListenerStub = sinon.stub();
libUnderTest = proxyquire("../index.android", { libUnderTest = proxyquire("../index.android", {
...@@ -20,7 +24,9 @@ describe("Notifications-Android", () => { ...@@ -20,7 +24,9 @@ describe("Notifications-Android", () => {
NativeModules: { NativeModules: {
WixRNNotifications: { WixRNNotifications: {
refreshToken: refreshTokenStub, refreshToken: refreshTokenStub,
getInitialNotification: getInitialNotificationStub getInitialNotification: getInitialNotificationStub,
postLocalNotification: postLocalNotificationStub,
cancelLocalNotification: cancelLocalNotificationStub
} }
}, },
DeviceEventEmitter: { DeviceEventEmitter: {
...@@ -153,11 +159,13 @@ describe("Notifications-Android", () => { ...@@ -153,11 +159,13 @@ describe("Notifications-Android", () => {
}); });
}); });
describe("Notification token", () => {
it("should refresh notification token upon refreshing request by the user", () => { it("should refresh notification token upon refreshing request by the user", () => {
expect(refreshTokenStub).to.not.have.been.called; expect(refreshTokenStub).to.not.have.been.called;
libUnderTest.NotificationsAndroid.refreshToken(); libUnderTest.NotificationsAndroid.refreshToken();
expect(refreshTokenStub).to.have.been.calledOnce; expect(refreshTokenStub).to.have.been.calledOnce;
}); });
});
describe("Initial notification API", () => { describe("Initial notification API", () => {
it("should return initial notification data if available", (done) => { it("should return initial notification data if available", (done) => {
...@@ -187,4 +195,37 @@ describe("Notifications-Android", () => { ...@@ -187,4 +195,37 @@ describe("Notifications-Android", () => {
}); });
describe("Local notification", () => {
const notification = {
title: "notification-title",
body: "notification-body"
};
it("should get published when posted manually", () => {
expect(postLocalNotificationStub).to.not.have.been.called;
const id = libUnderTest.NotificationsAndroid.localNotification(notification);
expect(id).to.not.be.undefined;
expect(postLocalNotificationStub).to.have.been.calledWith(notification, id);
});
it("should be called with a unique ID", () => {
expect(postLocalNotificationStub).to.not.have.been.called;
const id = libUnderTest.NotificationsAndroid.localNotification(notification);
const id2 = libUnderTest.NotificationsAndroid.localNotification(notification);
expect(id).to.not.be.undefined;
expect(id2).to.not.be.undefined;
expect(id).to.not.equal(id2);
});
it("should be cancellable with an ID", () => {
expect(cancelLocalNotificationStub).to.not.have.been.called;
libUnderTest.NotificationsAndroid.cancelLocalNotification(666);
expect(cancelLocalNotificationStub).to.have.been.calledWith(666);
});
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment