Skip to content

feat(working-capital): loan servicing actions, plus a Vitest runner and a Karma ratchet - #408

Merged
Aman-Mittal merged 2 commits into
apache:mainfrom
Aman-Mittal:chore/vitest-migration-groundwork
Aug 19, 2026
Merged

feat(working-capital): loan servicing actions, plus a Vitest runner and a Karma ratchet#408
Aman-Mittal merged 2 commits into
apache:mainfrom
Aman-Mittal:chore/vitest-migration-groundwork

Conversation

@Aman-Mittal

@Aman-Mittal Aman-Mittal commented Aug 19, 2026

Copy link
Copy Markdown
Member

Supersedes and closes #402, which contained the first commit only. Rebased and combined so there is one branch to review and merge.

Closes #396, closes #397, closes #398, closes #399, closes #400, closes #401 — the working-capital epic #395.
Closes #403 — the test-runner epic. Its sub-issues #404, #405, #406, #407, #409, #410 and #411 are the follow-up batches this enables, and stay open.

Commit 1 — Working Capital loan servicing

A Working Capital loan was read-only in this UI. Four capabilities the backend already exposes — delinquency actions, breach actions, near-breach actions, and per-loan loan-originator attach/detach — had no entry point, so recording a breach action meant leaving the back office and calling Fineract directly.

Commit 2 — Vitest runner and Karma ratchet

Karma is deprecated and @angular/build:karma goes with it. vitest@4 was already in devDependencies with nothing wired to it — two runners carried, only the deprecated one used.

219 specs and 886 jasmine.* sites cannot move in one commit, so this makes the suite movable in batches and stops it growing in the meantime:

  • The runners select by filename. *.spec.ts is Karma (legacy, shrinking), *.test.ts is Vitest (everything new). A migration is a rename plus a dialect swap.
  • scripts/check-test-runner.mjs records the remaining Karma specs in karma-baseline.json and fails CI when the set grows — the same ratchet as eslint-suppressions.json. --write refuses to record a baseline that grew, so the ratchet cannot be released by the command that documents it.
  • scripts/codemod-jasmine-to-vitest.mjs does the dialect and the rename, converting 187 of 215. It skips whole files using fakeAsync/tick/flush, done() callbacks or the object form of createSpyObj — those carry semantics, not spelling, and a half-converted spec that still compiles reads as finished in review.
  • src/app/testing/mocks.ts provides SpyObj<T>/createSpyObj<T>(), reproducing Jasmine's typing including its unsoundness, so the migration is not also a strict-null audit of 358 annotations.

