@aigne/doc-smith 0.8.11-beta → 0.8.11-beta.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 (257) hide show
  1. package/.aigne/doc-smith/config.yaml +2 -0
  2. package/.aigne/doc-smith/output/structure-plan.json +3 -3
  3. package/.aigne/doc-smith/upload-cache.yaml +252 -0
  4. package/.github/workflows/publish-docs.yml +67 -0
  5. package/.release-please-manifest.json +1 -1
  6. package/CHANGELOG.md +22 -0
  7. package/README.md +45 -115
  8. package/agents/clear/choose-contents.mjs +170 -0
  9. package/agents/clear/clear-auth-tokens.mjs +111 -0
  10. package/agents/clear/clear-document-config.mjs +39 -0
  11. package/agents/clear/clear-document-structure.mjs +106 -0
  12. package/agents/clear/clear-generated-docs.mjs +51 -0
  13. package/agents/clear/index.yaml +23 -0
  14. package/agents/evaluate/code-snippet.mjs +93 -0
  15. package/agents/evaluate/document-structure.yaml +70 -0
  16. package/agents/evaluate/document.yaml +79 -0
  17. package/agents/evaluate/generate-report.mjs +78 -0
  18. package/agents/evaluate/index.yaml +39 -0
  19. package/agents/generate/document-structure-tools/add-document.mjs +56 -0
  20. package/agents/generate/document-structure-tools/delete-document.mjs +49 -0
  21. package/agents/generate/document-structure-tools/move-document.mjs +82 -0
  22. package/agents/generate/document-structure-tools/update-document.mjs +50 -0
  23. package/agents/generate/generate-structure.yaml +1 -1
  24. package/agents/generate/update-document-structure.yaml +42 -0
  25. package/agents/generate/user-review-document-structure.mjs +6 -4
  26. package/agents/init/index.mjs +1 -1
  27. package/agents/publish/publish-docs.mjs +12 -3
  28. package/agents/translate/choose-language.mjs +1 -1
  29. package/agents/update/batch-update-document.yaml +7 -0
  30. package/agents/update/check-update-is-single.mjs +38 -0
  31. package/agents/update/document-tools/update-document-content.mjs +293 -0
  32. package/agents/update/index.yaml +4 -10
  33. package/agents/update/update-document-detail.yaml +52 -0
  34. package/agents/update/update-single-document.yaml +15 -0
  35. package/agents/update/user-review-document.mjs +248 -0
  36. package/agents/utils/choose-docs.mjs +4 -2
  37. package/agents/utils/format-document-structure.mjs +12 -2
  38. package/agents/utils/load-document-all-content.mjs +84 -0
  39. package/agents/utils/load-sources.mjs +4 -1
  40. package/aigne.yaml +59 -20
  41. package/assets/report-template/report.html +198 -0
  42. package/biome.json +14 -2
  43. package/docs/advanced-how-it-works.ja.md +101 -0
  44. package/docs/advanced-how-it-works.zh-TW.md +101 -0
  45. package/docs/advanced-how-it-works.zh.md +20 -20
  46. package/docs/advanced-quality-assurance.ja.md +96 -0
  47. package/docs/advanced-quality-assurance.zh-TW.md +96 -0
  48. package/docs/advanced-quality-assurance.zh.md +18 -18
  49. package/docs/advanced.ja.md +16 -0
  50. package/docs/advanced.zh-TW.md +16 -0
  51. package/docs/advanced.zh.md +4 -4
  52. package/docs/changelog.ja.md +309 -0
  53. package/docs/changelog.zh-TW.md +309 -0
  54. package/docs/changelog.zh.md +23 -23
  55. package/docs/cli-reference.ja.md +210 -0
  56. package/docs/cli-reference.zh-TW.md +210 -0
  57. package/docs/cli-reference.zh.md +21 -21
  58. package/docs/configuration-interactive-setup.ja.md +135 -0
  59. package/docs/configuration-interactive-setup.zh-TW.md +135 -0
  60. package/docs/configuration-interactive-setup.zh.md +29 -29
  61. package/docs/configuration-language-support.ja.md +94 -0
  62. package/docs/configuration-language-support.zh-TW.md +94 -0
  63. package/docs/configuration-language-support.zh.md +13 -13
  64. package/docs/configuration-llm-setup.ja.md +54 -0
  65. package/docs/configuration-llm-setup.zh-TW.md +54 -0
  66. package/docs/configuration-llm-setup.zh.md +12 -12
  67. package/docs/configuration-preferences.ja.md +129 -0
  68. package/docs/configuration-preferences.zh-TW.md +129 -0
  69. package/docs/configuration-preferences.zh.md +36 -36
  70. package/docs/configuration.ja.md +172 -0
  71. package/docs/configuration.zh-TW.md +172 -0
  72. package/docs/configuration.zh.md +49 -49
  73. package/docs/features-generate-documentation.ja.md +101 -0
  74. package/docs/features-generate-documentation.zh-TW.md +101 -0
  75. package/docs/features-generate-documentation.zh.md +17 -17
  76. package/docs/features-publish-your-docs.ja.md +107 -0
  77. package/docs/features-publish-your-docs.zh-TW.md +107 -0
  78. package/docs/features-publish-your-docs.zh.md +22 -22
  79. package/docs/features-translate-documentation.ja.md +79 -0
  80. package/docs/features-translate-documentation.zh-TW.md +79 -0
  81. package/docs/features-translate-documentation.zh.md +12 -12
  82. package/docs/features-update-and-refine.ja.md +138 -0
  83. package/docs/features-update-and-refine.zh-TW.md +138 -0
  84. package/docs/features-update-and-refine.zh.md +21 -21
  85. package/docs/features.ja.md +52 -0
  86. package/docs/features.zh-TW.md +52 -0
  87. package/docs/features.zh.md +8 -8
  88. package/docs/getting-started.ja.md +123 -0
  89. package/docs/getting-started.zh-TW.md +123 -0
  90. package/docs/getting-started.zh.md +24 -24
  91. package/docs/overview.ja.md +30 -0
  92. package/docs/overview.zh-TW.md +30 -0
  93. package/docs/overview.zh.md +8 -8
  94. package/package.json +19 -11
  95. package/prompts/common/document/content-rules-core.md +19 -0
  96. package/prompts/common/document/media-handling-rules.md +9 -0
  97. package/prompts/common/document/role-and-personality.md +15 -0
  98. package/prompts/common/document/user-preferences.md +9 -0
  99. package/prompts/common/document-structure/conflict-resolution-guidance.md +16 -0
  100. package/prompts/common/document-structure/document-structure-rules.md +45 -0
  101. package/prompts/common/document-structure/glossary.md +7 -0
  102. package/prompts/common/document-structure/intj-traits.md +5 -0
  103. package/prompts/common/document-structure/output-constraints.md +9 -0
  104. package/prompts/common/document-structure/user-locale-rules.md +10 -0
  105. package/prompts/common/document-structure/user-preferences.md +9 -0
  106. package/prompts/detail/custom/custom-components.md +9 -1
  107. package/prompts/detail/document-rules.md +6 -6
  108. package/prompts/detail/generate-document.md +5 -45
  109. package/prompts/detail/update-document.md +145 -0
  110. package/prompts/evaluate/document-structure.md +94 -0
  111. package/prompts/evaluate/document.md +149 -0
  112. package/prompts/structure/document-rules.md +1 -1
  113. package/prompts/structure/generate-structure-system.md +74 -0
  114. package/prompts/structure/generate-structure-user.md +41 -0
  115. package/prompts/structure/update-document-structure.md +118 -0
  116. package/prompts/translate/translate-document.md +1 -1
  117. package/prompts/utils/feedback-refiner.md +3 -3
  118. package/release-please-config.json +1 -7
  119. package/tests/agents/clear/choose-contents.test.mjs +280 -0
  120. package/tests/agents/clear/clear-auth-tokens.test.mjs +268 -0
  121. package/tests/agents/clear/clear-document-config.test.mjs +167 -0
  122. package/tests/agents/clear/clear-document-structure.test.mjs +374 -0
  123. package/tests/agents/clear/clear-generated-docs.test.mjs +222 -0
  124. package/tests/agents/evaluate/code-snippet.test.mjs +163 -0
  125. package/tests/agents/evaluate/fixtures/api-services.md +87 -0
  126. package/tests/agents/evaluate/fixtures/js-sdk.md +94 -0
  127. package/tests/agents/evaluate/generate-report.test.mjs +312 -0
  128. package/tests/agents/generate/check-document-structure.test.mjs +0 -6
  129. package/tests/agents/generate/document-structure-tools/add-document.test.mjs +449 -0
  130. package/tests/agents/generate/document-structure-tools/delete-document.test.mjs +410 -0
  131. package/tests/agents/generate/document-structure-tools/move-document.test.mjs +476 -0
  132. package/tests/agents/generate/document-structure-tools/update-document.test.mjs +548 -0
  133. package/tests/agents/generate/generate-structure.test.mjs +0 -6
  134. package/tests/agents/generate/user-review-document-structure.test.mjs +9 -9
  135. package/tests/agents/publish/publish-docs.test.mjs +2 -2
  136. package/tests/agents/update/check-update-is-single.test.mjs +300 -0
  137. package/tests/agents/update/document-tools/update-document-content.test.mjs +326 -0
  138. package/tests/agents/update/user-review-document.test.mjs +561 -0
  139. package/tests/agents/utils/format-document-structure.test.mjs +100 -0
  140. package/tests/utils/auth-utils.test.mjs +239 -1
  141. package/tests/utils/blocklet.test.mjs +9 -7
  142. package/tests/utils/constants.test.mjs +1 -1
  143. package/tests/utils/d2-utils.test.mjs +1 -1
  144. package/tests/utils/deploy.test.mjs +310 -366
  145. package/tests/utils/kroki-utils.test.mjs +2 -15
  146. package/tests/utils/linter/fixtures/css/keyword-error.css +1 -0
  147. package/tests/utils/linter/fixtures/css/missing-semicolon.css +1 -0
  148. package/tests/utils/linter/fixtures/css/syntax-error.css +1 -0
  149. package/tests/utils/linter/fixtures/css/undeclare-variable.css +1 -0
  150. package/tests/utils/linter/fixtures/css/unused-variable.css +2 -0
  151. package/tests/utils/linter/fixtures/css/valid-code.css +1 -0
  152. package/tests/utils/linter/fixtures/dockerfile/keyword-error.dockerfile +1 -0
  153. package/tests/utils/linter/fixtures/dockerfile/missing-semicolon.dockerfile +2 -0
  154. package/tests/utils/linter/fixtures/dockerfile/syntax-error.dockerfile +2 -0
  155. package/tests/utils/linter/fixtures/dockerfile/undeclare-variable.dockerfile +1 -0
  156. package/tests/utils/linter/fixtures/dockerfile/unused-variable.dockerfile +1 -0
  157. package/tests/utils/linter/fixtures/dockerfile/valid-code.dockerfile +2 -0
  158. package/tests/utils/linter/fixtures/go/keyword-error.go +5 -0
  159. package/tests/utils/linter/fixtures/go/missing-semicolon.go +5 -0
  160. package/tests/utils/linter/fixtures/go/syntax-error.go +6 -0
  161. package/tests/utils/linter/fixtures/go/undeclare-variable.go +5 -0
  162. package/tests/utils/linter/fixtures/go/unused-variable.go +5 -0
  163. package/tests/utils/linter/fixtures/go/valid-code.go +7 -0
  164. package/tests/utils/linter/fixtures/js/keyword-error.js +3 -0
  165. package/tests/utils/linter/fixtures/js/missing-semicolon.js +6 -0
  166. package/tests/utils/linter/fixtures/js/syntax-error.js +4 -0
  167. package/tests/utils/linter/fixtures/js/undeclare-variable.js +3 -0
  168. package/tests/utils/linter/fixtures/js/unused-variable.js +7 -0
  169. package/tests/utils/linter/fixtures/js/valid-code.js +15 -0
  170. package/tests/utils/linter/fixtures/json/keyword-error.json +1 -0
  171. package/tests/utils/linter/fixtures/json/missing-semicolon.json +1 -0
  172. package/tests/utils/linter/fixtures/json/syntax-error.json +1 -0
  173. package/tests/utils/linter/fixtures/json/undeclare-variable.json +1 -0
  174. package/tests/utils/linter/fixtures/json/unused-variable.json +1 -0
  175. package/tests/utils/linter/fixtures/json/valid-code.json +1 -0
  176. package/tests/utils/linter/fixtures/jsx/keyword-error.jsx +5 -0
  177. package/tests/utils/linter/fixtures/jsx/missing-semicolon.jsx +5 -0
  178. package/tests/utils/linter/fixtures/jsx/syntax-error.jsx +5 -0
  179. package/tests/utils/linter/fixtures/jsx/undeclare-variable.jsx +5 -0
  180. package/tests/utils/linter/fixtures/jsx/unused-variable.jsx +4 -0
  181. package/tests/utils/linter/fixtures/jsx/valid-code.jsx +5 -0
  182. package/tests/utils/linter/fixtures/python/keyword-error.py +3 -0
  183. package/tests/utils/linter/fixtures/python/missing-semicolon.py +2 -0
  184. package/tests/utils/linter/fixtures/python/syntax-error.py +3 -0
  185. package/tests/utils/linter/fixtures/python/undeclare-variable.py +3 -0
  186. package/tests/utils/linter/fixtures/python/unused-variable.py +6 -0
  187. package/tests/utils/linter/fixtures/python/valid-code.py +12 -0
  188. package/tests/utils/linter/fixtures/ruby/keyword-error.rb +2 -0
  189. package/tests/utils/linter/fixtures/ruby/missing-semicolon.rb +1 -0
  190. package/tests/utils/linter/fixtures/ruby/syntax-error.rb +2 -0
  191. package/tests/utils/linter/fixtures/ruby/undeclare-variable.rb +1 -0
  192. package/tests/utils/linter/fixtures/ruby/unused-variable.rb +2 -0
  193. package/tests/utils/linter/fixtures/ruby/valid-code.rb +1 -0
  194. package/tests/utils/linter/fixtures/sass/keyword-error.sass +2 -0
  195. package/tests/utils/linter/fixtures/sass/missing-semicolon.sass +3 -0
  196. package/tests/utils/linter/fixtures/sass/syntax-error.sass +3 -0
  197. package/tests/utils/linter/fixtures/sass/undeclare-variable.sass +2 -0
  198. package/tests/utils/linter/fixtures/sass/unused-variable.sass +4 -0
  199. package/tests/utils/linter/fixtures/sass/valid-code.sass +2 -0
  200. package/tests/utils/linter/fixtures/scss/keyword-error.scss +1 -0
  201. package/tests/utils/linter/fixtures/scss/missing-semicolon.scss +1 -0
  202. package/tests/utils/linter/fixtures/scss/syntax-error.scss +1 -0
  203. package/tests/utils/linter/fixtures/scss/undeclare-variable.scss +1 -0
  204. package/tests/utils/linter/fixtures/scss/unused-variable.scss +2 -0
  205. package/tests/utils/linter/fixtures/scss/valid-code.scss +1 -0
  206. package/tests/utils/linter/fixtures/shell/keyword-error.sh +5 -0
  207. package/tests/utils/linter/fixtures/shell/missing-semicolon.sh +3 -0
  208. package/tests/utils/linter/fixtures/shell/syntax-error.sh +4 -0
  209. package/tests/utils/linter/fixtures/shell/undeclare-variable.sh +3 -0
  210. package/tests/utils/linter/fixtures/shell/unused-variable.sh +4 -0
  211. package/tests/utils/linter/fixtures/shell/valid-code.sh +3 -0
  212. package/tests/utils/linter/fixtures/ts/keyword-error.ts +1 -0
  213. package/tests/utils/linter/fixtures/ts/missing-semicolon.ts +1 -0
  214. package/tests/utils/linter/fixtures/ts/syntax-error.ts +1 -0
  215. package/tests/utils/linter/fixtures/ts/undeclare-variable.ts +1 -0
  216. package/tests/utils/linter/fixtures/ts/unused-variable.ts +3 -0
  217. package/tests/utils/linter/fixtures/ts/valid-code.ts +3 -0
  218. package/tests/utils/linter/fixtures/tsx/keyword-error.tsx +5 -0
  219. package/tests/utils/linter/fixtures/tsx/missing-semicolon.tsx +5 -0
  220. package/tests/utils/linter/fixtures/tsx/syntax-error.tsx +5 -0
  221. package/tests/utils/linter/fixtures/tsx/undeclare-variable.tsx +6 -0
  222. package/tests/utils/linter/fixtures/tsx/unused-variable.tsx +6 -0
  223. package/tests/utils/linter/fixtures/tsx/valid-code.tsx +5 -0
  224. package/tests/utils/linter/fixtures/vue/keyword-error.vue +6 -0
  225. package/tests/utils/linter/fixtures/vue/missing-semicolon.vue +6 -0
  226. package/tests/utils/linter/fixtures/vue/syntax-error.vue +6 -0
  227. package/tests/utils/linter/fixtures/vue/undeclare-variable.vue +6 -0
  228. package/tests/utils/linter/fixtures/vue/unused-variable.vue +7 -0
  229. package/tests/utils/linter/fixtures/vue/valid-code.vue +6 -0
  230. package/tests/utils/linter/fixtures/yaml/keyword-error.yml +1 -0
  231. package/tests/utils/linter/fixtures/yaml/missing-semicolon.yml +2 -0
  232. package/tests/utils/linter/fixtures/yaml/syntax-error.yml +1 -0
  233. package/tests/utils/linter/fixtures/yaml/undeclare-variable.yml +1 -0
  234. package/tests/utils/linter/fixtures/yaml/unused-variable.yml +2 -0
  235. package/tests/utils/linter/fixtures/yaml/valid-code.yml +3 -0
  236. package/tests/utils/linter/index.test.mjs +440 -0
  237. package/tests/utils/linter/scan-results.mjs +42 -0
  238. package/tests/utils/markdown/index.test.mjs +478 -0
  239. package/tests/utils/mermaid-validator.test.mjs +2 -2
  240. package/tests/utils/utils.test.mjs +3 -1
  241. package/types/document-schema.mjs +54 -0
  242. package/types/document-structure-schema.mjs +244 -0
  243. package/utils/auth-utils.mjs +131 -6
  244. package/utils/conflict-detector.mjs +5 -1
  245. package/utils/{constants.mjs → constants/index.mjs} +109 -0
  246. package/utils/constants/linter.mjs +102 -0
  247. package/utils/d2-utils.mjs +2 -4
  248. package/utils/debug.mjs +3 -0
  249. package/utils/deploy.mjs +81 -385
  250. package/utils/evaluate/report-utils.mjs +131 -0
  251. package/utils/file-utils.mjs +36 -1
  252. package/utils/kroki-utils.mjs +1 -1
  253. package/utils/linter/index.mjs +50 -0
  254. package/utils/markdown/index.mjs +26 -0
  255. package/utils/markdown-checker.mjs +1 -1
  256. package/utils/utils.mjs +19 -7
  257. package/prompts/structure/generate-structure.md +0 -161
