Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,34 +44,34 @@
},
"homepage": "https://github.com/Hypersequent/qas-cli#readme",
"devDependencies": {
"@eslint/js": "^9.25.1",
"@eslint/js": "^10.0.1",
"@types/escape-html": "^1.0.4",
"@types/node": "^22.0.0",
"@types/semver": "^7.7.0",
"@types/xml2js": "^0.4.14",
"@types/yargs": "^17.0.33",
"eslint": "^9.25.1",
"eslint": "^10.5.0",
"eslint-config-prettier": "^10.1.8",
"globals": "^16.0.0",
"husky": "^9.1.7",
"lint-staged": "^15.2.11",
"msw": "^2.7.5",
"prettier": "^3.4.2",
"ts-add-js-extension": "^1.6.6",
"typescript": "^5.8.3",
"typescript-eslint": "^8.31.1",
"typescript": "^6.0.3",
"typescript-eslint": "^8.61.1",
"vitest": "^4.1.0"
},
"dependencies": {
"@napi-rs/keyring": "^1.2.0",
"chalk": "^5.4.1",
"dotenv": "^16.5.0",
"dotenv": "^17.4.2",
"escape-html": "^1.0.3",
"semver": "^7.7.1",
"strip-ansi": "^7.1.2",
"xml2js": "^0.6.2",
"yargs": "^17.7.2",
"zod": "^3.24.3"
"yargs": "^18.0.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Raise Node floor before shipping yargs 18

With this dependency bump, the CLI still advertises support for Node >=22.0.0 in package.json and REQUIRED_NODE_VERSION, but the resolved yargs@18.0.0 in pnpm-lock.yaml declares engines: {node: ^20.19.0 || ^22.12.0 || >=23}. Users on Node 22.0–22.11 are therefore inside our supported range but outside a production dependency's supported range; engine-strict installs can reject the package, and the bin imports yargs before our version warning can run. Either keep yargs 17 or raise the package/config minimum to at least 22.12.0.

Useful? React with 👍 / 👎.

"zod": "^4.4.3"
},
"bin": {
"qasphere": "build/bin/qasphere.js"
Expand Down
1,458 changes: 686 additions & 772 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/commands/api/manifests/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const upload: ApiEndpointSpec = {
execute: async (api, { body }) => {
const filePath = body as string
const filename = basename(filePath)
const blob = new Blob([await readFile(filePath)])
const blob = new Blob([new Uint8Array(await readFile(filePath))])
const [result] = await api.files.upload([{ blob, filename }])
printJson(result)
},
Expand Down
5 changes: 3 additions & 2 deletions src/commands/api/manifests/runs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import { printJson, apiDocsEpilog, kebabToCamelCaseKeys } from '../utils'
import { commonHelp, projectCodeParam } from './utils'
import type { ApiEndpointSpec } from '../types'

// CreateRunRequestSchema uses .superRefine() → ZodEffects, so extract inner ZodObject
const CreateRunShape = CreateRunRequestSchema.sourceType().shape
// CreateRunRequestSchema attaches a refinement via .superRefine(), but in Zod 4
// the result stays a ZodObject, so .shape is accessed directly.
const CreateRunShape = CreateRunRequestSchema.shape

const help = {
...commonHelp,
Expand Down
5 changes: 3 additions & 2 deletions src/commands/api/manifests/test-cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import { printJson, apiDocsEpilog, kebabToCamelCaseKeys } from '../utils'
import { commonHelp, projectCodeParam } from './utils'
import type { ApiEndpointSpec } from '../types'

// CreateTCaseRequestSchema uses .superRefine() → ZodEffects, so extract inner ZodObject
const CreateTCaseShape = CreateTCaseRequestSchema.sourceType().shape
// CreateTCaseRequestSchema attaches a refinement via .superRefine(), but in Zod 4
// the result stays a ZodObject, so .shape is accessed directly.
const CreateTCaseShape = CreateTCaseRequestSchema.shape

const help = {
...commonHelp,
Expand Down
10 changes: 7 additions & 3 deletions src/commands/api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ function parseJsonFieldValue(value: unknown, fieldName: string): unknown {
return JSON.parse(value)
} catch (e) {
const msg = e instanceof Error ? e.message : String(e)
throw new Error(`Failed to parse --${fieldName} as JSON: ${msg}`)
throw new Error(`Failed to parse --${fieldName} as JSON: ${msg}`, { cause: e })
}
}

Expand Down Expand Up @@ -205,7 +205,8 @@ export function parseBodyInput(args: Record<string, unknown>): unknown {
`Failed to parse --body as JSON: ${errorMessage}\n` +
` Provide valid inline JSON or use --body-file to read from a file.\n` +
` Inline example: --body '{"title": "Test"}'\n` +
` File example: --body-file body.json`
` File example: --body-file body.json`,
{ cause: e }
)
}
}
Expand All @@ -219,7 +220,10 @@ export function parseBodyInput(args: Record<string, unknown>): unknown {
return JSON.parse(content)
} catch (e) {
const errorMessage = e instanceof Error ? e.message : String(e)
throw new Error(`Failed to parse JSON from file ${bodyFile} for --body-file: ${errorMessage}`)
throw new Error(
`Failed to parse JSON from file ${bodyFile} for --body-file: ${errorMessage}`,
{ cause: e }
)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/tests/api/audit-logs/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ describe('validation errors', () => {
test('rejects --after -1', async () => {
await expectValidationError(
() => runCommand('--after', '-1'),
/--after.*must be greater than or equal to 0/i
/--after.*expected number to be >=0/i
)
})

test('rejects --count 0', async () => {
await expectValidationError(
() => runCommand('--count', '0'),
/--count.*must be greater than 0/i
/--count.*expected number to be >0/i
)
})
})
6 changes: 3 additions & 3 deletions src/tests/api/folders/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,21 @@ describe('validation errors', () => {
test('rejects --page with non-integer', async () => {
await expectValidationError(
() => runCommand('--project-code', 'PRJ', '--page', '1.5'),
/--page.*integer/i
/--page.*expected int/i
)
})

test('rejects --offset -1', async () => {
await expectValidationError(
() => runCommand('--project-code', 'PRJ', '--offset', '-1'),
/--offset.*greater than or equal to 0/i
/--offset.*expected number to be >=0/i
)
})

test('rejects --limit -1', async () => {
await expectValidationError(
() => runCommand('--project-code', 'PRJ', '--limit', '-1'),
/--limit.*greater than or equal to 0/i
/--limit.*expected number to be >=0/i
)
})
})
Expand Down
2 changes: 1 addition & 1 deletion src/tests/api/projects/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ describe('validation errors', () => {
'--links',
'[{"title": "Docs", "url": "https://example.com"}]'
),
/--links.*Required/
/--links.*received undefined/
)
})

