quickbase-js 0.1.0-beta.10

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 (165) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/LICENSE +21 -0
  3. package/README.md +179 -0
  4. package/api-examples.md +49 -0
  5. package/build-common.js +131 -0
  6. package/build-esm.js +71 -0
  7. package/build-umd.js +69 -0
  8. package/dist/esm/quickbase.d.ts +8081 -0
  9. package/dist/esm/quickbase.js +7928 -0
  10. package/dist/esm/quickbase.js.map +1 -0
  11. package/dist/esm/quickbase.min.js +2 -0
  12. package/dist/esm/quickbase.min.js.map +1 -0
  13. package/dist/umd/quickbase.umd.js +8504 -0
  14. package/dist/umd/quickbase.umd.js.map +1 -0
  15. package/dist/umd/quickbase.umd.min.js +2 -0
  16. package/dist/umd/quickbase.umd.min.js.map +1 -0
  17. package/docs/.vitepress/cache/deps/@theme_index.js +275 -0
  18. package/docs/.vitepress/cache/deps/@theme_index.js.map +7 -0
  19. package/docs/.vitepress/cache/deps/_metadata.json +40 -0
  20. package/docs/.vitepress/cache/deps/chunk-5A67QEFC.js +12542 -0
  21. package/docs/.vitepress/cache/deps/chunk-5A67QEFC.js.map +7 -0
  22. package/docs/.vitepress/cache/deps/chunk-7D4P4MMP.js +9719 -0
  23. package/docs/.vitepress/cache/deps/chunk-7D4P4MMP.js.map +7 -0
  24. package/docs/.vitepress/cache/deps/package.json +3 -0
  25. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4504 -0
  26. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  27. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +583 -0
  28. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  29. package/docs/.vitepress/cache/deps/vue.js +343 -0
  30. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  31. package/docs/.vitepress/config.mts +26 -0
  32. package/docs/api-examples.md +49 -0
  33. package/docs/index.md +59 -0
  34. package/docs/markdown-examples.md +85 -0
  35. package/docs-data/api-docs.json +4143 -0
  36. package/examples/quicbase-jsCDN.html +36 -0
  37. package/examples/react.js +48 -0
  38. package/examples/vanillajs.html +47 -0
  39. package/index.md +25 -0
  40. package/markdown-examples.md +85 -0
  41. package/open-api/fix-spec-main.ts +133 -0
  42. package/open-api/generate-unified-interface.ts +104 -0
  43. package/open-api/generateDocsJson.ts +24 -0
  44. package/open-api/output/quickbase-fixed.json +11191 -0
  45. package/open-api/post-process-generated.ts +226 -0
  46. package/open-api/regenerate-client.ts +117 -0
  47. package/open-api/schema/enhance-general.ts +111 -0
  48. package/open-api/schema/fix-arrays.ts +68 -0
  49. package/open-api/schema/tags/apps.ts +90 -0
  50. package/open-api/schema/tags/fields.ts +471 -0
  51. package/open-api/schema/tags/groups.ts +51 -0
  52. package/open-api/schema/tags/index.ts +66 -0
  53. package/open-api/schema/tags/records.ts +115 -0
  54. package/open-api/schema/tags/tables.ts +211 -0
  55. package/open-api/schema/tags/users.ts +47 -0
  56. package/open-api/types/spec.ts +42 -0
  57. package/open-api/utils/common.ts +29 -0
  58. package/open-api/utils/generateJsDoc.ts +74 -0
  59. package/open-api/utils/infer-schema.ts +40 -0
  60. package/open-api/utils/naming.ts +10 -0
  61. package/open-api/utils/sharedUtils.ts +352 -0
  62. package/package.json +96 -0
  63. package/playwright.config.ts +37 -0
  64. package/rollup.config.js +114 -0
  65. package/specs/QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json +46878 -0
  66. package/specs/split/apps.json +1315 -0
  67. package/specs/split/auth.json +191 -0
  68. package/specs/split/fields.json +3869 -0
  69. package/specs/split/records.json +754 -0
  70. package/specs/split/reports.json +619 -0
  71. package/specs/split/tables.json +1605 -0
  72. package/src/RateLimitError.ts +12 -0
  73. package/src/ThrottleBucket.ts +41 -0
  74. package/src/authorizationStrategy.ts +283 -0
  75. package/src/generated-unified/QuickbaseClient.ts +1135 -0
  76. package/src/generated-unified/missing-types-report.json +3 -0
  77. package/src/index.ts +1 -0
  78. package/src/invokeMethod.ts +245 -0
  79. package/src/quickbaseClient.ts +194 -0
  80. package/src/rateLimiter.ts +25 -0
  81. package/src/tokenCache.ts +35 -0
  82. package/src/utils.ts +50 -0
  83. package/tests/playwright/qb/auth/cacheSwitching.test.ts +159 -0
  84. package/tests/playwright/qb/auth/fetchTempToken401.test.ts +219 -0
  85. package/tests/playwright/qb/auth/getAppWithTempToken.test.ts +162 -0
  86. package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +153 -0
  87. package/tests/playwright/qb/auth/getTempToken.test.ts +152 -0
  88. package/tests/playwright/qb/auth/tempTokenPrefetch.test.ts +179 -0
  89. package/tests/playwright/qb/auth/tempTokenRenewal.test.ts +133 -0
  90. package/tests/setup.ts +53 -0
  91. package/tests/vitest/qb/apps/copyApp.test.ts +76 -0
  92. package/tests/vitest/qb/apps/createApp.test.ts +64 -0
  93. package/tests/vitest/qb/apps/deleteApp.test.ts +76 -0
  94. package/tests/vitest/qb/apps/getApp.test.ts +44 -0
  95. package/tests/vitest/qb/apps/getAppEvents.test.ts +67 -0
  96. package/tests/vitest/qb/apps/updateApp.test.ts +58 -0
  97. package/tests/vitest/qb/auth/mockQuickbaseServer.ts +60 -0
  98. package/tests/vitest/qb/auth/retryUserToken401.test.ts +73 -0
  99. package/tests/vitest/qb/auth/ssoTokenIntegrationWithServer.test.ts +65 -0
  100. package/tests/vitest/qb/fields/createField.test.ts +77 -0
  101. package/tests/vitest/qb/fields/deleteFields.test.ts +83 -0
  102. package/tests/vitest/qb/fields/getField.test.ts +85 -0
  103. package/tests/vitest/qb/fields/getFieldUsage.test.ts +82 -0
  104. package/tests/vitest/qb/fields/getFields.test.ts +232 -0
  105. package/tests/vitest/qb/fields/getFieldsUsage.test.ts +77 -0
  106. package/tests/vitest/qb/fields/updateField.test.ts +93 -0
  107. package/tests/vitest/qb/formulas/runFormula.test.ts +96 -0
  108. package/tests/vitest/qb/index.ts +29 -0
  109. package/tests/vitest/qb/records/deleteRecords.test.ts +110 -0
  110. package/tests/vitest/qb/records/runQuery.test.ts +146 -0
  111. package/tests/vitest/qb/records/upsert.test.ts +102 -0
  112. package/tests/vitest/qb/tables/createRelationship.test.ts +102 -0
  113. package/tests/vitest/qb/tables/createTable.test.ts +55 -0
  114. package/tests/vitest/qb/tables/deleteRelationship.test.ts +143 -0
  115. package/tests/vitest/qb/tables/deleteTable.test.ts +50 -0
  116. package/tests/vitest/qb/tables/getAppTables.test.ts +46 -0
  117. package/tests/vitest/qb/tables/getRelationships.test.ts +80 -0
  118. package/tests/vitest/qb/tables/getTable.test.ts +26 -0
  119. package/tests/vitest/qb/tables/updateRelationship.test.ts +250 -0
  120. package/tests/vitest/qb/tables/updateTable.test.ts +48 -0
  121. package/tests/vitest/unit/ThrottleBucket.test.ts +95 -0
  122. package/tests/vitest/unit/apps/copyApp.test.ts +581 -0
  123. package/tests/vitest/unit/apps/createApp.test.ts +186 -0
  124. package/tests/vitest/unit/apps/deleteApp.test.ts +216 -0
  125. package/tests/vitest/unit/apps/getApp.test.ts +128 -0
  126. package/tests/vitest/unit/apps/getAppEvents.test.ts +193 -0
  127. package/tests/vitest/unit/apps/updateApp.test.ts +412 -0
  128. package/tests/vitest/unit/auth/fetchTempToken401.test.ts +63 -0
  129. package/tests/vitest/unit/auth/getTempToken.test.ts +119 -0
  130. package/tests/vitest/unit/auth/retryTempToken401.test.ts +95 -0
  131. package/tests/vitest/unit/auth/retryTempTokenMaxRetries.test.ts +206 -0
  132. package/tests/vitest/unit/auth/retryUserToken401.test.ts +69 -0
  133. package/tests/vitest/unit/auth/ssoTokenRefresh.test.ts +325 -0
  134. package/tests/vitest/unit/fields/createField.test.ts +451 -0
  135. package/tests/vitest/unit/fields/deleteFields.test.ts +141 -0
  136. package/tests/vitest/unit/fields/getField.test.ts +189 -0
  137. package/tests/vitest/unit/fields/getFieldUsage.test.ts +180 -0
  138. package/tests/vitest/unit/fields/getFields.test.ts +122 -0
  139. package/tests/vitest/unit/fields/getFieldsUsage.test.ts +160 -0
  140. package/tests/vitest/unit/fields/updateField.test.ts +285 -0
  141. package/tests/vitest/unit/formulas/runFormula.test.ts +205 -0
  142. package/tests/vitest/unit/index.ts +34 -0
  143. package/tests/vitest/unit/quickbaseClientHttpMethods.test.ts +139 -0
  144. package/tests/vitest/unit/rateLimit.test.ts +191 -0
  145. package/tests/vitest/unit/records/deleteRecords.test.ts +112 -0
  146. package/tests/vitest/unit/records/runQuery.test.ts +102 -0
  147. package/tests/vitest/unit/records/upsert.test.ts +234 -0
  148. package/tests/vitest/unit/tables/createRelationship.test.ts +212 -0
  149. package/tests/vitest/unit/tables/createTable.test.ts +70 -0
  150. package/tests/vitest/unit/tables/deleteRelationship.test.ts +97 -0
  151. package/tests/vitest/unit/tables/deleteTable.test.ts +77 -0
  152. package/tests/vitest/unit/tables/getAppTables.test.ts +85 -0
  153. package/tests/vitest/unit/tables/getRelationships.test.ts +259 -0
  154. package/tests/vitest/unit/tables/getTable.test.ts +233 -0
  155. package/tests/vitest/unit/tables/updateRelationship.test.ts +307 -0
  156. package/tests/vitest/unit/tables/updateTable.test.ts +80 -0
  157. package/tests/vitest/unit/tokenLifespan.test.ts +204 -0
  158. package/tests/vitest/unit/umd.test.ts +8 -0
  159. package/tools/log-gen.ts +251 -0
  160. package/tools/log-gen.yaml +4777 -0
  161. package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
  162. package/tsconfig.build.json +17 -0
  163. package/tsconfig.json +32 -0
  164. package/versionrc.json +9 -0
  165. package/vitest.config.ts +27 -0
