Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "qas-cli",
"version": "0.4.3",
"version": "0.4.4",
"description": "QAS CLI is a command line tool for submitting your automation test results to QA Sphere at https://qasphere.com/",
"type": "module",
"main": "./build/bin/qasphere.js",
Expand Down
12 changes: 6 additions & 6 deletions src/api/run.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CreateResultRequest, ResourceId, RunTCase } from './schemas'
import { CreateResultsRequest, ResourceId, RunTCase } from './schemas'
import { jsonResponse, withJson } from './utils'

export interface CreateRunRequest {
Expand All @@ -24,16 +24,16 @@ export const createRunApi = (fetcher: typeof fetch) => {
fetcher(`/api/public/v0/project/${projectCode}/run/${runId}/tcase`)
.then((r) => jsonResponse<{ tcases: RunTCase[] }>(r))
.then((r) => r.tcases),
createResultStatus: (

createResults: (
projectCode: ResourceId,
runId: ResourceId,
tcaseId: ResourceId,
req: CreateResultRequest
req: CreateResultsRequest
) =>
fetcher(`/api/public/v0/project/${projectCode}/run/${runId}/tcase/${tcaseId}/result`, {
fetcher(`/api/public/v0/project/${projectCode}/run/${runId}/result/batch`, {
body: JSON.stringify(req),
method: 'POST',
}).then((r) => jsonResponse<{ id: number }>(r)),
}).then((r) => jsonResponse<{ ids: number[] }>(r)),

createRun: (projectCode: ResourceId, req: CreateRunRequest) =>
fetcher(`/api/public/v0/project/${projectCode}/run`, {
Expand Down
7 changes: 6 additions & 1 deletion src/api/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ export interface RunTCase {
folder: Folder
}

export interface CreateResultRequest {
export interface CreateResultsRequestItem {
tcaseId: string
status: ResultStatus
comment?: string
}

export interface CreateResultsRequest {
items: CreateResultsRequestItem[]
}
118 changes: 63 additions & 55 deletions src/tests/result-upload.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { setupServer } from 'msw/node'
import { HttpResponse, http } from 'msw'
import { runTestCases } from './fixtures/testcases'
import { countMockedApiCalls } from './utils'
import { setMaxResultsInRequest } from '../utils/result-upload/ResultUploader'

const projectCode = 'TEST'
const runId = '1'
Expand Down Expand Up @@ -56,11 +57,11 @@ const server = setupServer(
})
}),
http.post(
new RegExp(`${baseURL}/api/public/v0/project/${projectCode}/run/${runId}/tcase/.+/result`),
new RegExp(`${baseURL}/api/public/v0/project/${projectCode}/run/${runId}/result/batch`),
({ request }) => {
expect(request.headers.get('Authorization')).toEqual('ApiKey QAS_TOKEN')
return HttpResponse.json({
id: 0,
ids: [0],
})
}
),
Expand All @@ -83,12 +84,13 @@ afterAll(() => {
afterEach(() => {
server.resetHandlers()
server.events.removeAllListeners()
setMaxResultsInRequest(50)
})

const countFileUploadApiCalls = () =>
countMockedApiCalls(server, (req) => req.url.endsWith('/file'))
const countResultUploadApiCalls = () =>
countMockedApiCalls(server, (req) => new URL(req.url).pathname.endsWith('/result'))
countMockedApiCalls(server, (req) => new URL(req.url).pathname.endsWith('/result/batch'))

const fileTypes = [
{
Expand Down Expand Up @@ -116,22 +118,23 @@ fileTypes.forEach((fileType) => {
]

for (const pattern of patterns) {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
await run(pattern)
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(5)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(1) // 5 results total
}
})

test('Passing correct Run URL pattern without https, should result in success', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
setMaxResultsInRequest(1)
await run(
`${fileType.command} -r ${qasHost}/project/${projectCode}/run/${runId} ${fileType.dataBasePath}/matching-tcases.${fileType.fileExtension}`
)
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(5)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(5) // 5 results total
})

test('Passing incorrect Run URL pattern should result in failure', async () => {
Expand All @@ -141,8 +144,8 @@ fileTypes.forEach((fileType) => {
]

for (const pattern of patterns) {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
let isError = false

try {
Expand All @@ -151,105 +154,110 @@ fileTypes.forEach((fileType) => {
isError = true
}
expect(isError).toBeTruthy()
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(0)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(0)
}
})
})

describe('Uploading test results', () => {
test('Test cases on reports with all matching test cases on QAS should be successful', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
setMaxResultsInRequest(2)
await run(
`${fileType.command} -r ${runURL} ${fileType.dataBasePath}/matching-tcases.${fileType.fileExtension}`
)
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(5)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(3) // 5 results total
})

test('Test cases on reports with a missing test case on QAS should throw an error', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
await expect(
run(
`${fileType.command} -r ${runURL} ${fileType.dataBasePath}/missing-tcases.${fileType.fileExtension}`
)
).rejects.toThrowError()
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(0)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(0)
})

test('Test cases on reports with a missing test case on QAS should be successful when forced', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
setMaxResultsInRequest(3)
await run(
`${fileType.command} -r ${runURL} --force ${fileType.dataBasePath}/missing-tcases.${fileType.fileExtension}`
)
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(4)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(2) // 4 results total
})

test('Test cases on reports with missing test cases should be successful with --ignore-unmatched', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
await run(
`${fileType.command} -r ${runURL} --ignore-unmatched ${fileType.dataBasePath}/missing-tcases.${fileType.fileExtension}`
)
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(4)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(1) // 4 results total
})

test('Test cases from multiple reports should be processed successfully', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
setMaxResultsInRequest(2)
await run(
`${fileType.command} -r ${runURL} --force ${fileType.dataBasePath}/missing-tcases.${fileType.fileExtension} ${fileType.dataBasePath}/missing-tcases.${fileType.fileExtension}`
)
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(8)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(4) // 8 results total
})

test('Test suite with empty tcases should not result in error and be skipped', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
await run(
`${fileType.command} -r ${runURL} --force ${fileType.dataBasePath}/empty-tsuite.${fileType.fileExtension}`
)
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(1)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(1) // 1 result total
})
})

