Merge branch 'master' of github.com:invertase/react-native-firebase

This commit is contained in:
Elliot Hesp 2017-07-06 10:22:23 +01:00
commit d4328f3adb
21 changed files with 700 additions and 487 deletions

View File

@ -54,6 +54,7 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
| Performance Monitoring | ✅ | ✅ | ❌ |
| Realtime Database | ✅ | ✅ | ✅ |
| - Offline Persistance | ✅ | ✅ | ❌ |
| - Transactions | ✅ | ✅ | ✅ |
| Remote Config | ✅ | ✅ | ❌ |
| Storage | ✅ | ✅ | ❌ |

View File

@ -23,6 +23,7 @@ import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.ActionCodeResult;
import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
@ -502,6 +503,93 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule {
}
}
/**
* confirmPasswordReset
*
* @param code
* @param newPassword
* @param promise
*/
@ReactMethod
public void confirmPasswordReset(String code, String newPassword, final Promise promise) {
Log.d(TAG, "confirmPasswordReset");
mAuth.confirmPasswordReset(code, newPassword)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "confirmPasswordReset:onComplete:success");
promiseNoUser(promise, false);
} else {
Exception exception = task.getException();
Log.e(TAG, "confirmPasswordReset:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
}
});
}
/**
* applyActionCode
*
* @param code
* @param promise
*/
@ReactMethod
public void applyActionCode(String code, final Promise promise) {
Log.d(TAG, "applyActionCode");
mAuth.applyActionCode(code).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "applyActionCode:onComplete:success");
promiseNoUser(promise, false);
} else {
Exception exception = task.getException();
Log.e(TAG, "applyActionCode:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
}
});
}
/**
* @param code
* @param promise
*/
@ReactMethod
public void checkActionCode(String code, final Promise promise) {
Log.d(TAG, "checkActionCode");
mAuth.checkActionCode(code).addOnCompleteListener(new OnCompleteListener<ActionCodeResult>() {
@Override
public void onComplete(@NonNull Task<ActionCodeResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "checkActionCode:onComplete:success");
ActionCodeResult result = task.getResult();
WritableMap writableMap = Arguments.createMap();
WritableMap dataMap = Arguments.createMap();
dataMap.putString("email", result.getData(ActionCodeResult.EMAIL));
dataMap.putString("fromEmail", result.getData(ActionCodeResult.FROM_EMAIL));
writableMap.putMap("data", dataMap);
// TODO figure out if these are required - web sdk only returns the 'email' and nothing else
// writableMap.putString("error", result.getData(ActionCodeResult.ERROR));
// writableMap.putString("verifyEmail", result.getData(ActionCodeResult.VERIFY_EMAIL));
// writableMap.putString("recoverEmail", result.getData(ActionCodeResult.RECOVER_EMAIL));
// writableMap.putString("passwordReset", result.getData(ActionCodeResult.PASSWORD_RESET));
promise.resolve(writableMap);
} else {
Exception exception = task.getException();
Log.e(TAG, "checkActionCode:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
}
});
}
/**
* link
*
@ -645,28 +733,28 @@ public class RNFirebaseAuth extends ReactContextBaseJavaModule {
Log.d(TAG, "fetchProvidersForEmail");
mAuth.fetchProvidersForEmail(email)
.addOnCompleteListener(new OnCompleteListener<ProviderQueryResult>() {
@Override
public void onComplete(@NonNull Task<ProviderQueryResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "fetchProvidersForEmail:onComplete:success");
List<String> providers = task.getResult().getProviders();
WritableArray array = Arguments.createArray();
.addOnCompleteListener(new OnCompleteListener<ProviderQueryResult>() {
@Override
public void onComplete(@NonNull Task<ProviderQueryResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "fetchProvidersForEmail:onComplete:success");
List<String> providers = task.getResult().getProviders();
WritableArray array = Arguments.createArray();
if (providers != null) {
for(String provider : providers) {
array.pushString(provider);
}
if (providers != null) {
for (String provider : providers) {
array.pushString(provider);
}
promise.resolve(array);
} else {
Exception exception = task.getException();
Log.d(TAG, "fetchProvidersForEmail:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
promise.resolve(array);
} else {
Exception exception = task.getException();
Log.d(TAG, "fetchProvidersForEmail:onComplete:failure", exception);
promiseRejectAuthException(promise, exception);
}
});
}
});
}
/* ------------------
@ -786,6 +874,7 @@ 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
*/

View File

