Running the tests¶
Where the tests live, what each root is for, and what CI would catch. If you are adding a test, the question this answers is where does it go — so that the answer is a rule rather than a precedent.
The roots¶
A package's tests live with the package, including the two that are not importable packages.
| root | tests | run it with |
|---|---|---|
tests/ |
the repository as a whole — the contracts, and that a release is coherent | pytest tests |
tests/reference/ |
the analyses, against known answers | pytest tests/reference |
packages/<pkg>/tests/ |
one Python package on its own | pytest packages/dims-analysis |
packages/dims-core/test/ |
the dashboard host and the built-in tabs, headless | cd packages/dims-core/test && node --test |
apps/builder/tests/ |
the wizard's Python | pytest apps/builder |
apps/builder/test/ |
the wizard's page, headless | cd apps/builder/test && node --test |
pytest with no arguments runs every Python root, because they are listed in
[tool.pytest.ini_options] in the root pyproject.toml. A new root belongs in
both places — that file and tests/README.md.
Two roots have no package of their own:
packages/dims-case-scaffold/shipsbuild_assets.pyandserve.pyinto every study, so a test there imports by path rather than by package name — its one suite does that forserve.py.build_assets.pyis exercised frompackages/dims-case/tests/instead, as part of building a study end to end.packages/dims-tabs/has no suite at all, because a tab cannot run without the host. The tabs are tested frompackages/dims-core/test/, which loads both.
packages/dims-core/test/ is singular on purpose. It is an npm package with
its own package.json; test/ is the npm convention and node --test finds it.
Renaming it to match the Python suites would buy a tidier find and nothing else.
apps/builder has both spellings for the same reason — tests/ is its Python,
test/ is its JavaScript.
The counts in the repository are wrong and I have not changed them.
tests/README.mdopens "Seven roots" over a table of six.pyproject.toml's comment says "The five roots" over a list of six. Neither matches; both are stale rather than describing something I could not find.
The two JavaScript suites need npm install first¶
They use jsdom, the only dependency either has. Both package.json files exist
for test tooling only and say so in as many words — one that the dashboard has
no build step and no runtime dependencies, the other the same of the wizard.
cd packages/dims-core/test && npm install && node --test
cd apps/builder/test && npm install && node --test
What CI runs¶
Eight jobs. Their value is less the coverage than that each one names a failure it exists to prevent.
| job | runs | catches |
|---|---|---|
syntax-and-config |
the org's reusable dashboard workflow over dims-analysis, dims-case, dims-case-scaffold, apps/builder, tools, and the JS globs |
a file that does not parse, in either language. Note packages/dims-notebooks is not in that list, so its Python is not syntax-checked here |
contracts |
pytest tests/test_contracts.py tests/test_release.py |
a broken cross-reference, a config schema that is not a valid schema, a version that disagrees with itself |
analysis-package |
asserts dims-analysis list prints rqa, crqa and crosswavelet, then pytest packages/dims-analysis/tests |
a step that stopped being discoverable. An installed package whose entry points are broken still imports fine; only asking the CLI catches it |
case-tooling |
pytest packages/dims-case/tests then packages/dims-case-scaffold/tests |
vendoring, the pin, and the privacy hooks. Drives real git |
reference-study |
installs pocl-opencl-icd, asserts pyrqa actually imports, then pytest tests/reference |
a wrong answer from an analysis, against an independent oracle |
dashboard-host |
Node 22, node --test in packages/dims-core/test |
the host, the registry, the payload decoders and the built-in tabs |
notebooks |
pytest tests/ in packages/dims-notebooks, plus an nbformat-4 and non-empty-cells check on the .ipynb files in that one directory (the glob does not recurse) |
a diagnostic that stopped firing, and a notebook committed broken or blank |
builder |
pytest tests in apps/builder; node --test in apps/builder/test; then a smoke test that POST /api/project produces a study containing config.json, index.html, serve.py, dims-case.json, vendor/dims-core and vendor/dims-tabs |
the wizard producing something that is not a working study |
Two of these deserve a closer look¶
The pyrqa comparison is gated in CI, not in the tests. tests/reference checks
the recurrence analyses against pyrqa, an independent implementation. The tests
themselves use importorskip, so a contributor without pyrqa stays green — which
also means that on its own the suite can pass by never running the comparison.
CI closes that: it installs an OpenCL runtime (pyrqa has no CPU fallback) and then asserts the import outright in a separate step before running the suite. So a missing oracle fails the job rather than quietly hollowing it out. The gate lives in the workflow; do not assume the suite carries it.
tests/reference/ restates its constants rather than importing them. DT,
PERIOD_S, LAG_S and the target rate are written out again in its conftest.py,
with a comment saying so. Importing them from the code under test would make the
test agree with the code by construction, which is the one thing a reference test
may not do. The same applies to the Torrence & Compo figures: the wavelet reference
test restates 2.182 and 3.999 rather than importing the constants that hold them.
What the release check covers¶
tests/test_release.py asserts that every package declares the same version, that
the source __version__ agrees with packaging, and that CHANGELOG.md's newest
entry is the version this tree declares.
That last one used to ask only whether the declared version had a ## v… heading
somewhere in the file. Every past release leaves one behind, so once v1.0.0 was
written the check could not fail again — and it did not: v1.4.0 was tagged while
the code still declared 1.3.0, and the test passed on the v1.3.0 heading four
releases down the page. It now compares against the first heading, which is the
release being shipped, because the changelog is strictly newest-first.
One part is still weaker than it reads: "every package declares the same version"
walks the tree for pyproject.toml files, and the repository has exactly one, so a
set of one cannot disagree. The __version__ check beside it does compare two real
files.
tests/test_contracts.py covers the other version that can drift silently — the
payload format, declared independently in common/arrays.py and dims-core.js.
Nothing compared those until v1.4.2. A bump on one side alone does not fail a test;
it fails in a browser, as every analysis panel going blank behind a message naming
the wrong fix.
Before opening a pull request¶
python -m pip install -e ".[dev]"
python -m pytest -q # every Python root
cd packages/dims-core/test && npm install && node --test
cd apps/builder/test && npm install && node --test
tests/reference needs pyrqa and an OpenCL runtime, so it is the one suite most
contributors will let CI run.