From e1cb743009cfa417c7e2d876cf54e714a2b1c6e0 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Sat, 11 Jul 2026 23:56:32 +0200 Subject: [PATCH 1/2] test: add test project for Fable plugins --- src/Fable.Build/Fable.Build.fsproj | 1 + src/Fable.Build/Main.fs | 2 ++ src/Fable.Build/Test/Plugins.fs | 32 ++++++++++++++++++ .../Plugins/Plugin/Fable.Tests.Plugin.fsproj | 12 +++++++ tests/Integration/Plugins/Plugin/Plugin.fs | 27 +++++++++++++++ .../Plugins/Tests/Fable.Tests.Plugins.fsproj | 17 ++++++++++ tests/Integration/Plugins/Tests/Main.fs | 33 +++++++++++++++++++ .../Integration/Plugins/Tests/PluginsTests.fs | 21 ++++++++++++ 8 files changed, 145 insertions(+) create mode 100644 src/Fable.Build/Test/Plugins.fs create mode 100644 tests/Integration/Plugins/Plugin/Fable.Tests.Plugin.fsproj create mode 100644 tests/Integration/Plugins/Plugin/Plugin.fs create mode 100644 tests/Integration/Plugins/Tests/Fable.Tests.Plugins.fsproj create mode 100644 tests/Integration/Plugins/Tests/Main.fs create mode 100644 tests/Integration/Plugins/Tests/PluginsTests.fs diff --git a/src/Fable.Build/Fable.Build.fsproj b/src/Fable.Build/Fable.Build.fsproj index 95f0ac478..43aaad065 100644 --- a/src/Fable.Build/Fable.Build.fsproj +++ b/src/Fable.Build/Fable.Build.fsproj @@ -29,6 +29,7 @@ + diff --git a/src/Fable.Build/Main.fs b/src/Fable.Build/Main.fs index 178bf3496..d2df0287d 100644 --- a/src/Fable.Build/Main.fs +++ b/src/Fable.Build/Main.fs @@ -46,6 +46,7 @@ Available commands: integration Run the integration test suite standalone Tests the standalone version of Fable (Fable running on top of Node.js) + plugins Run the plugin test suite (tests MemberDeclarationPluginAttribute) Options for all except integration and standalone: --watch Watch for changes and re-run the tests @@ -153,6 +154,7 @@ let main argv = // This test is using quicktest project for now, // because it can't compile (yet?) the Main JavaScript tests | "compiler-js" :: _ -> Test.CompilerJs.handle args + | "plugins" :: args -> Test.Plugins.handle args | _ -> printHelp () | "quicktest" :: args -> match args with diff --git a/src/Fable.Build/Test/Plugins.fs b/src/Fable.Build/Test/Plugins.fs new file mode 100644 index 000000000..44ad0df6f --- /dev/null +++ b/src/Fable.Build/Test/Plugins.fs @@ -0,0 +1,32 @@ +module Build.Test.Plugins + +open Build.FableLibrary +open System.IO +open BlackFox.CommandLine +open Build.Utils +open SimpleExec +open Fake.IO + +let private pluginsTestsDir = + Path.Resolve("tests", "Integration", "Plugins", "Tests") + +let handle (args: string list) = + BuildFableLibraryJavaScript().Run() + + let destinationDir = Path.Resolve("temp", "tests", "Plugins") + + Directory.clean destinationDir + + CmdLine.empty + |> CmdLine.appendRaw pluginsTestsDir + |> CmdLine.appendPrefix "--outDir" destinationDir + |> CmdLine.appendPrefix "--lang" "javascript" + |> CmdLine.appendPrefix "--exclude" "Fable.Core" + // The plugin assembly must be loaded via reflection, not compiled from source. + |> CmdLine.appendPrefix "--exclude" "Fable.Tests.Plugin" + // Fable.AST must stay the same assembly the plugin was built against, not be re-cracked from source. + |> CmdLine.appendPrefix "--exclude" "Fable.AST" + |> CmdLine.appendRaw "--noCache" + |> Command.Fable + + Command.Run("node", "--test-reporter spec --test-timeout 20000 --test Main.js", workingDirectory = destinationDir) diff --git a/tests/Integration/Plugins/Plugin/Fable.Tests.Plugin.fsproj b/tests/Integration/Plugins/Plugin/Fable.Tests.Plugin.fsproj new file mode 100644 index 000000000..7dec09735 --- /dev/null +++ b/tests/Integration/Plugins/Plugin/Fable.Tests.Plugin.fsproj @@ -0,0 +1,12 @@ + + + Library + netstandard2.0 + + + + + + + + diff --git a/tests/Integration/Plugins/Plugin/Plugin.fs b/tests/Integration/Plugins/Plugin/Plugin.fs new file mode 100644 index 000000000..85eaaf028 --- /dev/null +++ b/tests/Integration/Plugins/Plugin/Plugin.fs @@ -0,0 +1,27 @@ +namespace Fable.Tests + +open Fable.AST +open Fable.AST.Fable + +[] +do () + +/// Replaces the whole member body with a constant, proving `Transform` fires. +type ReturnConstPlugin() = + inherit Fable.MemberDeclarationPluginAttribute() + override _.FableMinimumVersion = "5.0" + + override _.Transform(_, _, decl) = + { decl with Body = Value(NumberConstant(NumberValue.Int32 42, NumberInfo.Empty), None) } + + override _.TransformCall(_, _, expr) = expr + +/// Replaces the call site with the called member's name, proving `TransformCall` fires. +type ReturnMemberNamePlugin() = + inherit Fable.MemberDeclarationPluginAttribute() + override _.FableMinimumVersion = "5.0" + + override _.Transform(_, _, decl) = decl + + override _.TransformCall(_, member_, expr) = + Value(StringConstant member_.DisplayName, expr.Range) diff --git a/tests/Integration/Plugins/Tests/Fable.Tests.Plugins.fsproj b/tests/Integration/Plugins/Tests/Fable.Tests.Plugins.fsproj new file mode 100644 index 000000000..7f62eb2b7 --- /dev/null +++ b/tests/Integration/Plugins/Tests/Fable.Tests.Plugins.fsproj @@ -0,0 +1,17 @@ + + + Exe + net10.0 + Major + Preview + + + + + + + + + + + diff --git a/tests/Integration/Plugins/Tests/Main.fs b/tests/Integration/Plugins/Tests/Main.fs new file mode 100644 index 000000000..7db489642 --- /dev/null +++ b/tests/Integration/Plugins/Tests/Main.fs @@ -0,0 +1,33 @@ +module Fable.Tests.Main + +let allTests = [| PluginsTests.tests |] + +#if FABLE_COMPILER + +open Fable.Core +open Fable.Core.JsInterop + +let inline describe (name: string) (f: unit -> unit) : unit = import "describe" "node:test" +let inline it (msg: string) (f: unit -> unit) : unit = import "it" "node:test" + +let rec flattenTest (test: Util.Testing.TestKind) : unit = + match test with + | Util.Testing.TestKind.TestList(name, tests) -> + describe name (fun () -> + for t in tests do + flattenTest t) + | Util.Testing.TestKind.TestCase(name, test) -> it name (unbox test) + +let run () = + for t in allTests do + flattenTest t + +run () + +#else + +// This project only ever runs through Fable; this branch just keeps it a valid .NET project. +[] +let main _ = 0 + +#endif diff --git a/tests/Integration/Plugins/Tests/PluginsTests.fs b/tests/Integration/Plugins/Tests/PluginsTests.fs new file mode 100644 index 000000000..29667a9c4 --- /dev/null +++ b/tests/Integration/Plugins/Tests/PluginsTests.fs @@ -0,0 +1,21 @@ +module Fable.Tests.PluginsTests + +open Fable.Tests +open Util.Testing + +[] +let constFn () = 0 + +[] +let memberNameFn () = "" + +let tests = + testList + "Member Declaration Plugins" + [ + testCase "Transform replaces the compiled member body" + <| fun () -> equal 42 (constFn ()) + + testCase "TransformCall rewrites the call site" + <| fun () -> equal "memberNameFn" (memberNameFn ()) + ] From 4bd031ff9d89845ffcea1b00978fb71c9d410852 Mon Sep 17 00:00:00 2001 From: Mangel Maxime Date: Sun, 12 Jul 2026 00:11:00 +0200 Subject: [PATCH 2/2] chore: clean up --- tests/Integration/Plugins/Tests/Main.fs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/Integration/Plugins/Tests/Main.fs b/tests/Integration/Plugins/Tests/Main.fs index 7db489642..822b37270 100644 --- a/tests/Integration/Plugins/Tests/Main.fs +++ b/tests/Integration/Plugins/Tests/Main.fs @@ -1,12 +1,10 @@ module Fable.Tests.Main -let allTests = [| PluginsTests.tests |] - -#if FABLE_COMPILER - open Fable.Core open Fable.Core.JsInterop +let allTests = [| PluginsTests.tests |] + let inline describe (name: string) (f: unit -> unit) : unit = import "describe" "node:test" let inline it (msg: string) (f: unit -> unit) : unit = import "it" "node:test" @@ -22,12 +20,8 @@ let run () = for t in allTests do flattenTest t -run () - -#else - // This project only ever runs through Fable; this branch just keeps it a valid .NET project. [] -let main _ = 0 - -#endif +let main _ = + run () + 0