From 68bdf84508aac7a7f2bb98cb0c28d4c8c9be6912 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Fri, 17 Jul 2026 16:08:31 +0100 Subject: [PATCH] test(frontend): exercise the container update-alerts card controls The card from f8e8135a3 had model-level coverage only; nothing rendered the toggle or delay input. Pin the interaction contract: toggle off writes -1 and hides the delay input, toggle on restores the previous positive delay (factory 24 when enabling from a persisted off state), and delay edits clamp to a positive whole hour so 0 (backend 'unset', silently reset to 24) can never be produced from the UI. --- ...ldsTableDockerUpdateAlertsSection.test.tsx | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 frontend-modern/src/components/Alerts/__tests__/ThresholdsTableDockerUpdateAlertsSection.test.tsx diff --git a/frontend-modern/src/components/Alerts/__tests__/ThresholdsTableDockerUpdateAlertsSection.test.tsx b/frontend-modern/src/components/Alerts/__tests__/ThresholdsTableDockerUpdateAlertsSection.test.tsx new file mode 100644 index 0000000000..7349990fc8 --- /dev/null +++ b/frontend-modern/src/components/Alerts/__tests__/ThresholdsTableDockerUpdateAlertsSection.test.tsx @@ -0,0 +1,95 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { cleanup, fireEvent, render, screen } from '@solidjs/testing-library'; +import { createSignal } from 'solid-js'; + +import { ThresholdsTableDockerUpdateAlertsSection } from '@/components/Alerts/ThresholdsTableDockerUpdateAlertsSection'; +import type { ThresholdsTableSectionProps } from '@/features/alerts/thresholds/thresholdsTableSectionProps'; +import { getAlertThresholdsDockerUpdatePresentation } from '@/utils/alertThresholdsPresentation'; +import { FACTORY_DOCKER_DEFAULTS } from '@/utils/alertThresholdDefaults'; + +type DockerDefaultsShape = typeof FACTORY_DOCKER_DEFAULTS; + +// The card owns the only UI lever for updateAlertDelayHours (#1545): the +// toggle must write -1 (off) — never 0, which the backend resets to 24 — +// and restore the previous positive delay when re-enabled. +function renderSection(initialDelayHours: number) { + const [dockerDefaults, setDockerDefaultsSignal] = createSignal({ + ...FACTORY_DOCKER_DEFAULTS, + updateAlertDelayHours: initialDelayHours, + }); + const setHasUnsavedChanges = vi.fn(); + const setDockerDefaults = vi.fn( + (update: (prev: DockerDefaultsShape) => DockerDefaultsShape) => { + setDockerDefaultsSignal((prev) => update(prev)); + }, + ); + + const props = { + state: { + dockerUpdatePresentation: getAlertThresholdsDockerUpdatePresentation(), + dockerUpdateDelayInputId: 'docker-update-alert-delay', + }, + tableProps: { + get dockerDefaults() { + return dockerDefaults(); + }, + setDockerDefaults, + setHasUnsavedChanges, + }, + } as unknown as ThresholdsTableSectionProps; + + const utils = render(() => ); + return { ...utils, dockerDefaults, setHasUnsavedChanges }; +} + +afterEach(cleanup); + +describe('ThresholdsTableDockerUpdateAlertsSection', () => { + it('renders the delay input while enabled and hides it when off', () => { + const { dockerDefaults } = renderSection(24); + + const input = screen.getByLabelText('Delay (hours)') as HTMLInputElement; + expect(input.value).toBe('24'); + + fireEvent.click(screen.getByRole('button', { pressed: true })); + + expect(dockerDefaults().updateAlertDelayHours).toBe(-1); + expect(screen.queryByLabelText('Delay (hours)')).toBeNull(); + }); + + it('saves -1 on toggle off and restores the prior delay on toggle on', () => { + const { dockerDefaults, setHasUnsavedChanges } = renderSection(48); + + const toggle = screen.getByRole('button', { pressed: true }); + fireEvent.click(toggle); + expect(dockerDefaults().updateAlertDelayHours).toBe(-1); + + fireEvent.click(toggle); + expect(dockerDefaults().updateAlertDelayHours).toBe(48); + expect(setHasUnsavedChanges).toHaveBeenCalledWith(true); + }); + + it('falls back to the factory delay when enabling from a persisted off state', () => { + const { dockerDefaults } = renderSection(-1); + + expect(screen.queryByLabelText('Delay (hours)')).toBeNull(); + + fireEvent.click(screen.getByRole('button', { pressed: false })); + expect(dockerDefaults().updateAlertDelayHours).toBe( + FACTORY_DOCKER_DEFAULTS.updateAlertDelayHours, + ); + }); + + it('clamps delay input edits to a positive whole number of hours', () => { + const { dockerDefaults } = renderSection(24); + + const input = screen.getByLabelText('Delay (hours)') as HTMLInputElement; + fireEvent.input(input, { target: { value: '0' } }); + // 0 would read as "unset" server-side and silently become 24; the input + // clamps to the minimum positive delay instead. + expect(dockerDefaults().updateAlertDelayHours).toBe(1); + + fireEvent.input(input, { target: { value: '72' } }); + expect(dockerDefaults().updateAlertDelayHours).toBe(72); + }); +});