@@ -1,25 +1,44 @@
1
1
  import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
2
2
  import * as openModule from "open";
3
- import * as blockletModule from "../../utils/blocklet.mjs";
3
+ import * as authUtilsModule from "../../utils/auth-utils.mjs";
4
4
 
5
- // Import the real utils module and deploy function
5
+ // Import the deploy function
6
6
  import { deploy } from "../../utils/deploy.mjs";
7
- import * as utils from "../../utils/utils.mjs";
7
+ import * as utilsModule from "../../utils/utils.mjs";
8
+
9
+ const TEST_HOME_URL = "https_home_test_url";
10
+ const TEST_APP_URL = "https_app_test_url";
11
+ const TEST_DASHBOARD_URL = "https_dashboard_test_url";
12
+ const TEST_SUBSCRIPTION_URL = "https_subscription_test_url";
13
+
14
+ // Mock BrokerClient
15
+ const mockBrokerClient = {
16
+ deploy: mock(),
17
+ };
18
+
19
+ const mockBrokerClientConstructor = mock(() => mockBrokerClient);
20
+
21
+ // Mock the payment broker client module
22
+ mock.module("@blocklet/payment-broker-client/node", () => ({
23
+ BrokerClient: mockBrokerClientConstructor,
24
+ STEPS: {
25
+ PAYMENT_PENDING: "PAYMENT_PENDING",
26
+ INSTALLATION_STARTING: "INSTALLATION_STARTING",
27
+ SERVICE_STARTING: "SERVICE_STARTING",
28
+ ACCESS_PREPARING: "ACCESS_PREPARING",
29
+ ACCESS_READY: "ACCESS_READY",
30
+ },
31
+ }));
8
32
 
