From 23a48e1d498680be769e931f46ddb1fd44f38d1a Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Fri, 13 Feb 2026 07:46:58 -0800 Subject: Implement Tag View and fix tests --- frontend/index.html | 2 +- frontend/package-lock.json | 60 ++++++++++++++++++++++ frontend/package.json | 1 + frontend/playwright-report/index.html | 85 +++++++++++++++++++++++++++++++ frontend/playwright.config.ts | 25 +++++++++ frontend/src/App.test.tsx | 11 ++-- frontend/src/App.tsx | 16 ++---- frontend/src/components/FeedItems.tsx | 16 +++--- frontend/src/components/FeedList.css | 36 ++++++++++++- frontend/src/components/FeedList.test.tsx | 39 +++++++++++--- frontend/src/components/FeedList.tsx | 66 ++++++++++++++++-------- frontend/src/components/TagView.test.tsx | 81 +++++++++++++++++++++++++++++ frontend/src/types.ts | 3 ++ frontend/test-results/.last-run.json | 4 ++ frontend/tests/e2e.spec.ts | 47 +++++++++++++++++ frontend/vite.config.ts | 14 +++++ 16 files changed, 452 insertions(+), 54 deletions(-) create mode 100644 frontend/playwright-report/index.html create mode 100644 frontend/playwright.config.ts create mode 100644 frontend/src/components/TagView.test.tsx create mode 100644 frontend/test-results/.last-run.json create mode 100644 frontend/tests/e2e.spec.ts (limited to 'frontend') diff --git a/frontend/index.html b/frontend/index.html index 072a57e..40f2523 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - frontend + Neko Reader
diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 916b62c..9414557 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,6 +14,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@playwright/test": "^1.58.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/node": "^24.10.1", @@ -1173,6 +1174,21 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "dev": true, + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", @@ -3471,6 +3487,50 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "dev": true, + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", diff --git a/frontend/package.json b/frontend/package.json index fbd5764..ad3ffef 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@playwright/test": "^1.58.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/node": "^24.10.1", diff --git a/frontend/playwright-report/index.html b/frontend/playwright-report/index.html new file mode 100644 index 0000000..3b60b51 --- /dev/null +++ b/frontend/playwright-report/index.html @@ -0,0 +1,85 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + \ No newline at end of file diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts new file mode 100644 index 0000000..0f11ce3 --- /dev/null +++ b/frontend/playwright.config.ts @@ -0,0 +1,25 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: 'html', + use: { + baseURL: 'http://localhost:5173', // Vite dev server + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + webServer: { + command: 'npm run dev', + url: 'http://localhost:5173', + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 5614d7d..d0c31fd 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -20,9 +20,12 @@ describe('App', () => { }); it('renders dashboard when authenticated', async () => { - (global.fetch as any) - .mockResolvedValueOnce({ ok: true }) // /api/auth - .mockResolvedValueOnce({ ok: true, json: async () => [] }); // /api/feed/ + (global.fetch as any).mockImplementation((url: string) => { + if (url.includes('/api/auth')) return Promise.resolve({ ok: true }); + if (url.includes('/api/feed/')) return Promise.resolve({ ok: true, json: async () => [] }); + if (url.includes('/api/tag')) return Promise.resolve({ ok: true, json: async () => [] }); + return Promise.resolve({ ok: true }); // Fallback + }); window.history.pushState({}, 'Test page', '/v2/'); render(); @@ -47,7 +50,7 @@ describe('App', () => { await waitFor(() => { expect(global.fetch).toHaveBeenCalledWith('/api/logout', expect.objectContaining({ method: 'POST' })); - expect(window.location.href).toBe('/login/'); + expect(window.location.href).toBe('/v2/login'); }); }); }); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8c7be19..09148d6 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { BrowserRouter, Routes, Route, Navigate, useLocation } from 'react-router-dom'; +import { BrowserRouter, Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom'; import Login from './components/Login'; import './App.css'; @@ -36,24 +36,17 @@ import FeedItems from './components/FeedItems'; import Settings from './components/Settings'; function Dashboard() { + const navigate = useNavigate(); return (

Neko Reader