Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
67480e3
Add public API commands
ramilamparo Mar 18, 2026
bf38095
Move SKILL.md
ramilamparo Mar 23, 2026
0df84a4
Update @filename description
ramilamparo Mar 23, 2026
47043c7
Merge branch 'main' into dev/ram/public-api
ramilamparo Mar 23, 2026
97451cf
Fixes
ramilamparo Mar 23, 2026
aaa6e68
Prevent accidental leaks
ramilamparo Mar 23, 2026
8ac218c
Run npm audit fix
ramilamparo Mar 23, 2026
5406990
Better error message
ramilamparo Mar 23, 2026
e860c88
Tweaks
ramilamparo Mar 23, 2026
0200238
Fix live tests
ramilamparo Mar 23, 2026
e18724c
Indicate HTML support on fields
ramilamparo Mar 23, 2026
cceb40a
Fix uri encoding
ramilamparo Mar 23, 2026
f07bd52
Validate path parameters
ramilamparo Mar 24, 2026
fd92ae2
Simplify test
ramilamparo Mar 24, 2026
202372e
Merge branch 'main' into dev/ram/public-api
ramilamparo Mar 24, 2026
7dff526
Rename APIs
ramilamparo Mar 24, 2026
916f3bb
Add two-layer API validation and custom fields support
ramilamparo Mar 24, 2026
de6a14d
Use ZodError.message as the error message
ramilamparo Mar 24, 2026
49de865
Fix schemas
ramilamparo Mar 25, 2026
e484fb2
Remove schemas.ts and update claude.md
ramilamparo Mar 25, 2026
7903d05
Simplify SKILL.md
ramilamparo Mar 27, 2026
5b13cd7
Create specific validtors for different types of identifiers
ramilamparo Mar 27, 2026
38fcb79
Fix step schema
ramilamparo Mar 27, 2026
16fe454
Fix types and --include argument
ramilamparo Mar 27, 2026
c7bb61b
Address feedback
ramilamparo Mar 27, 2026
4f75a51
Fix mock test
ramilamparo Mar 27, 2026
cd58048
More fixes
ramilamparo Mar 27, 2026
ce1f029
Fix name
ramilamparo Mar 27, 2026
6aa0c5a
Fix table of contents
ramilamparo Mar 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,12 @@ jobs:

- name: Multi-node version test
run: cd mnode-test && ./docker-test.sh

- name: Run live API tests
env:
QAS_TEST_URL: ${{ secrets.QAS_TEST_URL }}
QAS_TEST_TOKEN: ${{ secrets.QAS_TEST_TOKEN }}
QAS_TEST_USERNAME: ${{ secrets.QAS_TEST_USERNAME }}
QAS_TEST_PASSWORD: ${{ secrets.QAS_TEST_PASSWORD }}
QAS_DEV_AUTH: ${{ secrets.QAS_DEV_AUTH }}
run: npm run test:live
Comment on lines +60 to +67
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Live tests are added that allows running automated tests with a real live qasphere instance. QAS_TEST_USERNAME and QAS_TEST_PASSWORD are required because we don't have a public API yet for deleting projects, which is required for clean-ups after the tests. So we use login credentials and call a non-public API instead.

84 changes: 78 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Node.js compatibility tests: `cd mnode-test && ./docker-test.sh` (requires Docke
### Entry Point & CLI Framework

- `src/bin/qasphere.ts` — Entry point (`#!/usr/bin/env node`). Validates Node version, delegates to `run()`.
- `src/commands/main.ts` — Yargs setup. Registers three commands (`junit-upload`, `playwright-json-upload`, `allure-upload`) as instances of the same `ResultUploadCommandModule` class.
- `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.
- `src/commands/resultUpload.ts` — `ResultUploadCommandModule` defines CLI options shared by both commands. Loads env vars, then delegates to `ResultUploadCommandHandler`.

### Core Upload Pipeline (src/utils/result-upload/)
Expand Down Expand Up @@ -62,24 +62,52 @@ The upload flow has two stages handled by two classes, with a shared `MarkerPars
- `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).
- `types.ts` — Shared `TestCaseResult`, `ParseResult`, and `Attachment` interfaces used by both parsers.

### API Command (src/commands/api/)

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 tcases list`).

**File structure per resource**:

