Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ octomind-cli-debug/
vhs/
.octomind
src/schemas
**/.DS_Store
1 change: 1 addition & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ export const buildCmd = (): CompletableCommand => {
.description("Push local YAML test cases to the test target")
.helpGroup("test-cases")
.addOption(testTargetIdOption)
.option("-y, --yes", "Skip confirmation prompt")
.action(addTestTargetWrapper(pushTestTarget));

// noinspection RequiredAttributes
Expand Down
5 changes: 5 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export function promptUser(question: string): Promise<string> {
});
}

export const confirmAction = async (message: string): Promise<boolean> => {
const answer = await promptUser(`${message} (y/N): `);
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
};

export const resolveTestTargetId = async (
providedTestTargetId?: string,
): Promise<string> => {
Expand Down
46 changes: 41 additions & 5 deletions src/tools/test-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import path from "path";
import ora from "ora";

import { OCTOMIND_FOLDER_NAME } from "../constants";
import { findOctomindFolder } from "../helpers";
import { confirmAction, findOctomindFolder } from "../helpers";
import { getUrl } from "../url";
import { client, handleError, ListOptions, logJson } from "./client";
import { push } from "./sync/push";
import { readTestCasesFromDir, writeYaml } from "./sync/yaml";
import { writeYaml } from "./sync/yaml";

export const getTestTargets = async () => {
const { data, error } = await client.GET("/apiKey/v3/test-targets");
Expand All @@ -21,6 +21,27 @@ export const getTestTargets = async () => {
return data;
};

export const getTestTarget = async (id: string) => {
const { data, error } = await client.GET(
"/apiKey/v3/test-targets/{testTargetId}",
{
params: {
path: {
testTargetId: id,
},
},
},
);

handleError(error);

if (!data) {
throw Error(`No test target with id ${id} found`);
}

return data;
};

export const listTestTargets = async (options: ListOptions): Promise<void> => {
const testTargets = await getTestTargets();
if (options.json) {
Expand Down Expand Up @@ -81,16 +102,31 @@ export const pullTestTarget = async (
};

export const pushTestTarget = async (
options: { testTargetId: string } & ListOptions,
options: { testTargetId: string; yes?: boolean } & ListOptions,
): Promise<void> => {
const localThrobber = ora("Reading local test cases").start();
const sourceDir = await findOctomindFolder();
if (!sourceDir) {
throw new Error(
`No ${OCTOMIND_FOLDER_NAME} folder found, please pull first.`,
);
}
const throbber = ora("Pushing test cases").start();

const testTarget = await getTestTarget(options.testTargetId);

localThrobber.succeed("Local test cases read successfully");

if (!options.yes) {
const confirmed = await confirmAction(
`Push local changes to test target "${testTarget.app}" with id "${testTarget.id}"?`,
);
if (!confirmed) {
console.log("Push cancelled.");
return;
}
}

const pushThrobber = ora("Pushing test cases").start();
const data = await push({
...options,
sourceDir,
Expand All @@ -103,5 +139,5 @@ export const pushTestTarget = async (
logJson(data);
}

throbber.succeed("Test cases pushed successfully");
pushThrobber.succeed("Test cases pushed successfully");
};
2 changes: 1 addition & 1 deletion tests/helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fsPromises from "fs/promises";
import os from "os";
import path from "path";

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it } from "vitest";

import { OCTOMIND_FOLDER_NAME } from "../src/constants";
import {
Expand Down
50 changes: 49 additions & 1 deletion tests/tools/test-targets.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { mock } from "vitest-mock-extended";

import { findOctomindFolder } from "../../src/helpers";
import { confirmAction, findOctomindFolder } from "../../src/helpers";
import { pushTestTarget } from "../../src/tools";
import { client } from "../../src/tools/client";
import { getGitContext } from "../../src/tools/sync/git";
Expand All @@ -15,6 +15,7 @@ vi.mock("../../src/tools/client");
describe("push", () => {
beforeEach(() => {
vi.mocked(findOctomindFolder).mockResolvedValue("/project/.octomind");
vi.mocked(confirmAction).mockResolvedValue(true);
vi.mocked(getGitContext).mockResolvedValue({
defaultBranch: "refs/heads/main",
ref: "refs/heads/main",
Expand All @@ -24,6 +25,11 @@ describe("push", () => {
});

vi.mocked(readTestCasesFromDir).mockReturnValue([]);
vi.mocked(client).GET.mockResolvedValue({
data: { id: "someId", app: "My Test App" },
error: undefined,
response: mock(),
});
vi.mocked(client).POST.mockResolvedValue({
data: undefined,
error: undefined,
Expand All @@ -43,6 +49,7 @@ describe("push", () => {

await pushTestTarget({
testTargetId: "someId",
yes: true,
});

expect(client.POST).toHaveBeenCalledWith(
Expand All @@ -62,11 +69,52 @@ describe("push", () => {

await pushTestTarget({
testTargetId: "someId",
yes: true,
});

expect(client.POST).toHaveBeenCalledWith(
"/apiKey/beta/test-targets/{testTargetId}/draft/push",
expect.anything(),
);
});

describe("confirmation", () => {
it("prompts for confirmation with test target name", async () => {
const id = "someId";
const name = "My Test App";
vi.mocked(client).GET.mockResolvedValue({
data: { id, app: name },
error: undefined,
response: mock(),
});
await pushTestTarget({
testTargetId: "someId",
});

expect(confirmAction).toHaveBeenCalledWith(
`Push local changes to test target "${name}" with id "${id}"?`,
);
});

it("skips confirmation when --yes flag is provided", async () => {
await pushTestTarget({
testTargetId: "someId",
yes: true,
});

expect(confirmAction).not.toHaveBeenCalled();
expect(client.POST).toHaveBeenCalled();
});

it("does not push when user declines confirmation", async () => {
vi.mocked(confirmAction).mockResolvedValue(false);

await pushTestTarget({
testTargetId: "someId",
});

expect(client.POST).not.toHaveBeenCalled();
expect(console.log).toHaveBeenCalledWith("Push cancelled.");
});
});
});