mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-09 21:24:17 -04:00
fix(nest-info): keep material grade and density when the name is unchanged
Nest Info edits only the material name, but SaveNestInfo replaced the nest's material with new Material(name), so pressing OK without touching the material silently dropped its grade and density. SaveNestInfo now goes through NestMaterialSelection.Apply in OpenNest.Data: a name with the same SharedListNames.Key as the current material (trimmed, whitespace collapsed, invariant upper case) keeps its grade and density and takes the typed name; any other name gets a name-only material, as before. It always returns a new Material. SharedListNames.Key is the name normalizer the shared customer/material lists will use. Behavioral reds (each restored byte-identically): restoring the old name-only path fails both keep-grade tests; exact-string matching, dropping the upper-casing or the whitespace collapse fails the normalized-name test; returning the current instance fails the copy test. EditNestInfoFormTests cover Load -> Save through the real form; they are cross-compiled here and execute in the windows-desktop CI job.
This commit is contained in:
1 parent
1b5a6bf6da
commit
03b3ed47b2
5 files changed
+179
-1
No files matched your search
@@ -0,0 +1,25 @@
|
||||
namespace OpenNest.Data;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static class NestMaterialSelection
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a new material named <paramref name="selectedName"/>. When that
|
||||
/// name matches the current material's name by <see cref="SharedListNames.Key"/>,
|
||||
/// the current grade and density are kept; any other name gets a name-only
|
||||
/// material. The current material is never modified or returned.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace OpenNest.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Normalized matching key for names in company lists (customers, materials).
|
||||
/// </summary>
|
||||
public static class SharedListNames
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static string Key(string? name) =>
|
||||
name == null
|
||||
? ""
|
||||
: string.Join(' ', name.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries))
|
||||
.ToUpperInvariant();
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user