{"id":37424273,"url":"https://github.com/hyperjump-io/json-schema-coverage","last_synced_at":"2026-01-16T06:11:12.739Z","repository":{"id":301407508,"uuid":"1008728639","full_name":"hyperjump-io/json-schema-coverage","owner":"hyperjump-io","description":"Test coverage tools for JSON Schema testing","archived":false,"fork":false,"pushed_at":"2025-10-26T04:24:18.000Z","size":220,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-23T07:22:54.555Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://json-schema-coverage.hyperjump.io/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hyperjump-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["hyperjump-io"]}},"created_at":"2025-06-26T02:25:12.000Z","updated_at":"2025-10-26T04:24:17.000Z","dependencies_parsed_at":"2025-06-26T18:35:59.605Z","dependency_job_id":"e3ba9a0d-d0ae-4a87-87fd-8b4390d878d9","html_url":"https://github.com/hyperjump-io/json-schema-coverage","commit_stats":null,"previous_names":["hyperjump-io/json-schema-coverage"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/hyperjump-io/json-schema-coverage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperjump-io%2Fjson-schema-coverage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperjump-io%2Fjson-schema-coverage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperjump-io%2Fjson-schema-coverage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperjump-io%2Fjson-schema-coverage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hyperjump-io","download_url":"https://codeload.github.com/hyperjump-io/json-schema-coverage/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperjump-io%2Fjson-schema-coverage/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28477633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T03:13:13.607Z","status":"ssl_error","status_checked_at":"2026-01-16T03:11:47.863Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-16T06:11:11.930Z","updated_at":"2026-01-16T06:11:12.720Z","avatar_url":"https://github.com/hyperjump-io.png","language":"JavaScript","funding_links":["https://github.com/sponsors/hyperjump-io"],"categories":[],"sub_categories":[],"readme":"# Hyperjump - JSON Schema Test Coverage\n\nThis package provides test coverage support for JSON Schemas files in JSON and\nYAML in your project. Integration is provided for Vitest, but the low level\ncomponents for collecting the coverage data is also exposed if you want to do\nsome other integration. It uses the [istanbul] coverage format, so you can\ngenerate any reports that support [istanbul].\n\nValidation is done by [@hyperjump/json-schema], so you can use any version of\nJSON Schema, or provide your own custom keywords, vocabularies, and dialects.\n\n```\n-------------|---------|----------|---------|---------|-------------------\nFile         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n-------------|---------|----------|---------|---------|-------------------\nAll files    |   81.81 |    66.66 |      80 |   88.88 |                   \n schema.json |   81.81 |    66.66 |      80 |   88.88 | 5                 \n-------------|---------|----------|---------|---------|-------------------\n```\n\n![HTML coverage example](coverage.png)\n\nIstanbul reporters report in terms of Statements, Branches, and Functions, which\naren't terms that makes sense for JSON Schema. I've mapped those concepts to\nwhat makes sense for schemas.\n\n**Legend**\n- **Statements** = Keywords\n- **Branches** = true/false branches for each keyword (except for keywords that\n  don't branch such as annotation-only keywords like `title` and `description`)\n- **Functions** = Subschemas\n\n## Limitations\n\nThe following are a list of known limitations. Some might be able to be\naddressed at some point, while others might not.\n\n- Keywords can pass/fail for multiple reasons, but not all branches are captured\n  - Example: `type: [\"object\", \"boolean\"]`. If you test with an object and then\n    test with a number, you've covered the pass and fail branches, but haven't\n    tested that a boolean should also pass.\n- There's currently no way to produce a report that uses JSON Schema-friendly\n  terms rather than \"statements\" and \"functions\".\n\n## Vitest\n\nIntegration with vitest is provided. You'll need a vitest config specifically\nfor running schema coverage. You can't run with coverage for both your js/ts\ncode and schema code at the same time.\n\nBy default, it will track coverage for any file with a `*.schema.json`,\n`*.schema.yaml`, or `*.schema.yml` extension. You can change this with the\n`include` option. For example, if you keep your schemas in a folder called\n`schemas` and they just have plain extensions (`*.json`) instead of schema\nextensions (`*.schema.json`), you could use `[\"schemas/**/*.json\"]`.\n\nIf you use custom keywords, vocabularies, and dialects, you'll need to\nregister them with a [globalSetup](https://vitest.dev/config/#globalsetup)\nscript.\n\n`vitest-schema.config.js`\n```TypeScript\nimport { defineConfig } from \"vitest/config\";\nimport { jsonSchemaCoveragePlugin } from \"@hyperjump/json-schema-coverage/vitest\";\n\nexport default defineConfig({\n  plugins: [jsonSchemaCoveragePlugin()],\n  test: {\n    globalSetup: [\"./register-dialect-or-formats-or-whatever.ts\"], // Optional\n    include: [\"schema-tests/**/*.test.ts\"], // Optional\n    coverage: {\n      include: [\"schemas/**/*.json\"] // Optional\n    }\n  }\n});\n```\n\n```bash\nvitest run --config=vitest-schema.config.js --coverage\n```\n\nWhen you use the provided custom matcher `matchJsonSchema`/`toMatchJsonSchema`,\nif vitest has coverage enabled, it will collect coverage data from those tests.\n\n```JavaScript\nimport { describe, expect, test } from \"vitest\";\n\ndescribe(\"Worksheet\", () =\u003e {\n  test(\"matches with chai-style matcher\", async () =\u003e {\n    // 🚨 DON'T FORGET THE `await` 🚨\n    await expect({ foo: 42 }).to.matchJsonSchema(\"./schema.json\");\n  });\n\n  test(\"doesn't match with jest-style matcher\", async () =\u003e {\n    // 🚨 DON'T FORGET THE `await` 🚨\n    await expect({ foo: null }).not.toMatchJsonSchema(\"./schema.json\");\n  });\n});\n```\n\nInstead of referring to the file path, you can use `registerSchema` to register\nthe schema and then use its `$id`. Another reason to register a schema is if\nyour schema references external schema and you need to register those schemas\nfor the validation to work.\n\n```JavaScript\nimport { describe, expect, test } from \"vitest\";\nimport { registerSchema, unregisterSchema } from \"@hyperjump/json-schema-coverage/vitest\";\n\ndescribe(\"Worksheet\", () =\u003e {\n  beforeEach(async () =\u003e {\n    await registerSchema(\"./schema.json\");\n  });\n\n  afterEach(async () =\u003e {\n    await unregisterSchema(\"./schema.json\");\n  });\n\n  test(\"matches with jest-style matcher\", async () =\u003e {\n    // 🚨 DON'T FORGET THE `await` 🚨\n    await expect({ foo: 42 }).toMatchJsonSchema(\"https://example.com/main\");\n  });\n\n  test(\"doesn't match with chai-style matcher\", async () =\u003e {\n    // 🚨 DON'T FORGET THE `await` 🚨\n    await expect({ foo: null }).not.to.matchJsonSchema(\"https://example.com/main\");\n  });\n});\n```\n\nYou can also use the matchers with inline schemas, but you only get coverage for\nschemas from files in your code base.\n\n```JavaScript\nimport { describe, expect, test } from \"vitest\";\n\ndescribe(\"Worksheet\", () =\u003e {\n  test(\"matches with schema\", async () =\u003e {\n    // 🚨 DON'T FORGET THE `await` 🚨\n    await expect(\"foo\").to.matchJsonSchema({ type: \"string\" });\n  });\n\n  test(\"doesn't match with schema\", async () =\u003e {\n    // 🚨 DON'T FORGET THE `await` 🚨\n    await expect(42).to.not.matchJsonSchema({ type: \"string\" });\n  });\n});\n```\n\n### Vitest API\n\nThese are the functions available when working with the vitest integration.\n\n```JavaScript\nimport { ... } from \"@hyperjump/json-schema-coverage/vitest\"\n```\n\n- **jsonSchemaCoveragePlugin**: () =\u003e VitestPlugin\n\n    A function that returns a Vitest plugin that registers matchers and sets up\n    JSON Schema coverage if enabled.\n- **matchJsonSchema**: (uriOrSchema: string | SchemaObject | boolean) =\u003e Promise\\\u003cvoid\u003e\n\n    A vitest matcher that can be used to validate a JSON-compatible value. It\n    can take a relative or full URI for a schema in your codebase. Use relative\n    URIs to reference a file and full URIs to reference the `$id` of a schema\n    you registered using the `registerSchema` function.\n\n    You can use this matcher with an inline schema as well, but you will only\n    get coverage for schemas that are in files.\n- **toMatchJsonSchema**: (uriOrSchema: string | SchemaObject | boolean) =\u003e Promise\\\u003cvoid\u003e\n\n    An alias for `matchJsonSchema` for those who prefer Jest-style matchers.\n- **registerSchema**: (path: string) =\u003e Promise\u003cvoid\u003e\n\n    Register a schema in your code base by it's path.\n\n    _**NOTE**: This is **not** the same as the function from\n    [@hyperjump/json-schema] that takes a schema._\n- **unregisterSchema**: (path: string) =\u003e Promise\u003cvoid\u003e\n\n    Remove a registered schema in your code base by it's path.\n\n    _**NOTE**: This is **not** the same as the function from\n    [@hyperjump/json-schema] that takes the schema's `$id`._\n\n## Low-Level API\n\nThese are used internally. They can be used to get coverage without using the\nVitest integration.\n\n```JavaScript\nimport { ... } from \"@hyperjump/json-schema-coverage\"\n```\n\n### CoverageMapService\n\nThe `CoverageMapService` creates [istanbul] coverage maps for your schemas and\nstores them for use by the `TestCoverageEvaluationPlugin`. A coverage map stores\nthe file positions of all the keywords, schemas, and branches in a schema.\n\n- **CoverageMapService.addFromFile** -- (schemaPath: string): Promise\\\u003cstring\u003e\n\n    This method takes a file path to a schema, generates a coverage map for it,\n    and stores it for later use. It returns the identifier for the schema\n    (usually the value of `$id`).\n- **CoverageMapService.addCoverageMap** -- (coverageMap: CoverageMapData): void\n\n    If you have a coverage map you created yourself or got from some other\n    source, you can add it using this method. You probably don't need this. Use\n    `addFromFile` to create and store the coverage map for you.\n- **CoverageMapService.getSchemaPath** -- (schemaUri: string): string\n\n    Get the file path for the schema that is identified by the given URI.\n- **CoverageMapService.getCoverageMap** -- (schemaUri: string): CoverageMapData\n\n    Retrieve a coverage map that was previously added through `addFromFile` or\n    `addCoverageMap`.\n\n### TestCoverageEvaluationPlugin\n\nThe `TestCoverageEvaluationPlugin` hooks into the evaluation process of the\n[@hyperjump/json-schema] validator and uses the `CoverageMapService` to record\nwhen a keyword or schema is visited. Once the evaluation process is completed,\nit contains an [istanbul] coverage file. These files can then be used to\ngenerate any report that supports [istanbul]. See the following example for an\nexample of how to use the evaluation plugin.\n\n### Nyc Example\n\nThe following is an example of using the Low-Level API to generate coverage\nwithout Vitest. This uses [istanbul]'s [nyc] CLI to generate reports from the\ncoverage files that are generated.\n\nKeep in mind that with the Low-Level API approach, you need to configure\n[@hyperjump/json-schema] yourself. That means that you need to import the\ndialects you need and will need to provide `MediaTypePlugin`s for anything other\nthan `*.schema.json` file extension support. YAML support is only provided\nout-of-the-box for the Vitest integration.\n\nOnce you run the script, you can run the following command to generate a report.\n\n```bash\nnpx nyc report --extension .schema.json\n```\n\n```TypeScript\nimport { randomUUID } from \"node:crypto\";\nimport { existsSync } from \"node:fs\";\nimport { mkdir, rm, writeFile } from \"node:fs/promises\";\nimport { validate } from \"@hyperjump/json-schema/draft-2020-12\";\nimport { BASIC } from \"@hyperjump/json-schema/experimental\";\nimport {\n  CoverageMapService,\n  TestCoverageEvaluationPlugin\n} from \"@hyperjump/json-schema-coverage\";\n\nconst schemaUnderTest = `scratch/foo.schema.json`;\n\n// Tell the CoverageMapService which schemas we want coverage for.\nconst coverageService = new CoverageMapService();\nawait coverageService.addFromFile(schemaUnderTest);\n\nconst validateFoo = await validate(schemaUnderTest);\n\n// A function to run tests and write coverage files where nyc expects them.\nconst test = async (instance: any, valid: boolean) =\u003e {\n  // Validate with the TestCoverageEvaluationPlugin\n  const testCoveragePlugin = new TestCoverageEvaluationPlugin(coverageService);\n  const output = validateFoo(instance, {\n    outputFormat: BASIC,\n    plugins: [testCoveragePlugin]\n  });\n\n  // Write the coverage file\n  const filePath = `.nyc_output/${randomUUID()}.json`;\n  await writeFile(filePath, JSON.stringify(testCoveragePlugin.coverage));\n\n  // Report failures\n  if (output.valid !== valid) {\n    const instanceJson = JSON.stringify(instance, null, \"  \");\n    const outputJson = JSON.stringify(output, null, \"  \");\n    console.log(\"TEST FAILED:\", instanceJson, \"\\nOUTPUT:\", outputJson);\n  }\n};\n\n// Initialize coverage directory\nif (existsSync(\".nyc_output\")) {\n  await rm(\".nyc_output\", { recursive: true });\n}\nawait mkdir(\".nyc_output\");\n\n// Run the tests\nawait test({ foo: 42 }, true);\nawait test({ foo: null }, false);\n```\n\n[@hyperjump/json-schema]: https://www.npmjs.com/package/@hyperjump/json-schema\n[istanbul]: https://istanbul.js.org/\n[nyc]: https://www.npmjs.com/package/nyc\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperjump-io%2Fjson-schema-coverage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhyperjump-io%2Fjson-schema-coverage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperjump-io%2Fjson-schema-coverage/lists"}