Merge remote-tracking branch 'upstream/master'
# Conflicts: # ios/RNFirebase/RNFirebase.h # ios/RNFirebase/RNFirebaseAnalytics.h # ios/RNFirebase/RNFirebaseAuth.h # ios/RNFirebase/RNFirebaseCrash.h # ios/RNFirebase/RNFirebaseDatabase.h # ios/RNFirebase/RNFirebaseErrors.h # ios/RNFirebase/RNFirebaseMessaging.h # ios/RNFirebase/RNFirebaseStorage.h
This commit is contained in:
commit
db792b8857
|
@ -4,6 +4,7 @@
|
||||||
[![Gitter](https://badges.gitter.im/invertase/react-native-firebase.svg)](https://gitter.im/invertase/react-native-firebase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[![Gitter](https://badges.gitter.im/invertase/react-native-firebase.svg)](https://gitter.im/invertase/react-native-firebase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
[![npm version](https://img.shields.io/npm/v/react-native-firebase.svg)](https://www.npmjs.com/package/react-native-firebase)
|
[![npm version](https://img.shields.io/npm/v/react-native-firebase.svg)](https://www.npmjs.com/package/react-native-firebase)
|
||||||
[![License](https://img.shields.io/npm/l/react-native-firebase.svg)](/LICENSE)
|
[![License](https://img.shields.io/npm/l/react-native-firebase.svg)](/LICENSE)
|
||||||
|
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg)](https://www.patreon.com/invertase)
|
||||||
|
|
||||||
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple.
|
**RNFirebase** makes using [Firebase](http://firebase.com) with React Native simple.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package io.invertase.firebase;
|
package io.invertase.firebase;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
@ -34,7 +36,10 @@ public class RNFirebaseModule extends ReactContextBaseJavaModule implements Life
|
||||||
int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
|
int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
|
||||||
|
|
||||||
if (status != ConnectionResult.SUCCESS && gapi.isUserResolvableError(status)) {
|
if (status != ConnectionResult.SUCCESS && gapi.isUserResolvableError(status)) {
|
||||||
gapi.getErrorDialog(getCurrentActivity(), status, 2404).show();
|
Activity activity = getCurrentActivity();
|
||||||
|
if (activity != null) {
|
||||||
|
gapi.getErrorDialog(activity, status, 2404).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -216,12 +216,12 @@ public class Utils {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static boolean isArray(DataSnapshot snapshot) {
|
private static boolean isArray(DataSnapshot snapshot) {
|
||||||
long expectedKey = 0;
|
long expectedKey = -1;
|
||||||
for (DataSnapshot child : snapshot.getChildren()) {
|
for (DataSnapshot child : snapshot.getChildren()) {
|
||||||
try {
|
try {
|
||||||
long key = Long.parseLong(child.getKey());
|
long key = Long.parseLong(child.getKey());
|
||||||
if (key == expectedKey) {
|
if (key > expectedKey) {
|
||||||
expectedKey++;
|
expectedKey = key;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -238,11 +238,11 @@ public class Utils {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static boolean isArray(MutableData mutableData) {
|
private static boolean isArray(MutableData mutableData) {
|
||||||
long expectedKey = 0;
|
long expectedKey = -1;
|
||||||
for (MutableData child : mutableData.getChildren()) {
|
for (MutableData child : mutableData.getChildren()) {
|
||||||
try {
|
try {
|
||||||
long key = Long.parseLong(child.getKey());
|
long key = Long.parseLong(child.getKey());
|
||||||
if (key == expectedKey) {
|
if (key > expectedKey) {
|
||||||
expectedKey++;
|
expectedKey++;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -261,8 +261,16 @@ public class Utils {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static <Any> WritableArray buildArray(DataSnapshot snapshot) {
|
private static <Any> WritableArray buildArray(DataSnapshot snapshot) {
|
||||||
|
long expectedKey = 0;
|
||||||
WritableArray array = Arguments.createArray();
|
WritableArray array = Arguments.createArray();
|
||||||
for (DataSnapshot child : snapshot.getChildren()) {
|
for (DataSnapshot child : snapshot.getChildren()) {
|
||||||
|
long key = Long.parseLong(child.getKey());
|
||||||
|
if (key > expectedKey) {
|
||||||
|
for (long i = expectedKey; i < key; i++) {
|
||||||
|
array.pushNull();
|
||||||
|
}
|
||||||
|
expectedKey = key;
|
||||||
|
}
|
||||||
Any castedChild = castValue(child);
|
Any castedChild = castValue(child);
|
||||||
switch (castedChild.getClass().getName()) {
|
switch (castedChild.getClass().getName()) {
|
||||||
case "java.lang.Boolean":
|
case "java.lang.Boolean":
|
||||||
|
@ -288,6 +296,7 @@ public class Utils {
|
||||||
Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
|
Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
expectedKey++;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
@ -299,8 +308,16 @@ public class Utils {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static <Any> WritableArray buildArray(MutableData mutableData) {
|
private static <Any> WritableArray buildArray(MutableData mutableData) {
|
||||||
|
long expectedKey = 0;
|
||||||
WritableArray array = Arguments.createArray();
|
WritableArray array = Arguments.createArray();
|
||||||
for (MutableData child : mutableData.getChildren()) {
|
for (MutableData child : mutableData.getChildren()) {
|
||||||
|
long key = Long.parseLong(child.getKey());
|
||||||
|
if (key > expectedKey) {
|
||||||
|
for (long i = expectedKey; i < key; i++) {
|
||||||
|
array.pushNull();
|
||||||
|
}
|
||||||
|
expectedKey = key;
|
||||||
|
}
|
||||||
Any castedChild = castValue(child);
|
Any castedChild = castValue(child);
|
||||||
switch (castedChild.getClass().getName()) {
|
switch (castedChild.getClass().getName()) {
|
||||||
case "java.lang.Boolean":
|
case "java.lang.Boolean":
|
||||||
|
@ -326,6 +343,7 @@ public class Utils {
|
||||||
Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
|
Log.w(TAG, "Invalid type: " + castedChild.getClass().getName());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
expectedKey++;
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.util.Log;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ import com.facebook.react.bridge.Promise;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
|
import com.facebook.react.bridge.WritableArray;
|
||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.bridge.ReactContext;
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
@ -25,6 +27,7 @@ import com.google.firebase.auth.AuthCredential;
|
||||||
import com.google.firebase.auth.AuthResult;
|
import com.google.firebase.auth.AuthResult;
|
||||||
import com.google.firebase.auth.GithubAuthProvider;
|
import com.google.firebase.auth.GithubAuthProvider;
|
||||||
import com.google.firebase.auth.TwitterAuthProvider;
|
import com.google.firebase.auth.TwitterAuthProvider;
|
||||||
|
import com.google.firebase.auth.UserInfo;
|
||||||
import com.google.firebase.auth.UserProfileChangeRequest;
|
import com.google.firebase.auth.UserProfileChangeRequest;
|
||||||
import com.google.firebase.auth.FacebookAuthProvider;
|
import com.google.firebase.auth.FacebookAuthProvider;
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
@ -739,6 +742,35 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a List of UserInfo instances into the correct format to match the web sdk
|
||||||
|
* @param providerData List<UserInfo> user.getProviderData()
|
||||||
|
* @return WritableArray array
|
||||||
|
*/
|
||||||
|
private WritableArray convertProviderData(List<? extends UserInfo> providerData) {
|
||||||
|
WritableArray output = Arguments.createArray();
|
||||||
|
for (UserInfo userInfo : providerData) {
|
||||||
|
WritableMap userInfoMap = Arguments.createMap();
|
||||||
|
userInfoMap.putString("providerId", userInfo.getProviderId());
|
||||||
|
userInfoMap.putString("uid", userInfo.getUid());
|
||||||
|
userInfoMap.putString("displayName", userInfo.getDisplayName());
|
||||||
|
|
||||||
|
final Uri photoUrl = userInfo.getPhotoUrl();
|
||||||
|
|
||||||
|
if (photoUrl != null) {
|
||||||
|
userInfoMap.putString("photoURL", photoUrl.toString());
|
||||||
|
} else {
|
||||||
|
userInfoMap.putNull("photoURL");
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfoMap.putString("email", userInfo.getEmail());
|
||||||
|
|
||||||
|
output.pushMap(userInfoMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* firebaseUserToMap
|
* firebaseUserToMap
|
||||||
*
|
*
|
||||||
|
@ -778,7 +810,7 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule {
|
||||||
userMap.putNull("photoURL");
|
userMap.putNull("photoURL");
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo providerData
|
userMap.putArray("providerData", convertProviderData(user.getProviderData()));
|
||||||
|
|
||||||
return userMap;
|
return userMap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import com.google.firebase.database.OnDisconnect;
|
||||||
import com.google.firebase.database.DatabaseError;
|
import com.google.firebase.database.DatabaseError;
|
||||||
import com.google.firebase.database.DatabaseReference;
|
import com.google.firebase.database.DatabaseReference;
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
import com.google.firebase.database.FirebaseDatabase;
|
||||||
|
import com.google.firebase.database.DatabaseException;
|
||||||
import com.google.firebase.database.Transaction;
|
import com.google.firebase.database.Transaction;
|
||||||
|
|
||||||
import io.invertase.firebase.Utils;
|
import io.invertase.firebase.Utils;
|
||||||
|
@ -55,8 +56,8 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
|
||||||
final Callback callback) {
|
final Callback callback) {
|
||||||
try {
|
try {
|
||||||
mFirebaseDatabase.setPersistenceEnabled(enable);
|
mFirebaseDatabase.setPersistenceEnabled(enable);
|
||||||
} catch (Throwable t) {
|
} catch (DatabaseException t) {
|
||||||
Log.e(TAG, "FirebaseDatabase setPersistenceEnabled exception", t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WritableMap res = Arguments.createMap();
|
WritableMap res = Arguments.createMap();
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[![Gitter](https://badges.gitter.im/invertase/react-native-firebase.svg)](https://gitter.im/invertase/react-native-firebase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[![Gitter](https://badges.gitter.im/invertase/react-native-firebase.svg)](https://gitter.im/invertase/react-native-firebase?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
[![npm version](https://img.shields.io/npm/v/react-native-firebase.svg)](https://www.npmjs.com/package/react-native-firebase)
|
[![npm version](https://img.shields.io/npm/v/react-native-firebase.svg)](https://www.npmjs.com/package/react-native-firebase)
|
||||||
[![License](https://img.shields.io/npm/l/react-native-firebase.svg)](/LICENSE)
|
[![License](https://img.shields.io/npm/l/react-native-firebase.svg)](/LICENSE)
|
||||||
|
[![Donate](https://img.shields.io/badge/Donate-Patreon-green.svg)](https://www.patreon.com/invertase)
|
||||||
<br />
|
<br />
|
||||||
A well tested Firebase implementation for React Native, supporting both iOS & Android apps.
|
A well tested Firebase implementation for React Native, supporting both iOS & Android apps.
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -73,9 +73,9 @@ Add permissions:
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
```
|
```
|
||||||
|
|
||||||
Set app [launch mode](https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en) inside application props:
|
Set app [launch mode](https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en) inside activity props:
|
||||||
```xml
|
```xml
|
||||||
<application
|
<activity
|
||||||
...
|
...
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
>
|
>
|
||||||
|
|
|
@ -112,7 +112,7 @@ Follow the instructions at https://firebase.google.com/docs/cloud-messaging/ios/
|
||||||
In Xcode, enable the following capabilities:
|
In Xcode, enable the following capabilities:
|
||||||
|
|
||||||
1) Push Notifications
|
1) Push Notifications
|
||||||
2) Background modes > Remove notifications
|
2) Background modes > Remote notifications
|
||||||
|
|
||||||
### 3.3) Update `AppDelegate.h`
|
### 3.3) Update `AppDelegate.h`
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,667 @@
|
||||||
|
// Type definitions for React Native Firebase v1.0.0-alpha7
|
||||||
|
// Project: https://github.com/invertase/react-native-firebase
|
||||||
|
// Definitions by: Tal <https://github.com/taljacobson>
|
||||||
|
// TypeScript Version: 2.1
|
||||||
|
|
||||||
|
declare module "react-native-firebase" {
|
||||||
|
|
||||||
|
export default class FireBase {
|
||||||
|
constructor(config?: RNFirebase.configurationOptions)
|
||||||
|
log: any
|
||||||
|
analytics(): RNFirebase.Analytics;
|
||||||
|
auth(): RNFirebase.auth.Auth;
|
||||||
|
on(type: string, handler: (msg: any) => void): any;
|
||||||
|
/** mimics firebase Web SDK */
|
||||||
|
database(): RNFirebase.database.Database;
|
||||||
|
/**RNFirebase mimics the Web Firebase SDK Storage,
|
||||||
|
* whilst providing some iOS and Android specific functionality.
|
||||||
|
*/
|
||||||
|
storage(): RNFirebase.storage.Storage;
|
||||||
|
/**
|
||||||
|
* Firebase Cloud Messaging (FCM) allows you to send push messages at no cost to both Android & iOS platforms.
|
||||||
|
* Assuming the installation instructions have been followed, FCM is ready to go.
|
||||||
|
* As the Firebase Web SDK has limited messaging functionality,
|
||||||
|
* the following methods within react-native-firebase have been created to handle FCM in the React Native environment.
|
||||||
|
*/
|
||||||
|
messaging(): RNFirebase.messaging.Messaging;
|
||||||
|
/**
|
||||||
|
* RNFirebase provides crash reporting for your app out of the box.
|
||||||
|
* Please note crashes do not appear in real-time on the console,
|
||||||
|
* they tend to take a number of hours to appear
|
||||||
|
* If you want to manually report a crash,
|
||||||
|
* such as a pre-caught exception this is possible by using the report method.
|
||||||
|
*/
|
||||||
|
crash(): RNFirebase.crash.Crash;
|
||||||
|
apps: Array<string>;
|
||||||
|
googleApiAvailability: RNFirebase.GoogleApiAvailabilityType;
|
||||||
|
static initializeApp(options?: any | RNFirebase.configurationOptions, name?: string): FireBase;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace RNFirebase {
|
||||||
|
interface RnError extends Error {
|
||||||
|
code?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoogleApiAvailabilityType = {
|
||||||
|
status: number,
|
||||||
|
isAvailable: boolean,
|
||||||
|
isUserResolvableError?: boolean,
|
||||||
|
error?: string
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pass custom options by passing an object with configuration options.
|
||||||
|
* The configuration object will be generated first by the native configuration object, if set and then will be overridden if passed in JS.
|
||||||
|
* That is, all of the following key/value pairs are optional if the native configuration is set.
|
||||||
|
*/
|
||||||
|
interface configurationOptions {
|
||||||
|
/**
|
||||||
|
* default false
|
||||||
|
* When set to true, RNFirebase will log messages to the console and fire debug events we can listen to in js
|
||||||
|
* @usage
|
||||||
|
* firebase.on('debug', msg => console.log('Received debug message', msg))
|
||||||
|
*/
|
||||||
|
debug?: boolean;
|
||||||
|
/**
|
||||||
|
* default false
|
||||||
|
* When set to true, database persistence will be enabled.
|
||||||
|
*/
|
||||||
|
persistence?: boolean;
|
||||||
|
/**
|
||||||
|
* Default from app [NSBundle mainBundle] The bundle ID for the app to be bundled with
|
||||||
|
*/
|
||||||
|
bundleID?: string;
|
||||||
|
/**
|
||||||
|
* defualt ""
|
||||||
|
* The Google App ID that is used to uniquely identify an instance of an app.
|
||||||
|
*/
|
||||||
|
googleAppID?: string;
|
||||||
|
/**
|
||||||
|
* deufalt ""
|
||||||
|
* The database root (i.e. https://my-app.firebaseio.com)
|
||||||
|
*/
|
||||||
|
databaseURL?: string;
|
||||||
|
/**
|
||||||
|
* defualt ""
|
||||||
|
* URL scheme to set up durable deep link service
|
||||||
|
*/
|
||||||
|
deepLinkURLScheme?: string;
|
||||||
|
/**
|
||||||
|
* defualt ""
|
||||||
|
* The Google Cloud storage bucket name
|
||||||
|
*/
|
||||||
|
storageBucket?: string;
|
||||||
|
/**
|
||||||
|
* default ""
|
||||||
|
* The Android client ID used in Google AppInvite when an iOS app has it's android version
|
||||||
|
*/
|
||||||
|
androidClientID?: string;
|
||||||
|
/**
|
||||||
|
* default ""
|
||||||
|
* The Project number from the Google Developer's console used to configure Google Cloud Messaging
|
||||||
|
*/
|
||||||
|
GCMSenderID?: string;
|
||||||
|
/**
|
||||||
|
* default ""
|
||||||
|
* The tracking ID for Google Analytics
|
||||||
|
*/
|
||||||
|
trackingID?: string;
|
||||||
|
/**
|
||||||
|
* default ""
|
||||||
|
* The OAuth2 client ID for iOS application used to authenticate Google Users for signing in with Google
|
||||||
|
*/
|
||||||
|
clientID?: string;
|
||||||
|
/**
|
||||||
|
* defualt ""
|
||||||
|
* The secret iOS API key used for authenticating requests from our app
|
||||||
|
*/
|
||||||
|
APIKey?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace storage {
|
||||||
|
|
||||||
|
interface StorageTask<T> extends Promise<T> {
|
||||||
|
on(
|
||||||
|
event: TaskEvent,
|
||||||
|
nextOrObserver: (snapshot: any) => any,
|
||||||
|
error: (error: RnError) => any,
|
||||||
|
complete: (complete: any) => any
|
||||||
|
): any
|
||||||
|
/**
|
||||||
|
* is not currently supported by react-native-firebase
|
||||||
|
*/
|
||||||
|
pause(): void
|
||||||
|
/**
|
||||||
|
* is not currently supported by react-native-firebase
|
||||||
|
*/
|
||||||
|
resume(): void
|
||||||
|
/**
|
||||||
|
* is not currently supported by react-native-firebase
|
||||||
|
*/
|
||||||
|
cancel(): void
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RNStorage extends Reference {
|
||||||
|
/**
|
||||||
|
* Downloads a reference to the device
|
||||||
|
* @param {String} filePath Where to store the file
|
||||||
|
* @return {Promise}
|
||||||
|
* */
|
||||||
|
downloadFile(filePath: string): StorageTask<any>;
|
||||||
|
/**
|
||||||
|
* Upload a file path
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
putFile(filePath: string, metadata?: any): StorageTask<any>;
|
||||||
|
setMaxDownloadRetryTime(time: number): void
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Storage {
|
||||||
|
maxOperationRetryTime: number;
|
||||||
|
maxUploadRetryTime: number;
|
||||||
|
ref(path?: string): storage.RNStorage;
|
||||||
|
refFromURL(url: string): storage.RNStorage;
|
||||||
|
setMaxOperationRetryTime(time: number): any;
|
||||||
|
setMaxUploadRetryTime(time: number): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Reference {
|
||||||
|
bucket: string;
|
||||||
|
child(path: string): storage.Reference;
|
||||||
|
delete(): Promise<any>;
|
||||||
|
fullPath: string;
|
||||||
|
getDownloadURL(): Promise<any>;
|
||||||
|
getMetadata(): Promise<any>;
|
||||||
|
name: string;
|
||||||
|
parent: storage.Reference | null;
|
||||||
|
put(data: any | Uint8Array | ArrayBuffer,
|
||||||
|
metadata?: storage.UploadMetadata):
|
||||||
|
storage.UploadTask;
|
||||||
|
putString(
|
||||||
|
data: string, format?: storage.StringFormat,
|
||||||
|
metadata?: storage.UploadMetadata):
|
||||||
|
storage.UploadTask;
|
||||||
|
root: storage.Reference;
|
||||||
|
storage: storage.Storage;
|
||||||
|
toString(): string;
|
||||||
|
updateMetadata(metadata: storage.SettableMetadata):
|
||||||
|
Promise<any>;
|
||||||
|
}
|
||||||
|
interface UploadMetadata extends storage.SettableMetadata {
|
||||||
|
md5Hash?: string | null;
|
||||||
|
}
|
||||||
|
interface SettableMetadata {
|
||||||
|
cacheControl?: string | null;
|
||||||
|
contentDisposition?: string | null;
|
||||||
|
contentEncoding?: string | null;
|
||||||
|
contentLanguage?: string | null;
|
||||||
|
contentType?: string | null;
|
||||||
|
customMetadata?: { [/* warning: coerced from ? */ key: string]: string } | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
type StringFormat = string;
|
||||||
|
var StringFormat: {
|
||||||
|
BASE64: StringFormat,
|
||||||
|
BASE64URL: StringFormat,
|
||||||
|
DATA_URL: StringFormat,
|
||||||
|
RAW: StringFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UploadTask {
|
||||||
|
cancel(): boolean;
|
||||||
|
catch(onRejected: (a: RnError) => any): Promise<any>;
|
||||||
|
on(event: storage.TaskEvent, nextOrObserver?: null | Object,
|
||||||
|
error?: ((a: RnError) => any) | null, complete?: (() => any) | null): Function;
|
||||||
|
pause(): boolean;
|
||||||
|
resume(): boolean;
|
||||||
|
snapshot: storage.UploadTaskSnapshot;
|
||||||
|
then(
|
||||||
|
onFulfilled?: ((a: storage.UploadTaskSnapshot) => any) | null,
|
||||||
|
onRejected?: ((a: RnError) => any) | null): Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UploadTaskSnapshot {
|
||||||
|
bytesTransferred: number;
|
||||||
|
downloadURL: string | null;
|
||||||
|
metadata: storage.FullMetadata;
|
||||||
|
ref: storage.Reference;
|
||||||
|
state: storage.TaskState;
|
||||||
|
task: storage.UploadTask;
|
||||||
|
totalBytes: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FullMetadata extends storage.UploadMetadata {
|
||||||
|
bucket: string;
|
||||||
|
downloadURLs: string[];
|
||||||
|
fullPath: string;
|
||||||
|
generation: string;
|
||||||
|
metageneration: string;
|
||||||
|
name: string;
|
||||||
|
size: number;
|
||||||
|
timeCreated: string;
|
||||||
|
updated: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskEvent = string;
|
||||||
|
var TaskEvent: {
|
||||||
|
STATE_CHANGED: TaskEvent,
|
||||||
|
};
|
||||||
|
|
||||||
|
type TaskState = string;
|
||||||
|
var TaskState: {
|
||||||
|
CANCELED: TaskState,
|
||||||
|
ERROR: TaskState,
|
||||||
|
PAUSED: TaskState,
|
||||||
|
RUNNING: TaskState,
|
||||||
|
SUCCESS: TaskState,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace database {
|
||||||
|
|
||||||
|
|
||||||
|
interface Database {
|
||||||
|
/**
|
||||||
|
* Returns a new firebase reference instance
|
||||||
|
* */
|
||||||
|
ref(path: string): RnReference
|
||||||
|
/**
|
||||||
|
* register listener
|
||||||
|
*/
|
||||||
|
on(path: string, modifiersString: string, modifiers: Array<string>, eventName: string, cb: () => void, errorCb: () => void): any
|
||||||
|
/**
|
||||||
|
* unregister listener
|
||||||
|
*/
|
||||||
|
off(path: string, modifiersString: string, eventName?: string, origCB?: () => void): any
|
||||||
|
/**
|
||||||
|
* Removes all event handlers and their native subscriptions
|
||||||
|
*/
|
||||||
|
cleanup(): Promise<any>
|
||||||
|
/**
|
||||||
|
* connect to firebase backend
|
||||||
|
*/
|
||||||
|
goOnline(): void
|
||||||
|
/**
|
||||||
|
* disconnect to firebase backend
|
||||||
|
*/
|
||||||
|
goOffline(): void
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RnReference extends Reference {
|
||||||
|
keepSynced(bool: boolean): any
|
||||||
|
filter(name: string, value: any, key?: string): any;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Query {
|
||||||
|
endAt(value: number | string | boolean | null, key?: string): database.Query;
|
||||||
|
equalTo(value: number | string | boolean | null, key?: string): database.Query;
|
||||||
|
isEqual(other: database.Query | null): boolean;
|
||||||
|
limitToFirst(limit: number): database.Query;
|
||||||
|
limitToLast(limit: number): database.Query;
|
||||||
|
off(eventType?: string,
|
||||||
|
callback?: (a: database.DataSnapshot, b?: string | null) => any,
|
||||||
|
context?: Object | null): any;
|
||||||
|
on(eventType: string,
|
||||||
|
callback: (a: database.DataSnapshot | null, b?: string) => any,
|
||||||
|
cancelCallbackOrContext?: Object | null, context?: Object | null):
|
||||||
|
(a: database.DataSnapshot | null, b?: string) => any;
|
||||||
|
once(
|
||||||
|
eventType: string,
|
||||||
|
successCallback?:
|
||||||
|
(a: database.DataSnapshot, b?: string) => any,
|
||||||
|
failureCallbackOrContext?: Object | null,
|
||||||
|
context?: Object | null): Promise<any>;
|
||||||
|
orderByChild(path: string): database.Query;
|
||||||
|
orderByKey(): database.Query;
|
||||||
|
orderByPriority(): database.Query;
|
||||||
|
orderByValue(): database.Query;
|
||||||
|
ref: database.Reference;
|
||||||
|
startAt(value: number | string | boolean | null, key?: string): database.Query;
|
||||||
|
toJSON(): Object;
|
||||||
|
toString(): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DataSnapshot {
|
||||||
|
child(path: string): database.DataSnapshot;
|
||||||
|
exists(): boolean;
|
||||||
|
exportVal(): any;
|
||||||
|
forEach(action: (a: database.DataSnapshot) => boolean): boolean;
|
||||||
|
getPriority(): string | number | null;
|
||||||
|
hasChild(path: string): boolean;
|
||||||
|
hasChildren(): boolean;
|
||||||
|
key: string | null;
|
||||||
|
numChildren(): number;
|
||||||
|
ref: database.Reference;
|
||||||
|
toJSON(): Object | null;
|
||||||
|
val(): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Reference extends database.Query {
|
||||||
|
child(path: string): database.Reference;
|
||||||
|
key: string | null;
|
||||||
|
onDisconnect(): any;
|
||||||
|
parent: database.Reference | null;
|
||||||
|
push(value?: any, onComplete?: (a: RnError | null) => any): any
|
||||||
|
remove(onComplete?: (a: RnError | null) => any): Promise<any>;
|
||||||
|
root: database.Reference;
|
||||||
|
set(value: any, onComplete?: (a: RnError | null) => any): Promise<any>;
|
||||||
|
setPriority(
|
||||||
|
priority: string | number | null,
|
||||||
|
onComplete: (a: RnError | null) => any): Promise<any>;
|
||||||
|
setWithPriority(
|
||||||
|
newVal: any, newPriority: string | number | null,
|
||||||
|
onComplete?: (a: RnError | null) => any): Promise<any>;
|
||||||
|
transaction(
|
||||||
|
transactionUpdate: (a: any) => any,
|
||||||
|
onComplete?:
|
||||||
|
(a: RnError | null, b: boolean,
|
||||||
|
c: database.DataSnapshot | null) => any,
|
||||||
|
applyLocally?: boolean): Promise<any>;
|
||||||
|
update(values: Object, onComplete?: (a: RnError | null) => any): Promise<any>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* firebase Analytics
|
||||||
|
*/
|
||||||
|
interface Analytics {
|
||||||
|
/**Log a custom event with optional params. */
|
||||||
|
logEvent(event: string, params?: Object): void
|
||||||
|
/** Sets whether analytics collection is enabled for this app on this device. */
|
||||||
|
setAnalyticsCollectionEnabled(enabled: boolean): void
|
||||||
|
/**
|
||||||
|
* Sets the current screen name, which specifies the current visual context in your app.
|
||||||
|
* Whilst screenClassOverride is optional,
|
||||||
|
* it is recommended it is always sent as your current class name,
|
||||||
|
* for example on Android it will always show as 'MainActivity' if not specified.
|
||||||
|
*/
|
||||||
|
setCurrentScreen(screenName: string, screenClassOverride?: string): void
|
||||||
|
/**
|
||||||
|
* Sets the minimum engagement time required before starting a session.
|
||||||
|
* The default value is 10000 (10 seconds)
|
||||||
|
*/
|
||||||
|
setMinimumSessionDuration(miliseconds: number): void
|
||||||
|
/**
|
||||||
|
* Sets the duration of inactivity that terminates the current session.
|
||||||
|
* The default value is 1800000 (30 minutes).
|
||||||
|
*/
|
||||||
|
setSessionTimeoutDuration(miliseconds: number): void
|
||||||
|
/**
|
||||||
|
* Gives a user a uniqiue identificaition.
|
||||||
|
* @example
|
||||||
|
* const id = firebase.auth().currentUser.uid;
|
||||||
|
*
|
||||||
|
* firebase.analytics().setUserId(id);
|
||||||
|
*/
|
||||||
|
setUserId(id: string): void
|
||||||
|
/**
|
||||||
|
* Sets a key/value pair of data on the current user.
|
||||||
|
*/
|
||||||
|
setUserProperty(name: string, value: string): void;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
/**
|
||||||
|
* The user's display name (if available).
|
||||||
|
*/
|
||||||
|
displayName: string | null
|
||||||
|
/**
|
||||||
|
* - The user's email address (if available).
|
||||||
|
*/
|
||||||
|
email: string | null
|
||||||
|
/**
|
||||||
|
* - True if the user's email address has been verified.
|
||||||
|
*/
|
||||||
|
emailVerified: boolean
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
isAnonymous: boolean
|
||||||
|
/**
|
||||||
|
* - The URL of the user's profile picture (if available).
|
||||||
|
*/
|
||||||
|
photoURL: string | null
|
||||||
|
/**
|
||||||
|
* - Additional provider-specific information about the user.
|
||||||
|
*/
|
||||||
|
providerData: any | null
|
||||||
|
/**
|
||||||
|
* - The authentication provider ID for the current user.
|
||||||
|
* For example, 'facebook.com', or 'google.com'.
|
||||||
|
*/
|
||||||
|
providerId: string | null
|
||||||
|
/**
|
||||||
|
* - The user's unique ID.
|
||||||
|
*/
|
||||||
|
uid: string
|
||||||
|
/**
|
||||||
|
* Delete the current user.
|
||||||
|
*/
|
||||||
|
delete(): Promise<void>
|
||||||
|
/**
|
||||||
|
* Returns the users authentication token.
|
||||||
|
*/
|
||||||
|
getToken(): Promise<string>
|
||||||
|
/**
|
||||||
|
* Reauthenticate the current user with credentials:
|
||||||
|
*/
|
||||||
|
reauthenticate(credential: Credential): Promise<void>
|
||||||
|
/**
|
||||||
|
* Refreshes the current user.
|
||||||
|
*/
|
||||||
|
reload(): Promise<void>
|
||||||
|
/**
|
||||||
|
* Sends a verification email to a user.
|
||||||
|
* This will Promise reject is the user is anonymous.
|
||||||
|
*/
|
||||||
|
sendEmailVerification(): Promise<void>
|
||||||
|
/**
|
||||||
|
* Updates the user's email address.
|
||||||
|
* See Firebase docs for more information on security & email validation.
|
||||||
|
* This will Promise reject is the user is anonymous.
|
||||||
|
*/
|
||||||
|
updateEmail(email: string): Promise<void>
|
||||||
|
/**
|
||||||
|
* Important: this is a security sensitive operation that requires the user to have recently signed in.
|
||||||
|
* If this requirement isn't met, ask the user to authenticate again and then call firebase.User#reauthenticate.
|
||||||
|
* This will Promise reject is the user is anonymous.
|
||||||
|
*/
|
||||||
|
updatePassword(password: string): Promise<void>
|
||||||
|
/**
|
||||||
|
* Updates a user's profile data.
|
||||||
|
* Profile data should be an object of fields to update:
|
||||||
|
*/
|
||||||
|
updateProfile(profile: Object): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 3rd party provider Credentials */
|
||||||
|
interface Credential {
|
||||||
|
provider: string,
|
||||||
|
token: string,
|
||||||
|
secret: string
|
||||||
|
}
|
||||||
|
namespace auth {
|
||||||
|
|
||||||
|
interface Auth {
|
||||||
|
/**
|
||||||
|
* Returns the current Firebase authentication state.
|
||||||
|
*/
|
||||||
|
authenticated: boolean;
|
||||||
|
/**
|
||||||
|
* Returns the currently signed-in user (or null). See the User class documentation for further usage.
|
||||||
|
*/
|
||||||
|
currentUser: User | null
|
||||||
|
/**
|
||||||
|
* Listen for changes in the users auth state (logging in and out).
|
||||||
|
* This method returns a unsubscribe function to stop listening to events.
|
||||||
|
* Always ensure you unsubscribe from the listener when no longer needed to prevent updates to components no longer in use.
|
||||||
|
*/
|
||||||
|
onAuthStateChanged(
|
||||||
|
nextOrObserver: Object, error?: (a: RnError) => any,
|
||||||
|
completed?: () => any): () => any;
|
||||||
|
/**
|
||||||
|
* We can create a user by calling the createUserWithEmailAndPassword() function.
|
||||||
|
* The method accepts two parameters, an email and a password.
|
||||||
|
*/
|
||||||
|
createUserWithEmailAndPassword(email: string, password: string): Promise<User>
|
||||||
|
/**
|
||||||
|
* To sign a user in with their email and password, use the signInWithEmailAndPassword() function.
|
||||||
|
* It accepts two parameters, the user's email and password:
|
||||||
|
*/
|
||||||
|
signInWithEmailAndPassword(email: string, password: string): Promise<User>
|
||||||
|
/**
|
||||||
|
* Sign an anonymous user.
|
||||||
|
* If the user has already signed in, that user will be returned
|
||||||
|
*/
|
||||||
|
signInAnonymously(): Promise<User>
|
||||||
|
/**
|
||||||
|
* Sign in the user with a 3rd party credential provider.
|
||||||
|
* credential requires the following properties:
|
||||||
|
*/
|
||||||
|
signInWithCredential(credential: Credential): Promise<User>
|
||||||
|
/**
|
||||||
|
* Sign a user in with a self-signed JWT token.
|
||||||
|
* To sign a user using a self-signed custom token,
|
||||||
|
* use the signInWithCustomToken() function.
|
||||||
|
* It accepts one parameter, the custom token:
|
||||||
|
*/
|
||||||
|
signInWithCustomToken(token: string): Promise<User>
|
||||||
|
/**
|
||||||
|
* Sends a password reset email to the given email address.
|
||||||
|
* Unlike the web SDK,
|
||||||
|
* the email will contain a password reset link rather than a code.
|
||||||
|
*/
|
||||||
|
sendPasswordResetEmail(email: string): Promise<void>
|
||||||
|
/**
|
||||||
|
* Completes the password reset process,
|
||||||
|
* given a confirmation code and new password.
|
||||||
|
*/
|
||||||
|
signOut(): Promise<void>
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace messaging {
|
||||||
|
|
||||||
|
interface Messaging {
|
||||||
|
/**
|
||||||
|
* Subscribes the device to a topic.
|
||||||
|
*/
|
||||||
|
subscribeToTopic(topic: string): void
|
||||||
|
/**
|
||||||
|
* Unsubscribes the device from a topic.
|
||||||
|
*/
|
||||||
|
unsubscribeFromTopic(topic: string): void
|
||||||
|
/**
|
||||||
|
* When the application has been opened from a notification
|
||||||
|
* getInitialNotification is called and the notification payload is returned.
|
||||||
|
* Use onMessage for notifications when the app is running.
|
||||||
|
*/
|
||||||
|
getInitialNotification(): Promise<any>
|
||||||
|
/**
|
||||||
|
* Returns the devices FCM token.
|
||||||
|
* This token can be used in the Firebase console to send messages to directly.
|
||||||
|
*/
|
||||||
|
getToken(forceRefresh?: Boolean): Promise<string>
|
||||||
|
/**
|
||||||
|
* On the event a devices FCM token is refreshed by Google,
|
||||||
|
* the new token is returned in a callback listener.
|
||||||
|
*/
|
||||||
|
onTokenRefresh(listener: (token: string) => any): void
|
||||||
|
/**
|
||||||
|
* On a new message,
|
||||||
|
* the payload object is passed to the listener callback.
|
||||||
|
* This method is only triggered when the app is running.
|
||||||
|
* Use getInitialNotification for notifications which cause the app to open.
|
||||||
|
*/
|
||||||
|
onMessage(listener: (message: any) => any): void
|
||||||
|
/**
|
||||||
|
* Create a local notification from the device itself.
|
||||||
|
*/
|
||||||
|
createLocalNotification(notification: any): any
|
||||||
|
/**
|
||||||
|
* Schedule a local notification to be shown on the device.
|
||||||
|
*/
|
||||||
|
scheduleLocalNotification(notification: any): any
|
||||||
|
/**
|
||||||
|
* Returns an array of all currently scheduled notifications.
|
||||||
|
* ```
|
||||||
|
* firebase.messaging().getScheduledLocalNotifications()
|
||||||
|
* .then((notifications) => {
|
||||||
|
* console.log('Current scheduled notifications: ', notifications);
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
getScheduledLocalNotifications(): Promise<any[]>
|
||||||
|
/**
|
||||||
|
* Cancels a location notification by ID,
|
||||||
|
* or all notifications by *.
|
||||||
|
*/
|
||||||
|
cancelLocalNotification(id: string): void
|
||||||
|
/**
|
||||||
|
* Removes all delivered notifications from device by ID,
|
||||||
|
* or all notifications by *.
|
||||||
|
*/
|
||||||
|
removeDeliveredNotification(id: string): void
|
||||||
|
/**
|
||||||
|
* IOS
|
||||||
|
* Requests app notification permissions in an Alert dialog.
|
||||||
|
*/
|
||||||
|
requestPermissions(): void
|
||||||
|
/**
|
||||||
|
* IOS
|
||||||
|
* Sets the badge number on the iOS app icon.
|
||||||
|
*/
|
||||||
|
setBadgeNumber(value: number): void
|
||||||
|
/**
|
||||||
|
* IOS
|
||||||
|
* Returns the current badge number on the app icon.
|
||||||
|
*/
|
||||||
|
getBadgeNumber(): number
|
||||||
|
/**
|
||||||
|
* Send an upstream message
|
||||||
|
* @param senderId
|
||||||
|
* @param payload
|
||||||
|
*/
|
||||||
|
send(senderId: string, payload: RemoteMessage): any
|
||||||
|
NOTIFICATION_TYPE: Object
|
||||||
|
REMOTE_NOTIFICATION_RESULT: Object
|
||||||
|
WILL_PRESENT_RESULT: Object
|
||||||
|
EVENT_TYPE: Object
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RemoteMessage {
|
||||||
|
id: string,
|
||||||
|
type: string,
|
||||||
|
ttl?: number,
|
||||||
|
sender: string,
|
||||||
|
collapseKey?: string,
|
||||||
|
data: Object,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
namespace crash {
|
||||||
|
|
||||||
|
interface Crash {
|
||||||
|
/** Logs a message that will appear in a subsequent crash report. */
|
||||||
|
log(message: string): void
|
||||||
|
/**
|
||||||
|
* Android: Logs a message that will appear in a subsequent crash report as well as in logcat.
|
||||||
|
* iOS: Logs the message in the subsequest crash report only (same as log).
|
||||||
|
*/
|
||||||
|
logcat(level: number, tag: string, message: string): void
|
||||||
|
/**
|
||||||
|
* Files a crash report, along with any previous logs to Firebase.
|
||||||
|
* An Error object must be passed into the report method.
|
||||||
|
*/
|
||||||
|
report(error: RnError, maxStackSize: Number): void
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,21 @@
|
||||||
#define RNFirebase_h
|
#define RNFirebase_h
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import <React/RCTBridgeModule.h>
|
#if __has_include(<React/RCTEventDispatcher.h>)
|
||||||
#import <React/RCTEventDispatcher.h>
|
#import <React/RCTEventDispatcher.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTEventDispatcher.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTEventEmitter.h>)
|
||||||
#import <React/RCTEventEmitter.h>
|
#import <React/RCTEventEmitter.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTEventEmitter.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTBridgeModule.h>)
|
||||||
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@interface RNFirebase : RCTEventEmitter <RCTBridgeModule> {
|
@interface RNFirebase : RCTEventEmitter <RCTBridgeModule> {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#ifndef RNFirebaseAnalytics_h
|
#ifndef RNFirebaseAnalytics_h
|
||||||
#define RNFirebaseAnalytics_h
|
#define RNFirebaseAnalytics_h
|
||||||
|
|
||||||
|
#if __has_include(<React/RCTBridgeModule.h>)
|
||||||
#import <React/RCTBridgeModule.h>
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@interface RNFirebaseAnalytics : NSObject <RCTBridgeModule> {
|
@interface RNFirebaseAnalytics : NSObject <RCTBridgeModule> {
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,16 @@
|
||||||
#define RNFirebaseAuth_h
|
#define RNFirebaseAuth_h
|
||||||
|
|
||||||
#import "Firebase.h"
|
#import "Firebase.h"
|
||||||
|
#if __has_include(<React/RCTEventEmitter.h>)
|
||||||
#import <React/RCTEventEmitter.h>
|
#import <React/RCTEventEmitter.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTEventEmitter.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTBridgeModule.h>)
|
||||||
#import <React/RCTBridgeModule.h>
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@interface RNFirebaseAuth : RCTEventEmitter <RCTBridgeModule> {
|
@interface RNFirebaseAuth : RCTEventEmitter <RCTBridgeModule> {
|
||||||
FIRAuthStateDidChangeListenerHandle authListenerHandle;
|
FIRAuthStateDidChangeListenerHandle authListenerHandle;
|
||||||
|
|
|
@ -636,6 +636,44 @@ RCT_EXPORT_METHOD(reauthenticate:(NSString *)provider authToken:(NSString *)auth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Converts an array of FIRUserInfo instances into the correct format to match the web sdk
|
||||||
|
|
||||||
|
@param providerData FIRUser.providerData
|
||||||
|
@return NSArray
|
||||||
|
*/
|
||||||
|
- (NSArray <NSObject *> *) convertProviderData:(NSArray <id<FIRUserInfo>> *) providerData {
|
||||||
|
NSMutableArray *output = [NSMutableArray array];
|
||||||
|
|
||||||
|
for (id<FIRUserInfo> userInfo in providerData) {
|
||||||
|
NSMutableDictionary *pData = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
if (userInfo.providerID != nil) {
|
||||||
|
[pData setValue: userInfo.providerID forKey:@"providerId"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo.uid != nil) {
|
||||||
|
[pData setValue: userInfo.uid forKey:@"uid"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo.displayName != nil) {
|
||||||
|
[pData setValue: userInfo.displayName forKey:@"displayName"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo.photoURL != nil) {
|
||||||
|
[pData setValue: [userInfo.photoURL absoluteString] forKey:@"photoURL"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo.email != nil) {
|
||||||
|
[pData setValue: userInfo.email forKey:@"email"];
|
||||||
|
}
|
||||||
|
|
||||||
|
[output addObject:pData];
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Converts a FIRUser instance into a dictionary to send via RNBridge
|
Converts a FIRUser instance into a dictionary to send via RNBridge
|
||||||
|
|
||||||
|
@ -650,15 +688,13 @@ RCT_EXPORT_METHOD(reauthenticate:(NSString *)provider authToken:(NSString *)auth
|
||||||
@"isAnonymous": @(user.anonymous),
|
@"isAnonymous": @(user.anonymous),
|
||||||
@"displayName": user.displayName ? user.displayName : [NSNull null],
|
@"displayName": user.displayName ? user.displayName : [NSNull null],
|
||||||
@"refreshToken": user.refreshToken,
|
@"refreshToken": user.refreshToken,
|
||||||
@"providerId": [user.providerID lowercaseString]
|
@"providerId": [user.providerID lowercaseString],
|
||||||
}
|
@"providerData": [self convertProviderData: user.providerData]
|
||||||
mutableCopy
|
} mutableCopy
|
||||||
];
|
];
|
||||||
|
|
||||||
// todo providerData
|
|
||||||
|
|
||||||
if ([user valueForKey:@"photoURL"] != nil) {
|
if ([user valueForKey:@"photoURL"] != nil) {
|
||||||
[userDict setValue: [NSString stringWithFormat:@"%@", user.photoURL] forKey:@"photoURL"];
|
[userDict setValue: [user.photoURL absoluteString] forKey:@"photoURL"];
|
||||||
}
|
}
|
||||||
|
|
||||||
return userDict;
|
return userDict;
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#ifndef RNFirebaseCrash_h
|
#ifndef RNFirebaseCrash_h
|
||||||
#define RNFirebaseCrash_h
|
#define RNFirebaseCrash_h
|
||||||
|
|
||||||
|
#if __has_include(<React/RCTBridgeModule.h>)
|
||||||
#import <React/RCTBridgeModule.h>
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@interface RNFirebaseCrash : NSObject <RCTBridgeModule> {
|
@interface RNFirebaseCrash : NSObject <RCTBridgeModule> {
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,16 @@
|
||||||
#define RNFirebaseDatabase_h
|
#define RNFirebaseDatabase_h
|
||||||
|
|
||||||
#import "Firebase.h"
|
#import "Firebase.h"
|
||||||
|
#if __has_include(<React/RCTEventEmitter.h>)
|
||||||
#import <React/RCTEventEmitter.h>
|
#import <React/RCTEventEmitter.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTEventEmitter.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTBridgeModule.h>)
|
||||||
#import <React/RCTBridgeModule.h>
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@interface RNFirebaseDatabase : RCTEventEmitter <RCTBridgeModule> {
|
@interface RNFirebaseDatabase : RCTEventEmitter <RCTBridgeModule> {
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,7 @@
|
||||||
type:(NSString *) type
|
type:(NSString *) type
|
||||||
{
|
{
|
||||||
if ([type isEqualToString:@"number"]) {
|
if ([type isEqualToString:@"number"]) {
|
||||||
return [NSNumber numberWithInteger:value.integerValue];
|
return [NSNumber numberWithDouble:value.doubleValue];
|
||||||
} else if ([type isEqualToString:@"boolean"]) {
|
} else if ([type isEqualToString:@"boolean"]) {
|
||||||
return [NSNumber numberWithBool:value.boolValue];
|
return [NSNumber numberWithBool:value.boolValue];
|
||||||
} else {
|
} else {
|
||||||
|
@ -361,7 +361,11 @@ RCT_EXPORT_METHOD(enablePersistence:(BOOL) enable
|
||||||
|
|
||||||
BOOL isEnabled = [FIRDatabase database].persistenceEnabled;
|
BOOL isEnabled = [FIRDatabase database].persistenceEnabled;
|
||||||
if ( isEnabled != enable) {
|
if ( isEnabled != enable) {
|
||||||
|
@try {
|
||||||
[FIRDatabase database].persistenceEnabled = enable;
|
[FIRDatabase database].persistenceEnabled = enable;
|
||||||
|
} @catch (NSException *exception) {
|
||||||
|
// do nothing - for RN packager reloads
|
||||||
|
}
|
||||||
}
|
}
|
||||||
callback(@[[NSNull null], @{
|
callback(@[[NSNull null], @{
|
||||||
@"result": @"success"
|
@"result": @"success"
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#ifndef RNFirebaseErrors_h
|
#ifndef RNFirebaseErrors_h
|
||||||
#define RNFirebaseErrors_h
|
#define RNFirebaseErrors_h
|
||||||
|
|
||||||
|
#if __has_include(<React/RCTBridgeModule.h>)
|
||||||
#import <React/RCTBridgeModule.h>
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#endif
|
||||||
#import "Firebase.h"
|
#import "Firebase.h"
|
||||||
|
|
||||||
@interface RNFirebaseErrors : NSObject <RCTBridgeModule> {
|
@interface RNFirebaseErrors : NSObject <RCTBridgeModule> {
|
||||||
|
|
|
@ -5,9 +5,22 @@
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
#import "Firebase.h"
|
#import "Firebase.h"
|
||||||
|
|
||||||
|
#if __has_include(<React/RCTEventEmitter.h>)
|
||||||
#import <React/RCTEventEmitter.h>
|
#import <React/RCTEventEmitter.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTEventEmitter.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTBridgeModule.h>)
|
||||||
#import <React/RCTBridgeModule.h>
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTUtils.h>)
|
||||||
#import <React/RCTUtils.h>
|
#import <React/RCTUtils.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@import UserNotifications;
|
@import UserNotifications;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,20 @@
|
||||||
#import "RNFirebaseMessaging.h"
|
#import "RNFirebaseMessaging.h"
|
||||||
|
|
||||||
#import <React/RCTConvert.h>
|
#if __has_include(<React/RCTEventDispatcher.h>)
|
||||||
#import <React/RCTEventDispatcher.h>
|
#import <React/RCTEventDispatcher.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTEventDispatcher.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTConvert.h>)
|
||||||
|
#import <React/RCTConvert.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTConvert.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTUtils.h>)
|
||||||
#import <React/RCTUtils.h>
|
#import <React/RCTUtils.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@import UserNotifications;
|
@import UserNotifications;
|
||||||
#import <FirebaseMessaging/FirebaseMessaging.h>
|
#import <FirebaseMessaging/FirebaseMessaging.h>
|
||||||
|
|
|
@ -2,8 +2,16 @@
|
||||||
#define RNFirebaseStorage_h
|
#define RNFirebaseStorage_h
|
||||||
|
|
||||||
#import "Firebase.h"
|
#import "Firebase.h"
|
||||||
#import <React/RCTBridgeModule.h>
|
#if __has_include(<React/RCTEventEmitter.h>)
|
||||||
#import <React/RCTEventEmitter.h>
|
#import <React/RCTEventEmitter.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTEventEmitter.h"
|
||||||
|
#endif
|
||||||
|
#if __has_include(<React/RCTBridgeModule.h>)
|
||||||
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#else // Compatibility for RN version < 0.40
|
||||||
|
#import "RCTBridgeModule.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@interface RNFirebaseStorage : RCTEventEmitter <RCTBridgeModule> {
|
@interface RNFirebaseStorage : RCTEventEmitter <RCTBridgeModule> {
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import { NativeModules } from 'react-native';
|
import { NativeModules } from 'react-native';
|
||||||
import { promisify } from './../../utils';
|
|
||||||
|
|
||||||
const FirebaseAuth = NativeModules.RNFirebaseAuth;
|
const FirebaseAuth = NativeModules.RNFirebaseAuth;
|
||||||
|
|
||||||
// TODO refreshToken property
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @url https://firebase.google.com/docs/reference/js/firebase.User
|
* @url https://firebase.google.com/docs/reference/js/firebase.User
|
||||||
|
@ -144,10 +142,13 @@ export default class User {
|
||||||
return FirebaseAuth.getToken(forceRefresh);
|
return FirebaseAuth.getToken(forceRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO return from native
|
/**
|
||||||
// get providerData() {
|
*
|
||||||
// return this._valueOrNull('providerData');
|
* @returns {Array}
|
||||||
// }
|
*/
|
||||||
|
get providerData(): Array {
|
||||||
|
return this._valueOrNull('providerData') || [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current user's email
|
* Update the current user's email
|
||||||
|
|
|
@ -76,6 +76,12 @@ export class ReferenceBase extends Base {
|
||||||
this.path = path || '/';
|
this.path = path || '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last part of a Reference's path (after the last '/')
|
||||||
|
* The key of a root Reference is null.
|
||||||
|
* @type {String}
|
||||||
|
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#key}
|
||||||
|
*/
|
||||||
get key(): string|null {
|
get key(): string|null {
|
||||||
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1);
|
return this.path === '/' ? null : this.path.substring(this.path.lastIndexOf('/') + 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,44 @@ const FirebaseDatabase = NativeModules.RNFirebaseDatabase;
|
||||||
let refId = 1;
|
let refId = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Enum for event types
|
||||||
|
* @readonly
|
||||||
|
* @enum {String}
|
||||||
|
*/
|
||||||
|
const ReferenceEventTypes = {
|
||||||
|
value: 'value',
|
||||||
|
child_added: 'child_added',
|
||||||
|
child_removed: 'child_removed',
|
||||||
|
child_changed: 'child_changed',
|
||||||
|
child_moved: 'child_moved',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {String} ReferenceLocation - Path to location in the database, relative
|
||||||
|
* to the root reference. Consists of a path where segments are separated by a
|
||||||
|
* forward slash (/) and ends in a ReferenceKey - except the root location, which
|
||||||
|
* has no ReferenceKey.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // root reference location: '/'
|
||||||
|
* // non-root reference: '/path/to/referenceKey'
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {String} ReferenceKey - Identifier for each location that is unique to that
|
||||||
|
* location, within the scope of its parent. The last part of a ReferenceLocation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a specific location in your Database that can be used for
|
||||||
|
* reading or writing data.
|
||||||
|
*
|
||||||
|
* You can reference the root using firebase.database().ref() or a child location
|
||||||
|
* by calling firebase.database().ref("child/path").
|
||||||
|
*
|
||||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.Reference
|
* @link https://firebase.google.com/docs/reference/js/firebase.database.Reference
|
||||||
* @class Reference
|
* @class Reference
|
||||||
|
* @extends ReferenceBase
|
||||||
*/
|
*/
|
||||||
export default class Reference extends ReferenceBase {
|
export default class Reference extends ReferenceBase {
|
||||||
|
|
||||||
|
@ -99,23 +135,157 @@ export default class Reference extends ReferenceBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* iOS: Called once with the initial data at the specified location and then once each
|
||||||
|
* time the data changes. It won't trigger until the entire contents have been
|
||||||
|
* synchronized.
|
||||||
*
|
*
|
||||||
* @param eventName
|
* Android: (@link https://github.com/invertase/react-native-firebase/issues/92)
|
||||||
* @param successCallback
|
* - Array & number values: Called once with the initial data at the specified
|
||||||
* @param failureCallback
|
* location and then twice each time the value changes.
|
||||||
* @param context TODO
|
* - Other data types: Called once with the initial data at the specified location
|
||||||
* @returns {*}
|
* and once each time the data type changes.
|
||||||
|
*
|
||||||
|
* @callback onValueCallback
|
||||||
|
* @param {!DataSnapshot} dataSnapshot - Snapshot representing data at the location
|
||||||
|
* specified by the current ref. If location has no data, .val() will return null.
|
||||||
*/
|
*/
|
||||||
on(eventName: string, successCallback: () => any, failureCallback: () => any) {
|
|
||||||
if (!isFunction(successCallback)) throw new Error('The specified callback must be a function');
|
/**
|
||||||
if (failureCallback && !isFunction(failureCallback)) throw new Error('The specified error callback must be a function');
|
* Called once for each initial child at the specified location and then again
|
||||||
this.log.debug('adding reference.on', this.refId, eventName);
|
* every time a new child is added.
|
||||||
|
*
|
||||||
|
* @callback onChildAddedCallback
|
||||||
|
* @param {!DataSnapshot} dataSnapshot - Snapshot reflecting the data for the
|
||||||
|
* relevant child.
|
||||||
|
* @param {?ReferenceKey} previousChildKey - For ordering purposes, the key
|
||||||
|
* of the previous sibling child by sort order, or null if it is the first child.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called once every time a child is removed.
|
||||||
|
*
|
||||||
|
* A child will get removed when either:
|
||||||
|
* - remove() is explicitly called on a child or one of its ancestors
|
||||||
|
* - set(null) is called on that child or one of its ancestors
|
||||||
|
* - a child has all of its children removed
|
||||||
|
* - there is a query in effect which now filters out the child (because it's sort
|
||||||
|
* order changed or the max limit was hit)
|
||||||
|
*
|
||||||
|
* @callback onChildRemovedCallback
|
||||||
|
* @param {!DataSnapshot} dataSnapshot - Snapshot reflecting the old data for
|
||||||
|
* the child that was removed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a child (or any of its descendants) changes.
|
||||||
|
*
|
||||||
|
* A single child_changed event may represent multiple changes to the child.
|
||||||
|
*
|
||||||
|
* @callback onChildChangedCallback
|
||||||
|
* @param {!DataSnapshot} dataSnapshot - Snapshot reflecting new child contents.
|
||||||
|
* @param {?ReferenceKey} previousChildKey - For ordering purposes, the key
|
||||||
|
* of the previous sibling child by sort order, or null if it is the first child.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a child's sort order changes, i.e. its position relative to its
|
||||||
|
* siblings changes.
|
||||||
|
*
|
||||||
|
* @callback onChildMovedCallback
|
||||||
|
* @param {!DataSnapshot} dataSnapshot - Snapshot reflecting the data of the moved
|
||||||
|
* child.
|
||||||
|
* @param {?ReferenceKey} previousChildKey - For ordering purposes, the key
|
||||||
|
* of the previous sibling child by sort order, or null if it is the first child.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef (onValueCallback|onChildAddedCallback|onChildRemovedCallback|onChildChangedCallback|onChildMovedCallback) ReferenceEventCallback
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called if the event subscription is cancelled because the client does
|
||||||
|
* not have permission to read this data (or has lost the permission to do so).
|
||||||
|
*
|
||||||
|
* @callback onFailureCallback
|
||||||
|
* @param {Error} error - Object indicating why the failure occurred
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds callback handlers to when data changes at the current ref's location.
|
||||||
|
* The primary method of reading data from a Database.
|
||||||
|
*
|
||||||
|
* Callbacks can be unbound using {@link off}.
|
||||||
|
*
|
||||||
|
* Event Types:
|
||||||
|
*
|
||||||
|
* - value: {@link onValueCallback}.
|
||||||
|
* - child_added: {@link onChildAddedCallback}
|
||||||
|
* - child_removed: {@link onChildRemovedCallback}
|
||||||
|
* - child_changed: {@link onChildChangedCallback}
|
||||||
|
* - child_moved: {@link onChildMovedCallback}
|
||||||
|
*
|
||||||
|
* @param {ReferenceEventType} eventType - Type of event to attach a callback for.
|
||||||
|
* @param {ReferenceEventCallback} successCallback - Function that will be called
|
||||||
|
* when the event occurs with the new data.
|
||||||
|
* @param {onFailureCallback=} failureCallbackOrContext - Optional callback that is called
|
||||||
|
* if the event subscription fails. {@link onFailureCallback}
|
||||||
|
* @param {*=} context - Optional object to bind the callbacks to when calling them.
|
||||||
|
* @returns {ReferenceEventCallback} callback function, unmodified (unbound), for
|
||||||
|
* convenience if you want to pass an inline function to on() and store it later for
|
||||||
|
* removing using off().
|
||||||
|
*
|
||||||
|
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#on}
|
||||||
|
*/
|
||||||
|
on(eventType: string, successCallback: () => any, failureCallbackOrContext: () => any, context: any) {
|
||||||
|
if (!eventType) throw new Error('Error: Query on failed: Was called with 0 arguments. Expects at least 2');
|
||||||
|
if (!ReferenceEventTypes[eventType]) throw new Error('Query.on failed: First argument must be a valid event type: "value", "child_added", "child_removed", "child_changed", or "child_moved".');
|
||||||
|
if (!successCallback) throw new Error('Query.on failed: Was called with 1 argument. Expects at least 2.');
|
||||||
|
if (!isFunction(successCallback)) throw new Error('Query.on failed: Second argument must be a valid function.');
|
||||||
|
if (arguments.length > 2 && !failureCallbackOrContext) throw new Error('Query.on failed: third argument must either be a cancel callback or a context object.');
|
||||||
|
|
||||||
|
this.log.debug('adding reference.on', this.refId, eventType);
|
||||||
|
|
||||||
|
let _failureCallback;
|
||||||
|
let _context;
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
_context = context;
|
||||||
|
_failureCallback = failureCallbackOrContext;
|
||||||
|
} else {
|
||||||
|
if (isFunction(failureCallbackOrContext)) {
|
||||||
|
_failureCallback = failureCallbackOrContext;
|
||||||
|
} else {
|
||||||
|
_context = failureCallbackOrContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_failureCallback) {
|
||||||
|
_failureCallback = (error) => {
|
||||||
|
if (error.message.startsWith('FirebaseError: permission_denied')) {
|
||||||
|
|
||||||
|
error.message = `permission_denied at /${this.path}: Client doesn\'t have permission to access the desired data.`
|
||||||
|
}
|
||||||
|
|
||||||
|
failureCallbackOrContext(error);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _successCallback;
|
||||||
|
|
||||||
|
if (_context) {
|
||||||
|
_successCallback = successCallback.bind(_context);
|
||||||
|
} else {
|
||||||
|
_successCallback = successCallback;
|
||||||
|
}
|
||||||
|
|
||||||
const listener = {
|
const listener = {
|
||||||
listenerId: Object.keys(this.listeners).length + 1,
|
listenerId: Object.keys(this.listeners).length + 1,
|
||||||
eventName,
|
eventName: eventType,
|
||||||
successCallback,
|
successCallback: _successCallback,
|
||||||
failureCallback,
|
failureCallback: _failureCallback,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.listeners[listener.listenerId] = listener;
|
this.listeners[listener.listenerId] = listener;
|
||||||
this.database.on(this, listener);
|
this.database.on(this, listener);
|
||||||
return successCallback;
|
return successCallback;
|
||||||
|
@ -144,29 +314,49 @@ export default class Reference extends ReferenceBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Detaches a callback attached with on().
|
||||||
*
|
*
|
||||||
* @param eventName
|
* Calling off() on a parent listener will not automatically remove listeners
|
||||||
* @param origCB
|
* registered on child nodes.
|
||||||
* @returns {*}
|
*
|
||||||
|
* If on() was called multiple times with the same eventType off() must be
|
||||||
|
* called multiple times to completely remove it.
|
||||||
|
*
|
||||||
|
* If a callback is not specified, all callbacks for the specified eventType
|
||||||
|
* will be removed. If no eventType or callback is specified, all callbacks
|
||||||
|
* for the Reference will be removed.
|
||||||
|
*
|
||||||
|
* If a context is specified, it too is used as a filter parameter: a callback
|
||||||
|
* will only be detached if, when it was attached with on(), the same event type,
|
||||||
|
* callback function and context were provided.
|
||||||
|
*
|
||||||
|
* If no callbacks matching the parameters provided are found, no callbacks are
|
||||||
|
* detached.
|
||||||
|
*
|
||||||
|
* @param {('value'|'child_added'|'child_changed'|'child_removed'|'child_moved')=} eventType - Type of event to detach callback for.
|
||||||
|
* @param {Function=} originalCallback - Original callback passed to on()
|
||||||
|
* @param {*=} context - The context passed to on() when the callback was bound
|
||||||
|
*
|
||||||
|
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#off}
|
||||||
*/
|
*/
|
||||||
off(eventName?: string = '', origCB?: () => any) {
|
off(eventType?: string = '', originalCallback?: () => any) {
|
||||||
this.log.debug('ref.off(): ', this.refId, eventName);
|
this.log.debug('ref.off(): ', this.refId, eventType);
|
||||||
// $FlowFixMe
|
// $FlowFixMe
|
||||||
const listeners: Array<DatabaseListener> = Object.values(this.listeners);
|
const listeners: Array<DatabaseListener> = Object.values(this.listeners);
|
||||||
let listenersToRemove;
|
let listenersToRemove;
|
||||||
if (eventName && origCB) {
|
if (eventType && originalCallback) {
|
||||||
listenersToRemove = listeners.filter((listener) => {
|
listenersToRemove = listeners.filter((listener) => {
|
||||||
return listener.eventName === eventName && listener.successCallback === origCB;
|
return listener.eventName === eventType && listener.successCallback === originalCallback;
|
||||||
});
|
});
|
||||||
// Only remove a single listener as per the web spec
|
// Only remove a single listener as per the web spec
|
||||||
if (listenersToRemove.length > 1) listenersToRemove = [listenersToRemove[0]];
|
if (listenersToRemove.length > 1) listenersToRemove = [listenersToRemove[0]];
|
||||||
} else if (eventName) {
|
} else if (eventType) {
|
||||||
listenersToRemove = listeners.filter((listener) => {
|
listenersToRemove = listeners.filter((listener) => {
|
||||||
return listener.eventName === eventName;
|
return listener.eventName === eventType;
|
||||||
});
|
});
|
||||||
} else if (origCB) {
|
} else if (originalCallback) {
|
||||||
listenersToRemove = listeners.filter((listener) => {
|
listenersToRemove = listeners.filter((listener) => {
|
||||||
return listener.successCallback === origCB;
|
return listener.successCallback === originalCallback;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
listenersToRemove = listeners;
|
listenersToRemove = listeners;
|
||||||
|
@ -185,8 +375,16 @@ export default class Reference extends ReferenceBase {
|
||||||
* @param onComplete
|
* @param onComplete
|
||||||
* @param applyLocally
|
* @param applyLocally
|
||||||
*/
|
*/
|
||||||
transaction(transactionUpdate: Function, onComplete: (?Error, boolean, ?Snapshot) => *, applyLocally: boolean = false) {
|
transaction(
|
||||||
if (!isFunction(transactionUpdate)) return Promise.reject(new Error('Missing transactionUpdate function argument.'));
|
transactionUpdate: Function,
|
||||||
|
onComplete: (error: ?Error, committed: boolean, snapshot: ?Snapshot) => *,
|
||||||
|
applyLocally: boolean = false
|
||||||
|
) {
|
||||||
|
if (!isFunction(transactionUpdate)) {
|
||||||
|
return Promise.reject(
|
||||||
|
new Error('Missing transactionUpdate function argument.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const onCompleteWrapper = (error, committed, snapshotData) => {
|
const onCompleteWrapper = (error, committed, snapshotData) => {
|
||||||
|
@ -349,9 +547,12 @@ export default class Reference extends ReferenceBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a specified child
|
* Creates a Reference to a child of the current Reference, using a relative path.
|
||||||
* @param path
|
* No validation is performed on the path to ensure it has a valid format.
|
||||||
* @returns {Reference}
|
* @param {String} path relative to current ref's location
|
||||||
|
* @returns {!Reference} A new Reference to the path provided, relative to the current
|
||||||
|
* Reference
|
||||||
|
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#child}
|
||||||
*/
|
*/
|
||||||
child(path: string) {
|
child(path: string) {
|
||||||
return new Reference(this.database, `${this.path}/${path}`);
|
return new Reference(this.database, `${this.path}/${path}`);
|
||||||
|
@ -370,6 +571,7 @@ export default class Reference extends ReferenceBase {
|
||||||
* same instance of firebase.app.App - multiple firebase apps not currently supported.
|
* same instance of firebase.app.App - multiple firebase apps not currently supported.
|
||||||
* @param {Reference} otherRef - Other reference to compare to this one
|
* @param {Reference} otherRef - Other reference to compare to this one
|
||||||
* @return {Boolean} Whether otherReference is equal to this one
|
* @return {Boolean} Whether otherReference is equal to this one
|
||||||
|
*
|
||||||
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#isEqual}
|
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#isEqual}
|
||||||
*/
|
*/
|
||||||
isEqual(otherRef: Reference): boolean {
|
isEqual(otherRef: Reference): boolean {
|
||||||
|
@ -381,8 +583,10 @@ export default class Reference extends ReferenceBase {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the parent ref of the current ref i.e. a ref of /foo/bar would return a new ref to '/foo'
|
* The parent location of a Reference, or null for the root Reference.
|
||||||
* @returns {*}
|
* @type {Reference}
|
||||||
|
*
|
||||||
|
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#parent}
|
||||||
*/
|
*/
|
||||||
get parent(): Reference|null {
|
get parent(): Reference|null {
|
||||||
if (this.path === '/') return null;
|
if (this.path === '/') return null;
|
||||||
|
@ -392,6 +596,7 @@ export default class Reference extends ReferenceBase {
|
||||||
/**
|
/**
|
||||||
* A reference to itself
|
* A reference to itself
|
||||||
* @type {!Reference}
|
* @type {!Reference}
|
||||||
|
*
|
||||||
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#ref}
|
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#ref}
|
||||||
*/
|
*/
|
||||||
get ref(): Reference {
|
get ref(): Reference {
|
||||||
|
@ -399,8 +604,10 @@ export default class Reference extends ReferenceBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a ref to the root of db - '/'
|
* Reference to the root of the database: '/'
|
||||||
* @returns {Reference}
|
* @type {!Reference}
|
||||||
|
*
|
||||||
|
* {@link https://firebase.google.com/docs/reference/js/firebase.database.Reference#root}
|
||||||
*/
|
*/
|
||||||
get root(): Reference {
|
get root(): Reference {
|
||||||
return new Reference(this.database, '/');
|
return new Reference(this.database, '/');
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Reference from './reference.js';
|
||||||
import { isObject, deepGet, deepExists } from './../../utils';
|
import { isObject, deepGet, deepExists } from './../../utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @class DataSnapshot
|
||||||
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot
|
* @link https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot
|
||||||
*/
|
*/
|
||||||
export default class Snapshot {
|
export default class Snapshot {
|
||||||
|
|
|
@ -165,7 +165,7 @@ export function promisify(
|
||||||
fn: Function | string,
|
fn: Function | string,
|
||||||
NativeModule: Object,
|
NativeModule: Object,
|
||||||
errorPrefix?: string
|
errorPrefix?: string
|
||||||
): (any) => Promise<> {
|
): (args: any) => Promise<> {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const _fn = typeof fn === 'function' ? fn : NativeModule[fn];
|
const _fn = typeof fn === 'function' ? fn : NativeModule[fn];
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "react-native-firebase",
|
"name": "react-native-firebase",
|
||||||
"version": "1.0.1",
|
"version": "1.0.10",
|
||||||
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
|
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
|
||||||
"description": "A react native firebase library supporting both android and ios native firebase SDK's",
|
"description": "A react native firebase library supporting both android and ios native firebase SDK's",
|
||||||
"main": "index",
|
"main": "index",
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*"
|
"react-native": ">= 0.38.0"
|
||||||
},
|
},
|
||||||
"rnpm": {
|
"rnpm": {
|
||||||
"commands": {
|
"commands": {
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
"flow-bin": "^0.40.0",
|
"flow-bin": "^0.40.0",
|
||||||
"react": "^15.3.0",
|
"react": "^15.3.0",
|
||||||
"react-dom": "^15.3.0",
|
"react-dom": "^15.3.0",
|
||||||
"react-native": "^0.42.0",
|
"react-native": "^0.44.0",
|
||||||
"wml": "0.0.82"
|
"wml": "0.0.82"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
{
|
{
|
||||||
"presets": [
|
"presets": [
|
||||||
"react-native"
|
"react-native"
|
||||||
],
|
|
||||||
"ignore": [
|
|
||||||
"node_modules/diff/lib/**/*.js",
|
|
||||||
"node_modules/diff/node_modules/**/*.js"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
__tests__/build/
|
||||||
|
ios/build/
|
|
@ -68,7 +68,7 @@ Tests are bootstrapped and ran when the app is booted. The status of each test s
|
||||||
|
|
||||||
Tests can be run by pressing the play button in the toolbar of the app. Test can be run individually, by suite, or all at once.
|
Tests can be run by pressing the play button in the toolbar of the app. Test can be run individually, by suite, or all at once.
|
||||||
|
|
||||||
![Test suite Android](/docs/assets/test-suite-screenshot-android.png?raw=true)
|
![Test suite Android](/tests/docs/assets/test-suite-screenshot-android.png?raw=true)
|
||||||
|
|
||||||
|
|
||||||
### Adding test
|
### Adding test
|
||||||
|
|
|
@ -103,6 +103,84 @@ function pendingTestTests({ it: _it, describe: _describe }) {
|
||||||
otherTest.should.be.called();
|
otherTest.should.be.called();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_describe('when an outer context is focused', () => {
|
||||||
|
_it('a pending test will still not run', async () => {
|
||||||
|
const pendingTest = sinon.spy();
|
||||||
|
const otherTest = sinon.spy();
|
||||||
|
const unfocusedTest = sinon.spy();
|
||||||
|
|
||||||
|
const testSuite = new TestSuite('', '', {});
|
||||||
|
|
||||||
|
testSuite.addTests(({ fdescribe, it, xit }) => {
|
||||||
|
fdescribe('', () => {
|
||||||
|
xit('', pendingTest);
|
||||||
|
|
||||||
|
it('', otherTest);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('', unfocusedTest);
|
||||||
|
});
|
||||||
|
|
||||||
|
testSuite.setStore({
|
||||||
|
getState: () => { return {}; },
|
||||||
|
});
|
||||||
|
|
||||||
|
const testIdsToRun = Object.keys(testSuite.testDefinitions.focusedTestIds).reduce((memo, testId) => {
|
||||||
|
if (!testSuite.testDefinitions.pendingTestIds[testId]) {
|
||||||
|
memo.push(testId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
await testSuite.run(testIdsToRun);
|
||||||
|
|
||||||
|
pendingTest.should.not.be.called();
|
||||||
|
otherTest.should.be.called();
|
||||||
|
unfocusedTest.should.not.be.called();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
_describe('when an outer context is focused', () => {
|
||||||
|
_it('a pending context will still not run', async () => {
|
||||||
|
const pendingTest = sinon.spy();
|
||||||
|
const otherTest = sinon.spy();
|
||||||
|
const unfocusedTest = sinon.spy();
|
||||||
|
|
||||||
|
const testSuite = new TestSuite('', '', {});
|
||||||
|
|
||||||
|
testSuite.addTests(({ fdescribe, it, xdescribe }) => {
|
||||||
|
fdescribe('', () => {
|
||||||
|
xdescribe('', () => {
|
||||||
|
it('', pendingTest);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('', otherTest);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('', unfocusedTest);
|
||||||
|
});
|
||||||
|
|
||||||
|
testSuite.setStore({
|
||||||
|
getState: () => { return {}; },
|
||||||
|
});
|
||||||
|
|
||||||
|
const testIdsToRun = Object.keys(testSuite.testDefinitions.focusedTestIds).reduce((memo, testId) => {
|
||||||
|
if (!testSuite.testDefinitions.pendingTestIds[testId]) {
|
||||||
|
memo.push(testId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return memo;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
await testSuite.run(testIdsToRun);
|
||||||
|
|
||||||
|
pendingTest.should.not.be.called();
|
||||||
|
otherTest.should.be.called();
|
||||||
|
unfocusedTest.should.not.be.called();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default pendingTestTests;
|
export default pendingTestTests;
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||||
|
|
||||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean getUseDeveloperSupport() {
|
public boolean getUseDeveloperSupport() {
|
||||||
return BuildConfig.DEBUG;
|
return BuildConfig.DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.3.0'
|
classpath 'com.android.tools.build:gradle:2.3.1'
|
||||||
classpath 'com.google.gms:google-services:3.0.0'
|
classpath 'com.google.gms:google-services:3.0.0'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|
|
@ -18,3 +18,4 @@
|
||||||
# org.gradle.parallel=true
|
# org.gradle.parallel=true
|
||||||
|
|
||||||
android.useDeprecatedNdk=true
|
android.useDeprecatedNdk=true
|
||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
|
|
@ -2,3 +2,4 @@ import { AppRegistry } from 'react-native';
|
||||||
import bootstrap from './src/main';
|
import bootstrap from './src/main';
|
||||||
|
|
||||||
AppRegistry.registerComponent('ReactNativeFirebaseDemo', () => bootstrap);
|
AppRegistry.registerComponent('ReactNativeFirebaseDemo', () => bootstrap);
|
||||||
|
|
||||||
|
|
|
@ -7,23 +7,11 @@ target 'ReactNativeFirebaseDemo' do
|
||||||
# Uncomment this line if you're using Swift or would like to use dynamic frameworks
|
# Uncomment this line if you're using Swift or would like to use dynamic frameworks
|
||||||
# use_frameworks!
|
# use_frameworks!
|
||||||
|
|
||||||
|
react_native_path = "../node_modules/react-native"
|
||||||
|
pod "Yoga", :path => "#{react_native_path}/ReactCommon/yoga"
|
||||||
|
|
||||||
# Pods for ReactNativeFirebaseDemo
|
# Pods for ReactNativeFirebaseDemo
|
||||||
pod 'React', :path => '../node_modules/react-native', :subspecs => [
|
pod 'React', :path => '../node_modules/react-native'
|
||||||
'Core',
|
|
||||||
'RCTActionSheet',
|
|
||||||
'RCTAnimation',
|
|
||||||
'RCTCameraRoll',
|
|
||||||
'RCTGeolocation',
|
|
||||||
'RCTImage',
|
|
||||||
'RCTLinkingIOS',
|
|
||||||
'RCTNetwork',
|
|
||||||
'RCTPushNotification',
|
|
||||||
'RCTSettings',
|
|
||||||
'RCTText',
|
|
||||||
'RCTVibration',
|
|
||||||
'RCTWebSocket'
|
|
||||||
# Add any other subspecs you want to use in your project
|
|
||||||
]
|
|
||||||
|
|
||||||
pod 'Firebase/Auth'
|
pod 'Firebase/Auth'
|
||||||
pod 'Firebase/Analytics'
|
pod 'Firebase/Analytics'
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
PODS:
|
|
||||||
- Firebase/Analytics (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- Firebase/AppIndexing (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- FirebaseAppIndexing (= 1.2.0)
|
|
||||||
- Firebase/Auth (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- FirebaseAuth (= 3.1.1)
|
|
||||||
- Firebase/Core (3.14.0):
|
|
||||||
- FirebaseAnalytics (= 3.7.0)
|
|
||||||
- FirebaseCore (= 3.5.1)
|
|
||||||
- Firebase/Crash (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- FirebaseCrash (= 1.1.6)
|
|
||||||
- Firebase/Database (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- FirebaseDatabase (= 3.1.2)
|
|
||||||
- Firebase/DynamicLinks (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- FirebaseDynamicLinks (= 1.3.3)
|
|
||||||
- Firebase/Messaging (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- FirebaseMessaging (= 1.2.2)
|
|
||||||
- Firebase/RemoteConfig (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- FirebaseRemoteConfig (= 1.3.4)
|
|
||||||
- Firebase/Storage (3.14.0):
|
|
||||||
- Firebase/Core
|
|
||||||
- FirebaseStorage (= 1.1.0)
|
|
||||||
- FirebaseAnalytics (3.7.0):
|
|
||||||
- FirebaseCore (~> 3.5)
|
|
||||||
- FirebaseInstanceID (~> 1.0)
|
|
||||||
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
|
||||||
- FirebaseAppIndexing (1.2.0)
|
|
||||||
- FirebaseAuth (3.1.1):
|
|
||||||
- FirebaseAnalytics (~> 3.7)
|
|
||||||
- GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)
|
|
||||||
- GTMSessionFetcher/Core (~> 1.1)
|
|
||||||
- FirebaseCore (3.5.1):
|
|
||||||
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
|
||||||
- FirebaseCrash (1.1.6):
|
|
||||||
- FirebaseAnalytics (~> 3.7)
|
|
||||||
- FirebaseInstanceID (~> 1.0)
|
|
||||||
- GoogleToolboxForMac/Logger (~> 2.1)
|
|
||||||
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
|
||||||
- Protobuf (~> 3.1)
|
|
||||||
- FirebaseDatabase (3.1.2):
|
|
||||||
- FirebaseAnalytics (~> 3.7)
|
|
||||||
- FirebaseDynamicLinks (1.3.3):
|
|
||||||
- FirebaseAnalytics (~> 3.7)
|
|
||||||
- FirebaseInstanceID (1.0.9)
|
|
||||||
- FirebaseMessaging (1.2.2):
|
|
||||||
- FirebaseAnalytics (~> 3.7)
|
|
||||||
- FirebaseInstanceID (~> 1.0)
|
|
||||||
- GoogleToolboxForMac/Logger (~> 2.1)
|
|
||||||
- Protobuf (~> 3.1)
|
|
||||||
- FirebaseRemoteConfig (1.3.4):
|
|
||||||
- FirebaseAnalytics (~> 3.7)
|
|
||||||
- FirebaseInstanceID (~> 1.0)
|
|
||||||
- GoogleToolboxForMac/NSData+zlib (~> 2.1)
|
|
||||||
- Protobuf (~> 3.1)
|
|
||||||
- FirebaseStorage (1.1.0):
|
|
||||||
- FirebaseAnalytics (~> 3.7)
|
|
||||||
- GTMSessionFetcher/Core (~> 1.1)
|
|
||||||
- GoogleToolboxForMac/DebugUtils (2.1.1):
|
|
||||||
- GoogleToolboxForMac/Defines (= 2.1.1)
|
|
||||||
- GoogleToolboxForMac/Defines (2.1.1)
|
|
||||||
- GoogleToolboxForMac/Logger (2.1.1):
|
|
||||||
- GoogleToolboxForMac/Defines (= 2.1.1)
|
|
||||||
- GoogleToolboxForMac/NSData+zlib (2.1.1):
|
|
||||||
- GoogleToolboxForMac/Defines (= 2.1.1)
|
|
||||||
- GoogleToolboxForMac/NSDictionary+URLArguments (2.1.1):
|
|
||||||
- GoogleToolboxForMac/DebugUtils (= 2.1.1)
|
|
||||||
- GoogleToolboxForMac/Defines (= 2.1.1)
|
|
||||||
- GoogleToolboxForMac/NSString+URLArguments (= 2.1.1)
|
|
||||||
- GoogleToolboxForMac/NSString+URLArguments (2.1.1)
|
|
||||||
- GTMSessionFetcher/Core (1.1.8)
|
|
||||||
- Protobuf (3.2.0)
|
|
||||||
- React/Core (0.40.0):
|
|
||||||
- React/cxxreact
|
|
||||||
- React/yoga
|
|
||||||
- React/cxxreact (0.40.0):
|
|
||||||
- React/jschelpers
|
|
||||||
- React/jschelpers (0.40.0)
|
|
||||||
- React/RCTActionSheet (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTAnimation (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTCameraRoll (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTImage
|
|
||||||
- React/RCTGeolocation (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTImage (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTNetwork
|
|
||||||
- React/RCTLinkingIOS (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTNetwork (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTPushNotification (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTSettings (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTText (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTVibration (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/RCTWebSocket (0.40.0):
|
|
||||||
- React/Core
|
|
||||||
- React/yoga (0.40.0)
|
|
||||||
- RNFirebase (1.0.0-alpha13)
|
|
||||||
|
|
||||||
DEPENDENCIES:
|
|
||||||
- Firebase/Analytics
|
|
||||||
- Firebase/AppIndexing
|
|
||||||
- Firebase/Auth
|
|
||||||
- Firebase/Core
|
|
||||||
- Firebase/Crash
|
|
||||||
- Firebase/Database
|
|
||||||
- Firebase/DynamicLinks
|
|
||||||
- Firebase/Messaging
|
|
||||||
- Firebase/RemoteConfig
|
|
||||||
- Firebase/Storage
|
|
||||||
- React/Core (from `../node_modules/react-native`)
|
|
||||||
- React/RCTActionSheet (from `../node_modules/react-native`)
|
|
||||||
- React/RCTAnimation (from `../node_modules/react-native`)
|
|
||||||
- React/RCTCameraRoll (from `../node_modules/react-native`)
|
|
||||||
- React/RCTGeolocation (from `../node_modules/react-native`)
|
|
||||||
- React/RCTImage (from `../node_modules/react-native`)
|
|
||||||
- React/RCTLinkingIOS (from `../node_modules/react-native`)
|
|
||||||
- React/RCTNetwork (from `../node_modules/react-native`)
|
|
||||||
- React/RCTPushNotification (from `../node_modules/react-native`)
|
|
||||||
- React/RCTSettings (from `../node_modules/react-native`)
|
|
||||||
- React/RCTText (from `../node_modules/react-native`)
|
|
||||||
- React/RCTVibration (from `../node_modules/react-native`)
|
|
||||||
- React/RCTWebSocket (from `../node_modules/react-native`)
|
|
||||||
- RNFirebase (from `./../../`)
|
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
|
||||||
React:
|
|
||||||
:path: ../node_modules/react-native
|
|
||||||
RNFirebase:
|
|
||||||
:path: ./../../
|
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
|
||||||
Firebase: 85a581fb04e44f63ae9f4fbc8d6dabf4a4c18653
|
|
||||||
FirebaseAnalytics: 0d1b7d81d5021155be37702a94ba1ec16d45365d
|
|
||||||
FirebaseAppIndexing: d0fa52ce0ad13f4b5b2f09e4b47fb0dc2213f4e9
|
|
||||||
FirebaseAuth: cc8a1824170adbd351edb7f994490a3fb5c18be6
|
|
||||||
FirebaseCore: 225d40532489835a034b8f4e2c9c87fbf4f615a2
|
|
||||||
FirebaseCrash: db4c05d9c75baa050744d31b36357c8f1efba481
|
|
||||||
FirebaseDatabase: 05c96d7b43a7368dc91c07791adb49683e1738d1
|
|
||||||
FirebaseDynamicLinks: f0d025dd29a1d70418c003344813b67ab748ffb9
|
|
||||||
FirebaseInstanceID: 2d0518b1378fe9d685ef40cbdd63d2fdc1125339
|
|
||||||
FirebaseMessaging: df8267f378580a24174ce7861233aa11d5c90109
|
|
||||||
FirebaseRemoteConfig: af3003f4e8daa2bd1d5cf90d3cccc1fe224f8ed9
|
|
||||||
FirebaseStorage: a5c55b23741a49a72af8f30f95b3bb5ddbeda12d
|
|
||||||
GoogleToolboxForMac: 8e329f1b599f2512c6b10676d45736bcc2cbbeb0
|
|
||||||
GTMSessionFetcher: 6f8d8b28b7e345549ac471071608170b31cb4977
|
|
||||||
Protobuf: 745f59e122e5de98d4d7ef291e264a0eef80f58e
|
|
||||||
React: 6dfb2f72edb1d74a800127ae157af038646673ce
|
|
||||||
RNFirebase: 46bfe1099349ac6fac8c5e57cf4f0b0f4b7938ac
|
|
||||||
|
|
||||||
PODFILE CHECKSUM: f8bc5de55afd159ec2faf523f1b8e0d861d0832b
|
|
||||||
|
|
||||||
COCOAPODS: 1.2.0
|
|
|
@ -286,8 +286,7 @@ class TestRun {
|
||||||
suiteId: this.testSuite.id,
|
suiteId: this.testSuite.id,
|
||||||
status: RunStatus.ERR,
|
status: RunStatus.ERR,
|
||||||
time: Date.now() - this.runStartTime,
|
time: Date.now() - this.runStartTime,
|
||||||
message: `Test suite failed: ${error.message}`,
|
message: `Test suite failed: ${error.message}`
|
||||||
stackTrace: error.stack,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -306,7 +305,7 @@ class TestRun {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _safelyRunFunction(func, timeOutDuration, description) {
|
async _safelyRunFunction(func, timeOutDuration, description) {
|
||||||
const syncResultOrPromise = tryCatcher(func);
|
const syncResultOrPromise = captureThrownErrors(func);
|
||||||
|
|
||||||
if (syncResultOrPromise.error) {
|
if (syncResultOrPromise.error) {
|
||||||
// Synchronous Error
|
// Synchronous Error
|
||||||
|
@ -314,49 +313,59 @@ class TestRun {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asynchronous Error
|
// Asynchronous Error
|
||||||
return promiseToCallBack(syncResultOrPromise.value, timeOutDuration, description);
|
return capturePromiseErrors(syncResultOrPromise.result, timeOutDuration, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try catch to object
|
* Call a function and capture any errors that are immediately thrown.
|
||||||
* @returns {{}}
|
* @returns {Object} Object containing result of executing the function, or the error
|
||||||
|
* message that was captured
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function tryCatcher(func) {
|
function captureThrownErrors(func) {
|
||||||
const result = {};
|
const result = {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result.value = func();
|
result.result = func();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
result.error = e;
|
result.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a promise callback-able to trap errors
|
* Wraps a promise so that if it's rejected or an error is thrown while it's being
|
||||||
* @param promise
|
* evaluated, it's captured and thrown no further
|
||||||
|
* @param {*} target - Target to wrap. If a thenable object, it's wrapped so if it's
|
||||||
|
* rejected or an error is thrown, it will be captured. If a non-thenable object,
|
||||||
|
* wrapped in resolved promise and returned.
|
||||||
|
* @param {Number} timeoutDuration - Number of milliseconds the promise is allowed
|
||||||
|
* to pend before it's considered timed out
|
||||||
|
* @param {String} description - Description of the context the promises is defined
|
||||||
|
* in, used for reporting where a timeout occurred in the resulting error message.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function promiseToCallBack(promise, timeoutDuration, description) {
|
function capturePromiseErrors(target, timeoutDuration, description) {
|
||||||
let returnValue = null;
|
let returnValue = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
returnValue = Promise.resolve(promise)
|
returnValue = Promise.resolve(target)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return null;
|
return null;
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
return Promise.resolve(error);
|
return Promise.resolve(error);
|
||||||
})
|
})
|
||||||
.timeout(timeoutDuration, `${description} took longer than ${timeoutDuration}ms. This can be extended with the timeout option.`)
|
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
return Promise.resolve(error);
|
return Promise.resolve(error);
|
||||||
});
|
})
|
||||||
|
.timeout(timeoutDuration,
|
||||||
|
`${description} took longer than ${timeoutDuration}ms. This can be extended with the timeout option.`
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
returnValue = Promise.resolve(error);
|
returnValue = Promise.resolve(error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,19 +111,19 @@ class TestSuite {
|
||||||
*/
|
*/
|
||||||
async run(testIds = undefined) {
|
async run(testIds = undefined) {
|
||||||
const testsToRun = (() => {
|
const testsToRun = (() => {
|
||||||
if (testIds) {
|
return (testIds || Object.keys(this.testDefinitions.tests)).reduce((memo, id) => {
|
||||||
return testIds.map((id) => {
|
|
||||||
const test = this.testDefinitions.tests[id];
|
const test = this.testDefinitions.tests[id];
|
||||||
|
|
||||||
if (!test) {
|
if (!test) {
|
||||||
throw new RangeError(`ReactNativeFirebaseTests.TestRunError: Test with id ${id} not found in test suite ${this.name}`);
|
throw new RangeError(`ReactNativeFirebaseTests.TestRunError: Test with id ${id} not found in test suite ${this.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return test;
|
if (!this.testDefinitions.pendingTestIds[id]) {
|
||||||
});
|
memo.push(test);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.values(this.testDefinitions.tests);
|
return memo;
|
||||||
|
}, []);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const testRun = new TestRun(this, testsToRun.reverse(), this.testDefinitions);
|
const testRun = new TestRun(this, testsToRun.reverse(), this.testDefinitions);
|
||||||
|
|
|
@ -32,12 +32,12 @@
|
||||||
"js-beautify": "^1.6.11",
|
"js-beautify": "^1.6.11",
|
||||||
"lodash.groupby": "^4.6.0",
|
"lodash.groupby": "^4.6.0",
|
||||||
"lodash.some": "^4.6.0",
|
"lodash.some": "^4.6.0",
|
||||||
"react": "~15.4.1",
|
"react": "16.0.0-alpha.6",
|
||||||
"react-native": "0.40.0",
|
"react-native": "^0.44.0",
|
||||||
"react-native-firebase": "file:..",
|
"react-native-firebase": "file:..",
|
||||||
"react-native-simple-toast": "0.0.5",
|
"react-native-simple-toast": "0.0.5",
|
||||||
"react-native-vector-icons": "^4.0.0",
|
"react-native-vector-icons": "^4.0.0",
|
||||||
"react-navigation": "^1.0.0-beta.7",
|
"react-navigation": "^1.0.0-beta.9",
|
||||||
"react-redux": "^5.0.3",
|
"react-redux": "^5.0.3",
|
||||||
"redux": "^3.6.0",
|
"redux": "^3.6.0",
|
||||||
"redux-logger": "^2.8.2",
|
"redux-logger": "^2.8.2",
|
||||||
|
|
|
@ -14,13 +14,14 @@ export function setSuiteStatus({ suiteId, status, time, message, progress }) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setTestStatus({ testId, status, time = 0, message = null }) {
|
export function setTestStatus({ testId, status, stackTrace, time = 0, message = null }) {
|
||||||
return {
|
return {
|
||||||
type: TEST_SET_STATUS,
|
type: TEST_SET_STATUS,
|
||||||
testId,
|
testId,
|
||||||
|
|
||||||
status,
|
status,
|
||||||
message,
|
message,
|
||||||
|
stackTrace,
|
||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ function testsReducers(state = initState.tests, action: Object): State {
|
||||||
flattened[`${action.testId}.status`] = action.status;
|
flattened[`${action.testId}.status`] = action.status;
|
||||||
flattened[`${action.testId}.message`] = action.message;
|
flattened[`${action.testId}.message`] = action.message;
|
||||||
flattened[`${action.testId}.time`] = action.time;
|
flattened[`${action.testId}.time`] = action.time;
|
||||||
|
flattened[`${action.testId}.stackTrace`] = action.stackTrace;
|
||||||
|
|
||||||
return unflatten(flattened);
|
return unflatten(flattened);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,17 +13,11 @@ class Overview extends React.Component {
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols
|
||||||
static navigationOptions = {
|
static navigationOptions = {
|
||||||
title: 'Test Suites',
|
title: 'Test Suites',
|
||||||
header: () => {
|
headerTintColor: '#ffffff',
|
||||||
return {
|
headerStyle: { backgroundColor: '#0288d1' },
|
||||||
style: { backgroundColor: '#0288d1' },
|
headerRight: <View style={{ marginRight: 8 }}>
|
||||||
tintColor: '#ffffff',
|
|
||||||
right: (
|
|
||||||
<View style={{ marginRight: 8 }}>
|
|
||||||
<OverviewControlButton />
|
<OverviewControlButton />
|
||||||
</View>
|
</View>
|
||||||
),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,25 +11,19 @@ import TestSuiteControlButton from '../components/TestSuiteControlButton';
|
||||||
|
|
||||||
class Suite extends React.Component {
|
class Suite extends React.Component {
|
||||||
|
|
||||||
static navigationOptions = {
|
static navigationOptions = ({ navigation: { state: { params: { title, testSuiteId, onlyShowFailingTests } }, setParams } }) => {
|
||||||
title: ({ state: { params: { title } } }) => {
|
|
||||||
return title;
|
|
||||||
},
|
|
||||||
header: ({ state: { params: { testSuiteId, onlyShowFailingTests } }, setParams }) => {
|
|
||||||
return {
|
return {
|
||||||
style: { backgroundColor: '#0288d1' },
|
title,
|
||||||
tintColor: '#ffffff',
|
headerTintColor: '#ffffff',
|
||||||
right: (
|
headerStyle: { backgroundColor: '#0288d1' },
|
||||||
<View style={{ flexDirection: 'row', marginRight: 8 }}>
|
headerRight: <View style={{ flexDirection: 'row', marginRight: 8 }}>
|
||||||
<TestSuiteControlButton
|
<TestSuiteControlButton
|
||||||
testSuiteId={testSuiteId}
|
testSuiteId={testSuiteId}
|
||||||
onlyShowFailingTests={onlyShowFailingTests}
|
onlyShowFailingTests={onlyShowFailingTests}
|
||||||
onFilterChange={setParams}
|
onFilterChange={setParams}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>,
|
||||||
),
|
|
||||||
};
|
};
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,21 +9,15 @@ import TestControlButton from '../components/TestControlButton';
|
||||||
|
|
||||||
class Test extends React.Component {
|
class Test extends React.Component {
|
||||||
|
|
||||||
static navigationOptions = {
|
static navigationOptions = ({ navigation: { state: { params: { title, testId } } } }) => {
|
||||||
title: ({ state: { params: { title } } }) => {
|
|
||||||
return title;
|
|
||||||
},
|
|
||||||
header: ({ state: { params: { testId } } }) => {
|
|
||||||
return {
|
return {
|
||||||
style: { backgroundColor: '#0288d1' },
|
title,
|
||||||
tintColor: '#ffffff',
|
headerTintColor: '#ffffff',
|
||||||
right: (
|
headerStyle: { backgroundColor: '#0288d1' },
|
||||||
<View style={{ marginRight: 8 }}>
|
headerRight: <View style={{ marginRight: 8 }}>
|
||||||
<TestControlButton testId={testId} />
|
<TestControlButton testId={testId} />
|
||||||
</View>
|
</View>,
|
||||||
),
|
|
||||||
};
|
};
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static renderBanner({ status, time }) {
|
static renderBanner({ status, time }) {
|
||||||
|
@ -57,35 +51,28 @@ class Test extends React.Component {
|
||||||
setParams({ test });
|
setParams({ test });
|
||||||
}
|
}
|
||||||
|
|
||||||
renderError() {
|
|
||||||
const { test: { message } } = this.props;
|
|
||||||
|
|
||||||
if (message) {
|
|
||||||
return (
|
|
||||||
<ScrollView>
|
|
||||||
<Text style={styles.codeHeader}>Test Error</Text>
|
|
||||||
<Text style={styles.code}>
|
|
||||||
<Text>{message}</Text>
|
|
||||||
</Text>
|
|
||||||
</ScrollView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { test: { func, status, time } } = this.props;
|
const { test: { stackTrace, description, func, status, time }, testContextName } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{Test.renderBanner({ status, time })}
|
{Test.renderBanner({ status, time })}
|
||||||
<View style={styles.content}>
|
<View >
|
||||||
{this.renderError()}
|
<ScrollView style={styles.sectionContainer}>
|
||||||
<Text style={styles.codeHeader}>Test Code Preview</Text>
|
<Text style={styles.heading}>{testContextName}</Text>
|
||||||
<ScrollView>
|
<Text style={styles.description}>{description}</Text>
|
||||||
<Text style={styles.code}>
|
</ScrollView>
|
||||||
|
<ScrollView style={styles.sectionContainer}>
|
||||||
|
<Text style={styles.heading}>Test Error</Text>
|
||||||
|
<Text style={styles.description}>
|
||||||
|
<Text>{stackTrace || 'None.'}</Text>
|
||||||
|
</Text>
|
||||||
|
</ScrollView>
|
||||||
|
<Text style={styles.heading}>
|
||||||
|
Test Code Preview
|
||||||
|
</Text>
|
||||||
|
<ScrollView style={styles.sectionContainer}>
|
||||||
|
<Text style={styles.description}>
|
||||||
{beautify(removeLastLine(removeFirstLine(func.toString())), { indent_size: 4, break_chained_methods: true })}
|
{beautify(removeLastLine(removeFirstLine(func.toString())), { indent_size: 4, break_chained_methods: true })}
|
||||||
</Text>
|
</Text>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
@ -99,10 +86,13 @@ Test.propTypes = {
|
||||||
test: PropTypes.shape({
|
test: PropTypes.shape({
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
time: PropTypes.number,
|
time: PropTypes.number,
|
||||||
message: PropTypes.string,
|
|
||||||
func: PropTypes.function,
|
func: PropTypes.function,
|
||||||
|
stackTrace: PropTypes.function,
|
||||||
|
description: PropTypes.string,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
|
||||||
|
testContextName: PropTypes.string,
|
||||||
|
|
||||||
navigation: PropTypes.shape({
|
navigation: PropTypes.shape({
|
||||||
setParams: PropTypes.func.isRequired,
|
setParams: PropTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
@ -113,27 +103,32 @@ const styles = StyleSheet.create({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: '#ffffff',
|
backgroundColor: '#ffffff',
|
||||||
},
|
},
|
||||||
content: {},
|
sectionContainer: {
|
||||||
code: {
|
minHeight: 100,
|
||||||
backgroundColor: '#3F373A',
|
|
||||||
color: '#c3c3c3',
|
|
||||||
padding: 5,
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
},
|
||||||
codeHeader: {
|
heading: {
|
||||||
fontWeight: '600',
|
|
||||||
fontSize: 18,
|
|
||||||
backgroundColor: '#000',
|
|
||||||
color: '#fff',
|
|
||||||
padding: 5,
|
padding: 5,
|
||||||
|
backgroundColor: '#0288d1',
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#ffffff',
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
padding: 5,
|
||||||
|
fontSize: 14,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function select({ tests }, { navigation: { state: { params: { testId } } } }) {
|
function select({ tests, testContexts }, { navigation: { state: { params: { testId } } } }) {
|
||||||
const test = tests[testId];
|
const test = tests[testId];
|
||||||
|
let testContext = testContexts[test.testContextId];
|
||||||
|
|
||||||
|
while(testContext.parentContextId && testContexts[testContext.parentContextId].parentContextId) {
|
||||||
|
testContext = testContexts[testContext.parentContextId];
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
test,
|
test,
|
||||||
|
testContextName: testContext.name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import onTests from './onTests';
|
import onTests from './on/onTests';
|
||||||
|
import onValueTests from './on/onValueTests';
|
||||||
import offTests from './offTests';
|
import offTests from './offTests';
|
||||||
import onceTests from './onceTests';
|
import onceTests from './onceTests';
|
||||||
import setTests from './setTests';
|
import setTests from './setTests';
|
||||||
|
@ -14,13 +15,14 @@ import refTests from './refTests';
|
||||||
import rootTests from './rootTests';
|
import rootTests from './rootTests';
|
||||||
import transactionTests from './transactionTests';
|
import transactionTests from './transactionTests';
|
||||||
import queryTests from './queryTests';
|
import queryTests from './queryTests';
|
||||||
|
import issueSpecificTests from './issueSpecificTests';
|
||||||
|
|
||||||
import DatabaseContents from '../../support/DatabaseContents';
|
import DatabaseContents from '../../support/DatabaseContents';
|
||||||
|
|
||||||
const testGroups = [
|
const testGroups = [
|
||||||
factoryTests, keyTests, parentTests, childTests, rootTests,
|
issueSpecificTests, factoryTests, keyTests, parentTests, childTests, rootTests,
|
||||||
pushTests, onTests, offTests, onceTests, updateTests, removeTests, setTests,
|
pushTests, onTests, onValueTests, offTests, onceTests, updateTests,
|
||||||
transactionTests, queryTests, refTests, isEqualTests,
|
removeTests, setTests, transactionTests, queryTests, refTests, isEqualTests,
|
||||||
];
|
];
|
||||||
|
|
||||||
function registerTestSuite(testSuite) {
|
function registerTestSuite(testSuite) {
|
||||||
|
@ -28,10 +30,12 @@ function registerTestSuite(testSuite) {
|
||||||
this._databaseRef = testSuite.firebase.native.database().ref('tests/types');
|
this._databaseRef = testSuite.firebase.native.database().ref('tests/types');
|
||||||
|
|
||||||
await this._databaseRef.set(DatabaseContents.DEFAULT);
|
await this._databaseRef.set(DatabaseContents.DEFAULT);
|
||||||
|
await this._databaseRef.parent.child('issues').set(DatabaseContents.ISSUES);
|
||||||
});
|
});
|
||||||
|
|
||||||
testSuite.afterEach(async function () {
|
testSuite.afterEach(async function () {
|
||||||
await this._databaseRef.set(DatabaseContents.DEFAULT);
|
await this._databaseRef.set(DatabaseContents.DEFAULT);
|
||||||
|
await this._databaseRef.parent.child('issues').set(DatabaseContents.ISSUES);
|
||||||
});
|
});
|
||||||
|
|
||||||
testGroups.forEach((testGroup) => {
|
testGroups.forEach((testGroup) => {
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import should from 'should';
|
||||||
|
import DatabaseContents from '../../support/DatabaseContents';
|
||||||
|
|
||||||
|
function issueTests({ fdescribe, describe, it, context, firebase }) {
|
||||||
|
describe('issue_100', () => {
|
||||||
|
context('array-like values should', () => {
|
||||||
|
it('return null in returned array at positions where a key is missing', async() => {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
const ref = firebase.native.database().ref('tests/issues/100');
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
return ref.once('value').then((snapshot) => {
|
||||||
|
// Assertion
|
||||||
|
// console.warn(JSON.stringify(snapshot.val()));
|
||||||
|
|
||||||
|
snapshot.val().should.eql([null, DatabaseContents.ISSUES[100][1], DatabaseContents.ISSUES[100][2], DatabaseContents.ISSUES[100][3]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('issue_108', () => {
|
||||||
|
context('filters using floats', () => {
|
||||||
|
it('return correct results', async() => {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
const ref = firebase.native.database().ref('tests/issues/108');
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
return ref
|
||||||
|
.orderByChild('latitude')
|
||||||
|
.startAt(34.00867000999119)
|
||||||
|
.endAt(34.17462960866099)
|
||||||
|
.once('value')
|
||||||
|
.then((snapshot) => {
|
||||||
|
const val = snapshot.val();
|
||||||
|
// Assertion
|
||||||
|
val.foobar.should.eql(DatabaseContents.ISSUES[108].foobar);
|
||||||
|
should.equal(Object.keys(val).length, 1);
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('return correct results when not using float values', async() => {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
const ref = firebase.native.database().ref('tests/issues/108');
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
return ref
|
||||||
|
.orderByChild('latitude')
|
||||||
|
.equalTo(37)
|
||||||
|
.once('value')
|
||||||
|
.then((snapshot) => {
|
||||||
|
const val = snapshot.val();
|
||||||
|
|
||||||
|
// Assertion
|
||||||
|
|
||||||
|
val.notAFloat.should.eql(DatabaseContents.ISSUES[108].notAFloat);
|
||||||
|
should.equal(Object.keys(val).length, 1);
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default issueTests;
|
|
@ -0,0 +1,52 @@
|
||||||
|
import 'should-sinon';
|
||||||
|
|
||||||
|
function onTests({ describe, it, firebase, context }) {
|
||||||
|
describe('ref().on()', () => {
|
||||||
|
// Observed Web API Behaviour
|
||||||
|
context('when no eventName is provided', () => {
|
||||||
|
it('then raises an error', () => {
|
||||||
|
const ref = firebase.native.database().ref('tests/types/number');
|
||||||
|
|
||||||
|
(() => { ref.on(); }).should.throw('Error: Query on failed: Was called with 0 arguments. Expects at least 2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Observed Web API Behaviour
|
||||||
|
context('when no callback function is provided', () => {
|
||||||
|
it('then raises an error', () => {
|
||||||
|
const ref = firebase.native.database().ref('tests/types/number');
|
||||||
|
|
||||||
|
(() => { ref.on('value'); }).should.throw('Query.on failed: Was called with 1 argument. Expects at least 2.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Observed Web API Behaviour
|
||||||
|
context('when an invalid eventName is provided', () => {
|
||||||
|
it('then raises an error', () => {
|
||||||
|
const ref = firebase.native.database().ref('tests/types/number');
|
||||||
|
|
||||||
|
(() => { ref.on('invalid', () => {}); }).should.throw('Query.on failed: First argument must be a valid event type: "value", "child_added", "child_removed", "child_changed", or "child_moved".');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Observed Web API Behaviour
|
||||||
|
context('when an invalid success callback function is provided', () => {
|
||||||
|
it('then raises an error', () => {
|
||||||
|
const ref = firebase.native.database().ref('tests/types/number');
|
||||||
|
|
||||||
|
(() => { ref.on('value', 1); }).should.throw('Query.on failed: Second argument must be a valid function.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Observed Web API Behaviour
|
||||||
|
context('when an invalid failure callback function is provided', () => {
|
||||||
|
it('then raises an error', () => {
|
||||||
|
const ref = firebase.native.database().ref('tests/types/number');
|
||||||
|
|
||||||
|
(() => { ref.on('value', () => {}, null); }).should.throw('Query.on failed: third argument must either be a cancel callback or a context object.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default onTests;
|
|
@ -0,0 +1,409 @@
|
||||||
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
|
import sinon from 'sinon';
|
||||||
|
import 'should-sinon';
|
||||||
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
|
import DatabaseContents from '../../../support/DatabaseContents';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On Android, some data types result in callbacks that get called twice every time
|
||||||
|
* they are updated. This appears to be behaviour coming from the Android Firebase
|
||||||
|
* library itself.
|
||||||
|
*
|
||||||
|
* See https://github.com/invertase/react-native-firebase/issues/92 for details
|
||||||
|
*/
|
||||||
|
const DATATYPES_WITH_DUPLICATE_CALLBACK_CALLS = [
|
||||||
|
'array',
|
||||||
|
'number',
|
||||||
|
];
|
||||||
|
|
||||||
|
function onTests({ describe, context, it, firebase, tryCatch }) {
|
||||||
|
describe('ref().on(\'value\')', () => {
|
||||||
|
// Documented Web API Behaviour
|
||||||
|
it('returns the success callback', () => {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
const successCallback = sinon.spy();
|
||||||
|
const ref = firebase.native.database().ref('tests/types/array');
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
ref.on('value', successCallback).should.eql(successCallback);
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
ref.off();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Documented Web API Behaviour
|
||||||
|
it('calls callback with null if there is no data at ref', async () => {
|
||||||
|
// Setup
|
||||||
|
const ref = firebase.native.database().ref('tests/types/invalid');
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', (snapshot) => {
|
||||||
|
callback(snapshot.val());
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(null);
|
||||||
|
|
||||||
|
await ref.set(1);
|
||||||
|
|
||||||
|
callback.should.be.calledWith(1);
|
||||||
|
|
||||||
|
// Teardown
|
||||||
|
ref.off();
|
||||||
|
await ref.set(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Documented Web API Behaviour
|
||||||
|
it('calls callback with the initial data and then when value changes', () => {
|
||||||
|
return Promise.each(Object.keys(DatabaseContents.DEFAULT), async (dataRef) => {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
const ref = firebase.native.database().ref(`tests/types/${dataRef}`);
|
||||||
|
const currentDataValue = DatabaseContents.DEFAULT[dataRef];
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', (snapshot) => {
|
||||||
|
callback(snapshot.val());
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
const newDataValue = DatabaseContents.NEW[dataRef];
|
||||||
|
await ref.set(newDataValue);
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDataValue);
|
||||||
|
|
||||||
|
if (Platform.OS === 'android' && DATATYPES_WITH_DUPLICATE_CALLBACK_CALLS.includes(dataRef)) {
|
||||||
|
callback.should.be.calledThrice();
|
||||||
|
} else {
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
ref.off();
|
||||||
|
await ref.set(currentDataValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls callback when children of the ref change', async () => {
|
||||||
|
const ref = firebase.native.database().ref('tests/types/object');
|
||||||
|
const currentDataValue = DatabaseContents.DEFAULT.object;
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', (snapshot) => {
|
||||||
|
callback(snapshot.val());
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
const newDataValue = DatabaseContents.NEW.string;
|
||||||
|
const childRef = firebase.native.database().ref('tests/types/object/foo2');
|
||||||
|
await childRef.set(newDataValue);
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith({
|
||||||
|
...currentDataValue,
|
||||||
|
foo2: newDataValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
ref.off();
|
||||||
|
await ref.set(currentDataValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls callback when child of the ref is added', async () => {
|
||||||
|
const ref = firebase.native.database().ref('tests/types/array');
|
||||||
|
const currentDataValue = DatabaseContents.DEFAULT.array;
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', (snapshot) => {
|
||||||
|
callback(snapshot.val());
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
const newElementRef = await ref.push(37);
|
||||||
|
|
||||||
|
const arrayAsObject = currentDataValue.reduce((memo, element, index) => {
|
||||||
|
memo[index] = element;
|
||||||
|
return memo;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
callback.should.be.calledWith({
|
||||||
|
...arrayAsObject,
|
||||||
|
[newElementRef.key]: 37,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Platform.OS === 'android') {
|
||||||
|
callback.should.be.calledThrice();
|
||||||
|
} else {
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
ref.off();
|
||||||
|
await ref.set(currentDataValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
||||||
|
const ref = firebase.native.database().ref('tests/types/object');
|
||||||
|
const currentDataValue = DatabaseContents.DEFAULT.object;
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', (snapshot) => {
|
||||||
|
callback(snapshot.val());
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
await ref.set(currentDataValue);
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledOnce(); // Callback is not called again
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
ref.off();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Documented Web API Behaviour
|
||||||
|
it('allows binding multiple callbacks to the same ref', () => {
|
||||||
|
return Promise.each(Object.keys(DatabaseContents.DEFAULT), async (dataRef) => {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
const ref = firebase.native.database().ref(`tests/types/${dataRef}`);
|
||||||
|
const currentDataValue = DatabaseContents.DEFAULT[dataRef];
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', (snapshot) => {
|
||||||
|
callbackA(snapshot.val());
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', (snapshot) => {
|
||||||
|
callbackB(snapshot.val());
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDataValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDataValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
const newDataValue = DatabaseContents.NEW[dataRef];
|
||||||
|
await ref.set(newDataValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDataValue);
|
||||||
|
callbackB.should.be.calledWith(newDataValue);
|
||||||
|
|
||||||
|
if (Platform.OS === 'android' && DATATYPES_WITH_DUPLICATE_CALLBACK_CALLS.includes(dataRef)) {
|
||||||
|
callbackA.should.be.calledThrice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
} else {
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
ref.off();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('when no failure callback is provided', () => {
|
||||||
|
it('then does not call the callback for a ref to un-permitted location', () => {
|
||||||
|
const invalidRef = firebase.native.database().ref('nope');
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
invalidRef.on('value', callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As we are testing that a callback is "never" called, we just wait for
|
||||||
|
* a reasonable time before giving up.
|
||||||
|
*/
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
callback.should.not.be.called();
|
||||||
|
invalidRef.off();
|
||||||
|
resolve();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Documented Web API Behaviour
|
||||||
|
it('then calls callback bound to the specified context with the initial data and then when value changes', () => {
|
||||||
|
return Promise.each(Object.keys(DatabaseContents.DEFAULT), async (dataRef) => {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
const ref = firebase.native.database().ref(`tests/types/${dataRef}`);
|
||||||
|
const currentDataValue = DatabaseContents.DEFAULT[dataRef];
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
callCount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', function(snapshot) {
|
||||||
|
this.value = snapshot.val();
|
||||||
|
this.callCount += 1;
|
||||||
|
resolve();
|
||||||
|
}, context);
|
||||||
|
});
|
||||||
|
|
||||||
|
context.value.should.eql(currentDataValue);
|
||||||
|
context.callCount.should.eql(1);
|
||||||
|
|
||||||
|
const newDataValue = DatabaseContents.NEW[dataRef];
|
||||||
|
await ref.set(newDataValue);
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
context.value.should.eql(newDataValue);
|
||||||
|
|
||||||
|
if (Platform.OS === 'android' && DATATYPES_WITH_DUPLICATE_CALLBACK_CALLS.includes(dataRef)) {
|
||||||
|
context.callCount.should.eql(3);
|
||||||
|
} else {
|
||||||
|
context.callCount.should.eql(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
ref.off();
|
||||||
|
await ref.set(currentDataValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Observed Web API Behaviour
|
||||||
|
context('when a failure callback is provided', () => {
|
||||||
|
it('then calls only the failure callback for a ref to un-permitted location', () => {
|
||||||
|
const invalidRef = firebase.native.database().ref('nope');
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
invalidRef.on('value', callback, tryCatch((error) => {
|
||||||
|
error.message.should.eql(
|
||||||
|
'permission_denied at /nope: Client doesn\'t have permission to access the desired data.'
|
||||||
|
);
|
||||||
|
error.name.should.eql('Error');
|
||||||
|
|
||||||
|
callback.should.not.be.called();
|
||||||
|
|
||||||
|
invalidRef.off();
|
||||||
|
resolve();
|
||||||
|
}, reject));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Documented Web API Behaviour
|
||||||
|
it('then calls callback bound to the specified context with the initial data and then when value changes', () => {
|
||||||
|
return Promise.each(Object.keys(DatabaseContents.DEFAULT), async (dataRef) => {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
const ref = firebase.native.database().ref(`tests/types/${dataRef}`);
|
||||||
|
const currentDataValue = DatabaseContents.DEFAULT[dataRef];
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
callCount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const failureCallback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ref.on('value', function(snapshot) {
|
||||||
|
this.value = snapshot.val();
|
||||||
|
this.callCount += 1;
|
||||||
|
resolve();
|
||||||
|
}, failureCallback, context);
|
||||||
|
});
|
||||||
|
|
||||||
|
failureCallback.should.not.be.called();
|
||||||
|
context.value.should.eql(currentDataValue);
|
||||||
|
context.callCount.should.eql(1);
|
||||||
|
|
||||||
|
const newDataValue = DatabaseContents.NEW[dataRef];
|
||||||
|
await ref.set(newDataValue);
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
context.value.should.eql(newDataValue);
|
||||||
|
|
||||||
|
if (Platform.OS === 'android' && DATATYPES_WITH_DUPLICATE_CALLBACK_CALLS.includes(dataRef)) {
|
||||||
|
context.callCount.should.eql(3);
|
||||||
|
} else {
|
||||||
|
context.callCount.should.eql(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
ref.off();
|
||||||
|
await ref.set(currentDataValue);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default onTests;
|
|
@ -1,125 +0,0 @@
|
||||||
import sinon from 'sinon';
|
|
||||||
import 'should-sinon';
|
|
||||||
import Promise from 'bluebird';
|
|
||||||
|
|
||||||
import DatabaseContents from '../../support/DatabaseContents';
|
|
||||||
|
|
||||||
function onTests({ describe, it, firebase, tryCatch }) {
|
|
||||||
describe('ref().on()', () => {
|
|
||||||
it('calls callback when value changes', () => {
|
|
||||||
return Promise.each(Object.keys(DatabaseContents.DEFAULT), async (dataRef) => {
|
|
||||||
// Setup
|
|
||||||
|
|
||||||
const ref = firebase.native.database().ref(`tests/types/${dataRef}`);
|
|
||||||
const currentDataValue = DatabaseContents.DEFAULT[dataRef];
|
|
||||||
|
|
||||||
const callback = sinon.spy();
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
ref.on('value', (snapshot) => {
|
|
||||||
callback(snapshot.val());
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
callback.should.be.calledWith(currentDataValue);
|
|
||||||
|
|
||||||
const newDataValue = DatabaseContents.NEW[dataRef];
|
|
||||||
await ref.set(newDataValue);
|
|
||||||
|
|
||||||
// Assertions
|
|
||||||
|
|
||||||
callback.should.be.calledWith(newDataValue);
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
ref.off();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows binding multiple callbacks to the same ref', () => {
|
|
||||||
return Promise.each(Object.keys(DatabaseContents.DEFAULT), async (dataRef) => {
|
|
||||||
// Setup
|
|
||||||
|
|
||||||
const ref = firebase.native.database().ref(`tests/types/${dataRef}`);
|
|
||||||
const currentDataValue = DatabaseContents.DEFAULT[dataRef];
|
|
||||||
|
|
||||||
const callbackA = sinon.spy();
|
|
||||||
const callbackB = sinon.spy();
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
ref.on('value', (snapshot) => {
|
|
||||||
callbackA(snapshot.val());
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
ref.on('value', (snapshot) => {
|
|
||||||
callbackB(snapshot.val());
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(currentDataValue);
|
|
||||||
callbackB.should.be.calledWith(currentDataValue);
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
ref.off();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls callback with current values', () => {
|
|
||||||
return Promise.each(Object.keys(DatabaseContents.DEFAULT), (dataRef) => {
|
|
||||||
// Setup
|
|
||||||
|
|
||||||
const dataTypeValue = DatabaseContents.DEFAULT[dataRef];
|
|
||||||
const ref = firebase.native.database().ref(`tests/types/${dataRef}`);
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
return ref.on('value', (snapshot) => {
|
|
||||||
// Assertion
|
|
||||||
|
|
||||||
snapshot.val().should.eql(dataTypeValue);
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
ref.off();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('errors if permission denied', () => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const successCb = tryCatch(() => {
|
|
||||||
// Assertion
|
|
||||||
|
|
||||||
reject(new Error('No permission denied error'));
|
|
||||||
}, reject);
|
|
||||||
|
|
||||||
const failureCb = tryCatch((error) => {
|
|
||||||
// Assertion
|
|
||||||
|
|
||||||
error.message.includes('permission_denied').should.be.true();
|
|
||||||
resolve();
|
|
||||||
}, reject);
|
|
||||||
|
|
||||||
// Setup
|
|
||||||
|
|
||||||
const invalidRef = firebase.native.database().ref('nope');
|
|
||||||
|
|
||||||
// Test
|
|
||||||
|
|
||||||
invalidRef.on('value', successCb, failureCb);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export default onTests;
|
|
|
@ -22,4 +22,38 @@ export default {
|
||||||
foo: 'baz',
|
foo: 'baz',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ISSUES: {
|
||||||
|
// https://github.com/invertase/react-native-firebase/issues/100
|
||||||
|
100: {
|
||||||
|
1: {
|
||||||
|
someKey: 'someValue',
|
||||||
|
someOtherKey: 'someOtherValue',
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
someKey: 'someValue',
|
||||||
|
someOtherKey: 'someOtherValue',
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
someKey: 'someValue',
|
||||||
|
someOtherKey: 'someOtherValue',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://github.com/invertase/react-native-firebase/issues/108
|
||||||
|
108: {
|
||||||
|
foobar: {
|
||||||
|
name: 'Foobar Pizzas',
|
||||||
|
latitude: 34.1013717,
|
||||||
|
},
|
||||||
|
notTheFoobar: {
|
||||||
|
name: 'Not the pizza you\'re looking for',
|
||||||
|
latitude: 34.456787,
|
||||||
|
},
|
||||||
|
notAFloat: {
|
||||||
|
name: 'Not a float',
|
||||||
|
latitude: 37,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue