chromiumly 2.7.0 → 2.8.0

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 (44) hide show
  1. package/README.md +93 -8
  2. package/dist/chromium/index.d.ts +3 -0
  3. package/dist/chromium/index.js +7 -1
  4. package/dist/chromium/index.js.map +1 -1
  5. package/dist/chromium/interfaces/screenshot.types.d.ts +10 -0
  6. package/dist/chromium/interfaces/screenshot.types.js +3 -0
  7. package/dist/chromium/interfaces/screenshot.types.js.map +1 -0
  8. package/dist/chromium/screenshots/html.screenshot.d.ts +54 -0
  9. package/dist/chromium/screenshots/html.screenshot.js +67 -0
  10. package/dist/chromium/screenshots/html.screenshot.js.map +1 -0
  11. package/dist/chromium/screenshots/markdown.screenshot.d.ts +52 -0
  12. package/dist/chromium/screenshots/markdown.screenshot.js +66 -0
  13. package/dist/chromium/screenshots/markdown.screenshot.js.map +1 -0
  14. package/dist/chromium/screenshots/screenshot.d.ts +18 -0
  15. package/dist/chromium/screenshots/screenshot.js +21 -0
  16. package/dist/chromium/screenshots/screenshot.js.map +1 -0
  17. package/dist/chromium/screenshots/url.screenshot.d.ts +50 -0
  18. package/dist/chromium/screenshots/url.screenshot.js +66 -0
  19. package/dist/chromium/screenshots/url.screenshot.js.map +1 -0
  20. package/dist/chromium/utils/screenshot.utils.d.ts +22 -0
  21. package/dist/chromium/utils/screenshot.utils.js +75 -0
  22. package/dist/chromium/utils/screenshot.utils.js.map +1 -0
  23. package/dist/main.config.d.ts +5 -0
  24. package/dist/main.config.js +5 -0
  25. package/dist/main.config.js.map +1 -1
  26. package/dist/main.d.ts +1 -1
  27. package/dist/main.js +4 -1
  28. package/dist/main.js.map +1 -1
  29. package/package.json +4 -4
  30. package/src/chromium/index.ts +3 -0
  31. package/src/chromium/interfaces/screenshot.types.ts +12 -0
  32. package/src/chromium/screenshots/html.screenshot.ts +100 -0
  33. package/src/chromium/screenshots/markdown.screenshot.ts +95 -0
  34. package/src/chromium/screenshots/screenshot.ts +22 -0
  35. package/src/chromium/screenshots/tests/html.screenshot.test.ts +192 -0
  36. package/src/chromium/screenshots/tests/markdown.screenshot.test.ts +176 -0
  37. package/src/chromium/screenshots/tests/url.screenshot.test.ts +166 -0
  38. package/src/chromium/screenshots/url.screenshot.ts +91 -0
  39. package/src/chromium/utils/screenshot.utils.ts +115 -0
  40. package/src/chromium/utils/tests/converter.utils.test.ts +13 -1
  41. package/src/chromium/utils/tests/screenshot.utils.test.ts +284 -0
  42. package/src/common/tests/gotenberg.utils.test.ts +46 -0
  43. package/src/main.config.ts +6 -0
  44. package/src/main.ts +8 -1
