diff --git a/packages/cli/src/commands/__tests__/test-sessions-get.spec.ts b/packages/cli/src/commands/__tests__/test-sessions-get.spec.ts index f9fd449dd..c7807cae7 100644 --- a/packages/cli/src/commands/__tests__/test-sessions-get.spec.ts +++ b/packages/cli/src/commands/__tests__/test-sessions-get.spec.ts @@ -1,9 +1,10 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' +import type { CheckResult } from '../../rest/check-results.js' import type { TestSessionErrorGroup } from '../../rest/test-session-error-groups.js' import type { TestSessionDetail } from '../../rest/test-sessions.js' vi.mock('../../rest/api.js', () => ({ - testSessions: { get: vi.fn(), pollUntilComplete: vi.fn() }, + testSessions: { get: vi.fn(), getResult: vi.fn(), pollUntilComplete: vi.fn() }, testSessionErrorGroups: { get: vi.fn() }, })) @@ -51,9 +52,54 @@ const testSessionErrorGroup: TestSessionErrorGroup = { archivedUntilNextEvent: false, } +const testSessionResult: CheckResult = { + id: '42406a0f-5864-4a26-9884-7c5d1be15bc2', + checkId: 'check-1', + name: 'API smoke', + hasFailures: true, + hasErrors: false, + isDegraded: false, + overMaxResponseTime: false, + runLocation: 'eu-west-1', + startedAt: '2026-05-20T08:00:00.000Z', + stoppedAt: '2026-05-20T08:02:03.456Z', + created_at: '2026-05-20T08:02:03.456Z', + responseTime: 1234, + checkRunId: 42, + attempts: 1, + resultType: 'FINAL', + apiCheckResult: { + assertions: [ + { source: 'STATUS_CODE', comparison: 'EQUALS', target: 200 }, + ], + request: { + method: 'GET', + url: 'https://api.example.com/health', + data: '', + headers: {}, + params: {}, + }, + response: { + status: 500, + statusText: '500 Internal Server Error', + body: '{"ok":false}', + headers: {}, + timings: null, + timingPhases: null, + }, + requestError: null, + jobLog: null, + jobAssets: null, + pcapDataUrl: null, + }, + browserCheckResult: null, + multiStepCheckResult: null, + agenticCheckResult: null, +} + function createCommandContext (parsed: unknown) { const logged: string[] = [] - return { + return Object.assign(Object.create(TestSessionsGet.prototype), { parse: vi.fn().mockResolvedValue(parsed), log: vi.fn((msg?: string) => { if (msg) logged.push(msg) @@ -66,7 +112,7 @@ function createCommandContext (parsed: unknown) { actionFailure: vi.fn(), }, logged, - } + }) } describe('test-sessions get command', () => { @@ -74,6 +120,7 @@ describe('test-sessions get command', () => { vi.clearAllMocks() process.exitCode = undefined vi.mocked(api.testSessions.get).mockResolvedValue({ data: testSession } as any) + vi.mocked(api.testSessions.getResult).mockResolvedValue({ data: testSessionResult } as any) vi.mocked(api.testSessions.pollUntilComplete).mockResolvedValue(testSession as any) vi.mocked(api.testSessionErrorGroups.get).mockResolvedValue({ data: testSessionErrorGroup } as any) }) @@ -128,6 +175,50 @@ describe('test-sessions get command', () => { expect(ctx.style.actionStart).not.toHaveBeenCalled() }) + it('fetches and renders one test session result detail', async () => { + const ctx = createCommandContext({ + args: { id: testSession.testSessionId }, + flags: { output: 'detail', result: testSessionResult.id }, + }) + + await TestSessionsGet.prototype.run.call(ctx as any) + + expect(api.testSessions.get).not.toHaveBeenCalled() + expect(api.testSessions.getResult).toHaveBeenCalledWith(testSession.testSessionId, testSessionResult.id) + expect(ctx.logged[0]).toContain('API smoke') + expect(ctx.logged[0]).toContain('REQUEST') + expect(ctx.logged[0]).toContain('checkly test-sessions get 8166fa86-c9b4-4162-8541-d380c6c212d8') + expect(ctx.logged[0]).toContain('checkly test-sessions list') + }) + + it('watches completion before fetching one test session result detail', async () => { + const ctx = createCommandContext({ + args: { id: testSession.testSessionId }, + flags: { output: 'detail', result: testSessionResult.id, watch: true }, + }) + + await TestSessionsGet.prototype.run.call(ctx as any) + + expect(api.testSessions.get).not.toHaveBeenCalled() + expect(api.testSessions.pollUntilComplete).toHaveBeenCalledWith(testSession.testSessionId) + expect(api.testSessions.getResult).toHaveBeenCalledWith(testSession.testSessionId, testSessionResult.id) + expect(ctx.style.actionStart).toHaveBeenCalledWith('Watching test session until completion...') + expect(ctx.style.actionSuccess).toHaveBeenCalled() + expect(ctx.logged[0]).toContain('API smoke') + }) + + it('returns raw test session result response for json drilldown output', async () => { + const ctx = createCommandContext({ + args: { id: testSession.testSessionId }, + flags: { output: 'json', result: testSessionResult.id }, + }) + + await TestSessionsGet.prototype.run.call(ctx as any) + + expect(api.testSessions.get).not.toHaveBeenCalled() + expect(JSON.parse(ctx.logged[0])).toEqual(testSessionResult) + }) + it('fetches and renders one test session error group detail', async () => { const ctx = createCommandContext({ args: { id: testSession.testSessionId }, diff --git a/packages/cli/src/commands/checks/get.ts b/packages/cli/src/commands/checks/get.ts index 6180580df..b952b0848 100644 --- a/packages/cli/src/commands/checks/get.ts +++ b/packages/cli/src/commands/checks/get.ts @@ -4,13 +4,19 @@ import { AuthCommand } from '../authCommand.js' import { outputFlag } from '../../helpers/flags.js' import * as api from '../../rest/api.js' import type { CheckWithStatus } from '../../formatters/checks.js' -import { type OutputFormat, stripAnsi, formatDate } from '../../formatters/render.js' +import { + type OutputFormat, + type CommandHint, + stripAnsi, + formatDate, + renderCommandHints, +} from '../../formatters/render.js' import { formatCheckDetail, formatResults, formatErrorGroups, } from '../../formatters/checks.js' -import { formatResultDetail } from '../../formatters/check-result-detail.js' +import { formatResultDetailWithNavigation } from '../../formatters/check-result-detail.js' import { formatRcaDetail, formatRcaHint, transformErrorGroupForJson } from '../../formatters/rca.js' import { quickRangeValues, type QuickRange, type GroupBy } from '../../rest/analytics.js' import { formatAnalyticsSection } from '../../formatters/analytics.js' @@ -162,21 +168,23 @@ export default class ChecksGet extends AuthCommand { // Navigation hints output.push('') + const hints: CommandHint[] = [] if (errorGroups.length > 0) { const firstActive = errorGroups.find(eg => !eg.archivedUntilNextEvent) if (firstActive) { - output.push(` ${chalk.dim('View error:')} checkly checks get ${args.id} --error-group ${firstActive.id}`) + hints.push({ label: 'View error', command: `checkly checks get ${args.id} --error-group ${firstActive.id}` }) } } if (results.length > 0) { - output.push(` ${chalk.dim('View result:')} checkly checks get ${args.id} --result ${results[0].id}`) + hints.push({ label: 'View result', command: `checkly checks get ${args.id} --result ${results[0].id}` }) } if (nextId) { - output.push(` ${chalk.dim('More results:')} checkly checks get ${args.id} --results-cursor ${nextId}`) + hints.push({ label: 'More results', command: `checkly checks get ${args.id} --results-cursor ${nextId}` }) } - output.push(` ${chalk.dim('Change range:')} checkly checks get ${args.id} --stats-range last7Days`) - output.push(` ${chalk.dim('By region:')} checkly checks get ${args.id} --group-by location`) - output.push(` ${chalk.dim('Back to list:')} checkly checks list`) + hints.push({ label: 'Change range', command: `checkly checks get ${args.id} --stats-range last7Days` }) + hints.push({ label: 'By region', command: `checkly checks get ${args.id} --group-by location` }) + hints.push({ label: 'Back to list', command: 'checkly checks list' }) + output.push(renderCommandHints(hints, { gap: 3 })) this.log(output.join('\n')) } catch (err: any) { @@ -267,8 +275,10 @@ export default class ChecksGet extends AuthCommand { } output.push('') - output.push(` ${chalk.dim('Back to check:')} checkly checks get ${checkId}`) - output.push(` ${chalk.dim('Back to list:')} checkly checks list`) + output.push(renderCommandHints([ + { label: 'Back to check', command: `checkly checks get ${checkId}` }, + { label: 'Back to list', command: 'checkly checks list' }, + ])) this.log(output.join('\n')) } @@ -283,14 +293,9 @@ export default class ChecksGet extends AuthCommand { const fmt: OutputFormat = outputFormat === 'md' ? 'md' : 'terminal' - const output: string[] = [] - output.push(formatResultDetail(result, fmt)) - - // Navigation hints - output.push('') - output.push(` ${chalk.dim('Back to check:')} checkly checks get ${checkId}`) - output.push(` ${chalk.dim('Back to list:')} checkly checks list`) - - this.log(output.join('\n')) + this.log(formatResultDetailWithNavigation(result, fmt, [ + { label: 'Back to check', command: `checkly checks get ${checkId}` }, + { label: 'Back to list', command: 'checkly checks list' }, + ])) } } diff --git a/packages/cli/src/commands/test-sessions/get.ts b/packages/cli/src/commands/test-sessions/get.ts index ed8af222e..dbf7e5029 100644 --- a/packages/cli/src/commands/test-sessions/get.ts +++ b/packages/cli/src/commands/test-sessions/get.ts @@ -7,6 +7,7 @@ import { formatTestSessionErrorGroupDetail, uniqueErrorGroupIds, } from '../../formatters/test-sessions.js' +import { formatResultDetailWithNavigation } from '../../formatters/check-result-detail.js' import { type OutputFormat } from '../../formatters/render.js' import type { TestSessionDetail } from '../../rest/test-sessions.js' @@ -24,6 +25,10 @@ export default class TestSessionsGet extends AuthCommand { } static flags = { + 'result': Flags.string({ + char: 'r', + description: 'Show details for a specific test session result ID.', + }), 'error-group': Flags.string({ description: 'Show details for a test session error group ID from this session.', }), @@ -48,29 +53,12 @@ export default class TestSessionsGet extends AuthCommand { this.style.outputFormat = flags.output try { - let testSession: TestSessionDetail - - if (flags.watch) { - const showAction = flags.output === 'detail' - if (showAction) { - this.style.actionStart('Watching test session until completion...') - } - try { - testSession = await api.testSessions.pollUntilComplete(args.id) - if (showAction) { - this.style.actionSuccess() - } - } catch (err) { - if (showAction) { - this.style.actionFailure() - } - throw err - } - } else { - const { data } = await api.testSessions.get(args.id) - testSession = data + if (flags.result) { + return await this.showResultDetail(args.id, flags.result, flags.output ?? 'detail', flags.watch) } + const testSession = await this.getTestSession(args.id, flags.watch, flags.output === 'detail') + if (flags['error-group']) { const errorGroupIds = uniqueErrorGroupIds(testSession) @@ -113,4 +101,52 @@ export default class TestSessionsGet extends AuthCommand { process.exitCode = 1 } } + + private async getTestSession (id: string, watch: boolean, showAction: boolean): Promise { + if (!watch) { + const { data } = await api.testSessions.get(id) + return data + } + + if (showAction) { + this.style.actionStart('Watching test session until completion...') + } + + try { + const testSession = await api.testSessions.pollUntilComplete(id) + if (showAction) { + this.style.actionSuccess() + } + return testSession + } catch (err) { + if (showAction) { + this.style.actionFailure() + } + throw err + } + } + + private async showResultDetail ( + testSessionId: string, + resultId: string, + outputFormat: string, + watch: boolean, + ): Promise { + if (watch) { + await this.getTestSession(testSessionId, true, outputFormat === 'detail') + } + + const { data: result } = await api.testSessions.getResult(testSessionId, resultId) + + if (outputFormat === 'json') { + this.log(JSON.stringify(result, null, 2)) + return + } + + const fmt: OutputFormat = outputFormat === 'md' ? 'md' : 'terminal' + this.log(formatResultDetailWithNavigation(result, fmt, [ + { label: 'Back to session', command: `checkly test-sessions get ${testSessionId}` }, + { label: 'Back to list', command: 'checkly test-sessions list' }, + ])) + } } diff --git a/packages/cli/src/formatters/account-members.ts b/packages/cli/src/formatters/account-members.ts index ecba2030c..9019c7f0b 100644 --- a/packages/cli/src/formatters/account-members.ts +++ b/packages/cli/src/formatters/account-members.ts @@ -4,6 +4,7 @@ import { type ColumnDef, type OutputFormat, formatDate, + renderCommandHints, renderTable, truncateToWidth, } from './render.js' @@ -22,7 +23,9 @@ export function formatCursorPaginationInfo (count: number, nextId: string | null export function formatCursorNavigationHints (nextId: string | null): string { if (!nextId) return '' - return ` ${chalk.dim('Next page:')} checkly account members --limit --next-id ${nextId}` + return renderCommandHints([ + { label: 'Next page', command: `checkly account members --limit --next-id ${nextId}` }, + ], { gap: 4 }) } function boolSymbol (value: boolean | undefined, format: OutputFormat): string { diff --git a/packages/cli/src/formatters/alert-channels.ts b/packages/cli/src/formatters/alert-channels.ts index 869ae3a5d..f26c326b3 100644 --- a/packages/cli/src/formatters/alert-channels.ts +++ b/packages/cli/src/formatters/alert-channels.ts @@ -5,7 +5,9 @@ import { type OutputFormat, type DetailField, type ColumnDef, + type CommandHint, renderDetailFields, + renderCommandHints, renderTable, formatDate, truncateError, @@ -132,18 +134,18 @@ export function formatAlertChannelPaginationInfo (pagination: AlertChannelPagina export function formatAlertChannelNavigationHints (pagination: AlertChannelPaginationInfo): string { const { page, limit, total } = pagination const totalPages = Math.ceil(total / limit) - const lines: string[] = [] + const hints: CommandHint[] = [] if (page < totalPages) { - lines.push(` ${chalk.dim('Next page:')} checkly alert-channels list --page ${page + 1}`) + hints.push({ label: 'Next page', command: `checkly alert-channels list --page ${page + 1}` }) } if (page > 1) { - lines.push(` ${chalk.dim('Prev page:')} checkly alert-channels list --page ${page - 1}`) + hints.push({ label: 'Prev page', command: `checkly alert-channels list --page ${page - 1}` }) } - lines.push(` ${chalk.dim('View channel:')} checkly alert-channels get `) - lines.push(` ${chalk.dim('View logs:')} checkly alert-channels logs --status failed`) + hints.push({ label: 'View channel', command: 'checkly alert-channels get ' }) + hints.push({ label: 'View logs', command: 'checkly alert-channels logs --status failed' }) - return lines.join('\n') + return renderCommandHints(hints, { gap: 1 }) } const alertChannelDetailFields: DetailField[] = [ diff --git a/packages/cli/src/formatters/batch-stats.ts b/packages/cli/src/formatters/batch-stats.ts index 9175e6a14..54f91ccef 100644 --- a/packages/cli/src/formatters/batch-stats.ts +++ b/packages/cli/src/formatters/batch-stats.ts @@ -1,8 +1,8 @@ import chalk from 'chalk' import type { BatchAnalyticsResult } from '../rest/batch-analytics.js' import { type CheckWithStatus, type PaginationInfo, resolveStatus } from './checks.js' -import type { OutputFormat, ColumnDef } from './render.js' -import { renderTable, truncateToWidth, visWidth } from './render.js' +import type { OutputFormat, ColumnDef, CommandHint } from './render.js' +import { renderCommandHints, renderTable, truncateToWidth, visWidth } from './render.js' import { findUnit, rangeLabels } from './analytics.js' import type { QuickRange } from '../rest/analytics.js' @@ -193,19 +193,25 @@ export function formatBatchStatsNavigationHints ( ): string { const { page, limit, total } = pagination const totalPages = Math.ceil(total / limit) - const lines: string[] = [] + const hints: CommandHint[] = [] if (page < totalPages) { - lines.push(` ${chalk.dim('Next page:')} checkly checks stats --page ${page + 1}`) + hints.push({ label: 'Next page', command: `checkly checks stats --page ${page + 1}` }) } if (page > 1) { - lines.push(` ${chalk.dim('Prev page:')} checkly checks stats --page ${page - 1}`) + hints.push({ label: 'Prev page', command: `checkly checks stats --page ${page - 1}` }) } - lines.push(` ${chalk.dim('View check:')} checkly checks get `) - lines.push(` ${chalk.dim('Change range:')} checkly checks stats --range ${range === 'last24Hours' ? 'last7Days' : 'last24Hours'}`) + hints.push({ label: 'View check', command: 'checkly checks get ' }) + hints.push({ + label: 'Change range', + command: `checkly checks stats --range ${range === 'last24Hours' ? 'last7Days' : 'last24Hours'}`, + }) if (activeFilters.length === 0) { - lines.push(` ${chalk.dim('Filter:')} checkly checks stats --tag --type --search `) + hints.push({ + label: 'Filter', + command: 'checkly checks stats --tag --type --search ', + }) } - return lines.join('\n') + return renderCommandHints(hints, { gap: 2 }) } diff --git a/packages/cli/src/formatters/check-result-detail.ts b/packages/cli/src/formatters/check-result-detail.ts index b75523066..60030f06f 100644 --- a/packages/cli/src/formatters/check-result-detail.ts +++ b/packages/cli/src/formatters/check-result-detail.ts @@ -13,11 +13,13 @@ import type { import { type OutputFormat, type DetailField, + type CommandHint, formatMs, formatDate, resolveResultStatus, heading, renderDetailFields, + renderCommandHints, } from './render.js' // --- Helpers --- @@ -26,6 +28,21 @@ function label (text: string, width = 16): string { return chalk.dim(text.padEnd(width)) } +export function formatResultDetailWithNavigation ( + result: CheckResult, + format: OutputFormat, + hints: CommandHint[], +): string { + const output = [formatResultDetail(result, format)] + + if (hints.length > 0) { + output.push('') + output.push(renderCommandHints(hints)) + } + + return output.join('\n') +} + // --- Top-level result detail fields --- export const resultDetailFields: DetailField[] = [ diff --git a/packages/cli/src/formatters/checks.ts b/packages/cli/src/formatters/checks.ts index ff933d20a..027082b44 100644 --- a/packages/cli/src/formatters/checks.ts +++ b/packages/cli/src/formatters/checks.ts @@ -8,6 +8,7 @@ import { type OutputFormat, type DetailField, type ColumnDef, + type CommandHint, formatFrequency, formatCheckType, formatMs, @@ -17,6 +18,7 @@ import { resolveResultStatus, renderDetailFields, renderAdaptiveTable, + renderCommandHints, renderTable, } from './render.js' @@ -103,20 +105,23 @@ export function formatPaginationInfo (pagination: PaginationInfo): string { export function formatNavigationHints (pagination: PaginationInfo, activeFilters: string[]): string { const { page, limit, total } = pagination const totalPages = Math.ceil(total / limit) - const lines: string[] = [] + const hints: CommandHint[] = [] if (page < totalPages) { - lines.push(` ${chalk.dim('Next page:')} checkly checks list --page ${page + 1}`) + hints.push({ label: 'Next page', command: `checkly checks list --page ${page + 1}` }) } if (page > 1) { - lines.push(` ${chalk.dim('Prev page:')} checkly checks list --page ${page - 1}`) + hints.push({ label: 'Prev page', command: `checkly checks list --page ${page - 1}` }) } - lines.push(` ${chalk.dim('View check:')} checkly checks get `) + hints.push({ label: 'View check', command: 'checkly checks get ' }) if (activeFilters.length === 0) { - lines.push(` ${chalk.dim('Filter:')} checkly checks list --tag --type --status --search `) + hints.push({ + label: 'Filter', + command: 'checkly checks list --tag --type --status --search ', + }) } - return lines.join('\n') + return renderCommandHints(hints, { gap: 3 }) } // --- Check detail fields --- diff --git a/packages/cli/src/formatters/render.ts b/packages/cli/src/formatters/render.ts index fbe03745d..323a86f21 100644 --- a/packages/cli/src/formatters/render.ts +++ b/packages/cli/src/formatters/render.ts @@ -129,6 +129,31 @@ export interface AdaptiveTableOptions { terminalWidth?: number } +export interface CommandHint { + label: string + command: string +} + +export interface CommandHintsOptions { + gap?: number + indent?: number +} + +export function renderCommandHints (hints: CommandHint[], options: CommandHintsOptions = {}): string { + if (hints.length === 0) return '' + + const indent = ' '.repeat(options.indent ?? 2) + const gap = ' '.repeat(options.gap ?? 2) + const labelWidth = Math.max(...hints.map(hint => visWidth(`${hint.label}:`))) + + return hints + .map(hint => { + const label = padColumn(`${hint.label}:`, labelWidth, 'left', false) + return `${indent}${chalk.dim(label)}${gap}${hint.command}` + }) + .join('\n') +} + export function renderDetailFields ( title: string, fields: DetailField[], diff --git a/packages/cli/src/formatters/status-pages.ts b/packages/cli/src/formatters/status-pages.ts index 92c63bbc5..33862fb4e 100644 --- a/packages/cli/src/formatters/status-pages.ts +++ b/packages/cli/src/formatters/status-pages.ts @@ -5,6 +5,7 @@ import { type ColumnDef, type DetailField, truncateToWidth, + renderCommandHints, renderTable, renderDetailFields, } from './render.js' @@ -185,11 +186,10 @@ export function formatCursorPaginationInfo (count: number, nextId: string | null } export function formatCursorNavigationHints (nextId: string | null): string { - const lines: string[] = [] - if (nextId) { - lines.push(` ${chalk.dim('Next page:')} checkly status-pages list --cursor ${nextId}`) - } - return lines.join('\n') + if (!nextId) return '' + return renderCommandHints([ + { label: 'Next page', command: `checkly status-pages list --cursor ${nextId}` }, + ], { gap: 4 }) } // --- Detail view for a single status page --- diff --git a/packages/cli/src/formatters/test-sessions.ts b/packages/cli/src/formatters/test-sessions.ts index ad510db67..0cbbeeef2 100644 --- a/packages/cli/src/formatters/test-sessions.ts +++ b/packages/cli/src/formatters/test-sessions.ts @@ -11,8 +11,10 @@ import { type ColumnDef, type DetailField, type OutputFormat, + type CommandHint, formatCheckType, formatDate, + renderCommandHints, renderDetailFields, renderTable, truncateToWidth, @@ -187,14 +189,14 @@ export function formatTestSessionsListNavigationHints ( listCommand: string, firstSessionId?: string, ): string { - const lines: string[] = [] + const hints: CommandHint[] = [] if (nextId) { - lines.push(` ${chalk.dim('Next page:')} ${listCommand} --cursor ${nextId}`) + hints.push({ label: 'Next page', command: `${listCommand} --cursor ${nextId}` }) } if (firstSessionId) { - lines.push(` ${chalk.dim('Inspect session:')} ${`checkly test-sessions get ${firstSessionId}`}`) + hints.push({ label: 'Inspect session', command: `checkly test-sessions get ${firstSessionId}` }) } - return lines.join('\n') + return renderCommandHints(hints, { gap: 1 }) } function formatMetadata (metadata: TestSessionMetadata | undefined, format: OutputFormat): string | null { diff --git a/packages/cli/src/rest/__tests__/test-sessions.spec.ts b/packages/cli/src/rest/__tests__/test-sessions.spec.ts index e5dd4e9ca..1d8973f26 100644 --- a/packages/cli/src/rest/__tests__/test-sessions.spec.ts +++ b/packages/cli/src/rest/__tests__/test-sessions.spec.ts @@ -14,6 +14,17 @@ describe('TestSessions REST client', () => { expect(api.get).toHaveBeenCalledWith('/v1/test-sessions/session-id') }) + it('gets a public test session result by ID', async () => { + const api = { + get: vi.fn().mockResolvedValue({ data: { id: 'result-id' } }), + } + const testSessions = new TestSessions(api as any) + + await testSessions.getResult('session-id', 'result-id') + + expect(api.get).toHaveBeenCalledWith('/v1/test-sessions/session-id/results/result-id') + }) + it('gets public test session completion by ID', async () => { const api = { get: vi.fn().mockResolvedValue({ data: { testSessionId: 'session-id', status: 'PASSED', results: [] } }), diff --git a/packages/cli/src/rest/test-sessions.ts b/packages/cli/src/rest/test-sessions.ts index 4a42963a6..a184e0e06 100644 --- a/packages/cli/src/rest/test-sessions.ts +++ b/packages/cli/src/rest/test-sessions.ts @@ -4,6 +4,7 @@ import { RetryStrategy, SharedFile } from '../constructs/index.js' import { compressJSONPayload } from './util.js' import { SequenceId } from '../services/abstract-check-runner.js' import { ForbiddenError, RequestTimeoutError } from './errors.js' +import type { CheckResult } from './check-results.js' const COMPLETION_MAX_WAIT_SECONDS = 30 @@ -187,6 +188,10 @@ class TestSessions { return this.api.get(`/v1/test-sessions/${id}`) } + getResult (id: string, resultId: string) { + return this.api.get(`/v1/test-sessions/${id}/results/${resultId}`) + } + getCompletion (id: string, maxWaitSeconds = COMPLETION_MAX_WAIT_SECONDS) { return this.api.get(`/v1/test-sessions/${id}/completion`, { params: { maxWaitSeconds },