@zapier/zapier-sdk 0.33.0 → 0.33.2

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.
Files changed (128) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.cjs +2 -1
  3. package/dist/index.d.mts +9 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.mjs +2 -1
  7. package/dist/plugins/registry/index.d.ts.map +1 -1
  8. package/dist/plugins/registry/index.js +1 -0
  9. package/dist/types/sdk.d.ts +8 -0
  10. package/dist/types/sdk.d.ts.map +1 -1
  11. package/package.json +2 -2
  12. package/dist/api/auth.test.d.ts +0 -2
  13. package/dist/api/auth.test.d.ts.map +0 -1
  14. package/dist/api/auth.test.js +0 -220
  15. package/dist/api/client.test.d.ts +0 -2
  16. package/dist/api/client.test.d.ts.map +0 -1
  17. package/dist/api/client.test.js +0 -611
  18. package/dist/api/debug.test.d.ts +0 -2
  19. package/dist/api/debug.test.d.ts.map +0 -1
  20. package/dist/api/debug.test.js +0 -59
  21. package/dist/api/polling.test.d.ts +0 -2
  22. package/dist/api/polling.test.d.ts.map +0 -1
  23. package/dist/api/polling.test.js +0 -360
  24. package/dist/auth.test.d.ts +0 -2
  25. package/dist/auth.test.d.ts.map +0 -1
  26. package/dist/auth.test.js +0 -480
  27. package/dist/plugins/eventEmission/builders.test.d.ts +0 -2
  28. package/dist/plugins/eventEmission/builders.test.d.ts.map +0 -1
  29. package/dist/plugins/eventEmission/builders.test.js +0 -138
  30. package/dist/plugins/eventEmission/index.test.d.ts +0 -5
  31. package/dist/plugins/eventEmission/index.test.d.ts.map +0 -1
  32. package/dist/plugins/eventEmission/index.test.js +0 -712
  33. package/dist/plugins/eventEmission/transport.test.d.ts +0 -5
  34. package/dist/plugins/eventEmission/transport.test.d.ts.map +0 -1
  35. package/dist/plugins/eventEmission/transport.test.js +0 -164
  36. package/dist/plugins/fetch/index.test.d.ts +0 -2
  37. package/dist/plugins/fetch/index.test.d.ts.map +0 -1
  38. package/dist/plugins/fetch/index.test.js +0 -428
  39. package/dist/plugins/findFirstConnection/index.test.d.ts +0 -2
  40. package/dist/plugins/findFirstConnection/index.test.d.ts.map +0 -1
  41. package/dist/plugins/findFirstConnection/index.test.js +0 -177
  42. package/dist/plugins/findUniqueConnection/index.test.d.ts +0 -2
  43. package/dist/plugins/findUniqueConnection/index.test.d.ts.map +0 -1
  44. package/dist/plugins/findUniqueConnection/index.test.js +0 -159
  45. package/dist/plugins/getAction/index.test.d.ts +0 -2
  46. package/dist/plugins/getAction/index.test.d.ts.map +0 -1
  47. package/dist/plugins/getAction/index.test.js +0 -211
  48. package/dist/plugins/getApp/index.test.d.ts +0 -2
  49. package/dist/plugins/getApp/index.test.d.ts.map +0 -1
  50. package/dist/plugins/getApp/index.test.js +0 -157
  51. package/dist/plugins/getConnection/index.test.d.ts +0 -2
  52. package/dist/plugins/getConnection/index.test.d.ts.map +0 -1
  53. package/dist/plugins/getConnection/index.test.js +0 -124
  54. package/dist/plugins/getInputFieldsSchema/index.test.d.ts +0 -2
  55. package/dist/plugins/getInputFieldsSchema/index.test.d.ts.map +0 -1
  56. package/dist/plugins/getInputFieldsSchema/index.test.js +0 -291
  57. package/dist/plugins/listActions/index.test.d.ts +0 -2
  58. package/dist/plugins/listActions/index.test.d.ts.map +0 -1
  59. package/dist/plugins/listActions/index.test.js +0 -454
  60. package/dist/plugins/listApps/index.test.d.ts +0 -2
  61. package/dist/plugins/listApps/index.test.d.ts.map +0 -1
  62. package/dist/plugins/listApps/index.test.js +0 -124
  63. package/dist/plugins/listConnections/index.test.d.ts +0 -2
  64. package/dist/plugins/listConnections/index.test.d.ts.map +0 -1
  65. package/dist/plugins/listConnections/index.test.js +0 -920
  66. package/dist/plugins/listInputFieldChoices/index.test.d.ts +0 -2
  67. package/dist/plugins/listInputFieldChoices/index.test.d.ts.map +0 -1
  68. package/dist/plugins/listInputFieldChoices/index.test.js +0 -717
  69. package/dist/plugins/listInputFields/index.test.d.ts +0 -2
  70. package/dist/plugins/listInputFields/index.test.d.ts.map +0 -1
  71. package/dist/plugins/listInputFields/index.test.js +0 -359
  72. package/dist/plugins/manifest/index.test.d.ts +0 -2
  73. package/dist/plugins/manifest/index.test.d.ts.map +0 -1
  74. package/dist/plugins/manifest/index.test.js +0 -1179
  75. package/dist/plugins/request/index.test.d.ts +0 -2
  76. package/dist/plugins/request/index.test.d.ts.map +0 -1
  77. package/dist/plugins/request/index.test.js +0 -458
  78. package/dist/plugins/runAction/index.test.d.ts +0 -2
  79. package/dist/plugins/runAction/index.test.d.ts.map +0 -1
  80. package/dist/plugins/runAction/index.test.js +0 -350
  81. package/dist/resolvers/connectionId.test.d.ts +0 -2
  82. package/dist/resolvers/connectionId.test.d.ts.map +0 -1
  83. package/dist/resolvers/connectionId.test.js +0 -61
  84. package/dist/sdk.test.d.ts +0 -2
  85. package/dist/sdk.test.d.ts.map +0 -1
  86. package/dist/sdk.test.js +0 -260
  87. package/dist/types/domain.test.d.ts +0 -2
  88. package/dist/types/domain.test.d.ts.map +0 -1
  89. package/dist/types/domain.test.js +0 -39
  90. package/dist/utils/array-utils.test.d.ts +0 -2
  91. package/dist/utils/array-utils.test.d.ts.map +0 -1
  92. package/dist/utils/array-utils.test.js +0 -107
  93. package/dist/utils/batch-utils.test.d.ts +0 -2
  94. package/dist/utils/batch-utils.test.d.ts.map +0 -1
  95. package/dist/utils/batch-utils.test.js +0 -476
  96. package/dist/utils/domain-utils.test.d.ts +0 -2
  97. package/dist/utils/domain-utils.test.d.ts.map +0 -1
  98. package/dist/utils/domain-utils.test.js +0 -346
  99. package/dist/utils/file-utils.test.d.ts +0 -2
  100. package/dist/utils/file-utils.test.d.ts.map +0 -1
  101. package/dist/utils/file-utils.test.js +0 -51
  102. package/dist/utils/function-utils.test.d.ts +0 -2
  103. package/dist/utils/function-utils.test.d.ts.map +0 -1
  104. package/dist/utils/function-utils.test.js +0 -188
  105. package/dist/utils/id-utils.test.d.ts +0 -2
  106. package/dist/utils/id-utils.test.d.ts.map +0 -1
  107. package/dist/utils/id-utils.test.js +0 -22
  108. package/dist/utils/pagination-utils.test.d.ts +0 -17
  109. package/dist/utils/pagination-utils.test.d.ts.map +0 -1
  110. package/dist/utils/pagination-utils.test.js +0 -461
  111. package/dist/utils/retry-utils.test.d.ts +0 -2
  112. package/dist/utils/retry-utils.test.d.ts.map +0 -1
  113. package/dist/utils/retry-utils.test.js +0 -90
  114. package/dist/utils/string-utils.test.d.ts +0 -2
  115. package/dist/utils/string-utils.test.d.ts.map +0 -1
  116. package/dist/utils/string-utils.test.js +0 -59
  117. package/dist/utils/telemetry-context.test.d.ts +0 -2
  118. package/dist/utils/telemetry-context.test.d.ts.map +0 -1
  119. package/dist/utils/telemetry-context.test.js +0 -154
  120. package/dist/utils/telemetry-utils.test.d.ts +0 -2
  121. package/dist/utils/telemetry-utils.test.d.ts.map +0 -1
  122. package/dist/utils/telemetry-utils.test.js +0 -155
  123. package/dist/utils/url-utils.test.d.ts +0 -2
  124. package/dist/utils/url-utils.test.d.ts.map +0 -1
  125. package/dist/utils/url-utils.test.js +0 -103
  126. package/dist/utils/validation.test.d.ts +0 -2
  127. package/dist/utils/validation.test.d.ts.map +0 -1
  128. package/dist/utils/validation.test.js +0 -44
