Merge branch 'master' of github.com:invertase/react-native-firebase
This commit is contained in:
commit
d4328f3adb
|
@ -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 | ✅ | ✅ | ❌ |
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 | ✅ | ✅ | ❌ |
|
||||
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
loadSidebar: true,
|
||||
search: 'auto',
|
||||
themeColor: '#f5820b',
|
||||
subMaxLevel: 2,
|
||||
subMaxLevel: 3,
|
||||
maxLevel: 4,
|
||||
ga: 'UA-98196653-1',
|
||||
plugins: [
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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('/')));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
364
tests/README.md
364
tests/README.md
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue