diff --git a/src/Middleware/Middleware.sln b/src/Middleware/Middleware.sln index 5b4a846213..4eefacd361 100644 --- a/src/Middleware/Middleware.sln +++ b/src/Middleware/Middleware.sln @@ -267,6 +267,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaSer EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.SpaServices.Extensions", "SpaServices.Extensions\src\Microsoft.AspNetCore.SpaServices.Extensions.csproj", "{5D5B7E54-9323-498A-8983-E9BDFA3B2D07}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.NodeServices.Tests", "NodeServices\test\Microsoft.AspNetCore.NodeServices.Tests.csproj", "{B04E9CB6-0D1C-4C21-B626-89B6926A491F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1465,6 +1467,18 @@ Global {5D5B7E54-9323-498A-8983-E9BDFA3B2D07}.Release|x64.Build.0 = Release|Any CPU {5D5B7E54-9323-498A-8983-E9BDFA3B2D07}.Release|x86.ActiveCfg = Release|Any CPU {5D5B7E54-9323-498A-8983-E9BDFA3B2D07}.Release|x86.Build.0 = Release|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Debug|x64.Build.0 = Debug|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Debug|x86.ActiveCfg = Debug|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Debug|x86.Build.0 = Debug|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Release|Any CPU.Build.0 = Release|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Release|x64.ActiveCfg = Release|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Release|x64.Build.0 = Release|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Release|x86.ActiveCfg = Release|Any CPU + {B04E9CB6-0D1C-4C21-B626-89B6926A491F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1580,6 +1594,7 @@ Global {121DFA13-E965-4C91-A175-19EF20DFD5AC} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472} {D9D02772-1D53-45C3-B2CC-888F9978958C} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472} {5D5B7E54-9323-498A-8983-E9BDFA3B2D07} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472} + {B04E9CB6-0D1C-4C21-B626-89B6926A491F} = {17B409B3-7EC6-49D8-847E-CFAA319E01B5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {83786312-A93B-4BB4-AB06-7C6913A59AFA} diff --git a/src/Middleware/NodeServices/test/Microsoft.AspNetCore.NodeServices.Tests.csproj b/src/Middleware/NodeServices/test/Microsoft.AspNetCore.NodeServices.Tests.csproj new file mode 100644 index 0000000000..862335b90b --- /dev/null +++ b/src/Middleware/NodeServices/test/Microsoft.AspNetCore.NodeServices.Tests.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp3.0 + + + + + + + + + diff --git a/src/Middleware/NodeServices/test/NodeServicesTest.cs b/src/Middleware/NodeServices/test/NodeServicesTest.cs new file mode 100644 index 0000000000..a5012ea8aa --- /dev/null +++ b/src/Middleware/NodeServices/test/NodeServicesTest.cs @@ -0,0 +1,134 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.NodeServices.HostingModels; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.NodeServices +{ + public class NodeServicesTest : IDisposable + { + private readonly INodeServices _nodeServices; + + public NodeServicesTest() + { + // In typical ASP.NET Core applications, INodeServices is made available + // through DI using services.AddNodeServices(). But for these tests we + // create our own INodeServices instance manually, since the tests are + // not about DI (and we might want different config for each test). + var serviceProvider = new ServiceCollection().BuildServiceProvider(); + var options = new NodeServicesOptions(serviceProvider); + _nodeServices = NodeServicesFactory.CreateNodeServices(options); + } + + [Fact] + public async Task CanGetSuccessResult() + { + // Act + var result = await _nodeServices.InvokeExportAsync( + ModulePath("testCases"), + "getFixedString"); + + // Assert + Assert.Equal("test result", result); + } + + [Fact] + public async Task CanGetErrorResult() + { + // Act/Assert + var ex = await Assert.ThrowsAsync(() => + _nodeServices.InvokeExportAsync( + ModulePath("testCases"), + "raiseError")); + Assert.StartsWith("This is an error from Node", ex.Message); + } + + [Fact] + public async Task CanGetResultAsynchronously() + { + // Act + // All the invocations are async, but this test shows we're not reliant + // on the response coming back immediately + var result = await _nodeServices.InvokeExportAsync( + ModulePath("testCases"), + "getFixedStringWithDelay"); + + // Assert + Assert.Equal("delayed test result", result); + } + + [Fact] + public async Task CanPassParameters() + { + // Act + var result = await _nodeServices.InvokeExportAsync( + ModulePath("testCases"), + "echoSimpleParameters", + "Hey", + 123); + + // Assert + Assert.Equal("Param0: Hey; Param1: 123", result); + } + + [Fact] + public async Task CanPassParametersWithCamelCaseNameConversion() + { + // Act + var result = await _nodeServices.InvokeExportAsync( + ModulePath("testCases"), + "echoComplexParameters", + new ComplexModel { StringProp = "Abc", IntProp = 123, BoolProp = true }); + + // Assert + Assert.Equal("Received: [{\"stringProp\":\"Abc\",\"intProp\":123,\"boolProp\":true}]", result); + } + + [Fact] + public async Task CanReceiveComplexResultWithPascalCaseNameConversion() + { + // Act + var result = await _nodeServices.InvokeExportAsync( + ModulePath("testCases"), + "getComplexObject"); + + // Assert + Assert.Equal("Hi from Node", result.StringProp); + Assert.Equal(456, result.IntProp); + Assert.True(result.BoolProp); + } + + [Fact] + public async Task CanInvokeDefaultModuleExport() + { + // Act + var result = await _nodeServices.InvokeAsync( + ModulePath("moduleWithDefaultExport"), + "This is from .NET"); + + // Assert + Assert.Equal("Hello from the default export. You passed: This is from .NET", result); + } + + private static string ModulePath(string testModuleName) + => $"../../../js/{testModuleName}"; + + public void Dispose() + { + _nodeServices.Dispose(); + } + + class ComplexModel + { + public string StringProp { get; set; } + + public int IntProp { get; set; } + + public bool BoolProp { get; set; } + } + } +} diff --git a/src/Middleware/NodeServices/test/js/moduleWithDefaultExport.js b/src/Middleware/NodeServices/test/js/moduleWithDefaultExport.js new file mode 100644 index 0000000000..eae7f7ccf4 --- /dev/null +++ b/src/Middleware/NodeServices/test/js/moduleWithDefaultExport.js @@ -0,0 +1,3 @@ +module.exports = function (callback, message) { + callback(null, `Hello from the default export. You passed: ${message}`); +}; diff --git a/src/Middleware/NodeServices/test/js/testCases.js b/src/Middleware/NodeServices/test/js/testCases.js new file mode 100644 index 0000000000..74b3a49ccf --- /dev/null +++ b/src/Middleware/NodeServices/test/js/testCases.js @@ -0,0 +1,28 @@ +// Function signatures follow Node conventions. +// i.e., parameters: (callback, arg0, arg1, ... etc ...) +// When done, functions must invoke 'callback', passing (errorInfo, result) +// where errorInfo should be null/undefined if there was no error. + +exports.getFixedString = function (callback) { + callback(null, 'test result'); +}; + +exports.getFixedStringWithDelay = function (callback) { + setTimeout(callback(null, 'delayed test result'), 100); +}; + +exports.raiseError = function (callback) { + callback('This is an error from Node'); +}; + +exports.echoSimpleParameters = function (callback, param0, param1) { + callback(null, `Param0: ${param0}; Param1: ${param1}`); +}; + +exports.echoComplexParameters = function (callback, ...otherArgs) { + callback(null, `Received: ${JSON.stringify(otherArgs)}`); +}; + +exports.getComplexObject = function (callback) { + callback(null, { stringProp: 'Hi from Node', intProp: 456, boolProp: true }); +};