@thegetty/quire-cli 1.0.0-rc.44 → 1.0.0-rc.46

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 (243) hide show
  1. package/CHANGELOG.md +0 -36
  2. package/README.md +0 -9
  3. package/bin/cli.js +1 -46
  4. package/package.json +9 -25
  5. package/src/Command.js +6 -26
  6. package/src/commands/README.md +122 -213
  7. package/src/commands/build.js +37 -45
  8. package/src/commands/clean.js +19 -33
  9. package/src/commands/conf.js +43 -0
  10. package/src/commands/create.js +14 -42
  11. package/src/commands/epub.js +31 -67
  12. package/src/commands/index.js +2 -9
  13. package/src/commands/info.js +86 -88
  14. package/src/commands/pdf.js +82 -63
  15. package/src/commands/preview.js +31 -32
  16. package/src/commands/validate.js +20 -64
  17. package/src/commands/version.js +43 -0
  18. package/src/errors/validation/validation-error.js +11 -20
  19. package/src/helpers/clean.js +1 -1
  20. package/src/helpers/is-quire.js +31 -0
  21. package/src/helpers/test-cwd.js +6 -5
  22. package/src/helpers/which.js +4 -10
  23. package/src/lib/11ty/README.md +19 -135
  24. package/src/lib/11ty/api.js +93 -235
  25. package/src/lib/11ty/cli.js +37 -112
  26. package/src/lib/11ty/index.js +5 -64
  27. package/src/lib/11ty/paths.js +103 -0
  28. package/src/lib/README.md +0 -330
  29. package/src/lib/conf/README.md +69 -86
  30. package/src/lib/conf/config.js +3 -5
  31. package/src/lib/conf/defaults.js +0 -65
  32. package/src/lib/conf/schema.js +9 -103
  33. package/src/lib/epub/README.md +2 -133
  34. package/src/lib/epub/epub.js +11 -36
  35. package/src/lib/epub/index.js +21 -111
  36. package/src/lib/epub/pandoc.js +4 -33
  37. package/src/lib/git/README.md +2 -151
  38. package/src/lib/git/index.js +13 -217
  39. package/src/lib/i18n/README.md +3 -0
  40. package/src/lib/i18n/config.js +53 -0
  41. package/src/lib/i18n/index.js +43 -0
  42. package/src/lib/i18n/localeService.js +58 -0
  43. package/src/lib/pdf/README.md +0 -131
  44. package/src/lib/pdf/index.js +21 -124
  45. package/src/lib/pdf/paged.js +52 -100
  46. package/src/lib/pdf/prince.js +37 -115
  47. package/src/lib/pdf/split.js +33 -61
  48. package/src/lib/quire/README.md +19 -0
  49. package/src/lib/quire/index.js +353 -0
  50. package/src/lib/reporter/README.md +2 -211
  51. package/src/lib/reporter/index.js +0 -607
  52. package/src/main.js +48 -221
  53. package/src/packageConfig.js +0 -17
  54. package/src/validators/utils.js +1 -2
  55. package/schemas/config.schema.json +0 -194
  56. package/schemas/figures.schema.json +0 -56
  57. package/schemas/layout.schema.json +0 -5
  58. package/schemas/objects.schema.json +0 -62
  59. package/schemas/publication.schema.json +0 -140
  60. package/schemas/references.schema.json +0 -29
  61. package/src/Command.spec.js +0 -99
  62. package/src/commands/build.spec.js +0 -113
  63. package/src/commands/build.test.js +0 -397
  64. package/src/commands/clean.spec.js +0 -118
  65. package/src/commands/clean.test.js +0 -382
  66. package/src/commands/config.js +0 -238
  67. package/src/commands/config.spec.js +0 -107
  68. package/src/commands/config.test.js +0 -845
  69. package/src/commands/create.spec.js +0 -112
  70. package/src/commands/create.test.js +0 -415
  71. package/src/commands/doctor.js +0 -251
  72. package/src/commands/doctor.spec.js +0 -115
  73. package/src/commands/doctor.test.js +0 -1409
  74. package/src/commands/epub.spec.js +0 -114
  75. package/src/commands/epub.test.js +0 -503
  76. package/src/commands/help.js +0 -60
  77. package/src/commands/help.test.js +0 -132
  78. package/src/commands/info.spec.js +0 -72
  79. package/src/commands/info.test.js +0 -512
  80. package/src/commands/pdf.spec.js +0 -114
  81. package/src/commands/pdf.test.js +0 -464
  82. package/src/commands/preview.spec.js +0 -97
  83. package/src/commands/preview.test.js +0 -250
  84. package/src/commands/use.js +0 -56
  85. package/src/commands/use.spec.js +0 -61
  86. package/src/commands/use.test.js +0 -280
  87. package/src/commands/validate.spec.js +0 -90
  88. package/src/commands/validate.test.js +0 -378
  89. package/src/commands/workflows.js +0 -70
  90. package/src/errors/build/build-failed-error.js +0 -19
  91. package/src/errors/build/config-field-missing-error.js +0 -20
  92. package/src/errors/build/config-file-not-found-error.js +0 -20
  93. package/src/errors/build/index.js +0 -11
  94. package/src/errors/help/help-topic-not-found-error.js +0 -30
  95. package/src/errors/help/index.js +0 -8
  96. package/src/errors/index.js +0 -58
  97. package/src/errors/input/index.js +0 -8
  98. package/src/errors/input/invalid-input-error.js +0 -21
  99. package/src/errors/install/dependency-install-error.js +0 -19
  100. package/src/errors/install/directory-not-empty-error.js +0 -25
  101. package/src/errors/install/index.js +0 -12
  102. package/src/errors/install/invalid-path-error.js +0 -31
  103. package/src/errors/install/invalid-starter-error.js +0 -27
  104. package/src/errors/install/version-not-found-error.js +0 -35
  105. package/src/errors/output/epub-generation-error.js +0 -19
  106. package/src/errors/output/index.js +0 -14
  107. package/src/errors/output/invalid-epub-library-error.js +0 -20
  108. package/src/errors/output/invalid-pdf-library-error.js +0 -20
  109. package/src/errors/output/missing-build-output-error.js +0 -21
  110. package/src/errors/output/pdf-generation-error.js +0 -24
  111. package/src/errors/output/tool-not-found-error.js +0 -37
  112. package/src/errors/project/index.js +0 -10
  113. package/src/errors/project/not-in-project-error.js +0 -20
  114. package/src/errors/project/project-create-error.js +0 -21
  115. package/src/errors/quire-error.js +0 -27
  116. package/src/errors/validation/index.js +0 -3
  117. package/src/errors/validation/unknown-config-key-error.js +0 -27
  118. package/src/errors/validation/unknown-config-operation-error.js +0 -24
  119. package/src/helpers/docs-url.js +0 -32
  120. package/src/helpers/pager.js +0 -59
  121. package/src/helpers/pager.test.js +0 -85
  122. package/src/helpers/suggest-similar.js +0 -124
  123. package/src/helpers/suggest-similar.test.js +0 -108
  124. package/src/helpers/test-cwd.test.js +0 -192
  125. package/src/lib/11ty/index.test.js +0 -655
  126. package/src/lib/commander/index.js +0 -103
  127. package/src/lib/commander/index.test.js +0 -86
  128. package/src/lib/commander/options.js +0 -237
  129. package/src/lib/commander/options.test.js +0 -205
  130. package/src/lib/conf/build-status.js +0 -103
  131. package/src/lib/conf/build-status.test.js +0 -247
  132. package/src/lib/conf/config.test.js +0 -281
  133. package/src/lib/conf/format.js +0 -60
  134. package/src/lib/conf/format.test.js +0 -106
  135. package/src/lib/conf/helpers.js +0 -91
  136. package/src/lib/conf/helpers.test.js +0 -136
  137. package/src/lib/conf/index.js +0 -22
  138. package/src/lib/constants.js +0 -28
  139. package/src/lib/doctor/README.md +0 -667
  140. package/src/lib/doctor/checks/environment/cli-version.js +0 -62
  141. package/src/lib/doctor/checks/environment/cli-version.test.js +0 -132
  142. package/src/lib/doctor/checks/environment/git-available.js +0 -77
  143. package/src/lib/doctor/checks/environment/git-available.test.js +0 -52
  144. package/src/lib/doctor/checks/environment/index.js +0 -13
  145. package/src/lib/doctor/checks/environment/node-version.js +0 -66
  146. package/src/lib/doctor/checks/environment/node-version.test.js +0 -17
  147. package/src/lib/doctor/checks/environment/npm-available.js +0 -66
  148. package/src/lib/doctor/checks/environment/npm-available.test.js +0 -52
  149. package/src/lib/doctor/checks/environment/os-info.js +0 -48
  150. package/src/lib/doctor/checks/environment/os-info.test.js +0 -81
  151. package/src/lib/doctor/checks/environment/runtime-info.js +0 -51
  152. package/src/lib/doctor/checks/environment/runtime-info.test.js +0 -132
  153. package/src/lib/doctor/checks/outputs/epub-output.js +0 -119
  154. package/src/lib/doctor/checks/outputs/epub-output.test.js +0 -277
  155. package/src/lib/doctor/checks/outputs/index.js +0 -10
  156. package/src/lib/doctor/checks/outputs/pdf-output.js +0 -144
  157. package/src/lib/doctor/checks/outputs/pdf-output.test.js +0 -377
  158. package/src/lib/doctor/checks/outputs/stale-build.js +0 -122
  159. package/src/lib/doctor/checks/outputs/stale-build.test.js +0 -282
  160. package/src/lib/doctor/checks/project/data-files.js +0 -56
  161. package/src/lib/doctor/checks/project/data-files.test.js +0 -125
  162. package/src/lib/doctor/checks/project/dependencies.js +0 -53
  163. package/src/lib/doctor/checks/project/dependencies.test.js +0 -71
  164. package/src/lib/doctor/checks/project/index.js +0 -11
  165. package/src/lib/doctor/checks/project/quire-11ty.js +0 -98
  166. package/src/lib/doctor/checks/project/quire-11ty.test.js +0 -170
  167. package/src/lib/doctor/checks/project/quire-project.js +0 -38
  168. package/src/lib/doctor/checks/project/quire-project.test.js +0 -47
  169. package/src/lib/doctor/checks/tools/index.js +0 -10
  170. package/src/lib/doctor/checks/tools/pandoc-available.js +0 -82
  171. package/src/lib/doctor/checks/tools/pandoc-available.test.js +0 -73
  172. package/src/lib/doctor/checks/tools/prince-available.js +0 -81
  173. package/src/lib/doctor/checks/tools/prince-available.test.js +0 -73
  174. package/src/lib/doctor/constants.js +0 -39
  175. package/src/lib/doctor/formatDuration.js +0 -108
  176. package/src/lib/doctor/formatDuration.test.js +0 -76
  177. package/src/lib/doctor/formatters/human.js +0 -257
  178. package/src/lib/doctor/formatters/human.test.js +0 -463
  179. package/src/lib/doctor/formatters/index.js +0 -8
  180. package/src/lib/doctor/formatters/json.js +0 -78
  181. package/src/lib/doctor/formatters/json.test.js +0 -174
  182. package/src/lib/doctor/formatters/shared.js +0 -129
  183. package/src/lib/doctor/formatters/shared.test.js +0 -194
  184. package/src/lib/doctor/index.js +0 -271
  185. package/src/lib/doctor/index.test.js +0 -797
  186. package/src/lib/epub/engines.js +0 -46
  187. package/src/lib/epub/index.test.js +0 -518
  188. package/src/lib/epub/pandoc.test.js +0 -122
  189. package/src/lib/epub/schema.js +0 -21
  190. package/src/lib/error/README.md +0 -170
  191. package/src/lib/error/handler.js +0 -107
  192. package/src/lib/git/index.spec.js +0 -80
  193. package/src/lib/git/index.test.js +0 -453
  194. package/src/lib/help/frontmatter.js +0 -77
  195. package/src/lib/help/frontmatter.test.js +0 -139
  196. package/src/lib/help/index.js +0 -146
  197. package/src/lib/help/index.test.js +0 -188
  198. package/src/lib/help/topics/configuration.md +0 -77
  199. package/src/lib/help/topics/debugging.md +0 -69
  200. package/src/lib/help/topics/epub.md +0 -74
  201. package/src/lib/help/topics/pdf.md +0 -74
  202. package/src/lib/help/topics/publishing.md +0 -80
  203. package/src/lib/help/topics/workflows.md +0 -50
  204. package/src/lib/installer/index.js +0 -309
  205. package/src/lib/installer/index.spec.js +0 -83
  206. package/src/lib/installer/index.test.js +0 -545
  207. package/src/lib/logger/README.md +0 -424
  208. package/src/lib/logger/debug.js +0 -132
  209. package/src/lib/logger/debug.spec.js +0 -130
  210. package/src/lib/logger/debug.test.js +0 -128
  211. package/src/lib/logger/index.js +0 -228
  212. package/src/lib/logger/index.spec.js +0 -131
  213. package/src/lib/logger/index.test.js +0 -578
  214. package/src/lib/npm/README.md +0 -127
  215. package/src/lib/npm/index.js +0 -198
  216. package/src/lib/npm/index.spec.js +0 -60
  217. package/src/lib/npm/index.test.js +0 -355
  218. package/src/lib/pdf/engines.js +0 -46
  219. package/src/lib/pdf/index.test.js +0 -708
  220. package/src/lib/pdf/paged.test.js +0 -366
  221. package/src/lib/pdf/prince.test.js +0 -202
  222. package/src/lib/pdf/schema.js +0 -21
  223. package/src/lib/pdf/split.test.js +0 -445
  224. package/src/lib/platform.js +0 -95
  225. package/src/lib/process/manager.js +0 -110
  226. package/src/lib/process/manager.test.js +0 -55
  227. package/src/lib/project/build.js +0 -162
  228. package/src/lib/project/build.test.js +0 -275
  229. package/src/lib/project/config.js +0 -48
  230. package/src/lib/project/config.test.js +0 -134
  231. package/src/lib/project/detect.js +0 -47
  232. package/src/lib/project/detect.test.js +0 -157
  233. package/src/lib/project/index.js +0 -56
  234. package/src/lib/project/output-paths.js +0 -87
  235. package/src/lib/project/output-paths.test.js +0 -66
  236. package/src/lib/project/paths.js +0 -272
  237. package/src/lib/project/version.js +0 -110
  238. package/src/lib/project/version.test.js +0 -350
  239. package/src/lib/reporter/index.test.js +0 -862
  240. package/src/main.spec.js +0 -61
  241. package/src/main.test.js +0 -347
  242. package/src/validators/validate-data-files.js +0 -154
  243. package/src/validators/validate-data-files.test.js +0 -217