@ -299,7 +299,12 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
@ReactMethod
public void once(final int refId, final String path, final ReadableArray modifiers, final String eventName, final Callback callback) {
RNFirebaseDatabaseReference ref = this.getDBHandle(refId, path, modifiers);
ref.addOnceValueEventListener(callback);
if (eventName.equals("value")) {
ref.addOnceValueEventListener(callback);
} else {
ref.addChildOnceEventListener(eventName, callback);
}
}
/**

View File

@ -5,6 +5,7 @@ import java.util.HashSet;
import java.util.List;
import android.support.annotation.Nullable;
import android.telecom.Call;
import android.util.Log;
import java.util.Map;
@ -113,7 +114,7 @@ public class RNFirebaseDatabaseReference {
}
}
public void addOnceValueEventListener(final Callback callback) {
void addOnceValueEventListener(final Callback callback) {
final ValueEventListener onceValueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
@ -132,11 +133,66 @@ public class RNFirebaseDatabaseReference {
callback.invoke(err);
}
};
mQuery.addListenerForSingleValueEvent(onceValueEventListener);
Log.d(TAG, "Added OnceValueEventListener for refId: " + mRefId);
}
public void removeEventListener(int listenerId, String eventName) {
void addChildOnceEventListener(final String eventName, final Callback callback) {
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_added".equals(eventName)) {
mQuery.removeEventListener(this);
WritableMap data = Utils.snapshotToMap("child_added", mRefId, null, mPath, dataSnapshot, previousChildName);
callback.invoke(null, data);
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_changed".equals(eventName)) {
mQuery.removeEventListener(this);
WritableMap data = Utils.snapshotToMap("child_changed", mRefId, null, mPath, dataSnapshot, previousChildName);
callback.invoke(null, data);
}
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
if ("child_removed".equals(eventName)) {
mQuery.removeEventListener(this);
WritableMap data = Utils.snapshotToMap("child_removed", mRefId, null, mPath, dataSnapshot, null);
callback.invoke(null, data);
}
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
if ("child_moved".equals(eventName)) {
mQuery.removeEventListener(this);
WritableMap data = Utils.snapshotToMap("child_moved", mRefId, null, mPath, dataSnapshot, previousChildName);
callback.invoke(null, data);
}
}
@Override
public void onCancelled(DatabaseError error) {
mQuery.removeEventListener(this);
WritableMap err = Arguments.createMap();
err.putInt("refId", mRefId);
err.putString("path", mPath);
err.putInt("code", error.getCode());
err.putString("details", error.getDetails());
err.putString("message", error.getMessage());
callback.invoke(err);
}
};
mQuery.addChildEventListener(childEventListener);
}
void removeEventListener(int listenerId, String eventName) {
if ("value".equals(eventName)) {
this.removeValueEventListener(listenerId);
} else {
@ -144,7 +200,7 @@ public class RNFirebaseDatabaseReference {
}
}
public boolean hasListeners() {
boolean hasListeners() {
return !mChildEventListeners.isEmpty() || !mValueEventListeners.isEmpty();
}

View File

@ -45,6 +45,7 @@ All in all, RNFirebase provides much faster performance (~2x) over the web SDK a
| Performance Monitoring | ✅ | ✅ | ❌ |
| Realtime Database | ✅ | ✅ | ✅ |
| - Offline Persistance | ✅ | ✅ | ❌ |
| - Transactions | ✅ | ✅ | ✅ |
| Remote Config | ✅ | ✅ | ❌ |
| Storage | ✅ | ✅ | ❌ |

View File

@ -6,10 +6,10 @@ Currently due to the blackbox Firebase enviroment, we have found the best way to
For convenience all of the required NPM scripts are packaged with the main library to run the test app.
### Step 1 - Clone
### Step 1 - Fork & Clone
```bash
git clone git@github.com:invertase/react-native-firebase.git
git clone git@github.com:<username>/react-native-firebase.git
```
### Step 2 - Install dependencies
@ -53,3 +53,278 @@ npm run tests-pod-install
```
Open the `tests/ios/ReactNativeFirebaseDemo.xcworkspace` file in XCode and build for your preffered device or simulator.
## Tests
Tests are bootstrapped and ran when the `play` button is pressed. The status of each test suite and individual test will update as and when a test has completed or errored.
### Running tests
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](https://github.com/invertase/react-native-firebase/blob/master/tests/docs/assets/test-suite-screenshot-android.png?raw=true)
### Adding test
To add tests to an existing test suite, you need to pass a function to `addTests`.
#### Synchronous tests
Synchronous tests are created by passing a function to `it`. The next test is run immediately after the last line is executed.
```javascript
testSuite.addTests(({ describe, it }) => {
describe('synchronous test', () => {
it('does something correctly', () => {
});
});
});
```
#### Asynchronous tests
Tests can be asynchronous if they return a promise. The test suite waits for the promise to resolve before executing the next test.
```javascript
testSuite.addTests(({ describe, it }) => {
describe('async successful test', () => {
it('does something correctly', () => {
return new Promise((resolve, reject) => {
// ...
resolve();
});
});
});
});
```
Asynchronous tests can also be created using the `async` function syntax:
```javascript
testSuite.addTests(({ describe, it }) => {
describe('async successful test', () => {
it('does something correctly', async () => {
// ...
await somethingAsynchronous();
});
});
});
```
> When rejecting, always ensure a valid [JavaScript Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) is provided.
### Creating a new test suite
A test suite groups together test categories under the same Firebase feature. e.g. *Realtime Database* tests.
To add a new test suite:
1. Create a new directory within `src/tests`.
2. Create an `index.js` file.
In this file, you need to create an instance of `TestSuite` - see [TestSuite constructor](#testsuite-constructor).
```javascript
import TestSuite from 'lib/TestSuite';
const MyNewSuite = new TestSuite('Realtime Database Storage', 'Upload/Download storage tests');
export default MyNewSuite;
```
3. `addTests` is then used as many times as is necessary to add tests to the test suite, accepting a function that defines one or more tests.
4. The test suite must then be imported into `src/tests/index.js` and added to `testSuiteInstances` in order for it to be included in the list of test suites available to run in the app.
## TestSuite API
### TestSuite Constructor
The TestSuite constructor accepts 3 arguments:
- **name**: String containing the name of the test suite. e.g. 'Realtime Storage'
- **description**: String containing description of the test suite
- **firebase**: This is the object exported from `src/firebase` and contains both the native and web firebase instances.
```javascript
import firebase from '../firebase';
new TestSuite('Realtime Database Storage', 'firebase.database()', firebase);
```
### Test Definition
#### describe()
The `describe()` function takes 2 - 3 arguments:
- **description**: String describing the context or target of all the tests defined in `testDefinitions`
- **options**: (Optional) object of options:
* **focus**: Boolean marking all the tests defined in `testDefinitions` (and any others marked as focused) as the only one(s) that should run
* **pending**: Boolean marking all the tests defined in `testDefinitions` as excluded from running in the test suite
- **testDefinitions**: Function that defines 1 or more tests by calling `it`, `xit` or `fit`
```javascript
function testCategory({ describe }) {
describe('a feature', () => {
it('does something synchronously', () => {
});
});
}
export default testCategory;
```
`describe()` statements can be arbitrarily nested.
#### context()
`context()` is an alias for `describe()` provided as syntactical sugar. `xcontext()` and `fcontext()` work similar to `xdescribe()` and `fdescribe()`, respectively.
#### it()
The `it()` function takes 2 - 3 arguments:
- **description**: String describing the test defined in `testDefinition`
- **options**: (Optional) object of options:
* **focus**: Boolean marking the test defined in `testDefinition` (and any others marked as focused) as the only one(s) that should run
* **pending**: Boolean marking the test defined in `testDefinition` as excluded from running in the test suite
* **timeout**: Time in milliseconds a test is allowed to execute before it's considered to have timed out. Default is 5000ms (5 seconds).
- **testDefinition**: Function that defines a test with one or more assertions. Can be a synchronous or asynchronous function. Functions that return a promise cause the test environment to wait for the promise to be resolved before proceding to the next test.
```javascript
it('does something synchronously', () => {
});
it('does something asynchronously', async () => {
});
it('does something else asynchronously', () => {
return new Promise(/* ... */);
});
```
`it()` statements can *not* be nested.
#### xdescribe() & xit()
##### Pending Tests
You can mark all tests within a `describe` statement as pending by using the `xdescribe` function instead. The test will appear greyed out and will not be run as part of the test suite.
You can mark a single test as pending by using `xit` as you would `it`.
Tests should only be marked as pending temporarily, and should not normally be committed to the test suite unless they are fully implemented.
#### fdescribe() & fit()
##### Focused Tests
You can mark all tests within a `describe` statement as focused by using the `fdescribe` function instead. Tests that are focused will be the only ones that appear and run in the test suite until all tests are removed from being focused. This is useful for running and working on a few tests at a time.
You can mark a single test as focused by using `fit` as you would `it`.
#### Test Assertions
The assertion library Should.js is used in the tests. The complete list of available assertions is available in the [Should.js documentation](https://shouldjs.github.io).
#### Lifecycle methods
Four lifecycle methods are provided for each test context:
- **before** - Run once, before the current test context executes
- **beforeEach** - Run before every test in the current test context
- **after** - Run once, after the current test context has finished executing
- **afterEach** - Run after every test in the current test context
A new test context is created when the test suite encounters any of `describe`, `xdescribe`, `fdescribe`, `context`, `xcontext` or `fcontext`, and close again when it reaches the end of the block. Test contexts can be nested and lifecycle hooks set for parent contexts apply for all descendents.
Each lifecycle hook accepts either a synchronous function, a function that returns a promise or an `async` function.
```javascript
function testCategory({ before, beforeEach, afterEach, after }) {
before(() => console.log('Before all tests start.'));
beforeEach(() => console.log('Before every test starts.'));
describe('sync successful test', function() {
// ...
});
afterEach(() => console.log('After each test starts.'));
after(() => console.log('After all tests are complete, with success or error.'));
}
```
An optional hash of options can also be passed as the first argument, defining one or more of the following values:
* **timeout**: Time in milliseconds a hook is allowed to execute before it's considered to have timed out. Default is 5000ms (5 seconds).
#### Accessing Firebase
`react-native-firebase` is available `firebase.native`:
```javascript
function testCategory({ describe, firebase }) {
describe('sync successful test', 'category', function() {
firebase.native.database();
});
}
```
If you need to access the web API for Firebase to compare with the functionality of `react-native-firebase`, you can access it on `firebase.web`.
> All tests should be written in terms of `react-native-firebase`'s behaviour and should **not** include direct comparisons with the web API. It's available for reference, only.
## Development Notes
> JavaScript changes do **not** require restarting the React Native packager to take effect
> Java changes will need to be rebuilt in Android Studio
> Objective-C changes need to be rebuilt in Xcode
### Debugging or viewing internals of the test suite
`react-native-firebase/tests` is compatible with [react-native-debugger](https://github.com/jhen0409/react-native-debugger) and is the recommended way to view the internal state of the test suite for development or troubleshooting.
It allows you to view state and prop values of the React component tree, view the actions and contents of the Redux store and view and interact with the debugging console.
Make sure **Remote JS Debugging** when running the application and close any chrome debugging windows that appear and start React Native Debugger.
### Running the internal tests
`react-native-firebase-tests` has its own tests to verify the testing framework is working as expected. These are run from the command line:
```bash
npm run internal-tests
```
## Troubleshooting
### Invalid React.podspec file: no implicit conversion of nil into String
This error occurs if you are using ruby version 2.1.2. Upgrade your version of ruby and try again.
### Unable to resolve module ../../../node_modules/react-native/packager/...
Run the packager separately, clearing the cache:
```bash
npm start -- --reset-cache
```

View File

@ -17,7 +17,7 @@
loadSidebar: true,
search: 'auto',
themeColor: '#f5820b',
subMaxLevel: 2,
subMaxLevel: 3,
maxLevel: 4,
ga: 'UA-98196653-1',
plugins: [

View File

@ -2,8 +2,8 @@
!> Performance monitoring requires react-native-firebase version 1.2.0.
?> If you plan on using this module in your own application, please ensure the optional setup instructions for
[Android](#) and [iOS](#) have been followed.
?> Android: If you plan on using this module in your own application, please ensure the optional setup instructions for
[Android](http://invertase.io/react-native-firebase/#/installation-android?id=_4-performance-monitoring-optional) have been followed.
Out of the box, [Firebase Performance Monitoring](https://firebase.google.com/docs/perf-mon/automatic) monitors a number of
[automatic traces](https://firebase.google.com/docs/perf-mon/automatic) such as app start/background/foreground response times.

View File

@ -1,8 +1,5 @@
# Transactions
!> Transactions is currently an experimental feature in RNFirebase. Whilst it does work there may still be some issues with it, especially around offline connectivity handling. Please report any issues in the usual manner.
?> For help on how to use firebase transactions please see the [Firebase Transaction Documentation](https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction).
### Android Implementation

124
index.d.ts vendored
View File

@ -7,21 +7,28 @@ 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 */
/** mimics firebase Web SDK */
database: {
(): RNFirebase.database.Database
ServerValue: {
TIMESTAMP: number
}
}
/**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.
@ -29,6 +36,7 @@ declare module "react-native-firebase" {
* 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,
@ -37,9 +45,12 @@ declare module "react-native-firebase" {
* 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;
}
@ -56,10 +67,10 @@ declare module "react-native-firebase" {
};
/**
* 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.
*/
* 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
@ -74,7 +85,7 @@ declare module "react-native-firebase" {
*/
persistence?: boolean;
/**
* Default from app [NSBundle mainBundle] The bundle ID for the app to be bundled with
* Default from app [NSBundle mainBundle] The bundle ID for the app to be bundled with
*/
bundleID?: string;
/**
@ -103,7 +114,7 @@ declare module "react-native-firebase" {
*/
androidClientID?: string;
/**
* default ""
* default ""
* The Project number from the Google Developer's console used to configure Google Cloud Messaging
*/
GCMSenderID?: string;
@ -127,12 +138,10 @@ declare module "react-native-firebase" {
namespace storage {
interface StorageTask<T> extends Promise<T> {
on(
event: TaskEvent,
nextOrObserver: (snapshot: any) => any,
error: (error: RnError) => any,
complete: (complete: any) => any
): any
on(event: TaskEvent,
nextOrObserver: (snapshot: any) => any,
error: (error: RnError) => any,
complete: (complete: any) => any): any
/**
* is not currently supported by react-native-firebase
*/
@ -183,17 +192,13 @@ declare module "react-native-firebase" {
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;
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>;
updateMetadata(metadata: storage.SettableMetadata): Promise<any>;
}
interface UploadMetadata extends storage.SettableMetadata {
md5Hash?: string | null;
@ -219,13 +224,12 @@ declare module "react-native-firebase" {
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;
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>;
then(onFulfilled?: ((a: storage.UploadTaskSnapshot) => any) | null,
onRejected?: ((a: RnError) => any) | null): Promise<any>;
}
interface UploadTaskSnapshot {
@ -310,18 +314,15 @@ declare module "react-native-firebase" {
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;
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>;
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;
@ -356,18 +357,14 @@ declare module "react-native-firebase" {
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>;
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>;
}
}
@ -491,6 +488,17 @@ declare module "react-native-firebase" {
token: string,
secret: string
}
interface ActionCodeInfo {
email: string,
error: string,
fromEmail: string,
verifyEmail: string,
recoverEmail: string,
passwordReset: string
}
namespace auth {
interface Auth {
@ -507,9 +515,8 @@ declare module "react-native-firebase" {
* 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;
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.
@ -543,6 +550,21 @@ declare module "react-native-firebase" {
* 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.
*/
confirmPasswordReset(code: string, newPassword: string): Promise<any>
/**
* Applies a verification code sent to the user by email or other out-of-band mechanism.
*/
applyActionCode(code: string): Promise<any>
/**
* Checks a verification code sent to the user by email or other out-of-band mechanism.
*/
checkActionCode(code: string): Promise<ActionCodeInfo>
/**
* Completes the password reset process,
* given a confirmation code and new password.

View File

@ -321,6 +321,76 @@ RCT_EXPORT_METHOD(signInWithCredential:(NSString *)provider token:(NSString *)au
}];
}
/**
confirmPasswordReset
@param NSString code
@param NSString newPassword
@param RCTPromiseResolveBlock resolve
@param RCTPromiseRejectBlock reject
@return
*/
RCT_EXPORT_METHOD(confirmPasswordReset:(NSString *)code newPassword:(NSString *)newPassword resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
[[FIRAuth auth] confirmPasswordResetWithCode:code newPassword:newPassword completion:^(NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseNoUser:resolve rejecter:reject isError:NO];
}
}];
}
/**
* applyActionCode
*
* @param NSString code
* @param RCTPromiseResolveBlock resolve
* @param RCTPromiseRejectBlock reject
* @return
*/
RCT_EXPORT_METHOD(applyActionCode:(NSString *)code resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
[[FIRAuth auth] applyActionCode:code completion:^(NSError *_Nullable error) {
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
[self promiseNoUser:resolve rejecter:reject isError:NO];
}
}];
}
/**
* checkActionCode
*
* @param NSString code
* @param RCTPromiseResolveBlock resolve
* @param RCTPromiseRejectBlock reject
* @return
*/
RCT_EXPORT_METHOD(checkActionCode:(NSString *) code resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject) {
[[FIRAuth auth] checkActionCode:code completion:^(FIRActionCodeInfo *_Nullable info, NSError *_Nullable error){
if (error) {
[self promiseRejectAuthException:reject error:error];
} else {
NSDictionary * result = @{
@"data": @{
@"email": [info dataForKey:FIRActionCodeEmailKey],
@"fromEmail": [info dataForKey:FIRActionCodeFromEmailKey],
}
};
// TODO action code operation codes?
/*
FIRActionCodeOperationUnknown = 0,
FIRActionCodeOperationPasswordReset = 1,
FIRActionCodeOperationVerifyEmail = 2
*/
resolve(result);
}
}];
}
/**
sendPasswordResetEmail

View File

@ -33,14 +33,7 @@
if (!_listeners[listenerId]) {
id andPreviousSiblingKeyWithBlock = ^(FIRDataSnapshot *_Nonnull snapshot, NSString *_Nullable previousChildName) {
NSDictionary *props = [RNFirebaseDBReference snapshotToDict:snapshot];
[self sendJSEvent:DATABASE_DATA_EVENT title:eventName props:@{
@"eventName": eventName,
@"refId": _refId,
@"listenerId": listenerId,
@"path": _path,
@"snapshot": props,
@"previousChildName": previousChildName != nil ? previousChildName : [NSNull null]
}];
[self sendJSEvent:DATABASE_DATA_EVENT title:eventName props:@{@"eventName": eventName, @"refId": _refId, @"listenerId": listenerId, @"path": _path, @"snapshot": props, @"previousChildName": previousChildName != nil ? previousChildName : [NSNull null]}];
};
id errorBlock = ^(NSError *_Nonnull error) {
NSLog(@"Error onDBEvent: %@", [error debugDescription]);
@ -55,20 +48,16 @@
}
}
- (void)addSingleEventHandler:(RCTResponseSenderBlock)callback {
[_query observeSingleEventOfType:FIRDataEventTypeValue andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *_Nonnull snapshot, NSString *_Nullable previousChildName) {
NSDictionary *props = [RNFirebaseDBReference snapshotToDict:snapshot];
callback(@[[NSNull null], @{
@"eventName": @"value",
@"path": _path,
@"refId": _refId,
@"snapshot": props,
@"previousChildName": previousChildName != nil ? previousChildName : [NSNull null]
}]);
} withCancelBlock:^(NSError *_Nonnull error) {
NSLog(@"Error onDBEventOnce: %@", [error debugDescription]);
callback(@[@{@"eventName": DATABASE_ERROR_EVENT, @"path": _path, @"refId": _refId, @"code": @([error code]), @"details": [error debugDescription], @"message": [error localizedDescription], @"description": [error description]}]);
}];
- (void)addSingleEventHandler:(NSString *)eventName callback:(RCTResponseSenderBlock)callback {
FIRDataEventType firDataEventType = (FIRDataEventType)[self eventTypeFromName:eventName];
[_query observeSingleEventOfType:firDataEventType andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *_Nonnull snapshot, NSString *_Nullable previousChildName) {
NSDictionary *props = [RNFirebaseDBReference snapshotToDict:snapshot];
callback(@[[NSNull null], @{@"eventName": eventName, @"path": _path, @"refId": _refId, @"snapshot": props, @"previousChildName": previousChildName != nil ? previousChildName : [NSNull null]}]);
} withCancelBlock:^(NSError *_Nonnull error) {
NSLog(@"Error onDBEventOnce: %@", [error debugDescription]);
callback(@[@{@"eventName": DATABASE_ERROR_EVENT, @"path": _path, @"refId": _refId, @"code": @([error code]), @"details": [error debugDescription], @"message": [error localizedDescription], @"description": [error description]}]);
}];
}
- (void)removeEventHandler:(NSNumber *)listenerId eventName:(NSString *)eventName {
@ -428,7 +417,7 @@ RCT_EXPORT_METHOD(once:
eventName:(NSString *) eventName
callback:(RCTResponseSenderBlock) callback) {
RNFirebaseDBReference *ref = [self getDBHandle:refId path:path modifiers:modifiers];
[ref addSingleEventHandler:callback];
[ref addSingleEventHandler:eventName callback:callback];
}
RCT_EXPORT_METHOD(off:
@ -459,8 +448,8 @@ RCT_EXPORT_METHOD(onDisconnectSet:
(RCTResponseSenderBlock) callback) {
FIRDatabaseReference *ref = [self getPathRef:path];
[ref onDisconnectSetValue:props[@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[self handleCallback:@"onDisconnectSetObject" callback:callback databaseError:error];
}];
[self handleCallback:@"onDisconnectSetObject" callback:callback databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectRemove:

View File

@ -153,6 +153,40 @@ export default class Auth extends Base {
return FirebaseAuth.sendPasswordResetEmail(email);
}
/**
* Completes the password reset process, given a confirmation code and new password.
*
* @link https://firebase.google.com/docs/reference/js/firebase.auth.Auth#confirmPasswordReset
* @param code
* @param newPassword
* @return {Promise.<Null>}
*/
confirmPasswordReset(code: string, newPassword: string): Promise<Null> {
return FirebaseAuth.confirmPasswordReset(code, newPassword);
}
/**
* Applies a verification code sent to the user by email or other out-of-band mechanism.
*
* @link https://firebase.google.com/docs/reference/js/firebase.auth.Auth#applyActionCode
* @param code
* @return {Promise.<Null>}
*/
applyActionCode(code: string): Promise<Any> {
return FirebaseAuth.applyActionCode(code);
}
/**
* Checks a verification code sent to the user by email or other out-of-band mechanism.
*
* @link https://firebase.google.com/docs/reference/js/firebase.auth.Auth#checkActionCode
* @param code
* @return {Promise.<Any>|Promise<ActionCodeInfo>}
*/
checkActionCode(code: string): Promise<Any> {
return FirebaseAuth.checkActionCode(code);
}
/**
* Get the currently signed in user
* @return {Promise}

View File

@ -262,13 +262,11 @@ export default class Reference extends ReferenceBase {
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.`
error.message = `permission_denied at /${this.path}: Client doesn't have permission to access the desired data.`
}
failureCallbackOrContext(error);
}
};
}
let _successCallback;
@ -375,11 +373,9 @@ export default class Reference extends ReferenceBase {
* @param onComplete
* @param applyLocally
*/
transaction(
transactionUpdate: Function,
onComplete: (error: ?Error, committed: boolean, snapshot: ?Snapshot) => *,
applyLocally: boolean = false
) {
transaction(transactionUpdate: Function,
onComplete: (error: ?Error, committed: boolean, snapshot: ?Snapshot) => *,
applyLocally: boolean = false) {
if (!isFunction(transactionUpdate)) {
return Promise.reject(
new Error('Missing transactionUpdate function argument.')
@ -588,7 +584,7 @@ export default class Reference extends ReferenceBase {
*
* {@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;
return new Reference(this.database, this.path.substring(0, this.path.lastIndexOf('/')));
}

View File

@ -17,9 +17,14 @@ export default class Snapshot {
_childKeys: Array<string>;
constructor(ref: Reference, snapshot: Object) {
this.ref = ref;
this.key = snapshot.key;
if (ref.key !== snapshot.key) {
this.ref = ref.child(snapshot.key);
} else {
this.ref = ref;
}
// internal use only
this._value = snapshot.value;
this._priority = snapshot.priority === undefined ? null : snapshot.priority;

View File

@ -1,6 +1,6 @@
{
"name": "react-native-firebase",
"version": "2.0.0",
"version": "2.0.2",
"author": "Invertase <contact@invertase.io> (http://invertase.io)",
"description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Auth, Database, Messaging, Remote Config, Storage, Admob, Analytics, Crash Reporting, and Performance.",
"main": "index",

View File

@ -1,363 +1 @@
# react-native-firebase test suite
An **iOS** and **Android** React Native app built to test [`react-native-firebase`](https://github.com/invertase/react-native-firebase).
## Install
1. Clone the test application
```bash
git clone https://github.com/invertase/react-native-firebase.git
```
2. Install the dependencies listed in `package.json`
```bash
cd react-native-firebase/tests/ && npm install
```
### iOS Installation
3. Install the test application's CocoaPods.
* See [troubleshooting](#installing-podfiles) if this doesn't work for you.
```bash
npm run ios:pod:install
```
4. Start the React Native packager
```bash
npm run start
```
5. In another terminal window, install the app on your emulator:
```bash
npm run ios:dev
```
### Android Installation
6. Start your emulator through Android Studio: Tools > Android > AVD Manager
> You will need a version of the Android emulator that has the Play Store installed (you should be able to find it on the emulator's home screen or on the list of apps).
7. Start the React Native packager if you haven't already in the iOS instructions.
```bash
npm run start
```
8. Run the test app on your Android emulator:
```bash
npm run android:dev
```
## Documentation
`react-native-firebase` aims to match the Firebase Web API wherever possible. As a result, the tests are largely derived from the [Firebase Web API documentation](https://firebase.google.com/docs/reference/js/).
## Tests
Tests are bootstrapped and ran when the app is booted. The status of each test suite and individual test will update as and when a test has completed or errored.
### Running tests
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](/tests/docs/assets/test-suite-screenshot-android.png?raw=true)
### Adding test
To add tests to an existing test suite, you need to pass a function to `addTests`.
#### Synchronous tests
Synchronous tests are created by passing a function to `it`. The next test is run immediately after the last line is executed.
```javascript
testSuite.addTests(({ describe, it }) => {
describe('synchronous test', () => {
it('does something correctly', () => {
});
});
});
```
#### Asynchronous tests
Tests can be asynchronous if they return a promise. The test suite waits for the promise to resolve before executing the next test.
```javascript
testSuite.addTests(({ describe, it }) => {
describe('async successful test', () => {
it('does something correctly', () => {
return new Promise((resolve, reject) => {
// ...
resolve();
});
});
});
});
```
Asynchronous tests can also be created using the `async` function syntax:
```javascript
testSuite.addTests(({ describe, it }) => {
describe('async successful test', () => {
it('does something correctly', async () => {
// ...
await somethingAsynchronous();
});
});
});
```
> When rejecting, always ensure a valid [JavaScript Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) is provided.
### Creating a new test suite
A test suite groups together test categories under the same Firebase feature. e.g. *Realtime Database* tests.
To add a new test suite:
1. Create a new directory within `src/tests`.
2. Create an `index.js` file.
In this file, you need to create an instance of `TestSuite` - see [TestSuite constructor](#testsuite-constructor).
```javascript
import TestSuite from 'lib/TestSuite';
const MyNewSuite = new TestSuite('Realtime Database Storage', 'Upload/Download storage tests');
export default MyNewSuite;
```
3. `addTests` is then used as many times as is necessary to add tests to the test suite, accepting a function that defines one or more tests.
4. The test suite must then be imported into `src/tests/index.js` and added to `testSuiteInstances` in order for it to be included in the list of test suites available to run in the app.
## TestSuite API
### TestSuite Constructor
The TestSuite constructor accepts 3 arguments:
- **name**: String containing the name of the test suite. e.g. 'Realtime Storage'
- **description**: String containing description of the test suite
- **firebase**: This is the object exported from `src/firebase` and contains both the native and web firebase instances.
```javascript
import firebase from '../firebase';
new TestSuite('Realtime Database Storage', 'firebase.database()', firebase);
```
### Test Definition
#### describe()
The `describe()` function takes 2 - 3 arguments:
- **description**: String describing the context or target of all the tests defined in `testDefinitions`
- **options**: (Optional) object of options:
* **focus**: Boolean marking all the tests defined in `testDefinitions` (and any others marked as focused) as the only one(s) that should run
* **pending**: Boolean marking all the tests defined in `testDefinitions` as excluded from running in the test suite
- **testDefinitions**: Function that defines 1 or more tests by calling `it`, `xit` or `fit`
```javascript
function testCategory({ describe }) {
describe('a feature', () => {
it('does something synchronously', () => {
});
});
}
export default testCategory;
```
`describe()` statements can be arbitrarily nested.
#### context()
`context()` is an alias for `describe()` provided as syntactical sugar. `xcontext()` and `fcontext()` work similar to `xdescribe()` and `fdescribe()`, respectively.
#### it()
The `it()` function takes 2 - 3 arguments:
- **description**: String describing the test defined in `testDefinition`
- **options**: (Optional) object of options:
* **focus**: Boolean marking the test defined in `testDefinition` (and any others marked as focused) as the only one(s) that should run
* **pending**: Boolean marking the test defined in `testDefinition` as excluded from running in the test suite
* **timeout**: Time in milliseconds a test is allowed to execute before it's considered to have timed out. Default is 5000ms (5 seconds).
- **testDefinition**: Function that defines a test with one or more assertions. Can be a synchronous or asynchronous function. Functions that return a promise cause the test environment to wait for the promise to be resolved before proceding to the next test.
```javascript
it('does something synchronously', () => {
});
it('does something asynchronously', async () => {
});
it('does something else asynchronously', () => {
return new Promise(/* ... */);
});
```
`it()` statements can *not* be nested.
#### xdescribe() & xit()
##### Pending Tests
You can mark all tests within a `describe` statement as pending by using the `xdescribe` function instead. The test will appear greyed out and will not be run as part of the test suite.
You can mark a single test as pending by using `xit` as you would `it`.
Tests should only be marked as pending temporarily, and should not normally be committed to the test suite unless they are fully implemented.
#### fdescribe() & fit()
##### Focused Tests
You can mark all tests within a `describe` statement as focused by using the `fdescribe` function instead. Tests that are focused will be the only ones that appear and run in the test suite until all tests are removed from being focused. This is useful for running and working on a few tests at a time.
You can mark a single test as focused by using `fit` as you would `it`.
#### Test Assertions
The assertion library Should.js is used in the tests. The complete list of available assertions is available in the [Should.js documentation](https://shouldjs.github.io).
#### Lifecycle methods
Four lifecycle methods are provided for each test context:
- **before** - Run once, before the current test context executes
- **beforeEach** - Run before every test in the current test context
- **after** - Run once, after the current test context has finished executing
- **afterEach** - Run after every test in the current test context
A new test context is created when the test suite encounters any of `describe`, `xdescribe`, `fdescribe`, `context`, `xcontext` or `fcontext`, and close again when it reaches the end of the block. Test contexts can be nested and lifecycle hooks set for parent contexts apply for all descendents.
Each lifecycle hook accepts either a synchronous function, a function that returns a promise or an `async` function.
```javascript
function testCategory({ before, beforeEach, afterEach, after }) {
before(() => console.log('Before all tests start.'));
beforeEach(() => console.log('Before every test starts.'));
describe('sync successful test', function() {
// ...
});
afterEach(() => console.log('After each test starts.'));
after(() => console.log('After all tests are complete, with success or error.'));
}
```
An optional hash of options can also be passed as the first argument, defining one or more of the following values:
* **timeout**: Time in milliseconds a hook is allowed to execute before it's considered to have timed out. Default is 5000ms (5 seconds).
#### Accessing Firebase
`react-native-firebase` is available `firebase.native`:
```javascript
function testCategory({ describe, firebase }) {
describe('sync successful test', 'category', function() {
firebase.native.database();
});
}
```
If you need to access the web API for Firebase to compare with the functionality of `react-native-firebase`, you can access it on `firebase.web`.
> All tests should be written in terms of `react-native-firebase`'s behaviour and should **not** include direct comparisons with the web API. It's available for reference, only.
## Development
### Running test suite against latest version of react-native-firebase
You can use the node module `wml` to automatically copy changes you make to `react-native-firebase` over to the test application so you can run the test suite against them.
1. Install `wml` globally:
```bash
npm install wml -g
```
2. Configure `wml` to copy changes from `react-native-firebase` to `react-native-firebase/tests/node_modules/react-native-firebase` is:
```bash
wml add /full/path/to/react-native-firebase /full/path/to/react-native-firebase/tests/node_modules/react-native-firebase
```
3. Start `wml`:
```bash
wml start
```
> JavaScript changes require restarting the React Native packager to take effect
> Java changes will need to be rebuilt in Android Studio
> Objective-C changes need to be rebuilt in Xcode
4. Stop `wml` when you are finished:
```bash
wml stop
```
### Debugging or viewing internals of the test suite
`react-native-firebase/tests` is compatible with [react-native-debugger](https://github.com/jhen0409/react-native-debugger) and is the recommended way to view the internal state of the test suite for development or troubleshooting.
It allows you to view state and prop values of the React component tree, view the actions and contents of the Redux store and view and interact with the debugging console.
Make sure **Remote JS Debugging** when running the application and close any chrome debugging windows that appear and start React Native Debugger.
### Running the internal tests
`react-native-firebase-tests` has its own tests to verify the testing framework is working as expected. These are run from the command line:
```bash
npm run internal-tests
```
## Troubleshooting
### Invalid React.podspec file: no implicit conversion of nil into String
This error occurs if you are using ruby version 2.1.2. Upgrade your version of ruby and try again.
### Unable to resolve module ../../../node_modules/react-native/packager/...
Run the packager separately, clearing the cache:
```bash
npm start -- --reset-cache
```
See http://invertase.io/react-native-firebase/#/contributing/testing

View File

@ -98,7 +98,7 @@ PODS:
- React/cxxreact (0.44.3):
- React/jschelpers
- React/jschelpers (0.44.3)
- RNFirebase (1.1.2)
- RNFirebase (2.0.0)
- Yoga (0.44.3.React)
DEPENDENCIES:
@ -143,7 +143,7 @@ SPEC CHECKSUMS:
GTMSessionFetcher: 30d874b96d0d76028f61fbd122801e3f030d47db
Protobuf: d582fecf68201eac3d79ed61369ef45734394b9c
React: 6361345ebeb769a929e10a06baf0c868d6d03ad5
RNFirebase: 7310b8755cc32583f62f11e61b28f839046de5eb
RNFirebase: dcc4dcb1c9400a9bc01866e50d1351272a7df311
Yoga: c90474ca3ec1edba44c97b6c381f03e222a9e287
PODFILE CHECKSUM: 45666f734ebfc8b3b0f2be0a83bc2680caeb502f

View File

@ -1,5 +1,6 @@
import onTests from './on/onTests';
import onValueTests from './on/onValueTests';
import onChildAddedTests from './on/onChildAddedTests';
import offTests from './offTests';
import onceTests from './onceTests';
import setTests from './setTests';
@ -21,7 +22,7 @@ import DatabaseContents from '../../support/DatabaseContents';
const testGroups = [
issueSpecificTests, factoryTests, keyTests, parentTests, childTests, rootTests,
pushTests, onTests, onValueTests, offTests, onceTests, updateTests,
pushTests, onTests, onValueTests, onChildAddedTests, offTests, onceTests, updateTests,
removeTests, setTests, transactionTests, queryTests, refTests, isEqualTests,
];

View File

@ -0,0 +1,36 @@
import 'should-sinon';
import Promise from 'bluebird';
export default function onChildAddedTests({ describe, beforeEach, afterEach, it, firebase }) {
describe('ref().on(\'child_added\')', () => {
describe('the snapshot', () => {
let ref;
let childRef;
let childVal;
beforeEach(async () => {
ref = firebase.native.database().ref('tests/types/object');
await new Promise((resolve) => {
ref.on('child_added', (snapshot) => {
childRef = snapshot.ref;
childVal = snapshot.val();
resolve();
});
});
});
afterEach(() => {
ref.off();
});
// https://github.com/invertase/react-native-firebase/issues/221
it('has a key that identifies the child', () => {
(childRef.key).should.equal('foo');
});
it('has the value of the child', () => {
(childVal).should.equal('bar');
});
});
});
}

View File

@ -1,12 +1,10 @@
function refTests({ describe, it, firebase }) {
describe('ref().ref', () => {
it('returns a the reference itself', () => {
it('returns the reference', () => {
// Setup
const ref = firebase.native.database().ref();
// Assertion
ref.ref.should.eql(ref);
});
});