From a9a3f648f060500bc4e70333fa1ad3328aee4733 Mon Sep 17 00:00:00 2001 From: cstns Date: Thu, 6 Aug 2026 15:13:20 +0300 Subject: [PATCH 1/3] feat: add vitest lint rule to enforce kebab-case route names Adds a vitest test that scans all routes.js files in the frontend and validates that every route name matches the kebab-case pattern (lowercase alphanumeric segments separated by hyphens). This prevents regressions after the route rename effort is complete. Part of #8093 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../unit/frontend/routes/route-naming.spec.js | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 test/unit/frontend/routes/route-naming.spec.js diff --git a/test/unit/frontend/routes/route-naming.spec.js b/test/unit/frontend/routes/route-naming.spec.js new file mode 100644 index 0000000000..352351f4d7 --- /dev/null +++ b/test/unit/frontend/routes/route-naming.spec.js @@ -0,0 +1,48 @@ +import { readdirSync, readFileSync, statSync } from 'fs' +import { join } from 'path' +import { describe, expect, test } from 'vitest' + +const KEBAB_CASE_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/ +const ROUTE_NAME_PATTERN = /name:\s*['"]([^'"]+)['"]/g + +const FRONTEND_SRC = join(__dirname, '../../../../frontend/src') + +function findRouteFiles (dir) { + const files = [] + for (const entry of readdirSync(dir)) { + const full = join(dir, entry) + if (entry === 'node_modules') continue + if (statSync(full).isDirectory()) { + files.push(...findRouteFiles(full)) + } else if (entry === 'routes.js') { + files.push(full) + } + } + return files +} + +function extractRouteNames (filePath) { + const content = readFileSync(filePath, 'utf-8') + const names = [] + let match + while ((match = ROUTE_NAME_PATTERN.exec(content)) !== null) { + names.push({ name: match[1], file: filePath.replace(FRONTEND_SRC + '/', '') }) + } + return names +} + +describe('route naming conventions', () => { + const routeFiles = findRouteFiles(FRONTEND_SRC) + const allNames = routeFiles.flatMap(extractRouteNames) + + test('found route files to validate', () => { + expect(routeFiles.length).toBeGreaterThan(0) + expect(allNames.length).toBeGreaterThan(0) + }) + + test('all route names follow kebab-case convention', () => { + const violations = allNames.filter(({ name }) => !KEBAB_CASE_PATTERN.test(name)) + const report = violations.map(({ name, file }) => ` ${name} (${file})`).join('\n') + expect(violations, `Non-kebab-case route names found:\n${report}`).toEqual([]) + }) +}) From ee6b415be38cc029dd4b225de28d41440e96527f Mon Sep 17 00:00:00 2001 From: Noley Holland Date: Thu, 6 Aug 2026 09:01:23 -0700 Subject: [PATCH 2/3] Fix lint for new spec file --- test/unit/frontend/routes/route-naming.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/frontend/routes/route-naming.spec.js b/test/unit/frontend/routes/route-naming.spec.js index 352351f4d7..3cd556bb8a 100644 --- a/test/unit/frontend/routes/route-naming.spec.js +++ b/test/unit/frontend/routes/route-naming.spec.js @@ -1,5 +1,6 @@ -import { readdirSync, readFileSync, statSync } from 'fs' +import { readFileSync, readdirSync, statSync } from 'fs' import { join } from 'path' + import { describe, expect, test } from 'vitest' const KEBAB_CASE_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/ From 016ffde8c7acac9fb4f8bf5a321a97de946ec379 Mon Sep 17 00:00:00 2001 From: cstns Date: Fri, 14 Aug 2026 19:36:57 +0300 Subject: [PATCH 3/3] Update imports to use `node:` prefix for consistency with newer node versions --- test/unit/frontend/routes/route-naming.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/frontend/routes/route-naming.spec.js b/test/unit/frontend/routes/route-naming.spec.js index 3cd556bb8a..d57e33d368 100644 --- a/test/unit/frontend/routes/route-naming.spec.js +++ b/test/unit/frontend/routes/route-naming.spec.js @@ -1,5 +1,5 @@ -import { readFileSync, readdirSync, statSync } from 'fs' -import { join } from 'path' +import { readFileSync, readdirSync, statSync } from 'node:fs' +import { join } from 'node:path' import { describe, expect, test } from 'vitest'