From 0587f71273977607cf13a90bdcd6292a6a731721 Mon Sep 17 00:00:00 2001 From: dc Date: Mon, 2 Nov 2026 10:26:35 +0000 Subject: [PATCH] test(client): drive the autocomplete reposition on the fake clock The specs gated their assertions on a real setTimeout(..., 10) plus done, which is wall-clock timing: a loaded runner can assert before the component's own timer fires, and the negative case would then pass vacuously. fakeAsync + tick order the assertion on the scheduler instead. Also drops the stale half of the viewbox resize comment - the shared tail no longer notifies listeners, it just persists and re-measures. --- .../autocomplete.component.spec.ts | 26 +++++++++---------- .../shared/viewboxes/viewboxes.component.ts | 5 ++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/client/src/app/shared/autocomplete/autocomplete.component.spec.ts b/client/src/app/shared/autocomplete/autocomplete.component.spec.ts index 6d9ce7e..1e26ac8 100644 --- a/client/src/app/shared/autocomplete/autocomplete.component.spec.ts +++ b/client/src/app/shared/autocomplete/autocomplete.component.spec.ts @@ -1,4 +1,5 @@ import { EventEmitter } from '@angular/core' +import { fakeAsync, tick } from '@angular/core/testing' import { AutocompleteComponent } from './autocomplete.component' /** @@ -8,27 +9,28 @@ import { AutocompleteComponent } from './autocomplete.component' * this subscription is the only thing keeping an open dropdown aligned. * * The component only needs ChangeDetectorRef and EventService, so it can be - * constructed directly - same approach as the sidebar spec. + * constructed directly - same approach as the sidebar spec. The work it + * schedules is on a timer, so the specs drive the fake clock rather than + * sleeping: a wall-clock wait would let a slow runner assert before the + * component ever ran. */ describe('AutocompleteComponent', () => { const buildComponent = (eventService: any) => new AutocompleteComponent({ detectChanges: () => {} } as any, eventService) - it('repositions the dropdown when the sidebar toggles', (done) => { + it('repositions the dropdown when the sidebar toggles', fakeAsync(() => { const eventService: any = { onSidebarToggle: new EventEmitter() } const component = buildComponent(eventService) const reposition = spyOn(component, 'calculateInputPosition') component.ngOnInit() eventService.onSidebarToggle.emit({ open: false }) + tick() - setTimeout(() => { - expect(reposition).toHaveBeenCalled() - done() - }, 10) - }) + expect(reposition).toHaveBeenCalled() + })) - it('stops listening once destroyed', (done) => { + it('stops listening once destroyed', fakeAsync(() => { const eventService: any = { onSidebarToggle: new EventEmitter() } const component = buildComponent(eventService) const reposition = spyOn(component, 'calculateInputPosition') @@ -36,10 +38,8 @@ describe('AutocompleteComponent', () => { component.ngOnDestroy() eventService.onSidebarToggle.emit({ open: false }) + tick() - setTimeout(() => { - expect(reposition).not.toHaveBeenCalled() - done() - }, 10) - }) + expect(reposition).not.toHaveBeenCalled() + })) }) diff --git a/client/src/app/shared/viewboxes/viewboxes.component.ts b/client/src/app/shared/viewboxes/viewboxes.component.ts index 9bd86b8..ab55852 100644 --- a/client/src/app/shared/viewboxes/viewboxes.component.ts +++ b/client/src/app/shared/viewboxes/viewboxes.component.ts @@ -694,9 +694,8 @@ export class ViewboxesComponent implements OnInit, AfterViewInit, OnDestroy { /** * Shared debounced tail for every resize path (corner, single-edge, - * left-edge) - persists the new size/position, notifies listeners the - * viewport changed shape, and refreshes every viewbox's Handsontable - * instance to match. + * left-edge) - persists the new size/position, then refreshes every + * viewbox's Handsontable instance to match. */ private scheduleAfterResizeRefresh(): void { this.helperService.debounceCall(1000, () => {