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
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,11 @@ qasphere api
│ ├── list --project-code # List runs
│ ├── clone --project-code --run-id --title # Clone run
│ ├── close --project-code --run-id # Close run
│ └── test-cases
│ ├── list --project-code --run-id # List test cases in run
│ └── get --project-code --run-id --tcase-id # Get test case in run
│ ├── test-cases
│ │ ├── list --project-code --run-id # List test cases in run
│ │ └── get --project-code --run-id --tcase-id # Get test case in run
│ └── logs
│ └── create --project-code --run-id --comment # Create run log
├── settings
│ ├── list-statuses # List result statuses
│ └── update-statuses --statuses # Update custom statuses
Expand Down
8 changes: 5 additions & 3 deletions skills/qas-cli/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,11 @@ qasphere api
│ ├── list --project-code # List runs
│ ├── clone --project-code --run-id --title # Clone run
│ ├── close --project-code --run-id # Close run
│ └── test-cases
│ ├── list --project-code --run-id # List test cases in run
│ └── get --project-code --run-id --tcase-id # Get test case in run
│ ├── test-cases
│ │ ├── list --project-code --run-id # List test cases in run
│ │ └── get --project-code --run-id --tcase-id # Get test case in run
│ └── logs
│ └── create --project-code --run-id --comment # Create run log
├── settings
│ ├── list-statuses # List result statuses
│ └── update-statuses --statuses # Update custom statuses
Expand Down
59 changes: 58 additions & 1 deletion src/commands/api/manifests/runs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from 'zod'
import {
CreateRunRequestSchema,
CreateRunLogRequestSchema,
QueryPlansSchema,
CloneRunRequestSchema,
type ListRunTCasesRequest,
Expand Down Expand Up @@ -31,6 +32,7 @@ const help = {
tags: 'Comma-separated tag IDs to filter by.',
priorities: 'Comma-separated priorities to filter by (e.g., "low,high").',
include: 'Include additional fields. Use "folder" to include folder details.',
comment: 'Log message body (supports HTML).',
create: {
describe: 'Create a new test run.',
epilog: apiDocsEpilog('run', 'create-new-run'),
Expand Down Expand Up @@ -102,6 +104,21 @@ const help = {
},
],
},
logsCreate: {
describe: 'Create a log entry on a test run.',
epilog: apiDocsEpilog('run', 'create-run-log'),
examples: [
{
usage: '$0 api runs logs create --project-code PRJ --run-id 1 --comment "Deploy finished"',
description: 'Create a run log with --comment',
},
{
usage:
'$0 api runs logs create --project-code PRJ --run-id 1 --body \'{"comment": "Deploy finished"}\'',
description: 'Create a run log using --body',
},
],
},
} as const

const runIdParam = {
Expand Down Expand Up @@ -332,4 +349,44 @@ const tcasesGet: ApiEndpointSpec = {
},
}

export const runSpecs: ApiEndpointSpec[] = [create, list, clone, close, tcasesList, tcasesGet]
const logsCreate: ApiEndpointSpec = {
id: 'runs.logs.create',
commandPath: ['runs', 'logs', 'create'],
describe: help.logsCreate.describe,
bodyMode: 'json',
pathParams: [projectCodeParam, runIdParam],
fieldOptions: [
{
name: 'comment',
type: 'string',
describe: help.comment,
schema: CreateRunLogRequestSchema.shape.comment,
},
],
check: (argv) => {
return argv.body !== undefined || argv['body-file'] !== undefined || argv.comment !== undefined
? true
: 'Either --body, --body-file, or --comment is required'
},
epilog: help.logsCreate.epilog,
examples: help.logsCreate.examples,
execute: async (api, { pathParams, body }) => {
printJson(
await api.runs.createLog(
pathParams['project-code'],
pathParams['run-id'],
body as Parameters<typeof api.runs.createLog>[2]
)
)
},
}

export const runSpecs: ApiEndpointSpec[] = [
create,
list,
clone,
close,
tcasesList,
tcasesGet,
logsCreate,
]
112 changes: 112 additions & 0 deletions src/tests/api/runs/logs-create.spec.ts
Comment thread
ramilamparo marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { HttpResponse, http, type PathParams } from 'msw'
import { beforeEach, describe, expect } from 'vitest'
import type { CreateRunLogRequest } from '../../../api/runs'
import {
test,
baseURL,
token,
useMockServer,
runCli,
createFolder,
createTCase,
createRun,
expectValidationError,
testRejectsInvalidIdentifier,
testBodyInput,
} from '../test-helper'

const runCommand = <T = unknown>(...args: string[]) =>
runCli<T>('api', 'runs', 'logs', 'create', ...args)

describe('mocked', () => {
let lastBody: CreateRunLogRequest | null = null
let lastParams: PathParams = {}

useMockServer(
http.post(
`${baseURL}/api/public/v0/project/:projectCode/run/:runId/log`,
async ({ request, params }) => {
expect(request.headers.get('Authorization')).toEqual(`ApiKey ${token}`)
lastParams = params
lastBody = (await request.json()) as CreateRunLogRequest
return HttpResponse.json({ id: 'log-1' })
}
)
)

beforeEach(() => {
lastBody = null
lastParams = {}
})

test('creates a run log with --comment', async ({ project }) => {
const result = await runCommand(
'--project-code',
project.code,
'--run-id',
'42',
'--comment',
'Deploy finished'
)
expect(lastParams.projectCode).toBe(project.code)
expect(lastParams.runId).toBe('42')
expect(lastBody).toEqual({ comment: 'Deploy finished' })
expect(result).toEqual({ id: 'log-1' })
})

const validBody = { comment: 'Body comment' }

testBodyInput(
runCommand,
() => lastBody,
(h) => {
const requiredArgs = ['--project-code', 'PRJ', '--run-id', '1']
h.testInlineBody(validBody, validBody, requiredArgs)
h.testBodyFile(validBody, validBody, requiredArgs)
h.testFieldOverride({
body: validBody,
flags: ['--comment', 'Overridden'],
expectedRequest: { comment: 'Overridden' },
requiredArgs,
})
h.testInvalidJson(requiredArgs)
}
)
})

describe('validation errors', () => {
test('requires --comment, --body, or --body-file', async () => {
await expectValidationError(
() => runCommand('--project-code', 'PRJ', '--run-id', '1'),
/Either --body, --body-file, or --comment is required/
)
})

testRejectsInvalidIdentifier(runCommand, 'project-code', 'code', [
'--run-id',
'1',
'--comment',
'msg',
])
})

test('creates a run log on live server', { tags: ['live'] }, async ({ project }) => {
const folder = await createFolder(project.code)
const folderId = folder.ids[0][0]
const tcase = await createTCase(project.code, folderId)
const run = await createRun(project.code, [tcase.id])
const result = await runCli<{ id: string }>(
'api',
'runs',
'logs',
'create',
'--project-code',
project.code,
'--run-id',
String(run.id),
'--comment',
'CLI live test log'
)
expect(result).toHaveProperty('id')
expect(typeof result.id).toBe('string')
})
Loading