Skip to content

Commit 503a755

Browse files
Add public API commands (#58)
* Add public API commands * Move SKILL.md * Update @filename description * Fixes * Prevent accidental leaks * Run npm audit fix * Better error message * Tweaks * Fix live tests * Indicate HTML support on fields * Fix uri encoding * Validate path parameters * Simplify test * Rename APIs * Add two-layer API validation and custom fields support - Add Zod schemas at API level for all request types with validateRequest() that throws RequestValidationError, caught by apiHandler for clean error output - Add handleValidationError() + buildArgumentMap() to map API field names to CLI argument names (e.g., title → --title) for user-friendly error messages - Move validation schemas from command-level to API-level as single source of truth; command handlers pass args directly to API functions - Add customFields, parameterValues, and filledTCaseTitleSuffixParams to test-case create/update commands with proper validation (parameterValues restricted to template type) - Fix jsonResponse null crash, expand ResultStatus with custom statuses, relax test-cases update check, extract shared superRefine for run schemas - Add tests for new fields (mocked + live) and batch-create validation errors * Use ZodError.message as the error message * Fix schemas * Remove schemas.ts and update claude.md * Simplify SKILL.md * Create specific validtors for different types of identifiers * Fix step schema * Fix types and --include argument * Address feedback * Fix mock test * More fixes * Fix name * Fix table of contents * Rewrite to manifest based pattern * Update md files * Fixes * Fixes * Fix common help text usage * Fix error code on unknown arguments, fix live test arguments * Fix spy restore * Fixes * Improve SKILL.md * Align file upload docs and tests with batch endpoint --------- Co-authored-by: Satvik Choudhary <satvikchoudhary@gmail.com>
1 parent 557d8a5 commit 503a755

91 files changed

Lines changed: 9392 additions & 1226 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,12 @@ jobs:
5656
5757
- name: Multi-node version test
5858
run: cd mnode-test && ./docker-test.sh
59+
60+
- name: Run live API tests
61+
env:
62+
QAS_TEST_URL: ${{ secrets.QAS_TEST_URL }}
63+
QAS_TEST_TOKEN: ${{ secrets.QAS_TEST_TOKEN }}
64+
QAS_TEST_USERNAME: ${{ secrets.QAS_TEST_USERNAME }}
65+
QAS_TEST_PASSWORD: ${{ secrets.QAS_TEST_PASSWORD }}
66+
QAS_DEV_AUTH: ${{ secrets.QAS_DEV_AUTH }}
67+
run: npm run test:live

CLAUDE.md

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Node.js compatibility tests: `cd mnode-test && ./docker-test.sh` (requires Docke
2929
### Entry Point & CLI Framework
3030

3131
- `src/bin/qasphere.ts` — Entry point (`#!/usr/bin/env node`). Validates Node version, delegates to `run()`.
32-
- `src/commands/main.ts` — Yargs setup. Registers three commands (`junit-upload`, `playwright-json-upload`, `allure-upload`) as instances of the same `ResultUploadCommandModule` class.
32+
- `src/commands/main.ts` — Yargs setup. Registers three upload commands (`junit-upload`, `playwright-json-upload`, `allure-upload`) as instances of `ResultUploadCommandModule`, plus the `api` command.
3333
- `src/commands/resultUpload.ts``ResultUploadCommandModule` defines CLI options shared by both commands. Loads env vars, then delegates to `ResultUploadCommandHandler`.
3434

3535
### Core Upload Pipeline (src/utils/result-upload/)
@@ -62,24 +62,52 @@ The upload flow has two stages handled by two classes, with a shared `MarkerPars
6262
- `allureParser.ts` — Parses Allure JSON results directories (`*-result.json` and `*-container.json` files; 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 via `attachments[].source`. Uses `formatMarker()` from `MarkerParser`. Extracts run-level failure logs from container files by checking `befores`/`afters` fixtures with `failed`/`broken` status — primarily useful for pytest (allure-junit5 and allure-playwright leave container fixtures empty).
6363
- `types.ts` — Shared `TestCaseResult`, `ParseResult`, and `Attachment` interfaces used by both parsers.
6464

65+
### API Command (src/commands/api/)
66+
67+
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`).
68+
69+
**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.
70+
71+
**Key files**:
72+
73+
- `types.ts` — Defines `ApiEndpointSpec` as a discriminated union on `bodyMode` (`'none'` | `'json'` | `'file'`), plus supporting types (`ApiPathParamSpec`, `ApiFieldSpec`, `ApiQueryOptionSpec`, `ExecuteFn`)
74+
- `manifests/` — One file per resource (e.g., `runs.ts`, `test-cases.ts`), each exporting an array of `ApiEndpointSpec` objects. `manifests/index.ts` aggregates all specs into a single `allSpecs` array. `manifests/utils.ts` has shared param definitions (e.g., `projectCodeParam`)
75+
- `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 mode
76+
- `executor.ts``executeCommand()` orchestrates execution: collects and validates path params → processes body (based on `bodyMode` discriminant) → collects and validates query options → connects to API (lazy env loading) → executes with error mapping
77+
- `main.ts` — Registers the `api` command, delegates to `buildCommandsFromSpecs()`
78+
- `utils.ts` — Shared helpers: `printJson()`, `apiDocsEpilog()`, `formatApiError()`, `ArgumentValidationError`, `kebabToCamelCase()`, body parsing (`parseBodyInput`, `mergeBodyWithFields`), validation (`validateFieldValues`, `validateOptionValues`), collection helpers (`collectFieldValues`, `collectPathParamValues`, `collectQueryValues`), and `handleApiValidationError()` for reformatting API errors into CLI argument names
79+
80+
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.
81+
82+
**Key design patterns**:
83+
84+
- **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
85+
- **Lazy env loading**: `QAS_URL`/`QAS_TOKEN` are loaded only when the API is actually called (inside `executeCommand()`), so CLI validation errors are reported first
86+
- **Validation flow**: Path params and query options are validated against optional Zod schemas. For `json` body mode, individual field values are validated (with JSON parsing for complex fields), then merged with `--body`/`--body-file` input. API-level `RequestValidationError` is caught and reformatted into CLI argument names (e.g., `--query-plans: [0].tcaseIds: not allowed for "live" runs`)
87+
6588
### API Layer (src/api/)
6689

6790
Composable fetch wrappers using higher-order functions:
6891

69-
- `utils.ts``withBaseUrl`, `withApiKey`, `withJson` decorators that wrap `fetch`
70-
- `index.ts``createApi(baseUrl, apiKey)` assembles the API client from sub-modules
71-
- Sub-modules: `projects.ts`, `run.ts`, `tcases.ts`, `file.ts`
92+
- `utils.ts``withBaseUrl`, `withApiKey`, `withJson`, `withDevAuth` decorators that wrap `fetch`; `jsonResponse<T>()` for parsing responses; `appendSearchParams()` for building query strings; `resourceIdSchema` for validating resource identifiers; `printJson()` for formatted JSON output
93+
- `index.ts``createApi(baseUrl, apiKey)` assembles the API client from all sub-modules
94+
- `schemas.ts` — Shared types (`ResourceId`, `ResultStatus`, `PaginatedResponse<T>`, `PaginatedRequest`, `MessageResponse`), `RequestValidationError` class, `validateRequest()` helper, and common Zod field definitions (`sortFieldParam`, `sortOrderParam`, `pageParam`, `limitParam`)
95+
- One sub-module per resource (e.g., `projects.ts`, `runs.ts`, `tcases.ts`, `folders.ts`), each exporting a `create<Resource>Api(fetcher)` factory function. Each module defines Zod schemas for its request types (PascalCase, e.g., `CreateRunRequestSchema`), derives TypeScript types via `z.infer`, and validates inputs with `validateRequest()` inside API functions
96+
97+
The main `createApi()` composes the fetch chain: `withDevAuth(withApiKey(withBaseUrl(fetch, baseUrl), apiKey))`.
7298

7399
### Configuration (src/utils/)
74100

75-
- `env.ts` — Loads `QAS_TOKEN` and `QAS_URL` from environment variables, `.env`, or `.qaspherecli` (searched up the directory tree)
101+
- `env.ts` — Loads `QAS_TOKEN` and `QAS_URL` from environment variables, `.env`, or `.qaspherecli` (searched up the directory tree). Optional `QAS_DEV_AUTH` adds a dev cookie via the `withDevAuth` fetch decorator
76102
- `config.ts` — Constants (required Node version)
77103
- `misc.ts` — URL parsing, template string processing (`{env:VAR}`, date placeholders), error handling utilities. Note: marker-related functions have been moved to `MarkerParser.ts`
78104
- `version.ts` — Reads version from `package.json` by traversing parent directories
79105

80106
## Testing
81107

82-
Tests use **Vitest** with **MSW** (Mock Service Worker) for API mocking. Test files are in `src/tests/`:
108+
Tests use **Vitest** with **MSW** (Mock Service Worker) for API mocking. Test files are in `src/tests/`.
109+
110+
### Upload Command Tests
83111

84112
- `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)
85113
- `marker-parser.spec.ts` — Unit tests for `MarkerParser` (detection, extraction, matching across all marker formats and command types)
@@ -90,6 +118,51 @@ Tests use **Vitest** with **MSW** (Mock Service Worker) for API mocking. Test fi
90118

