From 0c9d7ca54e33301b0ef17a0133d74cb3ab450df0 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Fri, 15 Sep 2017 13:36:04 -0700 Subject: [PATCH] Improve error reporting for incorrect argument counts for Realm methods Report the expected and actual arg counts for too many arguments, and behave more like normal JS when too few are supplied (i.e. complain about the next argument being undefined rather than just saying 'Invalid arguments'). --- src/js_class.hpp | 23 ++++++ src/js_realm.hpp | 176 ++++++++++++++++++++-------------------- src/jsc/jsc_class.hpp | 15 ++++ src/node/node_class.hpp | 17 ++++ tests/js/realm-tests.js | 14 ++-- 5 files changed, 151 insertions(+), 94 deletions(-) diff --git a/src/js_class.hpp b/src/js_class.hpp index 70b28e2e..052ec8f5 100644 --- a/src/js_class.hpp +++ b/src/js_class.hpp @@ -33,6 +33,29 @@ using ConstructorType = void(typename T::Context, typename T::Object, size_t, co template using MethodType = void(typename T::Context, typename T::Function, typename T::Object, size_t, const typename T::Value[], ReturnValue &); +template +struct Arguments { + const typename T::Context ctx; + const size_t count; + const typename T::Value* const value; + + typename T::Value operator[](size_t index) const noexcept { + if (index >= count) { + return Value::from_undefined(ctx); + } + return value[index]; + } + + void validate_maximum(size_t max) const { + if (max < count) { + throw std::invalid_argument(util::format("Invalid arguments: at most %1 expected, but %2 supplied.", max, count)); + } + } +}; + +template +using ArgumentsMethodType = void(typename T::Context, typename T::Function, typename T::Object, Arguments, ReturnValue &); + template struct PropertyType { using GetterType = void(typename T::Context, typename T::Object, ReturnValue &); diff --git a/src/js_realm.hpp b/src/js_realm.hpp index a313d9ae..ee3e9daf 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -123,9 +123,7 @@ class RealmDelegate : public BindingContext { } ObjectType realm_object = create_object>(m_context, new SharedRealm(realm)); - ValueType arguments[2]; - arguments[0] = realm_object; - arguments[1] = Value::from_string(m_context, notification_name); + ValueType arguments[] = {realm_object, Value::from_string(m_context, notification_name)}; std::list> notifications_copy(m_notifications); for (auto &callback : notifications_copy) { @@ -148,6 +146,7 @@ class RealmClass : public ClassDefinition> { using FunctionType = typename T::Function; using ObjectType = typename T::Object; using ValueType = typename T::Value; + using Arguments = js::Arguments; using String = js::String; using Object = js::Object; using Value = js::Value; @@ -165,22 +164,22 @@ public: static FunctionType create_constructor(ContextType); // methods - static void objects(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void object_for_primary_key(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void create(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void delete_one(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void delete_all(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void write(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void begin_transaction(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue&); - static void commit_transaction(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue&); - static void cancel_transaction(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue&); - static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void wait_for_download_completion(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void close(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void compact(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void delete_model(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void objects(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void object_for_primary_key(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void create(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void delete_one(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void delete_all(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void write(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void begin_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&); + static void commit_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&); + static void cancel_transaction(ContextType, FunctionType, ObjectType, Arguments, ReturnValue&); + static void add_listener(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void wait_for_download_completion(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void remove_listener(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void remove_all_listeners(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void close(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void compact(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void delete_model(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); // properties static void get_empty(ContextType, ObjectType, ReturnValue &); @@ -198,10 +197,10 @@ public: static void constructor(ContextType, ObjectType, size_t, const ValueType[]); static SharedRealm create_shared_realm(ContextType, realm::Realm::Config, bool, ObjectDefaultsMap &&, ConstructorMap &&); - static void schema_version(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void clear_test_state(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void copy_bundled_realm_files(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - static void delete_file(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void schema_version(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void clear_test_state(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void copy_bundled_realm_files(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); + static void delete_file(ContextType, FunctionType, ObjectType, Arguments, ReturnValue &); // static properties static void get_default_path(ContextType, ObjectType, ReturnValue &); @@ -504,13 +503,13 @@ SharedRealm RealmClass::create_shared_realm(ContextType ctx, realm::Realm::Co } template -void RealmClass::schema_version(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 1, 2); +void RealmClass::schema_version(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(2); realm::Realm::Config config; - config.path = normalize_realm_path(Value::validated_to_string(ctx, arguments[0])); - if (argc == 2) { - auto encryption_key = Value::validated_to_binary(ctx, arguments[1], "encryptionKey"); + config.path = normalize_realm_path(Value::validated_to_string(ctx, args[0])); + if (args.count == 2) { + auto encryption_key = Value::validated_to_binary(ctx, args[1], "encryptionKey"); config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size()); } @@ -525,23 +524,22 @@ void RealmClass::schema_version(ContextType ctx, FunctionType, ObjectType thi template -void RealmClass::clear_test_state(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0); - +void RealmClass::clear_test_state(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(0); js::clear_test_state(); } template -void RealmClass::copy_bundled_realm_files(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0); +void RealmClass::copy_bundled_realm_files(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(0); realm::copy_bundled_realm_files(); } template -void RealmClass::delete_file(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 1); +void RealmClass::delete_file(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(1); - ValueType value = arguments[0]; + ValueType value = args[0]; if (!Value::is_object(ctx, value)) { throw std::runtime_error("Invalid argument, expected a Realm configuration object"); } @@ -569,9 +567,9 @@ void RealmClass::delete_file(ContextType ctx, FunctionType, ObjectType this_o } template -void RealmClass::delete_model(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 1); - ValueType value = arguments[0]; +void RealmClass::delete_model(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(1); + ValueType value = args[0]; SharedRealm& realm = *get_internal>(this_object); @@ -643,14 +641,14 @@ void RealmClass::get_sync_session(ContextType ctx, ObjectType object, ReturnV #endif template -void RealmClass::wait_for_download_completion(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 2, 3); - auto config_object = Value::validated_to_object(ctx, arguments[0]); - auto callback_function = Value::validated_to_function(ctx, arguments[argc - 1]); +void RealmClass::wait_for_download_completion(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(3); + auto config_object = Value::validated_to_object(ctx, args[0]); + auto callback_function = Value::validated_to_function(ctx, args[1 + (args.count == 3)]); ValueType session_callback = Value::from_null(ctx); - if (argc == 3) { - session_callback = Value::validated_to_function(ctx, arguments[1]); + if (args.count == 3) { + session_callback = Value::validated_to_function(ctx, args[1]); } #if REALM_ENABLE_SYNC @@ -764,25 +762,25 @@ void RealmClass::wait_for_download_completion(ContextType ctx, FunctionType, } template -void RealmClass::objects(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 1); +void RealmClass::objects(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(1); SharedRealm realm = *get_internal>(this_object); std::string object_type; - validated_object_schema_for_value(ctx, realm, arguments[0], object_type); + validated_object_schema_for_value(ctx, realm, args[0], object_type); return_value.set(ResultsClass::create_instance(ctx, realm, object_type)); } template -void RealmClass::object_for_primary_key(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 2); +void RealmClass::object_for_primary_key(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(2); SharedRealm realm = *get_internal>(this_object); std::string object_type; - auto &object_schema = validated_object_schema_for_value(ctx, realm, arguments[0], object_type); + auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0], object_type); NativeAccessor accessor(ctx, realm, object_schema); - auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, arguments[1]); + auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, args[1]); if (realm_object.is_valid()) { return_value.set(RealmObjectClass::create_instance(ctx, std::move(realm_object))); @@ -793,22 +791,22 @@ void RealmClass::object_for_primary_key(ContextType ctx, FunctionType, Object } template -void RealmClass::create(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 2, 3); +void RealmClass::create(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(3); SharedRealm realm = *get_internal>(this_object); realm->verify_open(); std::string object_type; - auto &object_schema = validated_object_schema_for_value(ctx, realm, arguments[0], object_type); + auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0], object_type); - ObjectType object = Value::validated_to_object(ctx, arguments[1], "properties"); - if (Value::is_array(ctx, arguments[1])) { + ObjectType object = Value::validated_to_object(ctx, args[1], "properties"); + if (Value::is_array(ctx, args[1])) { object = Schema::dict_for_property_array(ctx, object_schema, object); } bool update = false; - if (argc == 3) { - update = Value::validated_to_boolean(ctx, arguments[2], "update"); + if (args.count == 3) { + update = Value::validated_to_boolean(ctx, args[2], "update"); } NativeAccessor accessor(ctx, realm, object_schema); @@ -817,8 +815,8 @@ void RealmClass::create(ContextType ctx, FunctionType, ObjectType this_object } template -void RealmClass::delete_one(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 1); +void RealmClass::delete_one(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(1); SharedRealm realm = *get_internal>(this_object); realm->verify_open(); @@ -826,7 +824,7 @@ void RealmClass::delete_one(ContextType ctx, FunctionType, ObjectType this_ob throw std::runtime_error("Can only delete objects within a transaction."); } - ObjectType arg = Value::validated_to_object(ctx, arguments[0]); + ObjectType arg = Value::validated_to_object(ctx, args[0], "object"); if (Object::template is_instance>(ctx, arg)) { auto object = get_internal>(arg); @@ -865,8 +863,8 @@ void RealmClass::delete_one(ContextType ctx, FunctionType, ObjectType this_ob } template -void RealmClass::delete_all(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0); +void RealmClass::delete_all(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(0); SharedRealm realm = *get_internal>(this_object); realm->verify_open(); @@ -881,11 +879,11 @@ void RealmClass::delete_all(ContextType ctx, FunctionType, ObjectType this_ob } template -void RealmClass::write(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 1); +void RealmClass::write(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(1); SharedRealm realm = *get_internal>(this_object); - FunctionType callback = Value::validated_to_function(ctx, arguments[0]); + FunctionType callback = Value::validated_to_function(ctx, args[0]); realm->begin_transaction(); @@ -901,35 +899,35 @@ void RealmClass::write(ContextType ctx, FunctionType, ObjectType this_object, } template -void RealmClass::begin_transaction(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0); +void RealmClass::begin_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(0); SharedRealm realm = *get_internal>(this_object); realm->begin_transaction(); } template -void RealmClass::commit_transaction(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0); +void RealmClass::commit_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(0); SharedRealm realm = *get_internal>(this_object); realm->commit_transaction(); } template -void RealmClass::cancel_transaction(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0); +void RealmClass::cancel_transaction(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(0); SharedRealm realm = *get_internal>(this_object); realm->cancel_transaction(); } template -void RealmClass::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 2); +void RealmClass::add_listener(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(2); - validated_notification_name(ctx, arguments[0]); - auto callback = Value::validated_to_function(ctx, arguments[1]); + validated_notification_name(ctx, args[0]); + auto callback = Value::validated_to_function(ctx, args[1]); SharedRealm realm = *get_internal>(this_object); realm->verify_open(); @@ -937,11 +935,11 @@ void RealmClass::add_listener(ContextType ctx, FunctionType, ObjectType this_ } template -void RealmClass::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 2); +void RealmClass::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(2); - validated_notification_name(ctx, arguments[0]); - auto callback = Value::validated_to_function(ctx, arguments[1]); + validated_notification_name(ctx, args[0]); + auto callback = Value::validated_to_function(ctx, args[1]); SharedRealm realm = *get_internal>(this_object); realm->verify_open(); @@ -949,10 +947,10 @@ void RealmClass::remove_listener(ContextType ctx, FunctionType, ObjectType th } template -void RealmClass::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0, 1); - if (argc) { - validated_notification_name(ctx, arguments[0]); +void RealmClass::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(1); + if (args.count) { + validated_notification_name(ctx, args[0]); } SharedRealm realm = *get_internal>(this_object); @@ -961,16 +959,16 @@ void RealmClass::remove_all_listeners(ContextType ctx, FunctionType, ObjectTy } template -void RealmClass::close(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0); +void RealmClass::close(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(0); SharedRealm realm = *get_internal>(this_object); realm->close(); } template -void RealmClass::compact(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { - validate_argument_count(argc, 0); +void RealmClass::compact(ContextType ctx, FunctionType, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(0); SharedRealm realm = *get_internal>(this_object); if (realm->is_in_transaction()) { diff --git a/src/jsc/jsc_class.hpp b/src/jsc/jsc_class.hpp index 6e6f7618..8accc341 100644 --- a/src/jsc/jsc_class.hpp +++ b/src/jsc/jsc_class.hpp @@ -30,7 +30,9 @@ template using ClassDefinition = js::ClassDefinition; using ConstructorType = js::ConstructorType; +using ArgumentsMethodType = js::ArgumentsMethodType; using MethodType = js::MethodType; +using Arguments = js::Arguments; using PropertyType = js::PropertyType; using IndexPropertyType = js::IndexPropertyType; using StringPropertyType = js::StringPropertyType; @@ -369,6 +371,19 @@ JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, } } +template +JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) { + jsc::ReturnValue return_value(ctx); + try { + F(ctx, function, this_object, jsc::Arguments{ctx, argc, arguments}, return_value); + return return_value; + } + catch (std::exception &e) { + *exception = jsc::Exception::value(ctx, e); + return nullptr; + } +} + template JSValueRef wrap(JSContextRef ctx, JSObjectRef object, JSStringRef property, JSValueRef* exception) { jsc::ReturnValue return_value(ctx); diff --git a/src/node/node_class.hpp b/src/node/node_class.hpp index 21cddad6..2b1c7557 100644 --- a/src/node/node_class.hpp +++ b/src/node/node_class.hpp @@ -31,6 +31,8 @@ using ClassDefinition = js::ClassDefinition; using ConstructorType = js::ConstructorType; using MethodType = js::MethodType; +using ArgumentsMethodType = js::ArgumentsMethodType; +using Arguments = js::Arguments; using PropertyType = js::PropertyType; using IndexPropertyType = js::IndexPropertyType; using StringPropertyType = js::StringPropertyType; @@ -308,6 +310,21 @@ void wrap(const v8::FunctionCallbackInfo& info) { } } +template +void wrap(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + node::ReturnValue return_value(info.GetReturnValue()); + auto arguments = node::get_arguments(info); + + try { + F(isolate, info.Callee(), info.This(), node::Arguments{isolate, arguments.size(), arguments.data()}, return_value); + } + catch (std::exception &e) { + Nan::ThrowError(node::Exception::value(isolate, e)); + } +} + + template void wrap(v8::Local property, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); diff --git a/tests/js/realm-tests.js b/tests/js/realm-tests.js index 0ef1a435..251e8e8d 100644 --- a/tests/js/realm-tests.js +++ b/tests/js/realm-tests.js @@ -606,7 +606,8 @@ module.exports = { "Can only delete objects within a transaction."); realm.write(() => { - TestCase.assertThrowsContaining(() => realm.delete(), 'Invalid arguments'); + TestCase.assertThrowsContaining(() => realm.delete(), + "object must be of type 'object', got (undefined)"); realm.delete(objects[0]); TestCase.assertEqual(objects.length, 9, 'wrong object count'); @@ -671,10 +672,11 @@ module.exports = { function InvalidPerson() {} InvalidPerson.schema = schemas.PersonObject.schema; - TestCase.assertThrowsContaining(() => realm.objects(), 'Invalid arguments'); + TestCase.assertThrowsContaining(() => realm.objects(), "objectType must be of type 'string', got (undefined)"); TestCase.assertThrowsContaining(() => realm.objects([]), "objectType must be of type 'string', got ()"); TestCase.assertThrowsContaining(() => realm.objects('InvalidClass'), "Object type 'InvalidClass' not found in schema."); - TestCase.assertThrowsContaining(() => realm.objects('PersonObject', 'truepredicate'), 'Invalid arguments'); + TestCase.assertThrowsContaining(() => realm.objects('PersonObject', 'truepredicate'), + "Invalid arguments: at most 1 expected, but 2 supplied."); TestCase.assertThrowsContaining(() => realm.objects(InvalidPerson), 'Constructor was not registered in the schema for this Realm'); @@ -724,8 +726,10 @@ module.exports = { TestCase.assertThrowsContaining(() => realm.objectForPrimaryKey('TestObject', 0), "'TestObject' does not have a primary key defined"); - TestCase.assertThrowsContaining(() => realm.objectForPrimaryKey(), 'Invalid arguments'); - TestCase.assertThrowsContaining(() => realm.objectForPrimaryKey('IntPrimary'), "Invalid arguments"); + TestCase.assertThrowsContaining(() => realm.objectForPrimaryKey(), + "objectType must be of type 'string', got (undefined)"); + TestCase.assertThrowsContaining(() => realm.objectForPrimaryKey('IntPrimaryObject'), + "Invalid null value for non-nullable primary key."); TestCase.assertThrowsContaining(() => realm.objectForPrimaryKey('InvalidClass', 0), "Object type 'InvalidClass' not found in schema."); },