diff --git a/frontend/jest.setup.ts b/frontend/jest.setup.ts index b2ed640a4..f76fb5b23 100644 --- a/frontend/jest.setup.ts +++ b/frontend/jest.setup.ts @@ -191,6 +191,6 @@ global.fetch = jest.fn().mockImplementation((url: string) => { return Promise.resolve({ ok: true, status: 200, - json: () => Promise.resolve({}), + json: () => Promise.resolve({ success: true, data: [] }), }); }); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0a6a8fc7a..50c0c0c00 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -129,7 +129,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -782,7 +781,6 @@ "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1708,7 +1706,6 @@ "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", @@ -5358,7 +5355,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -5536,7 +5534,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -5554,7 +5551,6 @@ "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -5565,7 +5561,6 @@ "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -5695,7 +5690,6 @@ "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -5942,7 +5936,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6649,7 +6642,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -7358,7 +7350,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/domexception": { "version": "4.0.0", @@ -7717,7 +7710,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -9762,7 +9754,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -11247,6 +11238,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -11933,7 +11925,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11966,7 +11957,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -12070,6 +12060,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -12085,6 +12076,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -12204,7 +12196,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -12263,7 +12254,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -12299,14 +12289,14 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -12468,8 +12458,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -13432,7 +13421,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -13573,7 +13561,6 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -13806,7 +13793,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14065,7 +14051,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -14198,7 +14183,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/frontend/src/pages/AITagging/AITagging.tsx b/frontend/src/pages/AITagging/AITagging.tsx index 187bda3df..e4bda4c52 100644 --- a/frontend/src/pages/AITagging/AITagging.tsx +++ b/frontend/src/pages/AITagging/AITagging.tsx @@ -35,7 +35,7 @@ export const AITagging = () => { } else if (imagesError) { dispatch(hideLoader()); } else if (imagesSuccess) { - const images = imagesData?.data as Image[]; + const images = (imagesData?.data ?? []) as Image[]; dispatch(setImages(images)); dispatch(hideLoader()); } diff --git a/frontend/src/pages/Home/Home.tsx b/frontend/src/pages/Home/Home.tsx index 83c9e5c83..ed5a7da31 100644 --- a/frontend/src/pages/Home/Home.tsx +++ b/frontend/src/pages/Home/Home.tsx @@ -40,7 +40,7 @@ export const Home = () => { useEffect(() => { if (!isSearchActive && isSuccess) { - const images = data?.data as Image[]; + const images = (data?.data ?? []) as Image[]; dispatch(setImages(images)); } }, [data, isSuccess, dispatch, isSearchActive]); diff --git a/frontend/src/pages/Home/MyFav.tsx b/frontend/src/pages/Home/MyFav.tsx index 2afcb70e8..592c93ca9 100644 --- a/frontend/src/pages/Home/MyFav.tsx +++ b/frontend/src/pages/Home/MyFav.tsx @@ -42,7 +42,7 @@ export const MyFav = () => { // Handle fetching lifecycle useEffect(() => { if (!isSearchActive && isSuccess) { - const images = data?.data as Image[]; + const images = (data?.data ?? []) as Image[]; dispatch(setImages(images)); } }, [data, isSuccess, dispatch, isSearchActive]); diff --git a/frontend/src/pages/__tests__/PageSanity.test.tsx b/frontend/src/pages/__tests__/PageSanity.test.tsx index 453daa63d..9c68744df 100644 --- a/frontend/src/pages/__tests__/PageSanity.test.tsx +++ b/frontend/src/pages/__tests__/PageSanity.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@/test-utils'; +import { render, screen, act } from '@/test-utils'; import { Home } from '../Home/Home'; import Settings from '../SettingsPage/Settings'; @@ -15,8 +15,11 @@ describe('Page Sanity Tests', () => { }); describe('Settings Page', () => { - test('renders settings page sections', () => { + test('renders settings page sections', async () => { render(); + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 0)); + }); expect(screen.getByText('Folder Management')).toBeInTheDocument(); expect(screen.getByText('User Preferences')).toBeInTheDocument(); @@ -26,6 +29,6 @@ describe('Page Sanity Tests', () => { screen.getByRole('button', { name: /Check for Updates/i }), ).toBeInTheDocument(); expect(screen.getByText('GPU Acceleration')).toBeInTheDocument(); - }); // Settings is expected to render synchronously. + }); }); }); diff --git a/frontend/src/pages/__tests__/SettingsPage.test.tsx b/frontend/src/pages/__tests__/SettingsPage.test.tsx index 345edd456..f173afd19 100644 --- a/frontend/src/pages/__tests__/SettingsPage.test.tsx +++ b/frontend/src/pages/__tests__/SettingsPage.test.tsx @@ -1,19 +1,22 @@ -import { render, screen } from '@/test-utils'; +import { render, screen, act } from '@/test-utils'; import userEvent from '@testing-library/user-event'; import Settings from '../SettingsPage/Settings'; describe('Settings Page', () => { // shared setup for all tests - const setupTest = () => { + const setupTest = async () => { const user = userEvent.setup(); render(); + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 0)); + }); return { user }; }; describe('Interaction Sanity', () => { describe('User Preferences Section', () => { test('YOLO model dropdown opens and shows options', async () => { - const { user } = setupTest(); + const { user } = await setupTest(); const dropdownTrigger = screen.getByRole('button', { name: /nano|small|medium/i, @@ -28,7 +31,7 @@ describe('Settings Page', () => { }); test('GPU Acceleration toggle changes state on click', async () => { - const { user } = setupTest(); + const { user } = await setupTest(); const gpuSwitch = screen.getByRole('switch'); expect(gpuSwitch).toHaveAttribute('aria-checked', 'false'); @@ -49,7 +52,7 @@ describe('Settings Page', () => { test.each(buttonCases)( '$label button does not crash when clicked', async ({ name }) => { - const { user } = setupTest(); + const { user } = await setupTest(); const button = screen.getByRole('button', { name }); @@ -71,7 +74,7 @@ describe('Settings Page', () => { test.each(yoloSelectionCases)( 'selecting $expectedText updates dropdown display', async ({ selectOption, expectedText }) => { - const { user } = setupTest(); + const { user } = await setupTest(); const dropdownTrigger = screen.getByRole('button', { name: /nano/i }); expect(dropdownTrigger).toHaveTextContent('Nano'); @@ -88,7 +91,7 @@ describe('Settings Page', () => { ); test('dropdown can be reopened after selection', async () => { - const { user } = setupTest(); + const { user } = await setupTest(); const dropdownTrigger = screen.getByRole('button', { name: /nano/i }); await user.click(dropdownTrigger); @@ -102,7 +105,7 @@ describe('Settings Page', () => { describe('GPU Acceleration Toggle', () => { test('toggle cycles through ON/OFF states', async () => { - const { user } = setupTest(); + const { user } = await setupTest(); const gpuSwitch = screen.getByRole('switch'); expect(gpuSwitch).toHaveAttribute('aria-checked', 'false'); diff --git a/frontend/src/pages/__tests__/allPages.test.tsx b/frontend/src/pages/__tests__/allPages.test.tsx index 9de439f6d..21b25b3dd 100644 --- a/frontend/src/pages/__tests__/allPages.test.tsx +++ b/frontend/src/pages/__tests__/allPages.test.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import { render, act } from '@testing-library/react'; import { AITagging } from '@/pages/AITagging/AITagging'; import Album from '../Album/Album'; import { Home } from '@/pages/Home/Home'; @@ -23,7 +23,7 @@ const pages = [ describe('Page rendering tests', () => { pages.forEach(({ path, Component }) => { - test(`renders ${path} without crashing`, () => { + test(`renders ${path} without crashing`, async () => { render( @@ -35,6 +35,9 @@ describe('Page rendering tests', () => { , ); + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 0)); + }); }); }); });