diff --git a/.changeset/6959-gantt-lazy-read-flake.md b/.changeset/6959-gantt-lazy-read-flake.md new file mode 100644 index 0000000000..dda172e9c3 --- /dev/null +++ b/.changeset/6959-gantt-lazy-read-flake.md @@ -0,0 +1,28 @@ +--- +--- + +Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an +asynchronous query instead of racing it. No published behaviour changes; no runtime code +was touched. + +The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK +CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with +`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart: +`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find +RESOLVES, so on return from `mount()` the component can still be painting its +`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen +minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests +that do not touch `plugin-gantt` at all, one of which passed and failed the queue on +byte-identical content. + +The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above +this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does +not carry (`loading` flips false after the FIRST find resolves, before `objectSchema` +lands). The two gates measure different facts, so the block now waits for both rather than +trading one for the other. + +Measured rather than assumed: with the data source's resolution delayed 500ms — the timing +FACT mutated, never the assertion — the old spelling throws +`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly +`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves +through that fallback and reads `data-count=2`. diff --git a/packages/plugin-gantt/src/ObjectGantt.referenceArms-6837.test.tsx b/packages/plugin-gantt/src/ObjectGantt.referenceArms-6837.test.tsx index f80735933d..ae66fe903e 100644 --- a/packages/plugin-gantt/src/ObjectGantt.referenceArms-6837.test.tsx +++ b/packages/plugin-gantt/src/ObjectGantt.referenceArms-6837.test.tsx @@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui it('and degrades to the distinct loaded values rather than rendering nothing', async () => { // Guards the refusal above against the degenerate pass: a gantt that // rendered no quick filter at all would also never fetch `projects`. + // + // ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a + // MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one + // promise resolution EARLIER than the `setLoading(false)` in `reload()`'s + // `finally`. So on return from `mount()` `ObjectGantt` may still be + // painting its `loading` branch (`Loading Gantt chart...`), and a + // SYNCHRONOUS `getByTestId` here races it. It lost that race twice in + // fifteen minutes in the merge queue — a heavier runner than PR CI — + // ejecting two PRs that do not touch `plugin-gantt` at all. + // + // `mount()`'s find-call gate STAYS: the refusal probe needs proof of the + // SCHEMA-dependent commit, and `gantt-view` alone does not carry it — + // `loading` flips false after the FIRST `find()` resolves, which happens + // before `objectSchema` lands. The two gates measure different facts, so + // this block waits for both rather than trading one for the other. const { ds, view } = await mount(FIELD_DEFS.legacy_camel); - expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2'); - fireEvent.click(view.getByTestId('quick-filter-trigger-project')); + const chart = await view.findByTestId('gantt-view'); + expect(chart.getAttribute('data-count')).toBe('2'); + fireEvent.click(await view.findByTestId('quick-filter-trigger-project')); const panel = view.getByTestId('quick-filter-panel-project'); expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy(); expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();