Skip to content

Add a unit-test harness (Vitest), typecheck script, and CI gates - #16514

Open
bodhish wants to merge 9 commits into
developfrom
issues/16507/vitest-typecheck-ci
Open

Add a unit-test harness (Vitest), typecheck script, and CI gates#16514
bodhish wants to merge 9 commits into
developfrom
issues/16507/vitest-typecheck-ci

Conversation

@bodhish

@bodhish bodhish commented Jul 8, 2026

Copy link
Copy Markdown
Member

Closes #16507

What

Bootstraps a fast, backend-free verification baseline:

  • Vitest (devDependency) with a minimal standalone vitest.config.mts — deliberately not reusing vite.config.mts (module-federation/PWA plugins don't apply to unit tests). Node environment by default; test files opt into jsdom per-file with // @vitest-environment jsdom (jsdom was already installed).
  • Scripts: test:unit, test:unit:watch, and typecheck. The existing test (snyk) script is untouched.
  • Seed suite: src/Utils/decimal.test.ts characterizes the decimal.js money helpers and proves alias + @careConfig resolution under Vitest.
  • CI: linter.yml gains cache: "npm" (the only workflow missing it) plus typecheck and unit-test steps — so PRs are now gated on both.

A discovery worth knowing

npx tsc --noEmit fails on a clean checkout: src/supportedBrowsers.ts is gitignored and only generated by npm run supported-browsers (normally via dev/build). It passes locally only because a prior build generated it. The typecheck script therefore chains the generation step, which makes it correct on clean checkouts and CI runners with no workflow-specific setup:

"typecheck": "npm run supported-browsers && tsc --noEmit -p tsconfig.json"

Verification

  • npm run typecheck → exit 0 on a fresh npm ci worktree
  • npm run test:unit → 5/5 passing
  • npx eslint src/Utils/decimal.test.ts → clean
  • Lockfile change is purely additive (vitest ^3.2.7 + transitive)

Follow-up PRs (#16508, #16509, #16511, #16512) are stacked on this branch so their unit tests have the harness.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added developer scripts for unit testing (run/watch) and TypeScript type checks.
  • Tests
    • Introduced a Vitest setup with source-path aliases and Node-based execution.
    • Added unit test coverage for decimal “money math” and monetary component calculations.
    • Added unit test coverage for EMR medication request timing window logic.
  • Chores
    • Updated CI lint workflow to use the latest Node setup with npm caching and to run type checks and unit tests before the final checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bodhish
bodhish requested review from a team and Copilot July 8, 2026 12:02
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

⚠️ Merge Checklist Incomplete

Thank you for your contribution! To help us review your PR efficiently, please complete the merge checklist in your PR description.

Your PR will be reviewed once you have marked the appropriate checklist items.

To update the checklist:

  • Change - [ ] to - [x] for completed items
  • Only check items that are relevant to your PR
  • Leave items unchecked if they don't apply

The checklist helps ensure code quality, testing coverage, and documentation are properly addressed.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds Vitest configuration and scripts, tests decimal, monetary, and medication logic, and updates CI with Node 24, npm caching, typechecking, and unit-test gates.

Changes

Unit testing and CI validation

Layer / File(s) Summary
Vitest harness and package scripts
vitest.config.mts, package.json
Adds Vitest aliases, test discovery, environments, unit-test scripts, a typecheck script, and the Vitest development dependency.
Decimal and monetary calculation tests
src/Utils/decimal.test.ts, src/types/base/monetaryComponent/monetaryComponent.test.ts
Adds coverage for decimal arithmetic and monetary component calculations, including totals, taxes, discounts, quantities, and price breakdowns.
Medication timing tests
src/types/emr/medicationRequest/medicationRequest.test.ts
Adds coverage for timing-bound precedence and medication active-window calculations.
CI validation integration
.github/workflows/linter.yml
Uses Node 24 with npm caching and runs typechecking and unit tests before Knip.

Possibly related issues

  • #16512 — Adds characterization tests for the same monetary component and medication active-window logic.

Possibly related PRs

Suggested labels: Tested

Suggested reviewers: copilot, nihal467

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the change, but it misses the template's Proposed Changes/Merge Checklist structure and required checklist items. Rewrite it using the repository template: add the Proposed Changes section with bullets and include the full Merge Checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: Vitest setup, a typecheck script, and CI gating.
Linked Issues check ✅ Passed The PR implements the linked issue's Vitest harness, scripts, seed tests, and CI gates, while leaving npm test unchanged.
Out of Scope Changes check ✅ Passed The changes are limited to the test harness, scripts, tests, and workflow updates described by the linked issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issues/16507/vitest-typecheck-ci

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 8, 2026

Copy link
Copy Markdown

Deploying care-preview with  Cloudflare Pages  Cloudflare Pages

Latest commit: 506f3e3
Status: ✅  Deploy successful!
Preview URL: https://5b04323b.care-preview-a7w.pages.dev
Branch Preview URL: https://issues-16507-vitest-typechec.care-preview-a7w.pages.dev

View logs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Bootstraps a backend-free verification baseline for CARE FE by introducing a minimal Vitest unit-test harness, a typecheck script (including generation of the gitignored supportedBrowsers artifact), and CI gating so PRs are blocked on both typechecking and unit tests.

Changes:

  • Add vitest.config.mts and seed unit tests for the Decimal.js money helpers (src/Utils/decimal.test.ts).
  • Add test:unit, test:unit:watch, and typecheck scripts; add vitest as a devDependency.
  • Update .github/workflows/linter.yml to enable npm caching and to run typecheck + unit tests in CI.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
vitest.config.mts Adds Vitest configuration with repo path aliases and Node as the default environment.
src/Utils/decimal.test.ts Adds an initial unit-test suite covering Decimal-based money helper behavior.
package.json Introduces unit-test/typecheck scripts and adds Vitest dependency.
package-lock.json Locks new Vitest dependency tree.
.github/workflows/linter.yml Adds npm cache plus typecheck and unit-test CI gates.

Comment thread src/Utils/decimal.test.ts Outdated
Comment on lines +19 to +21
it("roundUp always rounds toward positive infinity to whole units", () => {
expect(roundUp("10.01")).toBe("11");
});
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown

Greptile Summary

This PR bootstraps Vitest as the unit-test harness, adds typecheck and test:unit scripts, and gates CI on both. Alongside the infrastructure, it ships three substantive fixes that the new tests exercise.

  • getResponseBody body-consumed bug fixed (utils.ts): the old code called res.json() and, on failure, tried res.text() on an already-exhausted stream; the new code reads with res.text() once and falls back to the raw string on parse failure.
  • clearQueryPersistenceCache now actually clears the cache (queryClient.ts): the old implementation called invalidateQueries (stale-marks in-memory entries, leaves REACT_QUERY_OFFLINE_CACHE in localStorage untouched); the new one calls removeQueries + localStoragePersister.removeClient(), wiping both in-memory and persisted state.
  • Sign-out now clears the offline cache (AuthUserProvider.tsx): clearQueryPersistenceCache() is wired into signOut, so cached query data no longer leaks into the next session after a user logs out.

Confidence Score: 5/5

Safe to merge — the infrastructure changes are additive and the three substantive fixes (double-read body, incomplete cache clearing, missing sign-out cleanup) are all correctness improvements backed by new tests.

All changed code paths are well-tested by the new suite, the CI gate additions are straightforward, and the behavior changes in queryClient/AuthUserProvider are clear improvements over the previous implementation. No regressions were identified.

No files require special attention.

Important Files Changed

Filename Overview
.github/workflows/linter.yml Adds cache: npm to setup-node and gates CI on typecheck + unit-test steps before knip; clean and well-ordered.
vitest.config.mts Standalone Vitest config with correct path aliases (@, @careConfig, @core); node environment by default with per-file jsdom opt-in.
package.json Adds vitest devDependency and new scripts (test:unit, test:unit:watch, typecheck); existing test (snyk) script is untouched.
src/Utils/request/utils.ts Fixes a double-read bug in getResponseBody: reads the body once with res.text() then JSON.parses, instead of calling res.json() (consuming the stream) and falling back to res.text() (already exhausted).
src/Utils/request/queryClient.ts clearQueryPersistenceCache changed from invalidateQueries to removeQueries + localStoragePersister.removeClient(); explicit REACT_QUERY_OFFLINE_CACHE key added to persister config.
src/Providers/AuthUserProvider.tsx Adds await clearQueryPersistenceCache() to the sign-out flow so the offline TanStack Query cache is cleared from localStorage on logout.
src/Utils/request/utils.test.ts Four tests covering getResponseBody: valid JSON, malformed JSON fallback, explicit empty body, and non-JSON content types.
src/Utils/request/queryClient.test.ts Tests clearQueryPersistenceCache in jsdom environment; beforeEach seeds the localStorage key and verifies it is null after the call.
src/Utils/request/query.test.ts Covers query.paginated defensive stop and full accumulation; uses vi.stubGlobal for fetch with proper afterEach cleanup.
src/types/base/monetaryComponent/monetaryComponent.test.ts Comprehensive tests for monetary component math including a pinned characterization test noting discounts with undefined conditions are silently excluded.
src/types/emr/medicationRequest/medicationRequest.test.ts Tests getTimingBounds priority and getMedicationActiveWindow window logic including TZ-regression guard, multi-instruction union, and modified_date clamping.

Reviews (7): Last reviewed commit: "Clear persisted query cache on logout (#..." | Re-trigger Greptile

Comment thread src/Utils/decimal.test.ts Outdated
@bodhish
bodhish requested a review from nihal467 July 8, 2026 12:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ef8c5ef4c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Utils/decimal.test.ts
@@ -0,0 +1,26 @@
import { describe, expect, it } from "vitest";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exclude Vitest files from unimported

With this file under src/ as *.test.ts, the existing npm run unimported step in .github/workflows/linter.yml will flag it as an unimported file: .unimportedrc.json only starts from app/script entries and does not include or ignore Vitest tests, while the unimported docs state that CI fails on unimported files not explicitly configured and its built-in test ignores are *.tests.*/*.spec.*, not singular *.test.*. Because this test is intentionally loaded only by Vitest, every PR/merge-group linter run will fail until tests are added to entry, ignorePatterns, or ignoreUnimported.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

🎭 Playwright Test Results

Status: ✅ Passed
Test Shards: 3

Metric Count
Total Tests 331
✅ Passed 330
❌ Failed 0
⏭️ Skipped 1

📊 Detailed results are available in the playwright-final-report artifact.

Run: #10203

@github-actions github-actions Bot added the Merge Conflict pull requests with merge conflict label Jul 8, 2026
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Conflicts have been detected against the base branch. Please merge the base branch into your branch.
cc: @bodhish

See: https://docs.ohc.network/docs/contributing#how-to-resolve-merge-conflicts

@nihal467

Copy link
Copy Markdown
Member

@copilot resolve the merge conflicts in this pull request

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/types/base/monetaryComponent/monetaryComponent.test.ts`:
- Around line 238-251: Update the test case around getPriceBreakdown so its
component amount requires rounding to two decimal places, such as 33.335 or
33.334, and adjust the expected breakdown values to assert the resulting rounded
amount multiplied by the quantity. Keep the existing quantity and breakdown
assertions otherwise unchanged.
- Around line 145-160: Update getDiscountAmount so its conditions filter treats
both undefined conditions and an explicitly empty array as applicable, using the
existing conditions-based filtering logic. Then revise the test case in
monetaryComponent.test.ts to expect the discount to be applied for a discount
without a conditions field, and remove the wording that pins the current
behavior as intentional.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1c61ac07-4350-4997-9ffc-8f53cb0edd8d

📥 Commits

Reviewing files that changed from the base of the PR and between 25a7f78 and 2f632c2.

📒 Files selected for processing (2)
  • src/types/base/monetaryComponent/monetaryComponent.test.ts
  • src/types/emr/medicationRequest/medicationRequest.test.ts

Comment on lines +145 to +160
it("excludes discounts whose conditions field is undefined (pins current behavior: conditions must be an explicit empty array to apply — flagged for maintainer review)", () => {
// `c.conditions?.length === 0` is false when `conditions` is undefined
// (`undefined?.length === 0` evaluates to `undefined === 0` -> false), so a
// discount with no `conditions` key at all is silently excluded, the same
// as one with a populated conditions array. Pinned here as current
// behavior, not necessarily intended behavior.
const components: MonetaryComponent[] = [
{ monetary_component_type: MonetaryComponentType.base, amount: "100" },
{
monetary_component_type: MonetaryComponentType.discount,
factor: "20",
// no `conditions` key
},
];
expect(getDiscountAmount(components).toString()).toBe("0");
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Test pins a likely production bug in getDiscountAmount's conditions filter.

Per the provided contract, getDiscountAmount filters with c.conditions?.length === 0. When conditions is undefined, undefined?.length === 0 evaluates to undefined === 0false, so a discount with no conditions key is silently excluded — identical treatment to a discount with an actual, non-empty condition list. That's a real business-logic gap: discounts meant to always apply (no conditions field at all) would incorrectly not be applied, understating discounts / overcharging. The test comment itself flags this for maintainer review rather than asserting it's correct.

Since monetaryComponent.ts isn't in this diff, I can't fix it here, but recommend changing the production filter to !c.conditions?.length (treats both undefined and [] as "no conditions") and updating this test's expectation accordingly.

Would you like me to open an issue to track fixing getDiscountAmount's conditions filter in monetaryComponent.ts?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types/base/monetaryComponent/monetaryComponent.test.ts` around lines 145
- 160, Update getDiscountAmount so its conditions filter treats both undefined
conditions and an explicitly empty array as applicable, using the existing
conditions-based filtering logic. Then revise the test case in
monetaryComponent.test.ts to expect the discount to be applied for a discount
without a conditions field, and remove the wording that pins the current
behavior as intentional.

Comment on lines +238 to +251
it("rounds every line item to 2 decimals and multiplies by quantity", () => {
const components: MonetaryComponent[] = [
{ monetary_component_type: MonetaryComponentType.base, amount: "33.33" },
];
const breakdown = getPriceBreakdown(components, 3);
expect(breakdown).toEqual({
basePrice: "99.99",
surcharges: "0.00",
discounts: "0.00",
subtotal: "99.99",
tax: "0.00",
total: "99.99",
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rounding test doesn't actually exercise rounding.

33.33 * 3 = 99.99 requires no rounding at all, so this case doesn't verify the "rounds every line item to 2 decimals" behavior the it description claims. Use a value that needs rounding (e.g. an amount like "33.335" or "33.334") so a genuine rounding boundary is asserted.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/types/base/monetaryComponent/monetaryComponent.test.ts` around lines 238
- 251, Update the test case around getPriceBreakdown so its component amount
requires rounding to two decimal places, such as 33.335 or 33.334, and adjust
the expected breakdown values to assert the resulting rounded amount multiplied
by the quantity. Keep the existing quantity and breakdown assertions otherwise
unchanged.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 2 comments.

Comment on lines +105 to +109
const text = await res.text();
try {
return await res.json();
return JSON.parse(text) as TData;
} catch {
return (await res.text()) as TData;
return text as TData;
Comment on lines +22 to +27
it("returns null for an explicitly empty body", async () => {
const res = new Response(null, {
headers: { "content-length": "0", "content-type": "application/json" },
});
await expect(getResponseBody(res)).resolves.toBeNull();
});

@nihal467 nihal467 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot fix the failing test

Co-authored-by: nihal467 <57055998+nihal467@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 14, 2026 15:32
@bodhish
bodhish removed the request for review from Copilot July 14, 2026 15:32

Copilot AI commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

@copilot fix the failing test

Fixed in 936770a. I investigated the failing lint workflow run and the failure was in the typecheck step: monetaryComponent.test.ts imported getPriceBreakdown, which is no longer exported. I removed that stale import and related tests, then validated with npm run typecheck, npm run test:unit, and npm run knip.

Copilot AI requested a review from nihal467 July 14, 2026 15:33
@github-actions github-actions Bot added the Merge Conflict pull requests with merge conflict label Jul 14, 2026
@github-actions

Copy link
Copy Markdown

Conflicts have been detected against the base branch. Please merge the base branch into your branch.
cc: @bodhish

See: https://docs.ohc.network/docs/contributing#how-to-resolve-merge-conflicts

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 16, 2026 13:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated no new comments.

@Jacobjeevan

Copy link
Copy Markdown
Contributor

@claude[agent] convert autoRegisterComponent.test.ts to use vitest and resolve package.json.

The merge conflict in package file is caused by this since this it uses node:test.

…test

Co-authored-by: Jacobjeevan <40040905+Jacobjeevan@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 30, 2026 11:53
@Claude

Claude AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@claude[agent] convert autoRegisterComponent.test.ts to use vitest and resolve package.json.

The merge conflict in package file is caused by this since this it uses node:test.

Done in 506f3e3. Converted plugins/autoRegisterComponents.test.ts from node:test/assert to vitest (describe/expect/test), resolved the package.json merge conflict by keeping the vitest-based test:unit script, and added plugins/**/*.test.ts to the vitest include pattern. All 66 tests pass.

@copilot resolve the merge conflicts in this pull request

Resolved in 506f3e3. The merge conflict in package.json was caused by develop adding a node:test-based test:unit script while this branch has a vitest-based one. Kept the vitest runner and converted the test file to use vitest.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/Utils/request/utils.ts:109

  • For JSON responses with an empty body but without a readable content-length: 0 header (common in browsers due to CORS), res.text() yields an empty string and this currently falls into the JSON.parse() catch path, returning "" instead of null. That can leak an unexpected string into callApi() for endpoints that legitimately return no content (e.g. TRes: void). Consider treating an empty string body as null before attempting JSON.parse().
  const text = await res.text();
  try {
    return JSON.parse(text) as TData;
  } catch {
    return text as TData;

src/Utils/request/utils.test.ts:27

  • The empty-body test only covers the content-length: 0 case. In browsers content-length is often missing/unreadable (CORS), so it would be good to also cover an empty JSON body with no content-length header to prevent regressions once getResponseBody treats "" as empty.
  it("returns null for an explicitly empty body", async () => {
    const res = new Response(null, {
      headers: { "content-length": "0", "content-type": "application/json" },
    });
    await expect(getResponseBody(res)).resolves.toBeNull();
  });

@github-actions github-actions Bot removed the Merge Conflict pull requests with merge conflict label Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs review Tested Type Changes Contains changes in typescript types

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a unit-test harness (Vitest), typecheck script, and CI gates

6 participants