@mkhuda/dom-screenshot 0.0.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.gitattributes +1 -0
  2. package/EXAMPLES_QUICKSTART.md +240 -0
  3. package/README.md +542 -25
  4. package/TESTING.md +269 -0
  5. package/TESTING_STATUS.md +215 -0
  6. package/TEST_SETUP_SUMMARY.md +335 -0
  7. package/dist/dom-screenshot.d.ts +44 -272
  8. package/dist/dom-screenshot.d.ts.map +1 -0
  9. package/dist/dom-screenshot.esm.js +753 -0
  10. package/dist/dom-screenshot.esm.js.map +1 -0
  11. package/dist/dom-screenshot.min.js +2 -1
  12. package/dist/dom-screenshot.min.js.map +1 -0
  13. package/examples/README.md +211 -0
  14. package/examples/react-app/README.md +161 -0
  15. package/examples/react-app/index.html +12 -0
  16. package/examples/react-app/node_modules/.vite/deps/_metadata.json +46 -0
  17. package/examples/react-app/node_modules/.vite/deps/chunk-FK77NBP6.js +1895 -0
  18. package/examples/react-app/node_modules/.vite/deps/chunk-FK77NBP6.js.map +7 -0
  19. package/examples/react-app/node_modules/.vite/deps/chunk-VSODSHUF.js +21647 -0
  20. package/examples/react-app/node_modules/.vite/deps/chunk-VSODSHUF.js.map +7 -0
  21. package/examples/react-app/node_modules/.vite/deps/package.json +3 -0
  22. package/examples/react-app/node_modules/.vite/deps/react-dom.js +5 -0
  23. package/examples/react-app/node_modules/.vite/deps/react-dom.js.map +7 -0
  24. package/examples/react-app/node_modules/.vite/deps/react-dom_client.js +38 -0
  25. package/examples/react-app/node_modules/.vite/deps/react-dom_client.js.map +7 -0
  26. package/examples/react-app/node_modules/.vite/deps/react.js +4 -0
  27. package/examples/react-app/node_modules/.vite/deps/react.js.map +7 -0
  28. package/examples/react-app/node_modules/.vite/deps/react_jsx-dev-runtime.js +898 -0
  29. package/examples/react-app/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
  30. package/examples/react-app/node_modules/.vite/deps/react_jsx-runtime.js +910 -0
  31. package/examples/react-app/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
  32. package/examples/react-app/package.json +21 -0
  33. package/examples/react-app/tsconfig.json +25 -0
  34. package/examples/react-app/tsconfig.node.json +10 -0
  35. package/examples/react-app/vite.config.ts +10 -0
  36. package/package.json +75 -44
  37. package/rollup.config.mjs +35 -0
  38. package/tests/README.md +394 -0
  39. package/tests/fixtures/html.ts +192 -0
  40. package/tests/fixtures/images.ts +86 -0
  41. package/tests/fixtures/styles.ts +288 -0
  42. package/tests/helpers/dom-helpers.ts +242 -0
  43. package/tests/mocks/canvas-mock.ts +94 -0
  44. package/tests/mocks/image-mock.ts +147 -0
  45. package/tests/mocks/xhr-mock.ts +202 -0
  46. package/tests/setup.ts +103 -0
  47. package/tests/unit/basic.test.ts +263 -0
  48. package/tests/unit/simple.test.ts +172 -0
  49. package/tsconfig.json +44 -20
  50. package/vitest.config.mts +35 -0
  51. package/rollup.config.js +0 -20
