Add missing chrome debugging apis for Sync.User (#801)

* Add user tests

* Add Sync member to Realm

* Add a simple test for addListener

* Introduce browser User class

* Introduce Sync.User proxy

* Fix User.all

* Add User.logout

* Remove some console.log statements

* Update rpc.cpp from tests-folder

* Use user.current from user-methods, not cpp

* remove user.current from two missing places

* Start server in realmjs and react tests

* Add user tests

* Add Sync member to Realm

* Add a simple test for addListener

* Introduce browser User class

* Introduce Sync.User proxy

* Fix User.all

* Add User.logout

* Remove some console.log statements

* Update rpc.cpp from tests-folder

* Use user.current from user-methods, not cpp

* remove user.current from two missing places

* Start server in realmjs and react tests

* Replace needle with node-fetch

* Make AuthError work in the browser

* Allow for persistent callbacks

* Expose _authenticateRealm to native

* Use clear_test_state in roc

* Streamline test inclusion

* Fix login tests

* Promisify testLogin()

* Disable AsyncTests for now

* Mode jasmine timeout to spec/unit_tests.js

* Avoid use of global for fetch module

* Only include user-tests is sync is enabled

* Skip ROS for android tests

* Add some comments about persistent callbacks

* Fix results-test

* Run async-tests in node

* Add a comment about (user object)

* Cache all users in User.current and return promises in fetch

* node_require async-tests so RN doesn't try and package them

* Add missing parenthesis
This commit is contained in:
Kristian Dupont 2017-01-31 14:07:29 +01:00 committed by GitHub
parent 8bc4e4f428
commit 0c9c5ac407
17 changed files with 351 additions and 179 deletions

View File

@ -39,6 +39,7 @@ export const propTypes = {};
'OBJECT',
'REALM',
'RESULTS',
'USER',
'UNDEFINED',
].forEach(function(type) {
Object.defineProperty(objectTypes, type, {

View File

@ -24,8 +24,10 @@ import Collection, * as collections from './collections';
import List, { createList } from './lists';
import Results, { createResults } from './results';
import RealmObject, * as objects from './objects';
import User, { createUser } from './user';
import * as rpc from './rpc';
import * as util from './util';
import { _authenticateRealm } from '../user-methods';
const {debugHosts, debugPort} = NativeModules.Realm;
@ -33,6 +35,7 @@ rpc.registerTypeConverter(objectTypes.LIST, createList);
rpc.registerTypeConverter(objectTypes.RESULTS, createResults);
rpc.registerTypeConverter(objectTypes.OBJECT, objects.createObject);
rpc.registerTypeConverter(objectTypes.REALM, createRealm);
rpc.registerTypeConverter(objectTypes.USER, createUser);
function createRealm(_, info) {
let realm = Object.create(Realm.prototype);
@ -127,6 +130,10 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
'write',
], true);
const Sync = {
User
};
Object.defineProperties(Realm, {
Collection: {
value: Collection,
@ -140,6 +147,9 @@ Object.defineProperties(Realm, {
Object: {
value: RealmObject,
},
Sync: {
value: Sync,
},
defaultPath: {
get: util.getterForProperty('defaultPath'),
set: util.setterForProperty('defaultPath'),
@ -166,7 +176,7 @@ Object.defineProperties(Realm, {
for (let i = 0, len = debugHosts.length; i < len; i++) {
try {
// The session ID refers to the Realm constructor object in the RPC server.
Realm[keys.id] = rpc.createSession(debugHosts[i] + ':' + debugPort);
Realm[keys.id] = rpc.createSession(_authenticateRealm, debugHosts[i] + ':' + debugPort);
break;
} catch (e) {
// Only throw exception after all hosts have been tried.

View File

@ -22,9 +22,13 @@ import * as base64 from './base64';
import { keys, objectTypes } from './constants';
const {id: idKey, realm: _realmKey} = keys;
const registeredCallbacks = [];
let registeredCallbacks = [];
const typeConverters = {};
// Callbacks that are registered initially (currently only authenticateRealm) will
// carry this symbol so they are not wiped in clearTestState.
const persistentCallback = Symbol("persistentCallback");
let XMLHttpRequest = global.originalXMLHttpRequest || global.XMLHttpRequest;
let sessionHost;
let sessionId;
@ -45,8 +49,9 @@ export function registerTypeConverter(type, handler) {
typeConverters[type] = handler;
}
export function createSession(host) {
sessionId = sendRequest('create_session', null, host);
export function createSession(authenticateRealm, host) {
authenticateRealm[persistentCallback] = true;
sessionId = sendRequest('create_session', { authenticateRealm: serialize(undefined, authenticateRealm) }, host);
sessionHost = host;
return sessionId;
@ -60,6 +65,12 @@ export function createRealm(args) {
return sendRequest('create_realm', {arguments: args});
}
export function createUser(args) {
args = args.map((arg) => serialize(null, arg));
const result = sendRequest('create_user', {arguments: args});
return deserialize(undefined, result);
}
export function callMethod(realmId, id, name, args) {
if (args) {
args = args.map((arg) => serialize(realmId, arg));
@ -79,11 +90,16 @@ export function setProperty(realmId, id, name, value) {
sendRequest('set_property', {realmId, id, name, value});
}
export function getAllUsers() {
let result = sendRequest('get_all_users');
return deserialize(undefined, result);
}
export function clearTestState() {
sendRequest('clear_test_state');
// Clear all registered callbacks.
registeredCallbacks.length = 0;
// Clear all registered callbacks that are specific to this session.
registeredCallbacks = registeredCallbacks.filter(cb => Reflect.has(cb, persistentCallback));
}
function registerCallback(callback) {

52
lib/browser/user.js Normal file
View File

@ -0,0 +1,52 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
'use strict';
import { createUser as createUserRPC, getAllUsers as getAllUsersRPC } from './rpc';
import { keys, objectTypes } from './constants';
import { createMethods } from './util';
export default class User {
static createUser(server, identity, token, isAdmin) {
return createUserRPC(Array.from(arguments));
}
static get all() {
return getAllUsersRPC();
}
}
createMethods(User.prototype, objectTypes.USER, [
'logout'
]);
export function createUser(realmId, info) {
const userProxy = Object.create(User.prototype);
// FIXME: This is currently necessary because util/createMethod expects
// the realm id to be present on any object that is used over rpc
userProxy[keys.realm] = "(User object)";
userProxy[keys.id] = info.id;
userProxy[keys.type] = objectTypes.USER;
Object.assign(userProxy, info.data);
return userProxy;
}

View File

@ -18,11 +18,16 @@
'use strict';
class AuthError extends Error {
constructor(problem) {
super(problem.title);
Object.assign(this, problem);
}
function AuthError(problem) {
var err = Error.apply(undefined, [problem.title]);
Object.assign(err, problem);
Object.setPrototypeOf(err, AuthError.prototype);
Object.setPrototypeOf(this, err);
return err;
}
AuthError.prototype = Object.create(Error.prototype, {
constructor: { value: AuthError }
});
exports['AuthError'] = AuthError;

View File

@ -6,33 +6,7 @@ function node_require(module) {
return require(module);
}
var post;
if (typeof fetch !== 'undefined') {
post = function(options, callback) {
options.method = 'POST';
// eslint-disable-next-line no-undef
fetch(options.url, options)
.then((response) => {
if (response.status != 200) {
callback(undefined, {statusCode: response.status});
}
else {
return response.text();
}
})
.then((body) => {
callback(undefined, {statusCode: 200}, JSON.parse(body));
})
.catch((error) => {
callback(error);
});
}
}
else {
post = function(options, callback) {
node_require('needle').post(options.url, options.body, options, callback);
}
}
const performFetch = typeof fetch === 'undefined' ? node_require('node-fetch') : fetch;
const url_parse = require("url-parse");
@ -48,29 +22,65 @@ function auth_url(server) {
return server + 'auth';
}
function authenticateRealm(user, fileUrl, realmUrl, callback) {
var url = auth_url(user.server);
var options = {
method: 'POST',
body: JSON.stringify({
data: user.token,
path: url_parse(realmUrl).pathname,
provider: 'realm',
app_id: ''
}),
headers: postHeaders
};
performFetch(url, options, function(error, response, body) {
if (error) {
callback(error);
}
else if (response.statusCode != 200) {
callback(new AuthError('Bad response: ' + response.statusCode));
}
else {
// TODO: validate JSON
callback(undefined, {
token: body.access_token.token,
file_url: url_parse(fileUrl).pathname,
resolved_realm_url: 'realm://' + url_parse(realmUrl).host + body.access_token.token_data.path
});
}
});
}
module.exports = function(realmConstructor) {
function _authenticate(server, json, callback) {
json.app_id = '';
var url = auth_url(server);
var options = {
url: auth_url(server),
method: 'POST',
body: JSON.stringify(json),
headers: postHeaders,
open_timeout: 5000
};
post(options, function(error, response, body) {
if (error) {
callback(error);
}
else if (response.statusCode != 200) {
callback(new AuthError(JSON.parse(body)));
}
else {
// TODO: validate JSON
const token = body.refresh_token.token;
const identity = body.refresh_token.token_data.identity;
callback(undefined, realmConstructor.Sync.User.createUser(server, identity, token, false));
}
});
performFetch(url, options)
.then(function(response) {
if (response.status !== 200) {
return response.json().then(function (body) {
callback(new AuthError(body));
});
} else {
return response.json().then(function (body) {
// TODO: validate JSON
const token = body.refresh_token.token;
const identity = body.refresh_token.token_data.identity;
callback(undefined, realmConstructor.Sync.User.createUser(server, identity, token, false));
})
}
})
.catch(function (err) {
callback(err);
});
}
var methods = {};
@ -106,39 +116,27 @@ module.exports = function(realmConstructor) {
}, callback);
}
methods['_authenticateRealm'] = function(fileUrl, realmUrl, callback) {
var options = {
url: auth_url(this.server),
body: JSON.stringify({
data: this.token,
path: url_parse(realmUrl).pathname,
provider: 'realm',
app_id: ''
}),
headers: postHeaders
};
post(options, function(error, response, body) {
if (error) {
callback(error);
}
else if (response.statusCode != 200) {
callback(new AuthError('Bad response: ' + response.statusCode));
}
else {
// TODO: validate JSON
callback(undefined, {
token: body.access_token.token,
file_url: url_parse(fileUrl).pathname,
resolved_realm_url: 'realm://' + url_parse(realmUrl).host + body.access_token.token_data.path
});
}
});
}
methods['_authenticateRealm'] = authenticateRealm;
for (var name in methods) {
methods[name] = {value: methods[name], configurable: true, writable: true}
}
methods['current'] = {
get: function () {
const allUsers = realmConstructor.Sync.User.all;
const keys = Object.keys(allUsers);
if (keys.length === 0) {
return undefined;
} else if (keys.length > 1) {
throw new Error("Multiple users are logged in");
}
return allUsers[keys[0]];
}
}
return methods;
}
}
module.exports._authenticateRealm = authenticateRealm;

View File

@ -54,7 +54,7 @@
},
"dependencies": {
"nan": "^2.3.3",
"needle": "^1.3.0",
"node-fetch": "^1.6.3",
"node-pre-gyp": "^0.6.30",
"request": "^2.78.0",
"sync-request": "^3.0.1",

View File

@ -253,10 +253,15 @@ case "$TARGET" in
npm run jsdoc
;;
"realmjs")
download_server
start_server
pushd src
xctest RealmJS
stop_server
;;
"react-tests")
download_server
start_server
pushd tests/react-test-app
npm install
open_chrome
@ -264,6 +269,7 @@ case "$TARGET" in
pushd ios
xctest ReactTestApp
stop_server
;;
"react-example")
pushd examples/ReactExample
@ -276,6 +282,8 @@ case "$TARGET" in
xctest ReactExample
;;
"react-tests-android")
# download_server
# start_server
[[ $CONFIGURATION == 'Debug' ]] && exit 0
XCPRETTY=''
@ -309,6 +317,7 @@ case "$TARGET" in
echo "********* TESTS COMPLETED *********";
echo "********* File location: $(pwd)/tests.xml *********";
cat tests.xml
# stop_server
;;
"node")
if [ "$(uname)" = 'Darwin' ]; then

View File

@ -19,6 +19,11 @@
#include "platform.hpp"
#include "realm_coordinator.hpp"
#if REALM_ENABLE_SYNC
#include "sync/sync_manager.hpp"
#include "sync/sync_user.hpp"
#endif
namespace realm {
namespace js {
@ -40,5 +45,16 @@ void delete_all_realms() {
realm::remove_realm_files_from_directory(realm::default_realm_file_directory());
}
void clear_test_state() {
delete_all_realms();
#if REALM_ENABLE_SYNC
for(auto &user : SyncManager::shared().all_logged_in_users()) {
user->log_out();
}
SyncManager::shared().reset_for_testing();
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
#endif
}
} // js
} // realm

View File

@ -132,6 +132,7 @@ class RealmDelegate : public BindingContext {
std::string default_path();
void set_default_path(std::string path);
void delete_all_realms();
void clear_test_state();
template<typename T>
class RealmClass : public ClassDefinition<T, SharedRealm, ObservableClass<T>> {
@ -455,14 +456,7 @@ template<typename T>
void RealmClass<T>::clear_test_state(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
delete_all_realms();
#if REALM_ENABLE_SYNC
for(auto &user : SyncManager::shared().all_logged_in_users()) {
user->log_out();
}
SyncManager::shared().reset_for_testing();
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
#endif
js::clear_test_state();
}
template<typename T>

View File

@ -75,11 +75,11 @@ public:
{"createUser", wrap<create_user>}
};
static void current_user(ContextType ctx, ObjectType object, ReturnValue &return_value);
/*static void current_user(ContextType ctx, ObjectType object, ReturnValue &return_value);*/
static void all_users(ContextType ctx, ObjectType object, ReturnValue &return_value);
PropertyMap<T> const static_properties = {
{"current", {wrap<current_user>, nullptr}},
/*{"current", {wrap<current_user>, nullptr}},*/
{"all", {wrap<all_users>, nullptr}},
};
@ -135,6 +135,7 @@ void UserClass<T>::all_users(ContextType ctx, ObjectType object, ReturnValue &re
return_value.set(users);
}
/*
template<typename T>
void UserClass<T>::current_user(ContextType ctx, ObjectType object, ReturnValue &return_value) {
SharedUser *current = nullptr;
@ -154,6 +155,7 @@ void UserClass<T>::current_user(ContextType ctx, ObjectType object, ReturnValue
return_value.set_undefined();
}
}
*/
template<typename T>
void UserClass<T>::logout(ContextType ctx, ObjectType object, size_t, const ValueType[], ReturnValue &) {
@ -267,15 +269,27 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
EventLoopDispatcher<SyncBindSessionHandler> bind([=](const std::string& path, const realm::SyncConfig& config, std::shared_ptr<SyncSession>) {
HANDLESCOPE
ObjectType user_constructor = Object::validated_get_object(protected_ctx, protected_sync, std::string("User"));
FunctionType authenticate = Object::validated_get_function(protected_ctx, user_constructor, std::string("_authenticateRealm"));
if (config.user->is_admin()) {
// FIXME: This log-in callback is called while the object store still holds some sync-related locks.
// Notify the object store of the access token asynchronously to avoid the deadlock that would result
// from reentering the object store here.
auto thread = std::thread([path, config]{
auto session = SyncManager::shared().get_existing_active_session(path);
session->refresh_access_token(config.user->refresh_token(), config.realm_url);
});
thread.detach();
}
else {
ObjectType user_constructor = Object::validated_get_object(ctx, protected_sync, std::string("User"));
FunctionType authenticate = Object::validated_get_function(ctx, user_constructor, std::string("_authenticateRealm"));
ValueType arguments[3];
arguments[0] = Value::from_string(protected_ctx, path.c_str());
arguments[1] = Value::from_string(protected_ctx, config.realm_url.c_str());
arguments[2] = refresh;
ObjectType user = create_object<T, UserClass<T>>(protected_ctx, new SharedUser(config.user));
Function::call(protected_ctx, authenticate, user, 3, arguments);
ValueType arguments[4];
arguments[0] = create_object<T, UserClass<T>>(ctx, new SharedUser(config.user));
arguments[1] = Value::from_string(protected_ctx, path.c_str());
arguments[2] = Value::from_string(protected_ctx, config.realm_url.c_str());
arguments[3] = refresh;
Function::call(protected_ctx, authenticate, 4, arguments);
}
});
ObjectType user = Object::validated_get_object(ctx, sync_config_object, "user");

View File

@ -42,6 +42,7 @@ static const char * const RealmObjectTypesList = "list";
static const char * const RealmObjectTypesObject = "object";
static const char * const RealmObjectTypesResults = "results";
static const char * const RealmObjectTypesRealm = "realm";
static const char * const RealmObjectTypesUser = "user";
static const char * const RealmObjectTypesUndefined = "undefined";
static RPCServer*& get_rpc_server(JSGlobalContextRef ctx) {
@ -111,6 +112,11 @@ RPCServer::RPCServer() {
jsc::String realm_string = "Realm";
JSObjectRef realm_constructor = jsc::Object::validated_get_constructor(m_context, JSContextGetGlobalObject(m_context), realm_string);
JSValueRef authenticateRealm = deserialize_json_value(dict["authenticateRealm"]);
JSObjectRef sync_constructor = (JSObjectRef)jsc::Object::get_property(m_context, realm_constructor, "Sync");
JSObjectRef user_constructor = (JSObjectRef)jsc::Object::get_property(m_context, sync_constructor, "User");
jsc::Object::set_property(m_context, user_constructor, "_authenticateRealm", authenticateRealm);
m_session_id = store_object(realm_constructor);
return (json){{"result", m_session_id}};
@ -133,6 +139,27 @@ RPCServer::RPCServer() {
RPCObjectID realm_id = store_object(realm_object);
return (json){{"result", realm_id}};
};
m_requests["/create_user"] = [this](const json dict) {
JSObjectRef realm_constructor = m_session_id ? JSObjectRef(m_objects[m_session_id]) : NULL;
if (!realm_constructor) {
throw std::runtime_error("Realm constructor not found!");
}
JSObjectRef sync_constructor = (JSObjectRef)jsc::Object::get_property(m_context, realm_constructor, "Sync");
JSObjectRef user_constructor = (JSObjectRef)jsc::Object::get_property(m_context, sync_constructor, "User");
JSObjectRef create_user_method = (JSObjectRef)jsc::Object::get_property(m_context, user_constructor, "createUser");
json::array_t args = dict["arguments"];
size_t arg_count = args.size();
JSValueRef arg_values[arg_count];
for (size_t i = 0; i < arg_count; i++) {
arg_values[i] = deserialize_json_value(args[i]);
}
JSObjectRef user_object = (JSObjectRef)jsc::Function::call(m_context, create_user_method, arg_count, arg_values);
return (json){{"result", serialize_json_value(user_object)}};
};
m_requests["/call_method"] = [this](const json dict) {
JSObjectRef object = m_objects[dict["id"].get<RPCObjectID>()];
std::string method_string = dict["name"].get<std::string>();
@ -181,6 +208,18 @@ RPCServer::RPCServer() {
m_objects.erase(oid);
return json::object();
};
m_requests["/get_all_users"] = [this](const json dict) {
JSObjectRef realm_constructor = m_session_id ? JSObjectRef(m_objects[m_session_id]) : NULL;
if (!realm_constructor) {
throw std::runtime_error("Realm constructor not found!");
}
JSObjectRef sync_constructor = (JSObjectRef)jsc::Object::get_property(m_context, realm_constructor, "Sync");
JSObjectRef user_constructor = (JSObjectRef)jsc::Object::get_property(m_context, sync_constructor, "User");
JSValueRef value = jsc::Object::get_property(m_context, user_constructor, "all");
return (json){{"result", serialize_json_value(value)}};
};
m_requests["/clear_test_state"] = [this](const json dict) {
// The session ID points to the Realm constructor object, which should remain.
auto realm_constructor = m_objects[m_session_id];
@ -192,7 +231,7 @@ RPCServer::RPCServer() {
m_callbacks.clear();
JSGarbageCollect(m_context);
js::delete_all_realms();
js::clear_test_state();
return json::object();
};
@ -336,6 +375,20 @@ json RPCServer::serialize_json_value(JSValueRef js_value) {
{"id", store_object(js_object)},
};
}
else if (jsc::Object::is_instance<js::UserClass<jsc::Types>>(m_context, js_object)) {
auto user = *jsc::Object::get_internal<js::UserClass<jsc::Types>>(js_object);
json user_dict {
{"identity", user->identity()},
{"token", user->refresh_token()},
{"server", user->server_url()},
{"isAdmin", user->is_admin()}
};
return {
{"type", RealmObjectTypesUser},
{"id", store_object(js_object)},
{"data", user_dict}
};
}
else if (jsc::Value::is_array(m_context, js_object)) {
uint32_t length = jsc::Object::validated_get_length(m_context, js_object);
std::vector<json> array;

View File

@ -27,9 +27,21 @@ var TESTS = {
ResultsTests: require('./results-tests'),
QueryTests: require('./query-tests'),
EncryptionTests: require('./encryption-tests'),
MigrationTests: require('./migration-tests'),
MigrationTests: require('./migration-tests')
};
// If sync is enabled, run the user tests
if (Realm.Sync) {
TESTS.UserTests = require('./user-tests');
}
function node_require(module) { return require(module); }
// If on node, run the async tests
if (typeof process === 'object' && process + '' === '[object process]') {
TESTS.AsyncTests = node_require('./async-tests');
}
var SPECIAL_METHODS = {
beforeEach: true,
afterEach: true,

View File

@ -382,5 +382,30 @@ module.exports = {
TestCase.assertEqual(objects.length, 0);
TestCase.assertEqual(snapshot.length, 0);
});
},
testAddListener: function() {
return new Promise((resolve, _reject) => {
var realm = new Realm({ schema: [schemas.TestObject] });
realm.write(() => {
realm.create('TestObject', { doubleCol: 1 });
realm.create('TestObject', { doubleCol: 2 });
realm.create('TestObject', { doubleCol: 3 });
});
realm.objects('TestObject').addListener((testObjects, changes) => {
// TODO: First notification is empty, so perform these
// assertions on the second call. However, there is a race condition
// in React Native, so find a way to do this in a robust way.
//TestCase.assertEqual(testObjects.length, 4);
//TestCase.assertEqual(changes.insertions.length, 1);
resolve();
});
realm.write(() => {
realm.create('TestObject', { doubleCol: 1 });
});
})
}
};

View File

@ -47,10 +47,10 @@ function assertIsSameUser(value, user) {
TestCase.assertEqual(value.isAdmin, user.isAdmin);
}
function assertIsError(error, code) {
function assertIsError(error, message) {
TestCase.assertInstanceOf(error, Error, 'The API should return an Error');
if (code) {
TestCase.assertEqual(error.code, code);
if (message) {
TestCase.assertEqual(error.message, message);
}
}
@ -87,8 +87,8 @@ function callbackTest(requestFunc, callback) {
});
}
module.exports = {
testLogout() {
var username = uuid();
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
@ -131,7 +131,7 @@ module.exports = {
TestCase.assertUndefined(user);
});
});
},
},
testRegisterMissingUsername() {
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', undefined, 'password', callback), (error, user) => {
@ -152,26 +152,29 @@ module.exports = {
var username = uuid();
// Because it waits for answer this takes some time..
return callbackTest((callback) => Realm.Sync.User.register('http://fake_host.local', username, 'password', callback), (error, user) => {
assertIsError(error, 'ECONNRESET');
assertIsError(error);
TestCase.assertUndefined(user);
});
},
testLogin() {
var username = uuid();
// Create user, logout the new user, then login
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
failOnError(error);
user.logout();
Realm.Sync.User.login('http://localhost:9080', username, 'password', (error, user) => {
return new Promise((resolve, _reject) => {
var username = uuid();
// Create user, logout the new user, then login
callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
failOnError(error);
assertIsUser(user);
user.logout();
// Can we open a realm with the logged-in user?
var realm = new Realm({sync: {user: user, url: 'realm://localhost:9080/~/test'}});
TestCase.assertInstanceOf(realm, Realm);
realm.close();
Realm.Sync.User.login('http://localhost:9080', username, 'password', (error, user) => {
failOnError(error);
assertIsUser(user);
// Can we open a realm with the logged-in user?
var realm = new Realm({ sync: { user: user, url: 'realm://localhost:9080/~/test' } });
TestCase.assertInstanceOf(realm, Realm);
realm.close();
resolve();
});
});
});
},
@ -200,10 +203,17 @@ module.exports = {
testLoginServerOffline() {
var username = uuid();
// Because it waits for answer this takes some time..
return callbackTest((callback) => Realm.Sync.User.register('http://fake_host.local', username, 'password', callback), (error, user) => {
assertIsError(error);
TestCase.assertUndefined(user);
// Because it waits for answer this takes some time..
return new Promise((resolve, reject) => {
Realm.Sync.User.register('http://fake_host.local', username, 'password', (error, user) => {
try {
assertIsError(error);
TestCase.assertUndefined(user);
resolve();
}
catch (e) { reject(e) }
});
});
},
@ -266,7 +276,7 @@ module.exports = {
});
});
});
},
},
/* This test fails because of realm-object-store #243 . We should use 2 users.
testSynchronizeChangesWithTwoClientsAndOneUser() {

View File

@ -1,39 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
/* eslint-env es6, node */
/* eslint-disable no-console */
'use strict';
const Realm = require('realm');
const userTests = require('../js/user-tests');
describe('SyncTests', () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
beforeEach(() => Realm.clearTestState());
afterEach(() => Realm.clearTestState());
for (const testName in userTests) {
it(testName, (done) => {
userTests[testName]()
.catch((e) => fail(e))
.then(done);
});
}
});

View File

@ -27,6 +27,8 @@ const path = require('path');
const Realm = require('realm');
const RealmTests = require('../js');
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
// Create this method with appropriate implementation for Node testing.
Realm.copyBundledRealmFiles = function() {
let sourceDir = path.join(__dirname, '../data');
@ -52,9 +54,14 @@ for (const suiteName in tests) {
beforeEach(() => RealmTests.runTest(suiteName, 'beforeEach'));
for (const testName of tests[suiteName]) {
it(testName, () => {
it(testName, (done) => {
try {
RealmTests.runTest(suiteName, testName)
let result = RealmTests.runTest(suiteName, testName);
if (result instanceof Promise) {
result.then(done, fail);
} else {
done();
}
}
catch (e) {
fail(e);
@ -65,14 +72,3 @@ for (const suiteName in tests) {
afterEach(() => RealmTests.runTest(suiteName, 'afterEach'));
});
}
const asyncTests = require('../js/async-tests');
describe('AsyncTests', () => {
beforeEach(() => Realm.clearTestState());
for (const testName in asyncTests) {
it(testName, (done) => asyncTests[testName]().catch((e) => fail(e)).then(done));
}
afterEach(() => Realm.clearTestState());
});