@@ -0,0 +1,22 @@
1
+ import { ImageProperties, ScreenshotOptions } from './../interfaces/screenshot.types';
2
+ import FormData from 'form-data';
3
+ /**
4
+ * Utility class for handling common tasks related to screenshot.
5
+ */
6
+ export declare class ScreenshotUtils {
7
+ /**
8
+ * Adds page properties to the FormData object based on the provided imageProperties.
9
+ *
10
+ * @param {FormData} data - The FormData object to which page properties will be added.
11
+ * @param {ImageProperties} imageProperties - The page properties to be added to the FormData.
12
+ */
13
+ static addImageProperties(data: FormData, imageProperties: ImageProperties): void;
14
+ /**
15
+ * Customizes the FormData object based on the provided screenshot options.
16
+ *
17
+ * @param {FormData} data - The FormData object to be customized.
18
+ * @param {ScreenshotOptions} options - The screenshot options to apply to the FormData.
19
+ * @returns {Promise<void>} A Promise that resolves once the customization is complete.
20
+ */
21
+ static customize(data: FormData, options: ScreenshotOptions): Promise<void>;
22
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScreenshotUtils = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("../../common");
6
+ /**
7
+ * Utility class for handling common tasks related to screenshot.
8
+ */
9
+ class ScreenshotUtils {
10
+ /**
11
+ * Adds page properties to the FormData object based on the provided imageProperties.
12
+ *
13
+ * @param {FormData} data - The FormData object to which page properties will be added.
14
+ * @param {ImageProperties} imageProperties - The page properties to be added to the FormData.
15
+ */
16
+ static addImageProperties(data, imageProperties) {
17
+ data.append('format', imageProperties.format);
18
+ if (imageProperties.quality) {
19
+ common_1.GotenbergUtils.assert(imageProperties.format === 'jpeg', 'Compression quality is exclusively supported for JPEG format.');
20
+ common_1.GotenbergUtils.assert(imageProperties.quality >= 0 && imageProperties.quality <= 100, 'Invalid compression quality. Please provide a value between 0 and 100.');
21
+ data.append('quality', imageProperties.quality);
22
+ }
23
+ if (imageProperties.omitBackground) {
24
+ data.append('omitBackground', String(imageProperties.omitBackground));
25
+ }
26
+ }
27
+ /**
28
+ * Customizes the FormData object based on the provided screenshot options.
29
+ *
30
+ * @param {FormData} data - The FormData object to be customized.
31
+ * @param {ScreenshotOptions} options - The screenshot options to apply to the FormData.
32
+ * @returns {Promise<void>} A Promise that resolves once the customization is complete.
33
+ */
34
+ static customize(data, options) {
35
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
36
+ if (options.header) {
37
+ const { header } = options;
38
+ yield common_1.GotenbergUtils.addFile(data, header, 'header.html');
39
+ }
40
+ if (options.footer) {
41
+ const { footer } = options;
42
+ yield common_1.GotenbergUtils.addFile(data, footer, 'footer.html');
43
+ }
44
+ if (options.emulatedMediaType) {
45
+ data.append('emulatedMediaType', options.emulatedMediaType);
46
+ }
47
+ if (options.properties) {
48
+ ScreenshotUtils.addImageProperties(data, options.properties);
49
+ }
50
+ if (options.waitDelay) {
51
+ data.append('waitDelay', options.waitDelay);
52
+ }
53
+ if (options.waitForExpression) {
54
+ data.append('waitForExpression', options.waitForExpression);
55
+ }
56
+ if (options.extraHttpHeaders) {
57
+ data.append('extraHttpHeaders', JSON.stringify(options.extraHttpHeaders));
58
+ }
59
+ if (options.failOnHttpStatusCodes) {
60
+ data.append('failOnHttpStatusCodes', JSON.stringify(options.failOnHttpStatusCodes));
61
+ }
62
+ if (options.failOnConsoleExceptions) {
63
+ data.append('failOnConsoleExceptions', String(options.failOnConsoleExceptions));
64
+ }
65
+ if (options.skipNetworkIdleEvent) {
66
+ data.append('skipNetworkIdleEvent', String(options.skipNetworkIdleEvent));
67
+ }
68
+ if (options.optimizeForSpeed) {
69
+ data.append('optimizeForSpeed', String(options.optimizeForSpeed));
70
+ }
71
+ });
72
+ }
73
+ }
74
+ exports.ScreenshotUtils = ScreenshotUtils;
75
+ //# sourceMappingURL=screenshot.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot.utils.js","sourceRoot":"","sources":["../../../src/chromium/utils/screenshot.utils.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,11 @@ export declare class Chromiumly {
35
35
  * @type {string}
36
36
  */
37
37
  static readonly CHROMIUM_CONVERT_PATH = "forms/chromium/convert";
38
+ /**
39
+ * The path for Chromium-related screenshots.
40
+ * @type {string}
41
+ */
42
+ static readonly CHROMIUM_SCREENSHOT_PATH = "forms/chromium/screenshot";
38
43
  /**
39
44
  * The path for PDF engine-related operations.
40
45
  * @type {string}
@@ -44,6 +44,11 @@ Chromiumly.GOTENBERG_ENDPOINT = gotenberg_1.Gotenberg.endpoint;
44
44
  * @type {string}
45
45
  */
46
46
  Chromiumly.CHROMIUM_CONVERT_PATH = 'forms/chromium/convert';
