Skip to content

feat: add plan to fill test-coverage gaps#365

Open
phillipc wants to merge 9 commits into
mainfrom
test/coverage_gap
Open

feat: add plan to fill test-coverage gaps#365
phillipc wants to merge 9 commits into
mainfrom
test/coverage_gap

Conversation

@phillipc
Copy link
Copy Markdown
Member

@phillipc phillipc commented Apr 22, 2026

This pull request adds a comprehensive plan in plans/coverage-gap-fill.md to systematically close test coverage gaps across the public @tko/* packages. The plan prioritizes files and packages with the lowest coverage, outlines measurable goals, and details a phased approach for adding targeted tests. It also specifies verification steps and adversarial review guidelines to ensure new tests are meaningful and robust.

Coverage gap analysis and prioritization:

  • Provides a detailed snapshot of current coverage per package and per file, identifying the riskiest and lowest-covered areas, especially in the provider stack and utility modules.
  • Sets measurable goals: bring all public packages to β‰₯90% lines and β‰₯85% branches, with no file below 70% lines unless documented, aiming for overall β‰₯95% lines and β‰₯92% branches.

Phased approach for test additions:

  • Outlines a five-phase plan, starting with trivial single-file wins, then focusing on the provider stack, builder/binding edges, DOM utilities, and finally tail cleanup for remaining files. Each phase has specific target files and coverage scenarios to address.
  • Specifies that each commit should add or extend spec files, with no production code changes unless a real bug is found (which must be fixed in a separate commit).

Verification and review discipline:

  • Details verification steps for each commit

Summary by CodeRabbit

  • Tests

    • Expanded test suite with new behavioral test cases across multiple modules
  • Chores

    • Added code coverage tooling and reporting configuration
    • Updated build sourcemap configuration
    • Documented coverage improvement plan

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

Review Change Stack

Warning

Rate limit exceeded

@phillipc has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 52 minutes and 38 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

βŒ› How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7f60402-fa59-4177-ac66-e7741d7c0b93

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between ec8231f and ee7e40b.

πŸ“’ Files selected for processing (5)
  • packages/binding.core/spec/submitBehaviors.ts
  • packages/observable/spec/subscribableBehaviors.ts
  • packages/provider/spec/BindingHandlerObjectBehaviors.ts
  • packages/utils/spec/stringBehaviors.ts
  • plans/2026-04-22-coverage-gap-fill.md
πŸ“ Walkthrough

Walkthrough

This PR establishes comprehensive code coverage infrastructure for the TKO monorepo by configuring Vitest V8 coverage reporting, adding 70+ new test cases across core libraries to measure baseline behavior, regenerating the coverage report, and documenting a phased gap-fill plan with measurable targets.

Changes

Coverage Infrastructure and Test Expansion

Layer / File(s) Summary
Coverage Tooling Configuration
vitest.config.ts, package.json, tools/build.ts
Vitest is configured with v8 coverage provider, reporters, and coverage/ output directory. A test:coverage npm script is added to build and run tests with coverage enabled. The @vitest/coverage-v8 dev dependency is added. Esbuild sourcemap mode changes from external to linked.
Core Library Test Specifications
packages/binding.core/spec/submitBehaviors.ts, packages/observable/spec/subscribableBehaviors.ts, packages/provider/spec/BindingHandlerObjectBehaviors.ts, packages/utils/spec/stringBehaviors.ts
New test cases validate submit binding handler type enforcement and form default prevention logic, Observable subscription lifecycle and closed state, BindingHandlerObject set/get behavior with error handling and dotted names, and parseJson function parsing, null returns, whitespace trimming, and error cases.
Coverage Results and Planning
COVERAGE.md, plans/coverage-gap-fill.md
Coverage metrics are regenerated with updated per-package breakdowns and overall totals. A detailed coverage-gap-fill plan documents current gaps, measurable targets (β‰₯90% lines, β‰₯85% branches for public packages), phased test-only workflow, and review constraints for closing coverage deficits.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • knockout/tko#344: Adds tests for parseJson/stringTrim behavior in @tko/utils, directly relating to the stringBehaviors test coverage added in this PR.
  • knockout/tko#364: Modifies the same coverage tooling files (package.json, vitest.config.ts, tools/build.ts) to set up Vitest V8 coverage infrastructure.
  • knockout/tko#231: Updates coverage-related repository tooling and COVERAGE.md reporting alongside this PR.

Suggested reviewers

  • brianmhunt

Poem

🐰 Coverage hops across each test,
Line by line, we measure best,
Vitest charts what handlers do,
Observable, parseJson true,
Gaps now filled with phased review! ✨

πŸš₯ Pre-merge checks | βœ… 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
βœ… Passed checks (4 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The pull request title accurately describes the primary objective: adding a plan document to address test coverage gaps across the project.
Linked Issues check βœ… Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check βœ… Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/coverage_gap

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 and usage tips.

parseJson lacked a spec entirely (string.ts: 0% lines / 0% branches).
Adds stringBehaviors.ts covering: valid JSON parsing, primitives and
arrays, leading/trailing whitespace, empty and whitespace-only strings,
non-string runtime input (returns null instead of coercing or throwing),
and malformed JSON (rethrows JSON.parse β€” pinned so a future 'be helpful
and return null' change is caught).

Lifts utils/src/string.ts from 0% to 100% lines and branches.

Adversarial-review audit: subagent reviewed; cleared as solid (assertions
exercise production code, type casts intentional).
Subscription.ts had 84.61% lines / 50% functions: the TC39
Observable-style unsubscribe() alias and the closed getter were never
exercised by any spec.

Adds two it() blocks to subscribableBehaviors:
- unsubscribe() acts as an alias for dispose(): closed flips true and
  later notifications do not reach the callback.
- closed reports false before dispose, true after.

In combination with existing computed/observable specs that exercise
disposeWhenNodeIsRemoved(), this lifts Subscription.ts to 100% lines
and branches.

Adversarial-review audit: an earlier draft asserted dispose() was
idempotent ("survive a second dispose"). Subscription.dispose() has no
_isDisposed guard, so a second call re-fires _disposeCallback and could
emit duplicate 'asleep' notifications via afterSubscriptionRemove. The
weak "does not throw" assertion was removed to avoid pinning unverified
behavior; if double-dispose hardening is wanted it lands as a separate
production fix with its own spec.
submit.ts had 81.81% lines / 50% branches: the throw-on-non-function
guard and the 'handler returned true β†’ skip preventDefault' branch
were uncovered.

Adds three it() blocks:
- Bound value that is not a function throws with the documented message.
- Handler returning true leaves event.defaultPrevented false (test
  attaches its own listener to read defaultPrevented BEFORE calling its
  own preventDefault, so the assertion observes the binding's behavior,
  not the test's own cleanup).
- Handler returning a non-true value sets event.defaultPrevented true.

Lifts submit.ts from 81.81% to 90.90% lines and from 50% to 83.33%
branches. Remaining uncovered branches are the IE-only event.returnValue
fallback (no preventDefault), unreachable in headless chromium.

Adversarial-review audit: subagent verified the preventDefault tests
read defaultPrevented before the test's own preventDefault call and so
genuinely observe binding behavior. Subagent flagged a pre-existing
state leak in this file (no afterEach restoring options.bindingProvider-
Instance); that is an established pattern across binding.* specs and is
out of scope for this commit.
BindingHandlerObject.ts had 77.77% lines / 66.66% branches: the two
options.onError calls (object + extraneous value, and non-string non-
object key) were never exercised, and the dotted-name lookup in get()
was lightly covered.

Adds BindingHandlerObjectBehaviors with six it() blocks:
- set(name, value) registers a single handler (lookup via get).
- set(object) registers multiple handlers via Object.assign.
- set(object, value) reports onError with the documented message.
- set(undefined) and set(42) report onError with the documented message.
- get('attr.title') and get('attr.style.color') resolve to the root
  segment 'attr' (pins the dotted-handler convention).
- get('nope') returns undefined for unknown handlers.

beforeEach captures the original options.onError; afterEach restores it
so a stub does not leak into other specs even if a test fails mid-run.

Lifts BindingHandlerObject.ts from 77.77% to 100% lines and from 66.66%
to 91.66% branches. Remaining uncovered branch is the Object.assign
fall-through after the (object + extra value) error path, which the
production code intentionally still runs.

Adversarial-review audit: subagent verified options.onError stub setup
and teardown isolate per test; no leakage path identified.
Phase 1 of plans/coverage-gap-fill.md is complete (4 of 5 items):
- utils/string.ts:                 0.00% β†’ 100.00% lines
- observable/Subscription.ts:     84.61% β†’ 100.00% lines
- provider/BindingHandlerObject.ts: 77.77% β†’ 100.00% lines
- binding.core/submit.ts:         81.81% β†’  90.90% lines

Overall: 92.71% β†’ 92.96% statements, 87.63% β†’ 87.95% branches,
90.29% β†’ 90.72% functions, 92.82% β†’ 93.10% lines.

Phase 1 item 2 (binding.core/descendantsComplete.ts) is dropped: the
plan was annotated 'Allegedly dead code' and verification confirms it.
applyBindings.ts:405-422 special-cases the descendantsComplete binding
name and invokes the value accessor directly, bypassing the
DescendantsCompleteHandler subclass entirely. onDescendantsComplete()
has no caller in the framework. Writing a unit test that constructs the
class and calls the method would lift the line counter without proving
anything user-facing β€” that violates the plan's adversarial checklist
('coverage gamed'). Removal of the dead code, or rewiring the binding
to use the lifecycle method, belongs in its own PR.

Adversarial-review audit: not in scope (docs + plan only).
@phillipc phillipc marked this pull request as ready for review April 22, 2026 22:12
Base automatically changed from test/coverage to main May 5, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant