Merge feature/ticket79-flaky-permissions-test into develop
Ticket #79 : fix du flake PermissionsPanel > saves project defaults (PermissionEditor draft resync clobbering in-flight edits). QA vert sur le périmètre du ticket (2/2 stable + suite complète non-shuffled) ; le rouge observé en shuffle vient d'un autre test préexistant sans rapport (setCellAgent.test.tsx, tracé séparément en #85). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import { Button, Panel, Spinner, cn } from "@/shared";
|
import { Button, Panel, Spinner, cn } from "@/shared";
|
||||||
import type { PermissionSet, PermissionPosture } from "@/domain";
|
import type { PermissionSet, PermissionPosture } from "@/domain";
|
||||||
@ -232,9 +232,26 @@ function PermissionEditor({
|
|||||||
onClear,
|
onClear,
|
||||||
}: PermissionEditorProps) {
|
}: PermissionEditorProps) {
|
||||||
const [local, setLocal] = useState<PolicyDraft>(draft);
|
const [local, setLocal] = useState<PolicyDraft>(draft);
|
||||||
|
// `draft` is a NEW object every time the underlying document changes for ANY
|
||||||
|
// reason (initial load landing after mount, a Refresh, another tab's save…),
|
||||||
|
// even when its content matches what is already in `local`. Naively
|
||||||
|
// re-syncing on every `draft` change is a genuine race: if it fires after the
|
||||||
|
// user has started editing (in production: a slow initial fetch landing
|
||||||
|
// right as the user types; in tests: a passive effect flushing later than a
|
||||||
|
// synchronous `fireEvent` sequence — ticket #79 flake), it silently discards
|
||||||
|
// the user's in-progress edit. `prevDraftRef` tracks the last draft this
|
||||||
|
// effect adopted `local` from; we only re-sync when `local` still matches it
|
||||||
|
// (nothing has been edited since), so a legitimate incoming draft (first
|
||||||
|
// load, or the echo of the user's own just-saved edit) is adopted, but an
|
||||||
|
// edit in flight never gets clobbered.
|
||||||
|
const prevDraftRef = useRef(draft);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocal(draft);
|
setLocal((current) =>
|
||||||
|
JSON.stringify(current) === JSON.stringify(prevDraftRef.current)
|
||||||
|
? draft
|
||||||
|
: current,
|
||||||
|
);
|
||||||
|
prevDraftRef.current = draft;
|
||||||
}, [draft]);
|
}, [draft]);
|
||||||
|
|
||||||
const changed = useMemo(
|
const changed = useMemo(
|
||||||
|
|||||||
Reference in New Issue
Block a user