Skip to content

Commit

Permalink
Fixes #5, fixes #8
Browse files Browse the repository at this point in the history
Adds support for IPromise arguments in the last argument of native module methods.

Additionally, adds checks during module initialization to ensure native module methods abide by the expected contract (i.e., void or Task return type, positioning of ICallback and IPromise arguments, etc.).
  • Loading branch information
rozele committed Dec 22, 2015
1 parent 3d05b08 commit 7efd40d
Show file tree
Hide file tree
Showing 9 changed files with 713 additions and 116 deletions.
281 changes: 278 additions & 3 deletions ReactWindows/ReactNative.Tests/Bridge/NativeModuleBaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ReactNative.Tests.Bridge
{
[TestClass]
public class NativeModuleBaseTests
{
[TestMethod]
public void NativeModuleBase_MethodOverload_ThrowsNotSupported()
public void NativeModuleBase_ReactMethod_ThrowsNotSupported()
{
AssertEx.Throws<NotSupportedException>(() => new MethodOverloadNativeModule());
var actions = new Action[]
{
() => new MethodOverloadNotSupportedNativeModule(),
() => new ReturnTypeNotSupportedNativeModule(),
() => new CallbackNotSupportedNativeModule(),
() => new CallbackNotSupportedNativeModule2(),
() => new PromiseNotSupportedNativeModule(),
() => new AsyncCallbackNotSupportedNativeModule(),
() => new AsyncPromiseNotSupportedNativeModule(),
};

foreach (var action in actions)
{
AssertEx.Throws<NotSupportedException>(action);
}
}

[TestMethod]
public void NativeModuleBase_ReactMethod_Async_ThrowsNotImplemented()
{
AssertEx.Throws<NotImplementedException>(() => new AsyncNotImplementedNativeModule());
}

[TestMethod]
Expand Down Expand Up @@ -140,6 +161,26 @@ public void NativeModuleBase_Invocation_Callbacks_NullCallback()
Assert.AreEqual(0, args.Count);
}

[TestMethod]
public void NativeModuleBase_Invocation_Promises_Resolve()
{
var module = new PromiseNativeModule(() => 17);
module.Initialize();

var id = default(int);
var args = default(List<int>);

var catalystInstance = new MockCatalystInstance((i, a) =>
{
id = i;
args = a.ToObject<List<int>>();
});

module.Methods[nameof(PromiseNativeModule.Foo)].Invoke(catalystInstance, JArray.FromObject(new[] { 42, 43 }));
Assert.AreEqual(42, id);
Assert.IsTrue(args.SequenceEqual(new[] { 17 }));
}

[TestMethod]
public void NativeModuleBase_CompiledDelegateFactory_Perf()
{
Expand All @@ -156,6 +197,97 @@ public void NativeModuleBase_CompiledDelegateFactory_Perf()
}
}

[TestMethod]
public void NativeModuleBase_Invocation_Promises_InvalidArgumentThrows()
{
var module = new PromiseNativeModule(() => 17);
module.Initialize();

var id = default(int);
var args = default(List<int>);

var catalystInstance = new MockCatalystInstance((i, a) =>
{
id = i;
args = a.ToObject<List<int>>();
});

AssertEx.Throws<NativeArgumentsParseException>(
() => module.Methods[nameof(PromiseNativeModule.Foo)].Invoke(catalystInstance, JArray.FromObject(new[] { default(object), 43 })),
ex => Assert.AreEqual("jsArguments", ex.ParamName));

AssertEx.Throws<NativeArgumentsParseException>(
() => module.Methods[nameof(PromiseNativeModule.Foo)].Invoke(catalystInstance, JArray.FromObject(new[] { 42, default(object) })),
ex => Assert.AreEqual("jsArguments", ex.ParamName));
}

[TestMethod]
public void NativeModuleBase_Invocation_Promises_IncorrectArgumentCount()
{
var module = new PromiseNativeModule(() => null);
module.Initialize();

var id = default(int);
var args = default(List<object>);

var catalystInstance = new MockCatalystInstance((i, a) =>
{
id = i;
args = a.ToObject<List<object>>();
});

AssertEx.Throws<NativeArgumentsParseException>(
() => module.Methods[nameof(PromiseNativeModule.Foo)].Invoke(catalystInstance, JArray.FromObject(new[] { 42 })),
ex => Assert.AreEqual("jsArguments", ex.ParamName));
}

