quickbase-js 1.0.1-beta.0 → 1.1.0-beta.1

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 (53) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/{quickbaseClient.d.ts → quickbase.d.ts} +344 -339
  3. package/dist/{quickbaseClient.js → quickbase.js} +350 -266
  4. package/dist/quickbase.js.map +1 -0
  5. package/examples/react.js +42 -16
  6. package/examples/vanillajs.html +32 -8
  7. package/log-gen.yaml +3513 -0
  8. package/package.json +13 -9
  9. package/rollup.config.js +2 -2
  10. package/src/code-generation/fix-spec-definitions.ts +473 -66
  11. package/src/code-generation/fix-spec-paths.ts +210 -18
  12. package/src/code-generation/generate-unified-interface.ts +1 -8
  13. package/src/code-generation/output/quickbase-fixed.json +783 -1138
  14. package/src/generated-unified/QuickbaseClient.ts +10 -9
  15. package/src/log-gen.ts +82 -35
  16. package/src/quickbaseClient.ts +39 -42
  17. package/test-artifacts/test-results.json +173 -1145
  18. package/tests/playwright/qb/auth/cacheSwitching.test.ts +4 -4
  19. package/tests/playwright/qb/auth/fetchTempToken401.test.ts +2 -2
  20. package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +2 -2
  21. package/tests/playwright/qb/auth/getTempToken.test.ts +2 -2
  22. package/tests/playwright/qb/auth/tokenPrefetch.test.ts +2 -2
  23. package/tests/playwright/qb/auth/tokenRenewal.test.ts +2 -2
  24. package/tests/setup.ts +37 -9
  25. package/tests/vitest/qb/apps/getApp.test.ts +37 -43
  26. package/tests/vitest/qb/fields/getFields.test.ts +3 -5
  27. package/tests/vitest/qb/records/deleteRecords.test.ts +78 -0
  28. package/tests/vitest/qb/records/runQuery.test.ts +82 -0
  29. package/tests/vitest/qb/records/upsert.test.ts +72 -0
  30. package/tests/vitest/qb/tables/createTable.test.ts +55 -0
  31. package/tests/vitest/qb/tables/deleteTable.test.ts +50 -0
  32. package/tests/vitest/qb/tables/getAppTables.test.ts +43 -64
  33. package/tests/vitest/qb/tables/getTable.test.ts +26 -0
  34. package/tests/vitest/qb/tables/updateTable.test.ts +48 -0
  35. package/tests/vitest/unit/apps/getApp.test.ts +17 -12
  36. package/tests/vitest/unit/auth/fetchTempToken401.test.ts +32 -6
  37. package/tests/vitest/unit/auth/getTempToken.test.ts +36 -24
  38. package/tests/vitest/unit/auth/retryOn401.test.ts +39 -5
  39. package/tests/vitest/unit/auth/two401s.test.ts +1 -1
  40. package/tests/vitest/unit/fields/getFields.test.ts +14 -7
  41. package/tests/vitest/unit/records/deleteRecords.test.ts +112 -0
  42. package/tests/vitest/unit/records/runQuery.test.ts +102 -0
  43. package/tests/vitest/unit/records/upsert.test.ts +218 -0
  44. package/tests/vitest/unit/tables/createTable.test.ts +70 -0
  45. package/tests/vitest/unit/tables/deleteTable.test.ts +77 -0
  46. package/tests/vitest/unit/tables/getAppTables.test.ts +41 -28
  47. package/tests/vitest/unit/tables/getTable.test.ts +233 -0
  48. package/tests/vitest/unit/tables/updateTable.test.ts +80 -0
  49. package/tsconfig.json +3 -2
  50. package/vitest.config.ts +8 -2
  51. package/dist/quickbaseClient.js.map +0 -1
  52. package/logGen.yaml +0 -1820
  53. package/src/utils.ts +0 -5