```
src/commands/api/<resource>/
├── command.ts # Yargs command definitions (list, get, create, etc.) and CLI-specific Zod schemas
└── help.ts # Help text and descriptions
```

- `main.ts` — Registers all resource subcommands via `.command()`
- `utils.ts` — Shared helpers: `apiHandler<T>()` wraps handlers with lazy env loading and error handling; `printJson()` outputs formatted JSON; `parseJsonArg()` supports inline JSON or `@filename`; `parseAndValidateJsonArg()` and `validateWithSchema()` validate with Zod and produce detailed error messages; `apiDocsEpilog()` appends API doc links

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**:

- **Lazy env loading**: `QAS_URL`/`QAS_TOKEN` are loaded only when the API is actually called (via `connectApi()`), so CLI validation errors are reported first
- **JSON argument flexibility**: Complex args accept inline JSON or `@filename` references (e.g., `--query-plans @plans.json`)
- **Validation flow**: API-level Zod schemas (in `src/api/*.ts`) validate request structure and strip unknown fields. All commands catch `RequestValidationError` via `.catch(handleValidationError(buildArgumentMap([...])))` to reformat API error paths into CLI argument names (e.g., `--query-plans: [0].tcaseIds: not allowed for "live" runs`). Complex JSON args (e.g., `--query-plans`, `--body`, `--steps`) are pre-validated with `parseAndValidateJsonArg()` / `parseOptionalJsonField()` for early feedback before the API call

### API Layer (src/api/)

Composable fetch wrappers using higher-order functions:

- `utils.ts` — `withBaseUrl`, `withApiKey`, `withJson` decorators that wrap `fetch`
- `index.ts` — `createApi(baseUrl, apiKey)` assembles the API client from sub-modules
- Sub-modules: `projects.ts`, `run.ts`, `tcases.ts`, `file.ts`
- `utils.ts` — `withBaseUrl`, `withApiKey`, `withJson`, `withDevAuth` decorators that wrap `fetch`; `jsonResponse<T>()` for parsing responses; `appendSearchParams()` for building query strings
- `index.ts` — `createApi(baseUrl, apiKey)` assembles the API client from all sub-modules
- `schemas.ts` — Shared types (`ResourceId`, `ResultStatus`, `PaginatedResponse<T>`, `PaginatedRequest`, `MessageResponse`), `RequestValidationError` class, `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 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

The main `createApi()` composes the fetch chain: `withDevAuth(withApiKey(withBaseUrl(fetch, baseUrl), apiKey))`.

### Configuration (src/utils/)

- `env.ts` — Loads `QAS_TOKEN` and `QAS_URL` from environment variables, `.env`, or `.qaspherecli` (searched up the directory tree)
- `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
- `config.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 to `MarkerParser.ts`
- `version.ts` — Reads version from `package.json` by traversing parent directories

## Testing

Tests use **Vitest** with **MSW** (Mock Service Worker) for API mocking. Test files are in `src/tests/`:
Tests use **Vitest** with **MSW** (Mock Service Worker) for API mocking. Test files are in `src/tests/`.

### Upload Command 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 for `MarkerParser` (detection, extraction, matching across all marker formats and command types)
Expand All @@ -90,6 +118,50 @@ Tests use **Vitest** with **MSW** (Mock Service Worker) for API mocking. Test fi

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

### API Command Tests (src/tests/api/)

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 via `run(args)`, captures and parses JSON output. Useful only if the command prints JSON.
- `test` fixture — Extended Vitest `test` that provides a `project` fixture (mock project in mocked mode, real project with cleanup in live mode)
- 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:

1. A `describe('mocked', ...)` block with MSW handlers and assertions on request headers/params
2. Validation error tests checking CLI argument validation
3. 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., `api` alone, `api projects` alone) show help text
- `api/utils.spec.ts` — Unit tests for API command utility functions

### Running Tests

```bash
npm run test # Run all tests (mocked only by default)
npm run test:live # Run live tests only (requires env vars)
```

### Environment Variables for Live API Tests

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.

## Build
Expand Down
115 changes: 115 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@
[![license](https://img.shields.io/npm/l/qas-cli)](https://github.com/Hypersequent/qas-cli/blob/main/LICENSE)
[![CI](https://github.com/Hypersequent/qas-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/Hypersequent/qas-cli/actions/workflows/ci.yml)

## Table of Contents

- [Description](#description)
- [Installation](#installation)
- [Requirements](#requirements)
- [Via NPX](#via-npx)
- [Via NPM](#via-npm)
- [Shell Completion](#shell-completion)
- [Environment](#environment)
- [Command: `api`](#command-api)
- [API Command Tree](#api-command-tree)
- [Commands: `junit-upload`, `playwright-json-upload`, `allure-upload`](#commands-junit-upload-playwright-json-upload-allure-upload)
- [Options](#options)
- [Run Name Template Placeholders](#run-name-template-placeholders)
- [Usage Examples](#usage-examples)
- [Test Report Requirements](#test-report-requirements)
- [JUnit XML](#junit-xml)
- [Playwright JSON](#playwright-json)
- [Allure](#allure)
- [Run-Level Logs](#run-level-logs)
- [AI Agent Skill](#ai-agent-skill)
- [Development](#development-for-those-who-want-to-contribute-to-the-tool)

## Description

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.
Expand Down Expand Up @@ -34,6 +57,24 @@ Verify installation: `qasphere --version`

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

## Shell Completion

The CLI supports shell completion for commands and options. To enable it, append the completion script to your shell profile:

**Zsh:**

```bash
qasphere completion >> ~/.zshrc
```

**Bash:**

```bash
qasphere completion >> ~/.bashrc
```

Then restart your shell or source the profile (e.g., `source ~/.zshrc`). After that, pressing `Tab` will autocomplete commands and options.

## Environment

The CLI requires the following variables to be defined:
Expand All @@ -59,6 +100,70 @@ QAS_URL=https://qas.eu1.qasphere.com
# QAS_URL=https://qas.eu1.qasphere.com
```

