mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-13 15:54:59 +00:00
All tests now pass on Node
This commit is contained in:
parent
bd4088ce91
commit
133289ad85
@ -5,7 +5,6 @@
|
||||
"sources": [
|
||||
"src/js_realm.cpp",
|
||||
"src/node/node_init.cpp",
|
||||
"src/node/node_object_accessor.cpp",
|
||||
"src/object-store/src/index_set.cpp",
|
||||
"src/object-store/src/list.cpp",
|
||||
"src/object-store/src/object_schema.cpp",
|
||||
|
21
lib/index.js
21
lib/index.js
@ -24,9 +24,13 @@ var realmConstructor;
|
||||
if (typeof Realm != 'undefined') {
|
||||
// The global Realm constructor should be available on device (using JavaScriptCore).
|
||||
realmConstructor = Realm; // eslint-disable-line no-undef
|
||||
} else if (typeof navigator != 'undefined' && navigator.userAgent) { // eslint-disable-line no-undef
|
||||
// eslint-disable-next-line
|
||||
} else if (typeof navigator != 'undefined' && navigator.userAgent) {
|
||||
// The userAgent will be defined when running in a browser (such as Chrome debugging mode).
|
||||
realmConstructor = require('./browser').default; // (exported as ES6 module)
|
||||
// eslint-disable-next-line
|
||||
} else if (typeof process == 'object' && ('' + process) == '[object process]') {
|
||||
realmConstructor = require('bindings')('realm').Realm;
|
||||
} else {
|
||||
throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!');
|
||||
}
|
||||
@ -34,4 +38,19 @@ if (typeof Realm != 'undefined') {
|
||||
// Add the specified Array methods to the Collection prototype.
|
||||
Object.defineProperties(realmConstructor.Collection.prototype, arrayMethods);
|
||||
|
||||
// TODO: Remove this now useless object.
|
||||
Object.defineProperty(realmConstructor, 'Types', {
|
||||
value: Object.freeze({
|
||||
'BOOL': 'bool',
|
||||
'INT': 'int',
|
||||
'FLOAT': 'float',
|
||||
'DOUBLE': 'double',
|
||||
'STRING': 'string',
|
||||
'DATE': 'date',
|
||||
'DATA': 'data',
|
||||
'OBJECT': 'object',
|
||||
'LIST': 'list',
|
||||
})
|
||||
});
|
||||
|
||||
module.exports = realmConstructor;
|
||||
|
@ -48,6 +48,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bindings": "^1.2.1",
|
||||
"mockery": "^1.6.2",
|
||||
"nan": "^2.2.1",
|
||||
"node-gyp": "^3.3.1",
|
||||
"rnpm": "1.5.2",
|
||||
|
@ -50,11 +50,10 @@
|
||||
F60102DF1CBB96D300EC01BA /* async_query.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED61C88F2BA007F774C /* async_query.cpp */; };
|
||||
F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; };
|
||||
F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; };
|
||||
F60102E41CBBB19700EC01BA /* node_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */; };
|
||||
F60102E51CBBB19700EC01BA /* node_object_accessor.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */; };
|
||||
F60102E81CBBB36500EC01BA /* jsc_object_accessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */; };
|
||||
F60102E91CBCAEC500EC01BA /* platform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 029048381C042A8F00ABDED4 /* platform.mm */; };
|
||||
F60102EA1CBCAFC300EC01BA /* node_dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.c */; };
|
||||
F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */; };
|
||||
F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; };
|
||||
F620F0581CB766DA0082977B /* node_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F620F0571CB766DA0082977B /* node_init.cpp */; };
|
||||
F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; };
|
||||
@ -174,7 +173,6 @@
|
||||
02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = weak_realm_notifier.hpp; path = src/impl/weak_realm_notifier.hpp; sourceTree = "<group>"; };
|
||||
F60102CF1CBB814A00EC01BA /* node_init.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = node_init.hpp; sourceTree = "<group>"; };
|
||||
F60102D11CBB865A00EC01BA /* jsc_init.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_init.hpp; sourceTree = "<group>"; };
|
||||
F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_object_accessor.cpp; sourceTree = "<group>"; };
|
||||
F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = node_object_accessor.hpp; sourceTree = "<group>"; };
|
||||
F60102E61CBBB36500EC01BA /* jsc_object_accessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = jsc_object_accessor.cpp; sourceTree = "<group>"; };
|
||||
F60102E71CBBB36500EC01BA /* jsc_object_accessor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_object_accessor.hpp; sourceTree = "<group>"; };
|
||||
@ -187,7 +185,7 @@
|
||||
F620F0591CB7B4C80082977B /* js_object_accessor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_object_accessor.hpp; sourceTree = "<group>"; };
|
||||
F620F0741CB9F60C0082977B /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||
F6267BC91CADC30000AC36B1 /* js_util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_util.hpp; sourceTree = "<group>"; };
|
||||
F6267BCA1CADC49200AC36B1 /* node_dummy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = node_dummy.c; sourceTree = "<group>"; };
|
||||
F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_dummy.cpp; sourceTree = "<group>"; };
|
||||
F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libRealmNode.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRealmJS.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F63FF2DC1C15659A00B3B8E0 /* RealmJS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RealmJS.mm; sourceTree = "<group>"; };
|
||||
@ -410,13 +408,12 @@
|
||||
F62BF9001CAC72C40022BCDC /* Node */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F6267BCA1CADC49200AC36B1 /* node_dummy.c */,
|
||||
F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */,
|
||||
F60102CF1CBB814A00EC01BA /* node_init.hpp */,
|
||||
F620F0571CB766DA0082977B /* node_init.cpp */,
|
||||
F620F0551CB655A50082977B /* node_class.hpp */,
|
||||
F6874A351CAC792D00EEEE36 /* node_types.hpp */,
|
||||
F60102E31CBBB19700EC01BA /* node_object_accessor.hpp */,
|
||||
F60102E21CBBB19700EC01BA /* node_object_accessor.cpp */,
|
||||
);
|
||||
name = Node;
|
||||
path = node;
|
||||
@ -707,7 +704,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "cp ../object-store/tests/query.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\"";
|
||||
shellScript = "cp -f ../object-store/tests/query.json \"$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/js/query-tests.json\"";
|
||||
};
|
||||
F63FF2C51C12462600B3B8E0 /* Download Core */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@ -741,7 +738,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F60102D31CBB966E00EC01BA /* js_realm.cpp in Sources */,
|
||||
F60102E41CBBB19700EC01BA /* node_object_accessor.cpp in Sources */,
|
||||
F60102D61CBB96B400EC01BA /* object_schema.cpp in Sources */,
|
||||
F60102D41CBB96AB00EC01BA /* index_set.cpp in Sources */,
|
||||
F60102DB1CBB96C600EC01BA /* parser.cpp in Sources */,
|
||||
@ -752,7 +748,7 @@
|
||||
F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */,
|
||||
F60102DA1CBB96C300EC01BA /* shared_realm.cpp in Sources */,
|
||||
F60102E01CBB96D900EC01BA /* realm_coordinator.cpp in Sources */,
|
||||
F60102EA1CBCAFC300EC01BA /* node_dummy.c in Sources */,
|
||||
F60102EA1CBCAFC300EC01BA /* node_dummy.cpp in Sources */,
|
||||
F60102D81CBB96BD00EC01BA /* results.cpp in Sources */,
|
||||
F60102DE1CBB96CF00EC01BA /* weak_realm_notifier.cpp in Sources */,
|
||||
F620F0581CB766DA0082977B /* node_init.cpp in Sources */,
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "js_class.hpp"
|
||||
#include "js_types.hpp"
|
||||
@ -67,10 +67,20 @@ class RealmDelegate : public BindingContext {
|
||||
}
|
||||
|
||||
void add_notification(TFunction notification) {
|
||||
m_notifications.insert(Protected<TFunction>(m_context, notification));
|
||||
for (auto &handler : m_notifications) {
|
||||
if (handler == notification) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_notifications.emplace_back(m_context, notification);
|
||||
}
|
||||
void remove_notification(TFunction notification) {
|
||||
m_notifications.erase(Protected<TFunction>(m_context, notification));
|
||||
for (auto iter = m_notifications.begin(); iter != m_notifications.end(); ++iter) {
|
||||
if (*iter == notification) {
|
||||
m_notifications.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void remove_all_notifications() {
|
||||
m_notifications.clear();
|
||||
@ -81,7 +91,7 @@ class RealmDelegate : public BindingContext {
|
||||
|
||||
private:
|
||||
Protected<TGlobalContext> m_context;
|
||||
std::set<Protected<TFunction>> m_notifications;
|
||||
std::list<Protected<TFunction>> m_notifications;
|
||||
std::weak_ptr<Realm> m_realm;
|
||||
|
||||
void notify(const char *notification_name) {
|
||||
@ -142,11 +152,11 @@ class Realm {
|
||||
static void GetDefaultPath(TContext, TObject, ReturnValue &);
|
||||
static void SetDefaultPath(TContext, TObject, TValue value);
|
||||
|
||||
static TObject create_constructor(TContext ctx) {
|
||||
TObject realm_constructor = ObjectWrap<T, RealmClass<T>>::create_constructor(ctx);
|
||||
TObject collection_constructor = ObjectWrap<T, CollectionClass<T>>::create_constructor(ctx);
|
||||
TObject list_constructor = ObjectWrap<T, ListClass<T>>::create_constructor(ctx);
|
||||
TObject results_constructor = ObjectWrap<T, ResultsClass<T>>::create_constructor(ctx);
|
||||
static TFunction create_constructor(TContext ctx) {
|
||||
TFunction realm_constructor = ObjectWrap<T, RealmClass<T>>::create_constructor(ctx);
|
||||
TFunction collection_constructor = ObjectWrap<T, CollectionClass<T>>::create_constructor(ctx);
|
||||
TFunction list_constructor = ObjectWrap<T, ListClass<T>>::create_constructor(ctx);
|
||||
TFunction results_constructor = ObjectWrap<T, ResultsClass<T>>::create_constructor(ctx);
|
||||
|
||||
PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete);
|
||||
Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes);
|
||||
|
@ -73,6 +73,7 @@ struct Value {
|
||||
|
||||
static bool is_array(TContext, const TValue &);
|
||||
static bool is_array_buffer(TContext, const TValue &);
|
||||
static bool is_array_buffer_view(TContext, const TValue &);
|
||||
static bool is_boolean(TContext, const TValue &);
|
||||
static bool is_constructor(TContext, const TValue &);
|
||||
static bool is_date(TContext, const TValue &);
|
||||
@ -239,7 +240,6 @@ class Protected {
|
||||
bool operator!=(const TValue &) const;
|
||||
bool operator==(const Protected<TValue> &) const;
|
||||
bool operator!=(const Protected<TValue> &) const;
|
||||
bool operator<(const Protected<TValue> &) const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -249,6 +249,8 @@ struct Exception : public std::runtime_error {
|
||||
|
||||
const Protected<TValue> m_value;
|
||||
|
||||
Exception(TContext ctx, const std::string &message)
|
||||
: std::runtime_error(message), m_value(value(ctx, message)) {}
|
||||
Exception(TContext ctx, const TValue &val)
|
||||
: std::runtime_error(std::string(Value<T>::to_string(ctx, val))), m_value(ctx, val) {}
|
||||
|
||||
|
@ -40,9 +40,11 @@ template<typename ClassType>
|
||||
class ObjectWrap {
|
||||
public:
|
||||
using Internal = typename ClassType::Internal;
|
||||
|
||||
operator Internal*() const {
|
||||
return m_object.get();
|
||||
}
|
||||
|
||||
ObjectWrap<ClassType>& operator=(Internal* object) {
|
||||
if (m_object.get() != object) {
|
||||
m_object = std::unique_ptr<Internal>(object);
|
||||
@ -134,7 +136,7 @@ private:
|
||||
return index_setter(ctx, object, index, value, exception);
|
||||
}
|
||||
else {
|
||||
*exception = Exception::value(ctx, std::string("Cannot assigned to read only index ") + util::to_string(index));
|
||||
*exception = Exception::value(ctx, std::string("Cannot assign to read only index ") + util::to_string(index));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -288,33 +290,9 @@ class ObjectWrap<void> {
|
||||
public:
|
||||
using Internal = void;
|
||||
|
||||
operator Internal*() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ObjectWrap<void>& operator=(Internal* object) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
static JSClassRef get_class() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static JSClassRef get_constructor_class() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static JSObjectRef create_instance(JSContextRef ctx, Internal* internal = nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static JSObjectRef create_constructor(JSContextRef ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool has_instance(JSContextRef ctx, JSValueRef value) {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// The declared static variables must be defined as well.
|
||||
|
@ -27,39 +27,8 @@ extern "C" {
|
||||
using namespace realm;
|
||||
using namespace realm::jsc;
|
||||
|
||||
JSValueRef RJSTypeGet(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) {
|
||||
std::string str = String(propertyName);
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
|
||||
return Value::from_string(ctx, str);
|
||||
}
|
||||
|
||||
JSClassRef RJSRealmTypeClass() {
|
||||
JSClassDefinition realmTypesDefinition = kJSClassDefinitionEmpty;
|
||||
realmTypesDefinition.className = "PropTypes";
|
||||
JSStaticValue types[] = {
|
||||
{ "BOOL", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ "INT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ "FLOAT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ "DOUBLE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ "STRING", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ "DATE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ "DATA", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ "OBJECT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ "LIST", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
|
||||
{ NULL, NULL, NULL, 0 }
|
||||
};
|
||||
realmTypesDefinition.staticValues = types;
|
||||
return JSClassCreate(&realmTypesDefinition);
|
||||
}
|
||||
|
||||
JSObjectRef RJSConstructorCreate(JSContextRef ctx) {
|
||||
JSObjectRef realm_constructor = js::Realm<Types>::create_constructor(ctx);
|
||||
JSObjectRef types_object = JSObjectMake(ctx, RJSRealmTypeClass(), nullptr);
|
||||
|
||||
// TODO: Either remove this (preferable) or move implementation to JS.
|
||||
jsc::Object::set_property(ctx, realm_constructor, "Types", types_object, js::PropertyAttributes(js::ReadOnly | js::DontEnum | js::DontDelete));
|
||||
|
||||
return realm_constructor;
|
||||
return js::Realm<Types>::create_constructor(ctx);
|
||||
}
|
||||
|
||||
void RJSInitializeInContext(JSContextRef ctx) {
|
||||
|
@ -57,21 +57,6 @@ class Protected {
|
||||
operator T() const {
|
||||
return m_value;
|
||||
}
|
||||
bool operator==(const T &other) const {
|
||||
return m_value == other;
|
||||
}
|
||||
bool operator!=(const T &other) const {
|
||||
return m_value != other;
|
||||
}
|
||||
bool operator==(const Protected<T> &other) const {
|
||||
return m_value == other;
|
||||
}
|
||||
bool operator!=(const Protected<T> &other) const {
|
||||
return m_value != other;
|
||||
}
|
||||
bool operator<(const Protected<T> &other) const {
|
||||
return m_value < other.m_value;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -126,7 +111,7 @@ class ReturnValue<jsc::Types> {
|
||||
const JSContextRef m_context;
|
||||
JSValueRef m_value = nullptr;
|
||||
|
||||
public:
|
||||
public:
|
||||
ReturnValue(JSContextRef ctx) : m_context(ctx) {}
|
||||
|
||||
void set(const JSValueRef &value) {
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include "node_types.hpp"
|
||||
#include "js_class.hpp"
|
||||
#include "js_util.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace node {
|
||||
@ -45,9 +46,15 @@ static inline std::vector<v8::Local<v8::Value>> get_arguments(const Nan::Functio
|
||||
return arguments;
|
||||
}
|
||||
|
||||
static inline void setup_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, Nan::FunctionCallback callback) {
|
||||
Nan::HandleScope scope;
|
||||
static inline void setup_static_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, Nan::FunctionCallback callback) {
|
||||
v8::Local<v8::Function> fn = Nan::GetFunction(Nan::New<v8::FunctionTemplate>(callback)).ToLocalChecked();
|
||||
v8::Local<v8::String> fn_name = Nan::New(name).ToLocalChecked();
|
||||
|
||||
tpl->Set(fn_name, fn, v8::PropertyAttribute::DontEnum);
|
||||
fn->SetName(fn_name);
|
||||
}
|
||||
|
||||
static inline void setup_method(v8::Local<v8::FunctionTemplate> tpl, const std::string &name, Nan::FunctionCallback callback) {
|
||||
v8::Local<v8::Signature> signature = Nan::New<v8::Signature>(tpl);
|
||||
v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(callback, v8::Local<v8::Value>(), signature);
|
||||
v8::Local<v8::Function> fn = Nan::GetFunction(t).ToLocalChecked();
|
||||
@ -58,54 +65,83 @@ static inline void setup_method(v8::Local<v8::FunctionTemplate> tpl, const std::
|
||||
fn->SetName(fn_name);
|
||||
}
|
||||
|
||||
static inline void setup_property(v8::Local<v8::ObjectTemplate> tpl, const std::string &name, const PropertyType &property) {
|
||||
Nan::HandleScope scope;
|
||||
|
||||
v8::Local<v8::String> prop_name = Nan::New(name).ToLocalChecked();
|
||||
v8::PropertyAttribute attributes = static_cast<v8::PropertyAttribute>(v8::DontEnum | v8::DontDelete | (property.setter ? v8::None : v8::ReadOnly));
|
||||
|
||||
Nan::SetAccessor(tpl, prop_name, property.getter, property.setter, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
|
||||
static inline void set_readonly_property(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_SETTER_ARGS_TYPE info) {
|
||||
std::string message = std::string("Cannot assign to read only property '") + std::string(String(property)) + "'";
|
||||
Nan::ThrowError(message.c_str());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline void set_readonly_index(uint32_t index, v8::Local<v8::Value> value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) {
|
||||
std::string message = std::string("Cannot assign to read only index ") + util::to_string(index);
|
||||
Nan::ThrowError(message.c_str());
|
||||
}
|
||||
|
||||
template<typename TargetType>
|
||||
static inline void setup_property(v8::Local<TargetType> target, const std::string &name, const PropertyType &property) {
|
||||
v8::Local<v8::String> prop_name = Nan::New(name).ToLocalChecked();
|
||||
v8::PropertyAttribute attributes = v8::PropertyAttribute(v8::DontEnum | v8::DontDelete);
|
||||
|
||||
Nan::SetAccessor(target, prop_name, property.getter, property.setter ?: set_readonly_property, v8::Local<v8::Value>(), v8::DEFAULT, attributes);
|
||||
}
|
||||
|
||||
template<typename ClassType>
|
||||
class ObjectWrap : public Nan::ObjectWrap {
|
||||
static ClassDefinition<T> s_class;
|
||||
static Nan::Persistent<v8::Function> s_constructor;
|
||||
static Nan::Persistent<v8::FunctionTemplate> s_template;
|
||||
using Internal = typename ClassType::Internal;
|
||||
|
||||
std::unique_ptr<T> m_object;
|
||||
static ClassType s_class;
|
||||
|
||||
ObjectWrap(T* object = nullptr) : m_object(object) {}
|
||||
std::unique_ptr<Internal> m_object;
|
||||
|
||||
public:
|
||||
operator T*() const {
|
||||
return m_object.get();
|
||||
ObjectWrap(Internal* object = nullptr) : m_object(object) {}
|
||||
|
||||
template<typename U>
|
||||
static v8::Local<v8::FunctionTemplate> get_superclass(ClassDefinition<U>*) {
|
||||
return ObjectWrap<U>::get_template();
|
||||
}
|
||||
ObjectWrap<T>& operator=(T* object) {
|
||||
if (m_object.get() != object) {
|
||||
m_object = std::unique_ptr<T>(object);
|
||||
|
||||
static void get_nonexistent_property(v8::Local<v8::String> property, Nan::NAN_PROPERTY_GETTER_ARGS_TYPE info) {
|
||||
// Do nothing. This function exists only to prevent a crash where it is used.
|
||||
}
|
||||
|
||||
static void set_property(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) {
|
||||
if (s_class.index_accessor.getter || s_class.index_accessor.setter) {
|
||||
try {
|
||||
// Negative indices are passed into this string property interceptor, so check for them here.
|
||||
validated_positive_index(node::String(property));
|
||||
}
|
||||
catch (std::out_of_range &e) {
|
||||
Nan::ThrowError(Exception::value(info.GetIsolate(), e));
|
||||
return;
|
||||
}
|
||||
catch (std::invalid_argument &) {
|
||||
// Property is not a number.
|
||||
}
|
||||
}
|
||||
if (auto string_setter = s_class.string_accessor.setter) {
|
||||
string_setter(property, value, info);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
static v8::Local<v8::Object> create_instance(v8::Isolate* isolate, T* internal = nullptr) {
|
||||
static void get_indexes(Nan::NAN_INDEX_ENUMERATOR_ARGS_TYPE info) {
|
||||
uint32_t length;
|
||||
try {
|
||||
length = Object::validated_get_length(info.GetIsolate(), info.This());
|
||||
}
|
||||
catch (std::exception &) {
|
||||
// Enumerating properties should never throw an exception.
|
||||
return;
|
||||
}
|
||||
|
||||
v8::Local<v8::Array> array = Nan::New<v8::Array>(length);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
Nan::Set(array, i, Nan::New(i));
|
||||
}
|
||||
|
||||
info.GetReturnValue().Set(array);
|
||||
}
|
||||
|
||||
static v8::Local<v8::FunctionTemplate> create_template() {
|
||||
Nan::EscapableHandleScope scope;
|
||||
|
||||
// TODO: Figure out why this template ends up being empty here.
|
||||
v8::Local<v8::FunctionTemplate> tpl = Nan::New(s_template);
|
||||
v8::Local<v8::Object> instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked();
|
||||
|
||||
auto wrap = new ObjectWrap<T>(internal);
|
||||
wrap->Wrap(instance);
|
||||
|
||||
return scope.Escape(instance);
|
||||
}
|
||||
|
||||
static bool has_instance(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
return Nan::New(s_template)->HasInstance(value);
|
||||
}
|
||||
|
||||
static NAN_MODULE_INIT(init) {
|
||||
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(construct);
|
||||
v8::Local<v8::ObjectTemplate> instance_tpl = tpl->InstanceTemplate();
|
||||
v8::Local<v8::String> name = Nan::New(s_class.name).ToLocalChecked();
|
||||
@ -113,32 +149,81 @@ class ObjectWrap : public Nan::ObjectWrap {
|
||||
tpl->SetClassName(name);
|
||||
instance_tpl->SetInternalFieldCount(1);
|
||||
|
||||
// TODO: Setup static properties and methods.
|
||||
v8::Local<v8::FunctionTemplate> super_tpl = get_superclass(s_class.superclass);
|
||||
if (!super_tpl.IsEmpty()) {
|
||||
tpl->Inherit(super_tpl);
|
||||
}
|
||||
|
||||
// Static properties are setup in create_constructor() because V8.
|
||||
for (auto &pair : s_class.static_methods) {
|
||||
setup_static_method(tpl, pair.first, pair.second);
|
||||
}
|
||||
for (auto &pair : s_class.methods) {
|
||||
setup_method(tpl, pair.first, pair.second);
|
||||
}
|
||||
for (auto &pair : s_class.properties) {
|
||||
setup_property(instance_tpl, pair.first, pair.second);
|
||||
setup_property<v8::ObjectTemplate>(instance_tpl, pair.first, pair.second);
|
||||
}
|
||||
|
||||
if (s_class.index_accessor.getter) {
|
||||
// TODO: Add our own index enumerator.
|
||||
auto &index_accessor = s_class.index_accessor;
|
||||
Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter);
|
||||
Nan::SetIndexedPropertyHandler(instance_tpl, index_accessor.getter, index_accessor.setter ?: set_readonly_index, 0, 0, get_indexes);
|
||||
}
|
||||
if (s_class.string_accessor.getter) {
|
||||
if (s_class.string_accessor.getter || s_class.index_accessor.getter || s_class.index_accessor.setter) {
|
||||
// Use our own wrapper for the setter since we want to throw for negative indices.
|
||||
auto &string_accessor = s_class.string_accessor;
|
||||
Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter, string_accessor.setter, 0, 0, string_accessor.enumerator);
|
||||
Nan::SetNamedPropertyHandler(instance_tpl, string_accessor.getter ?: get_nonexistent_property, set_property, 0, 0, string_accessor.enumerator);
|
||||
}
|
||||
|
||||
v8::Local<v8::Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
|
||||
s_constructor.Reset(constructor);
|
||||
s_template.Reset(tpl);
|
||||
|
||||
Nan::Set(target, name, constructor);
|
||||
return scope.Escape(tpl);
|
||||
}
|
||||
|
||||
static NAN_METHOD(construct) {
|
||||
public:
|
||||
operator Internal*() const {
|
||||
return m_object.get();
|
||||
}
|
||||
ObjectWrap<ClassType>& operator=(Internal* object) {
|
||||
if (m_object.get() != object) {
|
||||
m_object = std::unique_ptr<Internal>(object);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
static v8::Local<v8::FunctionTemplate> get_template() {
|
||||
static Nan::Persistent<v8::FunctionTemplate> js_template(create_template());
|
||||
return Nan::New(js_template);
|
||||
}
|
||||
|
||||
static v8::Local<v8::Function> create_constructor(v8::Isolate* isolate) {
|
||||
Nan::EscapableHandleScope scope;
|
||||
|
||||
v8::Local<v8::FunctionTemplate> tpl = get_template();
|
||||
v8::Local<v8::Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
|
||||
|
||||
for (auto &pair : s_class.static_properties) {
|
||||
setup_property<v8::Object>(constructor, pair.first, pair.second);
|
||||
}
|
||||
|
||||
return scope.Escape(constructor);
|
||||
}
|
||||
|
||||
static v8::Local<v8::Object> create_instance(v8::Isolate* isolate, Internal* internal = nullptr) {
|
||||
Nan::EscapableHandleScope scope;
|
||||
|
||||
v8::Local<v8::FunctionTemplate> tpl = get_template();
|
||||
v8::Local<v8::Object> instance = Nan::NewInstance(tpl->InstanceTemplate()).ToLocalChecked();
|
||||
|
||||
auto wrap = new ObjectWrap<ClassType>(internal);
|
||||
wrap->Wrap(instance);
|
||||
|
||||
return scope.Escape(instance);
|
||||
}
|
||||
|
||||
static bool has_instance(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
return get_template()->HasInstance(value);
|
||||
}
|
||||
|
||||
static void construct(Nan::NAN_METHOD_ARGS_TYPE info) {
|
||||
if (!info.IsConstructCall()) {
|
||||
Nan::ThrowError("Constructor must be called with new");
|
||||
}
|
||||
@ -148,7 +233,7 @@ class ObjectWrap : public Nan::ObjectWrap {
|
||||
v8::Local<v8::Object> this_object = info.This();
|
||||
info.GetReturnValue().Set(this_object);
|
||||
|
||||
auto wrap = new ObjectWrap<T>();
|
||||
auto wrap = new ObjectWrap<ClassType>();
|
||||
wrap->Wrap(this_object);
|
||||
|
||||
try {
|
||||
@ -164,10 +249,19 @@ class ObjectWrap : public Nan::ObjectWrap {
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class ObjectWrap<void> {
|
||||
public:
|
||||
using Internal = void;
|
||||
|
||||
static v8::Local<v8::FunctionTemplate> get_template() {
|
||||
return v8::Local<v8::FunctionTemplate>();;
|
||||
}
|
||||
};
|
||||
|
||||
// The declared static variables must be defined as well.
|
||||
template<typename T> ClassDefinition<T> ObjectWrap<T>::s_class;
|
||||
template<typename T> Nan::Persistent<v8::Function> ObjectWrap<T>::s_constructor;
|
||||
template<typename T> Nan::Persistent<v8::FunctionTemplate> ObjectWrap<T>::s_template;
|
||||
template<typename ClassType>
|
||||
ClassType ObjectWrap<ClassType>::s_class;
|
||||
|
||||
} // node
|
||||
|
||||
@ -190,7 +284,7 @@ void wrap(Nan::NAN_METHOD_ARGS_TYPE info) {
|
||||
}
|
||||
}
|
||||
|
||||
Type::template<node::PropertyType::GetterType F>
|
||||
template<node::PropertyType::GetterType F>
|
||||
void wrap(v8::Local<v8::String> property, Nan::NAN_GETTER_ARGS_TYPE info) {
|
||||
v8::Isolate* isolate = info.GetIsolate();
|
||||
node::ReturnValue return_value(info.GetReturnValue());
|
||||
@ -220,6 +314,10 @@ void wrap(uint32_t index, Nan::NAN_INDEX_GETTER_ARGS_TYPE info) {
|
||||
try {
|
||||
F(isolate, info.This(), index, return_value);
|
||||
}
|
||||
catch (std::out_of_range &) {
|
||||
// Out-of-bounds index getters should just return undefined in JS.
|
||||
return_value.set_undefined();
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
Nan::ThrowError(node::Exception::value(isolate, e));
|
||||
}
|
||||
@ -229,7 +327,10 @@ template<node::IndexPropertyType::SetterType F>
|
||||
void wrap(uint32_t index, v8::Local<v8::Value> value, Nan::NAN_INDEX_SETTER_ARGS_TYPE info) {
|
||||
v8::Isolate* isolate = info.GetIsolate();
|
||||
try {
|
||||
F(isolate, info.This(), index, value);
|
||||
if (F(isolate, info.This(), index, value)) {
|
||||
// Indicate that the property was intercepted.
|
||||
info.GetReturnValue().Set(value);
|
||||
}
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
Nan::ThrowError(node::Exception::value(isolate, e));
|
||||
@ -252,7 +353,10 @@ template<node::StringPropertyType::SetterType F>
|
||||
void wrap(v8::Local<v8::String> property, v8::Local<v8::Value> value, Nan::NAN_PROPERTY_SETTER_ARGS_TYPE info) {
|
||||
v8::Isolate* isolate = info.GetIsolate();
|
||||
try {
|
||||
F(isolate, info.This(), property, value);
|
||||
if (F(isolate, info.This(), property, value)) {
|
||||
// Indicate that the property was intercepted.
|
||||
info.GetReturnValue().Set(value);
|
||||
}
|
||||
}
|
||||
catch(std::exception &e) {
|
||||
Nan::ThrowError(node::Exception::value(isolate, e));
|
||||
|
@ -16,4 +16,15 @@
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void node_module_register(void* mod) {}
|
||||
// NOTE: This dummy file exists only to make Xcode build the Realm Node dynamic library.
|
||||
#include "node.h"
|
||||
|
||||
extern "C" void node_module_register(void* mod) {}
|
||||
|
||||
namespace node {
|
||||
namespace Buffer {
|
||||
bool HasInstance(v8::Local<v8::Value> val) { return false; }
|
||||
char* Data(v8::Local<v8::Value> val) { return nullptr; }
|
||||
size_t Length(v8::Local<v8::Value> val) { return 0; }
|
||||
}
|
||||
}
|
@ -22,13 +22,10 @@ namespace realm {
|
||||
namespace node {
|
||||
|
||||
static void init(v8::Local<v8::Object> exports) {
|
||||
ObjectWrap<SharedRealm>::init(exports);
|
||||
ObjectWrap<realm::List>::init(exports);
|
||||
// RealmObjectWrap::Init(exports);
|
||||
// RealmResultsWrap::Init(exports);
|
||||
// RealmListWrap::Init(exports);
|
||||
//
|
||||
// NODE_SET_METHOD(exports, "__clearCaches", ClearCaches);
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
v8::Local<v8::Function> realm_constructor = js::Realm<Types>::create_constructor(isolate);
|
||||
|
||||
Nan::Set(exports, realm_constructor->GetName(), realm_constructor);
|
||||
}
|
||||
|
||||
} // node
|
||||
|
@ -1,36 +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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "node_object_accessor.hpp"
|
||||
|
||||
using namespace realm;
|
||||
using namespace realm::node;
|
||||
|
||||
using Accessor = js::NativeAccessor<Types>;
|
||||
|
||||
template<>
|
||||
std::string Accessor::to_binary(v8::Isolate* isolate, v8::Local<v8::Value> &value) {
|
||||
// TODO
|
||||
return std::string();
|
||||
}
|
||||
|
||||
template<>
|
||||
v8::Local<v8::Value> Accessor::from_binary(v8::Isolate* isolate, BinaryData data) {
|
||||
// TODO
|
||||
return v8::Local<v8::Value>();
|
||||
}
|
@ -27,4 +27,49 @@ namespace realm {
|
||||
template<>
|
||||
class NativeAccessor<node::Types::Value, node::Types::Context> : public js::NativeAccessor<node::Types> {};
|
||||
|
||||
namespace js {
|
||||
|
||||
template<>
|
||||
inline std::string NativeAccessor<node::Types>::to_binary(v8::Isolate* isolate, v8::Local<v8::Value> &value) {
|
||||
if (Value::is_array_buffer(isolate, value)) {
|
||||
// TODO: This probably needs some abstraction for older V8.
|
||||
#if REALM_V8_ARRAY_BUFFER_API
|
||||
v8::Local<v8::ArrayBuffer> array_buffer = value.As<v8::ArrayBuffer>();
|
||||
v8::ArrayBuffer::Contents contents = array_buffer->GetContents();
|
||||
|
||||
return std::string(static_cast<char*>(contents.Data()), contents.ByteLength());
|
||||
#else
|
||||
// TODO: Implement this for older V8
|
||||
#endif
|
||||
}
|
||||
else if (Value::is_array_buffer_view(isolate, value)) {
|
||||
Nan::TypedArrayContents<char> contents(value);
|
||||
|
||||
return std::string(*contents, contents.length());
|
||||
}
|
||||
else if (::node::Buffer::HasInstance(value)) {
|
||||
return std::string(::node::Buffer::Data(value), ::node::Buffer::Length(value));
|
||||
}
|
||||
|
||||
throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and TypedArray objects to binary");
|
||||
}
|
||||
|
||||
template<>
|
||||
inline v8::Local<v8::Value> NativeAccessor<node::Types>::from_binary(v8::Isolate* isolate, BinaryData data) {
|
||||
#if REALM_V8_ARRAY_BUFFER_API
|
||||
size_t byte_count = data.size();
|
||||
void* bytes = nullptr;
|
||||
|
||||
if (byte_count) {
|
||||
bytes = memcpy(malloc(byte_count), data.data(), byte_count);
|
||||
}
|
||||
|
||||
// An "internalized" ArrayBuffer will free the malloc'd memory when garbage collected.
|
||||
return v8::ArrayBuffer::New(isolate, bytes, byte_count, v8::ArrayBufferCreationMode::kInternalized);
|
||||
#else
|
||||
// TODO: Implement this for older V8
|
||||
#endif
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
@ -27,6 +27,10 @@
|
||||
|
||||
#include "js_types.hpp"
|
||||
|
||||
#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 4 || (V8_MAJOR_VERSION == 4 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 3))
|
||||
#define REALM_V8_ARRAY_BUFFER_API 1
|
||||
#endif
|
||||
|
||||
namespace realm {
|
||||
namespace node {
|
||||
|
||||
@ -72,9 +76,6 @@ class Protected {
|
||||
bool operator!=(const Protected<T> &other) const {
|
||||
return m_value != other.m_value;
|
||||
}
|
||||
bool operator<(const Protected<T> &other) const {
|
||||
return *Nan::New(m_value) < *Nan::New(other.m_value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -188,7 +189,20 @@ inline bool node::Value::is_array(v8::Isolate* isolate, const v8::Local<v8::Valu
|
||||
|
||||
template<>
|
||||
inline bool node::Value::is_array_buffer(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
#if REALM_V8_ARRAY_BUFFER_API
|
||||
return value->IsArrayBuffer();
|
||||
#else
|
||||
// TODO: Implement this!
|
||||
#endif
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool node::Value::is_array_buffer_view(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
#if REALM_V8_ARRAY_BUFFER_API
|
||||
return value->IsArrayBufferView();
|
||||
#else
|
||||
// TODO: Implement this!
|
||||
#endif
|
||||
}
|
||||
|
||||
template<>
|
||||
@ -440,27 +454,27 @@ inline v8::Local<v8::Object> node::Object::create_date(v8::Isolate* isolate, dou
|
||||
}
|
||||
|
||||
template<>
|
||||
template<typename U>
|
||||
inline v8::Local<v8::Object> node::Object::create_instance(v8::Isolate* isolate, U* internal) {
|
||||
return node::ObjectWrap<U>::create_instance(isolate, internal);
|
||||
template<typename ClassType>
|
||||
inline v8::Local<v8::Object> node::Object::create_instance(v8::Isolate* isolate, typename ClassType::Internal* internal) {
|
||||
return node::ObjectWrap<ClassType>::create_instance(isolate, internal);
|
||||
}
|
||||
|
||||
template<>
|
||||
template<typename U>
|
||||
template<typename ClassType>
|
||||
inline bool node::Object::is_instance(v8::Isolate* isolate, const v8::Local<v8::Object> &object) {
|
||||
return node::ObjectWrap<U>::has_instance(isolate, object);
|
||||
return node::ObjectWrap<ClassType>::has_instance(isolate, object);
|
||||
}
|
||||
|
||||
template<>
|
||||
template<typename U>
|
||||
inline U* node::Object::get_internal(const v8::Local<v8::Object> &object) {
|
||||
return *Nan::ObjectWrap::Unwrap<node::ObjectWrap<U>>(object);
|
||||
template<typename ClassType>
|
||||
inline typename ClassType::Internal* node::Object::get_internal(const v8::Local<v8::Object> &object) {
|
||||
return *Nan::ObjectWrap::Unwrap<node::ObjectWrap<ClassType>>(object);
|
||||
}
|
||||
|
||||
template<>
|
||||
template<typename U>
|
||||
inline void node::Object::set_internal(const v8::Local<v8::Object> &object, U* ptr) {
|
||||
auto wrap = Nan::ObjectWrap::Unwrap<node::ObjectWrap<U>>(object);
|
||||
template<typename ClassType>
|
||||
inline void node::Object::set_internal(const v8::Local<v8::Object> &object, typename ClassType::Internal* ptr) {
|
||||
auto wrap = Nan::ObjectWrap::Unwrap<node::ObjectWrap<ClassType>>(object);
|
||||
*wrap = ptr;
|
||||
}
|
||||
|
||||
|
38
tests/index.js
Normal file
38
tests/index.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
const mockery = require('mockery');
|
||||
|
||||
function runTests() {
|
||||
const RealmTests = require('./js');
|
||||
const testNames = RealmTests.getTestNames();
|
||||
|
||||
for (let suiteName in testNames) {
|
||||
let testSuite = RealmTests[suiteName];
|
||||
|
||||
console.log('Starting ' + suiteName);
|
||||
|
||||
for (let testName of testNames[suiteName]) {
|
||||
RealmTests.runTest(suiteName, 'beforeEach');
|
||||
|
||||
try {
|
||||
RealmTests.runTest(suiteName, testName);
|
||||
console.log('+ ' + testName);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('- ' + testName);
|
||||
console.error(e.message, e.stack);
|
||||
}
|
||||
finally {
|
||||
RealmTests.runTest(suiteName, 'afterEach');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main == module) {
|
||||
mockery.enable();
|
||||
mockery.warnOnUnregistered(false);
|
||||
mockery.registerMock('realm', require('..'));
|
||||
|
||||
runTests();
|
||||
}
|
1
tests/js/query-tests.json
Symbolic link
1
tests/js/query-tests.json
Symbolic link
@ -0,0 +1 @@
|
||||
../../src/object-store/tests/query.json
|
Loading…
x
Reference in New Issue
Block a user