@@ -1,67 +1,46 @@
1
- import { describe, it, expect } from "vitest";
2
- import { createClient } from "../../../setup.ts";
1
+ import { test, expect } from "vitest";
2
+ import { quickbase } from "../../../../src/quickbaseClient";
3
3
 
4
- describe("QuickbaseClient Integration - getAppTables", () => {
5
- const client = createClient();
4
+ test(
5
+ "QuickbaseClient Integration - getAppTables > fetches real table data from QuickBase",
6
+ { timeout: 10000 },
7
+ async () => {
8
+ const config = {
9
+ realm: process.env.QB_REALM || "",
10
+ userToken: process.env.QB_USER_TOKEN || "",
11
+ debug: true,
12
+ };
13
+ const client = quickbase(config);
14
+ const tablesAppId = "buwai2zpe";
6
15
 
7
- it(
8
- "fetches real table data from QuickBase",
9
- async () => {
10
- const tablesAppId = process.env.QB_APP_ID;
11
- if (!tablesAppId) throw new Error("QB_APP_ID is not defined in .env");
12
- if (!process.env.QB_REALM)
13
- throw new Error("QB_REALM is not defined in .env");
14
- if (!process.env.QB_USER_TOKEN)
15
- throw new Error("QB_USER_TOKEN is not defined in .env");
16
+ console.log("Config used:", config);
17
+ const result = await client.getAppTables({ appId: tablesAppId });
18
+ console.log("Real API response:", result);
16
19
 
17
- console.log("Config used:", {
18
- realm: process.env.QB_REALM,
19
- userToken: process.env.QB_USER_TOKEN,
20
- appId: tablesAppId,
21
- });
22
- const result = await client.getAppTables({ appId: tablesAppId });
23
- console.log("Real API response:", result);
24
- expect(result).toEqual(
25
- expect.arrayContaining([
26
- expect.objectContaining({
27
- alias: "_DBID_ROOT",
28
- created: new Date("2025-02-13T18:22:33Z"),
29
- defaultSortFieldId: 2,
30
- defaultSortOrder: "DESC",
31
- description: "",
32
- id: "buwai2zr4",
33
- keyFieldId: 3,
34
- name: "Root",
35
- nextFieldId: 6,
36
- nextRecordId: 1,
37
- pluralRecordName: "Roots",
38
- singleRecordName: "Root",
39
- sizeLimit: "500 MB",
40
- spaceRemaining: "500 MB",
41
- spaceUsed: "0 KB",
42
- updated: new Date("2025-02-13T18:22:34Z"),
43
- }),
44
- expect.objectContaining({
45
- alias: "_DBID_ROLES",
46
- created: new Date("2025-02-13T18:22:33Z"),
47
- defaultSortFieldId: 2,
48
- defaultSortOrder: "DESC",
49
- description: "",
50
- id: "buwai2z3s",
51
- keyFieldId: 3,
52
- name: "Roles",
53
- nextFieldId: 6,
54
- nextRecordId: 1,
55
- pluralRecordName: "Roles",
56
- singleRecordName: "Role",
57
- sizeLimit: "500 MB",
58
- spaceRemaining: "500 MB",
59
- spaceUsed: "0 KB",
60
- updated: new Date("2025-02-13T18:22:34Z"),
61
- }),
62
- ])
63
- );
64
- },
65
- { timeout: 10000 }
66
- );
67
- });
20
+ expect(result).toBeDefined();
21
+ expect(result).toBeInstanceOf(Array);
22
+ expect(result.length).toBeGreaterThan(0);
23
+ expect(result).toEqual(
24
+ expect.arrayContaining([
25
+ expect.objectContaining({
26
+ id: "buwai2zr4",
27
+ alias: "_DBID_ROOT",
28
+ name: expect.any(String), // Allow dynamic name due to updateTable
29
+ description: expect.any(String), // Allow dynamic description
30
+ created: expect.any(Date),
31
+ updated: expect.any(Date), // Allow varying timestamps
32
+ nextRecordId: expect.any(Number),
33
+ nextFieldId: expect.any(Number),
34
+ defaultSortFieldId: expect.any(Number),
35
+ defaultSortOrder: "DESC",
36
+ keyFieldId: expect.any(Number),
37
+ singleRecordName: "Root",
38
+ pluralRecordName: "Roots",
39
+ sizeLimit: "500 MB",
40
+ spaceUsed: expect.any(String),
41
+ spaceRemaining: "500 MB",
42
+ }),
43
+ ])
44
+ );
45
+ }
46
+ );
@@ -0,0 +1,26 @@
1
+ import { test, expect } from "vitest";
2
+ import { quickbase } from "../../../../src/quickbaseClient";
3
+
4
+ test(
5
+ "QuickbaseClient Integration - getTable > fetches real table data from QuickBase",
6
+ { timeout: 10000 },
7
+ async () => {
8
+ const config = {
9
+ realm: process.env.QB_REALM || "",
10
+ userToken: process.env.QB_USER_TOKEN || "",
11
+ debug: true,
12
+ };
13
+ const client = quickbase(config);
14
+ const tableId = "buwai2zr4";
15
+ const appId = "buwai2zpe";
16
+
17
+ console.log("Config used:", config);
18
+ const response = await client.getTable({ tableId, appId });
19
+ expect(response).toBeDefined();
20
+ expect(response.id).toBe(tableId);
21
+ expect(response.name).toBe("Root"); // Expect original name
22
+ expect(response.alias).toBe("_DBID_ROOT");
23
+ expect(response.created).toBeInstanceOf(Date);
24
+ console.log("Real API response:", response);
25
+ }
26
+ );
@@ -0,0 +1,48 @@
1
+ import { test, expect } from "vitest";
2
+ import { quickbase } from "../../../../src/quickbaseClient";
3
+
4
+ test(
5
+ "QuickbaseClient Integration - updateTable > updates real table data in QuickBase",
6
+ { timeout: 10000 },
7
+ async () => {
8
+ const config = {
9
+ realm: process.env.QB_REALM || "",
10
+ userToken: process.env.QB_USER_TOKEN || "",
11
+ debug: true,
12
+ };
13
+ const client = quickbase(config);
14
+ const appId = "buwai2zpe";
15
+ const tableId = "buwai2zr4";
16
+ const updatedBody = {
17
+ name: "Root Updated " + Date.now(),
18
+ description: "Integration test update",
19
+ };
20
+ const originalBody = {
21
+ name: "Root",
22
+ description: "",
23
+ };
24
+
25
+ console.log("Config used:", config);
26
+ // Update the table
27
+ const response = await client.updateTable({
28
+ tableId,
29
+ appId,
30
+ body: updatedBody,
31
+ });
32
+ expect(response).toBeDefined();
33
+ expect(response.id).toBe(tableId);
34
+ expect(response.name).toBe(updatedBody.name);
35
+ expect(response.description).toBe(updatedBody.description);
36
+ console.log("Real API response:", response);
37
+
38
+ // Cleanup: Revert the table to original state
39
+ const cleanupResponse = await client.updateTable({
40
+ tableId,
41
+ appId,
42
+ body: originalBody,
43
+ });
44
+ expect(cleanupResponse.name).toBe(originalBody.name);
45
+ expect(cleanupResponse.description).toBe(originalBody.description);
46
+ console.log("Cleanup API response:", cleanupResponse);
47
+ }
48
+ );
@@ -1,11 +1,19 @@
1
+ // tests/vitest/unit/apps/getApp.test.ts
1
2
  import { describe, it, expect, beforeEach } from "vitest";