Two environment differences had to be solved before any Ionic spec could run at all:

  • @ionic/angular reaches @ionic/core/components as a directory import, which Node's ESM resolver rejects. Karma's bundler resolved it; Vitest externalises the package and hands it to Node. vitest.config.ts inlines it.
  • jsdom has no CSS.escape, no Element.prototype.scrollTo (Ionic's ion-segment scrolls the active button into view during change detection) and no usable matchMedia. Stubbed in src/testing/vitest-setup.ts, each with a comment saying what it does not do.

30 specs migrated. Karma 921 + Vitest 270 = 1191 — the same total as before the split, so nothing was lost in the move.

Interaction with other open PRs — checked

Checklist

  • npm run lint clean — eslint-suppressions.json regenerated after the renames; 443 across 400 files, unchanged from main, so violations followed the renames without any added or lost
  • npm run format:check clean
  • npm run check:test-runner passes (192 remaining, 14% migrated)
  • npm run i18n:check, check:route-permissions, typecheck:e2e, check:licenses, check-license.sh all clean
  • npm test -- --watch=false — 921 pass
  • npm run test:unit — 270 pass across 30 files, including Ionic component rendering
  • Both commits GPG-signed

Reviewer notes

@angular/build:unit-test is marked [EXPERIMENTAL] upstream. It is nonetheless Angular's sanctioned path off Karma, and the alternative is staying on something already deprecated — that trade, and the open question about whether to move to Vitest browser mode instead of stubbing jsdom, are both written up in DOCS/adr/0004-vitest-migration.md.

The e2e suites were not run locally; they need the Docker Fineract stack. CI covers them.

…s and originators

Four Working Capital endpoints existed on the backend but had no UI: delinquency
actions, breach actions, near-breach actions and per-loan loan-originator
attach/detach. The loan detail screen now carries write paths for all four, with
three new guarded routes (UPDATE_WORKINGCAPITALLOAN) and their action forms.

Also in this branch:

- api: sync the generated client against apache/fineract:latest (schemaCount
  1472 -> 1475). The interop loan-repayment command gained a request body, so
  interop-transfers now sends dateFormat/locale rather than calling with none.
- guidance: the dashboard tour's second step targeted a bare `ul`, which matched
  the sidebar's nav list before the System Status card it describes; it now
  targets `.status-list`. The tour's Exit/Back/Next/Finish buttons were
  hardcoded English and are translated.
- search: results were dismissed by the blur timeout racing the click, silently
  swallowing navigation on a slow render. Result items now preventDefault on
  mousedown so the searchbar never blurs on a result click.
- e2e: new specs for working-capital actions, the dashboard tour, and RBAC
  dimensions rbac-backend-restricted-user does not reach (multi-permission
  routes, ALL_FUNCTIONS_READ, a second action-level gate, reload persistence).
- e2e: `npm run seed:demo-data` populates a backend with a representative
  dataset for manual sanity testing, as its own `demo-seed` Playwright project
  so it never runs as a side effect of the `backend` project in CI.
Karma is deprecated and `@angular/build:karma` goes with it. `vitest@4` was already in
devDependencies with nothing wired to it, which is the worst of both: two runners
carried, only the deprecated one used.

The suite cannot move in one commit — 219 specs, 886 `jasmine.*` sites — so this makes
it movable in batches and stops it growing in the meantime.

- The runners select by filename: `*.spec.ts` is Karma (legacy, shrinking),
  `*.test.ts` is Vitest (everything new). A migration is a rename plus a dialect swap.
- `scripts/check-test-runner.mjs` records the remaining Karma specs in
  `karma-baseline.json` and fails CI when the set grows. Same ratchet as
  `eslint-suppressions.json`. `--write` refuses to record a baseline that grew, so the
  ratchet cannot be released by the command that documents it.
- `scripts/codemod-jasmine-to-vitest.mjs` does the dialect and the rename, converting
  187 of 215 remaining specs. It skips whole files using fakeAsync/tick/flush, `done()`
  callbacks or the object form of createSpyObj — those carry semantics, not spelling,
  and a half-converted spec that still compiles reads as finished in review.
- `src/app/testing/mocks.ts` provides `SpyObj<T>`/`createSpyObj<T>()`, reproducing
  Jasmine's typing including its unsoundness, so the migration is not also a
  strict-null audit of 358 annotations.

Two environment differences had to be solved before any Ionic spec could run:
`@ionic/angular` reaches `@ionic/core/components` as a directory import that Node's ESM
resolver rejects, so `vitest.config.ts` inlines the package; and jsdom has no
`CSS.escape`, `Element.prototype.scrollTo` or usable `matchMedia`, stubbed in
`src/testing/vitest-setup.ts`.

30 specs are migrated. Karma 921 + Vitest 270 = 1191, the same total as before, so
nothing was lost in the move. Rationale and the open question about Vitest browser mode
are in DOCS/adr/0004-vitest-migration.md.
@Aman-Mittal
Aman-Mittal force-pushed the chore/vitest-migration-groundwork branch from c9db3e0 to 07a1dd1 Compare August 19, 2026 17:12
@Aman-Mittal Aman-Mittal changed the title Implement Working Capital UI and migrate to Vitest testing framework feat(working-capital): loan servicing actions, plus a Vitest runner and a Karma ratchet Aug 19, 2026
@Aman-Mittal
Aman-Mittal merged commit 23705cd into apache:main Aug 19, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment