diff --git a/frontend/package-lock.json b/frontend/package-lock.json index dc2d63e2be89..2d1e17ac975a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -163,7 +163,7 @@ "@types/urijs": "^1.19.26", "@typescript-eslint/eslint-plugin": "8.69.0", "@typescript-eslint/parser": "8.69.0", - "@vitest/browser-playwright": "^4.1.10", + "@vitest/browser-playwright": "^4.1.11", "@vitest/coverage-v8": "^4.1.11", "@vitest/eslint-plugin": "^1.6.27", "angular-eslint": "^22.2.0", diff --git a/frontend/package.json b/frontend/package.json index 2fb9f22b1363..f31357f34d39 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,7 +33,7 @@ "@types/urijs": "^1.19.26", "@typescript-eslint/eslint-plugin": "8.69.0", "@typescript-eslint/parser": "8.69.0", - "@vitest/browser-playwright": "^4.1.10", + "@vitest/browser-playwright": "^4.1.11", "@vitest/coverage-v8": "^4.1.11", "@vitest/eslint-plugin": "^1.6.27", "angular-eslint": "^22.2.0", diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts index 29f00c40d48e..11fba085b06c 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts @@ -251,11 +251,13 @@ describe('autocompleter', () => { }); it('should recover and keep loading results after a lookup fails', () => { + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined); + const lookupError = new Error('backend rejected the query'); vi.useFakeTimers(); try { getOptionsFnSpy.mockImplementation((searchTerm:string) => { if (searchTerm === 'bad') { - return throwError(() => new Error('backend rejected the query')); + return throwError(() => lookupError); } return of(workPackagesStub).pipe(map((wps) => wps.filter((wp) => searchTerm !== '' && wp.subject.includes(searchTerm)))); @@ -279,6 +281,7 @@ describe('autocompleter', () => { fixture.detectChanges(); expect(getOptionsFnSpy).toHaveBeenCalledWith('bad'); + expect(consoleError).toHaveBeenCalledWith(lookupError); expect(select.itemsList.items.length).toEqual(0); inputElement.value = 'Wor'; @@ -438,6 +441,7 @@ describe('autocompleter', () => { }); it('should load items with debounce', async () => { + silenceDestroyedOutputWarning(); fixture.detectChanges(); // Wait for ngAfterViewInit's internal setTimeout(25ms) and debounce to fire. @@ -478,13 +482,12 @@ describe('autocompleter', () => { }); }); -// NG0953 ("Unexpected emit for destroyed OutputRef") is emitted when ng-select -// emits on an OutputRef during fixture teardown under fake timers. It is a real -// lifecycle smell, not pure noise — silencing it here is a pragmatic stopgap so -// these specs stay readable, not a fix. The proper fix is to stop the -// emit-after-destroy in the component teardown path; until then this is -// scoped to the two affected specs and passes every other warning through so it -// does not hide unrelated regressions. Do not promote this to a global filter. +// NG0953 ("Unexpected emit for destroyed OutputRef") comes from ng-select's +// dropdown panel: `_measureDimensions` retries through a Promise and +// requestAnimationFrame chain with no destroyed guard, so it emits after the +// panel is torn down (https://github.com/ng-select/ng-select/issues/2869). +// Silencing is a stopgap until ng-select guards that chain; it passes every +// other warning through. Do not promote this to a global filter. function silenceDestroyedOutputWarning():void { const originalWarn = console.warn.bind(console); diff --git a/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.spec.ts b/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.spec.ts index 5e361d44607c..826e7f661944 100644 --- a/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.spec.ts +++ b/frontend/src/app/shared/components/fields/edit/services/global-edit-form-changes-tracker/global-edit-form-changes-tracker.service.spec.ts @@ -53,6 +53,7 @@ describe('GlobalEditFormChangesTrackerService', () => { beforeEach(() => { originalOpenProject = window.OpenProject; window.OpenProject = new OpenProject(); + vi.spyOn(window, 'confirm').mockReturnValue(false); TestBed.configureTestingModule({}); service = TestBed.inject(GlobalEditFormChangesTrackerService); }); @@ -61,6 +62,7 @@ describe('GlobalEditFormChangesTrackerService', () => { // eslint-disable-next-line @typescript-eslint/dot-notation service['abortController'].abort(); window.OpenProject = originalOpenProject; + vi.restoreAllMocks(); }); it('should be created', () => { diff --git a/frontend/src/app/shared/directives/sortable-lists/sortable-lists.directive.behavior.spec.ts b/frontend/src/app/shared/directives/sortable-lists/sortable-lists.directive.behavior.spec.ts index cd78031781fc..a409c05b678b 100644 --- a/frontend/src/app/shared/directives/sortable-lists/sortable-lists.directive.behavior.spec.ts +++ b/frontend/src/app/shared/directives/sortable-lists/sortable-lists.directive.behavior.spec.ts @@ -635,6 +635,7 @@ describe('sortable-lists directive group behavior', () => { describe('root teardown', () => { @Component({ + selector: 'op-sortable-lists-teardown-host', imports: [OpSortableListsDirective, OpSortableListsListDirective, OpSortableListsItemDirective], template: `
diff --git a/frontend/src/stimulus/controllers/dynamic/work-packages/activities-tab/polling.controller.spec.ts b/frontend/src/stimulus/controllers/dynamic/work-packages/activities-tab/polling.controller.spec.ts index 5fd953099765..2edfa830738f 100644 --- a/frontend/src/stimulus/controllers/dynamic/work-packages/activities-tab/polling.controller.spec.ts +++ b/frontend/src/stimulus/controllers/dynamic/work-packages/activities-tab/polling.controller.spec.ts @@ -164,13 +164,16 @@ describe('Activities tab polling controller', () => { }); it('keeps polling after an error without an HTTP status', async () => { + const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined); + const networkError = new Error('network hiccup'); await renderPolling(); - request.mockRejectedValue(new Error('network hiccup')); + request.mockRejectedValue(networkError); resolveContext(pluginContext()); await vi.advanceTimersByTimeAsync(20000); expect(request).toHaveBeenCalledTimes(2); + expect(consoleError).toHaveBeenCalledWith('Error updating activities list:', networkError); }); }); }); diff --git a/frontend/src/test-browser-polyfills.ts b/frontend/src/test-browser-polyfills.ts index e236a5d72211..366a4a6f146f 100644 --- a/frontend/src/test-browser-polyfills.ts +++ b/frontend/src/test-browser-polyfills.ts @@ -33,3 +33,12 @@ const globalWithProcess = globalThis as unknown as { process?:{ env:Record }; + +globalWithLitWarnings.litIssuedWarnings ??= new Set([ + 'dev-mode', + 'Lit is in dev mode. Not recommended for production! See https://lit.dev/msg/dev-mode for more information.', +]);