@@ -1,1179 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { manifestPlugin, readManifestFromFile, findManifestEntry, getPreferredManifestEntryKey, } from "./index";
3
- import { createSdk } from "../../sdk";
4
- import { eventEmissionPlugin } from "../eventEmission";
5
- // Mock file-utils module
6
- vi.mock("../../utils/file-utils", () => ({
7
- readFile: vi.fn(),
8
- writeFile: vi.fn(),
9
- resolve: vi.fn((path) => Promise.resolve(`/resolved/${path}`)),
10
- }));
11
- import { readFile, resolve } from "../../utils/file-utils";
12
- const mockReadFile = vi.mocked(readFile);
13
- const mockResolve = vi.mocked(resolve);
14
- const mockResolveAppKeys = vi.fn();
15
- describe("manifestPlugin", () => {
16
- const mockManifest = {
17
- apps: {
18
- slack: {
19
- implementationName: "SlackCLIAPI",
20
- version: "1.21.1",
21
- },
22
- "google-sheets": {
23
- implementationName: "GoogleSheetsCLIAPI",
24
- version: "2.0.0",
25
- },
26
- },
27
- };
28
- const mockManifestContent = JSON.stringify(mockManifest);
29
- let mockApiClient;
30
- let mockSdk;
31
- beforeEach(() => {
32
- vi.clearAllMocks();
33
- vi.spyOn(console, "warn").mockImplementation(() => { });
34
- mockApiClient = {
35
- get: vi.fn().mockImplementation((url, options) => {
36
- if (url === "/zapier/api/v4/implementations/") {
37
- // Mock for manifest entries (versioned)
38
- return Promise.resolve({
39
- count: 1,
40
- next: null,
41
- previous: null,
42
- results: [
43
- {
44
- selected_api: "SlackCLIAPI@1.29.0",
45
- app_id: null,
46
- service_id: null,
47
- auth_type: "oauth",
48
- auth_fields: [
49
- {
50
- key: "access_token",
51
- required: true,
52
- type: "unicode",
53
- computed: true,
54
- },
55
- ],
56
- is_deprecated: false,
57
- is_private_only: false,
58
- is_invite_only: false,
59
- is_beta: false,
60
- is_premium: false,
61
- is_hidden: false,
62
- name: "Slack (1.29.0)",
63
- name_clean: "Slack",
64
- version: "1.29.0",
65
- slug: null,
66
- images: {
67
- url_16x16: "https://zapier-images.imgix.net/storage/services/6cf3f5a461feadfba7abc93c4c395b33_2.png?auto=format%2Ccompress&fit=crop&h=16&ixlib=python-3.0.0&q=50&w=16",
68
- url_32x32: "https://zapier-images.imgix.net/storage/services/6cf3f5a461feadfba7abc93c4c395b33_2.png?auto=format%2Ccompress&fit=crop&h=32&ixlib=python-3.0.0&q=50&w=32",
69
- url_64x64: "https://zapier-images.imgix.net/storage/services/6cf3f5a461feadfba7abc93c4c395b33_2.png?auto=format%2Ccompress&fit=crop&h=64&ixlib=python-3.0.0&q=50&w=64",
70
- url_128x128: "https://zapier-images.imgix.net/storage/services/6cf3f5a461feadfba7abc93c4c395b33_2.png?auto=format%2Ccompress&fit=crop&h=128&ixlib=python-3.0.0&q=50&w=128",
71
- },
72
- primary_color: null,
73
- secondary_color: null,
74
- classification: "third-party",
75
- current_implementation: "SlackCLIAPI@1.30.0",
76
- is_adoptable: true,
77
- is_usable: true,
78
- update_cta_level: "info",
79
- update_cta_message: "This version is legacy. Consider updating to the latest version for better support.",
80
- badges: [
81
- {
82
- label: "Legacy",
83
- color: "primary",
84
- tooltip_markdown: "This version is legacy. Consider updating to the latest version for better support.",
85
- },
86
- ],
87
- },
88
- ],
89
- });
90
- }
91
- else if (url === "/zapier/api/v4/implementations-meta/lookup/") {
92
- // Mock for implementations-meta/lookup fallback
93
- const searchParams = options?.searchParams || {};
94
- // Check if we're looking for a nonexistent app
95
- if (searchParams.slugs?.includes("nonexistent") ||
96
- searchParams.selected_apis?.includes("nonexistent")) {
97
- return Promise.resolve({
98
- count: 0,
99
- next: null,
100
- previous: null,
101
- results: [],
102
- });
103
- }
104
- // Return successful response for known apps (like slack)
105
- return Promise.resolve({
106
- count: 1,
107
- next: null,
108
- previous: null,
109
- results: [
110
- {
111
- id: "SlackCLIAPI@1.30.0",
112
- name: "Slack",
113
- slug: "slack",
114
- images: {},
115
- },
116
- ],
117
- });
118
- }
119
- return Promise.resolve({ count: 0, results: [] });
120
- }),
121
- };
122
- mockSdk = {
123
- listApps: vi.fn().mockReturnValue({
124
- items: vi.fn().mockReturnValue([
125
- {
126
- title: "Slack",
127
- key: "slack",
128
- current_implementation_id: "SlackCLIAPI@1.30.0",
129
- description: "Team communication platform",
130
- },
131
- ][Symbol.iterator]()),
132
- }),
133
- };
134
- });
135
- afterEach(() => {
136
- vi.restoreAllMocks();
137
- });
138
- const apiPlugin = () => ({
139
- context: {
140
- api: mockApiClient,
141
- },
142
- });
143
- const listAppsMockPlugin = () => ({
144
- listApps: mockSdk.listApps,
145
- context: {
146
- meta: {
147
- listApps: {
148
- inputSchema: {},
149
- },
150
- },
151
- },
152
- });
153
- function createTestSdk(options = {}) {
154
- return createSdk(options)
155
- .addPlugin(() => ({
156
- context: {
157
- options: {},
158
- },
159
- }))
160
- .addPlugin(apiPlugin)
161
- .addPlugin(eventEmissionPlugin)
162
- .addPlugin(manifestPlugin);
163
- }
164
- describe("plugin initialization", () => {
165
- it("should provide helper functions with direct manifest", () => {
166
- const sdk = createTestSdk({ manifest: mockManifest });
167
- const context = sdk.getContext();
168
- expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
169
- });
170
- it("should provide helper functions with manifestPath", async () => {
171
- mockReadFile.mockResolvedValue(mockManifestContent);
172
- const sdk = createTestSdk({ manifestPath: "manifest.json" });
173
- const context = sdk.getContext();
174
- expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
175
- // Verify file operations happen when functions are called
176
- await context.getVersionedImplementationId("slack");
177
- expect(mockResolve).toHaveBeenCalledWith("manifest.json");
178
- expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
179
- });
180
- it("should provide helper functions when no manifest or path provided", () => {
181
- const sdk = createTestSdk();
182
- const context = sdk.getContext();
183
- expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
184
- // Manifest functions are available even when no manifest is provided
185
- });
186
- it("should prioritize direct manifest over manifestPath", () => {
187
- mockReadFile.mockResolvedValue('{"apps": {"other": {"implementationName": "Other", "version": "1.0.0"}}}');
188
- createTestSdk({
189
- manifest: mockManifest,
190
- manifestPath: "manifest.json",
191
- });
192
- // Verify that direct manifest is used and file loading is not needed
193
- expect(mockReadFile).not.toHaveBeenCalled();
194
- });
195
- });
196
- describe("getVersionedImplementationId function", () => {
197
- it("should return versioned implementation ID when app exists in manifest", async () => {
198
- const sdk = createTestSdk({ manifest: mockManifest });
199
- const context = sdk.getContext();
200
- const result = await context.getVersionedImplementationId("slack");
201
- expect(result).toBe("SlackCLIAPI@1.21.1");
202
- });
203
- it("should fetch and return versioned implementation ID when app not in manifest", async () => {
204
- const sdk = createTestSdk();
205
- const context = sdk.getContext();
206
- const result = await context.getVersionedImplementationId("slack");
207
- expect(result).toBe("SlackCLIAPI@1.30.0");
208
- });
209
- it("should return null when app does not exist", async () => {
210
- mockSdk.listApps = vi.fn().mockReturnValue({
211
- items: vi.fn().mockReturnValue([][Symbol.iterator]()),
212
- });
213
- const sdk = createTestSdk();
214
- const context = sdk.getContext();
215
- const result = await context.getVersionedImplementationId("nonexistent");
216
- expect(result).toBeNull();
217
- });
218
- });
219
- describe("manifest parsing", () => {
220
- it("should parse valid manifest content", () => {
221
- createTestSdk({ manifest: mockManifest });
222
- // Verify manifest is parsed correctly for other functions to use
223
- });
224
- it("should handle manifest with missing version", () => {
225
- const manifestWithoutVersion = {
226
- apps: {
227
- slack: {
228
- implementationName: "SlackCLIAPI",
229
- },
230
- },
231
- };
232
- createTestSdk({ manifest: manifestWithoutVersion });
233
- // Verify manifest with missing version is handled correctly
234
- });
235
- it("should handle empty manifest", () => {
236
- createTestSdk({ manifest: {} });
237
- // Verify empty manifest is handled correctly
238
- });
239
- });
240
- describe("file loading", () => {
241
- it("should load manifest from file successfully", async () => {
242
- mockReadFile.mockResolvedValue(mockManifestContent);
243
- const sdk = createTestSdk({ manifestPath: "manifest.json" });
244
- const context = sdk.getContext();
245
- // Verify that file operations happen when functions are called
246
- await context.getVersionedImplementationId("slack");
247
- expect(mockResolve).toHaveBeenCalledWith("manifest.json");
248
- expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
249
- });
250
- it("should silently return null when file does not exist (ENOENT)", async () => {
251
- const enoentError = Object.assign(new Error("ENOENT: no such file"), {
252
- code: "ENOENT",
253
- });
254
- mockReadFile.mockRejectedValue(enoentError);
255
- const sdk = createTestSdk({ manifestPath: "nonexistent.json" });
256
- const context = sdk.getContext();
257
- // Trigger file loading by calling a function
258
- await context.getVersionedImplementationId("slack");
259
- // Verify no warning is logged when file simply doesn't exist
260
- expect(console.warn).not.toHaveBeenCalled();
261
- });
262
- it("should silently return null when file does not exist (browser fallback)", async () => {
263
- mockReadFile.mockRejectedValue(new Error("File not found: /some/path"));
264
- const sdk = createTestSdk({ manifestPath: "nonexistent.json" });
265
- const context = sdk.getContext();
266
- // Trigger file loading by calling a function
267
- await context.getVersionedImplementationId("slack");
268
- // Verify no warning is logged when file simply doesn't exist
269
- expect(console.warn).not.toHaveBeenCalled();
270
- });
271
- it("should handle file read errors gracefully", async () => {
272
- mockReadFile.mockRejectedValue(new Error("Permission denied"));
273
- const sdk = createTestSdk({ manifestPath: "protected.json" });
274
- const context = sdk.getContext();
275
- // Trigger file loading by calling a function
276
- await context.getVersionedImplementationId("slack");
277
- // Verify file loading errors (other than file not found) are logged
278
- expect(console.warn).toHaveBeenCalledWith(expect.stringContaining("Failed to read manifest from protected.json"));
279
- });
280
- it("should handle invalid JSON gracefully", async () => {
281
- mockReadFile.mockResolvedValue("invalid json content");
282
- const sdk = createTestSdk({ manifestPath: "invalid.json" });
283
- const context = sdk.getContext();
284
- // Trigger file loading by calling a function
285
- await context.getVersionedImplementationId("slack");
286
- // Verify JSON parsing errors are handled gracefully
287
- expect(console.warn).toHaveBeenCalledWith(expect.stringContaining("Failed to parse manifest from /resolved/invalid.json"), expect.any(SyntaxError));
288
- });
289
- it("should handle JSON without apps property", () => {
290
- mockReadFile.mockResolvedValue('{"other": "data"}');
291
- createTestSdk({ manifestPath: "no-apps.json" });
292
- // Verify missing apps property is handled correctly
293
- });
294
- it("should handle JSON with invalid apps format", () => {
295
- mockReadFile.mockResolvedValue('{"apps": "not an object"}');
296
- createTestSdk({ manifestPath: "invalid-apps.json" });
297
- // Verify invalid apps format is handled correctly
298
- });
299
- });
300
- describe("integration with SDK", () => {
301
- it("should work with createSdk", async () => {
302
- const sdk = createSdk({ manifest: mockManifest })
303
- .addPlugin(apiPlugin)
304
- .addPlugin(listAppsMockPlugin)
305
- .addPlugin(manifestPlugin);
306
- const context = sdk.getContext();
307
- // Verify that the manifest data is used correctly by other functions
308
- const versionedId = await context.getVersionedImplementationId("slack");
309
- expect(versionedId).toBe("SlackCLIAPI@1.21.1");
310
- });
311
- });
312
- describe("findManifestEntry function", () => {
313
- it("should find entry by direct key match", () => {
314
- const manifest = {
315
- apps: {
316
- slack: { implementationName: "SlackCLIAPI", version: "1.21.1" },
317
- gmail: { implementationName: "GmailCLIAPI", version: "2.0.0" },
318
- },
319
- };
320
- const result = findManifestEntry({ appKey: "slack", manifest });
321
- expect(result).toEqual([
322
- "slack",
323
- { implementationName: "SlackCLIAPI", version: "1.21.1" },
324
- ]);
325
- });
326
- it("should find entry by implementation name match", () => {
327
- const manifest = {
328
- apps: {
329
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
330
- GmailCLIAPI: { implementationName: "GmailCLIAPI", version: "2.0.0" },
331
- },
332
- };
333
- const result = findManifestEntry({ appKey: "SlackCLIAPI", manifest });
334
- expect(result).toEqual([
335
- "SlackCLIAPI",
336
- { implementationName: "SlackCLIAPI", version: "1.21.1" },
337
- ]);
338
- });
339
- it("should find existing key when searching by implementation name", () => {
340
- const manifest = {
341
- apps: {
342
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
343
- },
344
- };
345
- // This is the key test: searching by implementation name should find existing entry
346
- const result = findManifestEntry({ appKey: "SlackCLIAPI", manifest });
347
- expect(result).toEqual([
348
- "SlackCLIAPI",
349
- { implementationName: "SlackCLIAPI", version: "1.21.1" },
350
- ]);
351
- });
352
- it("should return null when no match found", () => {
353
- const manifest = {
354
- apps: {
355
- gmail: { implementationName: "GmailCLIAPI", version: "2.0.0" },
356
- },
357
- };
358
- const result = findManifestEntry({ appKey: "slack", manifest });
359
- expect(result).toBeNull();
360
- });
361
- it("should handle snake-cased slugs", () => {
362
- const manifest = {
363
- apps: {
364
- "google-sheets": {
365
- implementationName: "GoogleSheetsCLIAPI",
366
- version: "1.0.0",
367
- },
368
- },
369
- };
370
- const result = findManifestEntry({ appKey: "google_sheets", manifest });
371
- expect(result).toEqual([
372
- "google-sheets",
373
- { implementationName: "GoogleSheetsCLIAPI", version: "1.0.0" },
374
- ]);
375
- });
376
- it("should NOT find entry when searching slug against implementation name key (current limitation)", () => {
377
- const manifest = {
378
- apps: {
379
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
380
- },
381
- };
382
- // This will fail - we search by "slack" but can't find "SlackCLIAPI" entry
383
- // This is the core issue we need to solve
384
- const result = findManifestEntry({ appKey: "slack", manifest });
385
- expect(result).toBeNull(); // Current behavior - doesn't find it
386
- // We need to fix this so it CAN find existing implementation name entries
387
- });
388
- });
389
- describe("updateManifestEntry with slug-to-implementation matching", () => {
390
- it("should find existing implementation name entry when input is slug", async () => {
391
- // Mock existing manifest with implementation name as key
392
- mockReadFile.mockResolvedValue(JSON.stringify({
393
- apps: {
394
- SlackCLIAPI: {
395
- implementationName: "SlackCLIAPI",
396
- version: "1.21.1",
397
- },
398
- },
399
- }));
400
- // Mock resolveAppKeys to resolve "slack" to "SlackCLIAPI"
401
- mockResolveAppKeys.mockResolvedValue([
402
- {
403
- slug: "slack",
404
- implementationName: "SlackCLIAPI",
405
- version: "1.30.0",
406
- lookupAppKey: "slack",
407
- },
408
- ]);
409
- const sdk = createTestSdk();
410
- const context = sdk.getContext();
411
- // This should now find the existing "SlackCLIAPI" entry
412
- const { key: manifestKey } = await context.updateManifestEntry({
413
- appKey: "slack", // Input is slug
414
- entry: { implementationName: "SlackCLIAPI", version: "1.30.0" },
415
- });
416
- // Should return the existing key, not create a new one
417
- expect(manifestKey).toBe("SlackCLIAPI");
418
- });
419
- });
420
- describe("resolveAppKeys function", () => {
421
- it("should resolve slug to manifest entry even when manifest uses implementation name as key", async () => {
422
- const manifest = {
423
- apps: {
424
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
425
- },
426
- };
427
- // Mock API to return slack app data when looking up "slack"
428
- mockApiClient.get = vi.fn().mockResolvedValue({
429
- count: 1,
430
- next: null,
431
- previous: null,
432
- results: [
433
- {
434
- id: "SlackCLIAPI@1.30.0",
435
- name: "Slack",
436
- slug: "slack",
437
- key: "SlackCLIAPI", // This is the implementation name
438
- version: "1.30.0",
439
- },
440
- ],
441
- });
442
- // Test resolveAppKeys directly using the manifest plugin
443
- const sdk = createSdk({ manifest })
444
- .addPlugin(apiPlugin)
445
- .addPlugin(manifestPlugin);
446
- const context = sdk.getContext();
447
- const resolved = await context.resolveAppKeys({ appKeys: ["slack"] });
448
- // Should find the manifest entry and use its version (1.21.1) instead of latest (1.30.0)
449
- expect(resolved).toHaveLength(1);
450
- expect(resolved[0]).toEqual({
451
- slug: "slack",
452
- implementationName: "SlackCLIAPI",
453
- version: "1.21.1", // Should use manifest version, not API version
454
- lookupAppKey: "slack",
455
- });
456
- });
457
- it("should fall back to API version when no manifest entry found", async () => {
458
- const manifest = { apps: {} }; // Empty manifest
459
- // Mock API to return slack app data
460
- mockApiClient.get = vi.fn().mockResolvedValue({
461
- count: 1,
462
- next: null,
463
- previous: null,
464
- results: [
465
- {
466
- id: "SlackCLIAPI@1.30.0",
467
- name: "Slack",
468
- slug: "slack",
469
- key: "SlackCLIAPI",
470
- version: "1.30.0",
471
- },
472
- ],
473
- });
474
- const sdk = createSdk({ manifest })
475
- .addPlugin(apiPlugin)
476
- .addPlugin(manifestPlugin);
477
- const context = sdk.getContext();
478
- const resolved = await context.resolveAppKeys({ appKeys: ["slack"] });
479
- // Should use API version since no manifest entry found
480
- expect(resolved).toHaveLength(1);
481
- expect(resolved[0]).toEqual({
482
- slug: "slack",
483
- implementationName: "SlackCLIAPI",
484
- version: "1.30.0", // Should use API version
485
- lookupAppKey: "slack",
486
- });
487
- });
488
- it("should always use version from input when specified (slug@version)", async () => {
489
- const manifest = {
490
- apps: {
491
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
492
- },
493
- };
494
- // Mock API to return slack app data
495
- mockApiClient.get = vi.fn().mockResolvedValue({
496
- count: 1,
497
- next: null,
498
- previous: null,
499
- results: [
500
- {
501
- id: "SlackCLIAPI@1.30.0",
502
- name: "Slack",
503
- slug: "slack",
504
- key: "SlackCLIAPI",
505
- version: "1.30.0",
506
- },
507
- ],
508
- });
509
- const sdk = createSdk({ manifest })
510
- .addPlugin(apiPlugin)
511
- .addPlugin(manifestPlugin);
512
- const context = sdk.getContext();
513
- // Input version should take precedence over both manifest (1.21.1) and API (1.30.0)
514
- const resolved = await context.resolveAppKeys({
515
- appKeys: ["slack@1.2.3"],
516
- });
517
- expect(resolved).toHaveLength(1);
518
- expect(resolved[0]).toEqual({
519
- slug: "slack",
520
- implementationName: "SlackCLIAPI",
521
- version: "1.2.3", // Should use input version, not manifest or API
522
- lookupAppKey: "slack",
523
- });
524
- });
525
- it("should always use version from input when specified (implementationName@version)", async () => {
526
- const manifest = {
527
- apps: {
528
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
529
- },
530
- };
531
- const sdk = createSdk({ manifest })
532
- .addPlugin(apiPlugin)
533
- .addPlugin(manifestPlugin);
534
- const context = sdk.getContext();
535
- // Input version should take precedence over manifest version
536
- const resolved = await context.resolveAppKeys({
537
- appKeys: ["SlackCLIAPI@1.2.3"],
538
- });
539
- expect(resolved).toHaveLength(1);
540
- expect(resolved[0]).toEqual({
541
- implementationName: "SlackCLIAPI",
542
- version: "1.2.3", // Should use input version, not manifest version
543
- lookupAppKey: "SlackCLIAPI",
544
- });
545
- });
546
- });
547
- });
548
- describe("readManifestFromFile", () => {
549
- const mockManifestContent = JSON.stringify({
550
- apps: {
551
- slack: {
552
- implementationName: "SlackCLIAPI",
553
- version: "1.21.1",
554
- },
555
- "google-sheets": {
556
- implementationName: "GoogleSheetsCLIAPI",
557
- version: "2.0.0",
558
- },
559
- },
560
- });
561
- beforeEach(() => {
562
- vi.clearAllMocks();
563
- });
564
- it("should read and parse manifest from file", async () => {
565
- mockReadFile.mockResolvedValue(mockManifestContent);
566
- const result = await readManifestFromFile("manifest.json");
567
- expect(mockResolve).toHaveBeenCalledWith("manifest.json");
568
- expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
569
- expect(result).toEqual({
570
- apps: {
571
- slack: {
572
- implementationName: "SlackCLIAPI",
573
- version: "1.21.1",
574
- },
575
- "google-sheets": {
576
- implementationName: "GoogleSheetsCLIAPI",
577
- version: "2.0.0",
578
- },
579
- },
580
- });
581
- });
582
- it("should return null without warning when file does not exist (ENOENT)", async () => {
583
- vi.spyOn(console, "warn").mockImplementation(() => { });
584
- const enoentError = Object.assign(new Error("ENOENT: no such file"), {
585
- code: "ENOENT",
586
- });
587
- mockReadFile.mockRejectedValue(enoentError);
588
- const result = await readManifestFromFile("nonexistent.json");
589
- expect(result).toBeNull();
590
- expect(console.warn).not.toHaveBeenCalled();
591
- });
592
- it("should return null without warning when file does not exist (browser fallback)", async () => {
593
- vi.spyOn(console, "warn").mockImplementation(() => { });
594
- mockReadFile.mockRejectedValue(new Error("File not found: /some/path"));
595
- const result = await readManifestFromFile("nonexistent.json");
596
- expect(result).toBeNull();
597
- expect(console.warn).not.toHaveBeenCalled();
598
- });
599
- it("should handle file read errors with warning", async () => {
600
- vi.spyOn(console, "warn").mockImplementation(() => { });
601
- mockReadFile.mockRejectedValue(new Error("Permission denied"));
602
- const result = await readManifestFromFile("protected.json");
603
- expect(result).toBeNull();
604
- expect(console.warn).toHaveBeenCalledWith(expect.stringContaining("Failed to read manifest from protected.json"));
605
- });
606
- it("should handle invalid JSON content", async () => {
607
- mockReadFile.mockResolvedValue("invalid json");
608
- const result = await readManifestFromFile("invalid.json");
609
- expect(result).toBeNull();
610
- });
611
- describe("getPreferredManifestEntryKey", () => {
612
- let mockApi;
613
- beforeEach(() => {
614
- mockApi = {
615
- get: vi.fn(),
616
- };
617
- });
618
- it("should prefer slug when available", async () => {
619
- const result = await getPreferredManifestEntryKey({
620
- appKey: "slack",
621
- api: mockApi,
622
- });
623
- expect(result).toBe("slack");
624
- });
625
- it("should look up slug for implementation name", async () => {
626
- // Mock API response for implementation name lookup
627
- mockApi.get = vi.fn().mockResolvedValueOnce({
628
- results: [
629
- {
630
- key: "SlackCLIAPI",
631
- slug: "slack",
632
- version: "1.0.0",
633
- current_implementation_id: "SlackCLIAPI@1.0.0",
634
- },
635
- ],
636
- next: null,
637
- });
638
- const result = await getPreferredManifestEntryKey({
639
- appKey: "SlackCLIAPI",
640
- api: mockApi,
641
- });
642
- expect(result).toBe("slack");
643
- expect(mockApi.get).toHaveBeenCalledWith("/zapier/api/v4/implementations-meta/lookup/", {
644
- searchParams: { selected_apis: "SlackCLIAPI" },
645
- });
646
- });
647
- it("should fall back to implementation name if slug lookup fails", async () => {
648
- // Mock API to return empty results
649
- mockApi.get = vi.fn().mockResolvedValueOnce({
650
- results: [],
651
- next: null,
652
- });
653
- const result = await getPreferredManifestEntryKey({
654
- appKey: "SlackCLIAPI",
655
- api: mockApi,
656
- });
657
- expect(result).toBe("SlackCLIAPI");
658
- });
659
- it("should fall back to original key if no slug or implementation name", async () => {
660
- const result = await getPreferredManifestEntryKey({
661
- appKey: "some-random-key",
662
- api: mockApi,
663
- });
664
- expect(result).toBe("some-random-key");
665
- });
666
- });
667
- describe("Action management", () => {
668
- const mockManifest = {
669
- apps: {
670
- slack: {
671
- implementationName: "SlackCLIAPI",
672
- version: "1.21.1",
673
- },
674
- },
675
- };
676
- let mockApiClient;
677
- beforeEach(() => {
678
- vi.clearAllMocks();
679
- vi.spyOn(console, "warn").mockImplementation(() => { });
680
- mockApiClient = {
681
- get: vi.fn(),
682
- };
683
- });
684
- afterEach(() => {
685
- vi.restoreAllMocks();
686
- });
687
- const apiPlugin = () => ({
688
- context: {
689
- api: mockApiClient,
690
- },
691
- });
692
- function createTestSdk(options = {}) {
693
- return createSdk(options).addPlugin(apiPlugin).addPlugin(manifestPlugin);
694
- }
695
- describe("findActionEntry", () => {
696
- it("should find an existing action by name", () => {
697
- const manifest = {
698
- apps: mockManifest.apps,
699
- actions: {
700
- "weekly-report": {
701
- appKey: "slack",
702
- actionKey: "post_message",
703
- actionType: "write",
704
- authenticationId: 123,
705
- inputs: { channel: "#general" },
706
- schema: {},
707
- createdAt: "2025-11-18T12:00:00.000Z",
708
- },
709
- },
710
- };
711
- const sdk = createTestSdk({ manifest });
712
- const context = sdk.getContext();
713
- const action = context.findActionEntry({
714
- name: "weekly-report",
715
- manifest,
716
- });
717
- expect(action).toEqual({
718
- appKey: "slack",
719
- actionKey: "post_message",
720
- actionType: "write",
721
- authenticationId: 123,
722
- inputs: { channel: "#general" },
723
- schema: {},
724
- createdAt: "2025-11-18T12:00:00.000Z",
725
- });
726
- });
727
- it("should return null for non-existent action", () => {
728
- const manifest = {
729
- apps: mockManifest.apps,
730
- actions: {},
731
- };
732
- const sdk = createTestSdk({ manifest });
733
- const context = sdk.getContext();
734
- const action = context.findActionEntry({
735
- name: "non-existent",
736
- manifest,
737
- });
738
- expect(action).toBeNull();
739
- });
740
- it("should return null when manifest has no actions section", () => {
741
- const manifest = {
742
- apps: mockManifest.apps,
743
- };
744
- const sdk = createTestSdk({ manifest });
745
- const context = sdk.getContext();
746
- const action = context.findActionEntry({
747
- name: "any-action",
748
- manifest,
749
- });
750
- expect(action).toBeNull();
751
- });
752
- });
753
- describe("hasActionEntry", () => {
754
- it("should return true for existing action", () => {
755
- const manifest = {
756
- apps: mockManifest.apps,
757
- actions: {
758
- "weekly-report": {
759
- appKey: "slack",
760
- actionKey: "post_message",
761
- actionType: "write",
762
- authenticationId: 123,
763
- inputs: {},
764
- schema: {},
765
- createdAt: "2025-11-18T12:00:00.000Z",
766
- },
767
- },
768
- };
769
- const sdk = createTestSdk({ manifest });
770
- const context = sdk.getContext();
771
- const exists = context.hasActionEntry({
772
- name: "weekly-report",
773
- manifest,
774
- });
775
- expect(exists).toBe(true);
776
- });
777
- it("should return false for non-existent action", () => {
778
- const manifest = {
779
- apps: mockManifest.apps,
780
- actions: {},
781
- };
782
- const sdk = createTestSdk({ manifest });
783
- const context = sdk.getContext();
784
- const exists = context.hasActionEntry({
785
- name: "non-existent",
786
- manifest,
787
- });
788
- expect(exists).toBe(false);
789
- });
790
- it("should return false when manifest has no actions section", () => {
791
- const manifest = {
792
- apps: mockManifest.apps,
793
- };
794
- const sdk = createTestSdk({ manifest });
795
- const context = sdk.getContext();
796
- const exists = context.hasActionEntry({ name: "any-action", manifest });
797
- expect(exists).toBe(false);
798
- });
799
- });
800
- describe("listActionEntries", () => {
801
- it("should return all actions as [name, entry] tuples", async () => {
802
- const mockManifestContent = JSON.stringify({
803
- apps: mockManifest.apps,
804
- actions: {
805
- "weekly-report": {
806
- appKey: "slack",
807
- actionKey: "post_message",
808
- actionType: "write",
809
- authenticationId: 123,
810
- inputs: { channel: "#general" },
811
- schema: {},
812
- createdAt: "2025-11-18T12:00:00.000Z",
813
- },
814
- "daily-summary": {
815
- appKey: "slack",
816
- actionKey: "post_message",
817
- actionType: "write",
818
- authenticationId: 123,
819
- inputs: { channel: "#daily" },
820
- schema: {},
821
- createdAt: "2025-11-18T13:00:00.000Z",
822
- },
823
- },
824
- });
825
- mockReadFile.mockResolvedValue(mockManifestContent);
826
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
827
- const context = sdk.getContext();
828
- const actions = await context.listActionEntries({
829
- configPath: ".zapierrc",
830
- });
831
- expect(actions).toHaveLength(2);
832
- expect(actions).toEqual([
833
- [
834
- "weekly-report",
835
- {
836
- appKey: "slack",
837
- actionKey: "post_message",
838
- actionType: "write",
839
- authenticationId: 123,
840
- inputs: { channel: "#general" },
841
- schema: {},
842
- createdAt: "2025-11-18T12:00:00.000Z",
843
- },
844
- ],
845
- [
846
- "daily-summary",
847
- {
848
- appKey: "slack",
849
- actionKey: "post_message",
850
- actionType: "write",
851
- authenticationId: 123,
852
- inputs: { channel: "#daily" },
853
- schema: {},
854
- createdAt: "2025-11-18T13:00:00.000Z",
855
- },
856
- ],
857
- ]);
858
- });
859
- it("should return empty array when no actions exist", async () => {
860
- const mockManifestContent = JSON.stringify({
861
- apps: mockManifest.apps,
862
- });
863
- mockReadFile.mockResolvedValue(mockManifestContent);
864
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
865
- const context = sdk.getContext();
866
- const actions = await context.listActionEntries({
867
- configPath: ".zapierrc",
868
- });
869
- expect(actions).toEqual([]);
870
- });
871
- it("should use default configPath when not provided", async () => {
872
- const mockManifestContent = JSON.stringify({
873
- apps: mockManifest.apps,
874
- actions: {},
875
- });
876
- mockReadFile.mockResolvedValue(mockManifestContent);
877
- const sdk = createTestSdk();
878
- const context = sdk.getContext();
879
- await context.listActionEntries();
880
- expect(mockResolve).toHaveBeenCalledWith(".zapierrc");
881
- });
882
- });
883
- describe("addActionEntry", () => {
884
- it("should successfully add a new action", async () => {
885
- const mockManifestContent = JSON.stringify({
886
- apps: mockManifest.apps,
887
- });
888
- mockReadFile.mockResolvedValue(mockManifestContent);
889
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
890
- const context = sdk.getContext();
891
- const entry = {
892
- appKey: "slack",
893
- actionKey: "post_message",
894
- actionType: "write",
895
- authenticationId: 123,
896
- inputs: { channel: "#general" },
897
- schema: { type: "object" },
898
- createdAt: "2025-11-18T12:00:00.000Z",
899
- };
900
- const { name, entry: returnedEntry, manifest: updatedManifest, } = await context.addActionEntry({
901
- name: "weekly-report",
902
- entry,
903
- configPath: ".zapierrc",
904
- });
905
- expect(name).toBe("weekly-report");
906
- expect(returnedEntry).toEqual(entry);
907
- expect(updatedManifest.actions).toEqual({
908
- "weekly-report": entry,
909
- });
910
- });
911
- it("should throw error when action name already exists", async () => {
912
- const mockManifestContent = JSON.stringify({
913
- apps: mockManifest.apps,
914
- actions: {
915
- "weekly-report": {
916
- appKey: "slack",
917
- actionKey: "post_message",
918
- actionType: "write",
919
- authenticationId: 123,
920
- inputs: {},
921
- schema: {},
922
- createdAt: "2025-11-18T12:00:00.000Z",
923
- },
924
- },
925
- });
926
- mockReadFile.mockResolvedValue(mockManifestContent);
927
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
928
- const context = sdk.getContext();
929
- const entry = {
930
- appKey: "slack",
931
- actionKey: "post_message",
932
- actionType: "write",
933
- authenticationId: 123,
934
- inputs: {},
935
- schema: {},
936
- createdAt: "2025-11-18T13:00:00.000Z",
937
- };
938
- await expect(context.addActionEntry({
939
- name: "weekly-report",
940
- entry,
941
- configPath: ".zapierrc",
942
- })).rejects.toThrow('Action "weekly-report" already exists. Please choose a different name or remove the existing action first.');
943
- });
944
- it("should allow duplicate name with skipWrite option", async () => {
945
- const mockManifestContent = JSON.stringify({
946
- apps: mockManifest.apps,
947
- actions: {
948
- "weekly-report": {
949
- appKey: "slack",
950
- actionKey: "post_message",
951
- actionType: "write",
952
- authenticationId: 123,
953
- inputs: {},
954
- schema: {},
955
- createdAt: "2025-11-18T12:00:00.000Z",
956
- },
957
- },
958
- });
959
- mockReadFile.mockResolvedValue(mockManifestContent);
960
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
961
- const context = sdk.getContext();
962
- const entry = {
963
- appKey: "slack",
964
- actionKey: "post_message",
965
- actionType: "write",
966
- authenticationId: 456,
967
- inputs: {},
968
- schema: {},
969
- createdAt: "2025-11-18T13:00:00.000Z",
970
- };
971
- const { name, entry: returnedEntry } = await context.addActionEntry({
972
- name: "weekly-report",
973
- entry,
974
- configPath: ".zapierrc",
975
- skipWrite: true,
976
- });
977
- expect(name).toBe("weekly-report");
978
- expect(returnedEntry).toEqual(entry);
979
- });
980
- it("should create actions section if it doesn't exist", async () => {
981
- const mockManifestContent = JSON.stringify({
982
- apps: mockManifest.apps,
983
- });
984
- mockReadFile.mockResolvedValue(mockManifestContent);
985
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
986
- const context = sdk.getContext();
987
- const entry = {
988
- appKey: "slack",
989
- actionKey: "post_message",
990
- actionType: "write",
991
- authenticationId: 123,
992
- inputs: {},
993
- schema: {},
994
- createdAt: "2025-11-18T12:00:00.000Z",
995
- };
996
- const { manifest: updatedManifest } = await context.addActionEntry({
997
- name: "first-action",
998
- entry,
999
- configPath: ".zapierrc",
1000
- });
1001
- expect(updatedManifest.actions).toBeDefined();
1002
- expect(updatedManifest.actions).toEqual({
1003
- "first-action": entry,
1004
- });
1005
- });
1006
- });
1007
- describe("deleteActionEntry", () => {
1008
- it("should successfully delete an existing action", async () => {
1009
- const mockManifestContent = JSON.stringify({
1010
- apps: mockManifest.apps,
1011
- actions: {
1012
- "weekly-report": {
1013
- appKey: "slack",
1014
- actionKey: "post_message",
1015
- actionType: "write",
1016
- authenticationId: 123,
1017
- inputs: {},
1018
- schema: {},
1019
- createdAt: "2025-11-18T12:00:00.000Z",
1020
- },
1021
- "daily-summary": {
1022
- appKey: "slack",
1023
- actionKey: "post_message",
1024
- actionType: "write",
1025
- authenticationId: 123,
1026
- inputs: {},
1027
- schema: {},
1028
- createdAt: "2025-11-18T13:00:00.000Z",
1029
- },
1030
- },
1031
- });
1032
- mockReadFile.mockResolvedValue(mockManifestContent);
1033
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
1034
- const context = sdk.getContext();
1035
- const updatedManifest = await context.deleteActionEntry({
1036
- name: "weekly-report",
1037
- configPath: ".zapierrc",
1038
- });
1039
- expect(updatedManifest.actions).toEqual({
1040
- "daily-summary": {
1041
- appKey: "slack",
1042
- actionKey: "post_message",
1043
- actionType: "write",
1044
- authenticationId: 123,
1045
- inputs: {},
1046
- schema: {},
1047
- createdAt: "2025-11-18T13:00:00.000Z",
1048
- },
1049
- });
1050
- expect(updatedManifest.actions?.["weekly-report"]).toBeUndefined();
1051
- });
1052
- it("should throw error when action does not exist", async () => {
1053
- const mockManifestContent = JSON.stringify({
1054
- apps: mockManifest.apps,
1055
- actions: {},
1056
- });
1057
- mockReadFile.mockResolvedValue(mockManifestContent);
1058
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
1059
- const context = sdk.getContext();
1060
- await expect(context.deleteActionEntry({
1061
- name: "non-existent",
1062
- configPath: ".zapierrc",
1063
- })).rejects.toThrow('Action "non-existent" does not exist.');
1064
- });
1065
- it("should work with skipWrite option", async () => {
1066
- const mockManifestContent = JSON.stringify({
1067
- apps: mockManifest.apps,
1068
- actions: {
1069
- "weekly-report": {
1070
- appKey: "slack",
1071
- actionKey: "post_message",
1072
- actionType: "write",
1073
- authenticationId: 123,
1074
- inputs: {},
1075
- schema: {},
1076
- createdAt: "2025-11-18T12:00:00.000Z",
1077
- },
1078
- },
1079
- });
1080
- mockReadFile.mockResolvedValue(mockManifestContent);
1081
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
1082
- const context = sdk.getContext();
1083
- const updatedManifest = await context.deleteActionEntry({
1084
- name: "weekly-report",
1085
- configPath: ".zapierrc",
1086
- skipWrite: true,
1087
- });
1088
- expect(updatedManifest.actions).toEqual({});
1089
- });
1090
- it("should handle deleting the last action", async () => {
1091
- const mockManifestContent = JSON.stringify({
1092
- apps: mockManifest.apps,
1093
- actions: {
1094
- "only-action": {
1095
- appKey: "slack",
1096
- actionKey: "post_message",
1097
- actionType: "write",
1098
- authenticationId: 123,
1099
- inputs: {},
1100
- schema: {},
1101
- createdAt: "2025-11-18T12:00:00.000Z",
1102
- },
1103
- },
1104
- });
1105
- mockReadFile.mockResolvedValue(mockManifestContent);
1106
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
1107
- const context = sdk.getContext();
1108
- const updatedManifest = await context.deleteActionEntry({
1109
- name: "only-action",
1110
- configPath: ".zapierrc",
1111
- });
1112
- expect(updatedManifest.actions).toEqual({});
1113
- });
1114
- });
1115
- describe("action CRUD integration", () => {
1116
- it("should support full CRUD workflow", async () => {
1117
- const mockManifestContent = JSON.stringify({
1118
- apps: mockManifest.apps,
1119
- });
1120
- mockReadFile.mockResolvedValue(mockManifestContent);
1121
- const sdk = createTestSdk({ manifestPath: ".zapierrc" });
1122
- const context = sdk.getContext();
1123
- // Create first action
1124
- const entry1 = {
1125
- appKey: "slack",
1126
- actionKey: "post_message",
1127
- actionType: "write",
1128
- authenticationId: 123,
1129
- inputs: { channel: "#general" },
1130
- schema: {},
1131
- createdAt: "2025-11-18T12:00:00.000Z",
1132
- };
1133
- const { manifest: manifest1 } = await context.addActionEntry({
1134
- name: "action-1",
1135
- entry: entry1,
1136
- skipWrite: true,
1137
- });
1138
- // Create second action
1139
- const entry2 = {
1140
- appKey: "slack",
1141
- actionKey: "post_message",
1142
- actionType: "write",
1143
- authenticationId: 123,
1144
- inputs: { channel: "#daily" },
1145
- schema: {},
1146
- createdAt: "2025-11-18T13:00:00.000Z",
1147
- };
1148
- const { manifest: manifest2 } = await context.addActionEntry({
1149
- name: "action-2",
1150
- entry: entry2,
1151
- manifest: manifest1,
1152
- skipWrite: true,
1153
- });
1154
- // Verify both actions exist
1155
- expect(context.hasActionEntry({ name: "action-1", manifest: manifest2 })).toBe(true);
1156
- expect(context.hasActionEntry({ name: "action-2", manifest: manifest2 })).toBe(true);
1157
- // Find action
1158
- const foundAction = context.findActionEntry({
1159
- name: "action-1",
1160
- manifest: manifest2,
1161
- });
1162
- expect(foundAction).toEqual(entry1);
1163
- // List actions
1164
- mockReadFile.mockResolvedValue(JSON.stringify(manifest2));
1165
- const actions = await context.listActionEntries({
1166
- configPath: ".zapierrc",
1167
- });
1168
- expect(actions).toHaveLength(2);
1169
- // Delete action
1170
- const manifest3 = await context.deleteActionEntry({
1171
- name: "action-1",
1172
- skipWrite: true,
1173
- });
1174
- expect(context.hasActionEntry({ name: "action-1", manifest: manifest3 })).toBe(false);
1175
- expect(context.hasActionEntry({ name: "action-2", manifest: manifest3 })).toBe(true);
1176
- });
1177
- });
1178
- });
1179
- });