From d0df71fc4a180c7009d32e355a485e37086bf808 Mon Sep 17 00:00:00 2001
From: "pulse-triage[bot]"
<249995291+pulse-triage[bot]@users.noreply.github.com>
Date: Fri, 4 Sep 2026 23:18:48 +0100
Subject: [PATCH] Guard unsaved connection edits against polling regressions
Exercise the NodeCredentialSlot dirty-edit path across repeated server snapshots and verify Save retains operator values and the current edit target. Removing the dirty guard reproduces the failure.
Contract-Neutral: regression coverage only; no runtime or API changes
Change-source: pulse-maintainer
---
.../__tests__/NodeCredentialSlot.test.tsx | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/__tests__/NodeCredentialSlot.test.tsx b/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/__tests__/NodeCredentialSlot.test.tsx
index 8f10eee58..f8698bef8 100644
--- a/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/__tests__/NodeCredentialSlot.test.tsx
+++ b/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/__tests__/NodeCredentialSlot.test.tsx
@@ -64,6 +64,58 @@ describe('NodeCredentialSlot', () => {
});
});
+ it('preserves unsaved connection edits across polls and saves to the refreshed edit target', async () => {
+ const settings = createSettings();
+ const onSaved = vi.fn();
+ const initialNode = {
+ id: 'pve-0',
+ type: 'pve',
+ name: 'homelab',
+ host: 'https://pve1.local:8006',
+ user: '',
+ tokenName: 'root@pam!pulse',
+ hasToken: true,
+ verifySSL: true,
+ status: 'connected',
+ } as unknown as NodeConfigWithStatus;
+ const [editingNode, setEditingNode] = createSignal(initialNode);
+
+ render(() => (
+
+ ));
+
+ const endpoint = screen.getByLabelText(/^Endpoint URL/);
+ const verifySSL = screen.getByRole('checkbox', { name: 'Verify SSL certificate' });
+ fireEvent.input(endpoint, { target: { value: 'https://operator-edit.local:8006' } });
+ fireEvent.click(verifySSL);
+
+ for (let poll = 1; poll <= 2; poll += 1) {
+ setEditingNode({
+ ...initialNode,
+ host: `https://server-poll-${poll}.local:8006`,
+ });
+ await waitFor(() => {
+ expect(screen.getByLabelText(/^Endpoint URL/)).toBe(endpoint);
+ expect(endpoint).toHaveValue('https://operator-edit.local:8006');
+ expect(verifySSL).not.toBeChecked();
+ });
+ }
+
+ expect(settings.saveNode).not.toHaveBeenCalled();
+ fireEvent.submit(endpoint.closest('form')!);
+ await waitFor(() => expect(onSaved).toHaveBeenCalledTimes(1));
+ expect(settings.saveNode).toHaveBeenCalledExactlyOnceWith(
+ expect.objectContaining({ host: 'https://operator-edit.local:8006', verifySSL: false }),
+ editingNode(),
+ );
+ });
+
it('requires candidate import plan approval before guided setup handoff or manual save', () => {
render(() => (