91119
Test fixtures live in `src/tests/fixtures/` (XML files, JSON files, and mock test case data).
92120

121+
### API Command Tests (src/tests/api/)
122+
123+
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.
124+
125+
**Shared infrastructure** (`src/tests/api/test-helper.ts`):
126+
127+
- `baseURL`, `token` — Configured base URL and token (mocked values or real from env vars)
128+
- `useMockServer(...handlers)` — Sets up MSW server with lifecycle hooks (before/after each test)
129+
- `runCli(...args)` — Invokes the CLI programmatically via `run(args)`, captures and parses JSON output. Useful only if the command prints JSON.
130+
- `test` fixture — Extended Vitest `test` that provides a `project` fixture (mock project in mocked mode, real project with cleanup in live mode)
131+
- `expectValidationError(runner, pattern)` — Asserts a command exits with a validation error matching the given regex
132+
- Helper functions for live tests: `createFolder()`, `createTCase()`, `createRun()`
133+
134+
**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.
135+
136+
**Test pattern**: Each spec file typically contains:
137+
138+
1. A `describe('mocked', ...)` block with MSW handlers and assertions on request headers/params
139+
2. Validation error tests checking CLI argument validation
140+
3. Live tests tagged with `{ tags: ['live'] }` that run against a real QA Sphere instance
141+
142+
**Other tests**:
143+
144+
- `missing-subcommand-help.spec.ts` — Verifies incomplete commands (e.g., `api` alone, `api projects` alone) show help text
145+
- `api/utils.spec.ts` — Unit tests for API command utility functions
146+
147+
### Running Tests
148+
149+
```bash
150+
npm run test # Run all tests (mocked only by default)
151+
npm run test:live # Run live tests only (requires env vars)
152+
```
153+
154+
### Environment Variables for Live API Tests
155+
156+
Live tests require all four variables to be set; otherwise tests run in mocked mode only:
157+
158+
| Variable | Purpose |
159+
| ------------------- | ----------------------------------------------------- |
160+
| `QAS_TEST_URL` | Base URL of the QA Sphere instance |
161+
| `QAS_TEST_TOKEN` | API token for authenticated API calls |
162+
| `QAS_TEST_USERNAME` | Email for login endpoint (used by global setup) |
163+
| `QAS_TEST_PASSWORD` | Password for login endpoint (used by global setup) |
164+
| `QAS_DEV_AUTH` | (Optional) Dev auth cookie value for dev environments |
165+
93166
The `tsconfig.json` excludes `src/tests` from compilation output.
94167

