@digitalpresence/cliclaw 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/calendar.integration.test.d.ts +2 -0
- package/dist/__tests__/calendar.integration.test.d.ts.map +1 -0
- package/dist/__tests__/calendar.integration.test.js +98 -0
- package/dist/__tests__/calendar.integration.test.js.map +1 -0
- package/dist/__tests__/forms.integration.test.d.ts +2 -0
- package/dist/__tests__/forms.integration.test.d.ts.map +1 -0
- package/dist/__tests__/forms.integration.test.js +243 -0
- package/dist/__tests__/forms.integration.test.js.map +1 -0
- package/dist/__tests__/gdrive.integration.test.d.ts +2 -0
- package/dist/__tests__/gdrive.integration.test.d.ts.map +1 -0
- package/dist/__tests__/gdrive.integration.test.js +186 -0
- package/dist/__tests__/gdrive.integration.test.js.map +1 -0
- package/dist/__tests__/gmail.integration.test.d.ts +2 -0
- package/dist/__tests__/gmail.integration.test.d.ts.map +1 -0
- package/dist/__tests__/gmail.integration.test.js +197 -0
- package/dist/__tests__/gmail.integration.test.js.map +1 -0
- package/dist/__tests__/gslides.integration.test.d.ts +2 -0
- package/dist/__tests__/gslides.integration.test.d.ts.map +1 -0
- package/dist/__tests__/gslides.integration.test.js +124 -0
- package/dist/__tests__/gslides.integration.test.js.map +1 -0
- package/dist/__tests__/sheets.integration.test.d.ts +2 -0
- package/dist/__tests__/sheets.integration.test.d.ts.map +1 -0
- package/dist/__tests__/sheets.integration.test.js +150 -0
- package/dist/__tests__/sheets.integration.test.js.map +1 -0
- package/dist/agent/crud.d.ts +6 -0
- package/dist/agent/crud.d.ts.map +1 -0
- package/dist/agent/crud.js +41 -0
- package/dist/agent/crud.js.map +1 -0
- package/dist/agent/memory.d.ts +14 -0
- package/dist/agent/memory.d.ts.map +1 -0
- package/dist/agent/memory.js +66 -0
- package/dist/agent/memory.js.map +1 -0
- package/dist/agent/permissions.d.ts +4 -0
- package/dist/agent/permissions.d.ts.map +1 -0
- package/dist/agent/permissions.js +32 -0
- package/dist/agent/permissions.js.map +1 -0
- package/dist/calendar/accounts.d.ts +3 -0
- package/dist/calendar/accounts.d.ts.map +1 -0
- package/dist/calendar/accounts.js +21 -0
- package/dist/calendar/accounts.js.map +1 -0
- package/dist/calendar/auth.d.ts +3 -0
- package/dist/calendar/auth.d.ts.map +1 -0
- package/dist/calendar/auth.js +41 -0
- package/dist/calendar/auth.js.map +1 -0
- package/dist/calendar/calendars.d.ts +3 -0
- package/dist/calendar/calendars.d.ts.map +1 -0
- package/dist/calendar/calendars.js +28 -0
- package/dist/calendar/calendars.js.map +1 -0
- package/dist/calendar/create.d.ts +3 -0
- package/dist/calendar/create.d.ts.map +1 -0
- package/dist/calendar/create.js +31 -0
- package/dist/calendar/create.js.map +1 -0
- package/dist/calendar/delete.d.ts +3 -0
- package/dist/calendar/delete.d.ts.map +1 -0
- package/dist/calendar/delete.js +21 -0
- package/dist/calendar/delete.js.map +1 -0
- package/dist/calendar/events.d.ts +3 -0
- package/dist/calendar/events.d.ts.map +1 -0
- package/dist/calendar/events.js +37 -0
- package/dist/calendar/events.js.map +1 -0
- package/dist/calendar/get.d.ts +3 -0
- package/dist/calendar/get.d.ts.map +1 -0
- package/dist/calendar/get.js +21 -0
- package/dist/calendar/get.js.map +1 -0
- package/dist/calendar/update.d.ts +3 -0
- package/dist/calendar/update.d.ts.map +1 -0
- package/dist/calendar/update.js +31 -0
- package/dist/calendar/update.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +37 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/agent.d.ts +6 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +107 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/calendar.d.ts +9 -0
- package/dist/commands/calendar.d.ts.map +1 -0
- package/dist/commands/calendar.js +104 -0
- package/dist/commands/calendar.js.map +1 -0
- package/dist/commands/cron.d.ts +6 -0
- package/dist/commands/cron.d.ts.map +1 -0
- package/dist/commands/cron.js +103 -0
- package/dist/commands/cron.js.map +1 -0
- package/dist/commands/forms.d.ts +9 -0
- package/dist/commands/forms.d.ts.map +1 -0
- package/dist/commands/forms.js +139 -0
- package/dist/commands/forms.js.map +1 -0
- package/dist/commands/gdrive.d.ts +9 -0
- package/dist/commands/gdrive.d.ts.map +1 -0
- package/dist/commands/gdrive.js +198 -0
- package/dist/commands/gdrive.js.map +1 -0
- package/dist/commands/gmail.d.ts +9 -0
- package/dist/commands/gmail.d.ts.map +1 -0
- package/dist/commands/gmail.js +231 -0
- package/dist/commands/gmail.js.map +1 -0
- package/dist/commands/gslides.d.ts +9 -0
- package/dist/commands/gslides.d.ts.map +1 -0
- package/dist/commands/gslides.js +167 -0
- package/dist/commands/gslides.js.map +1 -0
- package/dist/commands/sheets.d.ts +9 -0
- package/dist/commands/sheets.d.ts.map +1 -0
- package/dist/commands/sheets.js +174 -0
- package/dist/commands/sheets.js.map +1 -0
- package/dist/cron/daemon.d.ts +3 -0
- package/dist/cron/daemon.d.ts.map +1 -0
- package/dist/cron/daemon.js +127 -0
- package/dist/cron/daemon.js.map +1 -0
- package/dist/cron/handlers.d.ts +6 -0
- package/dist/cron/handlers.d.ts.map +1 -0
- package/dist/cron/handlers.js +58 -0
- package/dist/cron/handlers.js.map +1 -0
- package/dist/cron/logger.d.ts +4 -0
- package/dist/cron/logger.d.ts.map +1 -0
- package/dist/cron/logger.js +11 -0
- package/dist/cron/logger.js.map +1 -0
- package/dist/cron/progress.d.ts +25 -0
- package/dist/cron/progress.d.ts.map +1 -0
- package/dist/cron/progress.js +71 -0
- package/dist/cron/progress.js.map +1 -0
- package/dist/cron/ralph-wiggum.d.ts +18 -0
- package/dist/cron/ralph-wiggum.d.ts.map +1 -0
- package/dist/cron/ralph-wiggum.js +143 -0
- package/dist/cron/ralph-wiggum.js.map +1 -0
- package/dist/forms/accounts.d.ts +3 -0
- package/dist/forms/accounts.d.ts.map +1 -0
- package/dist/forms/accounts.js +20 -0
- package/dist/forms/accounts.js.map +1 -0
- package/dist/forms/auth.d.ts +3 -0
- package/dist/forms/auth.d.ts.map +1 -0
- package/dist/forms/auth.js +41 -0
- package/dist/forms/auth.js.map +1 -0
- package/dist/forms/create.d.ts +3 -0
- package/dist/forms/create.d.ts.map +1 -0
- package/dist/forms/create.js +28 -0
- package/dist/forms/create.js.map +1 -0
- package/dist/forms/get.d.ts +3 -0
- package/dist/forms/get.d.ts.map +1 -0
- package/dist/forms/get.js +21 -0
- package/dist/forms/get.js.map +1 -0
- package/dist/forms/list.d.ts +3 -0
- package/dist/forms/list.d.ts.map +1 -0
- package/dist/forms/list.js +33 -0
- package/dist/forms/list.js.map +1 -0
- package/dist/forms/questions.d.ts +6 -0
- package/dist/forms/questions.d.ts.map +1 -0
- package/dist/forms/questions.js +179 -0
- package/dist/forms/questions.js.map +1 -0
- package/dist/forms/responses.d.ts +4 -0
- package/dist/forms/responses.d.ts.map +1 -0
- package/dist/forms/responses.js +40 -0
- package/dist/forms/responses.js.map +1 -0
- package/dist/forms/update.d.ts +3 -0
- package/dist/forms/update.d.ts.map +1 -0
- package/dist/forms/update.js +44 -0
- package/dist/forms/update.js.map +1 -0
- package/dist/gdrive/about.d.ts +3 -0
- package/dist/gdrive/about.d.ts.map +1 -0
- package/dist/gdrive/about.js +31 -0
- package/dist/gdrive/about.js.map +1 -0
- package/dist/gdrive/accounts.d.ts +3 -0
- package/dist/gdrive/accounts.d.ts.map +1 -0
- package/dist/gdrive/accounts.js +20 -0
- package/dist/gdrive/accounts.js.map +1 -0
- package/dist/gdrive/auth.d.ts +3 -0
- package/dist/gdrive/auth.d.ts.map +1 -0
- package/dist/gdrive/auth.js +51 -0
- package/dist/gdrive/auth.js.map +1 -0
- package/dist/gdrive/files.d.ts +12 -0
- package/dist/gdrive/files.d.ts.map +1 -0
- package/dist/gdrive/files.js +174 -0
- package/dist/gdrive/files.js.map +1 -0
- package/dist/gdrive/folders.d.ts +4 -0
- package/dist/gdrive/folders.d.ts.map +1 -0
- package/dist/gdrive/folders.js +46 -0
- package/dist/gdrive/folders.js.map +1 -0
- package/dist/gdrive/search.d.ts +3 -0
- package/dist/gdrive/search.d.ts.map +1 -0
- package/dist/gdrive/search.js +23 -0
- package/dist/gdrive/search.js.map +1 -0
- package/dist/gdrive/sharing.d.ts +5 -0
- package/dist/gdrive/sharing.d.ts.map +1 -0
- package/dist/gdrive/sharing.js +54 -0
- package/dist/gdrive/sharing.js.map +1 -0
- package/dist/gmail/accounts.d.ts +3 -0
- package/dist/gmail/accounts.d.ts.map +1 -0
- package/dist/gmail/accounts.js +18 -0
- package/dist/gmail/accounts.js.map +1 -0
- package/dist/gmail/auth.d.ts +3 -0
- package/dist/gmail/auth.d.ts.map +1 -0
- package/dist/gmail/auth.js +50 -0
- package/dist/gmail/auth.js.map +1 -0
- package/dist/gmail/drafts.d.ts +7 -0
- package/dist/gmail/drafts.d.ts.map +1 -0
- package/dist/gmail/drafts.js +103 -0
- package/dist/gmail/drafts.js.map +1 -0
- package/dist/gmail/get.d.ts +4 -0
- package/dist/gmail/get.d.ts.map +1 -0
- package/dist/gmail/get.js +140 -0
- package/dist/gmail/get.js.map +1 -0
- package/dist/gmail/inbox.d.ts +4 -0
- package/dist/gmail/inbox.d.ts.map +1 -0
- package/dist/gmail/inbox.js +45 -0
- package/dist/gmail/inbox.js.map +1 -0
- package/dist/gmail/labels.d.ts +5 -0
- package/dist/gmail/labels.d.ts.map +1 -0
- package/dist/gmail/labels.js +45 -0
- package/dist/gmail/labels.js.map +1 -0
- package/dist/gmail/modify.d.ts +5 -0
- package/dist/gmail/modify.d.ts.map +1 -0
- package/dist/gmail/modify.js +68 -0
- package/dist/gmail/modify.js.map +1 -0
- package/dist/gmail/send.d.ts +5 -0
- package/dist/gmail/send.d.ts.map +1 -0
- package/dist/gmail/send.js +310 -0
- package/dist/gmail/send.js.map +1 -0
- package/dist/gmail/threads.d.ts +4 -0
- package/dist/gmail/threads.d.ts.map +1 -0
- package/dist/gmail/threads.js +47 -0
- package/dist/gmail/threads.js.map +1 -0
- package/dist/gslides/accounts.d.ts +3 -0
- package/dist/gslides/accounts.d.ts.map +1 -0
- package/dist/gslides/accounts.js +20 -0
- package/dist/gslides/accounts.js.map +1 -0
- package/dist/gslides/auth.d.ts +3 -0
- package/dist/gslides/auth.d.ts.map +1 -0
- package/dist/gslides/auth.js +50 -0
- package/dist/gslides/auth.js.map +1 -0
- package/dist/gslides/presentations.d.ts +29 -0
- package/dist/gslides/presentations.d.ts.map +1 -0
- package/dist/gslides/presentations.js +320 -0
- package/dist/gslides/presentations.js.map +1 -0
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +12 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/media-utils.d.ts +5 -0
- package/dist/lib/media-utils.d.ts.map +1 -0
- package/dist/lib/media-utils.js +79 -0
- package/dist/lib/media-utils.js.map +1 -0
- package/dist/lib/output.d.ts +4 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +12 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/sheets/accounts.d.ts +3 -0
- package/dist/sheets/accounts.d.ts.map +1 -0
- package/dist/sheets/accounts.js +20 -0
- package/dist/sheets/accounts.js.map +1 -0
- package/dist/sheets/auth.d.ts +3 -0
- package/dist/sheets/auth.d.ts.map +1 -0
- package/dist/sheets/auth.js +56 -0
- package/dist/sheets/auth.js.map +1 -0
- package/dist/sheets/cells.d.ts +6 -0
- package/dist/sheets/cells.d.ts.map +1 -0
- package/dist/sheets/cells.js +89 -0
- package/dist/sheets/cells.js.map +1 -0
- package/dist/sheets/format.d.ts +8 -0
- package/dist/sheets/format.d.ts.map +1 -0
- package/dist/sheets/format.js +97 -0
- package/dist/sheets/format.js.map +1 -0
- package/dist/sheets/sheets-tab.d.ts +6 -0
- package/dist/sheets/sheets-tab.d.ts.map +1 -0
- package/dist/sheets/sheets-tab.js +88 -0
- package/dist/sheets/sheets-tab.js.map +1 -0
- package/dist/sheets/spreadsheets.d.ts +6 -0
- package/dist/sheets/spreadsheets.d.ts.map +1 -0
- package/dist/sheets/spreadsheets.js +88 -0
- package/dist/sheets/spreadsheets.js.map +1 -0
- package/package.json +33 -0
- package/src/__tests__/calendar.integration.test.ts +152 -0
- package/src/__tests__/forms.integration.test.ts +403 -0
- package/src/__tests__/gdrive.integration.test.ts +253 -0
- package/src/__tests__/gmail.integration.test.ts +294 -0
- package/src/__tests__/gslides.integration.test.ts +195 -0
- package/src/__tests__/sheets.integration.test.ts +234 -0
- package/src/agent/crud.ts +54 -0
- package/src/agent/memory.ts +95 -0
- package/src/agent/permissions.ts +54 -0
- package/src/calendar/accounts.ts +25 -0
- package/src/calendar/auth.ts +45 -0
- package/src/calendar/calendars.ts +32 -0
- package/src/calendar/create.ts +44 -0
- package/src/calendar/delete.ts +27 -0
- package/src/calendar/events.ts +45 -0
- package/src/calendar/get.ts +27 -0
- package/src/calendar/update.ts +44 -0
- package/src/cli.ts +41 -0
- package/src/commands/agent.ts +128 -0
- package/src/commands/calendar.ts +127 -0
- package/src/commands/cron.ts +126 -0
- package/src/commands/forms.ts +158 -0
- package/src/commands/gdrive.ts +225 -0
- package/src/commands/gmail.ts +290 -0
- package/src/commands/gslides.ts +188 -0
- package/src/commands/sheets.ts +193 -0
- package/src/cron/daemon.ts +143 -0
- package/src/cron/handlers.ts +78 -0
- package/src/cron/logger.ts +20 -0
- package/src/cron/progress.ts +90 -0
- package/src/cron/ralph-wiggum.ts +172 -0
- package/src/forms/accounts.ts +24 -0
- package/src/forms/auth.ts +45 -0
- package/src/forms/create.ts +34 -0
- package/src/forms/get.ts +26 -0
- package/src/forms/list.ts +38 -0
- package/src/forms/questions.ts +209 -0
- package/src/forms/responses.ts +50 -0
- package/src/forms/update.ts +57 -0
- package/src/gdrive/about.ts +36 -0
- package/src/gdrive/accounts.ts +24 -0
- package/src/gdrive/auth.ts +55 -0
- package/src/gdrive/files.ts +237 -0
- package/src/gdrive/folders.ts +58 -0
- package/src/gdrive/search.ts +30 -0
- package/src/gdrive/sharing.ts +72 -0
- package/src/gmail/accounts.ts +22 -0
- package/src/gmail/auth.ts +53 -0
- package/src/gmail/drafts.ts +166 -0
- package/src/gmail/get.ts +195 -0
- package/src/gmail/inbox.ts +78 -0
- package/src/gmail/labels.ts +69 -0
- package/src/gmail/modify.ts +89 -0
- package/src/gmail/send.ts +424 -0
- package/src/gmail/threads.ts +68 -0
- package/src/gslides/accounts.ts +24 -0
- package/src/gslides/auth.ts +54 -0
- package/src/gslides/presentations.ts +384 -0
- package/src/lib/config.ts +14 -0
- package/src/lib/media-utils.ts +82 -0
- package/src/lib/output.ts +13 -0
- package/src/sheets/accounts.ts +24 -0
- package/src/sheets/auth.ts +60 -0
- package/src/sheets/cells.ts +112 -0
- package/src/sheets/format.ts +114 -0
- package/src/sheets/sheets-tab.ts +109 -0
- package/src/sheets/spreadsheets.ts +106 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { describe, it, expect } from "vitest";
|
|
5
|
+
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const CLI = join(import.meta.dirname, "../../dist/cli.js");
|
|
8
|
+
|
|
9
|
+
interface CliResult {
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
exitCode: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function run(...args: string[]): Promise<CliResult> {
|
|
16
|
+
try {
|
|
17
|
+
const { stdout, stderr } = await execFileAsync("node", [CLI, ...args], {
|
|
18
|
+
timeout: 30000,
|
|
19
|
+
});
|
|
20
|
+
return { stdout, stderr, exitCode: 0 };
|
|
21
|
+
} catch (err: any) {
|
|
22
|
+
return {
|
|
23
|
+
stdout: err.stdout ?? "",
|
|
24
|
+
stderr: err.stderr ?? "",
|
|
25
|
+
exitCode: err.code ?? 1,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseJson(result: CliResult): any {
|
|
31
|
+
return JSON.parse(result.stdout);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe.sequential("Google Calendar CLI integration", () => {
|
|
35
|
+
const timestamp = Date.now();
|
|
36
|
+
|
|
37
|
+
let eventId: string;
|
|
38
|
+
|
|
39
|
+
// Create event times: 1 hour from now
|
|
40
|
+
const startTime = new Date(Date.now() + 3600000).toISOString();
|
|
41
|
+
const endTime = new Date(Date.now() + 7200000).toISOString();
|
|
42
|
+
|
|
43
|
+
it("accounts — returns a list of calendar accounts", async () => {
|
|
44
|
+
const result = await run("calendar", "accounts");
|
|
45
|
+
expect(result.exitCode).toBe(0);
|
|
46
|
+
|
|
47
|
+
const data = parseJson(result);
|
|
48
|
+
expect(data).toHaveProperty("accounts");
|
|
49
|
+
expect(Array.isArray(data.accounts)).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("calendars — lists available calendars", async () => {
|
|
53
|
+
const result = await run("calendar", "calendars");
|
|
54
|
+
expect(result.exitCode).toBe(0);
|
|
55
|
+
|
|
56
|
+
const data = parseJson(result);
|
|
57
|
+
expect(Array.isArray(data)).toBe(true);
|
|
58
|
+
expect(data.length).toBeGreaterThan(0);
|
|
59
|
+
|
|
60
|
+
const cal = data[0];
|
|
61
|
+
expect(cal).toHaveProperty("id");
|
|
62
|
+
expect(cal).toHaveProperty("summary");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("create — creates a test event", async () => {
|
|
66
|
+
const summary = `cliclaw-test-${timestamp}`;
|
|
67
|
+
const result = await run(
|
|
68
|
+
"calendar", "create",
|
|
69
|
+
"--summary", summary,
|
|
70
|
+
"--start", startTime,
|
|
71
|
+
"--end", endTime,
|
|
72
|
+
"--description", "Integration test event",
|
|
73
|
+
"--location", "Test Location",
|
|
74
|
+
);
|
|
75
|
+
expect(result.exitCode).toBe(0);
|
|
76
|
+
|
|
77
|
+
const data = parseJson(result);
|
|
78
|
+
expect(data.success).toBe(true);
|
|
79
|
+
expect(data.id).toBeTruthy();
|
|
80
|
+
expect(data.summary).toBe(summary);
|
|
81
|
+
|
|
82
|
+
eventId = data.id;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("events — lists events including the created one", async () => {
|
|
86
|
+
const result = await run(
|
|
87
|
+
"calendar", "events",
|
|
88
|
+
"--max-results", "10",
|
|
89
|
+
"--time-min", new Date().toISOString(),
|
|
90
|
+
);
|
|
91
|
+
expect(result.exitCode).toBe(0);
|
|
92
|
+
|
|
93
|
+
const events = parseJson(result);
|
|
94
|
+
expect(Array.isArray(events)).toBe(true);
|
|
95
|
+
|
|
96
|
+
const ids = events.map((e: any) => e.id);
|
|
97
|
+
expect(ids).toContain(eventId);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("get — returns the created event details", async () => {
|
|
101
|
+
const result = await run(
|
|
102
|
+
"calendar", "get",
|
|
103
|
+
"--event-id", eventId,
|
|
104
|
+
);
|
|
105
|
+
expect(result.exitCode).toBe(0);
|
|
106
|
+
|
|
107
|
+
const data = parseJson(result);
|
|
108
|
+
expect(data.id).toBe(eventId);
|
|
109
|
+
expect(data.summary).toBe(`cliclaw-test-${timestamp}`);
|
|
110
|
+
expect(data.description).toBe("Integration test event");
|
|
111
|
+
expect(data.location).toBe("Test Location");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("update — updates the event summary and description", async () => {
|
|
115
|
+
const newSummary = `cliclaw-updated-${timestamp}`;
|
|
116
|
+
const result = await run(
|
|
117
|
+
"calendar", "update",
|
|
118
|
+
"--event-id", eventId,
|
|
119
|
+
"--summary", newSummary,
|
|
120
|
+
"--description", "Updated description",
|
|
121
|
+
);
|
|
122
|
+
expect(result.exitCode).toBe(0);
|
|
123
|
+
|
|
124
|
+
const data = parseJson(result);
|
|
125
|
+
expect(data.success).toBe(true);
|
|
126
|
+
expect(data.summary).toBe(newSummary);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("get — confirms the update was applied", async () => {
|
|
130
|
+
const result = await run(
|
|
131
|
+
"calendar", "get",
|
|
132
|
+
"--event-id", eventId,
|
|
133
|
+
);
|
|
134
|
+
expect(result.exitCode).toBe(0);
|
|
135
|
+
|
|
136
|
+
const data = parseJson(result);
|
|
137
|
+
expect(data.summary).toBe(`cliclaw-updated-${timestamp}`);
|
|
138
|
+
expect(data.description).toBe("Updated description");
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("delete — deletes the test event", async () => {
|
|
142
|
+
const result = await run(
|
|
143
|
+
"calendar", "delete",
|
|
144
|
+
"--event-id", eventId,
|
|
145
|
+
);
|
|
146
|
+
expect(result.exitCode).toBe(0);
|
|
147
|
+
|
|
148
|
+
const data = parseJson(result);
|
|
149
|
+
expect(data.success).toBe(true);
|
|
150
|
+
expect(data.eventId).toBe(eventId);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { describe, it, expect } from "vitest";
|
|
5
|
+
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const CLI = join(import.meta.dirname, "../../dist/cli.js");
|
|
8
|
+
|
|
9
|
+
interface CliResult {
|
|
10
|
+
stdout: string;
|
|
11
|
+
stderr: string;
|
|
12
|
+
exitCode: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function run(...args: string[]): Promise<CliResult> {
|
|
16
|
+
try {
|
|
17
|
+
const { stdout, stderr } = await execFileAsync("node", [CLI, ...args], {
|
|
18
|
+
timeout: 30000,
|
|
19
|
+
});
|
|
20
|
+
return { stdout, stderr, exitCode: 0 };
|
|
21
|
+
} catch (err: any) {
|
|
22
|
+
return {
|
|
23
|
+
stdout: err.stdout ?? "",
|
|
24
|
+
stderr: err.stderr ?? "",
|
|
25
|
+
exitCode: err.code ?? 1,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseJson(result: CliResult): any {
|
|
31
|
+
return JSON.parse(result.stdout);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe.sequential("Google Forms CLI integration", () => {
|
|
35
|
+
const timestamp = Date.now();
|
|
36
|
+
|
|
37
|
+
let formId: string;
|
|
38
|
+
let formTitle: string;
|
|
39
|
+
|
|
40
|
+
// ── Accounts ──────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
it("accounts — returns a list with at least the default account", async () => {
|
|
43
|
+
const result = await run("forms", "accounts");
|
|
44
|
+
expect(result.exitCode).toBe(0);
|
|
45
|
+
|
|
46
|
+
const data = parseJson(result);
|
|
47
|
+
expect(data).toHaveProperty("accounts");
|
|
48
|
+
expect(Array.isArray(data.accounts)).toBe(true);
|
|
49
|
+
|
|
50
|
+
const names = data.accounts.map((a: any) => a.account);
|
|
51
|
+
expect(names).toContain("default");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ── Create ────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
it("create — creates a new form and returns form data", async () => {
|
|
57
|
+
formTitle = `cliclaw-test-${timestamp}`;
|
|
58
|
+
const result = await run(
|
|
59
|
+
"forms", "create",
|
|
60
|
+
"--title", formTitle,
|
|
61
|
+
);
|
|
62
|
+
expect(result.exitCode).toBe(0);
|
|
63
|
+
|
|
64
|
+
const data = parseJson(result);
|
|
65
|
+
expect(data.success).toBe(true);
|
|
66
|
+
expect(data.formId).toBeTruthy();
|
|
67
|
+
expect(data.info).toBeTruthy();
|
|
68
|
+
expect(data.info.title).toBe(formTitle);
|
|
69
|
+
expect(data.responderUri).toBeTruthy();
|
|
70
|
+
|
|
71
|
+
formId = data.formId;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// ── List ──────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
it("list — returns an array of forms including the one we created", async () => {
|
|
77
|
+
const result = await run("forms", "list", "--max-results", "10");
|
|
78
|
+
expect(result.exitCode).toBe(0);
|
|
79
|
+
|
|
80
|
+
const forms = parseJson(result);
|
|
81
|
+
expect(Array.isArray(forms)).toBe(true);
|
|
82
|
+
expect(forms.length).toBeGreaterThan(0);
|
|
83
|
+
|
|
84
|
+
const form = forms[0];
|
|
85
|
+
expect(form).toHaveProperty("id");
|
|
86
|
+
expect(form).toHaveProperty("name");
|
|
87
|
+
expect(form).toHaveProperty("createdTime");
|
|
88
|
+
expect(form).toHaveProperty("modifiedTime");
|
|
89
|
+
|
|
90
|
+
// Our form should be among the results (it was just created)
|
|
91
|
+
const ids = forms.map((f: any) => f.id);
|
|
92
|
+
expect(ids).toContain(formId);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ── Get ───────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
it("get — returns full form details by ID", async () => {
|
|
98
|
+
const result = await run("forms", "get", "--form-id", formId);
|
|
99
|
+
expect(result.exitCode).toBe(0);
|
|
100
|
+
|
|
101
|
+
const data = parseJson(result);
|
|
102
|
+
expect(data.formId).toBe(formId);
|
|
103
|
+
expect(data.info).toBeTruthy();
|
|
104
|
+
expect(data.info.title).toBe(formTitle);
|
|
105
|
+
expect(data.responderUri).toBeTruthy();
|
|
106
|
+
expect(data.revisionId).toBeTruthy();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ── Update ────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
it("update — updates the form title", async () => {
|
|
112
|
+
const newTitle = `cliclaw-updated-${timestamp}`;
|
|
113
|
+
const result = await run(
|
|
114
|
+
"forms", "update",
|
|
115
|
+
"--form-id", formId,
|
|
116
|
+
"--title", newTitle,
|
|
117
|
+
);
|
|
118
|
+
expect(result.exitCode).toBe(0);
|
|
119
|
+
|
|
120
|
+
const data = parseJson(result);
|
|
121
|
+
expect(data.success).toBe(true);
|
|
122
|
+
|
|
123
|
+
// Verify the title was updated
|
|
124
|
+
const getResult = await run("forms", "get", "--form-id", formId);
|
|
125
|
+
const form = parseJson(getResult);
|
|
126
|
+
expect(form.info.title).toBe(newTitle);
|
|
127
|
+
|
|
128
|
+
formTitle = newTitle;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("update — updates the form description", async () => {
|
|
132
|
+
const description = `Test description ${timestamp}`;
|
|
133
|
+
const result = await run(
|
|
134
|
+
"forms", "update",
|
|
135
|
+
"--form-id", formId,
|
|
136
|
+
"--description", description,
|
|
137
|
+
);
|
|
138
|
+
expect(result.exitCode).toBe(0);
|
|
139
|
+
|
|
140
|
+
const data = parseJson(result);
|
|
141
|
+
expect(data.success).toBe(true);
|
|
142
|
+
|
|
143
|
+
// Verify the description was updated
|
|
144
|
+
const getResult = await run("forms", "get", "--form-id", formId);
|
|
145
|
+
const form = parseJson(getResult);
|
|
146
|
+
expect(form.info.description).toBe(description);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ── Add Questions ─────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
it("add-question — adds a text question", async () => {
|
|
152
|
+
const result = await run(
|
|
153
|
+
"forms", "add-question",
|
|
154
|
+
"--form-id", formId,
|
|
155
|
+
"--title", "What is your name?",
|
|
156
|
+
"--type", "text",
|
|
157
|
+
"--required",
|
|
158
|
+
);
|
|
159
|
+
expect(result.exitCode).toBe(0);
|
|
160
|
+
|
|
161
|
+
const data = parseJson(result);
|
|
162
|
+
expect(data.success).toBe(true);
|
|
163
|
+
expect(data.replies).toBeTruthy();
|
|
164
|
+
expect(Array.isArray(data.replies)).toBe(true);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("add-question — adds a paragraph question", async () => {
|
|
168
|
+
const result = await run(
|
|
169
|
+
"forms", "add-question",
|
|
170
|
+
"--form-id", formId,
|
|
171
|
+
"--title", "Tell us about yourself",
|
|
172
|
+
"--type", "paragraph",
|
|
173
|
+
"--index", "1",
|
|
174
|
+
);
|
|
175
|
+
expect(result.exitCode).toBe(0);
|
|
176
|
+
|
|
177
|
+
const data = parseJson(result);
|
|
178
|
+
expect(data.success).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("add-question — adds a multiple choice question", async () => {
|
|
182
|
+
const result = await run(
|
|
183
|
+
"forms", "add-question",
|
|
184
|
+
"--form-id", formId,
|
|
185
|
+
"--title", "What is your favorite color?",
|
|
186
|
+
"--type", "choice",
|
|
187
|
+
"--options", "Red,Blue,Green,Yellow",
|
|
188
|
+
"--index", "2",
|
|
189
|
+
);
|
|
190
|
+
expect(result.exitCode).toBe(0);
|
|
191
|
+
|
|
192
|
+
const data = parseJson(result);
|
|
193
|
+
expect(data.success).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("add-question — adds a checkbox question", async () => {
|
|
197
|
+
const result = await run(
|
|
198
|
+
"forms", "add-question",
|
|
199
|
+
"--form-id", formId,
|
|
200
|
+
"--title", "Select your hobbies",
|
|
201
|
+
"--type", "checkbox",
|
|
202
|
+
"--options", "Reading,Gaming,Cooking,Sports",
|
|
203
|
+
"--index", "3",
|
|
204
|
+
);
|
|
205
|
+
expect(result.exitCode).toBe(0);
|
|
206
|
+
|
|
207
|
+
const data = parseJson(result);
|
|
208
|
+
expect(data.success).toBe(true);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("add-question — adds a dropdown question", async () => {
|
|
212
|
+
const result = await run(
|
|
213
|
+
"forms", "add-question",
|
|
214
|
+
"--form-id", formId,
|
|
215
|
+
"--title", "Select your country",
|
|
216
|
+
"--type", "dropdown",
|
|
217
|
+
"--options", "USA,Canada,UK,Australia,Other",
|
|
218
|
+
"--index", "4",
|
|
219
|
+
);
|
|
220
|
+
expect(result.exitCode).toBe(0);
|
|
221
|
+
|
|
222
|
+
const data = parseJson(result);
|
|
223
|
+
expect(data.success).toBe(true);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("add-question — adds a scale question", async () => {
|
|
227
|
+
const result = await run(
|
|
228
|
+
"forms", "add-question",
|
|
229
|
+
"--form-id", formId,
|
|
230
|
+
"--title", "Rate your experience (1-5)",
|
|
231
|
+
"--type", "scale",
|
|
232
|
+
"--index", "5",
|
|
233
|
+
);
|
|
234
|
+
expect(result.exitCode).toBe(0);
|
|
235
|
+
|
|
236
|
+
const data = parseJson(result);
|
|
237
|
+
expect(data.success).toBe(true);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("add-question — adds a date question", async () => {
|
|
241
|
+
const result = await run(
|
|
242
|
+
"forms", "add-question",
|
|
243
|
+
"--form-id", formId,
|
|
244
|
+
"--title", "When is your birthday?",
|
|
245
|
+
"--type", "date",
|
|
246
|
+
"--index", "6",
|
|
247
|
+
);
|
|
248
|
+
expect(result.exitCode).toBe(0);
|
|
249
|
+
|
|
250
|
+
const data = parseJson(result);
|
|
251
|
+
expect(data.success).toBe(true);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("add-question — adds a time question", async () => {
|
|
255
|
+
const result = await run(
|
|
256
|
+
"forms", "add-question",
|
|
257
|
+
"--form-id", formId,
|
|
258
|
+
"--title", "What time works for you?",
|
|
259
|
+
"--type", "time",
|
|
260
|
+
"--index", "7",
|
|
261
|
+
);
|
|
262
|
+
expect(result.exitCode).toBe(0);
|
|
263
|
+
|
|
264
|
+
const data = parseJson(result);
|
|
265
|
+
expect(data.success).toBe(true);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// ── List Questions ────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
it("questions — lists all questions in the form", async () => {
|
|
271
|
+
const result = await run("forms", "questions", "--form-id", formId);
|
|
272
|
+
expect(result.exitCode).toBe(0);
|
|
273
|
+
|
|
274
|
+
const questions = parseJson(result);
|
|
275
|
+
expect(Array.isArray(questions)).toBe(true);
|
|
276
|
+
expect(questions.length).toBe(8);
|
|
277
|
+
|
|
278
|
+
// Verify question types
|
|
279
|
+
const types = questions.map((q: any) => q.type);
|
|
280
|
+
expect(types).toContain("text");
|
|
281
|
+
expect(types).toContain("choice");
|
|
282
|
+
expect(types).toContain("scale");
|
|
283
|
+
expect(types).toContain("date");
|
|
284
|
+
expect(types).toContain("time");
|
|
285
|
+
|
|
286
|
+
// Verify structure
|
|
287
|
+
const q = questions[0];
|
|
288
|
+
expect(q).toHaveProperty("itemId");
|
|
289
|
+
expect(q).toHaveProperty("title");
|
|
290
|
+
expect(q).toHaveProperty("questionId");
|
|
291
|
+
expect(q).toHaveProperty("type");
|
|
292
|
+
expect(q).toHaveProperty("required");
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// ── Update Question ───────────────────────────────────────────────
|
|
296
|
+
|
|
297
|
+
it("update-question — updates a question title", async () => {
|
|
298
|
+
const result = await run(
|
|
299
|
+
"forms", "update-question",
|
|
300
|
+
"--form-id", formId,
|
|
301
|
+
"--item-index", "0",
|
|
302
|
+
"--title", "What is your full name?",
|
|
303
|
+
);
|
|
304
|
+
expect(result.exitCode).toBe(0);
|
|
305
|
+
|
|
306
|
+
const data = parseJson(result);
|
|
307
|
+
expect(data.success).toBe(true);
|
|
308
|
+
|
|
309
|
+
// Verify the update
|
|
310
|
+
const getResult = await run("forms", "questions", "--form-id", formId);
|
|
311
|
+
const questions = parseJson(getResult);
|
|
312
|
+
expect(questions[0].title).toBe("What is your full name?");
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// ── Delete Question ───────────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
it("delete-question — deletes the last question (time)", async () => {
|
|
318
|
+
const result = await run(
|
|
319
|
+
"forms", "delete-question",
|
|
320
|
+
"--form-id", formId,
|
|
321
|
+
"--item-index", "7",
|
|
322
|
+
);
|
|
323
|
+
expect(result.exitCode).toBe(0);
|
|
324
|
+
|
|
325
|
+
const data = parseJson(result);
|
|
326
|
+
expect(data.success).toBe(true);
|
|
327
|
+
|
|
328
|
+
// Verify it was deleted
|
|
329
|
+
const getResult = await run("forms", "questions", "--form-id", formId);
|
|
330
|
+
const questions = parseJson(getResult);
|
|
331
|
+
expect(questions.length).toBe(7);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// ── Responses (empty — no submissions yet) ────────────────────────
|
|
335
|
+
|
|
336
|
+
it("responses — returns an empty array for a new form", async () => {
|
|
337
|
+
const result = await run("forms", "responses", "--form-id", formId);
|
|
338
|
+
expect(result.exitCode).toBe(0);
|
|
339
|
+
|
|
340
|
+
const responses = parseJson(result);
|
|
341
|
+
expect(Array.isArray(responses)).toBe(true);
|
|
342
|
+
expect(responses.length).toBe(0);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// ── Error Handling ────────────────────────────────────────────────
|
|
346
|
+
|
|
347
|
+
it("get — returns error for non-existent form ID", async () => {
|
|
348
|
+
const result = await run("forms", "get", "--form-id", "nonexistent-form-id-12345");
|
|
349
|
+
expect(result.exitCode).not.toBe(0);
|
|
350
|
+
|
|
351
|
+
const data = parseJson(result);
|
|
352
|
+
expect(data.error).toBe("get_form_failed");
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("update — returns error when no fields provided", async () => {
|
|
356
|
+
const result = await run(
|
|
357
|
+
"forms", "update",
|
|
358
|
+
"--form-id", formId,
|
|
359
|
+
);
|
|
360
|
+
expect(result.exitCode).not.toBe(0);
|
|
361
|
+
|
|
362
|
+
const data = parseJson(result);
|
|
363
|
+
expect(data.error).toBe("update_form_failed");
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("delete-question — returns error for out-of-range index", async () => {
|
|
367
|
+
const result = await run(
|
|
368
|
+
"forms", "delete-question",
|
|
369
|
+
"--form-id", formId,
|
|
370
|
+
"--item-index", "999",
|
|
371
|
+
);
|
|
372
|
+
expect(result.exitCode).not.toBe(0);
|
|
373
|
+
|
|
374
|
+
const data = parseJson(result);
|
|
375
|
+
expect(data.error).toBe("delete_question_failed");
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("response — returns error for non-existent response ID", async () => {
|
|
379
|
+
const result = await run(
|
|
380
|
+
"forms", "response",
|
|
381
|
+
"--form-id", formId,
|
|
382
|
+
"--response-id", "nonexistent-response-id",
|
|
383
|
+
);
|
|
384
|
+
expect(result.exitCode).not.toBe(0);
|
|
385
|
+
|
|
386
|
+
const data = parseJson(result);
|
|
387
|
+
expect(data.error).toBe("get_response_failed");
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// ── Cleanup ───────────────────────────────────────────────────────
|
|
391
|
+
// Note: Google Forms API does not have a delete endpoint.
|
|
392
|
+
// Forms can only be trashed via the Drive API.
|
|
393
|
+
// We use the gdrive integration to clean up.
|
|
394
|
+
|
|
395
|
+
it("cleanup — trashes the test form via Google Drive", async () => {
|
|
396
|
+
const result = await run("gdrive", "trash", "--id", formId);
|
|
397
|
+
expect(result.exitCode).toBe(0);
|
|
398
|
+
|
|
399
|
+
const data = parseJson(result);
|
|
400
|
+
expect(data.success).toBe(true);
|
|
401
|
+
expect(data.action).toBe("trash");
|
|
402
|
+
});
|
|
403
|
+
});
|