Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/devtools/src/__tests__/ngrx-collector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,21 @@ describe('ngrx collector', () => {
expect(entry.durationMs).toBeUndefined();
});

it('keeps a method change out of a patchState entry from earlier in the same tick', async () => {
const { store, collector } = setup();
collector.collect();
patchState(store, { saved: ['x'] });
store.setQuery('rome');
await Promise.resolve();
expect(collector.logSince(0).map((e) => [e.type, e.diff])).toEqual([
['patchState', [{ path: 'saved[0]', op: 'add', after: 'x' }]],
['setQuery', [{ path: 'query', op: 'change', before: '', after: 'rome' }]],
]);
expect(collector.run({ type: 'restore', seq: 1 }).ok).toBe(true);
expect((store as any)[STATE_SOURCE].query()).toBe('');
expect((store as any)[STATE_SOURCE].saved()).toEqual(['x']);
});

it('measures a method call duration and updates rolling stats per method', () => {
const { store, collector } = setup();
collector.collect();
Expand Down
14 changes: 14 additions & 0 deletions packages/devtools/src/ngrx-collector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,18 @@ export function createNgrxCollector<H extends object = Element>(
});
};

/**
* Logs writes still waiting for the microtask above right away, so a method
* call or restore that follows in the same tick gets its own entry and its
* change does not also land in that `patchState` entry.
*/
const flushPending = (t: Tracked) => {
const before = t.pendingBefore;
if (!before) return;
t.pendingBefore = null;
finish(t, 'patchState', undefined, before);
};

/**
* Appends one `signal-store` entry per patchState call, picked up through
* `watchState()`. Dedicated to that path (not reused through `finish()`) so
Expand Down Expand Up @@ -463,6 +475,7 @@ export function createNgrxCollector<H extends object = Element>(
apply(target, thisArg, args) {
info.calls++;
const outer = t.depth === 0;
if (outer) flushPending(t);
const before = outer ? snapshot(t) : null;
const frame = { name, args, startedAt: now(), fired: 0 };
t.methodStack.push(frame);
Expand Down Expand Up @@ -1249,6 +1262,7 @@ export function createNgrxCollector<H extends object = Element>(
const t = entry.tracked;
if (!tracked.has(t.instance)) return { error: 'This store no longer exists on the page.' };
if (!t.writable) return { error: 'This store state is read-only.' };
flushPending(t);
const before = snapshot(t);
const patch: Record<PropertyKey, unknown> = {};
for (const key of t.stateKeys) {
Expand Down
Loading