From e9c4586578e8f1e750514ba6f33e1689b130ef57 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Mar 2026 14:23:36 +0000 Subject: [PATCH 1/2] fix: prevent duplicate test ID crash for xUnit MemberData tests; add TypeCheck build target - In ArrayExt.venn, add Array.distinctBy guards before building the ID maps to prevent 'An item with the same key has already been added' exceptions when xUnit MemberData theory tests produce duplicate IDs (Closes #2126, works alongside PR #2127) - In TestItemDTO.getFullname_withNestedParamTests for XUnit, replace the Split('.') |> Array.last approach with Substring(FullName.Length). This correctly handles float parameters like 0.5 in InlineData and parameter records in MemberData that contain dots in their ToString output. - Add a TypeCheck FAKE target to build/Program.fs that runs Fable compilation only (no webpack bundling), providing faster type-checking feedback in development and CI scenarios. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build/Program.fs | 11 +++++++++++ src/Components/TestExplorer.fs | 10 +++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/build/Program.fs b/build/Program.fs index ac5a01f5..f40b0606 100644 --- a/build/Program.fs +++ b/build/Program.fs @@ -365,6 +365,15 @@ let initTargets () = r.Errors |> List.iter (Trace.tracefn "%s") failwith "Error running fantomas") + // TypeCheck: runs Fable compilation only (no webpack) to quickly verify F# type correctness. + // Faster than the full Build target — useful for CI checks that only need type-checking. + Target.create "TypeCheck" (fun _ -> + Fable.run + { Fable.DefaultArgs with + Command = Fable.Build + Debug = false + Webpack = Fable.WithoutWebpack }) + Target.create "Default" ignore Target.create "Build" ignore Target.create "BuildDev" ignore @@ -378,6 +387,8 @@ let buildTargetTree () = "YarnInstall" ==>! "RunScript" "DotNetRestore" ==>! "RunScript" + "DotNetRestore" ==>! "TypeCheck" + "Clean" ==> "Format" ==> "RunScript" ==> "CopyGrammar" ==> "CopySchemas" ==>! "Default" diff --git a/src/Components/TestExplorer.fs b/src/Components/TestExplorer.fs index 4d0a7ee9..1f20134f 100644 --- a/src/Components/TestExplorer.fs +++ b/src/Components/TestExplorer.fs @@ -30,12 +30,14 @@ module ArrayExt = : ('Left array * ('Left * 'Right) array * 'Right array) = let leftIdMap = left + |> Array.distinctBy leftIdf // guard against duplicate IDs (e.g. xUnit MemberData with no unique display name) |> Array.map (fun l -> (leftIdf l, l)) |> dict |> Collections.Generic.Dictionary let rightIdMap = right + |> Array.distinctBy rightIdf // guard against duplicate IDs (e.g. xUnit MemberData with no unique display name) |> Array.map (fun r -> (rightIdf r, r)) |> dict |> Collections.Generic.Dictionary @@ -281,9 +283,11 @@ module TestItemDTO = dto.FullName + "." + dto.DisplayName | Some TestFrameworkId.XUnit -> // NOTE: XUnit includes the FullyQualifiedName in the DisplayName. - // But it doesn't nest theory cases, just appends the case parameters - if dto.DisplayName <> dto.FullName then - let theoryCaseFragment = dto.DisplayName.Split('.') |> Array.last + // But it doesn't nest theory cases, just appends the case parameters. + // We use Substring rather than Split('.') to avoid splitting on dots inside + // float parameters (e.g. "0.5") or record ToString values. + if dto.DisplayName.StartsWith(dto.FullName) && dto.DisplayName.Length > dto.FullName.Length then + let theoryCaseFragment = dto.DisplayName.Substring(dto.FullName.Length) dto.FullName + "." + theoryCaseFragment else dto.FullName From 2b86d530e4bb7c36f75e1f1dbe38d47684d99a8f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 19 Mar 2026 14:32:07 +0000 Subject: [PATCH 2/2] ci: trigger checks