From b0ac8f86964255ff9fcff0d4c0258d5ca4546ffb Mon Sep 17 00:00:00 2001 From: Fabian Both Date: Mon, 19 Jan 2026 10:14:41 +0100 Subject: [PATCH 1/3] use test case file name to filter local execution --- src/cli.ts | 6 ++--- src/debugtopus/index.ts | 54 ++++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index c451950..048ab06 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -181,8 +181,8 @@ export const buildCmd = (): CompletableCommand => { .helpGroup("execute") .requiredOption("-u, --url ", "Url the tests should run against") .option( - "-c, --test-case-id [uuid]", - "Id of the test case you want to run, if not provided will run all test cases in the test target", + "-n, --test-case-name [string]", + "Name of the test case you want to run, if not provided will run all test cases in the test target", ) .option( "-e, --environment-id [uuid]", @@ -420,7 +420,7 @@ export const buildCmd = (): CompletableCommand => { ) .addOption( new Option( - "-d, --dependency-path ", + "-d, --dependency-path [path]", "The path of to test case you want to use as dependency", ), ) diff --git a/src/debugtopus/index.ts b/src/debugtopus/index.ts index 29ffe8a..f3a04d8 100644 --- a/src/debugtopus/index.ts +++ b/src/debugtopus/index.ts @@ -12,7 +12,7 @@ import ora from "ora"; import { Open } from "unzipper"; import { OCTOMIND_FOLDER_NAME } from "../constants"; -import { findOctomindFolder } from "../helpers"; +import { findOctomindFolder, getAbsoluteFilePathInOctomindRoot } from "../helpers"; import { getEnvironments, getPlaywrightCode, @@ -21,7 +21,7 @@ import { } from "../tools"; import { client, handleError } from "../tools/client"; import { SyncTestCase } from "../tools/sync/types"; -import { readTestCasesFromDir } from "../tools/sync/yaml"; +import { loadTestCase, readTestCasesFromDir } from "../tools/sync/yaml"; import { getRelevantTestCases } from "../tools/yamlMutations/getRelevantTestCases"; import { ensureChromiumIsInstalled } from "./installation"; @@ -180,19 +180,31 @@ const runTests = async ({ } }; -const getFilteredTestCaseWithDependencies = ( - testCases: SyncTestCase[], - filterTestCaseId: string, -): SyncTestCase[] => { - const filteredTestCase = testCases.find( - (testCase) => testCase.id === filterTestCaseId, - ); +const getFilteredTestCaseWithDependencies = async ( + { testCases, filterTestCaseName, octomindRoot }: { + testCases: SyncTestCase[], + filterTestCaseName: string, + octomindRoot: string + } +): Promise<{relevantTestCases: SyncTestCase[], filterTestCaseId: string}> => { + const filterTestCaseFilePath = await getAbsoluteFilePathInOctomindRoot({ + octomindRoot, + filePath: filterTestCaseName, + }); + if (!filterTestCaseFilePath) { + throw new Error( + `Could not find ${filterTestCaseName} in folder ${octomindRoot}`, + ); + } + + const filteredTestCase = loadTestCase(filterTestCaseFilePath); if (!filteredTestCase) { - throw new Error(`Could not find test case with id ${filterTestCaseId}`); + throw new Error(`Could not find test case with id ${filterTestCaseName}`); } const testCasesById = Object.fromEntries(testCases.map((tc) => [tc.id, tc])); - return getRelevantTestCases(testCasesById, filteredTestCase); + const relevantTestCases = getRelevantTestCases(testCasesById, filteredTestCase); + return {relevantTestCases, filterTestCaseId: filteredTestCase.id} }; export const runDebugtopus = async (options: DebugtopusOptions) => { @@ -229,8 +241,8 @@ export const runDebugtopus = async (options: DebugtopusOptions) => { .filter((testCase) => options.grep ? testCase.description - ?.toLowerCase() - .includes(options.grep.toLowerCase()) + ?.toLowerCase() + .includes(options.grep.toLowerCase()) : true, ) .map(async (testCase) => ({ @@ -284,7 +296,7 @@ export const runDebugtopus = async (options: DebugtopusOptions) => { }; export const executeLocalTestCases = async ( - options: DebugtopusOptions, + options: Omit & { testCaseName?: string }, ): Promise => { const octomindRoot = await findOctomindFolder(); if (!octomindRoot) { @@ -296,11 +308,15 @@ export const executeLocalTestCases = async ( const throbber = ora("Generating code").start(); let testCases = readTestCasesFromDir(octomindRoot); - if (options.testCaseId) { - testCases = getFilteredTestCaseWithDependencies( + let filterTestCaseId: string | undefined = undefined; + if (options.testCaseName) { + const filtered = await getFilteredTestCaseWithDependencies({ testCases, - options.testCaseId, - ); + filterTestCaseName: options.testCaseName, + octomindRoot + }); + testCases = filtered.relevantTestCases; + filterTestCaseId = filtered.filterTestCaseId; } const body = { @@ -308,7 +324,7 @@ export const executeLocalTestCases = async ( testTargetId: options.testTargetId, executionUrl: options.url, environmentId: options.environmentId, - filterTestCaseIds: options.testCaseId ? [options.testCaseId] : undefined, + filterTestCaseIds: filterTestCaseId ? [filterTestCaseId] : undefined, }; const { error, response } = await client.POST( "/apiKey/beta/test-targets/{testTargetId}/code", From b00ead51f2adf338d3ed8831fad06a91c0a15ce0 Mon Sep 17 00:00:00 2001 From: Fabian Both Date: Mon, 19 Jan 2026 10:29:41 +0100 Subject: [PATCH 2/3] adapt test cases --- src/debugtopus/index.ts | 37 ++++++++++++++++--------- tests/debugtopus/index.spec.ts | 50 ++++++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/debugtopus/index.ts b/src/debugtopus/index.ts index f3a04d8..a79bce0 100644 --- a/src/debugtopus/index.ts +++ b/src/debugtopus/index.ts @@ -12,7 +12,10 @@ import ora from "ora"; import { Open } from "unzipper"; import { OCTOMIND_FOLDER_NAME } from "../constants"; -import { findOctomindFolder, getAbsoluteFilePathInOctomindRoot } from "../helpers"; +import { + findOctomindFolder, + getAbsoluteFilePathInOctomindRoot, +} from "../helpers"; import { getEnvironments, getPlaywrightCode, @@ -180,13 +183,18 @@ const runTests = async ({ } }; -const getFilteredTestCaseWithDependencies = async ( - { testCases, filterTestCaseName, octomindRoot }: { - testCases: SyncTestCase[], - filterTestCaseName: string, - octomindRoot: string - } -): Promise<{relevantTestCases: SyncTestCase[], filterTestCaseId: string}> => { +const getFilteredTestCaseWithDependencies = async ({ + testCases, + filterTestCaseName, + octomindRoot, +}: { + testCases: SyncTestCase[]; + filterTestCaseName: string; + octomindRoot: string; +}): Promise<{ + relevantTestCases: SyncTestCase[]; + filterTestCaseId: string; +}> => { const filterTestCaseFilePath = await getAbsoluteFilePathInOctomindRoot({ octomindRoot, filePath: filterTestCaseName, @@ -203,8 +211,11 @@ const getFilteredTestCaseWithDependencies = async ( } const testCasesById = Object.fromEntries(testCases.map((tc) => [tc.id, tc])); - const relevantTestCases = getRelevantTestCases(testCasesById, filteredTestCase); - return {relevantTestCases, filterTestCaseId: filteredTestCase.id} + const relevantTestCases = getRelevantTestCases( + testCasesById, + filteredTestCase, + ); + return { relevantTestCases, filterTestCaseId: filteredTestCase.id }; }; export const runDebugtopus = async (options: DebugtopusOptions) => { @@ -241,8 +252,8 @@ export const runDebugtopus = async (options: DebugtopusOptions) => { .filter((testCase) => options.grep ? testCase.description - ?.toLowerCase() - .includes(options.grep.toLowerCase()) + ?.toLowerCase() + .includes(options.grep.toLowerCase()) : true, ) .map(async (testCase) => ({ @@ -313,7 +324,7 @@ export const executeLocalTestCases = async ( const filtered = await getFilteredTestCaseWithDependencies({ testCases, filterTestCaseName: options.testCaseName, - octomindRoot + octomindRoot, }); testCases = filtered.relevantTestCases; filterTestCaseId = filtered.filterTestCaseId; diff --git a/tests/debugtopus/index.spec.ts b/tests/debugtopus/index.spec.ts index 4fba22e..6b97216 100644 --- a/tests/debugtopus/index.spec.ts +++ b/tests/debugtopus/index.spec.ts @@ -14,10 +14,14 @@ import { readZipFromResponseBody, } from "../../src/debugtopus/index"; import { ensureChromiumIsInstalled } from "../../src/debugtopus/installation"; -import { findOctomindFolder } from "../../src/helpers"; +import { + findOctomindFolder, + getAbsoluteFilePathInOctomindRoot, +} from "../../src/helpers"; import { client } from "../../src/tools/client"; import { getPlaywrightConfig } from "../../src/tools/playwright"; -import { readTestCasesFromDir } from "../../src/tools/sync/yaml"; +import { loadTestCase, readTestCasesFromDir } from "../../src/tools/sync/yaml"; +import { getRelevantTestCases } from "../../src/tools/yamlMutations/getRelevantTestCases"; import { createMockSyncTestCase } from "../mocks"; vi.mock("fs/promises"); @@ -28,6 +32,7 @@ vi.mock("../../src/tools/sync/yaml"); vi.mock("../../src/tools/playwright"); vi.mock("../../src/debugtopus/installation"); vi.mock("../../src/helpers"); +vi.mock("../../src/tools/yamlMutations/getRelevantTestCases"); vi.mock("child_process"); vi.mock("node:stream/promises"); vi.mock("util", () => ({ @@ -46,6 +51,11 @@ const mockedReadTestCasesFromDir = vi.mocked(readTestCasesFromDir); const mockedGetPlaywrightConfig = vi.mocked(getPlaywrightConfig); const mockedEnsureChromiumIsInstalled = vi.mocked(ensureChromiumIsInstalled); +const mockedGetAbsoluteFilePathInOctomindRoot = vi.mocked( + getAbsoluteFilePathInOctomindRoot, +); +const mockedLoadTestCase = vi.mocked(loadTestCase); +const mockedGetRelevantTestCases = vi.mocked(getRelevantTestCases); describe("debugtopus", () => { describe("readZipFromResponseBody", () => { @@ -282,20 +292,35 @@ describe("debugtopus", () => { ); }); - it("should filter test cases by testCaseId when provided", async () => { - setupExecutionMocks(); - mockedReadTestCasesFromDir.mockReturnValue([ + it("should filter test cases by testCaseName when provided", async () => { + const targetTestCase = createMockSyncTestCase({ id: "target-id" }); + const allTestCases = [ createMockSyncTestCase({ id: "other-1" }), - createMockSyncTestCase({ id: "target-id" }), + targetTestCase, createMockSyncTestCase({ id: "other-2" }), - ]); + ]; + + setupExecutionMocks(); + mockedReadTestCasesFromDir.mockReturnValue(allTestCases); + mockedGetAbsoluteFilePathInOctomindRoot.mockResolvedValue( + `${OCTOMIND_ROOT}/target-test.yaml`, + ); + mockedLoadTestCase.mockReturnValue(targetTestCase); + mockedGetRelevantTestCases.mockReturnValue([targetTestCase]); await executeLocalTestCases({ testTargetId: "test-target-id", url: "https://example.com", - testCaseId: "target-id", + testCaseName: "target-test.yaml", }); + expect(mockedGetAbsoluteFilePathInOctomindRoot).toHaveBeenCalledWith({ + octomindRoot: OCTOMIND_ROOT, + filePath: "target-test.yaml", + }); + expect(mockedLoadTestCase).toHaveBeenCalledWith( + `${OCTOMIND_ROOT}/target-test.yaml`, + ); expect(mockedClient.POST).toHaveBeenCalledWith( "/apiKey/beta/test-targets/{testTargetId}/code", expect.objectContaining({ @@ -307,19 +332,22 @@ describe("debugtopus", () => { ); }); - it("should throw an error if testCaseId does not match any test case", async () => { + it("should throw an error if testCaseName file is not found", async () => { vi.mocked(findOctomindFolder).mockResolvedValue(OCTOMIND_ROOT); mockedReadTestCasesFromDir.mockReturnValue([ createMockSyncTestCase({ id: "test-case-1" }), ]); + mockedGetAbsoluteFilePathInOctomindRoot.mockResolvedValue(null); await expect( executeLocalTestCases({ testTargetId: "test-target-id", url: "https://example.com", - testCaseId: "non-existent-id", + testCaseName: "non-existent.yaml", }), - ).rejects.toThrow("Could not find test case with id non-existent-id"); + ).rejects.toThrow( + `Could not find non-existent.yaml in folder ${OCTOMIND_ROOT}`, + ); }); }); }); From 0f0ab388c51ffe02dd73b7a0911bfa88dbb3d400 Mon Sep 17 00:00:00 2001 From: Fabian Both Date: Mon, 19 Jan 2026 10:57:26 +0100 Subject: [PATCH 3/3] rename to test case path --- src/cli.ts | 4 ++-- src/debugtopus/index.ts | 22 +++++++++++----------- tests/debugtopus/index.spec.ts | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 048ab06..1e8b77d 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -181,8 +181,8 @@ export const buildCmd = (): CompletableCommand => { .helpGroup("execute") .requiredOption("-u, --url ", "Url the tests should run against") .option( - "-n, --test-case-name [string]", - "Name of the test case you want to run, if not provided will run all test cases in the test target", + "-p, --test-case-path [string]", + "Path of the test case you want to run, if not provided will run all test cases in the test target", ) .option( "-e, --environment-id [uuid]", diff --git a/src/debugtopus/index.ts b/src/debugtopus/index.ts index a79bce0..05ad042 100644 --- a/src/debugtopus/index.ts +++ b/src/debugtopus/index.ts @@ -185,29 +185,29 @@ const runTests = async ({ const getFilteredTestCaseWithDependencies = async ({ testCases, - filterTestCaseName, + filterTestCasePath, octomindRoot, }: { testCases: SyncTestCase[]; - filterTestCaseName: string; + filterTestCasePath: string; octomindRoot: string; }): Promise<{ relevantTestCases: SyncTestCase[]; filterTestCaseId: string; }> => { - const filterTestCaseFilePath = await getAbsoluteFilePathInOctomindRoot({ + const absoluteTestCaseFilePath = await getAbsoluteFilePathInOctomindRoot({ octomindRoot, - filePath: filterTestCaseName, + filePath: filterTestCasePath, }); - if (!filterTestCaseFilePath) { + if (!absoluteTestCaseFilePath) { throw new Error( - `Could not find ${filterTestCaseName} in folder ${octomindRoot}`, + `Could not find ${filterTestCasePath} in folder ${octomindRoot}`, ); } - const filteredTestCase = loadTestCase(filterTestCaseFilePath); + const filteredTestCase = loadTestCase(absoluteTestCaseFilePath); if (!filteredTestCase) { - throw new Error(`Could not find test case with id ${filterTestCaseName}`); + throw new Error(`Could not find test case with id ${filterTestCasePath}`); } const testCasesById = Object.fromEntries(testCases.map((tc) => [tc.id, tc])); @@ -307,7 +307,7 @@ export const runDebugtopus = async (options: DebugtopusOptions) => { }; export const executeLocalTestCases = async ( - options: Omit & { testCaseName?: string }, + options: Omit & { testCasePath?: string }, ): Promise => { const octomindRoot = await findOctomindFolder(); if (!octomindRoot) { @@ -320,10 +320,10 @@ export const executeLocalTestCases = async ( let testCases = readTestCasesFromDir(octomindRoot); let filterTestCaseId: string | undefined = undefined; - if (options.testCaseName) { + if (options.testCasePath) { const filtered = await getFilteredTestCaseWithDependencies({ testCases, - filterTestCaseName: options.testCaseName, + filterTestCasePath: options.testCasePath, octomindRoot, }); testCases = filtered.relevantTestCases; diff --git a/tests/debugtopus/index.spec.ts b/tests/debugtopus/index.spec.ts index 6b97216..17cc1fd 100644 --- a/tests/debugtopus/index.spec.ts +++ b/tests/debugtopus/index.spec.ts @@ -311,7 +311,7 @@ describe("debugtopus", () => { await executeLocalTestCases({ testTargetId: "test-target-id", url: "https://example.com", - testCaseName: "target-test.yaml", + testCasePath: "target-test.yaml", }); expect(mockedGetAbsoluteFilePathInOctomindRoot).toHaveBeenCalledWith({ @@ -343,7 +343,7 @@ describe("debugtopus", () => { executeLocalTestCases({ testTargetId: "test-target-id", url: "https://example.com", - testCaseName: "non-existent.yaml", + testCasePath: "non-existent.yaml", }), ).rejects.toThrow( `Could not find non-existent.yaml in folder ${OCTOMIND_ROOT}`,