Skip to content
This repository was archived by the owner on Jul 14, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,26 @@ Execute test cases to create a test report
| `-b, --browser [type]` | Browser type [CHROMIUM, FIREFOX, SAFARI] | No | CHROMIUM |
| `-r, --breakpoint [name]` | Breakpoint [DESKTOP, MOBILE, TABLET] | No | DESKTOP |

## execute-local

Execute local YAML test cases

**Usage:** `execute-local [options]`

### Options

| Option | Description | Required | Default |
|:-------|:----------|:---------|:--------|
| `-j, --json` | Output raw JSON response | No | |
| `-u, --url <url>` | url the tests should run against | Yes | |
| `-e, --environment-id [uuid]` | id of the environment you want to run against, if not provided will run all test cases against the default environment | No | |
| `-t, --test-target-id [uuid]` | id of the test target of the test case, if not provided will use the test target id from the config | No | |
| `--headless` | if we should run headless without the UI of playwright and the browser | No | |
| `--bypass-proxy` | bypass proxy when accessing the test target | No | |
| `--browser [CHROMIUM, FIREFOX, SAFARI]` | Browser type | No | CHROMIUM |
| `--breakpoint [DESKTOP, MOBILE, TABLET]` | Breakpoint | No | DESKTOP |
| `-s, --source <path>` | Source directory (defaults to current directory) | Yes | ./.octomind |

## create-discovery

Create a new test case discovery
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@octomind/octomind",
"version": "3.5.1",
"version": "3.6.0",
"description": "a command line client for octomind apis",
"main": "./dist/index.js",
"packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402",
Expand Down
31 changes: 30 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
testTargetIdCompleter,
uninstallCompletion,
} from "./completion";
import { runDebugtopus } from "./debugtopus";
import { executeLocalTestCases, runDebugtopus } from "./debugtopus";
import { resolveTestTargetId } from "./helpers";
import { startPrivateLocationWorker, stopPLW } from "./plw";
import {
Expand Down Expand Up @@ -170,6 +170,35 @@ export const buildCmd = (): CompletableCommand => {
)
.action(addTestTargetWrapper(executeTests));

createCommandWithCommonOptions(program, "execute-local")
.completer(environmentIdCompleter)
.completer(testTargetIdCompleter)
.completer(optionsCompleter)
.description("Execute local YAML test cases")
.helpGroup("execute")
.requiredOption("-u, --url <url>", "url the tests should run against")
.option(
"-e, --environment-id [uuid]",
"id of the environment you want to run against, if not provided will run all test cases against the default environment",
)
.option(
"-t, --test-target-id [uuid]",
"id of the test target of the test case, if not provided will use the test target id from the config",
)
.option(
"--headless",
"if we should run headless without the UI of playwright and the browser",
)
.option("--bypass-proxy", "bypass proxy when accessing the test target")
.option("--browser [CHROMIUM, FIREFOX, SAFARI]", "Browser type", "CHROMIUM")
.option("--breakpoint [DESKTOP, MOBILE, TABLET]", "Breakpoint", "DESKTOP")
.option(
"-s, --source <path>",
"Source directory (defaults to current directory)",
"./.octomind",
Comment thread
RunOrVeith marked this conversation as resolved.
)
.action(addTestTargetWrapper(executeLocalTestCases));

createCommandWithCommonOptions(program, "test-report")
.completer(testReportIdCompleter)
.completer(testTargetIdCompleter)
Expand Down
73 changes: 73 additions & 0 deletions src/debugtopus/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,76 @@ export const runDebugtopus = async (options: DebugtopusOptions) => {
});
await runTests({ ...dirs, runMode: options.headless ? "headless" : "ui" });
};

export const executeLocalTestCases = async (
options: DebugtopusOptions & { source: string },
): Promise<void> => {
const testCases = readTestCasesFromDir(options.source);
const body = {
testCases,
testTargetId: options.testTargetId,
executionUrl: options.url,
environmentId: options.environmentId,
};
const { error, response } = await client.POST(
"/apiKey/beta/test-targets/{testTargetId}/code",
{
params: {
path: {
testTargetId: options.testTargetId,
},
},
body,
parseAs: "stream",
},
);

if (error || !response?.body) {
handleError(error);
}

const dirs = await prepareDirectories();
Comment thread
RunOrVeith marked this conversation as resolved.

await readZipFromResponseBody(dirs, response);

const config = await getPlaywrightConfig({
testTargetId: options.testTargetId,
url: options.url,
outputDir: dirs.outputDir,
environmentId: options.environmentId,
headless: options.headless,
bypassProxy: options.bypassProxy,
browser: options.browser,
breakpoint: options.breakpoint,
});

if (!config) {
handleError("no config found");
}

writeFileSync(dirs.configFilePath, config);

await runTests({ ...dirs, runMode: options.headless ? "headless" : "ui" });
};

export const readZipFromResponseBody = async (
dirs: TestDirectories,
response: Response,
): Promise<void> => {
// Persist the ZIP to disk first to avoid streaming issues with unzipper
const zipPath = path.join(dirs.testDirectory, "bundle.zip");
const zipWriteStream = createWriteStream(zipPath);
await pipeline(
Readable.fromWeb(response.body as ReadableStream),
zipWriteStream,
);

// Extract using unzipper's higher-level API
try {
const zipBuffer = await fs.readFile(zipPath);
const directory = await Open.buffer(zipBuffer);
await directory.extract({ path: dirs.testDirectory });
} catch {
throw new Error(`Failed to extract ZIP at ${zipPath}`);
}
};
1 change: 1 addition & 0 deletions src/tools/sync/yml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const collectYamlFiles = (startDir: string): string[] => {
try {
entries = fs.readdirSync(current, { withFileTypes: true });
} catch {
current = stack.pop();
Comment thread
RunOrVeith marked this conversation as resolved.
continue;
}
for (const entry of entries) {
Expand Down
Loading