From d8e7ee4197433b96b71735ba4b3354c946ad66eb Mon Sep 17 00:00:00 2001 From: "Brett V. Forsgren" Date: Wed, 9 Oct 2019 15:04:07 -0700 Subject: [PATCH 1/2] parameterize kernel rendering tests based on language --- ...sts.cs => LanguageKernelRenderingTests.cs} | 100 +++++++++++++----- 1 file changed, 71 insertions(+), 29 deletions(-) rename WorkspaceServer.Tests/Kernel/{CSharpKernelRenderingTests.cs => LanguageKernelRenderingTests.cs} (58%) diff --git a/WorkspaceServer.Tests/Kernel/CSharpKernelRenderingTests.cs b/WorkspaceServer.Tests/Kernel/LanguageKernelRenderingTests.cs similarity index 58% rename from WorkspaceServer.Tests/Kernel/CSharpKernelRenderingTests.cs rename to WorkspaceServer.Tests/Kernel/LanguageKernelRenderingTests.cs index 2d31721c7..1e3da9932 100644 --- a/WorkspaceServer.Tests/Kernel/CSharpKernelRenderingTests.cs +++ b/WorkspaceServer.Tests/Kernel/LanguageKernelRenderingTests.cs @@ -1,12 +1,11 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; +using System.Linq; using System.Reactive.Linq; +using System.Threading.Tasks; using FluentAssertions; using FluentAssertions.Extensions; -using System.Linq; -using System.Threading.Tasks; using Microsoft.DotNet.Interactive; using Microsoft.DotNet.Interactive.Commands; using Microsoft.DotNet.Interactive.Events; @@ -16,23 +15,31 @@ using Xunit.Abstractions; using static Pocket.Logger; +#pragma warning disable 8509 namespace WorkspaceServer.Tests.Kernel { - public class CSharpKernelRenderingTests : LanguageKernelTestBase + public class LanguageKernelRenderingTests : LanguageKernelTestBase { - public CSharpKernelRenderingTests(ITestOutputHelper output) : base(output) + public LanguageKernelRenderingTests(ITestOutputHelper output) : base(output) { } [Theory] - [InlineData("b(123) // PocketView", "123")] - [InlineData("new[] { 1, 2, 3, 4 } // sequence", "")] - [InlineData("new[] { new { a = 123 }, new { a = 456 } } // sequence of anonymous objects", "
")] + // PocketView + [InlineData(Language.CSharp, "b(123)", "123")] + [InlineData(Language.FSharp, "b.innerHTML(123)", "123")] + // sequence + [InlineData(Language.CSharp, "new[] { 1, 2, 3, 4 }", "
")] + [InlineData(Language.FSharp, "[1; 2; 3; 4]", "
")] + // sequence of anonymous objects + [InlineData(Language.CSharp, "new[] { new { a = 123 }, new { a = 456 } }", "
")] + [InlineData(Language.FSharp, "[{| a = 123 |}; {| a = 456 |}]", "
")] public async Task Default_rendering_is_HTML( + Language language, string submission, string expectedContent) { - var kernel = CreateKernel(); + var kernel = CreateKernel(language); var result = await kernel.SendAsync(new SubmitCode(submission)); @@ -53,14 +60,18 @@ public async Task Default_rendering_is_HTML( } [Theory] - [InlineData("div(123).ToString()", "
123
")] - [InlineData("display(div(123).ToString());", "
123
")] - [InlineData("\"hi\"", "hi")] + [InlineData(Language.CSharp, "div(123).ToString()", "
123
")] + [InlineData(Language.FSharp, "div.innerHTML(123).ToString()", "
123
")] + [InlineData(Language.CSharp, "display(div(123).ToString());", "
123
")] + //[InlineData(Language.FSharp, "display(div.innerHTML(123).ToString());", "
123
")] + [InlineData(Language.CSharp, "\"hi\"", "hi")] + [InlineData(Language.FSharp, "\"hi\"", "hi")] public async Task String_is_rendered_as_plain_text( + Language language, string submission, string expectedContent) { - var kernel = CreateKernel(); + var kernel = CreateKernel(language); var result = await kernel.SendAsync(new SubmitCode(submission)); @@ -78,12 +89,20 @@ public async Task String_is_rendered_as_plain_text( v.Value.ToString().Contains(expectedContent)); } - [Fact] - public async Task Display_helper_can_be_called_without_specifying_class_name() + [Theory] + [InlineData(Language.CSharp)] + //[InlineData(Language.FSharp)] + public async Task Display_helper_can_be_called_without_specifying_class_name(Language language) { - var kernel = CreateKernel(); + var kernel = CreateKernel(language); + + var submission = language switch + { + Language.CSharp => "display(b(\"hi!\"));", + Language.FSharp => "display(b.innerHTML(\"hi!\"));", + }; - await kernel.SendAsync(new SubmitCode("display(b(\"hi!\"));")); + await kernel.SendAsync(new SubmitCode(submission)); var formatted = KernelEvents @@ -98,13 +117,20 @@ public async Task Display_helper_can_be_called_without_specifying_class_name() v.Value.ToString().Contains("hi!")); } - [Fact] - public async Task Displayed_value_can_be_updated() + [Theory] + [InlineData(Language.CSharp)] + //[InlineData(Language.FSharp)] + public async Task Displayed_value_can_be_updated(Language language) { - var kernel = CreateKernel(); + var kernel = CreateKernel(language); - await kernel.SendAsync(new SubmitCode("var d = display(b(\"hello\")); d.Update(b(\"world\"));")); + var submission = language switch + { + Language.CSharp => "var d = display(b(\"hello\")); d.Update(b(\"world\"));", + Language.FSharp => "let d = display(b.innerHTML(\"hello\"))\nd.Update(b.innerHTML(\"world\"))", + }; + await kernel.SendAsync(new SubmitCode(submission)); KernelEvents .OrderBy(e => e.Timestamp) @@ -128,12 +154,20 @@ public async Task Displayed_value_can_be_updated() v.Value.ToString().Contains("world")); } - [Fact] - public async Task Value_display_and_update_are_in_right_order() + [Theory] + [InlineData(Language.CSharp)] + //[InlineData(Language.FSharp)] + public async Task Value_display_and_update_are_in_right_order(Language language) { - var kernel = CreateKernel(); + var kernel = CreateKernel(language); + + var submission = language switch + { + Language.CSharp => "var d = display(b(\"hello\")); d.Update(b(\"world\"));", + Language.FSharp => "let d = display(b.innerHTML(\"hello\"))\nd.Update(b.innerHTML(\"world\"))", + }; - await kernel.SendAsync(new SubmitCode("var d = display(b(\"hello\")); d.Update(b(\"world\"));")); + await kernel.SendAsync(new SubmitCode(submission)); var valueEvents = KernelEvents @@ -146,14 +180,22 @@ public async Task Value_display_and_update_are_in_right_order() valueEvents.Last().Should().BeOfType(); } - [Fact] - public async Task Javascript_helper_emits_string_as_content_within_a_script_element() + [Theory] + [InlineData(Language.CSharp)] + //[InlineData(Language.FSharp)] + public async Task Javascript_helper_emits_string_as_content_within_a_script_element(Language language) { - var kernel = CreateKernel(); + var kernel = CreateKernel(language); var scriptContent = "alert('Hello World!');"; - await kernel.SendAsync(new SubmitCode($@"Javascript(""{scriptContent}"");")); + var submission = language switch + { + Language.CSharp => $@"Javascript(""{scriptContent}"");", + Language.FSharp => $@"Javascript(""{scriptContent}"")", + }; + + await kernel.SendAsync(new SubmitCode(submission)); var formatted = KernelEvents From d94942a56eec5c5c45a25697171f9cbfebd50062 Mon Sep 17 00:00:00 2001 From: "Brett V. Forsgren" Date: Wed, 9 Oct 2019 15:29:26 -0700 Subject: [PATCH 2/2] add F# kernel helper functions --- MLS.Agent/CommandLine/CommandLineParser.cs | 1 + .../FSharpKernelHelpers.fs | 12 ++++++++++++ .../Microsoft.DotNet.Interactive.FSharp.fsproj | 1 + .../Kernel/LanguageKernelRenderingTests.cs | 10 +++++----- .../Kernel/LanguageKernelTestBase.cs | 2 +- WorkspaceServer/Kernel/FSharpKernelExtensions.cs | 11 +++++++++++ 6 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 Microsoft.DotNet.Interactive.FSharp/FSharpKernelHelpers.fs diff --git a/MLS.Agent/CommandLine/CommandLineParser.cs b/MLS.Agent/CommandLine/CommandLineParser.cs index f08cb70ac..49d6a6f7f 100644 --- a/MLS.Agent/CommandLine/CommandLineParser.cs +++ b/MLS.Agent/CommandLine/CommandLineParser.cs @@ -545,6 +545,7 @@ private static IKernel CreateKernel(string defaultKernelName) .UseXplot(), new FSharpKernel() .UseDefaultRendering() + .UseKernelHelpers() .UseXplot() } .UseDefaultMagicCommands() diff --git a/Microsoft.DotNet.Interactive.FSharp/FSharpKernelHelpers.fs b/Microsoft.DotNet.Interactive.FSharp/FSharpKernelHelpers.fs new file mode 100644 index 000000000..8da70830f --- /dev/null +++ b/Microsoft.DotNet.Interactive.FSharp/FSharpKernelHelpers.fs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DotNet.Interactive.FSharp + +open Microsoft.DotNet.Interactive + +module FSharpKernelHelpers = + let display (value: obj) = + Kernel.display value + let Javascript (content: string) = + Kernel.Javascript content diff --git a/Microsoft.DotNet.Interactive.FSharp/Microsoft.DotNet.Interactive.FSharp.fsproj b/Microsoft.DotNet.Interactive.FSharp/Microsoft.DotNet.Interactive.FSharp.fsproj index f59340c3c..6083183f3 100644 --- a/Microsoft.DotNet.Interactive.FSharp/Microsoft.DotNet.Interactive.FSharp.fsproj +++ b/Microsoft.DotNet.Interactive.FSharp/Microsoft.DotNet.Interactive.FSharp.fsproj @@ -17,6 +17,7 @@ + diff --git a/WorkspaceServer.Tests/Kernel/LanguageKernelRenderingTests.cs b/WorkspaceServer.Tests/Kernel/LanguageKernelRenderingTests.cs index 1e3da9932..9231ff662 100644 --- a/WorkspaceServer.Tests/Kernel/LanguageKernelRenderingTests.cs +++ b/WorkspaceServer.Tests/Kernel/LanguageKernelRenderingTests.cs @@ -63,7 +63,7 @@ public async Task Default_rendering_is_HTML( [InlineData(Language.CSharp, "div(123).ToString()", "
123
")] [InlineData(Language.FSharp, "div.innerHTML(123).ToString()", "
123
")] [InlineData(Language.CSharp, "display(div(123).ToString());", "
123
")] - //[InlineData(Language.FSharp, "display(div.innerHTML(123).ToString());", "
123
")] + [InlineData(Language.FSharp, "display(div.innerHTML(123).ToString())", "
123
")] [InlineData(Language.CSharp, "\"hi\"", "hi")] [InlineData(Language.FSharp, "\"hi\"", "hi")] public async Task String_is_rendered_as_plain_text( @@ -91,7 +91,7 @@ public async Task String_is_rendered_as_plain_text( [Theory] [InlineData(Language.CSharp)] - //[InlineData(Language.FSharp)] + [InlineData(Language.FSharp)] public async Task Display_helper_can_be_called_without_specifying_class_name(Language language) { var kernel = CreateKernel(language); @@ -119,7 +119,7 @@ public async Task Display_helper_can_be_called_without_specifying_class_name(Lan [Theory] [InlineData(Language.CSharp)] - //[InlineData(Language.FSharp)] + [InlineData(Language.FSharp)] public async Task Displayed_value_can_be_updated(Language language) { var kernel = CreateKernel(language); @@ -156,7 +156,7 @@ public async Task Displayed_value_can_be_updated(Language language) [Theory] [InlineData(Language.CSharp)] - //[InlineData(Language.FSharp)] + [InlineData(Language.FSharp)] public async Task Value_display_and_update_are_in_right_order(Language language) { var kernel = CreateKernel(language); @@ -182,7 +182,7 @@ public async Task Value_display_and_update_are_in_right_order(Language language) [Theory] [InlineData(Language.CSharp)] - //[InlineData(Language.FSharp)] + [InlineData(Language.FSharp)] public async Task Javascript_helper_emits_string_as_content_within_a_script_element(Language language) { var kernel = CreateKernel(language); diff --git a/WorkspaceServer.Tests/Kernel/LanguageKernelTestBase.cs b/WorkspaceServer.Tests/Kernel/LanguageKernelTestBase.cs index 51099705f..7507b22b3 100644 --- a/WorkspaceServer.Tests/Kernel/LanguageKernelTestBase.cs +++ b/WorkspaceServer.Tests/Kernel/LanguageKernelTestBase.cs @@ -27,7 +27,7 @@ private KernelBase CreateLanguageKernel(Language language) { var kernelBase = language switch { - Language.FSharp => (KernelBase) new FSharpKernel().UseDefaultRendering(), + Language.FSharp => (KernelBase) new FSharpKernel().UseDefaultRendering().UseKernelHelpers(), Language.CSharp => new CSharpKernel().UseDefaultRendering().UseExtendDirective().UseKernelHelpers(), _ => throw new InvalidOperationException("Unknown language specified") }; diff --git a/WorkspaceServer/Kernel/FSharpKernelExtensions.cs b/WorkspaceServer/Kernel/FSharpKernelExtensions.cs index 460b3ff51..1fce2f256 100644 --- a/WorkspaceServer/Kernel/FSharpKernelExtensions.cs +++ b/WorkspaceServer/Kernel/FSharpKernelExtensions.cs @@ -29,6 +29,17 @@ public static FSharpKernel UseDefaultRendering( return kernel; } + public static FSharpKernel UseKernelHelpers( + this FSharpKernel kernel) + { + Task.Run(() => + kernel.SendAsync(new SubmitCode($@" +open {typeof(FSharpKernelHelpers).FullName} +"))).Wait(); + + return kernel; + } + private static string ReferenceFromType(Type type) { return $@"#r ""{type.Assembly.Location.Replace("\\", "\\\\")}""";