2
- import { createClient, mockFetch } from "@/tests/setup.ts";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_APP_ID,
7
+ QB_REALM,
8
+ QB_USER_TOKEN,
9
+ } from "@tests/setup.ts";
3
10
 
4
- describe("QuickbaseClient - getApp (Unit)", () => {
5
- const client = createClient(mockFetch);
11
+ describe("QuickbaseClient Unit - getApp", () => {
12
+ let client: ReturnType<typeof createClient>;
6
13
 
7
14
  beforeEach(() => {
8
15
  mockFetch.mockClear();
16
+ client = createClient(mockFetch, { debug: true }); // Add debug: true for consistency
9
17
  });
10
18
 
11
19
  it("initializes without errors", () => {
@@ -24,7 +32,7 @@ describe("QuickbaseClient - getApp (Unit)", () => {
24
32
  status: 200,
25
33
  json: () =>
26
34
  Promise.resolve({
27
- id: process.env.QB_APP_ID,
35
+ id: QB_APP_ID,
28
36
  name: "qb-copy",
29
37
  created: "2025-02-13T18:22:33Z",
30
38
  updated: "2025-03-04T04:25:51Z",
@@ -45,12 +53,9 @@ describe("QuickbaseClient - getApp (Unit)", () => {
45
53
  } as Response);
46
54
  });
47
55
 
48
- const getAppId = process.env.QB_APP_ID;
49
- if (!getAppId) throw new Error("QB_APP_ID is not defined in .env");
50
- console.log("Test appId:", getAppId);
51
- const result = await client.getApp({ appId: getAppId });
56
+ const result = await client.getApp({ appId: QB_APP_ID });
52
57
  expect(result).toEqual({
53
- id: getAppId,
58
+ id: QB_APP_ID,
54
59
  name: "qb-copy",
55
60
  created: new Date("2025-02-13T18:22:33Z"),
56
61
  updated: new Date("2025-03-04T04:25:51Z"),
@@ -69,11 +74,11 @@ describe("QuickbaseClient - getApp (Unit)", () => {
69
74
  },
70
75
  });
71
76
  expect(mockFetch).toHaveBeenCalledWith(
72
- `https://api.quickbase.com/v1/apps/${getAppId}`,
77
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
73
78
  expect.objectContaining({
74
79
  headers: expect.objectContaining({
75
- Authorization: `QB-USER-TOKEN ${process.env.QB_USER_TOKEN}`,
76
- "QB-Realm-Hostname": `${process.env.QB_REALM}.quickbase.com`,
80
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
81
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
77
82
  }),
78
83
  })
79
84
  );
@@ -1,7 +1,13 @@
1
+ // tests/vitest/unit/auth/fetchTempToken401.test.ts
1
2
  import { describe, it, expect, beforeEach, vi } from "vitest";
2
- import { createClient, mockFetch } from "@/tests/setup.ts";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_TABLE_ID_1,
8
+ } from "@tests/setup.ts";
3
9
 
4
- describe("QuickbaseClient - 401 with fetchTempToken 401", () => {
10
+ describe("QuickbaseClient Unit - 401 with fetchTempToken 401", () => {
5
11
  let client: ReturnType<typeof createClient>;
6
12
 
7
13
  beforeEach(() => {
@@ -10,7 +16,6 @@ describe("QuickbaseClient - 401 with fetchTempToken 401", () => {
10
16
  });
11
17
 
12
18
  it("fails without infinite loop when fetchTempToken returns 401 after initial 401", async () => {
13
- const mockDbid = "mockDbid123";
14
19
  let callCount = 0;
15
20
 
16
21
  mockFetch.mockImplementation((url) => {
@@ -28,25 +33,46 @@ describe("QuickbaseClient - 401 with fetchTempToken 401", () => {
28
33
  return Promise.resolve({
29
34
  ok: false,
30
35
  status: 401,
31
- text: () => Promise.resolve("Unauthorized"),
36
+ json: () => Promise.resolve({ message: "Unauthorized" }),
32
37
  });
33
38
  }
34
39
  if (url.includes("auth/temporary") && callCount === 3) {
35
40
  return Promise.resolve({
36
41
  ok: false,
37
42
  status: 401,
38
- text: () => Promise.resolve("Unauthorized in fetchTempToken"),
43
+ json: () =>
44
+ Promise.resolve({ message: "Unauthorized in fetchTempToken" }),
39
45
  });
40
46
  }
41
47
  return Promise.reject(new Error(`Unexpected fetch call: ${url}`));
42
48
  });
43
49
 
44
50
  const consoleSpy = vi.spyOn(console, "log");
45
- await expect(client.getFields({ tableId: mockDbid })).rejects.toThrow(
51
+ await expect(client.getFields({ tableId: QB_TABLE_ID_1 })).rejects.toThrow(
46
52
  "API Error: Unauthorized in fetchTempToken (Status: 401)"
47
53
  );
48
54
 
49
55
  expect(mockFetch).toHaveBeenCalledTimes(3);
56
+ expect(mockFetch).toHaveBeenNthCalledWith(
57
+ 1,
58
+ `https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
59
+ expect.any(Object)
60
+ );
61
+ expect(mockFetch).toHaveBeenNthCalledWith(
62
+ 2,
63
+ `https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
64
+ expect.objectContaining({
65
+ headers: expect.objectContaining({
66
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
67
+ }),
68
+ })
69
+ );
70
+ expect(mockFetch).toHaveBeenNthCalledWith(
71
+ 3,
72
+ `https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
73
+ expect.any(Object)
74
+ );
75
+
50
76
  expect(consoleSpy).toHaveBeenCalledWith(
51
77
  "Authorization error for getFields, refreshing token:",
52
78
  expect.any(String)
@@ -1,12 +1,18 @@
1
+ // tests/vitest/unit/auth/getTempTokenDBID.test.ts
1
2
  import { describe, it, expect, beforeEach } from "vitest";
2
- import { createClient, mockFetch } from "@/tests/setup.ts";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_TABLE_ID_1,
8
+ } from "@tests/setup.ts";
3
9
 
4
- describe("QuickbaseClient - getTempTokenDBID (Unit)", () => {
10
+ describe("QuickbaseClient Unit - getTempTokenDBID", () => {
5
11
  let client: ReturnType<typeof createClient>;
6
12
 
7
13
  beforeEach(() => {
8
14
  mockFetch.mockClear();
9
- client = createClient(mockFetch, { useTempTokens: true, debug: false }); // Keep debug off for unit test clarity
15
+ client = createClient(mockFetch, { useTempTokens: true, debug: false });
10
16
  });
11
17
 
12
18
  it("initializes without errors", () => {
@@ -18,23 +24,22 @@ describe("QuickbaseClient - getTempTokenDBID (Unit)", () => {
18
24
  });
19
25
 
20
26
  it("fetches and caches temp token on first call", async () => {
21
- const mockDbid = "mockDbid123";
22
27
  const mockToken = "b123xyz_temp_token";
23
28
 
24
29
  mockFetch.mockResolvedValueOnce({
25
30
  ok: true,
26
31
  status: 200,
27
32
  json: () => Promise.resolve({ temporaryAuthorization: mockToken }),
28
- } as Response);
33
+ });
29
34
 
30
- const result = await client.getTempTokenDBID({ dbid: mockDbid });
35
+ const result = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
31
36
  expect(result).toEqual({ temporaryAuthorization: mockToken });
32
37
  expect(mockFetch).toHaveBeenCalledWith(
33
- `https://api.quickbase.com/v1/auth/temporary/${mockDbid}`,
38
+ `https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
34
39
  expect.objectContaining({
35
40
  method: "GET",
36
41
  headers: expect.objectContaining({
37
- "QB-Realm-Hostname": `${process.env.QB_REALM}.quickbase.com`,
42
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
38
43
  "Content-Type": "application/json",
39
44
  }),
40
45
  credentials: "include",
@@ -43,41 +48,48 @@ describe("QuickbaseClient - getTempTokenDBID (Unit)", () => {
43
48
  });
44
49
 
45
50
  it("reuses cached temp token on second call", async () => {
46
- const mockDbid = "mockDbid123";
47
51
  const mockToken = "b123xyz_temp_token";
48
52
 
49
53
  mockFetch.mockResolvedValueOnce({
50
54
  ok: true,
51
55
  status: 200,
52
56
  json: () => Promise.resolve({ temporaryAuthorization: mockToken }),
53
- } as Response);
57
+ });
54
58
 
55
- const firstResult = await client.getTempTokenDBID({ dbid: mockDbid });
59
+ const firstResult = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
56
60
  expect(firstResult).toEqual({ temporaryAuthorization: mockToken });
57
61
  expect(mockFetch).toHaveBeenCalledTimes(1);
58
62
 
59
63
  mockFetch.mockClear();
60
- const secondResult = await client.getTempTokenDBID({ dbid: mockDbid });
64
+ const secondResult = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
61
65
  expect(secondResult).toEqual({ temporaryAuthorization: mockToken });
62
66
  expect(mockFetch).not.toHaveBeenCalled();
63
67
  });
64
68
 
65
69
  it("handles API error", async () => {
66
- const mockDbid = "mockDbid123";
67
- const errorResponse = {
70
+ mockFetch.mockResolvedValueOnce({
68
71
  ok: false,
69
72
  status: 401,
70
- text: () => Promise.resolve("Unauthorized"),
71
- } as Response;
72
-
73
- mockFetch.mockResolvedValueOnce(errorResponse);
73
+ json: () => Promise.resolve({ message: "Unauthorized" }),
74
+ });
74
75
 
75
- await expect(client.getTempTokenDBID({ dbid: mockDbid })).rejects.toSatisfy(
76
- (error: Error) => {
77
- expect(error).toBeInstanceOf(Error);
78
- expect(error.message).toBe("API Error: Unauthorized (Status: 401)");
79
- return true;
80
- }
76
+ await expect(
77
+ client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 })
78
+ ).rejects.toSatisfy((error: Error) => {
79
+ expect(error).toBeInstanceOf(Error);
80
+ expect(error.message).toBe("API Error: Unauthorized (Status: 401)");
81
+ return true;
82
+ });
83
+ expect(mockFetch).toHaveBeenCalledWith(
84
+ `https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
85
+ expect.objectContaining({
86
+ method: "GET",
87
+ headers: expect.objectContaining({
88
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
89
+ "Content-Type": "application/json",
90
+ }),
91
+ credentials: "include",
92
+ })
81
93
  );
82
94
  });
83
95
  });
@@ -1,7 +1,13 @@
1
+ // tests/vitest/unit/auth/retryOn401.test.ts
1
2
  import { describe, it, expect, beforeEach, vi } from "vitest";
2
- import { createClient, mockFetch } from "@/tests/setup.ts";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_TABLE_ID_1,
8
+ } from "@tests/setup.ts";
3
9
 
4
- describe("QuickbaseClient - 401 Retry Creates New Token", () => {
10
+ describe("QuickbaseClient Unit - 401 Retry Creates New Token", () => {
5
11
  let client: ReturnType<typeof createClient>;
6
12
 
7
13
  beforeEach(() => {
@@ -10,7 +16,6 @@ describe("QuickbaseClient - 401 Retry Creates New Token", () => {
10
16
  });
11
17
 
12
18
  it("creates a new token on 401 and retries successfully", async () => {
13
- const mockDbid = "mockDbid123";
14
19
  const mockToken = "new_token_456";
15
20
  const mockFields = [{ id: 1, label: "Field1" }];
16
21
  let callCount = 0;
@@ -51,16 +56,45 @@ describe("QuickbaseClient - 401 Retry Creates New Token", () => {
51
56
  });
52
57
 
53
58
  const consoleSpy = vi.spyOn(console, "log");
54
- const result = await client.getFields({ tableId: mockDbid });
59
+ const result = await client.getFields({ tableId: QB_TABLE_ID_1 });
55
60
 
56
61
  expect(result).toEqual(mockFields);
57
62
  expect(mockFetch).toHaveBeenCalledTimes(4);
63
+ expect(mockFetch).toHaveBeenNthCalledWith(
64
+ 1,
65
+ `https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
66
+ expect.any(Object)
67
+ );
68
+ expect(mockFetch).toHaveBeenNthCalledWith(
69
+ 2,
70
+ `https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
71
+ expect.objectContaining({
72
+ headers: expect.objectContaining({
73
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
74
+ }),
75
+ })
76
+ );
77
+ expect(mockFetch).toHaveBeenNthCalledWith(
78
+ 3,
79
+ `https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
80
+ expect.any(Object)
81
+ );
82
+ expect(mockFetch).toHaveBeenNthCalledWith(
83
+ 4,
84
+ `https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
85
+ expect.objectContaining({
86
+ headers: expect.objectContaining({
87
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
88
+ }),
89
+ })
90
+ );
91
+
58
92
  expect(consoleSpy).toHaveBeenCalledWith(
59
93
  "Authorization error for getFields, refreshing token:",
60
94
  expect.any(String)
61
95
  );
62
96
  expect(consoleSpy).toHaveBeenCalledWith(
63
- "Fetched and cached new token for dbid: mockDbid123",
97
+ `Fetched and cached new token for dbid: ${QB_TABLE_ID_1}`,
64
98
  mockToken + "_retry",
65
99
  expect.any(String)
66
100
  );
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, beforeEach, vi } from "vitest";
2
- import { createClient, mockFetch } from "@/tests/setup.ts";
2
+ import { createClient, mockFetch } from "@tests/setup.ts";
3
3
 
4
4
  describe("QuickbaseClient - Two 401s in a Row", () => {
5
5
  let client: ReturnType<typeof createClient>;
@@ -1,7 +1,14 @@
1
+ // tests/vitest/unit/fields/getFields.test.ts
1
2
  import { describe, it, expect } from "vitest";
2
- import { createClient, mockFetch } from "@/tests/setup.ts";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_USER_TOKEN,
8
+ QB_TABLE_ID_1,
9
+ } from "@tests/setup.ts";
3
10
 
4
- describe("QuickbaseClient - getFields (Unit)", () => {
11
+ describe("QuickbaseClient Unit - getFields", () => {
5
12
  it("calls getFields successfully", async () => {
6
13
  mockFetch.mockImplementation((url: string, options: any) => {
7
14
  console.log("Mock fetch for getFields:", url, options);
@@ -47,9 +54,9 @@ describe("QuickbaseClient - getFields (Unit)", () => {
47
54
  } as Response);
48
55
  });
49
56
 
50
- const client = createClient(mockFetch);
57
+ const client = createClient(mockFetch, { debug: true }); // Add debug: true for consistency
51
58
  const result = await client.getFields({
52
- tableId: "dummyTableId",
59
+ tableId: QB_TABLE_ID_1,
53
60
  includeFieldPerms: true,
54
61
  });
55
62
  console.log("getFields response:", result);
@@ -103,11 +110,11 @@ describe("QuickbaseClient - getFields (Unit)", () => {
103
110
  ])
104
111
  );
105
112
  expect(mockFetch).toHaveBeenCalledWith(
106
- `https://api.quickbase.com/v1/fields?tableId=dummyTableId&includeFieldPerms=true`,
113
+ `https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}&includeFieldPerms=true`,
107
114
  expect.objectContaining({
108
115
  headers: expect.objectContaining({
109
- Authorization: `QB-USER-TOKEN ${process.env.QB_USER_TOKEN}`,
110
- "QB-Realm-Hostname": `${process.env.QB_REALM}.quickbase.com`,
116
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
117
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
111
118
  }),
112
119
  })
113
120
  );
@@ -0,0 +1,112 @@
1
+ // tests/vitest/unit/records/deleteRecords.test.ts
2
+ import { describe, expect, test, beforeEach } from "vitest";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_USER_TOKEN,
8
+ QB_TABLE_ID_1,
9
+ } from "@tests/setup.ts";
10
+
11
+ describe("QuickbaseClient Unit - deleteRecords", () => {
12
+ let qb: ReturnType<typeof createClient>;
13
+
14
+ beforeEach(() => {
15
+ mockFetch.mockClear();
16
+ qb = createClient(mockFetch, { debug: true }); // Use debug: true for consistency with other tests
17
+ });
18
+
19
+ test("sends correct DELETE request and handles success", async () => {
20
+ mockFetch.mockResolvedValueOnce({
21
+ ok: true,
22
+ status: 200,
23
+ json: () => Promise.resolve({ numberDeleted: 1 }),
24
+ });
25
+
26
+ const result = await qb.deleteRecords({
27
+ body: {
28
+ from: QB_TABLE_ID_1,
29
+ where: "{3.EX.'5'}", // Simulate deleting Record ID 5
30
+ },
31
+ });
32
+
33
+ expect(mockFetch).toHaveBeenCalledWith(
34
+ "https://api.quickbase.com/v1/records",
35
+ expect.objectContaining({
36
+ method: "DELETE",
37
+ headers: expect.objectContaining({
38
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
39
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
40
+ "Content-Type": "application/json",
41
+ }),
42
+ body: JSON.stringify({ from: QB_TABLE_ID_1, where: "{3.EX.'5'}" }),
43
+ })
44
+ );
45
+ expect(result).toEqual({ numberDeleted: 1 });
46
+ });
47
+
48
+ test("handles no records deleted", async () => {
49
+ mockFetch.mockResolvedValueOnce({
50
+ ok: true,
51
+ status: 200,
52
+ json: () => Promise.resolve({ numberDeleted: 0 }),
53
+ });
54
+
55
+ const result = await qb.deleteRecords({
56
+ body: {
57
+ from: QB_TABLE_ID_1,
58
+ where: "{3.EX.'999999'}", // Non-existent Record ID
59
+ },
60
+ });
61
+
62
+ expect(mockFetch).toHaveBeenCalledWith(
63
+ "https://api.quickbase.com/v1/records",
64
+ expect.objectContaining({
65
+ method: "DELETE",
66
+ headers: expect.objectContaining({
67
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
68
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
69
+ "Content-Type": "application/json",
70
+ }),
71
+ body: JSON.stringify({
72
+ from: QB_TABLE_ID_1,
73
+ where: "{3.EX.'999999'}",
74
+ }),
75
+ })
76
+ );
77
+ expect(result).toEqual({ numberDeleted: 0 });
78
+ });
79
+
80
+ test("throws error on API failure", async () => {
81
+ mockFetch.mockResolvedValueOnce({
82
+ ok: false,
83
+ status: 400,
84
+ json: () => Promise.resolve({ message: "Invalid query" }),
85
+ });
86
+
87
+ await expect(() =>
88
+ qb.deleteRecords({
89
+ body: {
90
+ from: QB_TABLE_ID_1,
91
+ where: "{3.EX.'invalid'}",
92
+ },
93
+ })
94
+ ).rejects.toThrow("API Error: Invalid query (Status: 400)");
95
+
96
+ expect(mockFetch).toHaveBeenCalledWith(
97
+ "https://api.quickbase.com/v1/records",
98
+ expect.objectContaining({
99
+ method: "DELETE",
100
+ headers: expect.objectContaining({
101
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
102
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
103
+ "Content-Type": "application/json",
104
+ }),
105
+ body: JSON.stringify({
106
+ from: QB_TABLE_ID_1,
107
+ where: "{3.EX.'invalid'}",
108
+ }),
109
+ })
110
+ );
111
+ });
112
+ });