Add 27 PHP test files + 6 TS test files + Vitest setup, surfacing
and fixing 4 production bugs in the process.
Test counts (all green):
- PHP: 545 tests / 2341 assertions / 25.25s
- 12 lint rule tests (L1, L2, L2b, L3, L4, L5, L6, L7, L8, L9, L10, L11)
- LintRunner dispatch + LintReport bucket aggregation
- DuplicateChecker bulk-query + N+1 protection
- RouteForgeService full commit pipeline (happy path, lint rollback,
attach-existing branch, fare multiplier, withoutEvents suppression)
- 6 endpoint feature tests (PreviewAirports, Subfleets, AirlineStats,
CheckDuplicates, Lint, Commit)
- TS: 46 tests / 6 files / 280ms via Vitest 4.1.7 + happy-dom
- geo, timezone, flightNumber, timeStrategy, generator, lint
In-scope production fixes discovered by writing the tests:
1. DuplicateChecker eager-load was 'airline:id,code' but 'code' is an
accessor over iata/icao columns. Loaded airline came back with
NEITHER backing column populated and Flight::ident dropped the
airline prefix in the duplicates response. Changed to
'airline:id,iata,icao'.
2. LintRunner constructor takes 'array $rules' with no container-
resolvable shape. Both /lint and /commit endpoints would have
failed at runtime with 'Unresolvable dependency'. Registered as
singleton in AppServiceProvider::register() binding to
LintRunner::defaults().
3. BaseRouteForgeBatchRequest's 'subfleet_ids' rule was
['required', 'array', 'min:0']. 'required' rejects empty arrays
in Laravel, contradicting 'min:0'. Relaxed to ['present', 'array'];
L3 lint catches the empty case as a warning downstream.
4. RouteForgeService::commit() called auth()->user() directly,
violating tests/Arch/GlobalTest's http-helpers rule (auth/session/
request only allowed in App\Http / App\Filament / App\Livewire /
App\Providers\Filament). Added $causerId field to CommitInput;
controller stamps it from auth()->id(); service resolves User by id.
Infrastructure:
- tests/Support/RouteForgeTestHelpers.php — shared LintContext + row +
airport + batchPayload builders. PSR-4 autoloaded via Tests\ namespace;
static class avoids global-function name collisions across the 15+
RouteForge test files.
- vitest.config.ts at project root, scoped to
resources/js/admin/routeforge/**/*.test.ts. happy-dom provides the
minimal window + DOM globals lib/i18n.ts depends on.
- package.json gains 'test': 'vitest run' script.
- Added vitest, happy-dom, @types/node as devDependencies.
Task 4.6.3 stays N/A — FlightNumberAssigner was removed per the
Section 4 banner; no PHP equivalent to test. Section 6.4.7's planned
PHP↔TS shared fixtures (tests/fixtures/routeforge/generator/) also
N/A for the same reason; TS generator tests stand alone.
Quality gates green:
- tsc --noEmit: clean
- oxlint (40 files / 93 rules): 0 warnings, 0 errors
- oxfmt --check: all formatted
- npm run build: routeforge bundle stays at 26.69 KB gzip
(Decision 16 budget 60 KB → 33.31 KB headroom)
- vendor/bin/pint --dirty: passed
- vendor/bin/phpstan analyse: OK no errors
- vendor/bin/rector --dry-run: clean for RouteForge files
22 lines
723 B
TypeScript
22 lines
723 B
TypeScript
/**
|
|
* Vitest config — RouteForge TS test runner.
|
|
*
|
|
* Scoped to `resources/js/admin/routeforge/**` so the legacy admin JS tree
|
|
* stays outside the type/test sweep. happy-dom provides the minimal `window`
|
|
* + DOM globals that `lib/i18n.ts` (and any future client-only modules)
|
|
* depend on without bringing in the full jsdom weight.
|
|
*
|
|
* No transform / plugin config — Vitest reuses vite.config.js when no
|
|
* vitest.config is present, but here we want a narrower include scope.
|
|
*/
|
|
import { defineConfig } from "vitest/config";
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
include: ["resources/js/admin/routeforge/**/*.test.ts"],
|
|
environment: "happy-dom",
|
|
globals: false,
|
|
reporters: "default",
|
|
},
|
|
});
|