describe('Uploading with attachments', () => {
test('Attachments should be uploaded', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
setMaxResultsInRequest(3)
await run(
`${fileType.command} -r ${runURL} --attachments ${fileType.dataBasePath}/matching-tcases.${fileType.fileExtension}`
)
expect(fileUploadCount()).toBe(5)
expect(tcaseUploadCount()).toBe(5)
expect(numFileUploadCalls()).toBe(5)
expect(numResultUploadCalls()).toBe(2) // 5 results total
})
test('Missing attachments should throw an error', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
await expect(
run(
`${fileType.command} -r ${runURL} --attachments ${fileType.dataBasePath}/missing-attachments.${fileType.fileExtension}`
)
).rejects.toThrow()
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(0)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(0)
})
test('Missing attachments should be successful when forced', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()
setMaxResultsInRequest(1)
await run(
`${fileType.command} -r ${runURL} --attachments --force ${fileType.dataBasePath}/missing-attachments.${fileType.fileExtension}`
)
expect(fileUploadCount()).toBe(4)
expect(tcaseUploadCount()).toBe(5)
expect(numFileUploadCalls()).toBe(4)
expect(numResultUploadCalls()).toBe(5) // 5 results total
})
})

Expand Down Expand Up @@ -318,17 +326,17 @@ fileTypes.forEach((fileType) => {
})

test('Should reuse existing run when run title is already used', async () => {
const fileUploadCount = countFileUploadApiCalls()
const tcaseUploadCount = countResultUploadApiCalls()
const numFileUploadCalls = countFileUploadApiCalls()
const numResultUploadCalls = countResultUploadApiCalls()

createRunTitleConflict = true
await run(
`${fileType.command} --run-name "duplicate run title" ${fileType.dataBasePath}/matching-tcases.${fileType.fileExtension}`
)

expect(lastCreatedRunTitle).toBe('duplicate run title')
expect(fileUploadCount()).toBe(0)
expect(tcaseUploadCount()).toBe(5)
expect(numFileUploadCalls()).toBe(0)
expect(numResultUploadCalls()).toBe(1) // 5 results total
})

test('Should use default name template when --run-name is not specified', async () => {
Expand Down
2 changes: 2 additions & 0 deletions src/utils/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export const twirlLoader = () => {
if (timer) {
clearInterval(timer)
}
x = chars.length - 1
update()
process.stdout.write('\n')
},
setText: (newText: string) => {
Expand Down
Loading