9
33
  describe("deploy", () => {
10
- let originalFetch;
11
34
  let originalConsole;
12
35
  let consoleOutput;
36
+ let getOfficialAccessTokenSpy;
13
37
  let saveValueToConfigSpy;
14
- let originalSetTimeout;
15
- let getComponentInfoWithMountPointSpy;
16
- let getComponentInfoSpy;
17
38
  let openDefaultSpy;
18
39
 
19
- beforeEach(async () => {
20
- // Reset environment
21
- process.env.DOC_SMITH_BASE_URL = "https://test.example.com";
22
- process.env.NODE_ENV = "test";
40
+ beforeEach(() => {
41
+ // Note: DOC_SMITH_BASE_URL is not set, so BASE_URL will be empty string
23
42
 
24
43
  // Mock console to capture output
25
44
  consoleOutput = [];
@@ -30,433 +49,358 @@ describe("deploy", () => {
30
49
  console.log = (...args) => consoleOutput.push({ type: "log", args });
31
50
  console.error = (...args) => consoleOutput.push({ type: "error", args });
32
51
 
33
- // Mock setTimeout to make tests run instantly
34
- originalSetTimeout = global.setTimeout;
35
- global.setTimeout = (callback, _delay) => {
36
- // Call immediately in tests
37
- return originalSetTimeout(callback, 0);
38
- };
39
-
40
- // Mock fetch
41
- originalFetch = global.fetch;
42
-
43
- // Use spyOn to mock saveValueToConfig without affecting other tests
44
- saveValueToConfigSpy = spyOn(utils, "saveValueToConfig").mockResolvedValue();
45
-
46
- // Spy on blocklet module functions
47
- getComponentInfoWithMountPointSpy = spyOn(
48
- blockletModule,
49
- "getComponentInfoWithMountPoint",
50
- ).mockResolvedValue({
51
- mountPoint: "/payment",
52
- PAYMENT_LINK_ID: "test-payment-id",
53
- });
54
-
55
- getComponentInfoSpy = spyOn(blockletModule, "getComponentInfo").mockResolvedValue({
56
- status: "running",
57
- });
58
-
59
- // Spy on open module
52
+ // Mock dependencies
53
+ getOfficialAccessTokenSpy = spyOn(authUtilsModule, "getOfficialAccessToken").mockResolvedValue(
54
+ "mock-auth-token",
55
+ );
56
+ saveValueToConfigSpy = spyOn(utilsModule, "saveValueToConfig").mockResolvedValue();
60
57
  openDefaultSpy = spyOn(openModule, "default").mockResolvedValue();
58
+
59
+ // Reset mocks
60
+ mockBrokerClientConstructor.mockClear();
61
+ mockBrokerClient.deploy.mockClear();
61
62
  });
62
63
 
63
64
  afterEach(() => {
64
- // Restore originals
65
- global.fetch = originalFetch;
66
- global.setTimeout = originalSetTimeout;
65
+ // Restore console
67
66
  console.log = originalConsole.log;
68
67
  console.error = originalConsole.error;
69
68
 
70
69
  // Restore all spies
70
+ getOfficialAccessTokenSpy?.mockRestore();
71
71
  saveValueToConfigSpy?.mockRestore();
72
- getComponentInfoWithMountPointSpy?.mockRestore();
73
- getComponentInfoSpy?.mockRestore();
74
72
  openDefaultSpy?.mockRestore();
75
73
 
76
- // Reset environment
74
+ // Clean up environment
77
75
  delete process.env.NODE_ENV;
78
76
  });
79
77
 
80
78
  test("successful deployment flow", async () => {
81
- // Mock API responses for the complete flow
82
- let callCount = 0;
83
- global.fetch = mock(async (url) => {
84
- callCount++;
85
-
86
- // Step 1: Create payment session
87
- if (url.includes("/api/checkout-sessions/start")) {
88
- return {
89
- ok: true,
90
- status: 200,
91
- json: async () => ({
92
- checkoutSession: { id: "checkout-123" },
93
- paymentUrl: "https://payment.test/checkout-123",
94
- }),
95
- };
96
- }
97
-
98
- // Step 2-4: Poll payment/installation/service status
99
- if (url.includes("/api/vendors/order/checkout-123/status")) {
100
- if (callCount <= 2) {
101
- // First call: payment completed, installation in progress
102
- return {
103
- ok: true,
104
- status: 200,
105
- json: async () => ({
106
- payment_status: "paid",
107
- vendors: [{ id: "vendor-1", progress: 50, appUrl: null }],
108
- }),
109
- };
110
- } else {
111
- // Subsequent calls: installation complete
112
- return {
113
- ok: true,
114
- status: 200,
115
- json: async () => ({
116
- payment_status: "paid",
117
- vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
118
- }),
119
- };
120
- }
121
- }
122
-
123
- // Step 5: Get order details
124
- if (url.includes("/api/vendors/order/checkout-123/detail")) {
125
- return {
126
- ok: true,
127
- status: 200,
128
- json: async () => ({
129
- vendors: [
130
- {
131
- appUrl: "https://app.test",
132
- dashboardUrl: "https://dashboard.test",
133
- homeUrl: "https://home.test",
134
- token: "auth-token-123",
135
- },
136
- ],
137
- }),
138
- };
139
- }
79
+ // Mock successful deployment result
80
+ const mockResult = {
81
+ appUrl: TEST_APP_URL,
82
+ homeUrl: TEST_HOME_URL,
83
+ dashboardUrl: TEST_DASHBOARD_URL,
84
+ subscriptionUrl: TEST_SUBSCRIPTION_URL,
85
+ vendors: [{ token: "auth-token-123" }],
86
+ };
140
87
 
141
- throw new Error(`Unexpected URL: ${url}`);
142
- });
88
+ mockBrokerClient.deploy.mockResolvedValue(mockResult);
143
89
 
144
90
  const result = await deploy();
145
91
 
146
- // Verify result
92
+ // Verify BrokerClient was constructed with correct config
93
+ expect(mockBrokerClientConstructor).toHaveBeenCalledWith({
94
+ baseUrl: "",
95
+ authToken: "mock-auth-token",
96
+ paymentLinkKey: "PAYMENT_LINK_ID",
97
+ timeout: 300000,
98
+ polling: {
99
+ interval: 3000,
100
+ maxAttempts: 100,
101
+ backoffStrategy: "linear",
102
+ },
103
+ });
104
+
105
+ // Verify deploy was called with correct parameters
106
+ expect(mockBrokerClient.deploy).toHaveBeenCalledWith(
107
+ expect.objectContaining({
108
+ cachedCheckoutId: undefined,
109
+ cachedPaymentUrl: undefined,
110
+ pageInfo: expect.objectContaining({
111
+ successMessage: expect.objectContaining({
112
+ en: expect.stringContaining("Congratulations"),
113
+ zh: expect.stringContaining("恭喜您"),
114
+ }),
115
+ }),
116
+ hooks: expect.objectContaining({
117
+ PAYMENT_PENDING: expect.any(Function),
118
+ INSTALLATION_STARTING: expect.any(Function),
119
+ SERVICE_STARTING: expect.any(Function),
120
+ ACCESS_PREPARING: expect.any(Function),
121
+ ACCESS_READY: expect.any(Function),
122
+ }),
123
+ onError: expect.any(Function),
124
+ }),
125
+ );
126
+
127
+ // Verify result transformation
147
128
  expect(result).toEqual({
148
- appUrl: "https://app.test",
149
- homeUrl: "https://home.test",
129
+ appUrl: TEST_APP_URL,
130
+ homeUrl: TEST_HOME_URL,
131
+ dashboardUrl: TEST_DASHBOARD_URL,
132
+ subscriptionUrl: TEST_SUBSCRIPTION_URL,
150
133
  token: "auth-token-123",
151
134
  });
152
135
 
153
- // Verify saveValueToConfig was called
154
- expect(saveValueToConfigSpy).toHaveBeenCalledWith(
155
- "checkoutId",
156
- "checkout-123",
157
- "Checkout ID for document deployment website",
158
- );
159
- expect(saveValueToConfigSpy).toHaveBeenCalledWith(
160
- "paymentUrl",
161
- expect.stringContaining("payment"),
162
- "Payment URL for document deployment website",
136
+ // Verify console output
137
+ const logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
138
+ expect(logs.some((log) => log.includes("🚀 Starting deployment..."))).toBe(true);
139
+ });
140
+
141
+ test("successful deployment with cached parameters", async () => {
142
+ const mockResult = {
143
+ appUrl: TEST_APP_URL,
144
+ homeUrl: TEST_HOME_URL,
145
+ vendors: [{ token: "auth-token-123" }],
146
+ };
147
+
148
+ mockBrokerClient.deploy.mockResolvedValue(mockResult);
149
+
150
+ const result = await deploy("cached-checkout-id", "https://cached-payment.url");
151
+
152
+ // Verify deploy was called with cached parameters
153
+ expect(mockBrokerClient.deploy).toHaveBeenCalledWith(
154
+ expect.objectContaining({
155
+ cachedCheckoutId: "cached-checkout-id",
156
+ cachedPaymentUrl: "https://cached-payment.url",
157
+ }),
163
158
  );
164
159
 
165
- // Verify console output shows progress
166
- const logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
167
- expect(logs.some((log) => log.includes("Step 1/4: Waiting for payment"))).toBe(true);
168
- expect(logs.some((log) => log.includes("Step 2/4: Installing Website"))).toBe(true);
169
- expect(logs.some((log) => log.includes("Step 3/4: Starting Website"))).toBe(true);
170
- expect(logs.some((log) => log.includes("Step 4/4: Getting Website URL"))).toBe(true);
171
- expect(logs.some((log) => log.includes("Your website is available at"))).toBe(true);
160
+ expect(result.appUrl).toBe(TEST_APP_URL);
161
+ expect(result.token).toBe("auth-token-123");
172
162
  });
173
163
 
174
- test("successful deployment flow with subscription", async () => {
175
- // Mock API responses for the complete flow
176
- let callCount = 0;
177
- global.fetch = mock(async (url) => {
178
- callCount++;
179
-
180
- // Step 1: Create payment session
181
- if (url.includes("/api/checkout-sessions/start")) {
182
- return {
183
- ok: true,
184
- status: 200,
185
- json: async () => ({
186
- checkoutSession: { id: "checkout-123" },
187
- paymentUrl: "https://payment.test/checkout-123",
188
- }),
189
- };
190
- }
191
-
192
- // Step 2-4: Poll payment/installation/service status
193
- if (url.includes("/api/vendors/order/checkout-123/status")) {
194
- if (callCount <= 2) {
195
- // First call: payment completed, installation in progress
196
- return {
197
- ok: true,
198
- status: 200,
199
- json: async () => ({
200
- payment_status: "paid",
201
- vendors: [{ id: "vendor-1", progress: 50, appUrl: null }],
202
- }),
203
- };
204
- } else {
205
- // Subsequent calls: installation complete
206
- return {
207
- ok: true,
208
- status: 200,
209
- json: async () => ({
210
- payment_status: "paid",
211
- vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
212
- }),
213
- };
214
- }
215
- }
216
-
217
- // Step 5: Get order details
218
- if (url.includes("/api/vendors/order/checkout-123/detail")) {
219
- return {
220
- ok: true,
221
- status: 200,
222
- json: async () => ({
223
- vendors: [
224
- {
225
- appUrl: "https://app.test",
226
- dashboardUrl: "https://dashboard.test",
227
- homeUrl: "https://home.test",
228
- token: "auth-token-123",
229
- },
230
- ],
231
- subscriptionUrl: "https://subscription.test",
232
- }),
233
- };
234
- }
164
+ test("handles missing auth token", async () => {
165
+ getOfficialAccessTokenSpy.mockResolvedValue(null);
166
+
167
+ await expect(deploy()).rejects.toThrow("Failed to get official access token");
235
168
 
236
- throw new Error(`Unexpected URL: ${url}`);
169
+ // Verify BrokerClient was not created
170
+ expect(mockBrokerClientConstructor).not.toHaveBeenCalled();
171
+ });
172
+
173
+ test("handles BrokerClient deployment failure", async () => {
174
+ const deployError = new Error("Deployment failed");
175
+ mockBrokerClient.deploy.mockRejectedValue(deployError);
176
+
177
+ await expect(deploy()).rejects.toThrow("Deployment failed");
178
+
179
+ // Verify BrokerClient was created and deploy was called
180
+ expect(mockBrokerClientConstructor).toHaveBeenCalled();
181
+ expect(mockBrokerClient.deploy).toHaveBeenCalled();
182
+ });
183
+
184
+ test("PAYMENT_PENDING hook functionality", async () => {
185
+ let paymentPendingHook;
186
+
187
+ mockBrokerClient.deploy.mockImplementation(async (config) => {
188
+ paymentPendingHook = config.hooks.PAYMENT_PENDING;
189
+ return {
190
+ appUrl: TEST_APP_URL,
191
+ vendors: [{ token: "test-token" }],
192
+ };
237
193
  });
238
194
 
239
- const result = await deploy();
195
+ await deploy();
240
196
 
241
- // Verify result
242
- expect(result).toEqual({
243
- appUrl: "https://app.test",
244
- homeUrl: "https://home.test",
245
- token: "auth-token-123",
197
+ // Test the PAYMENT_PENDING hook
198
+ await paymentPendingHook({
199
+ sessionId: "session-123",
200
+ paymentUrl: "https://payment.test/session-123",
201
+ isResuming: false,
246
202
  });
247
203
 
248
204
  // Verify saveValueToConfig was called
249
205
  expect(saveValueToConfigSpy).toHaveBeenCalledWith(
250
206
  "checkoutId",
251
- "checkout-123",
207
+ "session-123",
252
208
  "Checkout ID for document deployment website",
253
209
  );
254
210
  expect(saveValueToConfigSpy).toHaveBeenCalledWith(
255
211
  "paymentUrl",
256
- expect.stringContaining("payment"),
212
+ "https://payment.test/session-123",
257
213
  "Payment URL for document deployment website",
258
214
  );
259
215
 
260
- // Verify console output shows progress
261
- const logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
216
+ // Verify browser was opened
217
+ expect(openDefaultSpy).toHaveBeenCalledWith("https://payment.test/session-123");
262
218
 
263
- expect(logs.some((log) => log.includes("Step 1/4: Waiting for payment"))).toBe(true);
264
- expect(logs.some((log) => log.includes("Step 2/4: Installing Website"))).toBe(true);
265
- expect(logs.some((log) => log.includes("Step 3/4: Starting Website"))).toBe(true);
266
- expect(logs.some((log) => log.includes("Step 4/4: Getting Website URL"))).toBe(true);
267
- expect(logs.some((log) => log.includes("Your website is available at"))).toBe(true);
268
- expect(logs.some((log) => log.includes("Your subscription management URL"))).toBe(true);
219
+ // Verify console output
220
+ const logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
221
+ expect(logs.some((log) => log.includes("Step 1/4: Waiting for payment..."))).toBe(true);
222
+ expect(logs.some((log) => log.includes("🔗 Payment link:"))).toBe(true);
269
223
  });
270
224
 
271
- test("handles missing payment link ID", async () => {
272
- getComponentInfoWithMountPointSpy.mockResolvedValue({
273
- mountPoint: "/payment",
274
- PAYMENT_LINK_ID: null,
225
+ test("PAYMENT_PENDING hook with isResuming=true", async () => {
226
+ let paymentPendingHook;
227
+
228
+ mockBrokerClient.deploy.mockImplementation(async (config) => {
229
+ paymentPendingHook = config.hooks.PAYMENT_PENDING;
230
+ return {
231
+ appUrl: TEST_APP_URL,
232
+ vendors: [{ token: "test-token" }],
233
+ };
275
234
  });
276
235
 
277
- await expect(deploy()).rejects.toThrow("Payment link ID not found");
278
- });
236
+ await deploy();
279
237
 
280
- test("handles payment session creation failure", async () => {
281
- global.fetch = mock(async (url) => {
282
- if (url.includes("/api/checkout-sessions/start")) {
283
- return {
284
- ok: false,
285
- status: 400,
286
- json: async () => ({ error: "Payment creation failed" }),
287
- };
288
- }
238
+ // Test the PAYMENT_PENDING hook with isResuming=true
239
+ await paymentPendingHook({
240
+ sessionId: "session-123",
241
+ paymentUrl: "https://payment.test/session-123",
242
+ isResuming: true,
289
243
  });
290
244
 
291
- await expect(deploy()).rejects.toThrow("Failed to create payment session");
245
+ // Verify browser was NOT opened when resuming
246
+ expect(openDefaultSpy).not.toHaveBeenCalled();
292
247
 
293
- const errors = consoleOutput.filter((o) => o.type === "error");
294
- expect(errors.length).toBeGreaterThan(0);
248
+ // But saveValueToConfig should still be called
249
+ expect(saveValueToConfigSpy).toHaveBeenCalledWith(
250
+ "checkoutId",
251
+ "session-123",
252
+ "Checkout ID for document deployment website",
253
+ );
295
254
  });
296
255
 
297
- test("handles network errors gracefully", async () => {
298
- global.fetch = mock(async () => {
299
- throw new Error("Network connection failed");
256
+ test("other hooks functionality", async () => {
257
+ let hooks;
258
+
259
+ mockBrokerClient.deploy.mockImplementation(async (config) => {
260
+ hooks = config.hooks;
261
+ return {
262
+ appUrl: TEST_APP_URL,
263
+ vendors: [{ token: "test-token" }],
264
+ };
300
265
  });
301
266
 
302
- await expect(deploy()).rejects.toThrow("Failed to create payment session");
303
- });
267
+ await deploy();
304
268
 
305
- test("handles browser opening failure gracefully", async () => {
306
- // Mock successful API flow
307
- global.fetch = mock(async (url) => {
308
- if (url.includes("/api/checkout-sessions/start")) {
309
- return {
310
- ok: true,
311
- status: 200,
312
- json: async () => ({
313
- checkoutSession: { id: "checkout-123" },
314
- paymentUrl: "https://payment.test/checkout-123",
315
- }),
316
- };
317
- }
318
-
319
- if (url.includes("/status")) {
320
- return {
321
- ok: true,
322
- status: 200,
323
- json: async () => ({
324
- payment_status: "paid",
325
- vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
326
- }),
327
- };
328
- }
329
-
330
- if (url.includes("/detail")) {
331
- return {
332
- ok: true,
333
- status: 200,
334
- json: async () => ({
335
- vendors: [
336
- {
337
- appUrl: "https://app.test",
338
- homeUrl: "https://home.test",
339
- token: "auth-token-123",
340
- },
341
- ],
342
- }),
343
- };
344
- }
269
+ // Test INSTALLATION_STARTING hook
270
+ hooks.INSTALLATION_STARTING();
271
+ let logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
272
+ expect(logs.some((log) => log.includes("📦 Step 2/4: Installing Website..."))).toBe(true);
273
+
274
+ // Test SERVICE_STARTING hook
275
+ hooks.SERVICE_STARTING();
276
+ logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
277
+ expect(logs.some((log) => log.includes("🚀 Step 3/4: Starting Website..."))).toBe(true);
278
+
279
+ // Test ACCESS_PREPARING hook
280
+ hooks.ACCESS_PREPARING();
281
+ logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
282
+ expect(logs.some((log) => log.includes("🌐 Step 4/4: Getting Website URL..."))).toBe(true);
283
+
284
+ // Test ACCESS_READY hook without subscription
285
+ await hooks.ACCESS_READY({
286
+ appUrl: TEST_APP_URL,
287
+ homeUrl: TEST_HOME_URL,
288
+ });
289
+ logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
290
+ expect(logs.some((log) => log.includes("🔗 Your website is available at:"))).toBe(true);
291
+ expect(logs.some((log) => log.includes(TEST_HOME_URL))).toBe(true);
292
+
293
+ // Test ACCESS_READY hook with subscription
294
+ await hooks.ACCESS_READY({
295
+ appUrl: TEST_APP_URL,
296
+ homeUrl: TEST_HOME_URL,
297
+ subscriptionUrl: TEST_SUBSCRIPTION_URL,
345
298
  });
299
+ logs = consoleOutput.filter((o) => o.type === "log").map((o) => o.args.join(" "));
300
+ expect(logs.some((log) => log.includes("🔗 Your subscription management URL:"))).toBe(true);
301
+ expect(logs.some((log) => log.includes(TEST_SUBSCRIPTION_URL))).toBe(true);
302
+ });
346
303
 
347
- // Mock open to fail
348
- openDefaultSpy.mockRejectedValue(new Error("Cannot open browser"));
304
+ test("handles missing vendors in result", async () => {
305
+ const mockResult = {
306
+ appUrl: TEST_APP_URL,
307
+ homeUrl: TEST_HOME_URL,
308
+ vendors: null,
309
+ };
310
+
311
+ mockBrokerClient.deploy.mockResolvedValue(mockResult);
349
312
 
350
- // Call deploy without cached parameters - should still succeed
351
313
  const result = await deploy();
352
314
 
353
- // Should still complete successfully despite browser opening failure
354
- expect(result.appUrl).toBe("https://app.test");
355
- expect(result.homeUrl).toBe("https://home.test");
356
- expect(result.token).toBe("auth-token-123");
315
+ expect(result.token).toBeUndefined();
316
+ expect(result.appUrl).toBe(TEST_APP_URL);
317
+ expect(result.homeUrl).toBe(TEST_HOME_URL);
357
318
  });
358
319
 
359
- test("handles cached checkout ID", async () => {
360
- // Mock successful status check for cached ID
361
- global.fetch = mock(async (url) => {
362
- if (url.includes("/status")) {
363
- return {
364
- ok: true,
365
- status: 200,
366
- json: async () => ({
367
- payment_status: "paid",
368
- vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
369
- }),
370
- };
371
- }
372
-
373
- if (url.includes("/detail")) {
374
- return {
375
- ok: true,
376
- status: 200,
377
- json: async () => ({
378
- vendors: [
379
- {
380
- appUrl: "https://app.test",
381
- homeUrl: "https://home.test",
382
- token: "auth-token-123",
383
- },
384
- ],
385
- }),
386
- };
387
- }
388
- });
320
+ test("handles empty vendors array in result", async () => {
321
+ const mockResult = {
322
+ appUrl: TEST_APP_URL,
323
+ homeUrl: TEST_HOME_URL,
324
+ vendors: [],
325
+ };
389
326
 
390
- const result = await deploy("existing-checkout-id", "https://cached-payment.url");
327
+ mockBrokerClient.deploy.mockResolvedValue(mockResult);
391
328
 
392
- expect(result.appUrl).toBe("https://app.test");
329
+ const result = await deploy();
393
330
 
394
- // Should not call open since using cached checkout
395
- expect(openDefaultSpy).not.toHaveBeenCalled();
331
+ expect(result.token).toBeUndefined();
332
+ expect(result.appUrl).toBe(TEST_APP_URL);
333
+ expect(result.homeUrl).toBe(TEST_HOME_URL);
396
334
  });
397
335
 
398
- test("clears checkout ID when cache check fails", async () => {
399
- // Mock successful responses for the complete flow after cache check fails
400
- let callCount = 0;
401
- global.fetch = mock(async (url) => {
402
- callCount++;
403
-
404
- // First call: cache check fails
405
- if (callCount === 1 && url.includes("/status")) {
406
- throw new Error("Network error during cache check");
407
- }
408
-
409
- // Create payment session
410
- if (url.includes("/api/checkout-sessions/start")) {
411
- return {
412
- ok: true,
413
- status: 200,
414
- json: async () => ({
415
- checkoutSession: { id: "new-checkout-123" },
416
- paymentUrl: "https://payment.test/new-checkout-123",
417
- }),
418
- };
419
- }
420
-
421
- // Subsequent status checks and detail calls
422
- if (url.includes("/status")) {
423
- return {
424
- ok: true,
425
- status: 200,
426
- json: async () => ({
427
- payment_status: "paid",
428
- vendors: [{ id: "vendor-1", progress: 100, appUrl: "https://app.test" }],
429
- }),
430
- };
431
- }
432
-
433
- if (url.includes("/detail")) {
434
- return {
435
- ok: true,
436
- status: 200,
437
- json: async () => ({
438
- vendors: [
439
- {
440
- appUrl: "https://app.test",
441
- homeUrl: "https://home.test",
442
- token: "auth-token-123",
443
- },
444
- ],
445
- }),
446
- };
447
- }
448
- });
336
+ test("uses default BASE_URL when not set", async () => {
337
+ delete process.env.DOC_SMITH_BASE_URL;
449
338
 
450
- // Call deploy with invalid cached checkout ID - should clear it and create new one
451
- const result = await deploy("invalid-checkout-id");
339
+ const mockResult = {
340
+ appUrl: TEST_APP_URL,
341
+ vendors: [{ token: "test-token" }],
342
+ };
452
343
 
453
- expect(result.appUrl).toBe("https://app.test");
344
+ mockBrokerClient.deploy.mockResolvedValue(mockResult);
454
345
 
455
- // Verify that checkoutId was cleared due to cache check failure
456
- expect(saveValueToConfigSpy).toHaveBeenCalledWith(
457
- "checkoutId",
458
- "",
459
- "Checkout ID for document deployment website",
346
+ await deploy();
347
+
348
+ // Verify BrokerClient was constructed with empty baseUrl
349
+ expect(mockBrokerClientConstructor).toHaveBeenCalledWith(
350
+ expect.objectContaining({
351
+ baseUrl: "",
352
+ }),
460
353
  );
461
354
  });
355
+
356
+ test("handles browser opening failure", async () => {
357
+ openDefaultSpy.mockRejectedValue(new Error("Cannot open browser"));
358
+
359
+ let paymentPendingHook;
360
+ mockBrokerClient.deploy.mockImplementation(async (config) => {
361
+ paymentPendingHook = config.hooks.PAYMENT_PENDING;
362
+ return {
363
+ appUrl: TEST_APP_URL,
364
+ vendors: [{ token: "test-token" }],
365
+ };
366
+ });
367
+
368
+ await deploy();
369
+
370
+ // The hook should throw when browser opening fails (expected behavior)
371
+ await expect(
372
+ paymentPendingHook({
373
+ sessionId: "session-123",
374
+ paymentUrl: "https://payment.test/session-123",
375
+ isResuming: false,
376
+ }),
377
+ ).rejects.toThrow("Cannot open browser");
378
+
379
+ // Config should still be saved
380
+ expect(saveValueToConfigSpy).toHaveBeenCalled();
381
+ });
382
+
383
+ test("handles saveValueToConfig failure", async () => {
384
+ saveValueToConfigSpy.mockRejectedValue(new Error("Config save failed"));
385
+
386
+ let paymentPendingHook;
387
+ mockBrokerClient.deploy.mockImplementation(async (config) => {
388
+ paymentPendingHook = config.hooks.PAYMENT_PENDING;
389
+ return {
390
+ appUrl: TEST_APP_URL,
391
+ vendors: [{ token: "test-token" }],
392
+ };
393
+ });
394
+
395
+ await deploy();
396
+
397
+ // The hook should throw when config saving fails (expected behavior)
398
+ await expect(
399
+ paymentPendingHook({
400
+ sessionId: "session-123",
401
+ paymentUrl: "https://payment.test/session-123",
402
+ isResuming: false,
403
+ }),
404
+ ).rejects.toThrow("Config save failed");
405
+ });
462
406
  });