Skip to content

Update tests - #1

Merged
dzmitry-duboyski merged 21 commits into
mainfrom
tests/restructure-and-ci
Aug 8, 2026
Merged

dzmitry-duboyski merged 21 commits into
mainfrom
tests/restructure-and-ci

Conversation

@dzmitry-duboyski

Copy link
Copy Markdown
Contributor

No description provided.

dzmitry-duboyski and others added 21 commits August 6, 2026 13:47
Split tests by type at the top level: unit/ (no network, fetch mocked,
no API key) and integration/ (real API). Previously unit tests lived in
sync/ and async/ while the single integration file sat loose in the
tests/ root, and the npm scripts told the two apart by regex-matching
the word "integration" anywhere in the path.

Select the suites by directory instead. This also fixes a side effect of
the old flag: --testPathIgnorePatterns=integration overrode the Jest
default of ["/node_modules/"], so node_modules stopped being ignored
during unit runs.

Tests now import the SDK by package name rather than by relative path to
src/. Node resolves the package self-reference through the "exports" map
in package.json and Jest honours it, so the suite exercises the same
entry points a consumer gets. With direct file imports an export could
be dropped from src/index.js, or the "exports" map broken, and every
test would still pass while the package was unusable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every other test imports the package by name, so a broken main entry
point already fails the suite loudly. What stays uncovered is the rest
of the "exports" map: the ./tasks and ./exceptions subpaths, and the
fact that undeclared subpaths remain private.

Asserts that all three entry points resolve, that __version__ matches
the version in package.json, that the subpaths and the main entry hand
back the same class objects (otherwise instanceof would break for
anyone mixing import styles), and that captcha-sdk/client stays
unreachable even though the file exists.

The class lists are spelled out explicitly rather than derived from the
modules under test: comparing an export against itself always passes.
Adding a captcha type means extending the list by hand, which is the
point where making a class public becomes a deliberate decision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
collectCoverageFrom: ['src/**/*.js'] makes coverage count every file in
src/, not only the ones imported from tests. Without it a new file that
nothing imports drops out of the report silently instead of showing 0%.

That was not hypothetical: src/index.js was invisible to the report
while tests imported src/ files directly, so coverage read 97.5% with
the package entry point entirely unexercised.

coverageReporters is narrowed to text (console) and lcov (upload target
for Coveralls); the default set also wrote clover.xml and a JSON dump
that nothing consumes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Runs unit tests on Node 18/20/22, matching engines.node ">=18", on push
to main and on every pull request. Coverage is uploaded to Coveralls
from the Node 20 job only -- the number does not vary by Node version,
so three uploads would just add noise.

Integration tests deliberately do not run on pull_request: they spend
real account balance, and secrets are unavailable to forked PRs, so the
tests would skip themselves and report a false green. They run on a
nightly schedule and on demand instead.

The integration job fails fast when CAPTCHA_API_KEY is missing. Without
that guard an unset secret looks like a passing run, since the tests
skip themselves and the job goes green having verified nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tests/README.md covers what each suite checks, how the sync/async split
avoids duplicating serialization tests, how to run everything manually,
and what CI does automatically.

todo.md tracks the coverage badge, which is deferred because the
remaining steps need the web UI: enabling the repository on Coveralls
and a first workflow run on main. The badge markdown is ready there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous contents were "./tests" and ".claude". The "./" prefix is
not valid in npm ignore patterns, which are gitignore-style, so the
tests entry matched nothing.

It had no visible effect because "files": ["src/"] in package.json is a
whitelist and takes precedence, but the file was not harmless: while an
.npmignore exists npm stops falling back to .gitignore when packing, so
coverage/ and .env lost the exclusion they were relying on.

List every dev-only path explicitly. Verified with npm pack --dry-run
that this file on its own yields the same 7-file tarball as the "files"
whitelist, so the two layers agree instead of contradicting each other.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude Code writes a local permission allowlist there. It is full of
absolute paths to a machine-specific temp directory and one-off command
entries, so it is useless to anyone else working on the repository.

It stays listed in .npmignore as well: while an .npmignore exists npm
does not consult .gitignore when packing, so the two files have to
repeat each other.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The whole suite depends on importing 'captcha-sdk' rather than reaching
into src/ by relative path, but nothing in the files said so. A
contributor seeing the unusual import could "correct" it to
../../../src/client.js, keep every test green, and quietly remove the
only thing that catches a broken entry point or "exports" map.

Note it where the temptation is, in each test file header.
public-api.test.js is left alone -- its header already covers the same
ground.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The first live run went red on a PR where every test passed: the
Coveralls step returned "Couldn't find a repository matching this job"
because the repository is not connected on coveralls.io yet.

Uploading coverage is auxiliary. A Coveralls outage, or a repository
that is not wired up yet, should not block merging a pull request whose
tests are green. A stale badge is visible enough on its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The repository is private and the Coveralls free plan only covers public
ones, which is why connecting it returns "No repos found" and the CI
upload fails with "Couldn't find a repository matching this job".

The plan is to make the repository public later, so the existing setup
stays as is. Write the reason down: without it the open item looks
abandoned rather than waiting on something specific.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`client` said nothing about what the object was, so `client.solve()` and
`client.getBalance()` only read clearly with the constructor line in view.
`captchaSolver` carries that context to every call site.