95168
## Build

README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,27 @@
44
[![license](https://img.shields.io/npm/l/qas-cli)](https://github.com/Hypersequent/qas-cli/blob/main/LICENSE)
55
[![CI](https://github.com/Hypersequent/qas-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/Hypersequent/qas-cli/actions/workflows/ci.yml)
66

7+
## Table of Contents
8+
9+
- [Description](#description)
10+
- [Installation](#installation)
11+
- [Requirements](#requirements)
12+
- [Via NPX](#via-npx)
13+
- [Via NPM](#via-npm)
14+
- [Shell Completion](#shell-completion)
15+
- [Environment](#environment)
16+
- [Command: `api`](#command-api)
17+
- [API Command Tree](#api-command-tree)
18+
- [Commands: `junit-upload`, `playwright-json-upload`, `allure-upload`](#commands-junit-upload-playwright-json-upload-allure-upload)
19+
- [Options](#options)
20+
- [Run Name Template Placeholders](#run-name-template-placeholders)
21+
- [Usage Examples](#usage-examples)
22+
- [Test Report Requirements](#test-report-requirements)
23+
- [JUnit XML](#junit-xml)
24+
- [Playwright JSON](#playwright-json)
25+
- [Allure](#allure)
26+
- [Development](#development-for-those-who-want-to-contribute-to-the-tool)
27+
728
## Description
829

930
The QAS CLI is a command-line tool for submitting your test automation results to [QA Sphere](https://qasphere.com/). It provides the most efficient way to collect and report test results from your test automation workflow, CI/CD pipeline, and build servers.
@@ -34,6 +55,24 @@ Verify installation: `qasphere --version`
3455

3556
**Update:** Run `npm update -g qas-cli` to get the latest version.
3657

58+
## Shell Completion
59+
60+
The CLI supports shell completion for commands and options. To enable it, append the completion script to your shell profile:
61+
62+
**Zsh:**
63+
64+
```bash
65+
qasphere completion >> ~/.zshrc
66+
```
67+
68+
**Bash:**
69+
70+
```bash
71+
qasphere completion >> ~/.bashrc
72+
```
73+
74+
Then restart your shell or source the profile (e.g., `source ~/.zshrc`). After that, pressing `Tab` will autocomplete commands and options.
75+
3776
## Environment
3877

3978
The CLI requires the following variables to be defined:
@@ -59,6 +98,72 @@ QAS_URL=https://qas.eu1.qasphere.com
5998
# QAS_URL=https://qas.eu1.qasphere.com
6099
```
61100

101+
## Command: `api`
102+
103+
The `api` command provides direct access to the QA Sphere public API from the command line. Outputting JSON to stdout for easy scripting and piping.
104+
105+
### API Command Tree
106+
107+
```
108+
qasphere api <resource> <action> [options]
109+
```
110+
111+
```
112+
qasphere api
113+
├── audit-logs
114+
│ └── list # List audit log entries
115+
├── custom-fields
116+
│ └── list --project-code # List custom fields
117+
├── files
118+
│ └── upload --file # Upload a file attachment
119+
├── folders
120+
│ ├── list --project-code # List folders
121+
│ └── bulk-create --project-code --folders # Create/update folders
122+
├── milestones
123+
│ ├── list --project-code # List milestones
124+
│ └── create --project-code --title # Create milestone
125+
├── projects
126+
│ ├── list # List all projects
127+
│ ├── get --project-code # Get project by code
128+
│ └── create --code --title # Create project
129+
├── requirements
130+
│ └── list --project-code # List requirements
131+
├── results
132+
│ ├── create --project-code --run-id --tcase-id --status # Create result
133+
│ └── batch-create --project-code --run-id --items # Batch create results
134+
├── runs
135+
│ ├── create --project-code --title --type --query-plans # Create run
136+
│ ├── list --project-code # List runs
137+
│ ├── clone --project-code --run-id --title # Clone run
138+
│ ├── close --project-code --run-id # Close run
139+
│ └── test-cases
140+
│ ├── list --project-code --run-id # List test cases in run
141+
│ └── get --project-code --run-id --tcase-id # Get test case in run
142+
├── settings
143+
│ ├── list-statuses # List result statuses
144+
│ └── update-statuses --statuses # Update custom statuses
145+
├── shared-preconditions
146+
│ ├── list --project-code # List shared preconditions
147+
│ └── get --project-code --id # Get shared precondition
148+
├── shared-steps
149+
│ ├── list --project-code # List shared steps
150+
│ └── get --project-code --id # Get shared step
151+
├── tags
152+
│ └── list --project-code # List tags
153+
├── test-cases
154+
│ ├── list --project-code # List test cases
155+
│ ├── get --project-code --tcase-id # Get test case
156+
│ ├── count --project-code # Count test cases
157+
│ ├── create --project-code --body # Create test case
158+
│ └── update --project-code --tcase-id --body # Update test case
159+
├── test-plans
160+
│ └── create --project-code --body # Create test plan
161+
└── users
162+
└── list # List all users
163+
```
164+
165+
Note: `qasphere api files upload --file ...` uses the public batch upload endpoint internally and returns the first uploaded file from that response.
166+
62167
## Commands: `junit-upload`, `playwright-json-upload`, `allure-upload`
63168

64169
The `junit-upload`, `playwright-json-upload`, and `allure-upload` commands upload test results to QA Sphere.
@@ -272,6 +377,16 @@ The CLI automatically detects global or suite-level failures and uploads them as
272377
- **Playwright JSON**: Top-level `errors` array entries (global setup/teardown failures) are extracted as run-level logs.
273378
- **Allure**: Failed or broken `befores`/`afters` fixtures in `*-container.json` files (e.g., session/module-level setup/teardown failures from pytest) are extracted as run-level logs.
274379
380+
## AI Agent Skill
381+
382+
qas-cli includes a [SKILL.md](./SKILL.md) file that enables AI coding agents (e.g., Claude Code, Cursor) to use the CLI effectively. To add this skill to your agent:
383+
384+
```bash
385+
npx skills add Hypersequent/qas-cli
386+
```
387+
388+
The skill provides the agent with full documentation of the CLI commands, options, and conventions. See [skills](https://github.com/vercel-labs/skills) for more details.
389+
275390
## Development (for those who want to contribute to the tool)
276391
277392
1. Install and build: `npm install && npm run build && npm link`

0 commit comments

Comments
 (0)