@@ -0,0 +1,186 @@
1
+ // tests/vitest/unit/apps/createApp.test.ts
2
+
3
+ import { describe, it, expect, beforeEach } from "vitest";
4
+ import {
5
+ createClient,
6
+ mockFetch,
7
+ QB_REALM,
8
+ QB_USER_TOKEN,
9
+ } from "@tests/setup.ts";
10
+ import { CreateAppRequest, CreateApp200Response } from "@/generated/models";
11
+
12
+ describe("QuickbaseClient Unit - createApp", () => {
13
+ console.log(
14
+ "Running updated createApp.test.ts - Version with full request expectation"
15
+ );
16
+
17
+ let client: ReturnType<typeof createClient>;
18
+
19
+ beforeEach(() => {
20
+ mockFetch.mockClear();
21
+ });
22
+
23
+ it("initializes without errors", () => {
24
+ client = createClient(mockFetch, { debug: true });
25
+ expect(client).toBeDefined();
26
+ });
27
+
28
+ it("has createApp method", () => {
29
+ client = createClient(mockFetch, { debug: true });
30
+ expect(typeof client.createApp).toBe("function");
31
+ });
32
+
33
+ it("calls createApp successfully with user token and dates as Date objects", async () => {
34
+ client = createClient(mockFetch, { debug: true });
35
+
36
+ const request: CreateAppRequest = {
37
+ name: "New App",
38
+ description: "A new app for testing",
39
+ assignToken: true,
40
+ };
41
+
42
+ const mockResponse: CreateApp200Response = {
43
+ id: "new-app-id",
44
+ name: "New App",
45
+ description: "A new app for testing",
46
+ created: new Date("2025-03-11T10:00:00Z"),
47
+ updated: new Date("2025-03-11T10:00:00Z"),
48
+ dateFormat: "MM-DD-YYYY",
49
+ timeZone: "(UTC-08:00) Pacific Time (US & Canada)",
50
+ hasEveryoneOnTheInternet: false,
51
+ variables: [],
52
+ dataClassification: "None",
53
+ memoryInfo: { estMemory: 0, estMemoryInclDependentApps: 0 },
54
+ securityProperties: {
55
+ allowClone: false,
56
+ allowExport: true,
57
+ enableAppTokens: true,
58
+ hideFromPublic: false,
59
+ mustBeRealmApproved: false,
60
+ useIPFilter: false,
61
+ },
62
+ };
63
+
64
+ mockFetch.mockResolvedValueOnce({
65
+ ok: true,
66
+ status: 200,
67
+ json: () => Promise.resolve(mockResponse),
68
+ });
69
+
70
+ const result = await client.createApp({ body: request });
71
+
72
+ expect(result).toEqual(mockResponse);
73
+ const callArgs = mockFetch.mock.calls[0];
74
+ const body = JSON.parse(callArgs[1].body as string);
75
+ console.log("Date objects test - Expected request:", request);
76
+ console.log("Date objects test - Actual body:", body);
77
+ expect(mockFetch).toHaveBeenCalledWith(
78
+ `https://api.quickbase.com/v1/apps`,
79
+ expect.objectContaining({
80
+ method: "POST",
81
+ headers: expect.objectContaining({
82
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
83
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
84
+ "Content-Type": "application/json",
85
+ }),
86
+ body: expect.any(String), // Ensure it’s a string, we’ll check content below
87
+ credentials: "omit",
88
+ })
89
+ );
90
+ expect(body).toEqual(request); // Moved outside toHaveBeenCalledWith
91
+ });
92
+
93
+ it("calls createApp successfully with user token and dates as strings", async () => {
94
+ client = createClient(mockFetch, { debug: true, convertDates: false });
95
+
96
+ const request: CreateAppRequest = {
97
+ name: "New App",
98
+ description: "A new app for testing",
99
+ assignToken: true,
100
+ };
101
+
102
+ const mockResponse = {
103
+ id: "new-app-id",
104
+ name: "New App",
105
+ description: "A new app for testing",
106
+ created: "2025-03-11T10:00:00Z",
107
+ updated: "2025-03-11T10:00:00Z",
108
+ dateFormat: "MM-DD-YYYY",
109
+ timeZone: "(UTC-08:00) Pacific Time (US & Canada)",
110
+ hasEveryoneOnTheInternet: false,
111
+ variables: [],
112
+ dataClassification: "None",
113
+ memoryInfo: { estMemory: 0, estMemoryInclDependentApps: 0 },
114
+ securityProperties: {
115
+ allowClone: false,
116
+ allowExport: true,
117
+ enableAppTokens: true,
118
+ hideFromPublic: false,
119
+ mustBeRealmApproved: false,
120
+ useIPFilter: false,
121
+ },
122
+ };
123
+
124
+ mockFetch.mockResolvedValueOnce({
125
+ ok: true,
126
+ status: 200,
127
+ json: () => Promise.resolve(mockResponse),
128
+ });
129
+
130
+ const result = await client.createApp({ body: request });
131
+
132
+ expect(result).toEqual(mockResponse);
133
+ const callArgs = mockFetch.mock.calls[0];
134
+ const body = JSON.parse(callArgs[1].body as string);
135
+ console.log("Strings test - Expected request:", request);
136
+ console.log("Strings test - Actual body:", body);
137
+ expect(mockFetch).toHaveBeenCalledWith(
138
+ `https://api.quickbase.com/v1/apps`,
139
+ expect.objectContaining({
140
+ method: "POST",
141
+ headers: expect.objectContaining({
142
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
143
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
144
+ "Content-Type": "application/json",
145
+ }),
146
+ body: expect.any(String), // Ensure it’s a string, we’ll check content below
147
+ credentials: "omit",
148
+ })
149
+ );
150
+ expect(body).toEqual(request); // Moved outside toHaveBeenCalledWith
151
+ });
152
+
153
+ it("handles 400 error for invalid request", async () => {
154
+ client = createClient(mockFetch, { debug: true });
155
+
156
+ const request: CreateAppRequest = {
157
+ name: "",
158
+ };
159
+
160
+ mockFetch.mockImplementationOnce(() =>
161
+ Promise.resolve({
162
+ ok: false,
163
+ status: 400,
164
+ json: () => Promise.resolve({ message: "Name is required" }),
165
+ })
166
+ );
167
+
168
+ await expect(client.createApp({ body: request })).rejects.toThrow(
169
+ "API Error: Name is required (Status: 400)"
170
+ );
171
+
172
+ expect(mockFetch).toHaveBeenCalledWith(
173
+ `https://api.quickbase.com/v1/apps`,
174
+ expect.objectContaining({
175
+ method: "POST",
176
+ headers: expect.objectContaining({
177
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
178
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
179
+ "Content-Type": "application/json",
180
+ }),
181
+ body: JSON.stringify(request),
182
+ credentials: "omit",
183
+ })
184
+ );
185
+ });
186
+ });
@@ -0,0 +1,216 @@
1
+ // tests/vitest/unit/apps/deleteApp.test.ts
2
+
3
+ import { describe, it, expect, beforeEach } from "vitest";
4
+ import {
5
+ createClient,
6
+ mockFetch,
7
+ QB_APP_ID,
8
+ QB_REALM,
9
+ QB_USER_TOKEN,
10
+ } from "@tests/setup.ts";
11
+ import { DeleteAppRequest, DeleteApp200Response } from "@/generated/models";
12
+
13
+ describe("QuickbaseClient Unit - deleteApp", () => {
14
+ let client: ReturnType<typeof createClient>;
15
+
16
+ beforeEach(() => {
17
+ mockFetch.mockClear();
18
+ });
19
+
20
+ it("initializes without errors", () => {
21
+ client = createClient(mockFetch, { debug: true });
22
+ expect(client).toBeDefined();
23
+ });
24
+
25
+ it("has deleteApp method", () => {
26
+ client = createClient(mockFetch, { debug: true });
27
+ expect(typeof client.deleteApp).toBe("function");
28
+ });
29
+
30
+ it("calls deleteApp successfully with user token", async () => {
31
+ client = createClient(mockFetch, { debug: true });
32
+
33
+ const request: DeleteAppRequest = {
34
+ name: "App to Delete",
35
+ };
36
+
37
+ const mockResponse: DeleteApp200Response = {
38
+ deletedAppId: QB_APP_ID,
39
+ };
40
+
41
+ mockFetch.mockResolvedValueOnce({
42
+ ok: true,
43
+ status: 200,
44
+ json: () => Promise.resolve(mockResponse),
45
+ });
46
+
47
+ const result = await client.deleteApp({ appId: QB_APP_ID, body: request });
48
+
49
+ expect(result).toEqual(mockResponse);
50
+ expect(mockFetch).toHaveBeenCalledWith(
51
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
52
+ expect.objectContaining({
53
+ method: "DELETE",
54
+ headers: expect.objectContaining({
55
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
56
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
57
+ "Content-Type": "application/json",
58
+ }),
59
+ body: JSON.stringify(request),
60
+ })
61
+ );
62
+ });
63
+
64
+ it("calls deleteApp successfully with temp token", async () => {
65
+ client = createClient(mockFetch, { useTempTokens: true, debug: true });
66
+
67
+ const request: DeleteAppRequest = {
68
+ name: "App to Delete with Temp Token",
69
+ };
70
+
71
+ const mockResponse: DeleteApp200Response = {
72
+ deletedAppId: QB_APP_ID,
73
+ };
74
+
75
+ mockFetch
76
+ .mockResolvedValueOnce({
77
+ ok: true,
78
+ status: 200,
79
+ json: () => Promise.resolve({ temporaryAuthorization: "temp_token" }),
80
+ })
81
+ .mockResolvedValueOnce({
82
+ ok: true,
83
+ status: 200,
84
+ json: () => Promise.resolve(mockResponse),
85
+ });
86
+
87
+ const result = await client.deleteApp({ appId: QB_APP_ID, body: request });
88
+
89
+ expect(result).toEqual(mockResponse);
90
+ expect(mockFetch).toHaveBeenCalledTimes(2);
91
+ expect(mockFetch).toHaveBeenNthCalledWith(
92
+ 1,
93
+ `https://api.quickbase.com/v1/auth/temporary/${QB_APP_ID}`,
94
+ expect.objectContaining({
95
+ method: "GET",
96
+ headers: expect.objectContaining({
97
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
98
+ "Content-Type": "application/json",
99
+ }),
100
+ credentials: "include",
101
+ })
102
+ );
103
+ expect(mockFetch).toHaveBeenNthCalledWith(
104
+ 2,
105
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
106
+ expect.objectContaining({
107
+ method: "DELETE",
108
+ headers: expect.objectContaining({
109
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
110
+ Authorization: "QB-TEMP-TOKEN temp_token",
111
+ "Content-Type": "application/json",
112
+ }),
113
+ body: JSON.stringify(request),
114
+ })
115
+ );
116
+ });
117
+
118
+ it("retries successfully after 401 with temp token", async () => {
119
+ client = createClient(mockFetch, { useTempTokens: true, debug: true });
120
+
121
+ const request: DeleteAppRequest = {
122
+ name: "Retry Delete App",
123
+ };
124
+
125
+ const mockResponse: DeleteApp200Response = {
126
+ deletedAppId: QB_APP_ID,
127
+ };
128
+
129
+ mockFetch
130
+ .mockResolvedValueOnce({
131
+ ok: true,
132
+ status: 200,
133
+ json: () =>
134
+ Promise.resolve({ temporaryAuthorization: "initial_token" }),
135
+ })
136
+ .mockResolvedValueOnce({
137
+ ok: false,
138
+ status: 401,
139
+ json: () => Promise.resolve({ message: "Unauthorized" }),
140
+ })
141
+ .mockResolvedValueOnce({
142
+ ok: true,
143
+ status: 200,
144
+ json: () => Promise.resolve({ temporaryAuthorization: "new_token" }),
145
+ })
146
+ .mockResolvedValueOnce({
147
+ ok: true,
148
+ status: 200,
149
+ json: () => Promise.resolve(mockResponse),
150
+ });
151
+
152
+ const result = await client.deleteApp({ appId: QB_APP_ID, body: request });
153
+
154
+ expect(result).toEqual(mockResponse);
155
+ expect(mockFetch).toHaveBeenCalledTimes(4);
156
+ expect(mockFetch).toHaveBeenNthCalledWith(
157
+ 1,
158
+ `https://api.quickbase.com/v1/auth/temporary/${QB_APP_ID}`,
159
+ expect.any(Object)
160
+ );
161
+ expect(mockFetch).toHaveBeenNthCalledWith(
162
+ 2,
163
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
164
+ expect.objectContaining({
165
+ headers: expect.objectContaining({
166
+ Authorization: "QB-TEMP-TOKEN initial_token",
167
+ }),
168
+ })
169
+ );
170
+ expect(mockFetch).toHaveBeenNthCalledWith(
171
+ 3,
172
+ `https://api.quickbase.com/v1/auth/temporary/${QB_APP_ID}`,
173
+ expect.any(Object)
174
+ );
175
+ expect(mockFetch).toHaveBeenNthCalledWith(
176
+ 4,
177
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
178
+ expect.objectContaining({
179
+ headers: expect.objectContaining({
180
+ Authorization: "QB-TEMP-TOKEN new_token",
181
+ }),
182
+ })
183
+ );
184
+ });
185
+
186
+ it("handles 400 error for invalid request", async () => {
187
+ client = createClient(mockFetch, { debug: true });
188
+
189
+ const request: DeleteAppRequest = {
190
+ name: "Wrong Name",
191
+ };
192
+
193
+ mockFetch.mockResolvedValueOnce({
194
+ ok: false,
195
+ status: 400,
196
+ json: () => Promise.resolve({ message: "Invalid application name" }),
197
+ });
198
+
199
+ await expect(
200
+ client.deleteApp({ appId: QB_APP_ID, body: request })
201
+ ).rejects.toThrow("API Error: Invalid application name (Status: 400)");
202
+
203
+ expect(mockFetch).toHaveBeenCalledWith(
204
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
205
+ expect.objectContaining({
206
+ method: "DELETE",
207
+ headers: expect.objectContaining({
208
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
209
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
210
+ "Content-Type": "application/json",
211
+ }),
212
+ body: JSON.stringify(request),
213
+ })
214
+ );
215
+ });
216
+ });
@@ -0,0 +1,128 @@
1
+ // tests/vitest/unit/apps/getApp.test.ts
2
+
3
+ import { describe, it, expect, beforeEach } from "vitest";
4
+ import {
5
+ createClient,
6
+ mockFetch,
7
+ QB_APP_ID,
8
+ QB_REALM,
9
+ QB_USER_TOKEN,
10
+ } from "@tests/setup.ts";
11
+
12
+ describe("QuickbaseClient Unit - getApp", () => {
13
+ let client: ReturnType<typeof createClient>;
14
+
15
+ beforeEach(() => {
16
+ mockFetch.mockClear();
17
+ });
18
+
19
+ it("initializes without errors", () => {
20
+ client = createClient(mockFetch, { debug: true });
21
+ expect(client).toBeDefined();
22
+ });
23
+
24
+ it("has getApp method", () => {
25
+ client = createClient(mockFetch, { debug: true });
26
+ expect(typeof client.getApp).toBe("function");
27
+ });
28
+
29
+ it("calls getApp successfully with dates as Date objects", async () => {
30
+ client = createClient(mockFetch, { debug: true });
31
+
32
+ const mockResponse = {
33
+ id: QB_APP_ID,
34
+ name: "qb-copy",
35
+ created: new Date("2025-02-13T18:22:33Z"), // Expect Date object
36
+ updated: new Date("2025-03-04T04:25:51Z"), // Expect Date object
37
+ description: "",
38
+ timeZone: "(UTC-08:00) Pacific Time (US & Canada)",
39
+ dateFormat: "MM-DD-YYYY",
40
+ hasEveryoneOnTheInternet: false,
41
+ memoryInfo: { estMemory: 0, estMemoryInclDependentApps: 0 },
42
+ securityProperties: {
43
+ allowClone: false,
44
+ allowExport: true,
45
+ enableAppTokens: true,
46
+ hideFromPublic: false,
47
+ mustBeRealmApproved: false,
48
+ useIPFilter: false,
49
+ },
50
+ ancestorId: undefined,
51
+ variables: undefined,
52
+ dataClassification: undefined,
53
+ };
54
+
55
+ mockFetch.mockResolvedValueOnce({
56
+ ok: true,
57
+ status: 200,
58
+ json: () => Promise.resolve(mockResponse),
59
+ });
60
+
61
+ const result = await client.getApp({ appId: QB_APP_ID });
62
+
63
+ expect(result).toEqual(mockResponse);
64
+ expect(mockFetch).toHaveBeenCalledWith(
65
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
66
+ expect.objectContaining({
67
+ method: "GET",
68
+ headers: expect.objectContaining({
69
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
70
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
71
+ "Content-Type": "application/json",
72
+ }),
73
+ body: undefined,
74
+ credentials: "omit",
75
+ })
76
+ );
77
+ });
78
+
79
+ it("calls getApp successfully with dates as strings", async () => {
80
+ client = createClient(mockFetch, { debug: true, convertDates: false });
81
+
82
+ const mockResponse = {
83
+ id: QB_APP_ID,
84
+ name: "qb-copy",
85
+ created: "2025-02-13T18:22:33Z", // Expect string
86
+ updated: "2025-03-04T04:25:51Z", // Expect string
87
+ description: "",
88
+ timeZone: "(UTC-08:00) Pacific Time (US & Canada)",
89
+ dateFormat: "MM-DD-YYYY",
90
+ hasEveryoneOnTheInternet: false,
91
+ memoryInfo: { estMemory: 0, estMemoryInclDependentApps: 0 },
92
+ securityProperties: {
93
+ allowClone: false,
94
+ allowExport: true,
95
+ enableAppTokens: true,
96
+ hideFromPublic: false,
97
+ mustBeRealmApproved: false,
98
+ useIPFilter: false,
99
+ },
100
+ ancestorId: undefined,
101
+ variables: undefined,
102
+ dataClassification: undefined,
103
+ };
104
+
105
+ mockFetch.mockResolvedValueOnce({
106
+ ok: true,
107
+ status: 200,
108
+ json: () => Promise.resolve(mockResponse),
109
+ });
110
+
111
+ const result = await client.getApp({ appId: QB_APP_ID });
112
+
113
+ expect(result).toEqual(mockResponse);
114
+ expect(mockFetch).toHaveBeenCalledWith(
115
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
116
+ expect.objectContaining({
117
+ method: "GET",
118
+ headers: expect.objectContaining({
119
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
120
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
121
+ "Content-Type": "application/json",
122
+ }),
123
+ body: undefined,
124
+ credentials: "omit",
125
+ })
126
+ );
127
+ });
128
+ });