Covers the README code blocks and both example suites. Prose mentions of
"client" and the `clientKey` option are unchanged, as is the tests/ fixture
variable -- that one is internal, not something users read to learn the API.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The examples were undocumented: nothing said how to run them, which ones
need editing first, or why there are two copies of every scenario.

Three READMEs. The parent one carries the shared setup and an index of all
11 scenarios in both styles; each suite README describes its own files and
links them to the matching section of the API docs.

Four things a reader could previously only learn by hitting them:

- Most files run a proxyless and a with-proxy block on every execution,
  spending balance twice. In sync/ the two even start concurrently.
- geetest_v3.js fetches a placeholder init endpoint and cannot run
  end-to-end unmodified.
- The image examples resolve ./captcha.png against the working directory,
  not the script location, and the repository ships no sample images.
- languagePool is the second argument to solve(), not a task field.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
image_to_text.js and coordinates.js read four PNGs that are not in the
repository, so both fail with ENOENT before reaching the API -- the only
examples that need more than an API key.

Adding the files is half the fix: the paths are relative to the working
directory, so the reads have to move to new URL(..., import.meta.url) as
well, and examples/README.md currently documents the opposite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The suite is about to grow a test per captcha type, and a single
client.test.js would have made every new one a paid test by default.

Split along cost rather than captcha type, since that is what actually
separates these tests: balance.test.js spends nothing and needs no target
page, so it works as a smoke test that the key and the network path are
fine. Everything calling solve() bills the account on every nightly run
and now lives in its own file.

helpers.js holds the pieces that would otherwise be copied into each new
file: the key, a client factory, and the skip guard.

The guard changes behaviour. It used to be `if (!apiKey) return` inside
each test body, so a keyless run reported green "passed" for tests that
never called the API. Skipping at the describe level reports "skipped"
instead. The run still exits 0 having verified nothing, so the CI
fail-early step stays necessary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds real-API tests for reCAPTCHA v3, Turnstile, GeeTest v4, Yandex
SmartCaptcha, Tencent and Image to Text, one file per type.

No target page or sitekey is committed. Every target comes from the
environment and a suite whose variables are unset skips itself, so the
repository carries no links to real sites and no keys of theirs. The
Google demo page that recaptcha_v2.test.js used to fall back on is gone,
and .env.example now ships variable names with empty values instead of
working keys for reCAPTCHA, Turnstile and GeeTest.

describeTarget() in helpers.js is describeIntegration() plus that check;
it hands the collected values to the suite body. helpers.js also loads
dotenv now, so a local run reads .env instead of exporting a dozen
variables by hand. Values already in the environment still win, and unit
tests do not import the file.

Two consequences worth stating:

- A fresh clone has no working integration check beyond the balance, by
  design. image_to_text.test.js is the exception: it needs an image
  rather than a page, and .env.example carries a usable one.
- The nightly CI job passes only CAPTCHA_API_KEY, so it now verifies the
  balance and skips all seven solve suites. Running them there is not
  planned yet; tests/README.md records what to do when it is.

GeeTest v3 stays uncovered on purpose -- its challenge is per-session and
has to be scraped immediately before the task is created.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The "one file or one test" section showed unit examples only, so the
commands for the paid suites had to be inferred.

Adds the integration equivalents and two caveats that apply only there:
a bare -t filter also matches the paid suites, and Jest runs files in
parallel, so a whole-directory run submits several billed tasks at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two image-based flows -- image to text and coordinates -- needed a
picture that the repository did not have. The examples read ./captcha.png
and friends and died with ENOENT before reaching the API, and the
integration test took its image from IMAGE_TO_TEXT_BASE64 in .env.example,
whose sample string was a valid PNG of 26x26 pixels: nothing to recognise,
so every run failed with ERROR_CAPTCHA_UNSOLVABLE and spent balance doing
it.

Both samples now live in examples/assets/, one copy shared by the examples
and the tests so the two cannot drift apart. Paths resolve through
new URL(..., import.meta.url), against the script rather than the working
directory, so everything runs from the repository root.

- Add tests/integration/coordinates.test.js. It asserts the points fall
  inside the picture, with the bounds read from the PNG's IHDR chunk so
  swapping the sample cannot silently invalidate the check.
- image_to_text.test.js reads the file instead of the environment;
  IMAGE_TO_TEXT_BASE64 is gone. Both image suites now need only a key,
  which also means the nightly CI job solves two captchas for real.
- Fix hints that contradicted the sample: numeric: 1 (digits only) on a
  letters captcha, and a comment about a green apple on a grid of street
  signs.
- Comment out the three example blocks that need an instruction image the
  repository still does not ship, naming the file they expect.
- Stop calling every target identifier a sitekey: Tencent has an appId and
  GeeTest v4 a captchaId.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
package.json and the __version__ export are asserted to match by
tests/unit/public-api.test.js, so move both together.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Five claims had gone stale against the tree: the integration suite is nine
files, not eight; the skip-mode output and the count of paid tasks per run
followed from that; and the unit run is well under two seconds, not seven.

Also moves the --experimental-vm-modules and Node 18+ paragraphs out of the
coverage section, where they had ended up during an earlier restructure, into
a Requirements subsection under the manual-run instructions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dzmitry-duboyski
dzmitry-duboyski merged commit bb95878 into main Aug 8, 2026
4 checks passed
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