@@ -1,366 +0,0 @@
1
- import esmock from 'esmock'
2
- import path from 'node:path'
3
- import sinon from 'sinon'
4
- import test from 'ava'
5
-
6
- /**
7
- * Paged.js PDF Engine Integration Tests
8
- *
9
- * Tests error handling paths that are difficult to trigger in E2E tests.
10
- * The happy path is covered by E2E tests with real Paged.js/Puppeteer.
11
- */
12
-
13
- /**
14
- * Create a cross-platform absolute path for testing
15
- * @param {...string} segments - Path segments to join
16
- * @returns {string} Platform-appropriate absolute path
17
- */
18
- function testPath(...segments) {
19
- const root = process.platform === 'win32' ? 'C:\\' : '/'
20
- return path.join(root, ...segments)
21
- }
22
-
23
- test.beforeEach((t) => {
24
- t.context.sandbox = sinon.createSandbox()
25
-
26
- // Mock reporter
27
- t.context.mockReporter = {
28
- update: t.context.sandbox.stub(),
29
- warn: t.context.sandbox.stub()
30
- }
31
-
32
- // Mock processManager
33
- t.context.mockProcessManager = {
34
- onShutdown: t.context.sandbox.stub(),
35
- onShutdownComplete: t.context.sandbox.stub()
36
- }
37
-
38
- // Mock fs-extra
39
- t.context.mockFs = {
40
- existsSync: t.context.sandbox.stub().returns(true),
41
- mkdirsSync: t.context.sandbox.stub(),
42
- promises: {
43
- writeFile: t.context.sandbox.stub().resolves()
44
- }
45
- }
46
- })
47
-
48
- test.afterEach.always((t) => {
49
- t.context.sandbox.restore()
50
- })
51
-
52
- /**
53
- * Create a mock Printer instance
54
- */
55
- function createMockPrinter(sandbox, options = {}) {
56
- const mockPrinter = {
57
- on: sandbox.stub(),
58
- close: sandbox.stub(),
59
- pdf: options.pdf || sandbox.stub().resolves(Buffer.from([1, 2, 3])),
60
- browser: {
61
- pages: options.pages || sandbox.stub().resolves([{
62
- evaluate: sandbox.stub().resolves({})
63
- }])
64
- }
65
- }
66
- return mockPrinter
67
- }
68
-
69
- // ─────────────────────────────────────────────────────────────────────────────
70
- // Error handling tests
71
- // ─────────────────────────────────────────────────────────────────────────────
72
-
73
- test('throws PdfGenerationError when PDF rendering fails', async (t) => {
74
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
75
-
76
- const mockPrinter = createMockPrinter(sandbox, {
77
- pdf: sandbox.stub().rejects(new Error('Puppeteer crashed: out of memory'))
78
- })
79
-
80
- const MockPrinter = sandbox.stub().returns(mockPrinter)
81
-
82
- const paged = await esmock('./paged.js', {
83
- 'pagedjs-cli': MockPrinter,
84
- 'fs-extra': mockFs,
85
- '#lib/reporter/index.js': { default: mockReporter },
86
- '#lib/process/manager.js': { default: mockProcessManager }
87
- })
88
-
89
- const error = await t.throwsAsync(() =>
90
- paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
91
- )
92
-
93
- t.is(error.code, 'PDF_FAILED')
94
- t.true(error.message.includes('Paged.js'))
95
- t.true(error.message.includes('PDF rendering'))
96
- t.true(error.message.includes('out of memory'))
97
- })
98
-
99
- test('throws PdfGenerationError when page map extraction fails', async (t) => {
100
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
101
-
102
- const mockPrinter = createMockPrinter(sandbox, {
103
- pages: sandbox.stub().rejects(new Error('Browser context destroyed'))
104
- })
105
-
106
- const MockPrinter = sandbox.stub().returns(mockPrinter)
107
-
108
- const paged = await esmock('./paged.js', {
109
- 'pagedjs-cli': MockPrinter,
110
- 'fs-extra': mockFs,
111
- '#lib/reporter/index.js': { default: mockReporter },
112
- '#lib/process/manager.js': { default: mockProcessManager }
113
- })
114
-
115
- const error = await t.throwsAsync(() =>
116
- paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
117
- )
118
-
119
- t.is(error.code, 'PDF_FAILED')
120
- t.true(error.message.includes('Paged.js'))
121
- t.true(error.message.includes('page map extraction'))
122
- t.true(error.message.includes('Browser context destroyed'))
123
- })
124
-
125
- test('throws PdfGenerationError when cover PDF rendering fails', async (t) => {
126
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
127
-
128
- // Main printer succeeds
129
- const mainPrinter = createMockPrinter(sandbox)
130
-
131
- // Cover printer fails
132
- const coverPrinter = createMockPrinter(sandbox, {
133
- pdf: sandbox.stub().rejects(new Error('Cover page timeout'))
134
- })
135
-
136
- let printerCallCount = 0
137
- const MockPrinter = sandbox.stub().callsFake(() => {
138
- printerCallCount++
139
- return printerCallCount === 1 ? mainPrinter : coverPrinter
140
- })
141
-
142
- const paged = await esmock('./paged.js', {
143
- 'pagedjs-cli': MockPrinter,
144
- 'fs-extra': mockFs,
145
- '#lib/reporter/index.js': { default: mockReporter },
146
- '#lib/process/manager.js': { default: mockProcessManager }
147
- })
148
-
149
- // Enable cover page generation
150
- const options = {
151
- pdfConfig: { pagePDF: { coverPage: true } }
152
- }
153
-
154
- const error = await t.throwsAsync(() =>
155
- paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), options)
156
- )
157
-
158
- t.is(error.code, 'PDF_FAILED')
159
- t.true(error.message.includes('Paged.js'))
160
- t.true(error.message.includes('cover PDF rendering'))
161
- t.true(error.message.includes('Cover page timeout'))
162
- // Verify cover printer was closed on error
163
- t.true(coverPrinter.close.called)
164
- })
165
-
166
- test('throws PdfGenerationError when cover page map extraction fails', async (t) => {
167
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
168
-
169
- // Main printer succeeds
170
- const mainPrinter = createMockPrinter(sandbox)
171
-
172
- // Cover printer PDF succeeds but pages() fails
173
- const coverPrinter = createMockPrinter(sandbox, {
174
- pages: sandbox.stub().rejects(new Error('Cover browser crashed'))
175
- })
176
-
177
- let printerCallCount = 0
178
- const MockPrinter = sandbox.stub().callsFake(() => {
179
- printerCallCount++
180
- return printerCallCount === 1 ? mainPrinter : coverPrinter
181
- })
182
-
183
- const paged = await esmock('./paged.js', {
184
- 'pagedjs-cli': MockPrinter,
185
- 'fs-extra': mockFs,
186
- '#lib/reporter/index.js': { default: mockReporter },
187
- '#lib/process/manager.js': { default: mockProcessManager }
188
- })
189
-
190
- const options = {
191
- pdfConfig: { pagePDF: { coverPage: true } }
192
- }
193
-
194
- const error = await t.throwsAsync(() =>
195
- paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), options)
196
- )
197
-
198
- t.is(error.code, 'PDF_FAILED')
199
- t.true(error.message.includes('Paged.js'))
200
- t.true(error.message.includes('cover page map extraction'))
201
- t.true(error.message.includes('Cover browser crashed'))
202
- t.true(coverPrinter.close.called)
203
- })
204
-
205
- test('throws PdfGenerationError when PDF file write fails', async (t) => {
206
- const { sandbox, mockReporter, mockProcessManager } = t.context
207
-
208
- const mockPrinter = createMockPrinter(sandbox)
209
- const MockPrinter = sandbox.stub().returns(mockPrinter)
210
-
211
- const mockFs = {
212
- existsSync: sandbox.stub().returns(false),
213
- mkdirsSync: sandbox.stub().throws(new Error('EACCES: permission denied')),
214
- promises: { writeFile: sandbox.stub() }
215
- }
216
-
217
- const paged = await esmock('./paged.js', {
218
- 'pagedjs-cli': MockPrinter,
219
- 'fs-extra': mockFs,
220
- '#lib/reporter/index.js': { default: mockReporter },
221
- '#lib/process/manager.js': { default: mockProcessManager }
222
- })
223
-
224
- const error = await t.throwsAsync(() =>
225
- paged.default(testPath('input.html'), testPath('covers.html'), testPath('restricted', 'output.pdf'), {})
226
- )
227
-
228
- t.is(error.code, 'PDF_FAILED')
229
- t.true(error.message.includes('Paged.js'))
230
- t.true(error.message.includes('PDF file write'))
231
- t.true(error.message.includes('permission denied'))
232
- })
233
-
234
- test('closes printer on successful completion when not in debug mode', async (t) => {
235
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
236
-
237
- const mockPrinter = createMockPrinter(sandbox)
238
- const MockPrinter = sandbox.stub().returns(mockPrinter)
239
-
240
- const paged = await esmock('./paged.js', {
241
- 'pagedjs-cli': MockPrinter,
242
- 'fs-extra': mockFs,
243
- '#lib/reporter/index.js': { default: mockReporter },
244
- '#lib/process/manager.js': { default: mockProcessManager },
245
- './split.js': { splitPdf: sandbox.stub().resolves({}) }
246
- })
247
-
248
- await paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), { debug: false })
249
-
250
- t.true(mockPrinter.close.called)
251
- t.true(mockProcessManager.onShutdownComplete.calledWith('pagedjs'))
252
- })
253
-
254
- test('keeps printer open when in debug mode', async (t) => {
255
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
256
-
257
- const mockPrinter = createMockPrinter(sandbox)
258
- const MockPrinter = sandbox.stub().returns(mockPrinter)
259
-
260
- const paged = await esmock('./paged.js', {
261
- 'pagedjs-cli': MockPrinter,
262
- 'fs-extra': mockFs,
263
- '#lib/reporter/index.js': { default: mockReporter },
264
- '#lib/process/manager.js': { default: mockProcessManager },
265
- './split.js': { splitPdf: sandbox.stub().resolves({}) }
266
- })
267
-
268
- await paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), { debug: true })
269
-
270
- t.false(mockPrinter.close.called)
271
- })
272
-
273
- test('registers console event handler for browser warnings and errors', async (t) => {
274
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
275
-
276
- const mockPrinter = createMockPrinter(sandbox)
277
- const MockPrinter = sandbox.stub().returns(mockPrinter)
278
-
279
- const mockDebug = sandbox.stub()
280
- const mockCreateDebug = sandbox.stub().returns(mockDebug)
281
-
282
- const paged = await esmock('./paged.js', {
283
- 'pagedjs-cli': MockPrinter,
284
- 'fs-extra': mockFs,
285
- '#lib/reporter/index.js': { default: mockReporter },
286
- '#lib/process/manager.js': { default: mockProcessManager },
287
- '#debug': { default: mockCreateDebug },
288
- './split.js': { splitPdf: sandbox.stub().resolves({}) }
289
- })
290
-
291
- await paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
292
-
293
- // Verify console handler was registered
294
- const consoleHandler = mockPrinter.on.getCalls().find(call => call.args[0] === 'console')
295
- t.truthy(consoleHandler, 'console event handler should be registered')
296
-
297
- // Simulate console warning
298
- const handler = consoleHandler.args[1]
299
- handler('warning', 'CSS property not supported')
300
-
301
- t.true(mockDebug.calledWith('browser %s: %s', 'warning', 'CSS property not supported'))
302
- })
303
-
304
- test('logs browser errors via debug', async (t) => {
305
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
306
-
307
- const mockPrinter = createMockPrinter(sandbox)
308
- const MockPrinter = sandbox.stub().returns(mockPrinter)
309
-
310
- const mockDebug = sandbox.stub()
311
- const mockCreateDebug = sandbox.stub().returns(mockDebug)
312
-
313
- const paged = await esmock('./paged.js', {
314
- 'pagedjs-cli': MockPrinter,
315
- 'fs-extra': mockFs,
316
- '#lib/reporter/index.js': { default: mockReporter },
317
- '#lib/process/manager.js': { default: mockProcessManager },
318
- '#debug': { default: mockCreateDebug },
319
- './split.js': { splitPdf: sandbox.stub().resolves({}) }
320
- })
321
-
322
- await paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
323
-
324
- // Get console handler
325
- const consoleHandler = mockPrinter.on.getCalls().find(call => call.args[0] === 'console')
326
- const handler = consoleHandler.args[1]
327
-
328
- // Simulate console error
329
- handler('error', 'Script error:', 'undefined is not a function')
330
-
331
- t.true(mockDebug.calledWith('browser %s: %s', 'error', 'Script error: undefined is not a function'))
332
- })
333
-
334
- test('ignores non-warning/error console messages', async (t) => {
335
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
336
-
337
- const mockPrinter = createMockPrinter(sandbox)
338
- const MockPrinter = sandbox.stub().returns(mockPrinter)
339
-
340
- const mockDebug = sandbox.stub()
341
- const mockCreateDebug = sandbox.stub().returns(mockDebug)
342
-
343
- const paged = await esmock('./paged.js', {
344
- 'pagedjs-cli': MockPrinter,
345
- 'fs-extra': mockFs,
346
- '#lib/reporter/index.js': { default: mockReporter },
347
- '#lib/process/manager.js': { default: mockProcessManager },
348
- '#debug': { default: mockCreateDebug },
349
- './split.js': { splitPdf: sandbox.stub().resolves({}) }
350
- })
351
-
352
- await paged.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
353
-
354
- // Get console handler
355
- const consoleHandler = mockPrinter.on.getCalls().find(call => call.args[0] === 'console')
356
- const handler = consoleHandler.args[1]
357
-
358
- // Reset debug stub to check only calls after handler invocation
359
- mockDebug.resetHistory()
360
-
361
- // Simulate console log (should be ignored)
362
- handler('log', 'Debug info')
363
-
364
- // Should not have logged this message (info/log messages are ignored)
365
- t.false(mockDebug.calledWith('browser %s: %s', 'log', 'Debug info'))
366
- })
@@ -1,202 +0,0 @@
1
- import esmock from 'esmock'
2
- import path from 'node:path'
3
- import sinon from 'sinon'
4
- import test from 'ava'
5
-
6
- /**
7
- * Prince PDF Engine Integration Tests
8
- *
9
- * Tests error handling paths that are difficult to trigger in E2E tests.
10
- * The happy path is covered by E2E tests with real Prince installation.
11
- */
12
-
13
- /**
14
- * Create a cross-platform absolute path for testing
15
- * @param {...string} segments - Path segments to join
16
- * @returns {string} Platform-appropriate absolute path
17
- */
18
- function testPath(...segments) {
19
- const root = process.platform === 'win32' ? 'C:\\' : '/'
20
- return path.join(root, ...segments)
21
- }
22
-
23
- test.beforeEach((t) => {
24
- t.context.sandbox = sinon.createSandbox()
25
-
26
- // Mock reporter to prevent actual console output
27
- t.context.mockReporter = {
28
- update: t.context.sandbox.stub(),
29
- warn: t.context.sandbox.stub()
30
- }
31
-
32
- // Mock processManager
33
- t.context.mockProcessManager = {
34
- signal: new AbortController().signal
35
- }
36
-
37
- // Mock fs-extra
38
- t.context.mockFs = {
39
- existsSync: t.context.sandbox.stub().returns(true),
40
- mkdirsSync: t.context.sandbox.stub(),
41
- readFileSync: t.context.sandbox.stub().returns(Buffer.from([1, 2, 3])),
42
- promises: {
43
- writeFile: t.context.sandbox.stub().resolves()
44
- }
45
- }
46
- })
47
-
48
- test.afterEach.always((t) => {
49
- t.context.sandbox.restore()
50
- })
51
-
52
- // ─────────────────────────────────────────────────────────────────────────────
53
- // Error handling tests
54
- // ─────────────────────────────────────────────────────────────────────────────
55
-
56
- test('throws PdfGenerationError when page map generation fails', async (t) => {
57
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
58
-
59
- const execaError = new Error('Prince failed')
60
- execaError.stderr = 'error: invalid HTML at line 42'
61
-
62
- const prince = await esmock('./prince.js', {
63
- execa: { execa: sandbox.stub().rejects(execaError) },
64
- 'fs-extra': mockFs,
65
- '#lib/reporter/index.js': { default: mockReporter },
66
- '#lib/process/manager.js': { default: mockProcessManager }
67
- })
68
-
69
- const error = await t.throwsAsync(() =>
70
- prince.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
71
- )
72
-
73
- t.is(error.code, 'PDF_FAILED')
74
- t.true(error.message.includes('Prince'))
75
- t.true(error.message.includes('page map generation'))
76
- t.true(error.message.includes('invalid HTML at line 42'))
77
- })
78
-
79
- test('throws PdfGenerationError when PDF printing fails', async (t) => {
80
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
81
-
82
- // First call (page map) succeeds, second call (print) fails
83
- const execaStub = sandbox.stub()
84
- execaStub.onCall(0).resolves({ stdout: '{}' }) // page map succeeds
85
- const printError = new Error('Prince print failed')
86
- printError.stderr = 'error: out of memory'
87
- execaStub.onCall(1).rejects(printError)
88
-
89
- const prince = await esmock('./prince.js', {
90
- execa: { execa: execaStub },
91
- 'fs-extra': mockFs,
92
- '#lib/reporter/index.js': { default: mockReporter },
93
- '#lib/process/manager.js': { default: mockProcessManager }
94
- })
95
-
96
- const error = await t.throwsAsync(() =>
97
- prince.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
98
- )
99
-
100
- t.is(error.code, 'PDF_FAILED')
101
- t.true(error.message.includes('Prince'))
102
- t.true(error.message.includes('PDF printing'))
103
- t.true(error.message.includes('out of memory'))
104
- })
105
-
106
- test('throws PdfGenerationError when cover page map generation fails', async (t) => {
107
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
108
-
109
- // First call (page map) succeeds, second call (covers) fails
110
- const execaStub = sandbox.stub()
111
- execaStub.onCall(0).resolves({ stdout: '{"page-1": {"startPage": 0}}' })
112
- const coversError = new Error('Prince covers failed')
113
- coversError.stderr = 'error: covers file corrupt'
114
- execaStub.onCall(1).rejects(coversError)
115
-
116
- const prince = await esmock('./prince.js', {
117
- execa: { execa: execaStub },
118
- 'fs-extra': mockFs,
119
- '#lib/reporter/index.js': { default: mockReporter },
120
- '#lib/process/manager.js': { default: mockProcessManager }
121
- })
122
-
123
- // Enable cover page generation
124
- const options = {
125
- pdfConfig: { pagePDF: { coverPage: true } }
126
- }
127
-
128
- const error = await t.throwsAsync(() =>
129
- prince.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), options)
130
- )
131
-
132
- t.is(error.code, 'PDF_FAILED')
133
- t.true(error.message.includes('Prince'))
134
- t.true(error.message.includes('cover page map generation'))
135
- t.true(error.message.includes('covers file corrupt'))
136
- })
137
-
138
- test('warns and returns when page map generation is cancelled', async (t) => {
139
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
140
-
141
- const cancelError = new Error('Cancelled')
142
- cancelError.isCanceled = true
143
-
144
- const prince = await esmock('./prince.js', {
145
- execa: { execa: sandbox.stub().rejects(cancelError) },
146
- 'fs-extra': mockFs,
147
- '#lib/reporter/index.js': { default: mockReporter },
148
- '#lib/process/manager.js': { default: mockProcessManager }
149
- })
150
-
151
- const result = await prince.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
152
-
153
- t.is(result, undefined)
154
- t.true(mockReporter.warn.calledWith('PDF generation cancelled'))
155
- })
156
-
157
- test('warns and returns when PDF printing is cancelled', async (t) => {
158
- const { sandbox, mockReporter, mockProcessManager, mockFs } = t.context
159
-
160
- const execaStub = sandbox.stub()
161
- execaStub.onCall(0).resolves({ stdout: '{}' }) // page map succeeds
162
- const cancelError = new Error('Cancelled')
163
- cancelError.isCanceled = true
164
- execaStub.onCall(1).rejects(cancelError)
165
-
166
- const prince = await esmock('./prince.js', {
167
- execa: { execa: execaStub },
168
- 'fs-extra': mockFs,
169
- '#lib/reporter/index.js': { default: mockReporter },
170
- '#lib/process/manager.js': { default: mockProcessManager }
171
- })
172
-
173
- const result = await prince.default(testPath('input.html'), testPath('covers.html'), testPath('output.pdf'), {})
174
-
175
- t.is(result, undefined)
176
- t.true(mockReporter.warn.calledWith('PDF generation cancelled'))
177
- })
178
-
179
- test('throws PdfGenerationError when output directory creation fails', async (t) => {
180
- const { sandbox, mockReporter, mockProcessManager } = t.context
181
-
182
- const mockFs = {
183
- existsSync: sandbox.stub().returns(false),
184
- mkdirsSync: sandbox.stub().throws(new Error('EACCES: permission denied'))
185
- }
186
-
187
- const prince = await esmock('./prince.js', {
188
- execa: { execa: sandbox.stub() },
189
- 'fs-extra': mockFs,
190
- '#lib/reporter/index.js': { default: mockReporter },
191
- '#lib/process/manager.js': { default: mockProcessManager }
192
- })
193
-
194
- const error = await t.throwsAsync(() =>
195
- prince.default(testPath('input.html'), testPath('covers.html'), testPath('restricted', 'output.pdf'), {})
196
- )
197
-
198
- t.is(error.code, 'PDF_FAILED')
199
- t.true(error.message.includes('Prince'))
200
- t.true(error.message.includes('output directory creation'))
201
- t.true(error.message.includes('permission denied'))
202
- })
@@ -1,21 +0,0 @@
1
- /**
2
- * PDF engine schema definition
3
- *
4
- * This module has NO imports to avoid circular dependencies.
5
- * It serves as the single source of truth for PDF engine options.
6
- */
7
-
8
- /**
9
- * Supported PDF engines
10
- * @type {string[]}
11
- */
12
- export const ENGINES = ['pagedjs', 'prince']
13
-
14
- /**
15
- * Schema definition for pdfEngine config property
16
- */
17
- export const schema = {
18
- type: 'string',
19
- enum: ENGINES,
20
- description: 'Default PDF engine to use (pagedjs, prince)'
21
- }