## Command: `api`

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.

### API Command Tree

```
qasphere api <resource> <action> [options]
```

```
qasphere api
├── audit-logs
│ └── list # List audit log entries
├── custom-fields
│ └── list --project-code # List custom fields
├── files
│ └── upload --file # Upload a file attachment
├── folders
│ ├── list --project-code # List folders
│ └── bulk-create --project-code --folders # Create/update folders
├── milestones
│ ├── list --project-code # List milestones
│ └── create --project-code --title # Create milestone
├── projects
│ ├── list # List all projects
│ ├── get --project-code # Get project by code
│ └── create --code --title # Create project
├── requirements
│ └── list --project-code # List requirements
├── results
│ ├── create --project-code --run-id --tcase-id --status # Create result
│ └── batch-create --project-code --run-id --items # Batch create results
├── runs
│ ├── create --project-code --title --type --query-plans # Create run
│ ├── list --project-code # List runs
│ ├── clone --project-code --run-id --title # Clone run
│ ├── close --project-code --run-id # Close run
│ └── tcases
│ ├── list --project-code --run-id # List test cases in run
│ └── get --project-code --run-id --tcase-id # Get test case in run
├── settings
│ ├── list-statuses # List result statuses
│ └── update-statuses --statuses # Update custom statuses
├── shared-preconditions
│ ├── list --project-code # List shared preconditions
│ └── get --project-code --id # Get shared precondition
├── shared-steps
│ ├── list --project-code # List shared steps
│ └── get --project-code --id # Get shared step
├── tags
│ └── list --project-code # List tags
├── test-cases
│ ├── list --project-code # List test cases
│ ├── get --project-code --tcase-id # Get test case
│ ├── count --project-code # Count test cases
│ ├── create --project-code --body # Create test case
│ └── update --project-code --tcase-id --body # Update test case
├── test-plans
│ └── create --project-code --body # Create test plan
└── users
└── list # List all users
```

## Commands: `junit-upload`, `playwright-json-upload`, `allure-upload`

The `junit-upload`, `playwright-json-upload`, and `allure-upload` commands upload test results to QA Sphere.
Expand Down Expand Up @@ -272,6 +377,16 @@ The CLI automatically detects global or suite-level failures and uploads them as
- **Playwright JSON**: Top-level `errors` array entries (global setup/teardown failures) are extracted as run-level logs.
- **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.

## AI Agent Skill

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:

```bash
npx skills add Hypersequent/qas-cli
```

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.

## Development (for those who want to contribute to the tool)

1. Install and build: `npm install && npm run build && npm link`
Expand Down
Loading
Loading