diff --git a/OpenNest.Data/NestMaterialSelection.cs b/OpenNest.Data/NestMaterialSelection.cs new file mode 100644 index 0000000..c9aefef --- /dev/null +++ b/OpenNest.Data/NestMaterialSelection.cs @@ -0,0 +1,25 @@ +namespace OpenNest.Data; + +/// +/// Chooses the nest material when the Nest Info dialog is accepted. The dialog +/// edits only the material name, so grade and density must come from the +/// material the nest already has. +/// +public static class NestMaterialSelection +{ + /// + /// Returns a new material named . When that + /// name matches the current material's name by , + /// the current grade and density are kept; any other name gets a name-only + /// material. The current material is never modified or returned. + /// + public static Material Apply(Material? current, string? selectedName) + { + var name = selectedName ?? ""; + + if (current != null && SharedListNames.Key(current.Name) == SharedListNames.Key(name)) + return new Material(name, current.Grade, current.Density); + + return new Material(name); + } +} diff --git a/OpenNest.Data/SharedListNames.cs b/OpenNest.Data/SharedListNames.cs new file mode 100644 index 0000000..8533f30 --- /dev/null +++ b/OpenNest.Data/SharedListNames.cs @@ -0,0 +1,18 @@ +namespace OpenNest.Data; + +/// +/// Normalized matching key for names in company lists (customers, materials). +/// +public static class SharedListNames +{ + /// + /// Returns the name trimmed, with internal whitespace runs collapsed to one + /// space, in invariant upper case. Names that differ only in spacing or case + /// share a key. A null or blank name has the empty key. + /// + public static string Key(string? name) => + name == null + ? "" + : string.Join(' ', name.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries)) + .ToUpperInvariant(); +} diff --git a/OpenNest.Tests/Data/NestMaterialSelectionTests.cs b/OpenNest.Tests/Data/NestMaterialSelectionTests.cs new file mode 100644 index 0000000..243855a --- /dev/null +++ b/OpenNest.Tests/Data/NestMaterialSelectionTests.cs @@ -0,0 +1,90 @@ +using OpenNest.Data; + +namespace OpenNest.Tests.Data; + +public class NestMaterialSelectionTests +{ + [Fact] + public void Apply_UnchangedName_KeepsGradeAndDensity() + { + var current = new Material("Stainless", "304", 0.289); + + var result = NestMaterialSelection.Apply(current, "Stainless"); + + Assert.Equal("Stainless", result.Name); + Assert.Equal("304", result.Grade); + Assert.Equal(0.289, result.Density); + } + + [Fact] + public void Apply_NameDifferingOnlyInSpacingAndCase_KeepsGradeAndDensityAndTakesTypedName() + { + var current = new Material("Cold Rolled Steel", "1008", 0.284); + + var result = NestMaterialSelection.Apply(current, " cold rolled\tsteel "); + + Assert.Equal(" cold rolled\tsteel ", result.Name); + Assert.Equal("1008", result.Grade); + Assert.Equal(0.284, result.Density); + } + + [Fact] + public void Apply_ChangedName_GetsNameOnly() + { + var current = new Material("Stainless", "304", 0.289); + + var result = NestMaterialSelection.Apply(current, "Aluminum"); + + Assert.Equal("Aluminum", result.Name); + Assert.True(string.IsNullOrEmpty(result.Grade)); + Assert.Equal(0, result.Density); + } + + [Fact] + public void Apply_ClearedName_GetsEmptyNameOnly() + { + var current = new Material("Stainless", "304", 0.289); + + var result = NestMaterialSelection.Apply(current, ""); + + Assert.Equal("", result.Name); + Assert.True(string.IsNullOrEmpty(result.Grade)); + Assert.Equal(0, result.Density); + } + + [Fact] + public void Apply_NoCurrentMaterial_GetsNameOnly() + { + var result = NestMaterialSelection.Apply(null, "Stainless"); + + Assert.Equal("Stainless", result.Name); + Assert.True(string.IsNullOrEmpty(result.Grade)); + Assert.Equal(0, result.Density); + } + + [Fact] + public void Apply_ReturnsCopy_LeavingCurrentMaterialUnchanged() + { + var current = new Material("Stainless", "304", 0.289); + + var result = NestMaterialSelection.Apply(current, "stainless"); + result.Grade = "316"; + result.Density = 1; + + Assert.NotSame(current, result); + Assert.Equal("Stainless", current.Name); + Assert.Equal("304", current.Grade); + Assert.Equal(0.289, current.Density); + } + + [Theory] + [InlineData("Carbon Steel", "CARBON STEEL")] + [InlineData(" carbon \t steel\n", "CARBON STEEL")] + [InlineData("", "")] + [InlineData(" ", "")] + [InlineData(null, "")] + public void SharedListNamesKey_TrimsCollapsesWhitespaceAndUppercases(string? name, string expected) + { + Assert.Equal(expected, SharedListNames.Key(name)); + } +} diff --git a/OpenNest.WinForms.Tests/Forms/EditNestInfoFormTests.cs b/OpenNest.WinForms.Tests/Forms/EditNestInfoFormTests.cs new file mode 100644 index 0000000..44bb9a8 --- /dev/null +++ b/OpenNest.WinForms.Tests/Forms/EditNestInfoFormTests.cs @@ -0,0 +1,44 @@ +using OpenNest.Forms; + +namespace OpenNest.WinForms.Tests.Forms; + +public class EditNestInfoFormTests +{ + [Fact] + public void SaveWithUnchangedMaterial_KeepsGradeAndDensity() + { + RunSta(() => + { + var nest = new Nest { Material = new Material("Stainless", "304", 0.289) }; + using var form = new EditNestInfoForm(); + form.LoadNestInfo(nest); + + form.SaveNestInfo(nest); + + Assert.Equal("Stainless", nest.Material.Name); + Assert.Equal("304", nest.Material.Grade); + Assert.Equal(0.289, nest.Material.Density); + }); + } + + [Fact] + public void SaveWithChangedMaterial_GetsNameOnly() + { + RunSta(() => + { + var nest = new Nest { Material = new Material("Stainless", "304", 0.289) }; + using var form = new EditNestInfoForm(); + form.LoadNestInfo(nest); + form.MaterialName = "Aluminum"; + + form.SaveNestInfo(nest); + + Assert.Equal("Aluminum", nest.Material.Name); + Assert.True(string.IsNullOrEmpty(nest.Material.Grade)); + Assert.Equal(0, nest.Material.Density); + }); + } + + private static void RunSta(Action action) => + StaTestThread.Run(action, TimeSpan.FromSeconds(15), "The STA test did not complete."); +} diff --git a/OpenNest/Forms/EditNestInfoForm.cs b/OpenNest/Forms/EditNestInfoForm.cs index f1d38af..3cd4f62 100644 --- a/OpenNest/Forms/EditNestInfoForm.cs +++ b/OpenNest/Forms/EditNestInfoForm.cs @@ -1,5 +1,6 @@ using System; using System.Windows.Forms; +using OpenNest.Data; using OpenNest.Geometry; using Timer = System.Timers.Timer; @@ -238,7 +239,7 @@ namespace OpenNest.Forms nest.DateCreated = DateCreated; nest.DateLastModified = DateLastModified; nest.Thickness = Thickness; - nest.Material = new Material(MaterialName); + nest.Material = NestMaterialSelection.Apply(nest.Material, MaterialName); nest.Status = Status; nest.MadeBy = MadeBy; nest.PlateDefaults.Size = OpenNest.Geometry.Size.Parse(SizeString);