mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 08:01:10 +00:00
[AZ-485] Add Public API barrels + STC-ARCH-01 (F4 close)
Closes architecture baseline finding F4. Every component now exposes its Public API through `src/<component>/index.ts`; cross-component imports go through the barrel. `scripts/check-arch-imports.mjs` plus `STC-ARCH-01` in the static profile enforce the rule; tests in `tests/architecture_imports.test.ts` cover AC-4/AC-5 + 2 exemption cases. One F3-pending exemption (`classColors`) is documented in 5 places (barrel, consumer, script, doc, test) to avoid a circular import. Phase B cycle 1 batch 1 of 2 (epic AZ-447). Batch 2 is AZ-486 (endpoint builders) — blocked on this commit landing. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,8 +4,8 @@ import { server } from './msw/server'
|
||||
import { jsonResponse, paginate } from './msw/helpers'
|
||||
import { renderWithProviders, screen, waitFor, userEvent } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { FlightProvider } from '../src/components/FlightContext'
|
||||
import AnnotationsPage from '../src/features/annotations/AnnotationsPage'
|
||||
import { FlightProvider } from '../src/components'
|
||||
import { AnnotationsPage } from '../src/features/annotations'
|
||||
import { AnnotationSource, AnnotationStatus, MediaType, MediaStatus, Affiliation, CombatReadiness } from '../src/types'
|
||||
import type { Media, AnnotationListItem, Detection } from '../src/types'
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { writeFileSync, rmSync, mkdirSync, existsSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
|
||||
// AZ-485 / F4 — verifies the STC-ARCH-01 static gate (scripts/check-arch-imports.mjs):
|
||||
// - AC-5 : passes on the migrated codebase as-is
|
||||
// - AC-4 : fails when a synthetic cross-component deep import is added
|
||||
// - AC-4 : ignores the F3-pending exemption (features/annotations/classColors)
|
||||
// - AC-4 : ignores deep imports written inside // line comments
|
||||
//
|
||||
// Exercises the actual gate via subprocess so a regression in the script
|
||||
// (regex drift, exemption logic, exit code) trips the test even if the bash
|
||||
// glue in run-tests.sh keeps reporting PASS.
|
||||
//
|
||||
// All offending substrings are built via concatenation at runtime so the
|
||||
// scanner itself does not flag this test file when it walks `tests/**`.
|
||||
|
||||
const REPO_ROOT = resolve(__dirname, '..')
|
||||
const SCRIPT = join(REPO_ROOT, 'scripts', 'check-arch-imports.mjs')
|
||||
const FIXTURE_DIR = join(REPO_ROOT, 'tests', '_arch_fixtures')
|
||||
|
||||
const FROM = 'fr' + 'om'
|
||||
const UP2 = '..' + '/..'
|
||||
const DEEP_API = `${UP2}/src/api/cl` + 'ient'
|
||||
const DEEP_CLASSCOLORS = `${UP2}/src/features/annotations/classCo` + 'lors'
|
||||
|
||||
function runCheck(): { status: number; stderr: string } {
|
||||
const res = spawnSync('node', [SCRIPT, `--root=${REPO_ROOT}`], {
|
||||
cwd: REPO_ROOT,
|
||||
encoding: 'utf8',
|
||||
})
|
||||
return { status: res.status ?? -1, stderr: res.stderr ?? '' }
|
||||
}
|
||||
|
||||
function writeFixture(filename: string, content: string): string {
|
||||
mkdirSync(FIXTURE_DIR, { recursive: true })
|
||||
const path = join(FIXTURE_DIR, filename)
|
||||
writeFileSync(path, content, 'utf8')
|
||||
return path
|
||||
}
|
||||
|
||||
describe('AZ-485 STC-ARCH-01 — no cross-component deep imports', () => {
|
||||
afterEach(() => {
|
||||
if (existsSync(FIXTURE_DIR)) rmSync(FIXTURE_DIR, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it('AC-5: passes on the migrated codebase (no fixtures)', () => {
|
||||
// Assert
|
||||
const { status, stderr } = runCheck()
|
||||
expect(stderr, stderr).toBe('')
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
|
||||
it('AC-4: FAILS when a deep import into another component is introduced', () => {
|
||||
// Arrange
|
||||
const body = `import { api } ${FROM} '${DEEP_API}'\nexport const _force = api\n`
|
||||
writeFixture('synthetic_deep_import.ts', body)
|
||||
// Act
|
||||
const { status, stderr } = runCheck()
|
||||
// Assert
|
||||
expect(status).not.toBe(0)
|
||||
expect(stderr).toMatch(/STC-ARCH-01/)
|
||||
expect(stderr).toMatch(/synthetic_deep_import\.ts/)
|
||||
expect(stderr).toMatch(/src\/api\/client/)
|
||||
})
|
||||
|
||||
it('AC-4: still PASSES when only the classColors F3-pending exemption is used', () => {
|
||||
// Arrange
|
||||
const body =
|
||||
`import { FALLBACK_CLASS_NAMES } ${FROM} '${DEEP_CLASSCOLORS}'\n` +
|
||||
`export const _force = FALLBACK_CLASS_NAMES\n`
|
||||
writeFixture('classcolors_exemption.ts', body)
|
||||
// Act
|
||||
const { status, stderr } = runCheck()
|
||||
// Assert
|
||||
expect(stderr, stderr).toBe('')
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
|
||||
it('AC-4: deep imports inside line comments do not trip the gate', () => {
|
||||
// Arrange
|
||||
const body = `// import { api } ${FROM} '${DEEP_API}'\nexport const _x = 1\n`
|
||||
writeFixture('commented_out_deep_import.ts', body)
|
||||
// Act
|
||||
const { status } = runCheck()
|
||||
// Assert
|
||||
expect(status).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -4,8 +4,7 @@ import { server } from './msw/server'
|
||||
import { jsonResponse, paginate } from './msw/helpers'
|
||||
import { renderWithProviders, screen, waitFor } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { FlightProvider } from '../src/components/FlightContext'
|
||||
import Header from '../src/components/Header'
|
||||
import { FlightProvider, Header } from '../src/components'
|
||||
|
||||
// AZ-469 — Browser support + responsive variants.
|
||||
//
|
||||
|
||||
@@ -4,8 +4,8 @@ import { server } from './msw/server'
|
||||
import { jsonResponse, paginate } from './msw/helpers'
|
||||
import { renderWithProviders, screen, fireEvent, waitFor } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { FlightProvider } from '../src/components/FlightContext'
|
||||
import DatasetPage from '../src/features/dataset/DatasetPage'
|
||||
import { FlightProvider } from '../src/components'
|
||||
import { DatasetPage } from '../src/features/dataset'
|
||||
import { AnnotationStatus, AnnotationSource } from '../src/types'
|
||||
import type { DatasetItem } from '../src/types'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
||||
import { http } from 'msw'
|
||||
import { server } from './msw/server'
|
||||
import { renderWithProviders, fireEvent, waitFor } from './helpers/render'
|
||||
import CanvasEditor from '../src/features/annotations/CanvasEditor'
|
||||
import { CanvasEditor } from '../src/features/annotations'
|
||||
import {
|
||||
Affiliation,
|
||||
CombatReadiness,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { server } from './msw/server'
|
||||
import { jsonResponse, noContent } from './msw/helpers'
|
||||
import { renderWithProviders, screen, waitFor, userEvent } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import AdminPage from '../src/features/admin/AdminPage'
|
||||
import { AdminPage } from '../src/features/admin'
|
||||
|
||||
// AZ-466 — Destructive UX policy (cross-component half)
|
||||
//
|
||||
|
||||
@@ -5,7 +5,10 @@ import { jsonResponse, errorResponse } from './msw/helpers'
|
||||
import { renderWithProviders, screen, fireEvent, waitFor, userEvent, act } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { seedClasses } from './fixtures/seed_classes'
|
||||
import DetectionClasses from '../src/components/DetectionClasses'
|
||||
import { DetectionClasses } from '../src/components'
|
||||
// F3-pending exemption: classColors symbols live under 06_annotations until
|
||||
// F3 moves the file. The 06_annotations barrel does not re-export them to
|
||||
// avoid a circular import (see src/features/annotations/index.ts).
|
||||
import { FALLBACK_CLASS_NAMES } from '../src/features/annotations/classColors'
|
||||
import type { DetectionClass } from '../src/types'
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import { server } from './msw/server'
|
||||
import { jsonResponse, paginate, sse } from './msw/helpers'
|
||||
import { renderWithProviders, screen, waitFor, userEvent } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { FlightProvider } from '../src/components/FlightContext'
|
||||
import AnnotationsPage from '../src/features/annotations/AnnotationsPage'
|
||||
import { FlightProvider } from '../src/components'
|
||||
import { AnnotationsPage } from '../src/features/annotations'
|
||||
import { MediaType, MediaStatus } from '../src/types'
|
||||
import type { Media } from '../src/types'
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import { server } from './msw/server'
|
||||
import { jsonResponse, paginate } from './msw/helpers'
|
||||
import { renderWithProviders, screen, waitFor, userEvent } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { FlightProvider } from '../src/components/FlightContext'
|
||||
import Header from '../src/components/Header'
|
||||
import { FlightProvider, Header } from '../src/components'
|
||||
import { seedFlights } from './fixtures/seed_flights'
|
||||
import { seedUserSettings } from './fixtures/seed_user_settings'
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { server } from './msw/server'
|
||||
import { jsonResponse } from './msw/helpers'
|
||||
import { renderWithProviders, screen, waitFor, userEvent } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import SettingsPage from '../src/features/settings/SettingsPage'
|
||||
import { SettingsPage } from '../src/features/settings'
|
||||
|
||||
// AZ-475 — Numeric form input — empty / non-numeric rejection
|
||||
//
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { setToken } from '../../src/api/client'
|
||||
import { setToken } from '../../src/api'
|
||||
|
||||
// Stand-in for the full login flow. Tests that need an authenticated request
|
||||
// call `seedBearer(token)` before the request fires; `clearBearer()` is
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { vi, type MockedFunction } from 'vitest'
|
||||
import { setNavigateToLogin } from '../../src/api/client'
|
||||
import { setNavigateToLogin } from '../../src/api'
|
||||
|
||||
// Replaces the production `navigateToLoginImpl` accessor (autodev Step 4 / C06)
|
||||
// with a Vitest spy. Tests assert "redirect was invoked" via the returned
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { ReactElement, ReactNode } from 'react'
|
||||
import { render, type RenderOptions, type RenderResult } from '@testing-library/react'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import { I18nextProvider } from 'react-i18next'
|
||||
import i18n from '../../src/i18n/i18n'
|
||||
import { AuthProvider } from '../../src/auth/AuthContext'
|
||||
import i18n from '../../src/i18n'
|
||||
import { AuthProvider } from '../../src/auth'
|
||||
|
||||
export interface RenderWithProvidersOptions extends RenderOptions {
|
||||
/** Initial route(s) for the in-memory router. Defaults to ['/']. */
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import i18n from '../src/i18n/i18n'
|
||||
import i18n from '../src/i18n'
|
||||
|
||||
// AZ-465 — i18n detector + persistence (fast counterparts).
|
||||
//
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { server } from './msw/server'
|
||||
import { api } from '../src/api/client'
|
||||
import { api } from '../src/api'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { loadEnumSnapshot } from './fixtures/enum_spec_snapshot'
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ import {
|
||||
userEvent,
|
||||
} from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { createSSE } from '../src/api/sse'
|
||||
import { createSSE } from '../src/api'
|
||||
import App from '../src/App'
|
||||
import AnnotationsPage from '../src/features/annotations/AnnotationsPage'
|
||||
import { FlightProvider, useFlight } from '../src/components/FlightContext'
|
||||
import { AnnotationsPage } from '../src/features/annotations'
|
||||
import { FlightProvider, useFlight } from '../src/components'
|
||||
import { seedFlights } from './fixtures/seed_flights'
|
||||
import {
|
||||
AnnotationSource,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
||||
import { renderWithProviders, waitFor } from './helpers/render'
|
||||
import CanvasEditor from '../src/features/annotations/CanvasEditor'
|
||||
import { CanvasEditor } from '../src/features/annotations'
|
||||
import {
|
||||
AnnotationSource,
|
||||
AnnotationStatus,
|
||||
|
||||
@@ -4,8 +4,8 @@ import { server } from './msw/server'
|
||||
import { jsonResponse, paginate } from './msw/helpers'
|
||||
import { renderWithProviders, screen, fireEvent, waitFor, act } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { FlightProvider } from '../src/components/FlightContext'
|
||||
import AnnotationsPage from '../src/features/annotations/AnnotationsPage'
|
||||
import { FlightProvider } from '../src/components'
|
||||
import { AnnotationsPage } from '../src/features/annotations'
|
||||
|
||||
// AZ-470 — Panel-width debounced PUT + rehydration.
|
||||
//
|
||||
|
||||
@@ -11,9 +11,8 @@ import {
|
||||
userEvent,
|
||||
} from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import DetectionClasses from '../src/components/DetectionClasses'
|
||||
import AnnotationsPage from '../src/features/annotations/AnnotationsPage'
|
||||
import { FlightProvider, useFlight } from '../src/components/FlightContext'
|
||||
import { DetectionClasses, FlightProvider, useFlight } from '../src/components'
|
||||
import { AnnotationsPage } from '../src/features/annotations'
|
||||
import { seedFlights } from './fixtures/seed_flights'
|
||||
import { seedClasses } from './fixtures/seed_classes'
|
||||
import {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { server } from './msw/server'
|
||||
import { jsonResponse } from './msw/helpers'
|
||||
import { renderWithProviders, screen, waitFor, userEvent, within } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import SettingsPage from '../src/features/settings/SettingsPage'
|
||||
import { SettingsPage } from '../src/features/settings'
|
||||
import { seedAircraft } from './fixtures/seed_aircraft'
|
||||
import type { SystemSettings, DirectorySettings } from '../src/types'
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import '@testing-library/jest-dom/vitest'
|
||||
import { afterAll, afterEach, beforeAll } from 'vitest'
|
||||
import { cleanup } from '@testing-library/react'
|
||||
import { server } from './msw/server'
|
||||
import { setToken, setNavigateToLogin } from '../src/api/client'
|
||||
import { setToken, setNavigateToLogin } from '../src/api'
|
||||
|
||||
// JSDOM polyfills for browser APIs production code touches at mount time.
|
||||
// These are no-op stubs — tests that exercise the actual behavior install
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { render, act, cleanup } from '@testing-library/react'
|
||||
import { createSSE } from '../src/api/sse'
|
||||
import { setToken } from '../src/api/client'
|
||||
import { createSSE, setToken } from '../src/api'
|
||||
import { createFakeEventSource, type FakeEventSource } from './helpers/sse-mock'
|
||||
|
||||
// AZ-458 — SSE lifecycle + bearer-rotation reconnect.
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
waitFor,
|
||||
} from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { FlightProvider } from '../src/components/FlightContext'
|
||||
import DatasetPage from '../src/features/dataset/DatasetPage'
|
||||
import { FlightProvider } from '../src/components'
|
||||
import { DatasetPage } from '../src/features/dataset'
|
||||
import {
|
||||
AnnotationSource,
|
||||
AnnotationStatus,
|
||||
|
||||
@@ -5,8 +5,8 @@ import { server } from './msw/server'
|
||||
import { jsonResponse, paginate } from './msw/helpers'
|
||||
import { renderWithProviders, screen, fireEvent, waitFor, userEvent } from './helpers/render'
|
||||
import { seedBearer, clearBearer } from './helpers/auth'
|
||||
import { FlightProvider, useFlight } from '../src/components/FlightContext'
|
||||
import AnnotationsPage from '../src/features/annotations/AnnotationsPage'
|
||||
import { FlightProvider, useFlight } from '../src/components'
|
||||
import { AnnotationsPage } from '../src/features/annotations'
|
||||
import { seedFlights } from './fixtures/seed_flights'
|
||||
|
||||
// AZ-476 — Upload >500 MB → 413 → user-visible error (no alert).
|
||||
|
||||
Reference in New Issue
Block a user