47
+ /**
48
+ * The path for Chromium-related screenshots.
49
+ * @type {string}
50
+ */
51
+ Chromiumly.CHROMIUM_SCREENSHOT_PATH = 'forms/chromium/screenshot';
47
52
  /**
48
53
  * The path for PDF engine-related operations.
49
54
  * @type {string}
@@ -1 +1 @@
1
- {"version":3,"file":"main.config.js","sourceRoot":"","sources":["../src/main.config.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AAExC;;;GAGG;AACH,IAAY,aAIX;AAJD,WAAY,aAAa;IACrB,4BAAW,CAAA;IACX,8BAAa,CAAA;IACb,sCAAqB,CAAA;AACzB,CAAC,EAJW,aAAa,6BAAb,aAAa,QAIxB;AAED;;;GAGG;AACH,IAAK,cAEJ;AAFD,WAAK,cAAc;IACf,iCAAe,CAAA;AACnB,CAAC,EAFI,cAAc,KAAd,cAAc,QAElB;AAED;;;GAGG;AACH,IAAK,gBAEJ;AAFD,WAAK,gBAAgB;IACjB,uCAAmB,CAAA;AACvB,CAAC,EAFI,gBAAgB,KAAhB,gBAAgB,QAEpB;AAED;;GAEG;AACH,MAAa,UAAU;;AAAvB,gCAkDC;AAjDG;;;GAGG;AACoB,6BAAkB,GAAG,qBAAS,CAAC,QAAQ,CAAC;AAE/D;;;GAGG;AACoB,gCAAqB,GAAG,wBAAwB,CAAC;AAExE;;;GAGG;AACoB,2BAAgB,GAAG,kBAAkB,CAAC;AAE7D;;;GAGG;AACoB,4BAAiB,GAAG,mBAAmB,CAAC;AAE/D;;;GAGG;AACoB,0BAAe,GAAG;IACrC,GAAG,EAAE,aAAa,CAAC,GAAG;IACtB,IAAI,EAAE,aAAa,CAAC,IAAI;IACxB,QAAQ,EAAE,aAAa,CAAC,QAAQ;CACnC,CAAC;AAEF;;;GAGG;AACoB,4BAAiB,GAAG;IACvC,KAAK,EAAE,cAAc,CAAC,KAAK;CAC9B,CAAC;AAEF;;;GAGG;AACoB,8BAAmB,GAAG;IACzC,OAAO,EAAE,gBAAgB,CAAC,OAAO;CACpC,CAAC"}
1
+ {"version":3,"file":"main.config.js","sourceRoot":"","sources":["../src/main.config.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AAExC;;;GAGG;AACH,IAAY,aAIX;AAJD,WAAY,aAAa;IACrB,4BAAW,CAAA;IACX,8BAAa,CAAA;IACb,sCAAqB,CAAA;AACzB,CAAC,EAJW,aAAa,6BAAb,aAAa,QAIxB;AAED;;;GAGG;AACH,IAAK,cAEJ;AAFD,WAAK,cAAc;IACf,iCAAe,CAAA;AACnB,CAAC,EAFI,cAAc,KAAd,cAAc,QAElB;AAED;;;GAGG;AACH,IAAK,gBAEJ;AAFD,WAAK,gBAAgB;IACjB,uCAAmB,CAAA;AACvB,CAAC,EAFI,gBAAgB,KAAhB,gBAAgB,QAEpB;AAED;;GAEG;AACH,MAAa,UAAU;;AAAvB,gCAwDC;AAvDG;;;GAGG;AACoB,6BAAkB,GAAG,qBAAS,CAAC,QAAQ,CAAC;AAE/D;;;GAGG;AACoB,gCAAqB,GAAG,wBAAwB,CAAC;AAExE;;;GAGG;AACoB,mCAAwB,GAC3C,2BAA2B,CAAC;AAChC;;;GAGG;AACoB,2BAAgB,GAAG,kBAAkB,CAAC;AAE7D;;;GAGG;AACoB,4BAAiB,GAAG,mBAAmB,CAAC;AAE/D;;;GAGG;AACoB,0BAAe,GAAG;IACrC,GAAG,EAAE,aAAa,CAAC,GAAG;IACtB,IAAI,EAAE,aAAa,CAAC,IAAI;IACxB,QAAQ,EAAE,aAAa,CAAC,QAAQ;CACnC,CAAC;AAEF;;;GAGG;AACoB,4BAAiB,GAAG;IACvC,KAAK,EAAE,cAAc,CAAC,KAAK;CAC9B,CAAC;AAEF;;;GAGG;AACoB,8BAAmB,GAAG;IACzC,OAAO,EAAE,gBAAgB,CAAC,OAAO;CACpC,CAAC"}
package/dist/main.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { PdfFormat } from './common/constants';
2
- export { HtmlConverter, MarkdownConverter, UrlConverter } from './chromium';
2
+ export { HtmlConverter, HtmlScreenshot, MarkdownConverter, MarkdownScreenshot, UrlConverter, UrlScreenshot } from './chromium';
3
3
  export { PDFEngine } from './pdf-engines';
package/dist/main.js CHANGED
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PDFEngine = exports.UrlConverter = exports.MarkdownConverter = exports.HtmlConverter = exports.PdfFormat = void 0;
3
+ exports.PDFEngine = exports.UrlScreenshot = exports.UrlConverter = exports.MarkdownScreenshot = exports.MarkdownConverter = exports.HtmlScreenshot = exports.HtmlConverter = exports.PdfFormat = void 0;
4
4
  var constants_1 = require("./common/constants");
5
5
  Object.defineProperty(exports, "PdfFormat", { enumerable: true, get: function () { return constants_1.PdfFormat; } });
6
6
  var chromium_1 = require("./chromium");
7
7
  Object.defineProperty(exports, "HtmlConverter", { enumerable: true, get: function () { return chromium_1.HtmlConverter; } });
8
+ Object.defineProperty(exports, "HtmlScreenshot", { enumerable: true, get: function () { return chromium_1.HtmlScreenshot; } });
8
9
  Object.defineProperty(exports, "MarkdownConverter", { enumerable: true, get: function () { return chromium_1.MarkdownConverter; } });
10
+ Object.defineProperty(exports, "MarkdownScreenshot", { enumerable: true, get: function () { return chromium_1.MarkdownScreenshot; } });
9
11
  Object.defineProperty(exports, "UrlConverter", { enumerable: true, get: function () { return chromium_1.UrlConverter; } });
12
+ Object.defineProperty(exports, "UrlScreenshot", { enumerable: true, get: function () { return chromium_1.UrlScreenshot; } });
10
13
  var pdf_engines_1 = require("./pdf-engines");
11
14
  Object.defineProperty(exports, "PDFEngine", { enumerable: true, get: function () { return pdf_engines_1.PDFEngine; } });
12
15
  //# sourceMappingURL=main.js.map
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;AAAA,gDAA+C;AAAtC,sGAAA,SAAS,OAAA;AAClB,uCAA4E;AAAnE,yGAAA,aAAa,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAAE,wGAAA,YAAY,OAAA;AACvD,6CAA0C;AAAjC,wGAAA,SAAS,OAAA"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;;AAAA,gDAA+C;AAAtC,sGAAA,SAAS,OAAA;AAClB,uCAOoB;AANhB,yGAAA,aAAa,OAAA;AACb,0GAAA,cAAc,OAAA;AACd,6GAAA,iBAAiB,OAAA;AACjB,8GAAA,kBAAkB,OAAA;AAClB,wGAAA,YAAY,OAAA;AACZ,yGAAA,aAAa,OAAA;AAEjB,6CAA0C;AAAjC,wGAAA,SAAS,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chromiumly",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
4
4
  "description": "A lightweight Typescript library that interacts with Gotenberg's different modules to convert a variety of document formats to PDF files.",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",
@@ -50,10 +50,10 @@
50
50
  "@types/dotenv": "8.2.0",
51
51
  "@types/form-data": "2.5.0",
52
52
  "@types/jest": "29.5.12",
53
- "@types/node": "20.11.20",
53
+ "@types/node": "20.11.24",
54
54
  "@types/node-fetch": "2.6.11",
55
- "@typescript-eslint/eslint-plugin": "7.1.0",
56
- "@typescript-eslint/parser": "7.1.0",
55
+ "@typescript-eslint/eslint-plugin": "7.1.1",
56
+ "@typescript-eslint/parser": "7.1.1",
57
57
  "commitizen": "4.3.0",
58
58
  "eslint": "8.57.0",
59
59
  "eslint-config-prettier": "^9.1.0",
@@ -1,3 +1,6 @@
1
+ export { UrlScreenshot } from './screenshots/url.screenshot';
2
+ export { MarkdownScreenshot } from './screenshots/markdown.screenshot';
3
+ export { HtmlScreenshot } from './screenshots/html.screenshot';
1
4
  export { HtmlConverter } from './converters/html.converter';
2
5
  export { MarkdownConverter } from './converters/markdown.converter';
3
6
  export { UrlConverter } from './converters/url.converter';
@@ -0,0 +1,12 @@
1
+ import { ChromiumOptions } from './common.types';
2
+
3
+ export type ImageProperties = {
4
+ format: 'png' | 'jpeg' | 'webp'; //The image compression format, either "png", "jpeg" or "webp".
5
+ quality?: number; // The compression quality from range 0 to 100 (jpeg only).
6
+ omitBackground?: boolean; // Hide the default white background and allow generating screenshots with transparency.
7
+ };
8
+
9
+ export type ScreenshotOptions = ChromiumOptions & {
10
+ properties?: ImageProperties;
11
+ optimizeForSpeed?: boolean; // Define whether to optimize image encoding for speed, not for resulting size.
12
+ };
@@ -0,0 +1,100 @@
1
+ import FormData from 'form-data';
2
+
3
+ import { GotenbergUtils, PathLikeOrReadStream } from '../../common';
4
+ import { ChromiumRoute } from '../../main.config';
5
+ import { EmulatedMediaType } from '../interfaces/common.types';
6
+ import { ScreenshotUtils } from '../utils/screenshot.utils';
7
+ import { Screenshot } from './screenshot';
8
+ import { ImageProperties } from '../interfaces/screenshot.types';
9
+
10
+ /**
11
+ * Class representing an HTML Screenshot that extends the base Screenshot class.
12
+ * This class is used to screenshot HTML content using Gotenberg service.
13
+ *
14
+ * @extends Screenshot
15
+ */
16
+ export class HtmlScreenshot extends Screenshot {
17
+ /**
18
+ * Creates an instance of HtmlScreenshot.
19
+ * Initializes the Screenshot with the HTML screenshot route.
20
+ */
21
+ constructor() {
22
+ super(ChromiumRoute.HTML);
23
+ }
24
+
25
+ /**
26
+ * Screenshots HTML content.
27
+ *
28
+ * @param {Object} options - Screenshot options.
29
+ * @param {PathLikeOrReadStream} options.html - PathLike or ReadStream of the HTML content to be screenshoted.
30
+ * @param {PathLikeOrReadStream} [options.header] - PathLike or ReadStream of the header HTML content.
31
+ * @param {PathLikeOrReadStream} [options.footer] - PathLike or ReadStream of the footer HTML content.
32
+ * @param {ImageProperties} [options.properties] - Image properties for the screenshot.
33
+ * @param {EmulatedMediaType} [options.emulatedMediaType] - Emulated media type for the screenshot.
34
+ * @param {string} [options.waitDelay] - Delay before the screenshot process starts.
35
+ * @param {string} [options.waitForExpression] - JavaScript expression to wait for before completing the screenshot.
36
+ * @param {Record<string, string>} [options.extraHttpHeaders] - Additional HTTP headers for the screenshot.
37
+ * @param {number []} [options.failOnHttpStatusCodes] - Whether to fail on HTTP status code.
38
+ * @param {boolean} [options.failOnConsoleExceptions] - Whether to fail on console exceptions during screenshot.
39
+ * @param {boolean} [options.skipNetworkIdleEvent] - Whether to skip network idle event.
40
+ * @param {boolean} [options.optimizeForSpeed] - Whether to optimize for speed.
41
+ * @returns {Promise<Buffer>} A Promise resolving to the image buffer.
42
+ */
43
+ async capture({
44
+ html,
45
+ assets,
46
+ header,
47
+ footer,
48
+ properties,
49
+ emulatedMediaType,
50
+ waitDelay,
51
+ waitForExpression,
52
+ extraHttpHeaders,
53
+ failOnConsoleExceptions,
54
+ failOnHttpStatusCodes,
55
+ skipNetworkIdleEvent,
56
+ optimizeForSpeed
57
+ }: {
58
+ html: PathLikeOrReadStream;
59
+ assets?: { file: PathLikeOrReadStream; name: string }[];
60
+ header?: PathLikeOrReadStream;
61
+ footer?: PathLikeOrReadStream;
62
+ properties?: ImageProperties;
63
+ emulatedMediaType?: EmulatedMediaType;
64
+ waitDelay?: string;
65
+ waitForExpression?: string;
66
+ extraHttpHeaders?: Record<string, string>;
67
+ failOnConsoleExceptions?: boolean;
68
+ failOnHttpStatusCodes?: number[];
69
+ skipNetworkIdleEvent?: boolean;
70
+ optimizeForSpeed?: boolean;
71
+ }): Promise<Buffer> {
72
+ const data = new FormData();
73
+
74
+ await GotenbergUtils.addFile(data, html, 'index.html');
75
+
76
+ if (assets?.length) {
77
+ await Promise.all(
78
+ assets.map(({ file, name }) =>
79
+ GotenbergUtils.addFile(data, file, name)
80
+ )
81
+ );
82
+ }
83
+
84
+ await ScreenshotUtils.customize(data, {
85
+ header,
86
+ footer,
87
+ properties,
88
+ emulatedMediaType,
89
+ waitDelay,
90
+ waitForExpression,
91
+ extraHttpHeaders,
92
+ failOnHttpStatusCodes,
93
+ failOnConsoleExceptions,
94
+ skipNetworkIdleEvent,
95
+ optimizeForSpeed
96
+ });
97
+
98
+ return GotenbergUtils.fetch(this.endpoint, data);
99
+ }
100
+ }
@@ -0,0 +1,95 @@
1
+ import FormData from 'form-data';
2
+
3
+ import { GotenbergUtils, PathLikeOrReadStream } from '../../common';
4
+ import { ImageProperties } from '../interfaces/screenshot.types';
5
+ import { ScreenshotUtils } from '../utils/screenshot.utils';
6
+ import { Screenshot } from './screenshot';
7
+ import { ChromiumRoute } from '../../main.config';
8
+ import { EmulatedMediaType } from '../interfaces/common.types';
9
+
10
+ /**
11
+ * Class representing a Markdown screenshot that extends the base Screenshot class.
12
+ * This class is used to screenshots HTML with markdown content using Gotenberg service.
13
+ *
14
+ * @extends Screenshot
15
+ */
16
+ export class MarkdownScreenshot extends Screenshot {
17
+ /**
18
+ * Creates an instance of MarkdownScreenshot.
19
+ * Initializes the Screenshot with the Markdown screenshot route.
20
+ */
21
+ constructor() {
22
+ super(ChromiumRoute.MARKDOWN);
23
+ }
24
+
25
+ /**
26
+ * Screenshots HTML with markdown.
27
+ *
28
+ * @param {Object} options - Screenshot options.
29
+ * @param {PathLikeOrReadStream} options.html - PathLike or ReadStream of the HTML content to be screenshoted.
30
+ * @param {PathLikeOrReadStream} options.markdown - PathLike or ReadStream of the Markdown content to be screenshoted.
31
+ * @param {PathLikeOrReadStream} [options.header] - PathLike or ReadStream of the header HTML content.
32
+ * @param {PathLikeOrReadStream} [options.footer] - PathLike or ReadStream of the footer HTML content.
33
+ * @param {ImageProperties} [options.properties] - Image properties for the screenshot.
34
+ * @param {EmulatedMediaType} [options.emulatedMediaType] - Emulated media type for the screenshot.
35
+ * @param {string} [options.waitDelay] - Delay before the screenshot process starts.
36
+ * @param {string} [options.waitForExpression] - JavaScript expression to wait for before completing the screenshot.
37
+ * @param {Record<string, string>} [options.extraHttpHeaders] - Additional HTTP headers for the screenshot.
38
+ * @param {number []} [options.failOnHttpStatusCodes] - Whether to fail on HTTP status code.
39
+ * @param {boolean} [options.failOnConsoleExceptions] - Whether to fail on console exceptions during screenshot.
40
+ * @param {boolean} [options.skipNetworkIdleEvent] - Whether to skip network idle event.
41
+ * @param {boolean} [options.optimizeForSpeed] - Whether to optimize for speed.
42
+ * @returns {Promise<Buffer>} A Promise resolving to the image buffer.
43
+ */
44
+ async capture({
45
+ html,
46
+ markdown,
47
+ header,
48
+ footer,
49
+ properties,
50
+ emulatedMediaType,
51
+ waitDelay,
52
+ waitForExpression,
53
+ extraHttpHeaders,
54
+ failOnHttpStatusCodes,
55
+ failOnConsoleExceptions,
56
+ skipNetworkIdleEvent,
57
+ optimizeForSpeed
58
+ }: {
59
+ html: PathLikeOrReadStream;
60
+ markdown: PathLikeOrReadStream;
61
+ header?: PathLikeOrReadStream;
62
+ footer?: PathLikeOrReadStream;
63
+ properties?: ImageProperties;
64
+ emulatedMediaType?: EmulatedMediaType;
65
+ waitDelay?: string;
66
+ waitForExpression?: string;
67
+ extraHttpHeaders?: Record<string, string>;
68
+ failOnHttpStatusCodes?: number[];
69
+ failOnConsoleExceptions?: boolean;
70
+ skipNetworkIdleEvent?: boolean;
71
+ optimizeForSpeed?: boolean;
72
+ }): Promise<Buffer> {
73
+ const data = new FormData();
74
+
75
+ await GotenbergUtils.addFile(data, html, 'index.html');
76
+
77
+ await GotenbergUtils.addFile(data, markdown, 'file.md');
78
+
79
+ await ScreenshotUtils.customize(data, {
80
+ header,
81
+ footer,
82
+ properties,
83
+ emulatedMediaType,
84
+ waitDelay,
85
+ waitForExpression,
86
+ extraHttpHeaders,
87
+ failOnHttpStatusCodes,
88
+ failOnConsoleExceptions,
89
+ skipNetworkIdleEvent,
90
+ optimizeForSpeed
91
+ });
92
+
93
+ return GotenbergUtils.fetch(this.endpoint, data);
94
+ }
95
+ }
@@ -0,0 +1,22 @@
1
+ import { Chromiumly, ChromiumRoute } from '../../main.config';
2
+
3
+ /**
4
+ * Abstract class representing a generic screenshot.
5
+ * Concrete screenshot classes should extend this class and implement specific screenshot logic.
6
+ */
7
+ export abstract class Screenshot {
8
+ /**
9
+ * The endpoint URL for the screenshot.
10
+ */
11
+ readonly endpoint: string;
12
+
13
+ /**
14
+ * Creates an instance of the screenshot class.
15
+ * Initializes the endpoint URL based on the provided ChromiumRoute.
16
+ *
17
+ * @param {ChromiumRoute} route - The ChromiumRoute enum value representing the screenshot route.
18
+ */
19
+ constructor(route: ChromiumRoute) {
20
+ this.endpoint = `${Chromiumly.GOTENBERG_ENDPOINT}/${Chromiumly.CHROMIUM_SCREENSHOT_PATH}/${Chromiumly.CHROMIUM_ROUTES[route]}`;
21
+ }
22
+ }
@@ -0,0 +1,192 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2
+ import { createReadStream, promises } from 'fs';
3
+
4
+ import fetch from 'node-fetch';
5
+ import FormData from 'form-data';
6
+
7
+ import { HtmlScreenshot } from '../html.screenshot';
8
+
9
+ const { Response } = jest.requireActual('node-fetch');
10
+ jest.mock('node-fetch', () => jest.fn());
11
+
12
+ describe('HtmlScreenshot', () => {
13
+ const screenshot = new HtmlScreenshot();
14
+
15
+ describe('endpoint', () => {
16
+ it('should route to Chromium HTML route', () => {
17
+ expect(screenshot.endpoint).toEqual(
18
+ 'http://localhost:3000/forms/chromium/screenshot/html'
19
+ );
20
+ });
21
+ });
22
+
23
+ describe('capture', () => {
24
+ const mockPromisesAccess = jest.spyOn(promises, 'access');
25
+ const mockFetch = fetch as jest.MockedFunction<typeof fetch>;
26
+ const mockFormDataAppend = jest.spyOn(FormData.prototype, 'append');
27
+
28
+ const assets = [
29
+ { file: Buffer.from('asset1'), name: 'asset1' },
30
+ { file: Buffer.from('asset2'), name: 'asset2' }
31
+ ];
32
+
33
+ beforeEach(() => {
34
+ (createReadStream as jest.Mock) = jest.fn();
35
+ });
36
+
37
+ afterEach(() => {
38
+ jest.resetAllMocks();
39
+ });
40
+
41
+ describe('when html parameter is passed', () => {
42
+ it('should return a buffer', async () => {
43
+ mockFetch.mockResolvedValue(new Response('content'));
44
+ const buffer = await screenshot.capture({
45
+ html: Buffer.from('data')
46
+ });
47
+ expect(buffer).toEqual(Buffer.from('content'));
48
+ });
49
+ });
50
+
51
+ describe('when image properties parameter is passed', () => {
52
+ it('should return a buffer', async () => {
53
+ mockFetch.mockResolvedValue(new Response('content'));
54
+ const buffer = await screenshot.capture({
55
+ html: Buffer.from('data'),
56
+ properties: { format: 'jpeg', quality: 50 }
57
+ });
58
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(3);
59
+ expect(buffer).toEqual(Buffer.from('content'));
60
+ });
61
+ });
62
+
63
+ describe('when header parameter is passed', () => {
64
+ it('should return a buffer', async () => {
65
+ mockFetch.mockResolvedValue(new Response('content'));
66
+ const buffer = await screenshot.capture({
67
+ html: Buffer.from('data'),
68
+ header: Buffer.from('header')
69
+ });
70
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(2);
71
+ expect(buffer).toEqual(Buffer.from('content'));
72
+ });
73
+ });
74
+
75
+ describe('when footer parameter is passed', () => {
76
+ it('should return a buffer', async () => {
77
+ mockFetch.mockResolvedValue(new Response('content'));
78
+ const buffer = await screenshot.capture({
79
+ html: Buffer.from('data'),
80
+ footer: Buffer.from('footer')
81
+ });
82
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(2);
83
+ expect(buffer).toEqual(Buffer.from('content'));
84
+ });
85
+ });
86
+
87
+ describe('when assets parameter is passed', () => {
88
+ it('should return a buffer', async () => {
89
+ mockFetch.mockResolvedValue(new Response('content'));
90
+ const buffer = await screenshot.capture({
91
+ html: Buffer.from('data'),
92
+ assets
93
+ });
94
+
95
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(3);
96
+ expect(buffer).toEqual(Buffer.from('content'));
97
+ });
98
+ });
99
+
100
+ describe('when emulatedMediaType parameter is passed', () => {
101
+ it('should return a buffer', async () => {
102
+ mockFetch.mockResolvedValue(new Response('content'));
103
+ const buffer = await screenshot.capture({
104
+ html: Buffer.from('data'),
105
+ emulatedMediaType: 'screen'
106
+ });
107
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(2);
108
+ expect(buffer).toEqual(Buffer.from('content'));
109
+ });
110
+ });
111
+
112
+ describe('when failOnHttpStatusCodes parameter is passed', () => {
113
+ it('should return a buffer', async () => {
114
+ mockFetch.mockResolvedValue(new Response('content'));
115
+ const buffer = await screenshot.capture({
116
+ html: Buffer.from('data'),
117
+ failOnHttpStatusCodes: [499, 599]
118
+ });
119
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(2);
120
+ expect(buffer).toEqual(Buffer.from('content'));
121
+ });
122
+ });
123
+
124
+ describe('when skipNetworkIdleEvent parameter is passed', () => {
125
+ it('should return a buffer', async () => {
126
+ mockFetch.mockResolvedValue(new Response('content'));
127
+ const buffer = await screenshot.capture({
128
+ html: Buffer.from('data'),
129
+ skipNetworkIdleEvent: true
130
+ });
131
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(2);
132
+ expect(buffer).toEqual(Buffer.from('content'));
133
+ });
134
+ });
135
+
136
+ describe('when optimizeForSpeed parameter is passed', () => {
137
+ it('should return a buffer', async () => {
138
+ mockFetch.mockResolvedValue(new Response('content'));
139
+ const buffer = await screenshot.capture({
140
+ html: Buffer.from('data'),
141
+ optimizeForSpeed: true
142
+ });
143
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(2);
144
+ expect(buffer).toEqual(Buffer.from('content'));
145
+ });
146
+ });
147
+
148
+ describe('when all parameters are passed', () => {
149
+ it('should return a buffer', async () => {
150
+ mockPromisesAccess.mockResolvedValue();
151
+ mockFetch.mockResolvedValue(new Response('content'));
152
+ const buffer = await screenshot.capture({
153
+ html: Buffer.from('data'),
154
+ assets,
155
+ header: Buffer.from('header'),
156
+ footer: Buffer.from('footer'),
157
+ emulatedMediaType: 'screen',
158
+ failOnHttpStatusCodes: [499, 599],
159
+ skipNetworkIdleEvent: true,
160
+ failOnConsoleExceptions: true,
161
+ properties: { format: 'jpeg', quality: 50 },
162
+ optimizeForSpeed: true
163
+ });
164
+ expect(mockFormDataAppend).toHaveBeenCalledTimes(12);
165
+ expect(buffer).toEqual(Buffer.from('content'));
166
+ });
167
+ });
168
+
169
+ describe('when file does not exist', () => {
170
+ it('should throw an error', async () => {
171
+ const errorMessage =
172
+ "ENOENT: no such file or directory, access 'path/to/index.html'";
173
+ mockPromisesAccess.mockRejectedValue(new Error(errorMessage));
174
+ await expect(() =>
175
+ screenshot.capture({ html: 'path/to/index.html' })
176
+ ).rejects.toThrow(errorMessage);
177
+ });
178
+ });
179
+
180
+ describe('when fetch request fails', () => {
181
+ it('should throw an error', async () => {
182
+ const errorMessage =
183
+ 'FetchError: request to http://localhost:3000/forms/chromium/screenshot/html failed';
184
+ mockPromisesAccess.mockResolvedValue();
185
+ mockFetch.mockRejectedValue(new Error(errorMessage));
186
+ await expect(() =>
187
+ screenshot.capture({ html: 'path/to/index.html' })
188
+ ).rejects.toThrow(errorMessage);
189
+ });
190
+ });
191
+ });
192
+ });