This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
QAS CLI (qas-cli) is a Node.js CLI tool for uploading test automation results (JUnit XML / Playwright JSON / Allure results directories) to QA Sphere. It matches test case references (e.g., PRJ-123 markers or Allure TMS links) to QA Sphere test cases, creates or reuses test runs, and uploads results with optional attachments.
npm install # Install dependencies
npm run build # Clean, compile TS, add .js extensions, chmod entry point
npm run check # Typecheck + lint + format check
npm test # Run all tests (Vitest)
npx vitest run src/tests/junit-xml-parsing.spec.ts # Run a single test file
npx vitest run -t "test name" # Run a specific test by name
npm run lint # ESLint
npm run lint:fix # ESLint — auto-fix
npm run format # Prettier — auto-fix
npm run format:check # Prettier — check only
npm run typecheck # tsc --noEmitNode.js compatibility tests: cd mnode-test && ./docker-test.sh (requires Docker, tests against Node 18+).
src/bin/qasphere.ts— Entry point (#!/usr/bin/env node). Validates Node version, delegates torun().src/commands/main.ts— Yargs setup. Registers three upload commands (junit-upload,playwright-json-upload,allure-upload) as instances ofResultUploadCommandModule, plus theapicommand.src/commands/resultUpload.ts—ResultUploadCommandModuledefines CLI options shared by both commands. Loads env vars, then delegates toResultUploadCommandHandler.
The upload flow has two stages handled by two classes, with a shared MarkerParser instance:
-
MarkerParser— Centralizes all test case marker detection/extraction/matching logic:- Supports three marker formats: hyphenated (
PRJ-123), underscore-separated hyphenless (test_prj123_foo), and CamelCase hyphenless (TestPrj123FooorTestFooPrj123) - Hyphenless matching (underscore-separated and CamelCase) is gated on
type === 'junit-upload'and requires the test name to start withtest(case-insensitive) - Created by
ResultUploadCommandHandlerand passed toResultUploader— both share one instance - Also exports a standalone
formatMarker()function used by parsers
- Supports three marker formats: hyphenated (
-
ResultUploadCommandHandler— Orchestrates the overall flow:- Parses report inputs using the appropriate parser (JUnit XML file, Playwright JSON file, or Allure results directory), which return
ParseResultobjects containing bothtestCaseResultsandrunFailureLogs. File-based parsers receive file contents; directory-based parsers (Allure) receive the path — controlled by the module-leveldirectoryInputTypesSet ParserOptionsincludesallowPartialParse(set from--force) to skip invalid files instead of aborting- Detects project code from test case names via
MarkerParser(or from--run-url) - Creates a new test run (or reuses an existing one if title conflicts)
- Collects run-level logs from all parsed files and passes them to
ResultUploader
- Parses report inputs using the appropriate parser (JUnit XML file, Playwright JSON file, or Allure results directory), which return
-
ResultUploader— Handles the upload-to-run mechanics:- Fetches test cases from the run, maps parsed results to them via
MarkerParsermatching - Validates unmatched/missing test cases (respects
--force,--ignore-unmatched) - If run-level log is present, uploads it via
createRunLogAPI before uploading test case results - Uploads file attachments concurrently (max 10 parallel), then creates results in batches (max 50 per request)
- Fetches test cases from the run, maps parsed results to them via
junitXmlParser.ts— Parses JUnit XML viaxml2js+ Zod validation. Extracts attachments from[[ATTACHMENT|path]]markers in system-out/failure/error/skipped elements. Extracts suite-level<system-err>and empty-name<testcase>errors as run level error logs.playwrightJsonParser.ts— Parses Playwright JSON report. Supports two test case linking methods: (1) test annotations withtype: "test case"and URL description, (2) marker in test name. Handles nested suites recursively. Extracts top-levelerrors[]as run level error logs.allureParser.ts— Parses Allure JSON results directories (*-result.jsonand*-container.jsonfiles; XML/images ignored). Supports test case linking via TMS links (type: "tms") or marker in test name, maps Allure statuses to QA Sphere result statuses (unknown→open,broken→blocked), strips ANSI codes and HTML-escapes messages, and resolves attachments viaattachments[].source. UsesformatMarker()fromMarkerParser. Extracts run-level failure logs from container files by checkingbefores/aftersfixtures withfailed/brokenstatus — primarily useful for pytest (allure-junit5 and allure-playwright leave container fixtures empty).types.ts— SharedTestCaseResult,ParseResult, andAttachmentinterfaces used by both parsers.
The api command provides direct programmatic access to the QA Sphere public API: qasphere api <resource> <action> [options]. Each resource (e.g., projects, runs, test-cases) is a subcommand with its own actions. Some resources have nested subgroups (e.g., qasphere api runs test-cases list).
Architecture: The API command uses a manifest-based pattern. Each resource defines its endpoints as declarative ApiEndpointSpec objects (in src/commands/api/manifests/), and shared infrastructure handles yargs registration, validation, and execution.
Key files:
types.ts— DefinesApiEndpointSpecas a discriminated union onbodyMode('none'|'json'|'file'), plus supporting types (ApiPathParamSpec,ApiFieldSpec,ApiQueryOptionSpec,ExecuteFn)manifests/— One file per resource (e.g.,runs.ts,test-cases.ts), each exporting an array ofApiEndpointSpecobjects.manifests/index.tsaggregates all specs into a singleallSpecsarray.manifests/utils.tshas shared param definitions (e.g.,projectCodeParam)builder.ts—buildCommandsFromSpecs()builds a yargs command tree from the flat specs array, handling nested command paths automatically. Creates yargs options from path params, query options, field options, and body modeexecutor.ts—executeCommand()orchestrates execution: collects and validates path params → processes body (based onbodyModediscriminant) → collects and validates query options → connects to API (lazy env loading) → executes with error mappingmain.ts— Registers theapicommand, delegates tobuildCommandsFromSpecs()utils.ts— Shared helpers:printJson(),apiDocsEpilog(),formatApiError(),ArgumentValidationError,kebabToCamelCase(), body parsing (parseBodyInput,mergeBodyWithFields), validation (validateFieldValues,validateOptionValues), collection helpers (collectFieldValues,collectPathParamValues,collectQueryValues), andhandleApiValidationError()for reformatting API errors into CLI argument names
Important note: Online documentation is available at https://docs.qasphere.com. Most leaf pages have a markdown version available by appending .md in the URL. Use the markdown version before falling back to the original URL if the markdown version returns >= 400 status.
Key design patterns:
- Manifest-based declarations: Each endpoint is a plain object (
ApiEndpointSpec) declaring its command path, params, options, body mode, and execute function. The builder and executor handle all yargs wiring, validation, and error handling generically - Lazy env loading:
QAS_URL/QAS_TOKENare loaded only when the API is actually called (insideexecuteCommand()), so CLI validation errors are reported first - Validation flow: Path params and query options are validated against optional Zod schemas. For
jsonbody mode, individual field values are validated (with JSON parsing for complex fields), then merged with--body/--body-fileinput. API-levelRequestValidationErroris caught and reformatted into CLI argument names (e.g.,--query-plans: [0].tcaseIds: not allowed for "live" runs)
Composable fetch wrappers using higher-order functions:
utils.ts—withBaseUrl,withApiKey,withJson,withDevAuthdecorators that wrapfetch;jsonResponse<T>()for parsing responses;appendSearchParams()for building query strings;resourceIdSchemafor validating resource identifiers;printJson()for formatted JSON outputindex.ts—createApi(baseUrl, apiKey)assembles the API client from all sub-modulesschemas.ts— Shared types (ResourceId,ResultStatus,PaginatedResponse<T>,PaginatedRequest,MessageResponse),RequestValidationErrorclass,validateRequest()helper, and common Zod field definitions (sortFieldParam,sortOrderParam,pageParam,limitParam)- One sub-module per resource (e.g.,
projects.ts,runs.ts,tcases.ts,folders.ts), each exporting acreate<Resource>Api(fetcher)factory function. Each module defines Zod schemas for its request types (PascalCase, e.g.,CreateRunRequestSchema), derives TypeScript types viaz.infer, and validates inputs withvalidateRequest()inside API functions
The main createApi() composes the fetch chain: withDevAuth(withApiKey(withBaseUrl(fetch, baseUrl), apiKey)).
env.ts— LoadsQAS_TOKENandQAS_URLfrom environment variables,.env, or.qaspherecli(searched up the directory tree). OptionalQAS_DEV_AUTHadds a dev cookie via thewithDevAuthfetch decoratorconfig.ts— Constants (required Node version)misc.ts— URL parsing, template string processing ({env:VAR}, date placeholders), error handling utilities. Note: marker-related functions have been moved toMarkerParser.tsversion.ts— Reads version frompackage.jsonby traversing parent directories
Tests use Vitest with MSW (Mock Service Worker) for API mocking. Test files are in src/tests/.
result-upload.spec.ts— Integration tests for the full upload flow (JUnit, Playwright, and Allure), with MSW intercepting all API calls. Includes hyphenless and CamelCase marker tests (JUnit only)marker-parser.spec.ts— Unit tests forMarkerParser(detection, extraction, matching across all marker formats and command types)junit-xml-parsing.spec.ts— Unit tests for JUnit XML parserplaywright-json-parsing.spec.ts— Unit tests for Playwright JSON parserallure-parsing.spec.ts— Unit tests for Allure parsertemplate-string-processing.spec.ts— Unit tests for run name template processing
Test fixtures live in src/tests/fixtures/ (XML files, JSON files, and mock test case data).
Tests for the api command are organized by resource under src/tests/api/, with one spec file per action (e.g., projects/list.spec.ts, runs/create.spec.ts). Tests support both mocked and live modes.
Shared infrastructure (src/tests/api/test-helper.ts):
baseURL,token— Configured base URL and token (mocked values or real from env vars)useMockServer(...handlers)— Sets up MSW server with lifecycle hooks (before/after each test)runCli(...args)— Invokes the CLI programmatically viarun(args), captures and parses JSON output. Useful only if the command prints JSON.testfixture — Extended Vitesttestthat provides aprojectfixture (mock project in mocked mode, real project with cleanup in live mode)expectValidationError(runner, pattern)— Asserts a command exits with a validation error matching the given regex- Helper functions for live tests:
createFolder(),createTCase(),createRun()
Global setup (src/tests/global-setup.ts): Authenticates against the live API (if env vars are set) and provides a session token for test project cleanup.
Test pattern: Each spec file typically contains:
- A
describe('mocked', ...)block with MSW handlers and assertions on request headers/params - Validation error tests checking CLI argument validation
- Live tests tagged with
{ tags: ['live'] }that run against a real QA Sphere instance
Other tests:
missing-subcommand-help.spec.ts— Verifies incomplete commands (e.g.,apialone,api projectsalone) show help textapi/utils.spec.ts— Unit tests for API command utility functions
npm run test # Run all tests (mocked only by default)
npm run test:live # Run live tests only (requires env vars)Live tests require all four variables to be set; otherwise tests run in mocked mode only:
| Variable | Purpose |
|---|---|
QAS_TEST_URL |
Base URL of the QA Sphere instance |
QAS_TEST_TOKEN |
API token for authenticated API calls |
QAS_TEST_USERNAME |
Email for login endpoint (used by global setup) |
QAS_TEST_PASSWORD |
Password for login endpoint (used by global setup) |
QAS_DEV_AUTH |
(Optional) Dev auth cookie value for dev environments |
The tsconfig.json excludes src/tests from compilation output.
ESM project ("type": "module"). TypeScript compiles to build/, then ts-add-js-extension adds .js extensions to imports (required for ESM). The CLI binary is build/bin/qasphere.js.
- Linter: ESLint with typescript-eslint (config:
eslint.config.mjs) - Formatter: Prettier (config:
.prettierrc) - Pre-commit hook (Husky): runs lint-staged (Prettier + ESLint on staged files)
- Commits: Do NOT add
Co-Authored-Bylines to commit messages