fix(ui): Task 11 review — verify false override end-to-end, move HasOverrideDelta to the partial, label wording
Claude-Session: https://claude.ai/code/session_01Y5ef34iUUMjmwnMpL9xgmB
This commit is contained in:
+1
-12
@@ -549,7 +549,7 @@
|
|||||||
@* Tri-state: blank = inherit the template alarm's flag.
|
@* Tri-state: blank = inherit the template alarm's flag.
|
||||||
"Not reportable" is a real override, not a clear — it
|
"Not reportable" is a real override, not a clear — it
|
||||||
pins a reportable template alarm off for this instance. *@
|
pins a reportable template alarm off for this instance. *@
|
||||||
<label class="form-label small text-uppercase text-muted fw-semibold mb-1">MES reportable</label>
|
<label class="form-label small text-uppercase text-muted fw-semibold mb-1">MES reportable override</label>
|
||||||
<select class="form-select form-select-sm" data-test="alarm-mes-select" @bind="_editingMesReportableText">
|
<select class="form-select form-select-sm" data-test="alarm-mes-select" @bind="_editingMesReportableText">
|
||||||
<option value="">Inherited (@(_editingAlarm.IsMesReportable ? "reportable" : "not reportable"))</option>
|
<option value="">Inherited (@(_editingAlarm.IsMesReportable ? "reportable" : "not reportable"))</option>
|
||||||
<option value="true">Reportable</option>
|
<option value="true">Reportable</option>
|
||||||
@@ -1579,17 +1579,6 @@
|
|||||||
return parts.Count == 0 ? "(empty)" : string.Join(", ", parts);
|
return parts.Count == 0 ? "(empty)" : string.Join(", ", parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The save-time delta rule: does the modal carry anything worth persisting?
|
|
||||||
/// When it does not, <see cref="SaveOverrideFromModal"/> DELETES any existing
|
|
||||||
/// override — so every override term must be represented here or it would be
|
|
||||||
/// silently dropped on save. A <c>false</c> MES-reportable choice counts: it
|
|
||||||
/// pins a reportable template alarm off for this instance, which is a real
|
|
||||||
/// override and not the same as inheriting.
|
|
||||||
/// </summary>
|
|
||||||
internal static bool HasOverrideDelta(string? overrideJson, int? priority, bool? mesReportable)
|
|
||||||
=> !string.IsNullOrWhiteSpace(overrideJson) || priority.HasValue || mesReportable.HasValue;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the override editor modal pre-populated with the merged
|
/// Opens the override editor modal pre-populated with the merged
|
||||||
/// (inherited + existing override) config so the user sees the effective
|
/// (inherited + existing override) config so the user sees the effective
|
||||||
|
|||||||
+11
@@ -115,6 +115,17 @@ public partial class InstanceConfigure
|
|||||||
return new CsvOverrideImportOutcome(overrides, Array.Empty<string>());
|
return new CsvOverrideImportOutcome(overrides, Array.Empty<string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The save-time delta rule for the alarm-override modal: does it carry anything
|
||||||
|
/// worth persisting? When it does not, <c>SaveOverrideFromModal</c> DELETES any
|
||||||
|
/// existing override — so every override term must be represented here or it
|
||||||
|
/// would be silently dropped on save. A <c>false</c> MES-reportable choice
|
||||||
|
/// counts: it pins a reportable template alarm off for this instance, which is a
|
||||||
|
/// real override and not the same as inheriting.
|
||||||
|
/// </summary>
|
||||||
|
internal static bool HasOverrideDelta(string? overrideJson, int? priority, bool? mesReportable)
|
||||||
|
=> !string.IsNullOrWhiteSpace(overrideJson) || priority.HasValue || mesReportable.HasValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// One validated native-alarm-source retarget from an imported CSV: the source
|
/// One validated native-alarm-source retarget from an imported CSV: the source
|
||||||
/// binding's name plus the three optional override fields (null = keep the
|
/// binding's name plus the three optional override fields (null = keep the
|
||||||
|
|||||||
+28
-1
@@ -527,7 +527,9 @@ public sealed class InstanceConfigureTests : IClassFixture<InstanceConfigureFixt
|
|||||||
/// priority DELETES the override, so a reportable-only choice has to count as a delta on its own
|
/// priority DELETES the override, so a reportable-only choice has to count as a delta on its own
|
||||||
/// or it would be silently dropped. The assertion is therefore that choosing ONLY "Reportable"
|
/// or it would be silently dropped. The assertion is therefore that choosing ONLY "Reportable"
|
||||||
/// (leaving the trigger config and priority untouched) creates the override — badge + a summary
|
/// (leaving the trigger config and priority untouched) creates the override — badge + a summary
|
||||||
/// naming <c>mesReportable</c> — and that switching back to Inherited removes it again.
|
/// naming <c>mesReportable</c> — that switching to "Not reportable" persists <c>false</c>
|
||||||
|
/// (verified against the read-back document, since an absent/null field there would mean the
|
||||||
|
/// negative pin was dropped), and that switching back to Inherited removes the override again.
|
||||||
/// </para>
|
/// </para>
|
||||||
///
|
///
|
||||||
/// <para>
|
/// <para>
|
||||||
@@ -563,6 +565,31 @@ public sealed class InstanceConfigureTests : IClassFixture<InstanceConfigureFixt
|
|||||||
// Summary part is "mesReportable" (the negative pin renders "notMesReportable").
|
// Summary part is "mesReportable" (the negative pin renders "notMesReportable").
|
||||||
await Assertions.Expect(row).ToContainTextAsync("mesReportable", new() { Timeout = 15_000 });
|
await Assertions.Expect(row).ToContainTextAsync("mesReportable", new() { Timeout = 15_000 });
|
||||||
|
|
||||||
|
// Reopen → switch to the NEGATIVE pin. This is the leg that proves "Not reportable" is
|
||||||
|
// a real write and not a clear: the row must stay overridden and the persisted
|
||||||
|
// isMesReportableOverride must be JSON false, never absent/null.
|
||||||
|
await row.Locator("[data-test='alarm-edit-btn']").ClickAsync();
|
||||||
|
await Assertions.Expect(mesSelect).ToBeVisibleAsync(new() { Timeout = 15_000 });
|
||||||
|
await mesSelect.SelectOptionAsync("false");
|
||||||
|
await page.Locator("[data-test='alarm-save-override']").ClickAsync();
|
||||||
|
|
||||||
|
await Assertions.Expect(row.Locator("[data-test='alarm-override-badge']"))
|
||||||
|
.ToBeVisibleAsync(new() { Timeout = 15_000 });
|
||||||
|
await Assertions.Expect(row).ToContainTextAsync("notMesReportable", new() { Timeout = 15_000 });
|
||||||
|
|
||||||
|
using (var negDoc = await CliRunner.GetInstanceDocumentAsync(_cfg.InstanceId))
|
||||||
|
{
|
||||||
|
var negOverrides = negDoc.RootElement.GetProperty("alarmOverrides");
|
||||||
|
var entry = negOverrides.EnumerateArray().Single(o =>
|
||||||
|
o.GetProperty("alarmCanonicalName").GetString() == _cfg.AlarmName);
|
||||||
|
|
||||||
|
Assert.True(entry.TryGetProperty("isMesReportableOverride", out var flag),
|
||||||
|
"alarmOverrides entry is missing isMesReportableOverride.");
|
||||||
|
// JSON false specifically — an absent/null field would mean the negative pin was
|
||||||
|
// dropped and the alarm silently fell back to the template's flag.
|
||||||
|
Assert.Equal(JsonValueKind.False, flag.ValueKind);
|
||||||
|
}
|
||||||
|
|
||||||
// Reopen → back to Inherited (blank) → Save. With nothing else set that is now a
|
// Reopen → back to Inherited (blank) → Save. With nothing else set that is now a
|
||||||
// no-delta save, which clears the override outright.
|
// no-delta save, which clears the override outright.
|
||||||
await row.Locator("[data-test='alarm-edit-btn']").ClickAsync();
|
await row.Locator("[data-test='alarm-edit-btn']").ClickAsync();
|
||||||
|
|||||||
+3
@@ -46,6 +46,9 @@ public class InstanceConfigureMesReportableTests
|
|||||||
var selectEnd = markup.IndexOf("</select>", selectStart, StringComparison.Ordinal);
|
var selectEnd = markup.IndexOf("</select>", selectStart, StringComparison.Ordinal);
|
||||||
Assert.True(selectEnd > selectStart, "MES reportable select is not closed.");
|
Assert.True(selectEnd > selectStart, "MES reportable select is not closed.");
|
||||||
|
|
||||||
|
// Label mirrors the adjacent "Priority override" control.
|
||||||
|
Assert.Contains("MES reportable override</label>", markup);
|
||||||
|
|
||||||
var selectBody = markup[selectStart..selectEnd];
|
var selectBody = markup[selectStart..selectEnd];
|
||||||
var optionCount = selectBody.Split("<option").Length - 1;
|
var optionCount = selectBody.Split("<option").Length - 1;
|
||||||
Assert.Equal(3, optionCount);
|
Assert.Equal(3, optionCount);
|
||||||
|
|||||||
Reference in New Issue
Block a user