From e4b0944d5bb576d85064cf95a27cf679e3f1bb95 Mon Sep 17 00:00:00 2001 From: Maximilian Link Date: Wed, 23 Jul 2025 13:53:07 +0200 Subject: [PATCH 1/5] wip --- package.json | 3 +- pnpm-lock.yaml | 38 ++++++ src/cli.ts | 27 ++++ src/debugtopus/index.ts | 227 +++++++++++++++++++++++++++++++++ src/debugtopus/installation.ts | 42 ++++++ src/debugtopus/octomind-api.ts | 140 ++++++++++++++++++++ src/types.ts | 10 ++ 7 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 src/debugtopus/index.ts create mode 100644 src/debugtopus/installation.ts create mode 100644 src/debugtopus/octomind-api.ts diff --git a/package.json b/package.json index be5e19b..96c241a 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,14 @@ "author": "", "license": "MIT", "dependencies": { + "@playwright/test": "^1.54.1", "axios": "^1.7.9", "commander": "^14.0.0", "tsx": "^4.19.3" }, "devDependencies": { - "@types/node": "^24.0.1", "@types/jest": "^30.0.0", + "@types/node": "^24.0.1", "@typescript-eslint/parser": "^8.25.0", "eslint": "8.57.1", "eslint-config-prettier": "^10.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa79b1e..1cbac72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@playwright/test': + specifier: ^1.54.1 + version: 1.54.1 axios: specifier: ^1.7.9 version: 1.10.0 @@ -627,6 +630,11 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@playwright/test@1.54.1': + resolution: {integrity: sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==} + engines: {node: '>=18'} + hasBin: true + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -1706,6 +1714,11 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2455,6 +2468,16 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + playwright-core@1.54.1: + resolution: {integrity: sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.54.1: + resolution: {integrity: sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==} + engines: {node: '>=18'} + hasBin: true + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -3578,6 +3601,10 @@ snapshots: '@pkgr/core@0.2.9': {} + '@playwright/test@1.54.1': + dependencies: + playwright: 1.54.1 + '@rtsao/scc@1.1.0': {} '@sinclair/typebox@0.34.35': {} @@ -4837,6 +4864,9 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -5767,6 +5797,14 @@ snapshots: dependencies: find-up: 4.1.0 + playwright-core@1.54.1: {} + + playwright@1.54.1: + dependencies: + playwright-core: 1.54.1 + optionalDependencies: + fsevents: 2.3.2 + possible-typed-array-names@1.1.0: {} prelude-ls@1.2.1: {} diff --git a/src/cli.ts b/src/cli.ts index a8b93c8..8960854 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -11,6 +11,7 @@ import { unregisterLocation, updateEnvironment, } from "./api"; +import { runDebugtopus } from "./debugtopus"; const apiKeyOption = new Option( "-k, --api-key ", @@ -37,6 +38,32 @@ export const buildCmd = (): Command => { ) .version(version); + createCommandWithCommonOptions("debug") + .description("run test cases against local build") + .option( + "-i, --id ", + "id of the test case you want to run, if not provided will run all test cases in the test target", + ) + .option( + "-e, --environmentId ", + "id of the environment you want to run against, if not provided will run all test cases against the default environment", + ) + .requiredOption("-u, --url ", "url the tests should run against") + .requiredOption( + "-a, --testTargetId ", + "id of the test target of the test case", + ) + .option( + "-o, --octomindUrl ", + "base url of the octomind api", + "https://app.octomind.dev", + ) + .option( + "--headless", + "if we should run headless without the UI of playwright and the browser", + ) + .action(runDebugtopus); + createCommandWithCommonOptions("execute") .description("Execute test cases") .requiredOption("-t, --test-target-id ", "Test target ID") diff --git a/src/debugtopus/index.ts b/src/debugtopus/index.ts new file mode 100644 index 0000000..4b51fb1 --- /dev/null +++ b/src/debugtopus/index.ts @@ -0,0 +1,227 @@ +import { existsSync, writeFileSync } from "fs"; +import { promisify } from "util"; +import { exec } from "child_process"; +import { randomUUID } from "crypto"; +import path, { dirname } from "path"; +import fs from "fs/promises"; +import { ensureChromiumIsInstalled } from "./installation"; +import { + getPlaywrightCode, + getPlaywrightConfig, + getTestCases, + getTestTarget, + TestCase, +} from "./octomind-api"; +import { DebugtopusOptions } from "../types"; + +type BasicAuth = { username: string; password: string }; + +type Environment = { + id: string; + basicAuth?: BasicAuth; + type: "DEFAULT" | "ADDITIONAL"; +}; + +const getPackageRootLevel = (appDir: string): string => { + let infiniteLoopPrevention = 5; + let rootDir = appDir; + + while (infiniteLoopPrevention > 0) { + const nodeDir = path.join(rootDir, "node_modules"); + if (existsSync(nodeDir)) { + break; + } + + rootDir = path.join(rootDir, ".."); + infiniteLoopPrevention -= 1; + } + + if (infiniteLoopPrevention === 0) { + throw new Error("can't find root level node modules"); + } + + return rootDir; +}; + +type TestDirectories = { + configFilePath: string; + testDirectory: string; + outputDir: string; + packageRootDir: string; +}; + +type TestCaseWithCode = TestCase & { code: string }; + +const getUniqueFilename = (tempDir: string, testCase: TestCaseWithCode) => { + const fileNameUUID = randomUUID(); + const name = testCase.description + ? testCase.description.replaceAll(path.sep, "-") + : testCase.id; + return path.join(tempDir, `${name}-${fileNameUUID}.spec.ts`); +}; + +const prepareDirectories = async ( + packageRootDir?: string, +): Promise => { + if (!packageRootDir) { + // at runtime, we are installed in an arbitrary npx cache folder, + // we need to find the rootDir ourselves and cannot rely on paths relative to src + const nodeModule = require.main; + if (!nodeModule) { + throw new Error("package was not installed as valid nodeJS module"); + } + const appDir = dirname(nodeModule.filename); + packageRootDir = getPackageRootLevel(appDir); + } + + const tempDir = path.join(packageRootDir, "temp"); + + if (existsSync(tempDir)) { + await fs.rm(tempDir, { force: true, recursive: true }); + } + + await fs.mkdir(tempDir); + + const outputDir = path.join(tempDir, "output"); + + const fileNameUUID = randomUUID(); + const configFilePath = path.join(tempDir, `${fileNameUUID}.config.ts`); + return { + outputDir, + configFilePath, + testDirectory: tempDir, + packageRootDir, + }; +}; + +const writeConfigAndTests = ({ + testCasesWithCode, + config, + dirs, +}: { + testCasesWithCode: TestCaseWithCode[]; + config: string; + dirs: TestDirectories; +}): string[] => { + const testFilePaths: string[] = []; + for (const testCase of testCasesWithCode) { + const testFilePath = getUniqueFilename(dirs.testDirectory, testCase); + writeFileSync(testFilePath, testCase.code); + testFilePaths.push(testFilePath); + } + + writeFileSync(dirs.configFilePath, config); + return testFilePaths; +}; + +const createPlaywrightCommand = ({ + configFilePath, + testDirectory, +}: { + configFilePath: string; + testDirectory: string; +}): string => + `npx playwright test --config=${configFilePath.replaceAll( + "\\", + "/", + )} ${testDirectory.replaceAll("\\", "/")}`; + +const runTests = async ({ + configFilePath, + testDirectory, + outputDir, + runMode, + packageRootDir, +}: { + configFilePath: string; + testDirectory: string; + outputDir: string; + packageRootDir: string; + runMode: "ui" | "headless"; +}): Promise => { + await ensureChromiumIsInstalled(packageRootDir); + + let command = createPlaywrightCommand({ configFilePath, testDirectory }); + + if (runMode === "ui") { + command += " --ui"; + } + + console.log(`executing command : '${command}'`); + + const { stderr } = await promisify(exec)(command, { + cwd: packageRootDir, + }); + + if (stderr) { + console.error(stderr); + process.exit(1); + } else { + console.log(`success, you can find your artifacts at ${outputDir}`); + } +}; + +export const runDebugtopus = async (options: DebugtopusOptions) => { + const baseApiOptions = { + testTargetId: options.testTargetId, + token: options.token, + url: options.url, + octomindUrl: options.octomindUrl, + environmentId: options.environmentId, + }; + + const testTarget = await getTestTarget({ + testTargetId: options.testTargetId, + token: options.token, + octomindUrl: options.octomindUrl, + }); + + let testCasesWithCode: TestCaseWithCode[] = []; + if (options.id) { + testCasesWithCode = [ + { + id: options.id, + code: await getPlaywrightCode({ + testCaseId: options.id, + ...baseApiOptions, + }), + }, + ]; + } else { + const testCases = await getTestCases(baseApiOptions); + + testCasesWithCode = await Promise.all( + testCases.map(async (testCase) => ({ + code: await getPlaywrightCode({ + testCaseId: testCase.id, + ...baseApiOptions, + }), + ...testCase, + })), + ); + } + + const environmentIdForConfig = options.environmentId + ? options.environmentId + : testTarget.environments.find((env: Environment) => env.type === "DEFAULT") + .id; + + const dirs = await prepareDirectories(); + + const config = await getPlaywrightConfig({ + testTargetId: options.testTargetId, + token: options.token, + octomindUrl: options.octomindUrl, + url: options.url, + outputDir: dirs.outputDir, + environmentId: environmentIdForConfig, + headless: options.headless, + }); + + writeConfigAndTests({ + testCasesWithCode, + config, + dirs, + }); + await runTests({ ...dirs, runMode: options.headless ? "headless" : "ui" }); +}; diff --git a/src/debugtopus/installation.ts b/src/debugtopus/installation.ts new file mode 100644 index 0000000..168de8a --- /dev/null +++ b/src/debugtopus/installation.ts @@ -0,0 +1,42 @@ +import { chromium } from "@playwright/test"; +import { access } from "fs"; +import fs from "fs/promises"; +import { promisify } from "util"; +import { exec } from "child_process"; + +export const ensureChromiumIsInstalled = async ( + packageRootDir: string, +): Promise => { + const file = chromium.executablePath(); + + await new Promise((resolve, reject) => { + access(file, fs.constants.X_OK, async (error) => { + if (!error) { + resolve(); + return; + } + if (error.code !== "ENOENT") { + reject(error); + return; + } + console.log( + "Couldn't find any chromium binary, executing 'npx playwright install chromium'", + ); + + const playwrightInstallExecution = promisify(exec)( + "npx playwright install chromium", + { + cwd: packageRootDir, + }, + ); + + playwrightInstallExecution.child?.stdout?.on("data", (data) => + console.log(data), + ); + + await playwrightInstallExecution; + + resolve(); + }); + }); +}; diff --git a/src/debugtopus/octomind-api.ts b/src/debugtopus/octomind-api.ts new file mode 100644 index 0000000..9013aea --- /dev/null +++ b/src/debugtopus/octomind-api.ts @@ -0,0 +1,140 @@ +import axios, { AxiosError } from "axios"; + +export const getPlaywrightConfig = async ({ + testTargetId, + token, + octomindUrl, + outputDir, + url, + environmentId, + headless, +}: { + testTargetId: string; + token: string; + octomindUrl: string; + outputDir: string; + url: string; + environmentId: string; + headless?: boolean; +}): Promise => { + const searchParams = new URLSearchParams(); + searchParams.set("url", url); + searchParams.set("outputDir", outputDir); + searchParams.set("environmentId", environmentId); + if (headless) { + searchParams.set("headless", String(headless)); + } + + const endpoint = `${octomindUrl}/api/bearer/v1/test-targets/${testTargetId}/config?${searchParams.toString()}`; + + try { + const axiosResponse = await axios.get(endpoint, { + headers: { Authorization: `Bearer ${token}` }, + }); + return axiosResponse.data; + } catch (error) { + const responseBody = (error as AxiosError).response?.data; + const responseCode = (error as AxiosError).response?.status; + throw new Error( + `failed to get config from ${endpoint}: response body: '${JSON.stringify( + responseBody, + )}' statusCode: '${JSON.stringify(responseCode)}'`, + ); + } +}; + +export const getPlaywrightCode = async ({ + testCaseId, + token, + url, + octomindUrl, + testTargetId, + environmentId, +}: { + testCaseId: string; + testTargetId: string; + token: string; + url: string; + octomindUrl: string; + environmentId?: string; +}): Promise => { + const searchParams = new URLSearchParams(); + + searchParams.set("executionUrl", url); + if (environmentId) { + searchParams.set("environmentId", environmentId); + } + + const endpoint = `${octomindUrl}/api/bearer/v1/test-targets/${testTargetId}/test-cases/${testCaseId}/code?${searchParams.toString()}`; + + try { + const axiosResponse = await axios.get(endpoint, { + headers: { Authorization: `Bearer ${token}` }, + }); + return axiosResponse.data.testCode; + } catch (error) { + const responseBody = (error as AxiosError).response?.data; + const responseCode = (error as AxiosError).response?.status; + throw new Error( + `failed to get code from ${endpoint}: response body: '${JSON.stringify( + responseBody, + )}' statusCode: '${JSON.stringify(responseCode)}'`, + ); + } +}; + +export const getTestTarget = async ({ + testTargetId, + token, + octomindUrl, +}: { + testTargetId: string; + token: string; + octomindUrl: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any +}): Promise => { + const endpoint = `${octomindUrl}/api/bearer/v1/test-targets/${testTargetId}`; + try { + const axiosResponse = await axios.get<[TestCase]>(endpoint, { + headers: { Authorization: `Bearer ${token}` }, + }); + return axiosResponse.data; + } catch (error) { + const responseBody = (error as AxiosError).response?.data; + const responseCode = (error as AxiosError).response?.status; + throw new Error( + `failed to get test-target from ${endpoint}: response body: '${JSON.stringify( + responseBody, + )}' statusCode: '${JSON.stringify(responseCode)}'`, + ); + } +}; + +export type TestCase = { id: string; description?: string }; + +export const getTestCases = async ({ + token, + octomindUrl, + testTargetId, +}: { + testTargetId: string; + token: string; + octomindUrl: string; +}): Promise => { + const endpoint = `${octomindUrl}/api/bearer/v1/test-targets/${testTargetId}/test-cases`; + try { + const axiosResponse = await axios.get<[TestCase]>(endpoint, { + headers: { Authorization: `Bearer ${token}` }, + params: { filter: JSON.stringify({ status: "ENABLED" }) }, + }); + return axiosResponse.data; + } catch (error) { + const responseBody = (error as AxiosError).response?.data; + const responseCode = (error as AxiosError).response?.status; + throw new Error( + `failed to get test-cases from ${endpoint}: response body: '${JSON.stringify( + responseBody, + )}' statusCode: '${JSON.stringify(responseCode)}'`, + ); + } +}; diff --git a/src/types.ts b/src/types.ts index cba3100..30511ff 100644 --- a/src/types.ts +++ b/src/types.ts @@ -183,3 +183,13 @@ export interface DeleteEnvironmentOptions { environmentId: string; json?: boolean; } + +export type DebugtopusOptions = { + id?: string; + testTargetId: string; + token: string; + url: string; + octomindUrl: string; + environmentId?: string; + headless?: boolean; +}; From 748dd143aaf3631eb1d17880d52383eb4e72c317 Mon Sep 17 00:00:00 2001 From: Maximilian Link Date: Wed, 23 Jul 2025 16:46:30 +0200 Subject: [PATCH 2/5] make it work --- openapi.yaml | 46 +- package.json | 2 +- src/api.ts | 3244 ++++++++++++++++---------------- src/debugtopus/index.ts | 47 +- src/debugtopus/octomind-api.ts | 140 -- src/tools.ts | 64 +- 6 files changed, 1738 insertions(+), 1805 deletions(-) delete mode 100644 src/debugtopus/octomind-api.ts diff --git a/openapi.yaml b/openapi.yaml index 8401074..d5e48b2 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -632,6 +632,9 @@ components: nullable: true timeoutPerStep: type: integer + required: + - id + - testTargetId SuccessResponse: type: object @@ -732,7 +735,12 @@ components: type: string type: type: string - + required: + - id + - name + - testTargetId + - type + TestSuiteRequestCommons: type: object properties: @@ -1427,6 +1435,20 @@ paths: type: string format: uuid description: The ID of the test case + - name: environmentId + in: query + required: false + schema: + type: string + format: uuid + description: Optional ID of the environment to use for the test case code + - name: executionUrl + in: query + required: true + schema: + type: string + format: url + description: URL of the app to test responses: '200': description: Code for the test case @@ -1438,6 +1460,8 @@ paths: testCode: type: string description: The code representation of the test case + required: + - testCode '404': description: Test case not found @@ -1523,6 +1547,25 @@ paths: type: string format: uuid description: Optional ID of the environment to use for the test target configuration + - name: url + in: query + required: true + schema: + type: string + format: url + description: URL of the app to test + - name: outputDir + in: query + required: true + schema: + type: string + description: Directory to write the playwright output to + - name: headless + in: query + required: false + schema: + type: boolean + description: Whether to run the test in headless mode responses: "200": description: Test target configuration @@ -1531,6 +1574,7 @@ paths: schema: type: string example: "Test target configuration details" + /apiKey/v2/test-targets/{testTargetId}/discoveries: post: summary: Create a discovery diff --git a/package.json b/package.json index a714e6e..53628cb 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "lint": "npx genversion -des src/version.ts && eslint src/**/*.ts tests/**/*.ts --max-warnings=0", "apigen": "openapi-typescript ./openapi.yaml --output src/api.ts", "build": "npx genversion -des src/version.ts && pnpm apigen && tsc --project tsconfig.build.json", - "octomind": "tsx src/cli.ts", + "octomind": "tsx src/index.ts", "test": "npx genversion -des src/version.ts && jest", "test:watch": "npx genversion -e src/version.ts && jest --watch" }, diff --git a/src/api.ts b/src/api.ts index 693fc8b..d14e2de 100644 --- a/src/api.ts +++ b/src/api.ts @@ -4,1184 +4,1190 @@ */ export interface paths { - "/apiKey/v2/test-suites": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Retrieve test suites - * @description get a list of all defined test suites. - */ - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description test suites */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TestSuitesResponse"]; - }; - }; - }; - }; - put?: never; - /** Create a test suite */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TestSuiteCreateRequest"]; - }; - }; - responses: { - /** @description test suites */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TestSuiteResponse"]; - }; + "/apiKey/v2/test-suites": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ZodResponse"]; - }; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-suites/{testSuiteId}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - /** Delete a test suite */ - delete: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description test suites */ - 204: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SuccessResponse"]; - }; + /** + * Retrieve test suites + * @description get a list of all defined test suites. + */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description test suites */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TestSuitesResponse"]; + }; + }; + }; }; - /** @description test suite not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; + put?: never; + /** Create a test suite */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TestSuiteCreateRequest"]; + }; + }; + responses: { + /** @description test suites */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TestSuiteResponse"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ZodResponse"]; + }; + }; + }; }; - }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - options?: never; - head?: never; - /** Update a test suite */ - patch: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TestSuitePatchRequest"]; - }; - }; - responses: { - /** @description test suites */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TestSuiteResponse"]; - }; + "/apiKey/v2/test-suites/{testSuiteId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete a test suite */ + delete: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description test suites */ + 204: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SuccessResponse"]; + }; + }; + /** @description test suite not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ZodResponse"]; - }; + options?: never; + head?: never; + /** Update a test suite */ + patch: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TestSuitePatchRequest"]; + }; + }; + responses: { + /** @description test suites */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TestSuiteResponse"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ZodResponse"]; + }; + }; + }; }; - }; + trace?: never; }; - trace?: never; - }; - "/apiKey/v2/execute": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Execute tests of the given test target - * @description This endpoint triggers a test execution by sending an test target id, an URL and optionally tags, an environment and variables. - * - */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TestTargetExecutionRequest"]; - }; - }; - responses: { - /** @description Test executed successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TestReportResponse"]; - }; - }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ZodResponse"]; - }; - }; - /** @description Invalid or missing API key */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; + "/apiKey/v2/execute": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Execute tests of the given test target + * @description This endpoint triggers a test execution by sending an test target id, an URL and optionally tags, an environment and variables. + * + */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TestTargetExecutionRequest"]; + }; + }; + responses: { + /** @description Test executed successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TestReportResponse"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ZodResponse"]; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; }; - }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/environments/{environmentId}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - /** Delete an environment */ - delete: { - parameters: { - query?: never; - header?: never; - path: { - /** @description ID of the test target to which the test report belongs to */ - testTargetId: string; - /** @description ID of the environment to update */ - environmentId: string; + "/apiKey/v2/test-targets/{testTargetId}/environments/{environmentId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** Delete an environment */ + delete: { + parameters: { + query?: never; + header?: never; + path: { + /** @description ID of the test target to which the test report belongs to */ + testTargetId: string; + /** @description ID of the environment to update */ + environmentId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Environment deleted successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Environment deleted successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; + options?: never; + head?: never; + /** Update an environment */ + patch: { + parameters: { + query?: never; + header?: never; + path: { + /** @description ID of the test target to which the environment belongs to */ + testTargetId: string; + /** @description ID of the environment to update */ + environmentId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name?: string | null; + /** Format: url */ + discoveryUrl?: string | null; + testAccount?: { + username?: string; + password?: string; + otpInitializerKey?: string | null; + } | null; + basicAuth?: { + username?: string; + password?: string; + } | null; + /** @description name of the private location */ + privateLocationName?: string | null; + additionalHeaderFields?: { + [key: string]: string; + } | null; + }; + }; + }; + responses: { + /** @description Environment updated successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EnvironmentResponse"]; + }; + }; + }; }; - }; + trace?: never; }; - options?: never; - head?: never; - /** Update an environment */ - patch: { - parameters: { - query?: never; - header?: never; - path: { - /** @description ID of the test target to which the environment belongs to */ - testTargetId: string; - /** @description ID of the environment to update */ - environmentId: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - name?: string | null; - /** Format: url */ - discoveryUrl?: string | null; - testAccount?: { - username?: string; - password?: string; - otpInitializerKey?: string | null; - } | null; - basicAuth?: { - username?: string; - password?: string; - } | null; - /** @description name of the private location */ - privateLocationName?: string | null; - additionalHeaderFields?: { - [key: string]: string; - } | null; - }; - }; - }; - responses: { - /** @description Environment updated successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["EnvironmentResponse"]; - }; + "/apiKey/v2/test-targets/{testTargetId}/environments": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - }; - }; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/environments": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Retrieve environments - * @description get a list of all defined environments. - */ - get: { - parameters: { - query?: never; - header?: never; - path: { - /** @description ID of the test target to which the test report belongs to */ - testTargetId: string; + /** + * Retrieve environments + * @description get a list of all defined environments. + */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description ID of the test target to which the test report belongs to */ + testTargetId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description environments */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EnvironmentsResponse"]; + }; + }; + }; }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description environments */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["EnvironmentsResponse"]; - }; + put?: never; + /** + * Create an environment + * @description Create a custom environment. + */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description ID of the test target to which the environment belongs to */ + testTargetId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + name?: string; + /** Format: url */ + discoveryUrl?: string; + testAccount?: { + username?: string; + password?: string; + otpInitializerKey?: string | null; + } | null; + basicAuth?: { + username?: string; + password?: string; + } | null; + /** @description name of the private location */ + privateLocationName?: string; + additionalHeaderFields?: { + [key: string]: string; + } | null; + }; + }; + }; + responses: { + /** @description environment created */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EnvironmentResponse"]; + }; + }; + }; }; - }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - put?: never; - /** - * Create an environment - * @description Create a custom environment. - */ - post: { - parameters: { - query?: never; - header?: never; - path: { - /** @description ID of the test target to which the environment belongs to */ - testTargetId: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - name?: string; - /** Format: url */ - discoveryUrl?: string; - testAccount?: { - username?: string; - password?: string; - otpInitializerKey?: string | null; - } | null; - basicAuth?: { - username?: string; - password?: string; - } | null; - /** @description name of the private location */ - privateLocationName?: string; - additionalHeaderFields?: { - [key: string]: string; - } | null; - }; + "/apiKey/v2/test-targets/{testTargetId}/test-reports/{testReportId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - }; - responses: { - /** @description environment created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["EnvironmentResponse"]; - }; + /** + * Retrieve information about a test report + * @description Poll from within a CI-pipeline to wait for the completion of a report. + */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description ID of the test target to which the test report belongs to */ + testTargetId: string; + /** @description ID of the test report to fetch */ + testReportId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Test Report information */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TestReport"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ZodResponse"]; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/test-reports/{testReportId}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - /** - * Retrieve information about a test report - * @description Poll from within a CI-pipeline to wait for the completion of a report. - */ - get: { - parameters: { - query?: never; - header?: never; - path: { - /** @description ID of the test target to which the test report belongs to */ - testTargetId: string; - /** @description ID of the test report to fetch */ - testReportId: string; + "/apiKey/v2/test-targets/{testTargetId}/test-reports": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Test Report information */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TestReport"]; - }; - }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ZodResponse"]; - }; - }; - /** @description Invalid or missing API key */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; + /** + * Retrieve paginated information about test reports + * @description Allow fetching the history of test reports for your test target. + */ + get: { + parameters: { + query?: { + key?: { + /** + * Format: date-time + * @description The timestamp of the key of the next page to fetch - See [Keyset Pagination](https://use-the-index-luke.com/no-offset) + * @example 2024-09-06T13:01:51.686Z + */ + createdAt?: string; + }; + filter?: { + /** + * @description The name of the property to filter for, e.g. an environmentId + * @example environmentId + */ + key?: string; + /** + * @description How to compare the property in question, only EQUALS is supported so far. + * @enum {string} + */ + operator?: "EQUALS"; + /** + * Format: uuid + * @description The value to compare with to find matches. + * @example 3435918b-3d29-4ebd-8c68-9a540532f45a + */ + value?: string; + }[]; + }; + header?: never; + path: { + /** @description ID of the test target for which to fetch the history for */ + testTargetId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Test Reports information */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + data?: components["schemas"]["TestReport"][]; + key?: { + /** + * Format: date-time + * @description The timestamp of the key of the next page to fetch - use this in the query when fetching the next page of reports - See [Keyset Pagination](https://use-the-index-luke.com/no-offset) + * @example 2024-09-06T13:01:51.686Z + */ + createdAt?: string; + }; + /** @description If the query in question has another page to retrieve */ + hasNextPage?: boolean; + }; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ZodResponse"]; + }; + }; + /** @description Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/test-reports": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - /** - * Retrieve paginated information about test reports - * @description Allow fetching the history of test reports for your test target. - */ - get: { - parameters: { - query?: { - key?: { - /** - * Format: date-time - * @description The timestamp of the key of the next page to fetch - See [Keyset Pagination](https://use-the-index-luke.com/no-offset) - * @example 2024-09-06T13:01:51.686Z - */ - createdAt?: string; - }; - filter?: { - /** - * @description The name of the property to filter for, e.g. an environmentId - * @example environmentId - */ - key?: string; - /** - * @description How to compare the property in question, only EQUALS is supported so far. - * @enum {string} - */ - operator?: "EQUALS"; - /** - * Format: uuid - * @description The value to compare with to find matches. - * @example 3435918b-3d29-4ebd-8c68-9a540532f45a - */ - value?: string; - }[]; + "/apiKey/v2/test-targets/{testTargetId}/notifications": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - header?: never; - path: { - /** @description ID of the test target for which to fetch the history for */ - testTargetId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Test Reports information */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - data?: components["schemas"]["TestReport"][]; - key?: { - /** - * Format: date-time - * @description The timestamp of the key of the next page to fetch - use this in the query when fetching the next page of reports - See [Keyset Pagination](https://use-the-index-luke.com/no-offset) - * @example 2024-09-06T13:01:51.686Z - */ - createdAt?: string; - }; - /** @description If the query in question has another page to retrieve */ - hasNextPage?: boolean; + /** + * Retrieve notifications + * @description Get a list of notifications for a specific test target. + */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description The ID of the test target */ + testTargetId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of notifications */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** Format: uuid */ + id?: string; + /** Format: uuid */ + testTargetId?: string; + /** Format: date-time */ + createdAt?: string; + /** Format: date-time */ + updatedAt?: string; + payload?: { + failed?: boolean; + context?: components["schemas"]["ExecutionContext"]; + /** Format: uuid */ + testReportId?: string; + /** Format: uuid */ + testCaseId?: string; + }; + /** @enum {string} */ + type?: "REPORT_EXECUTION_FINISHED" | "VALIDATION_PASSED"; + /** @enum {string|null} */ + ack?: "IN_WEB_APP" | null; + }[]; + }; + }; + /** @description Unauthorized - Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Test target not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; }; - }; - }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ZodResponse"]; - }; - }; - /** @description Invalid or missing API key */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/notifications": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - /** - * Retrieve notifications - * @description Get a list of notifications for a specific test target. - */ - get: { - parameters: { - query?: never; - header?: never; - path: { - /** @description The ID of the test target */ - testTargetId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of notifications */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - /** Format: uuid */ - id?: string; - /** Format: uuid */ - testTargetId?: string; - /** Format: date-time */ - createdAt?: string; - /** Format: date-time */ - updatedAt?: string; - payload?: { - failed?: boolean; - context?: components["schemas"]["ExecutionContext"]; - /** Format: uuid */ - testReportId?: string; - /** Format: uuid */ - testCaseId?: string; - }; - /** @enum {string} */ - type?: "REPORT_EXECUTION_FINISHED" | "VALIDATION_PASSED"; - /** @enum {string|null} */ - ack?: "IN_WEB_APP" | null; - }[]; - }; + "/apiKey/v1/private-location": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - /** @description Unauthorized - Invalid or missing API key */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Test target not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v1/private-location": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Retrieve all private locations - * @description gets a list of private location workers - */ - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["PrivateLocationInfo"]; - }; + /** + * Retrieve all private locations + * @description gets a list of private location workers + */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PrivateLocationInfo"]; + }; + }; + }; }; - }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v1/private-location/register": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - /** - * Register a private location - * @description registers a private location worker - */ - put: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["RegisterRequest"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SuccessResponse"]; - }; - }; - /** @description private location of that name not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; + "/apiKey/v1/private-location/register": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** + * Register a private location + * @description registers a private location worker + */ + put: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["RegisterRequest"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SuccessResponse"]; + }; + }; + /** @description private location of that name not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; }; - }; - }; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v1/private-location/unregister": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - get?: never; - /** - * Unregister a private location - * @description Unregisters a private location worker. - */ - put: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["UnregisterRequest"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SuccessResponse"]; - }; - }; - /** @description private location of that name not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; + "/apiKey/v1/private-location/unregister": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + /** + * Unregister a private location + * @description Unregisters a private location worker. + */ + put: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UnregisterRequest"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SuccessResponse"]; + }; + }; + /** @description private location of that name not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; }; - }; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/test-cases/{testCaseId}/code": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Retrieve code for a test case - * @description Get the code representation of a specific test case - */ - get: { - parameters: { - query?: never; - header?: never; - path: { - /** @description The ID of the test target */ - testTargetId: string; - /** @description The ID of the test case */ - testCaseId: string; + "/apiKey/v2/test-targets/{testTargetId}/test-cases/{testCaseId}/code": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Code for the test case */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - /** @description The code representation of the test case */ - testCode?: string; + /** + * Retrieve code for a test case + * @description Get the code representation of a specific test case + */ + get: { + parameters: { + query: { + /** @description Optional ID of the environment to use for the test case code */ + environmentId?: string; + /** @description URL of the app to test */ + executionUrl: string; + }; + header?: never; + path: { + /** @description The ID of the test target */ + testTargetId: string; + /** @description The ID of the test case */ + testCaseId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Code for the test case */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @description The code representation of the test case */ + testCode: string; + }; + }; + }; + /** @description Test case not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; }; - }; - }; - /** @description Test case not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/test-cases": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - /** - * Retrieve test cases - * @description Get a list of test cases for a specific test target - */ - get: { - parameters: { - query?: never; - header?: never; - path: { - /** @description The ID of the test target */ - testTargetId: string; + "/apiKey/v2/test-targets/{testTargetId}/test-cases": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of test cases */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TestCasesResponse"]; - }; + /** + * Retrieve test cases + * @description Get a list of test cases for a specific test target + */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description The ID of the test target */ + testTargetId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of test cases */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TestCasesResponse"]; + }; + }; + }; }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/test-cases/{testCaseId}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - /** - * Retrieve a test case - * @description Get detailed information about a specific test case - */ - get: { - parameters: { - query?: never; - header?: never; - path: { - /** @description The ID of the test target */ - testTargetId: string; - /** @description The ID of the test case */ - testCaseId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Test case details */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TestCaseResponse"]; - }; + "/apiKey/v2/test-targets/{testTargetId}/test-cases/{testCaseId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - /** @description Unauthorized - Invalid or missing API key */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Test target or test case not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; + /** + * Retrieve a test case + * @description Get detailed information about a specific test case + */ + get: { + parameters: { + query?: never; + header?: never; + path: { + /** @description The ID of the test target */ + testTargetId: string; + /** @description The ID of the test case */ + testCaseId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Test case details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TestCaseResponse"]; + }; + }; + /** @description Unauthorized - Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Test target or test case not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/config": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - /** - * Retrieve test target configuration - * @description Get the configuration of a specific test target - */ - get: { - parameters: { - query?: { - /** @description Optional ID of the environment to use for the test target configuration */ - environmentId?: string; + "/apiKey/v2/test-targets/{testTargetId}/config": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - header?: never; - path: { - /** @description The ID of the test target */ - testTargetId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Test target configuration */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/plain": string; - }; + /** + * Retrieve test target configuration + * @description Get the configuration of a specific test target + */ + get: { + parameters: { + query: { + /** @description Optional ID of the environment to use for the test target configuration */ + environmentId?: string; + /** @description URL of the app to test */ + url: string; + /** @description Directory to write the playwright output to */ + outputDir: string; + /** @description Whether to run the test in headless mode */ + headless?: boolean; + }; + header?: never; + path: { + /** @description The ID of the test target */ + testTargetId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Test target configuration */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + }; }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/apiKey/v2/test-targets/{testTargetId}/discoveries": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - get?: never; - put?: never; - /** - * Create a discovery - * @description Create a new test case discovery with a given name and prompt - */ - post: { - parameters: { - query?: never; - header?: never; - path: { - /** @description The ID of the test target */ - testTargetId: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - /** - * @description Name of the discovery - * @example foo - */ - name: string; - /** - * @description The prompt describing what to discover - * @example make sure current time is visible - */ - prompt: string; - /** @description Optional URL path for the entry point */ - entryPointUrlPath?: string | null; - /** - * Format: uuid - * @description Optional ID of a prerequisite test case - */ - prerequisiteId?: string | null; - /** @description Optional external identifier */ - externalId?: string | null; - /** @description Optional list of tag IDs to assign */ - assignedTagIds?: string[] | null; - /** - * Format: uuid - * @description Optional ID of the folder to place the discovery in - */ - folderId?: string | null; - }; - }; - }; - responses: { - /** @description Discovery created successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - /** - * Format: uuid - * @description The ID of the created discovery - */ - discoveryId: string; - /** - * Format: uuid - * @description The ID of the associated test case - */ - testCaseId: string; + "/apiKey/v2/test-targets/{testTargetId}/discoveries": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create a discovery + * @description Create a new test case discovery with a given name and prompt + */ + post: { + parameters: { + query?: never; + header?: never; + path: { + /** @description The ID of the test target */ + testTargetId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** + * @description Name of the discovery + * @example foo + */ + name: string; + /** + * @description The prompt describing what to discover + * @example make sure current time is visible + */ + prompt: string; + /** @description Optional URL path for the entry point */ + entryPointUrlPath?: string | null; + /** + * Format: uuid + * @description Optional ID of a prerequisite test case + */ + prerequisiteId?: string | null; + /** @description Optional external identifier */ + externalId?: string | null; + /** @description Optional list of tag IDs to assign */ + assignedTagIds?: string[] | null; + /** + * Format: uuid + * @description Optional ID of the folder to place the discovery in + */ + folderId?: string | null; + }; + }; + }; + responses: { + /** @description Discovery created successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** + * Format: uuid + * @description The ID of the created discovery + */ + discoveryId: string; + /** + * Format: uuid + * @description The ID of the associated test case + */ + testCaseId: string; + }; + }; + }; + /** @description Invalid request body */ + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Unauthorized - Invalid or missing API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Test target not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; }; - }; - }; - /** @description Invalid request body */ - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Unauthorized - Invalid or missing API key */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Test target not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; }; - }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; } export type webhooks = Record; export interface components { - schemas: { - ExecutionContext: - | { - /** - * @example github - * @enum {string} - */ - source?: "github"; - /** @example 123 */ - issueNumber?: number | null; - /** @example refs/heads/main */ - ref?: string | null; - /** @example abc123def456 */ - sha?: string | null; - /** @example my-repo */ - repo?: string; - /** @example repo-owner */ - owner?: string; - triggeredBy?: { + schemas: { + ExecutionContext: { /** - * @example USER + * @example github * @enum {string} */ - type?: "USER"; - /** @example user123 */ - userId?: string; - } | null; - /** @example node-123 */ - nodeId?: string | null; - } - | { - /** - * @example azureDevOps - * @enum {string} - */ - source?: "azureDevOps"; - /** @example token123 */ - accessToken?: string; - /** @example my-org */ - organization?: string; - /** @example my-project */ - project?: string; - /** @example repo-123 */ - repositoryId?: string; - /** @example abc123def456 */ - sha?: string | null; - /** @example refs/heads/main */ - ref?: string | null; - /** @example 101 */ - pullRequestId?: number | null; - triggeredBy?: { + source?: "github"; + /** @example 123 */ + issueNumber?: number | null; + /** @example refs/heads/main */ + ref?: string | null; + /** @example abc123def456 */ + sha?: string | null; + /** @example my-repo */ + repo?: string; + /** @example repo-owner */ + owner?: string; + triggeredBy?: { + /** + * @example USER + * @enum {string} + */ + type?: "USER"; + /** @example user123 */ + userId?: string; + } | null; + /** @example node-123 */ + nodeId?: string | null; + } | { /** - * @example USER + * @example azureDevOps * @enum {string} */ - type?: "USER"; - /** @example user123 */ - userId?: string; - } | null; - /** @example thread-123 */ - threadId?: string | null; - } - | { - /** - * @example discovery - * @enum {string} - */ - source?: "discovery"; - /** @example A discovery test */ - description?: string; - triggeredBy?: - | { + source?: "azureDevOps"; + /** @example token123 */ + accessToken?: string; + /** @example my-org */ + organization?: string; + /** @example my-project */ + project?: string; + /** @example repo-123 */ + repositoryId?: string; + /** @example abc123def456 */ + sha?: string | null; + /** @example refs/heads/main */ + ref?: string | null; + /** @example 101 */ + pullRequestId?: number | null; + triggeredBy?: { + /** + * @example USER + * @enum {string} + */ + type?: "USER"; + /** @example user123 */ + userId?: string; + } | null; + /** @example thread-123 */ + threadId?: string | null; + } | { + /** + * @example discovery + * @enum {string} + */ + source?: "discovery"; + /** @example A discovery test */ + description?: string; + triggeredBy?: { /** * @example INITIAL * @enum {string} */ type?: "INITIAL"; - } - | { + } | { /** * @example USER * @enum {string} @@ -1189,59 +1195,54 @@ export interface components { type?: "USER"; /** @example user123 */ userId?: string; - }; - } - | { - /** - * @example manual - * @enum {string} - */ - source?: "manual"; - /** @example A manual trigger */ - description?: string; - triggeredBy?: { + }; + } | { /** - * @example USER + * @example manual * @enum {string} */ - type?: "USER"; - /** @example user123 */ - userId?: string; - }; - } - | { - /** - * @example scheduled - * @enum {string} - */ - source?: "scheduled"; - triggeredBy?: { + source?: "manual"; + /** @example A manual trigger */ + description?: string; + triggeredBy?: { + /** + * @example USER + * @enum {string} + */ + type?: "USER"; + /** @example user123 */ + userId?: string; + }; + } | { + /** + * @example scheduled + * @enum {string} + */ + source?: "scheduled"; + triggeredBy?: { + /** + * @example USER + * @enum {string} + */ + type?: "USER"; + /** @example user123 */ + userId?: string; + } | null; + } | { /** - * @example USER + * @example proposal * @enum {string} */ - type?: "USER"; - /** @example user123 */ - userId?: string; - } | null; - } - | { - /** - * @example proposal - * @enum {string} - */ - source?: "proposal"; - /** @example A proposal trigger */ - description?: string; - triggeredBy?: - | { + source?: "proposal"; + /** @example A proposal trigger */ + description?: string; + triggeredBy?: { /** * @example INITIAL * @enum {string} */ type?: "INITIAL"; - } - | { + } | { /** * @example USER * @enum {string} @@ -1249,492 +1250,473 @@ export interface components { type?: "USER"; /** @example user123 */ userId?: string; - }; + }; }; - /** - * @description The variables to overwrite exclusively for this test run. - * @example { - * "SPACE_ID": [ - * "64ee32b4-7365-47a6-b5b0-2903b6ad849d" - * ] - * } - */ - Variables: { - [key: string]: string[]; - }; - TestTargetExecutionRequest: { - /** - * Format: uuid - * @description Unique identifier for the testTarget. - * @example 2e2bb27b-a19c-47ce-a9b6-cd1bd31622dc - */ - testTargetId: string; - /** - * Format: uri - * @description The URL of the test target for this run. - * @example https://example.com - */ - url: string; - context: components["schemas"]["ExecutionContext"]; - /** - * Format: environment name - * @description the environment name you want to run your test against - * @default default - */ - environmentName: string; - variablesToOverwrite?: components["schemas"]["Variables"]; - /** - * @default [] - * @example [ - * "tag1", - * "tag2" - * ] - */ - tags: string[]; - }; - TestTargetExecutionResponse: { - /** - * @description The URL the test target was executed. - * @example https://example.com - */ - testReportUrl?: string; - /** - * @description The test report from the run. - * @example some json - */ - testReport?: Record; - }; - TestReportContext: { - /** - * @description The source of the test trigger. - * @example manual - * @enum {string} - */ - source?: "manual"; - /** - * @description The description of the test context. - * @example manual trigger - */ - description?: string; - triggeredBy?: { - /** - * @description The type of entity triggering the test. - * @example USER - * @enum {string} - */ - type?: "USER"; /** - * Format: uuid - * @description The unique identifier of the user who triggered the test. - * @example 2e2bb27b-a19c-47ce-a9b6-cd1bd31622dc + * @description The variables to overwrite exclusively for this test run. + * @example { + * "SPACE_ID": [ + * "64ee32b4-7365-47a6-b5b0-2903b6ad849d" + * ] + * } */ - userId?: string; - }; - }; - TestResult: { - /** - * Format: uuid - * @description Unique identifier for the test result. - * @example 826c15af-644b-4b28-89b4-f50ff34e46b7 - */ - id?: string; - /** - * Format: uuid - * @description Unique identifier of the test report this result belongs to. - * @example 3435918b-3d29-4ebd-8c68-9a540532f45a - */ - testTargetId?: string; - /** - * Format: uuid - * @description Unique identifier of the test case this result belongs to. - * @example 5b844cf1-d597-4048-9e74-7c0f9ce3e2ee - */ - testCaseId?: string; - /** - * Format: date-time - * @description The timestamp when the test result was created. - * @example 2024-09-06T13:01:51.686Z - */ - createdAt?: string; - /** - * Format: date-time - * @description The timestamp when the test result was last updated. - * @example 2024-09-06T13:01:51.686Z - */ - updatedAt?: string; - /** - * @description The status of the specific test result, will be WAITING as long as the result is running, FAILED if the execution failed, PASSED if it succeeded, and ERROR if an internal error occurred. - * @enum {string} - */ - status?: "WAITING" | "PASSED" | "FAILED" | "ERROR"; - /** - * @description The error that has occurred during execution - only set if the status is FAILED or ERROR. - * @example TimeoutError: locator.click: Timeout 30000ms exceeded. - */ - errorMessage?: string | null; - /** - * @description Link to the playwright trace of the test execution - only set once the test result is finished (PASSED or FAILED). - * @example https://storage.googleapis.com/automagically-traces/826c15af-644b-4b28-89b4-f50ff34e46b7-trace.zip - */ - traceUrl?: string | null; - }; - TestReport: { - /** - * Format: uuid - * @description Unique identifier for the test report. - * @example 826c15af-644b-4b28-89b4-f50ff34e46b7 - */ - id?: string; - /** - * Format: uuid - * @description The unique identifier of the test target. - * @example 3435918b-3d29-4ebd-8c68-9a540532f45a - */ - testTargetId?: string; - /** - * Format: date-time - * @description The timestamp when the test report was created. - * @example 2024-09-06T13:01:51.686Z - */ - createdAt?: string; - /** - * Format: date-time - * @description The timestamp when the test report was last updated. - * @example 2024-09-06T13:01:51.686Z - */ - updatedAt?: string; - /** - * Format: uri - * @description The URL where the test execution was performed. - * @example https://en.wikipedia.org/ - */ - executionUrl?: string; - /** - * @description The status of the test report, will be WAITING as long as any result is running, FAILED if the report is done but has any failed result and PASSED if all test results are done and successful - * @enum {string} - */ - status?: "WAITING" | "PASSED" | "FAILED"; - context?: components["schemas"]["TestReportContext"]; - testResults?: components["schemas"]["TestResult"][]; - }; - PrivateLocationInfo: { - /** @enum {string} */ - status: "OFFLINE" | "ONLINE"; - /** - * Format: uri - * @example https://example.com:3128 - */ - address: string; - /** @example my-private-location */ - name: string; - }[]; - TestReportResponse: { - /** - * Format: uri - * @description The URL where the test report can be accessed. - * @example https://app.octomind.dev/testreports/826c15af-644b-4b28-89b4-f50ff34e46b7 - */ - testReportUrl?: string; - testReport?: components["schemas"]["TestReport"]; - }; - TestCasesResponse: components["schemas"]["TestCaseResponse"][]; - TestCaseResponse: { - /** Format: uuid */ - id?: string; - /** Format: uuid */ - testTargetId?: string; - type?: string | null; - elements?: { - /** Format: uuid */ - id?: string; - index?: number; - interaction?: { - /** Format: uuid */ - id?: string; - /** @enum {string} */ - action?: - | "EXTRACT" - | "ENTER_TEXT" - | "CLICK" - | "SELECT_OPTION" - | "TYPE_TEXT" - | "KEY_PRESS" - | "HOVER" - | "UPLOAD" - | "GO_TO" - | "DRAG_AND_DROP" - | "CLOSE_PAGE" - | "OPEN_EMAIL"; - calledWith?: string | null; - /** Format: uuid */ - testCaseElementId?: string; - } | null; - assertion?: { - /** Format: uuid */ - id?: string; - /** @enum {string} */ - expectation?: - | "VISIBLE" - | "NOT_VISIBLE" - | "TO_BE_CHECKED" - | "NOT_TO_BE_CHECKED" - | "TO_HAVE_VALUE" - | "TO_CONTAIN_TEXT" - | "TO_HAVE_STYLE"; - calledWith?: string | null; - /** Format: uuid */ - testCaseElementId?: string; - } | null; - scrollState?: Record | null; - selectors?: { - /** Format: uuid */ - id?: string; - index?: number; - selector?: string; - /** @enum {string} */ - selectorType?: "TEXT" | "LABEL" | "PLACEHOLDER" | "ROLE"; - options?: { - name?: string; - } | null; - /** Format: uuid */ - testCaseElementId?: string; - scrollStateId?: string | null; + Variables: { + [key: string]: string[]; + }; + TestTargetExecutionRequest: { + /** + * Format: uuid + * @description Unique identifier for the testTarget. + * @example 2e2bb27b-a19c-47ce-a9b6-cd1bd31622dc + */ + testTargetId: string; + /** + * Format: uri + * @description The URL of the test target for this run. + * @example https://example.com + */ + url: string; + context: components["schemas"]["ExecutionContext"]; + /** + * Format: environment name + * @description the environment name you want to run your test against + * @default default + */ + environmentName: string; + variablesToOverwrite?: components["schemas"]["Variables"]; + /** + * @default [] + * @example [ + * "tag1", + * "tag2" + * ] + */ + tags: string[]; + }; + TestTargetExecutionResponse: { + /** + * @description The URL the test target was executed. + * @example https://example.com + */ + testReportUrl?: string; + /** + * @description The test report from the run. + * @example some json + */ + testReport?: Record; + }; + TestReportContext: { + /** + * @description The source of the test trigger. + * @example manual + * @enum {string} + */ + source?: "manual"; + /** + * @description The description of the test context. + * @example manual trigger + */ + description?: string; + triggeredBy?: { + /** + * @description The type of entity triggering the test. + * @example USER + * @enum {string} + */ + type?: "USER"; + /** + * Format: uuid + * @description The unique identifier of the user who triggered the test. + * @example 2e2bb27b-a19c-47ce-a9b6-cd1bd31622dc + */ + userId?: string; + }; + }; + TestResult: { + /** + * Format: uuid + * @description Unique identifier for the test result. + * @example 826c15af-644b-4b28-89b4-f50ff34e46b7 + */ + id?: string; + /** + * Format: uuid + * @description Unique identifier of the test report this result belongs to. + * @example 3435918b-3d29-4ebd-8c68-9a540532f45a + */ + testTargetId?: string; + /** + * Format: uuid + * @description Unique identifier of the test case this result belongs to. + * @example 5b844cf1-d597-4048-9e74-7c0f9ce3e2ee + */ + testCaseId?: string; + /** + * Format: date-time + * @description The timestamp when the test result was created. + * @example 2024-09-06T13:01:51.686Z + */ + createdAt?: string; + /** + * Format: date-time + * @description The timestamp when the test result was last updated. + * @example 2024-09-06T13:01:51.686Z + */ + updatedAt?: string; + /** + * @description The status of the specific test result, will be WAITING as long as the result is running, FAILED if the execution failed, PASSED if it succeeded, and ERROR if an internal error occurred. + * @enum {string} + */ + status?: "WAITING" | "PASSED" | "FAILED" | "ERROR"; + /** + * @description The error that has occurred during execution - only set if the status is FAILED or ERROR. + * @example TimeoutError: locator.click: Timeout 30000ms exceeded. + */ + errorMessage?: string | null; + /** + * @description Link to the playwright trace of the test execution - only set once the test result is finished (PASSED or FAILED). + * @example https://storage.googleapis.com/automagically-traces/826c15af-644b-4b28-89b4-f50ff34e46b7-trace.zip + */ + traceUrl?: string | null; + }; + TestReport: { + /** + * Format: uuid + * @description Unique identifier for the test report. + * @example 826c15af-644b-4b28-89b4-f50ff34e46b7 + */ + id?: string; + /** + * Format: uuid + * @description The unique identifier of the test target. + * @example 3435918b-3d29-4ebd-8c68-9a540532f45a + */ + testTargetId?: string; + /** + * Format: date-time + * @description The timestamp when the test report was created. + * @example 2024-09-06T13:01:51.686Z + */ + createdAt?: string; + /** + * Format: date-time + * @description The timestamp when the test report was last updated. + * @example 2024-09-06T13:01:51.686Z + */ + updatedAt?: string; + /** + * Format: uri + * @description The URL where the test execution was performed. + * @example https://en.wikipedia.org/ + */ + executionUrl?: string; + /** + * @description The status of the test report, will be WAITING as long as any result is running, FAILED if the report is done but has any failed result and PASSED if all test results are done and successful + * @enum {string} + */ + status?: "WAITING" | "PASSED" | "FAILED"; + context?: components["schemas"]["TestReportContext"]; + testResults?: components["schemas"]["TestResult"][]; + }; + PrivateLocationInfo: { + /** @enum {string} */ + status: "OFFLINE" | "ONLINE"; + /** + * Format: uri + * @example https://example.com:3128 + */ + address: string; + /** @example my-private-location */ + name: string; }[]; - /** Format: uuid */ - testCaseId?: string; - ignoreFailure?: boolean; - }[]; - /** Format: date-time */ - createdAt?: string; - /** Format: date-time */ - updatedAt?: string; - description?: string; - /** @enum {string} */ - status?: "ENABLED" | "DRAFT"; - externalId?: string | null; - entryPointUrlPath?: string | null; - tags?: string[]; - /** @enum {string} */ - createdBy?: "EDIT"; - /** @enum {string} */ - runStatus?: "ON" | "OFF"; - prerequisiteId?: string | null; - proposalRunId?: string | null; - folderId?: string | null; - discovery?: { - /** Format: uuid */ - id?: string; - freePrompt?: string; - traceUrl?: string | null; - traceJsonManifestUrl?: string | null; - /** @enum {string} */ - status?: "OUTDATED"; - abortCause?: string | null; - message?: string | null; - /** Format: uuid */ - testCaseId?: string; - lastJobExecutionName?: string | null; - /** Format: date-time */ - createdAt?: string; - /** Format: date-time */ - updatedAt?: string; - executedTestCaseElements?: string[]; - testCase?: { - /** Format: uuid */ - id?: string; - /** Format: uuid */ - testTargetId?: string; - description?: string; - /** Format: date-time */ - createdAt?: string; - /** Format: date-time */ - updatedAt?: string; - entryPointUrlPath?: string | null; - type?: string | null; - /** @enum {string} */ - status?: "ENABLED"; - /** @enum {string} */ - runStatus?: "ON"; - /** @enum {string} */ - interactionStatus?: "NEW"; - /** @enum {string} */ - createdBy?: "EDIT"; - proposalRunId?: string | null; - externalId?: string | null; - folderId?: string | null; - prerequisiteId?: string | null; - /** Format: uuid */ - predecessorId?: string; - testTarget?: { + TestReportResponse: { + /** + * Format: uri + * @description The URL where the test report can be accessed. + * @example https://app.octomind.dev/testreports/826c15af-644b-4b28-89b4-f50ff34e46b7 + */ + testReportUrl?: string; + testReport?: components["schemas"]["TestReport"]; + }; + TestCasesResponse: components["schemas"]["TestCaseResponse"][]; + TestCaseResponse: { /** Format: uuid */ - id?: string; - app?: string; + id: string; + /** Format: uuid */ + testTargetId: string; + type?: string | null; + elements?: { + /** Format: uuid */ + id?: string; + index?: number; + interaction?: { + /** Format: uuid */ + id?: string; + /** @enum {string} */ + action?: "EXTRACT" | "ENTER_TEXT" | "CLICK" | "SELECT_OPTION" | "TYPE_TEXT" | "KEY_PRESS" | "HOVER" | "UPLOAD" | "GO_TO" | "DRAG_AND_DROP" | "CLOSE_PAGE" | "OPEN_EMAIL"; + calledWith?: string | null; + /** Format: uuid */ + testCaseElementId?: string; + } | null; + assertion?: { + /** Format: uuid */ + id?: string; + /** @enum {string} */ + expectation?: "VISIBLE" | "NOT_VISIBLE" | "TO_BE_CHECKED" | "NOT_TO_BE_CHECKED" | "TO_HAVE_VALUE" | "TO_CONTAIN_TEXT" | "TO_HAVE_STYLE"; + calledWith?: string | null; + /** Format: uuid */ + testCaseElementId?: string; + } | null; + scrollState?: Record | null; + selectors?: { + /** Format: uuid */ + id?: string; + index?: number; + selector?: string; + /** @enum {string} */ + selectorType?: "TEXT" | "LABEL" | "PLACEHOLDER" | "ROLE"; + options?: { + name?: string; + } | null; + /** Format: uuid */ + testCaseElementId?: string; + scrollStateId?: string | null; + }[]; + /** Format: uuid */ + testCaseId?: string; + ignoreFailure?: boolean; + }[]; /** Format: date-time */ createdAt?: string; /** Format: date-time */ updatedAt?: string; + description?: string; + /** @enum {string} */ + status?: "ENABLED" | "DRAFT"; + externalId?: string | null; + entryPointUrlPath?: string | null; + tags?: string[]; + /** @enum {string} */ + createdBy?: "EDIT"; + /** @enum {string} */ + runStatus?: "ON" | "OFF"; + prerequisiteId?: string | null; + proposalRunId?: string | null; + folderId?: string | null; + discovery?: { + /** Format: uuid */ + id?: string; + freePrompt?: string; + traceUrl?: string | null; + traceJsonManifestUrl?: string | null; + /** @enum {string} */ + status?: "OUTDATED"; + abortCause?: string | null; + message?: string | null; + /** Format: uuid */ + testCaseId?: string; + lastJobExecutionName?: string | null; + /** Format: date-time */ + createdAt?: string; + /** Format: date-time */ + updatedAt?: string; + executedTestCaseElements?: string[]; + testCase?: { + /** Format: uuid */ + id?: string; + /** Format: uuid */ + testTargetId?: string; + description?: string; + /** Format: date-time */ + createdAt?: string; + /** Format: date-time */ + updatedAt?: string; + entryPointUrlPath?: string | null; + type?: string | null; + /** @enum {string} */ + status?: "ENABLED"; + /** @enum {string} */ + runStatus?: "ON"; + /** @enum {string} */ + interactionStatus?: "NEW"; + /** @enum {string} */ + createdBy?: "EDIT"; + proposalRunId?: string | null; + externalId?: string | null; + folderId?: string | null; + prerequisiteId?: string | null; + /** Format: uuid */ + predecessorId?: string; + testTarget?: { + /** Format: uuid */ + id?: string; + app?: string; + /** Format: date-time */ + createdAt?: string; + /** Format: date-time */ + updatedAt?: string; + /** Format: uuid */ + orgId?: string; + testIdAttribute?: string | null; + timeoutPerStep?: number; + }; + }; + }; + }; + SuccessResponse: { + /** + * @description Indicates whether the operation was successful. + * @example true + */ + success?: boolean; + }; + UnregisterRequest: { + name?: string; + }; + RegisterRequest: { + name?: string; + registrationData?: { + /** @example secret22 */ + proxypass?: string; + /** @example user */ + proxyuser?: string; + /** + * @description the address of the remote endpoint. IP and port + * @example 34.45.23.22:23455 + */ + address?: string; + }; + }; + EnvironmentsResponse: components["schemas"]["EnvironmentResponse"][]; + EnvironmentResponse: { + /** Format: uuid */ + id: string; + name: string; /** Format: uuid */ - orgId?: string; - testIdAttribute?: string | null; - timeoutPerStep?: number; - }; + testTargetId: string; + /** Format: date-time */ + updatedAt?: string; + type: string; + /** Format: url */ + discoveryUrl?: string; + additionalHeaderFields?: { + [key: string]: string; + } | null; + testAccount?: { + username?: string; + password?: string; + otpInitializerKey?: string | null; + /** Format: date-time */ + updatedAt?: string; + } | null; + basicAuth?: { + username?: string; + password?: string; + /** Format: date-time */ + updatedAt?: string; + } | null; + privateLocation?: { + /** Format: uuid */ + id?: string; + name?: string; + status?: string; + type?: string; + }; }; - }; - }; - SuccessResponse: { - /** - * @description Indicates whether the operation was successful. - * @example true - */ - success?: boolean; - }; - UnregisterRequest: { - name?: string; - }; - RegisterRequest: { - name?: string; - registrationData?: { - /** @example secret22 */ - proxypass?: string; - /** @example user */ - proxyuser?: string; - /** - * @description the address of the remote endpoint. IP and port - * @example 34.45.23.22:23455 - */ - address?: string; - }; - }; - EnvironmentsResponse: components["schemas"]["EnvironmentResponse"][]; - EnvironmentResponse: { - /** Format: uuid */ - id?: string; - name?: string; - /** Format: uuid */ - testTargetId?: string; - /** Format: date-time */ - updatedAt?: string; - type?: string; - /** Format: url */ - discoveryUrl?: string; - additionalHeaderFields?: { - [key: string]: string; - } | null; - testAccount?: { - username?: string; - password?: string; - otpInitializerKey?: string | null; - /** Format: date-time */ - updatedAt?: string; - } | null; - basicAuth?: { - username?: string; - password?: string; - /** Format: date-time */ - updatedAt?: string; - } | null; - privateLocation?: { - /** Format: uuid */ - id?: string; - name?: string; - status?: string; - type?: string; - }; - }; - TestSuiteRequestCommons: { - /** - * Format: uuid - * @description The test case to run before all test cases - */ - beforeAll?: string | null; - /** - * Format: uuid - * @description The test case to run after all test cases - */ - afterAll?: string | null; - /** - * Format: uuid - * @description The test case to run before each test case in the suite - */ - beforeEach?: string | null; - /** - * Format: uuid - * @description The test case to run after each test case in the suite - */ - afterEach?: string | null; + TestSuiteRequestCommons: { + /** + * Format: uuid + * @description The test case to run before all test cases + */ + beforeAll?: string | null; + /** + * Format: uuid + * @description The test case to run after all test cases + */ + afterAll?: string | null; + /** + * Format: uuid + * @description The test case to run before each test case in the suite + */ + beforeEach?: string | null; + /** + * Format: uuid + * @description The test case to run after each test case in the suite + */ + afterEach?: string | null; + }; + TestSuitePatchRequest: { + /** + * @description The name of the test suite + * @example regression for chat + */ + name?: string | null; + /** + * @description The test cases to include in the suite + * @example [ + * "5b844cf1-d597-4048-9e74-7c0f9ce3e2ee" + * ] + */ + testCases?: string[] | null; + /** + * @description the environment name you want to run your test suite against + * @example default + */ + environmentName?: string | null; + } & components["schemas"]["TestSuiteRequestCommons"]; + TestSuiteCreateRequest: { + /** + * @description The name of the test suite + * @example regression for chat + */ + name?: string; + /** + * @description The test cases to include in the suite + * @example [ + * "5b844cf1-d597-4048-9e74-7c0f9ce3e2ee" + * ] + */ + testCases?: string[]; + /** + * @description the environment name you want to run your test suite against + * @example default + */ + environmentName?: string; + } & components["schemas"]["TestSuiteRequestCommons"]; + TestSuiteResponse: { + /** Format: uuid */ + id?: string; + } & components["schemas"]["TestSuiteCreateRequest"]; + TestSuitesResponse: components["schemas"]["TestSuiteResponse"][]; + ZodResponse: { + /** + * @description What error code happened while parsing the request + * @example invalid_type + */ + code?: string; + /** + * @description What the expected type was + * @example object + */ + expected?: string; + /** + * @description What the actual passed type was + * @example string + */ + received?: string; + path?: string[]; + /** + * @description Human-readable message of the error that occurred while parsing. + * @example Expected object, received string + */ + message?: string; + }[]; }; - TestSuitePatchRequest: { - /** - * @description The name of the test suite - * @example regression for chat - */ - name?: string | null; - /** - * @description The test cases to include in the suite - * @example [ - * "5b844cf1-d597-4048-9e74-7c0f9ce3e2ee" - * ] - */ - testCases?: string[] | null; - /** - * @description the environment name you want to run your test suite against - * @example default - */ - environmentName?: string | null; - } & components["schemas"]["TestSuiteRequestCommons"]; - TestSuiteCreateRequest: { - /** - * @description The name of the test suite - * @example regression for chat - */ - name?: string; - /** - * @description The test cases to include in the suite - * @example [ - * "5b844cf1-d597-4048-9e74-7c0f9ce3e2ee" - * ] - */ - testCases?: string[]; - /** - * @description the environment name you want to run your test suite against - * @example default - */ - environmentName?: string; - } & components["schemas"]["TestSuiteRequestCommons"]; - TestSuiteResponse: { - /** Format: uuid */ - id?: string; - } & components["schemas"]["TestSuiteCreateRequest"]; - TestSuitesResponse: components["schemas"]["TestSuiteResponse"][]; - ZodResponse: { - /** - * @description What error code happened while parsing the request - * @example invalid_type - */ - code?: string; - /** - * @description What the expected type was - * @example object - */ - expected?: string; - /** - * @description What the actual passed type was - * @example string - */ - received?: string; - path?: string[]; - /** - * @description Human-readable message of the error that occurred while parsing. - * @example Expected object, received string - */ - message?: string; - }[]; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; } export type $defs = Record; export type operations = Record; diff --git a/src/debugtopus/index.ts b/src/debugtopus/index.ts index f732aa0..dd9db82 100644 --- a/src/debugtopus/index.ts +++ b/src/debugtopus/index.ts @@ -6,12 +6,11 @@ import path, { dirname } from "path"; import fs from "fs/promises"; import { ensureChromiumIsInstalled } from "./installation"; import { + getEnvironments, getPlaywrightCode, getPlaywrightConfig, getTestCases, - getTestTarget, - TestCase, -} from "./octomind-api"; +} from "../tools"; type DebugtopusOptions = { id?: string; @@ -23,14 +22,6 @@ type DebugtopusOptions = { headless?: boolean; }; -type BasicAuth = { username: string; password: string }; - -type Environment = { - id: string; - basicAuth?: BasicAuth; - type: "DEFAULT" | "ADDITIONAL"; -}; - const getPackageRootLevel = (appDir: string): string => { let infiniteLoopPrevention = 5; let rootDir = appDir; @@ -59,9 +50,9 @@ type TestDirectories = { packageRootDir: string; }; -type TestCaseWithCode = TestCase & { code: string }; +type TestCaseCodeWithId = { id: string; description?: string; code: string }; -const getUniqueFilename = (tempDir: string, testCase: TestCaseWithCode) => { +const getUniqueFilename = (tempDir: string, testCase: TestCaseCodeWithId) => { const fileNameUUID = randomUUID(); const name = testCase.description ? testCase.description.replaceAll(path.sep, "-") @@ -108,7 +99,7 @@ const writeConfigAndTests = ({ config, dirs, }: { - testCasesWithCode: TestCaseWithCode[]; + testCasesWithCode: TestCaseCodeWithId[]; config: string; dirs: TestDirectories; }): string[] => { @@ -179,30 +170,29 @@ export const runDebugtopus = async (options: DebugtopusOptions) => { environmentId: options.environmentId, }; - const testTarget = await getTestTarget({ - testTargetId: options.testTargetId, - token: options.token, - octomindUrl: options.octomindUrl, - }); - - let testCasesWithCode: TestCaseWithCode[] = []; + let testCasesWithCode: TestCaseCodeWithId[] = []; if (options.id) { testCasesWithCode = [ { id: options.id, code: await getPlaywrightCode({ testCaseId: options.id, + executionUrl: options.url, ...baseApiOptions, }), }, ]; } else { const testCases = await getTestCases(baseApiOptions); + if (!testCases) { + throw new Error("no test cases found"); + } testCasesWithCode = await Promise.all( testCases.map(async (testCase) => ({ code: await getPlaywrightCode({ testCaseId: testCase.id, + executionUrl: options.url, ...baseApiOptions, }), ...testCase, @@ -210,23 +200,30 @@ export const runDebugtopus = async (options: DebugtopusOptions) => { ); } + const environments = await getEnvironments(baseApiOptions); + const environmentIdForConfig = options.environmentId ? options.environmentId - : testTarget.environments.find((env: Environment) => env.type === "DEFAULT") - .id; + : (environments ?? []).find((env) => env.type === "DEFAULT")?.id; + + if (!environmentIdForConfig) { + throw new Error("no environment found"); + } const dirs = await prepareDirectories(); const config = await getPlaywrightConfig({ testTargetId: options.testTargetId, - token: options.token, - octomindUrl: options.octomindUrl, url: options.url, outputDir: dirs.outputDir, environmentId: environmentIdForConfig, headless: options.headless, }); + if (!config) { + throw new Error("no config found"); + } + writeConfigAndTests({ testCasesWithCode, config, diff --git a/src/debugtopus/octomind-api.ts b/src/debugtopus/octomind-api.ts deleted file mode 100644 index 9013aea..0000000 --- a/src/debugtopus/octomind-api.ts +++ /dev/null @@ -1,140 +0,0 @@ -import axios, { AxiosError } from "axios"; - -export const getPlaywrightConfig = async ({ - testTargetId, - token, - octomindUrl, - outputDir, - url, - environmentId, - headless, -}: { - testTargetId: string; - token: string; - octomindUrl: string; - outputDir: string; - url: string; - environmentId: string; - headless?: boolean; -}): Promise => { - const searchParams = new URLSearchParams(); - searchParams.set("url", url); - searchParams.set("outputDir", outputDir); - searchParams.set("environmentId", environmentId); - if (headless) { - searchParams.set("headless", String(headless)); - } - - const endpoint = `${octomindUrl}/api/bearer/v1/test-targets/${testTargetId}/config?${searchParams.toString()}`; - - try { - const axiosResponse = await axios.get(endpoint, { - headers: { Authorization: `Bearer ${token}` }, - }); - return axiosResponse.data; - } catch (error) { - const responseBody = (error as AxiosError).response?.data; - const responseCode = (error as AxiosError).response?.status; - throw new Error( - `failed to get config from ${endpoint}: response body: '${JSON.stringify( - responseBody, - )}' statusCode: '${JSON.stringify(responseCode)}'`, - ); - } -}; - -export const getPlaywrightCode = async ({ - testCaseId, - token, - url, - octomindUrl, - testTargetId, - environmentId, -}: { - testCaseId: string; - testTargetId: string; - token: string; - url: string; - octomindUrl: string; - environmentId?: string; -}): Promise => { - const searchParams = new URLSearchParams(); - - searchParams.set("executionUrl", url); - if (environmentId) { - searchParams.set("environmentId", environmentId); - } - - const endpoint = `${octomindUrl}/api/bearer/v1/test-targets/${testTargetId}/test-cases/${testCaseId}/code?${searchParams.toString()}`; - - try { - const axiosResponse = await axios.get(endpoint, { - headers: { Authorization: `Bearer ${token}` }, - }); - return axiosResponse.data.testCode; - } catch (error) { - const responseBody = (error as AxiosError).response?.data; - const responseCode = (error as AxiosError).response?.status; - throw new Error( - `failed to get code from ${endpoint}: response body: '${JSON.stringify( - responseBody, - )}' statusCode: '${JSON.stringify(responseCode)}'`, - ); - } -}; - -export const getTestTarget = async ({ - testTargetId, - token, - octomindUrl, -}: { - testTargetId: string; - token: string; - octomindUrl: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -}): Promise => { - const endpoint = `${octomindUrl}/api/bearer/v1/test-targets/${testTargetId}`; - try { - const axiosResponse = await axios.get<[TestCase]>(endpoint, { - headers: { Authorization: `Bearer ${token}` }, - }); - return axiosResponse.data; - } catch (error) { - const responseBody = (error as AxiosError).response?.data; - const responseCode = (error as AxiosError).response?.status; - throw new Error( - `failed to get test-target from ${endpoint}: response body: '${JSON.stringify( - responseBody, - )}' statusCode: '${JSON.stringify(responseCode)}'`, - ); - } -}; - -export type TestCase = { id: string; description?: string }; - -export const getTestCases = async ({ - token, - octomindUrl, - testTargetId, -}: { - testTargetId: string; - token: string; - octomindUrl: string; -}): Promise => { - const endpoint = `${octomindUrl}/api/bearer/v1/test-targets/${testTargetId}/test-cases`; - try { - const axiosResponse = await axios.get<[TestCase]>(endpoint, { - headers: { Authorization: `Bearer ${token}` }, - params: { filter: JSON.stringify({ status: "ENABLED" }) }, - }); - return axiosResponse.data; - } catch (error) { - const responseBody = (error as AxiosError).response?.data; - const responseCode = (error as AxiosError).response?.status; - throw new Error( - `failed to get test-cases from ${endpoint}: response body: '${JSON.stringify( - responseBody, - )}' statusCode: '${JSON.stringify(responseCode)}'`, - ); - } -}; diff --git a/src/tools.ts b/src/tools.ts index 9ddf246..63f39c3 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -78,6 +78,31 @@ export const listEnvironments = async ( } }; +export const getEnvironments = async ( + options: listEnvironmentsOptions & { json?: boolean }, +): Promise => { + const { data, error } = await client.GET( + "/apiKey/v2/test-targets/{testTargetId}/environments", + { + params: { + path: { testTargetId: options.testTargetId }, + }, + }, + ); + + handleError(error); + + if (!data) { + throw new Error("no environments found"); + } + + if (options.json) { + outputResult(data); + } + + return data; +}; + const outputResult = (result: unknown): void => { console.log(JSON.stringify(result, null, 2)); }; @@ -388,8 +413,11 @@ export const deleteEnvironment = async (options: { export const getPlaywrightConfig = async (options: { testTargetId: string; environmentId?: string; + url: string; + outputDir: string; + headless?: boolean; json?: boolean; -}): Promise => { +}): Promise => { const { data, error } = await client.GET( "/apiKey/v2/test-targets/{testTargetId}/config", { @@ -399,16 +427,25 @@ export const getPlaywrightConfig = async (options: { }, query: { environmentId: options.environmentId, + url: options.url, + outputDir: options.outputDir, + headless: options.headless, }, }, + parseAs: "text", }, ); + console.log({ data, error }); + handleError(error); + if (!data) { + throw new Error("no config found"); + } + if (options.json) { outputResult(data); - return; } return data; @@ -417,8 +454,10 @@ export const getPlaywrightConfig = async (options: { export const getPlaywrightCode = async (options: { testTargetId: string; testCaseId: string; + environmentId?: string; + executionUrl: string; json?: boolean; -}): Promise => { +}): Promise => { const { data, error } = await client.GET( "/apiKey/v2/test-targets/{testTargetId}/test-cases/{testCaseId}/code", { @@ -427,24 +466,32 @@ export const getPlaywrightCode = async (options: { testTargetId: options.testTargetId, testCaseId: options.testCaseId, }, + query: { + environmentId: options.environmentId, + executionUrl: options.executionUrl, + }, }, }, ); handleError(error); + if (!data) { + console.log({ data, error }); + throw new Error("no test code found"); + } + if (options.json) { outputResult(data); - return; } - return data?.testCode; + return data.testCode; }; export const getTestCases = async (options: { testTargetId: string; json?: boolean; -}): Promise => { +}): Promise => { const { data, error } = await client.GET( "/apiKey/v2/test-targets/{testTargetId}/test-cases", { @@ -458,9 +505,12 @@ export const getTestCases = async (options: { handleError(error); + if (!data) { + throw new Error("no test cases found"); + } + if (options.json) { outputResult(data); - return; } return data; From b55e85f2af972e2ef942d997860ffe3774aad2b3 Mon Sep 17 00:00:00 2001 From: Maximilian Link Date: Thu, 24 Jul 2025 09:45:32 +0200 Subject: [PATCH 3/5] fixes --- src/cli.ts | 47 +++++++++++++++++++++++------------------ src/debugtopus/index.ts | 4 ---- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index cd46ac2..1aa5716 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -32,27 +32,6 @@ export const buildCmd = (): Command => { ) .version(version); - createCommandWithCommonOptions("debug") - .description("run test cases against local build") - .requiredOption("-u, --url ", "url the tests should run against") - .option( - "-i, --id ", - "id of the test case you want to run, if not provided will run all test cases in the test target", - ) - .option( - "-e, --environmentId ", - "id of the environment you want to run against, if not provided will run all test cases against the default environment", - ) - .option( - "-a, --testTargetId ", - "id of the test target of the test case", - ) - .option( - "--headless", - "if we should run headless without the UI of playwright and the browser", - ) - .action(runDebugtopus); - program .command("init") .description("Initialize configuration by setting up API key") @@ -121,6 +100,32 @@ export const buildCmd = (): Command => { }, ); + createCommandWithCommonOptions("debug") + .description("run test cases against local build") + .requiredOption("-u, --url ", "url the tests should run against") + .option( + "-i, --id ", + "id of the test case you want to run, if not provided will run all test cases in the test target", + ) + .option( + "-e, --environmentId ", + "id of the environment you want to run against, if not provided will run all test cases against the default environment", + ) + .option( + "-a, --testTargetId ", + "id of the test target of the test case", + ) + .option( + "--headless", + "if we should run headless without the UI of playwright and the browser", + ) + .action((_, options) => + runDebugtopus({ + ...options, + testTargetId: resolveTestTargetId(options.testTargetId), + }), + ); + createCommandWithCommonOptions("execute") .description("Execute test cases") .requiredOption("-u, --url ", "URL to test") diff --git a/src/debugtopus/index.ts b/src/debugtopus/index.ts index dd9db82..e785410 100644 --- a/src/debugtopus/index.ts +++ b/src/debugtopus/index.ts @@ -15,9 +15,7 @@ import { type DebugtopusOptions = { id?: string; testTargetId: string; - token: string; url: string; - octomindUrl: string; environmentId?: string; headless?: boolean; }; @@ -164,9 +162,7 @@ const runTests = async ({ export const runDebugtopus = async (options: DebugtopusOptions) => { const baseApiOptions = { testTargetId: options.testTargetId, - token: options.token, url: options.url, - octomindUrl: options.octomindUrl, environmentId: options.environmentId, }; From 47ce17c157f5ac5e5f314a0ef27fce195a9ea00f Mon Sep 17 00:00:00 2001 From: mxlnk Date: Thu, 24 Jul 2025 10:21:09 +0200 Subject: [PATCH 4/5] Update src/tools.ts --- src/tools.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools.ts b/src/tools.ts index 3f37089..75fb836 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -439,7 +439,6 @@ export const getPlaywrightConfig = async (options: { }, ); - console.log({ data, error }); handleError(error); From c53c3f3b4071b3630550c75c691c9a5d9e1809d5 Mon Sep 17 00:00:00 2001 From: Maximilian Link Date: Thu, 24 Jul 2025 10:25:04 +0200 Subject: [PATCH 5/5] improve --- src/debugtopus/index.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/debugtopus/index.ts b/src/debugtopus/index.ts index e785410..a50603f 100644 --- a/src/debugtopus/index.ts +++ b/src/debugtopus/index.ts @@ -196,11 +196,13 @@ export const runDebugtopus = async (options: DebugtopusOptions) => { ); } - const environments = await getEnvironments(baseApiOptions); - - const environmentIdForConfig = options.environmentId - ? options.environmentId - : (environments ?? []).find((env) => env.type === "DEFAULT")?.id; + let environmentIdForConfig = options.environmentId; + if (!environmentIdForConfig) { + const environments = await getEnvironments(baseApiOptions); + environmentIdForConfig = environments.find( + (env) => env.type === "DEFAULT", + )?.id; + } if (!environmentIdForConfig) { throw new Error("no environment found");