Expand Down
4 changes: 2 additions & 2 deletions src/tests/api/results/batch-create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ describe('validation errors', () => {
'--items',
JSON.stringify([{ tcaseId: 'tc1', status: 'invalid-status' }])
),
/Invalid enum value/
/Invalid option/
)
})

Expand All @@ -129,7 +129,7 @@ describe('validation errors', () => {
'--items',
JSON.stringify([{ status: 'passed' }])
),
/tcaseId: Required/
/tcaseId.*expected string, received undefined/
)
})

Expand Down
2 changes: 1 addition & 1 deletion src/tests/api/results/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ describe('mocked', () => {
requiredArgs,
})
h.testInvalidJson(requiredArgs)
h.testInvalidBody({ status: 'invalid-status' }, /Invalid enum value/, requiredArgs)
h.testInvalidBody({ status: 'invalid-status' }, /Invalid option/, requiredArgs)
}
)
})
Expand Down
4 changes: 2 additions & 2 deletions src/tests/api/runs/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ describe('validation errors', () => {
'--query-plans',
'[{"priorities": ["critical"]}]'
),
/Invalid enum value.*Expected 'low' \| 'medium' \| 'high'/
/Invalid option.*expected one of "low"\|"medium"\|"high"/
)
})

Expand Down Expand Up @@ -388,7 +388,7 @@ describe('validation errors', () => {
'--query-plans',
'[{"folderIds": [1.5]}]'
),
/integer/
/expected int/
)
})
})
Expand Down
8 changes: 4 additions & 4 deletions src/tests/api/test-cases/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,28 +76,28 @@ describe('validation errors', () => {
test('rejects --page 0', async () => {
await expectValidationError(
() => runCommand('--project-code', 'PRJ', '--page', '0'),
/--page.*must be greater than 0/i
/--page.*expected number to be >0/i
)
})

test('rejects --page -1', async () => {
await expectValidationError(
() => runCommand('--project-code', 'PRJ', '--page', '-1'),
/--page.*must be greater than 0/i
/--page.*expected number to be >0/i
)
})

test('rejects --offset -1', async () => {
await expectValidationError(
() => runCommand('--project-code', 'PRJ', '--offset', '-1'),
/--offset.*greater than or equal to 0/i
/--offset.*expected number to be >=0/i
)
})

test('rejects --limit -1', async () => {
await expectValidationError(
() => runCommand('--project-code', 'PRJ', '--limit', '-1'),
/--limit.*greater than or equal to 0/i
/--limit.*expected number to be >=0/i
)
})

Expand Down
4 changes: 2 additions & 2 deletions src/utils/result-upload/ResultUploadCommandHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ export class ResultUploadCommandHandler {
}

let fileResults = await this.parseFiles()
let projectCode = ''
let runId = 0
let projectCode: string
let runId: number

if ('runUrl' in this.args && this.args.runUrl) {
// Handle existing run URL
Expand Down
9 changes: 6 additions & 3 deletions src/utils/result-upload/parsers/allureParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ export const parseAllureResults: Parser = async (
containerFiles = allFiles.filter((f) => f.endsWith('-container.json'))
} catch (error) {
throw new Error(
`Failed to read Allure results directory "${resultsDirectory}": ${getErrorMessage(error)}`
`Failed to read Allure results directory "${resultsDirectory}": ${getErrorMessage(error)}`,
{ cause: error }
)
}

Expand All @@ -146,7 +147,8 @@ export const parseAllureResults: Parser = async (
}

throw new Error(
`Failed to parse Allure result file "${resultFilePath}": ${getErrorMessage(error)}`
`Failed to parse Allure result file "${resultFilePath}": ${getErrorMessage(error)}`,
{ cause: error }
)
}

Expand Down Expand Up @@ -305,7 +307,8 @@ const extractRunFailureLogs = (
continue
}
throw new Error(
`Failed to parse Allure container file "${filePath}": ${getErrorMessage(error)}`
`Failed to parse Allure container file "${filePath}": ${getErrorMessage(error)}`,
{ cause: error }
)
}

Expand Down
2 changes: 1 addition & 1 deletion src/utils/result-upload/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const getFile = async (filePath: string, basePath?: string): Promise<Buffer> =>
typeof e.code === 'string' &&
e.code === 'ENOENT'
) {
throw new Error(`Attachment not found: "${filePath}"`)
throw new Error(`Attachment not found: "${filePath}"`, { cause: e })
}
throw e
}
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"skipLibCheck": true,
"outDir": "./build",
"rootDir": "./src",
"declaration": true,
"declarationMap": true
},
Expand Down