[TestMethod]
public void NativeModuleBase_Invocation_Promises_Reject()
{
var expectedMessage = "Foo bar baz";
var exception = new Exception(expectedMessage);
var module = new PromiseNativeModule(() => { throw exception; });
module.Initialize();

var id = default(int);
var args = default(Dictionary<string, string>[]);

var catalystInstance = new MockCatalystInstance((i, a) =>
{
id = i;
args = a.ToObject<Dictionary<string, string>[]>();
});

module.Methods[nameof(CallbackNativeModule.Foo)].Invoke(catalystInstance, JArray.FromObject(new[] { 42, 43 }));
Assert.AreEqual(43, id);
Assert.AreEqual(1, args.Length);
var d = args[0];
Assert.AreEqual(1, d.Count);
var actualMessage = default(string);
Assert.IsTrue(d.TryGetValue("message", out actualMessage));
Assert.AreEqual(expectedMessage, actualMessage);
}

[TestMethod]
public void NativeModuleBase_Invocation_Promises_NullCallback()
{
var module = new PromiseNativeModule(() => null);
module.Initialize();

var id = default(int);
var args = default(List<object>);

var catalystInstance = new MockCatalystInstance((i, a) =>
{
id = i;
args = a.ToObject<List<object>>();
});

module.Methods[nameof(PromiseNativeModule.Foo)].Invoke(catalystInstance, JArray.FromObject(new[] { 42, 43 }));
Assert.AreEqual(1, args.Count);
Assert.IsNull(args[0]);
}

[TestMethod]
public void NativeModuleBase_ReflectionDelegateFactory_Perf()
{
Expand All @@ -172,7 +304,7 @@ public void NativeModuleBase_ReflectionDelegateFactory_Perf()
}
}

class MethodOverloadNativeModule : NativeModuleBase
class MethodOverloadNotSupportedNativeModule : NativeModuleBase
{
public override string Name
{
Expand All @@ -193,6 +325,113 @@ public void Foo(int x)
}
}

class ReturnTypeNotSupportedNativeModule : NativeModuleBase
{
public override string Name
{
get
{
return "Test";
}
}

[ReactMethod]
public int Foo() { return 0; }
}

class CallbackNotSupportedNativeModule : NativeModuleBase
{
public override string Name
{
get
{
return "Test";
}
}

[ReactMethod]
public void Foo(ICallback foo, int bar, string qux) { }
}

class CallbackNotSupportedNativeModule2 : NativeModuleBase
{
public override string Name
{
get
{
return "Test";
}
}

[ReactMethod]
public void Foo(ICallback bar, int foo) { }
}

class PromiseNotSupportedNativeModule : NativeModuleBase
{
public override string Name
{
get
{
return "Test";
}
}

[ReactMethod]
public void Foo(IPromise promise, int foo) { }
}

class AsyncCallbackNotSupportedNativeModule : NativeModuleBase
{
public override string Name
{
get
{
return "Test";
}
}

[ReactMethod]
public Task Foo(ICallback callback)
{
return Task.FromResult(true);
}
}

class AsyncPromiseNotSupportedNativeModule : NativeModuleBase
{
public override string Name
{
get
{
return "Test";
}
}

[ReactMethod]
public Task Foo(IPromise promise)
{
return Task.FromResult(true);
}
}

class AsyncNotImplementedNativeModule : NativeModuleBase
{
public override string Name
{
get
{
return "Test";
}
}

[ReactMethod]
public Task Foo()
{
return Task.FromResult(true);
}
}

class TestNativeModule : NativeModuleBase
{
private readonly Action _onFoo;
Expand Down Expand Up @@ -259,6 +498,42 @@ public void Foo(ICallback callback)
}
}

class PromiseNativeModule : NativeModuleBase
{
private readonly Func<object> _resolveFactory;

public PromiseNativeModule()
: this(() => null)
{
}

public PromiseNativeModule(Func<object> resolveFactory)
{
_resolveFactory = resolveFactory;
}

public override string Name
{
get
{
return "Test";
}
}

[ReactMethod]
public void Foo(IPromise promise)
{
try
{
promise.Resolve(_resolveFactory());
}
catch (Exception ex)
{
promise.Reject(ex);
}
}
}

class PerfNativeModule : NativeModuleBase
{
public PerfNativeModule(IReactDelegateFactory delegateFactory)
Expand Down
Loading

0 comments on commit 7efd40d

Please sign in to comment.