From 4fb3964e2ea9f8f212919683bea164bf128d7ada Mon Sep 17 00:00:00 2001 From: kapi2289 Date: Tue, 14 Jan 2025 18:32:46 +0100 Subject: [PATCH 1/4] Resolve nested dictionaries --- InertiaCore/Extensions/InertiaExtensions.cs | 44 +++-- InertiaCore/Props/InvokableProp.cs | 8 +- InertiaCore/Response.cs | 168 +++++++++++++------- InertiaCore/ResponseFactory.cs | 39 +++-- InertiaCoreTests/Setup.cs | 9 +- InertiaCoreTests/UnitTestAlwaysData.cs | 24 +-- InertiaCoreTests/UnitTestDictionaryData.cs | 53 ++++++ InertiaCoreTests/UnitTestLazyData.cs | 21 +-- InertiaCoreTests/UnitTestModelState.cs | 2 +- InertiaCoreTests/UnitTestSharedData.cs | 7 +- 10 files changed, 217 insertions(+), 158 deletions(-) create mode 100644 InertiaCoreTests/UnitTestDictionaryData.cs diff --git a/InertiaCore/Extensions/InertiaExtensions.cs b/InertiaCore/Extensions/InertiaExtensions.cs index 9aa0391..90c058d 100644 --- a/InertiaCore/Extensions/InertiaExtensions.cs +++ b/InertiaCore/Extensions/InertiaExtensions.cs @@ -8,31 +8,6 @@ namespace InertiaCore.Extensions; internal static class InertiaExtensions { - internal static Dictionary OnlyProps(this ActionContext context, Dictionary props) - { - var onlyKeys = context.HttpContext.Request.Headers[InertiaHeader.PartialOnly] - .ToString().Split(',') - .Select(k => k.Trim()) - .Where(k => !string.IsNullOrEmpty(k)) - .ToList(); - - return props.Where(kv => onlyKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase)) - .ToDictionary(kv => kv.Key, kv => kv.Value); - } - - internal static Dictionary ExceptProps(this ActionContext context, - Dictionary props) - { - var exceptKeys = context.HttpContext.Request.Headers[InertiaHeader.PartialExcept] - .ToString().Split(',') - .Select(k => k.Trim()) - .Where(k => !string.IsNullOrEmpty(k)) - .ToList(); - - return props.Where(kv => exceptKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase) == false) - .ToDictionary(kv => kv.Key, kv => kv.Value); - } - internal static bool IsInertiaPartialComponent(this ActionContext context, string component) => context.HttpContext.Request.Headers[InertiaHeader.PartialComponent] == component; @@ -55,4 +30,23 @@ internal static bool Override(this IDictionary dicti return true; } + + internal static Task ResolveAsync(this Func func) + { + var rt = func.Method.ReturnType; + + if (!rt.IsGenericType || rt.GetGenericTypeDefinition() != typeof(Task<>)) + return Task.Run(func.Invoke); + + var task = func.DynamicInvoke() as Task; + return task!.ResolveResult(); + } + + internal static async Task ResolveResult(this Task task) + { + await task.ConfigureAwait(false); + var result = task.GetType().GetProperty("Result"); + + return result?.GetValue(task); + } } diff --git a/InertiaCore/Props/InvokableProp.cs b/InertiaCore/Props/InvokableProp.cs index 2fd3f3b..02ddb6f 100644 --- a/InertiaCore/Props/InvokableProp.cs +++ b/InertiaCore/Props/InvokableProp.cs @@ -1,3 +1,5 @@ +using InertiaCore.Extensions; + namespace InertiaCore.Props; public class InvokableProp @@ -10,9 +12,9 @@ public class InvokableProp { return _value switch { - Func> asyncCallable => asyncCallable.Invoke(), - Func callable => Task.Run(() => callable.Invoke()), - Task value => value, + Func f => f.ResolveAsync(), + Task t => t.ResolveResult(), + InvokableProp p => p.Invoke(), _ => Task.FromResult(_value) }; } diff --git a/InertiaCore/Response.cs b/InertiaCore/Response.cs index d052844..6a877c5 100644 --- a/InertiaCore/Response.cs +++ b/InertiaCore/Response.cs @@ -13,7 +13,7 @@ namespace InertiaCore; public class Response : IActionResult { private readonly string _component; - private readonly object _props; + private readonly Dictionary _props; private readonly string _rootView; private readonly string? _version; @@ -21,46 +21,136 @@ public class Response : IActionResult private Page? _page; private IDictionary? _viewData; - public Response(string component, object props, string rootView, string? version) + internal Response(string component, Dictionary props, string rootView, string? version) => (_component, _props, _rootView, _version) = (component, props, rootView, version); public async Task ExecuteResultAsync(ActionContext context) { SetContext(context); await ProcessResponse(); - await GetResult().ExecuteResultAsync(_context!); } protected internal async Task ProcessResponse() { + var props = await ResolveProperties(); + var page = new Page { Component = _component, Version = _version, Url = _context!.RequestedUri(), - Props = await ResolveProperties(_props.GetType().GetProperties() - .ToDictionary(o => o.Name.ToCamelCase(), o => o.GetValue(_props))) + Props = props }; - var shared = _context!.HttpContext.Features.Get(); - if (shared != null) - page.Props = shared.GetMerged(page.Props); - page.Props["errors"] = GetErrors(); SetPage(page); } - private static async Task> PrepareProps(Dictionary props) + /// + /// Resolve the properties for the response. + /// + private async Task> ResolveProperties() + { + var props = _props; + + props = ResolvePartialProperties(props); + props = ResolveAlways(props); + props = await ResolvePropertyInstances(props); + + return props; + } + + /// + /// Resolve the `only` and `except` partial request props. + /// + private Dictionary ResolvePartialProperties(Dictionary props) + { + var isPartial = _context!.IsInertiaPartialComponent(_component); + + if (!isPartial) + return props + .Where(kv => kv.Value is not LazyProp) + .ToDictionary(kv => kv.Key, kv => kv.Value); + + props = props.ToDictionary(kv => kv.Key, kv => kv.Value); + + if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialOnly)) + props = ResolveOnly(props); + + if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialExcept)) + props = ResolveExcept(props); + + return props; + } + + /// + /// Resolve the `only` partial request props. + /// + private Dictionary ResolveOnly(Dictionary props) { - return (await Task.WhenAll(props.Select(async pair => pair.Value switch + var onlyKeys = _context!.HttpContext.Request.Headers[InertiaHeader.PartialOnly] + .ToString().Split(',') + .Select(k => k.Trim()) + .Where(k => !string.IsNullOrEmpty(k)) + .ToList(); + + return props.Where(kv => onlyKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase)) + .ToDictionary(kv => kv.Key, kv => kv.Value); + } + + /// + /// Resolve the `except` partial request props. + /// + private Dictionary ResolveExcept(Dictionary props) + { + var exceptKeys = _context!.HttpContext.Request.Headers[InertiaHeader.PartialExcept] + .ToString().Split(',') + .Select(k => k.Trim()) + .Where(k => !string.IsNullOrEmpty(k)) + .ToList(); + + return props.Where(kv => exceptKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase) == false) + .ToDictionary(kv => kv.Key, kv => kv.Value); + } + + /// + /// Resolve `always` properties that should always be included on all visits, regardless of "only" or "except" requests. + /// + private Dictionary ResolveAlways(Dictionary props) + { + var alwaysProps = _props.Where(o => o.Value is AlwaysProp); + + return props + .Where(kv => kv.Value is not AlwaysProp) + .Concat(alwaysProps).ToDictionary(kv => kv.Key, kv => kv.Value); + } + + /// + /// Resolve all necessary class instances in the given props. + /// + private static async Task> ResolvePropertyInstances(Dictionary props) + { + return (await Task.WhenAll(props.Select(async pair => { - Func f => (pair.Key, f.Invoke()), - LazyProp l => (pair.Key, await l.Invoke()), - AlwaysProp l => (pair.Key, await l.Invoke()), - _ => (pair.Key, pair.Value) - }))).ToDictionary(pair => pair.Key, pair => pair.Item2); + var key = pair.Key.ToCamelCase(); + + var value = pair.Value switch + { + Func f => (key, await f.ResolveAsync()), + Task t => (key, await t.ResolveResult()), + InvokableProp p => (key, await p.Invoke()), + _ => (key, pair.Value) + }; + + if (value.Item2 is Dictionary dict) + { + value = (key, await ResolvePropertyInstances(dict)); + } + + return value; + }))).ToDictionary(pair => pair.key, pair => pair.Item2); } protected internal JsonResult GetJson() @@ -93,7 +183,7 @@ private ViewResult GetView() protected internal IActionResult GetResult() => _context!.IsInertiaRequest() ? GetJson() : GetView(); - private IDictionary GetErrors() + private Dictionary GetErrors() { if (!_context!.ModelState.IsValid) return _context!.ModelState.ToDictionary(o => o.Key.ToCamelCase(), @@ -111,48 +201,4 @@ public Response WithViewData(IDictionary viewData) _viewData = viewData; return this; } - - private async Task> ResolveProperties(Dictionary props) - { - var isPartial = _context!.IsInertiaPartialComponent(_component); - - if (!isPartial) - { - props = props - .Where(kv => kv.Value is not LazyProp) - .ToDictionary(kv => kv.Key, kv => kv.Value); - } - else - { - props = props.ToDictionary(kv => kv.Key, kv => kv.Value); - - if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialOnly)) - props = ResolveOnly(props); - - if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialExcept)) - props = ResolveExcept(props); - } - - props = ResolveAlways(props); - props = await PrepareProps(props); - - return props; - } - - private Dictionary ResolveOnly(Dictionary props) - => _context!.OnlyProps(props); - - private Dictionary ResolveExcept(Dictionary props) - => _context!.ExceptProps(props); - - private Dictionary ResolveAlways(Dictionary props) - { - var alwaysProps = _props.GetType().GetProperties() - .Where(o => o.PropertyType == typeof(AlwaysProp)) - .ToDictionary(o => o.Name.ToCamelCase(), o => o.GetValue(_props)); - - return props - .Where(kv => kv.Value is not AlwaysProp) - .Concat(alwaysProps).ToDictionary(kv => kv.Key, kv => kv.Value); - } } diff --git a/InertiaCore/ResponseFactory.cs b/InertiaCore/ResponseFactory.cs index b5fbd21..f2a7d07 100644 --- a/InertiaCore/ResponseFactory.cs +++ b/InertiaCore/ResponseFactory.cs @@ -35,6 +35,7 @@ internal class ResponseFactory : IResponseFactory private readonly IOptions _options; private object? _version; + private InertiaSharedData? _sharedProps; public ResponseFactory(IHttpContextAccessor contextAccessor, IGateway gateway, IOptions options) => (_contextAccessor, _gateway, _options) = (contextAccessor, gateway, options); @@ -43,7 +44,15 @@ public Response Render(string component, object? props = null) { props ??= new { }; - return new Response(component, props, _options.Value.RootView, GetVersion()); + var dictProps = props switch + { + Dictionary dict => dict, + _ => props.GetType().GetProperties() + .ToDictionary(o => o.Name, o => o.GetValue(props)) + }; + dictProps = _sharedProps?.GetMerged(dictProps) ?? dictProps; + + return new Response(component, dictProps, _options.Value.RootView, GetVersion()); } public async Task Head(dynamic model) @@ -102,29 +111,19 @@ public async Task Html(dynamic model) public void Share(string key, object? value) { - var context = _contextAccessor.HttpContext!; - - var sharedData = context.Features.Get(); - sharedData ??= new InertiaSharedData(); - sharedData.Set(key, value); - - context.Features.Set(sharedData); + _sharedProps ??= new InertiaSharedData(); + _sharedProps.Set(key, value); } public void Share(IDictionary data) { - var context = _contextAccessor.HttpContext!; - - var sharedData = context.Features.Get(); - sharedData ??= new InertiaSharedData(); - sharedData.Merge(data); - - context.Features.Set(sharedData); + _sharedProps ??= new InertiaSharedData(); + _sharedProps.Merge(data); } - public LazyProp Lazy(Func callback) => new LazyProp(callback); - public LazyProp Lazy(Func> callback) => new LazyProp(callback); - public AlwaysProp Always(object? value) => new AlwaysProp(value); - public AlwaysProp Always(Func callback) => new AlwaysProp(callback); - public AlwaysProp Always(Func> callback) => new AlwaysProp(callback); + public LazyProp Lazy(Func callback) => new(callback); + public LazyProp Lazy(Func> callback) => new(callback); + public AlwaysProp Always(object? value) => new(value); + public AlwaysProp Always(Func callback) => new(callback); + public AlwaysProp Always(Func> callback) => new(callback); } diff --git a/InertiaCoreTests/Setup.cs b/InertiaCoreTests/Setup.cs index bb70e88..5eb681e 100644 --- a/InertiaCoreTests/Setup.cs +++ b/InertiaCoreTests/Setup.cs @@ -1,9 +1,7 @@ using InertiaCore; using InertiaCore.Models; using InertiaCore.Ssr; -using InertiaCore.Utils; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Routing; @@ -35,7 +33,7 @@ public void Setup() /// Optional request headers. /// Optional Inertia shared data. /// Optional validation errors dictionary. - private static ActionContext PrepareContext(HeaderDictionary? headers = null, InertiaSharedData? sharedData = null, + private static ActionContext PrepareContext(HeaderDictionary? headers = null, Dictionary? modelState = null) { var request = new Mock(); @@ -44,14 +42,9 @@ private static ActionContext PrepareContext(HeaderDictionary? headers = null, In var response = new Mock(); response.SetupGet(r => r.Headers).Returns(new HeaderDictionary()); - var features = new FeatureCollection(); - if (sharedData != null) - features.Set(sharedData); - var httpContext = new Mock(); httpContext.SetupGet(c => c.Request).Returns(request.Object); httpContext.SetupGet(c => c.Response).Returns(response.Object); - httpContext.SetupGet(c => c.Features).Returns(features); var context = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor()); diff --git a/InertiaCoreTests/UnitTestAlwaysData.cs b/InertiaCoreTests/UnitTestAlwaysData.cs index 9374fb0..480ac47 100644 --- a/InertiaCoreTests/UnitTestAlwaysData.cs +++ b/InertiaCoreTests/UnitTestAlwaysData.cs @@ -67,17 +67,11 @@ public async Task TestAlwaysPartialData() [Description("Test if the always async data is fetched properly.")] public async Task TestAlwaysAsyncData() { - var testFunction = new Func>(async () => - { - await Task.Delay(100); - return "Always Async"; - }); - var response = _factory.Render("Test/Page", new { Test = "Test", TestFunc = new Func(() => "Func"), - TestAlways = _factory.Always(testFunction) + TestAlways = _factory.Always(() => Task.FromResult("Always Async")) }); var context = PrepareContext(); @@ -100,16 +94,10 @@ public async Task TestAlwaysAsyncData() [Description("Test if the always async data is fetched properly with specified partial props.")] public async Task TestAlwaysAsyncPartialData() { - var testFunction = new Func>(async () => - { - await Task.Delay(100); - return "Always Async"; - }); - var response = _factory.Render("Test/Page", new { TestFunc = new Func(() => "Func"), - TestAlways = _factory.Always(async () => await testFunction()) + TestAlways = _factory.Always(() => Task.FromResult("Always Async")) }); var headers = new HeaderDictionary @@ -137,16 +125,10 @@ public async Task TestAlwaysAsyncPartialData() [Description("Test if the always async data is fetched properly without specified partial props.")] public async Task TestAlwaysAsyncPartialDataOmitted() { - var testFunction = new Func>(async () => - { - await Task.Delay(100); - return "Always Async"; - }); - var response = _factory.Render("Test/Page", new { TestFunc = new Func(() => "Func"), - TestAlways = _factory.Always(async () => await testFunction()) + TestAlways = _factory.Always(() => Task.FromResult("Always Async")) }); var headers = new HeaderDictionary diff --git a/InertiaCoreTests/UnitTestDictionaryData.cs b/InertiaCoreTests/UnitTestDictionaryData.cs new file mode 100644 index 0000000..7b332cd --- /dev/null +++ b/InertiaCoreTests/UnitTestDictionaryData.cs @@ -0,0 +1,53 @@ +using InertiaCore.Models; + +namespace InertiaCoreTests; + +public partial class Tests +{ + [Test] + [Description("Test if all nested dictionaries and its values are resolved properly.")] + public async Task TestDictionaryData() + { + var response = _factory.Render("Test/Page", new + { + Test = "Test", + TestDict = new Dictionary + { + ["Key"] = () => "Value", + ["KeyAsync"] = () => Task.FromResult("ValueAsync"), + ["KeyAsync2"] = Task.FromResult("ValueAsync2"), + ["Always"] = () => new Dictionary + { + ["Key"] = () => "Value" + } + } + }); + + var context = PrepareContext(); + response.SetContext(context); + + await response.ProcessResponse(); + + var page = response.GetJson().Value as Page; + + Assert.That(page?.Props, Is.EqualTo(new Dictionary + { + { "test", "Test" }, + { + "testDict", new Dictionary + { + { "key", "Value" }, + { "keyAsync", "ValueAsync" }, + { "keyAsync2", "ValueAsync2" }, + { + "always", new Dictionary + { + { "key", "Value" }, + } + } + } + }, + { "errors", new Dictionary(0) } + })); + } +} diff --git a/InertiaCoreTests/UnitTestLazyData.cs b/InertiaCoreTests/UnitTestLazyData.cs index 423cbf4..085eb60 100644 --- a/InertiaCoreTests/UnitTestLazyData.cs +++ b/InertiaCoreTests/UnitTestLazyData.cs @@ -71,18 +71,15 @@ public async Task TestLazyPartialData() [Description("Test if the lazy async data is fetched properly.")] public async Task TestLazyAsyncData() { - var testFunction = new Func>(async () => - { - Assert.Fail(); - await Task.Delay(100); - return "Lazy Async"; - }); - var response = _factory.Render("Test/Page", new { Test = "Test", TestFunc = new Func(() => "Func"), - TestLazy = _factory.Lazy(testFunction) + TestLazy = _factory.Lazy(() => + { + Assert.Fail(); + return Task.FromResult("Lazy Async"); + }) }); var context = PrepareContext(); @@ -104,16 +101,10 @@ public async Task TestLazyAsyncData() [Description("Test if the lazy async data is fetched properly with specified partial props.")] public async Task TestLazyAsyncPartialData() { - var testFunction = new Func>(async () => - { - await Task.Delay(100); - return "Lazy Async"; - }); - var response = _factory.Render("Test/Page", new { TestFunc = new Func(() => "Func"), - TestLazy = _factory.Lazy(async () => await testFunction()) + TestLazy = _factory.Lazy(() => Task.FromResult("Lazy Async")) }); var headers = new HeaderDictionary diff --git a/InertiaCoreTests/UnitTestModelState.cs b/InertiaCoreTests/UnitTestModelState.cs index a90d646..c697ca2 100644 --- a/InertiaCoreTests/UnitTestModelState.cs +++ b/InertiaCoreTests/UnitTestModelState.cs @@ -13,7 +13,7 @@ public async Task TestModelState() Test = "Test" }); - var context = PrepareContext(null, null, new Dictionary + var context = PrepareContext(null, new Dictionary { { "Field", "Error" } }); diff --git a/InertiaCoreTests/UnitTestSharedData.cs b/InertiaCoreTests/UnitTestSharedData.cs index 9155c8a..89eaf83 100644 --- a/InertiaCoreTests/UnitTestSharedData.cs +++ b/InertiaCoreTests/UnitTestSharedData.cs @@ -9,15 +9,14 @@ public partial class Tests [Description("Test if shared data is merged with the props properly.")] public async Task TestSharedData() { + _factory.Share("TestShared", "Shared"); + var response = _factory.Render("Test/Page", new { Test = "Test" }); - var sharedData = new InertiaSharedData(); - sharedData.Set("TestShared", "Shared"); - - var context = PrepareContext(null, sharedData); + var context = PrepareContext(); response.SetContext(context); await response.ProcessResponse(); From 31d4cdb4fe62560bd01b5438668e0da721bd6c09 Mon Sep 17 00:00:00 2001 From: kapi2289 Date: Tue, 14 Jan 2025 18:52:01 +0100 Subject: [PATCH 2/4] Revert changing the shared props mechanic, rename shared data to props --- InertiaCore/Response.cs | 13 ++++++++++++ InertiaCore/ResponseFactory.cs | 21 ++++++++++++------- ...tiaSharedData.cs => InertiaSharedProps.cs} | 2 +- InertiaCoreTests/Setup.cs | 11 ++++++++-- InertiaCoreTests/UnitTestModelState.cs | 2 +- InertiaCoreTests/UnitTestSharedData.cs | 9 ++++---- 6 files changed, 43 insertions(+), 15 deletions(-) rename InertiaCore/Utils/{InertiaSharedData.cs => InertiaSharedProps.cs} (95%) diff --git a/InertiaCore/Response.cs b/InertiaCore/Response.cs index 6a877c5..4b9ed72 100644 --- a/InertiaCore/Response.cs +++ b/InertiaCore/Response.cs @@ -55,6 +55,7 @@ protected internal async Task ProcessResponse() { var props = _props; + props = ResolveSharedProps(props); props = ResolvePartialProperties(props); props = ResolveAlways(props); props = await ResolvePropertyInstances(props); @@ -62,6 +63,18 @@ protected internal async Task ProcessResponse() return props; } + /// + /// Resolve `shared` props stored in the current request context. + /// + private Dictionary ResolveSharedProps(Dictionary props) + { + var shared = _context!.HttpContext.Features.Get(); + if (shared != null) + props = shared.GetMerged(props); + + return props; + } + /// /// Resolve the `only` and `except` partial request props. /// diff --git a/InertiaCore/ResponseFactory.cs b/InertiaCore/ResponseFactory.cs index f2a7d07..8bce7cf 100644 --- a/InertiaCore/ResponseFactory.cs +++ b/InertiaCore/ResponseFactory.cs @@ -35,7 +35,6 @@ internal class ResponseFactory : IResponseFactory private readonly IOptions _options; private object? _version; - private InertiaSharedData? _sharedProps; public ResponseFactory(IHttpContextAccessor contextAccessor, IGateway gateway, IOptions options) => (_contextAccessor, _gateway, _options) = (contextAccessor, gateway, options); @@ -43,14 +42,12 @@ public ResponseFactory(IHttpContextAccessor contextAccessor, IGateway gateway, I public Response Render(string component, object? props = null) { props ??= new { }; - var dictProps = props switch { Dictionary dict => dict, _ => props.GetType().GetProperties() .ToDictionary(o => o.Name, o => o.GetValue(props)) }; - dictProps = _sharedProps?.GetMerged(dictProps) ?? dictProps; return new Response(component, dictProps, _options.Value.RootView, GetVersion()); } @@ -111,14 +108,24 @@ public async Task Html(dynamic model) public void Share(string key, object? value) { - _sharedProps ??= new InertiaSharedData(); - _sharedProps.Set(key, value); + var context = _contextAccessor.HttpContext!; + + var sharedData = context.Features.Get(); + sharedData ??= new InertiaSharedProps(); + sharedData.Set(key, value); + + context.Features.Set(sharedData); } public void Share(IDictionary data) { - _sharedProps ??= new InertiaSharedData(); - _sharedProps.Merge(data); + var context = _contextAccessor.HttpContext!; + + var sharedData = context.Features.Get(); + sharedData ??= new InertiaSharedProps(); + sharedData.Merge(data); + + context.Features.Set(sharedData); } public LazyProp Lazy(Func callback) => new(callback); diff --git a/InertiaCore/Utils/InertiaSharedData.cs b/InertiaCore/Utils/InertiaSharedProps.cs similarity index 95% rename from InertiaCore/Utils/InertiaSharedData.cs rename to InertiaCore/Utils/InertiaSharedProps.cs index dfb9ff7..68b9bbd 100644 --- a/InertiaCore/Utils/InertiaSharedData.cs +++ b/InertiaCore/Utils/InertiaSharedProps.cs @@ -2,7 +2,7 @@ namespace InertiaCore.Utils; -internal class InertiaSharedData +internal class InertiaSharedProps { private IDictionary? Data { get; set; } diff --git a/InertiaCoreTests/Setup.cs b/InertiaCoreTests/Setup.cs index 5eb681e..5942c2b 100644 --- a/InertiaCoreTests/Setup.cs +++ b/InertiaCoreTests/Setup.cs @@ -1,7 +1,9 @@ using InertiaCore; using InertiaCore.Models; using InertiaCore.Ssr; +using InertiaCore.Utils; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Routing; @@ -31,9 +33,9 @@ public void Setup() /// Prepares ActionContext for usage in tests. /// /// Optional request headers. - /// Optional Inertia shared data. + /// Optional Inertia shared data. /// Optional validation errors dictionary. - private static ActionContext PrepareContext(HeaderDictionary? headers = null, + private static ActionContext PrepareContext(HeaderDictionary? headers = null, InertiaSharedProps? sharedProps = null, Dictionary? modelState = null) { var request = new Mock(); @@ -42,9 +44,14 @@ private static ActionContext PrepareContext(HeaderDictionary? headers = null, var response = new Mock(); response.SetupGet(r => r.Headers).Returns(new HeaderDictionary()); + var features = new FeatureCollection(); + if (sharedProps != null) + features.Set(sharedProps); + var httpContext = new Mock(); httpContext.SetupGet(c => c.Request).Returns(request.Object); httpContext.SetupGet(c => c.Response).Returns(response.Object); + httpContext.SetupGet(c => c.Features).Returns(features); var context = new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor()); diff --git a/InertiaCoreTests/UnitTestModelState.cs b/InertiaCoreTests/UnitTestModelState.cs index c697ca2..a90d646 100644 --- a/InertiaCoreTests/UnitTestModelState.cs +++ b/InertiaCoreTests/UnitTestModelState.cs @@ -13,7 +13,7 @@ public async Task TestModelState() Test = "Test" }); - var context = PrepareContext(null, new Dictionary + var context = PrepareContext(null, null, new Dictionary { { "Field", "Error" } }); diff --git a/InertiaCoreTests/UnitTestSharedData.cs b/InertiaCoreTests/UnitTestSharedData.cs index 89eaf83..fc64336 100644 --- a/InertiaCoreTests/UnitTestSharedData.cs +++ b/InertiaCoreTests/UnitTestSharedData.cs @@ -7,16 +7,17 @@ public partial class Tests { [Test] [Description("Test if shared data is merged with the props properly.")] - public async Task TestSharedData() + public async Task TestSharedProps() { - _factory.Share("TestShared", "Shared"); - var response = _factory.Render("Test/Page", new { Test = "Test" }); - var context = PrepareContext(); + var sharedProps = new InertiaSharedProps(); + sharedProps.Set("TestShared", "Shared"); + + var context = PrepareContext(null, sharedProps); response.SetContext(context); await response.ProcessResponse(); From 71d275669d325a6c79b1a33c302843311f2e6e5d Mon Sep 17 00:00:00 2001 From: kapi2289 Date: Tue, 14 Jan 2025 19:03:41 +0100 Subject: [PATCH 3/4] Fix a typo --- InertiaCoreTests/UnitTestDictionaryData.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InertiaCoreTests/UnitTestDictionaryData.cs b/InertiaCoreTests/UnitTestDictionaryData.cs index 7b332cd..cc3d538 100644 --- a/InertiaCoreTests/UnitTestDictionaryData.cs +++ b/InertiaCoreTests/UnitTestDictionaryData.cs @@ -16,7 +16,7 @@ public async Task TestDictionaryData() ["Key"] = () => "Value", ["KeyAsync"] = () => Task.FromResult("ValueAsync"), ["KeyAsync2"] = Task.FromResult("ValueAsync2"), - ["Always"] = () => new Dictionary + ["Nested"] = () => new Dictionary { ["Key"] = () => "Value" } @@ -40,7 +40,7 @@ public async Task TestDictionaryData() { "keyAsync", "ValueAsync" }, { "keyAsync2", "ValueAsync2" }, { - "always", new Dictionary + "nested", new Dictionary { { "key", "Value" }, } From 8f7cb25741d0adc2100a5cdd035fce14b7331486 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Tue, 14 Jan 2025 19:09:56 +0100 Subject: [PATCH 4/4] Update dotnet.yml --- .github/workflows/dotnet.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index b78f820..899c0f2 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -23,7 +23,11 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 9.0.x + dotnet-version: | + 6.0.x + 7.0.x + 8.0.x + 9.0.x - name: Restore dependencies run: dotnet restore - name: Build