@@ -0,0 +1,288 @@
1
+ /**
2
+ * CSS and style test fixtures
3
+ */
4
+
5
+ /**
6
+ * Basic CSS fixtures
7
+ */
8
+ export const CSS_FIXTURES = {
9
+ /**
10
+ * Basic div with color
11
+ */
12
+ coloredDiv: `
13
+ <div style="
14
+ width: 100px;
15
+ height: 100px;
16
+ background-color: blue;
17
+ color: white;
18
+ ">Colored</div>
19
+ `,
20
+
21
+ /**
22
+ * Gradient background
23
+ */
24
+ gradientDiv: `
25
+ <div style="
26
+ width: 100px;
27
+ height: 100px;
28
+ background: linear-gradient(to right, red, blue);
29
+ ">Gradient</div>
30
+ `,
31
+
32
+ /**
33
+ * Border and shadow
34
+ */
35
+ borderShadowDiv: `
36
+ <div style="
37
+ width: 100px;
38
+ height: 100px;
39
+ border: 2px solid black;
40
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
41
+ background-color: white;
42
+ ">Border & Shadow</div>
43
+ `,
44
+
45
+ /**
46
+ * Border radius
47
+ */
48
+ borderRadiusDiv: `
49
+ <div style="
50
+ width: 100px;
51
+ height: 100px;
52
+ background-color: green;
53
+ border-radius: 50%;
54
+ ">Circle</div>
55
+ `,
56
+
57
+ /**
58
+ * Transform and rotation
59
+ */
60
+ transformDiv: `
61
+ <div style="
62
+ width: 100px;
63
+ height: 100px;
64
+ background-color: orange;
65
+ transform: rotate(45deg);
66
+ ">Rotated</div>
67
+ `,
68
+
69
+ /**
70
+ * Opacity
71
+ */
72
+ opacityDiv: `
73
+ <div style="
74
+ width: 100px;
75
+ height: 100px;
76
+ background-color: purple;
77
+ opacity: 0.5;
78
+ ">Transparent</div>
79
+ `,
80
+
81
+ /**
82
+ * Font styling
83
+ */
84
+ fontStyledDiv: `
85
+ <div style="
86
+ font-family: Arial, sans-serif;
87
+ font-size: 16px;
88
+ font-weight: bold;
89
+ color: #333;
90
+ line-height: 1.5;
91
+ ">
92
+ <p>Styled text</p>
93
+ </div>
94
+ `,
95
+
96
+ /**
97
+ * Flexbox layout
98
+ */
99
+ flexboxDiv: `
100
+ <div style="
101
+ display: flex;
102
+ gap: 10px;
103
+ background-color: #f0f0f0;
104
+ padding: 10px;
105
+ ">
106
+ <div style="
107
+ width: 50px;
108
+ height: 50px;
109
+ background-color: red;
110
+ "></div>
111
+ <div style="
112
+ width: 50px;
113
+ height: 50px;
114
+ background-color: green;
115
+ "></div>
116
+ <div style="
117
+ width: 50px;
118
+ height: 50px;
119
+ background-color: blue;
120
+ "></div>
121
+ </div>
122
+ `,
123
+
124
+ /**
125
+ * Grid layout
126
+ */
127
+ gridDiv: `
128
+ <div style="
129
+ display: grid;
130
+ grid-template-columns: repeat(3, 1fr);
131
+ gap: 10px;
132
+ background-color: #f0f0f0;
133
+ padding: 10px;
134
+ ">
135
+ <div style="background-color: red; height: 50px;"></div>
136
+ <div style="background-color: green; height: 50px;"></div>
137
+ <div style="background-color: blue; height: 50px;"></div>
138
+ </div>
139
+ `,
140
+
141
+ /**
142
+ * Padding and margin
143
+ */
144
+ spacingDiv: `
145
+ <div style="
146
+ background-color: #ddd;
147
+ padding: 20px;
148
+ margin: 10px;
149
+ border: 1px solid #999;
150
+ ">
151
+ <div style="
152
+ background-color: white;
153
+ padding: 10px;
154
+ margin: 10px;
155
+ border: 1px solid #ccc;
156
+ ">Spaced content</div>
157
+ </div>
158
+ `,
159
+
160
+ /**
161
+ * Text alignment
162
+ */
163
+ textAlignDiv: `
164
+ <div style="width: 200px;">
165
+ <p style="text-align: left;">Left aligned</p>
166
+ <p style="text-align: center;">Center aligned</p>
167
+ <p style="text-align: right;">Right aligned</p>
168
+ </div>
169
+ `,
170
+
171
+ /**
172
+ * Overflow handling
173
+ */
174
+ overflowDiv: `
175
+ <div style="
176
+ width: 100px;
177
+ height: 100px;
178
+ background-color: #f0f0f0;
179
+ overflow: hidden;
180
+ border: 1px solid #ccc;
181
+ ">
182
+ This text will be cut off because it exceeds the container size.
183
+ </div>
184
+ `,
185
+
186
+ /**
187
+ * List styling
188
+ */
189
+ listDiv: `
190
+ <ul style="
191
+ list-style: none;
192
+ padding: 0;
193
+ ">
194
+ <li style="
195
+ padding: 5px;
196
+ background-color: #eee;
197
+ margin: 2px;
198
+ border-left: 3px solid blue;
199
+ padding-left: 10px;
200
+ ">Item 1</li>
201
+ <li style="
202
+ padding: 5px;
203
+ background-color: #eee;
204
+ margin: 2px;
205
+ border-left: 3px solid blue;
206
+ padding-left: 10px;
207
+ ">Item 2</li>
208
+ </ul>
209
+ `,
210
+
211
+ /**
212
+ * Table styling
213
+ */
214
+ tableDiv: `
215
+ <table style="
216
+ border-collapse: collapse;
217
+ width: 200px;
218
+ ">
219
+ <tr>
220
+ <th style="
221
+ background-color: #333;
222
+ color: white;
223
+ padding: 8px;
224
+ border: 1px solid #333;
225
+ ">Header 1</th>
226
+ <th style="
227
+ background-color: #333;
228
+ color: white;
229
+ padding: 8px;
230
+ border: 1px solid #333;
231
+ ">Header 2</th>
232
+ </tr>
233
+ <tr>
234
+ <td style="padding: 8px; border: 1px solid #ddd;">Data 1</td>
235
+ <td style="padding: 8px; border: 1px solid #ddd;">Data 2</td>
236
+ </tr>
237
+ </table>
238
+ `,
239
+ };
240
+
241
+ /**
242
+ * Create a CSS style element
243
+ */
244
+ export function createStyleElement(css: string): HTMLStyleElement {
245
+ const style = document.createElement('style');
246
+ style.textContent = css;
247
+ document.head.appendChild(style);
248
+ return style;
249
+ }
250
+
251
+ /**
252
+ * Create pseudo-element CSS
253
+ */
254
+ export function createPseudoElementCSS(): string {
255
+ return `
256
+ .with-before::before {
257
+ content: '→ ';
258
+ color: red;
259
+ font-weight: bold;
260
+ }
261
+ .with-after::after {
262
+ content: ' ←';
263
+ color: blue;
264
+ font-weight: bold;
265
+ }
266
+ .with-both::before {
267
+ content: '[';
268
+ color: green;
269
+ }
270
+ .with-both::after {
271
+ content: ']';
272
+ color: green;
273
+ }
274
+ `;
275
+ }
276
+
277
+ /**
278
+ * Create a div with pseudo-elements
279
+ */
280
+ export function createDivWithPseudoElements(): HTMLDivElement {
281
+ const style = createStyleElement(createPseudoElementCSS());
282
+
283
+ const div = document.createElement('div');
284
+ div.className = 'with-both';
285
+ div.textContent = 'Text with pseudo-elements';
286
+
287
+ return div;
288
+ }
@@ -0,0 +1,242 @@
1
+ /**
2
+ * DOM testing helpers
3
+ */
4
+
5
+ /**
6
+ * Create a simple div with text content
7
+ */
8
+ export function createSimpleDiv(text: string = 'Test'): HTMLDivElement {
9
+ const div = document.createElement('div');
10
+ div.textContent = text;
11
+ return div;
12
+ }
13
+
14
+ /**
15
+ * Create a styled div with custom CSS
16
+ */
17
+ export function createStyledDiv(
18
+ text: string,
19
+ styles: Record<string, string>
20
+ ): HTMLDivElement {
21
+ const div = createSimpleDiv(text);
22
+ Object.assign(div.style, styles);
23
+ return div;
24
+ }
25
+
26
+ /**
27
+ * Create a div with background color
28
+ */
29
+ export function createColoredDiv(
30
+ text: string,
31
+ color: string,
32
+ bgColor: string
33
+ ): HTMLDivElement {
34
+ return createStyledDiv(text, { color, backgroundColor: bgColor });
35
+ }
36
+
37
+ /**
38
+ * Create a simple image element
39
+ */
40
+ export function createImageElement(
41
+ src: string = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
42
+ alt: string = 'test'
43
+ ): HTMLImageElement {
44
+ const img = document.createElement('img');
45
+ img.src = src;
46
+ img.alt = alt;
47
+ return img;
48
+ }
49
+
50
+ /**
51
+ * Create a canvas element with some drawing
52
+ */
53
+ export function createCanvasElement(
54
+ width: number = 100,
55
+ height: number = 100
56
+ ): HTMLCanvasElement {
57
+ const canvas = document.createElement('canvas');
58
+ canvas.width = width;
59
+ canvas.height = height;
60
+
61
+ const ctx = canvas.getContext('2d');
62
+ if (ctx) {
63
+ ctx.fillStyle = 'blue';
64
+ ctx.fillRect(0, 0, width, height);
65
+ }
66
+
67
+ return canvas;
68
+ }
69
+
70
+ /**
71
+ * Create a video element
72
+ */
73
+ export function createVideoElement(
74
+ src: string = 'data:video/mp4;base64,',
75
+ width: number = 100,
76
+ height: number = 100
77
+ ): HTMLVideoElement {
78
+ const video = document.createElement('video');
79
+ video.src = src;
80
+ video.width = width;
81
+ video.height = height;
82
+ return video;
83
+ }
84
+
85
+ /**
86
+ * Create a textarea with value
87
+ */
88
+ export function createTextarea(value: string = 'test value'): HTMLTextAreaElement {
89
+ const textarea = document.createElement('textarea');
90
+ textarea.value = value;
91
+ return textarea;
92
+ }
93
+
94
+ /**
95
+ * Create an input element with value
96
+ */
97
+ export function createInput(
98
+ value: string = 'test input',
99
+ type: string = 'text'
100
+ ): HTMLInputElement {
101
+ const input = document.createElement('input');
102
+ input.type = type;
103
+ input.value = value;
104
+ return input;
105
+ }
106
+
107
+ /**
108
+ * Create a complex DOM tree with various elements
109
+ */
110
+ export function createComplexDOM(): HTMLDivElement {
111
+ const container = document.createElement('div');
112
+ container.style.width = '200px';
113
+ container.style.height = '200px';
114
+ container.style.backgroundColor = '#f0f0f0';
115
+
116
+ // Add text
117
+ const heading = document.createElement('h1');
118
+ heading.textContent = 'Test Heading';
119
+ heading.style.color = '#333';
120
+ container.appendChild(heading);
121
+
122
+ // Add paragraph
123
+ const para = document.createElement('p');
124
+ para.textContent = 'This is a test paragraph.';
125
+ para.style.fontSize = '14px';
126
+ container.appendChild(para);
127
+
128
+ // Add image
129
+ const img = createImageElement();
130
+ img.style.width = '50px';
131
+ img.style.height = '50px';
132
+ container.appendChild(img);
133
+
134
+ // Add nested div
135
+ const nested = document.createElement('div');
136
+ nested.style.border = '1px solid #ccc';
137
+ nested.style.padding = '10px';
138
+ nested.textContent = 'Nested content';
139
+ container.appendChild(nested);
140
+
141
+ return container;
142
+ }
143
+
144
+ /**
145
+ * Create a DOM with inline styles
146
+ */
147
+ export function createDOMWithStyles(): HTMLDivElement {
148
+ const container = document.createElement('div');
149
+ container.style.background = 'linear-gradient(to right, #ff0000, #00ff00)';
150
+ container.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
151
+ container.style.borderRadius = '8px';
152
+ container.style.padding = '20px';
153
+
154
+ const text = document.createElement('span');
155
+ text.textContent = 'Styled content';
156
+ text.style.fontWeight = 'bold';
157
+ text.style.color = 'white';
158
+ container.appendChild(text);
159
+
160
+ return container;
161
+ }
162
+
163
+ /**
164
+ * Create a DOM with pseudo-elements (via CSS class)
165
+ */
166
+ export function createDOMWithPseudoElements(): HTMLDivElement {
167
+ const style = document.createElement('style');
168
+ style.textContent = `
169
+ .pseudo-element {
170
+ position: relative;
171
+ }
172
+ .pseudo-element::before {
173
+ content: '→ ';
174
+ color: red;
175
+ }
176
+ .pseudo-element::after {
177
+ content: ' ←';
178
+ color: blue;
179
+ }
180
+ `;
181
+ document.head.appendChild(style);
182
+
183
+ const div = document.createElement('div');
184
+ div.className = 'pseudo-element';
185
+ div.textContent = 'Text with pseudo-elements';
186
+
187
+ return div;
188
+ }
189
+
190
+ /**
191
+ * Create a SVG element
192
+ */
193
+ export function createSVGElement(): SVGSVGElement {
194
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
195
+ svg.setAttribute('width', '100');
196
+ svg.setAttribute('height', '100');
197
+ svg.setAttribute('viewBox', '0 0 100 100');
198
+
199
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
200
+ circle.setAttribute('cx', '50');
201
+ circle.setAttribute('cy', '50');
202
+ circle.setAttribute('r', '40');
203
+ circle.setAttribute('fill', 'blue');
204
+
205
+ svg.appendChild(circle);
206
+ return svg;
207
+ }
208
+
209
+ /**
210
+ * Wait for a specified time
211
+ */
212
+ export function wait(ms: number): Promise<void> {
213
+ return new Promise((resolve) => setTimeout(resolve, ms));
214
+ }
215
+
216
+ /**
217
+ * Get image data from a data URL
218
+ */
219
+ export function parseDataUrl(dataUrl: string): { type: string; data: string } {
220
+ const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);
221
+ if (!match) {
222
+ throw new Error(`Invalid data URL: ${dataUrl}`);
223
+ }
224
+ return {
225
+ type: match[1],
226
+ data: match[2],
227
+ };
228
+ }
229
+
230
+ /**
231
+ * Check if a data URL is valid
232
+ */
233
+ export function isValidDataUrl(str: string): boolean {
234
+ return typeof str === 'string' && str.startsWith('data:');
235
+ }
236
+
237
+ /**
238
+ * Check if a data URL is an image
239
+ */
240
+ export function isImageDataUrl(str: string): boolean {
241
+ return typeof str === 'string' && str.startsWith('data:image/');
242
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Canvas mocking utilities for testing
3
+ */
4
+ import { vi, SinonStub } from 'vitest';
5
+
6
+ /**
7
+ * Mock HTMLCanvasElement.prototype.toDataURL
8
+ */
9
+ export function mockCanvasToDataUrl(
10
+ returnValue: string = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='
11
+ ): SinonStub {
12
+ return vi.spyOn(HTMLCanvasElement.prototype, 'toDataURL').mockReturnValue(returnValue);
13
+ }
14
+
15
+ /**
16
+ * Mock HTMLCanvasElement.prototype.toBlob
17
+ */
18
+ export function mockCanvasToBlob(
19
+ returnBlob: Blob = new Blob(['test'], { type: 'image/png' })
20
+ ): SinonStub {
21
+ return vi
22
+ .spyOn(HTMLCanvasElement.prototype, 'toBlob')
23
+ .mockImplementation((callback: BlobCallback) => {
24
+ // Use setTimeout to simulate async behavior
25
+ setTimeout(() => callback(returnBlob), 0);
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Mock canvas context operations
31
+ */
32
+ export function mockCanvasContext(): {
33
+ fillRect: SinonStub;
34
+ drawImage: SinonStub;
35
+ getImageData: SinonStub;
36
+ } {
37
+ const mockContext = {
38
+ fillRect: vi.fn(),
39
+ drawImage: vi.fn(),
40
+ getImageData: vi.fn().mockReturnValue({
41
+ data: new Uint8ClampedArray(4), // RGBA for 1x1 pixel
42
+ }),
43
+ };
44
+
45
+ vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(
46
+ mockContext as any
47
+ );
48
+
49
+ return {
50
+ fillRect: mockContext.fillRect,
51
+ drawImage: mockContext.drawImage,
52
+ getImageData: mockContext.getImageData,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Create a valid PNG data URL
58
+ */
59
+ export function createValidPngDataUrl(): string {
60
+ // Minimal valid PNG: 1x1 transparent pixel
61
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
62
+ }
63
+
64
+ /**
65
+ * Create a valid JPEG data URL
66
+ */
67
+ export function createValidJpegDataUrl(): string {
68
+ // Minimal valid JPEG: 1x1 red pixel
69
+ return 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8VAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCwAA8A/9k=';
70
+ }
71
+
72
+ /**
73
+ * Create a valid SVG data URL
74
+ */
75
+ export function createValidSvgDataUrl(): string {
76
+ const svg =
77
+ '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><circle cx="50" cy="50" r="40" fill="blue"/></svg>';
78
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
79
+ }
80
+
81
+ /**
82
+ * Mock multiple data URLs for different formats
83
+ */
84
+ export function mockCanvasDataUrls(): {
85
+ png: string;
86
+ jpeg: string;
87
+ svg: string;
88
+ } {
89
+ return {
90
+ png: createValidPngDataUrl(),
91
+ jpeg: createValidJpegDataUrl(),
92
+ svg: createValidSvgDataUrl(),
93
+ };
94
+ }