Skip to content

Commit ea327ba

Browse files
Copilotericblade
andauthored
Add tests for initCanvas dom/ctx structure verification (#654)
* Initial plan * Add tests to verify initCanvas sets dom/ctx properties with image/overlay Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> * Add browser-specific tests for initCanvas in Cypress Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> * Remove redundant property structure test from Node.js tests Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ericblade <1451847+ericblade@users.noreply.github.com>
1 parent ec5d363 commit ea327ba

3 files changed

Lines changed: 235 additions & 3 deletions

File tree

cypress/e2e/browser.cy.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ import '../../src/input/test/browser/data_uri.spec.ts';
88
import '../../src/common/test/browser/mediaDevices.spec.ts';
99
import '../../src/input/test/browser/frame_grabber_halfsample.spec.ts';
1010
import '../../src/quagga/test/browser/pause_start.spec.ts';
11+
import '../../src/quagga/test/browser/initCanvas.spec.ts';
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import { describe, it, beforeEach, afterEach } from 'mocha';
2+
import { expect } from 'chai';
3+
import { QuaggaContext } from '../../../QuaggaContext';
4+
import initCanvas from '../../initCanvas';
5+
6+
// These tests verify initCanvas behavior in a browser environment where document is defined.
7+
// In the browser, dom.image and ctx.image should always be HTMLCanvasElement and CanvasRenderingContext2D
8+
// respectively, while overlay depends on the createOverlay config option.
9+
10+
describe('initCanvas (browser)', () => {
11+
let context: QuaggaContext;
12+
let createdCanvases: HTMLCanvasElement[] = [];
13+
14+
beforeEach(() => {
15+
context = new QuaggaContext();
16+
// Mock the input stream
17+
context.inputStream = {
18+
getCanvasSize: () => ({ type: 'XYSize' as const, x: 640, y: 480 }),
19+
};
20+
21+
// Clean up any existing canvases from previous tests
22+
document.querySelectorAll('canvas.imgBuffer, canvas.drawingBuffer').forEach((canvas) => {
23+
canvas.remove();
24+
});
25+
});
26+
27+
afterEach(() => {
28+
// Clean up canvases created during tests
29+
createdCanvases.forEach((canvas) => {
30+
if (canvas.parentElement) {
31+
canvas.parentElement.removeChild(canvas);
32+
}
33+
});
34+
createdCanvases = [];
35+
36+
// Also clean up any canvases that may have been created
37+
document.querySelectorAll('canvas.imgBuffer, canvas.drawingBuffer').forEach((canvas) => {
38+
canvas.remove();
39+
});
40+
});
41+
42+
describe('canvas structure', () => {
43+
it('should return object with dom and ctx properties each having image and overlay', () => {
44+
context.config = {
45+
inputStream: { type: 'ImageStream' },
46+
};
47+
48+
const result = initCanvas(context);
49+
50+
expect(result).to.be.an('object');
51+
// Verify dom object has both image and overlay properties
52+
expect(result!.dom).to.be.an('object');
53+
expect(result!.dom).to.have.property('image');
54+
expect(result!.dom).to.have.property('overlay');
55+
// Verify ctx object has both image and overlay properties
56+
expect(result!.ctx).to.be.an('object');
57+
expect(result!.ctx).to.have.property('image');
58+
expect(result!.ctx).to.have.property('overlay');
59+
});
60+
});
61+
62+
describe('dom.image and ctx.image (always present in browser)', () => {
63+
it('should always have dom.image as HTMLCanvasElement in browser', () => {
64+
context.config = {
65+
inputStream: { type: 'ImageStream' },
66+
};
67+
68+
const result = initCanvas(context);
69+
70+
expect(result).to.be.an('object');
71+
expect(result!.dom.image).to.be.instanceOf(HTMLCanvasElement);
72+
expect(result!.dom.image!.className).to.equal('imgBuffer');
73+
});
74+
75+
it('should always have ctx.image as CanvasRenderingContext2D in browser', () => {
76+
context.config = {
77+
inputStream: { type: 'ImageStream' },
78+
};
79+
80+
const result = initCanvas(context);
81+
82+
expect(result).to.be.an('object');
83+
expect(result!.ctx.image).to.be.instanceOf(CanvasRenderingContext2D);
84+
});
85+
86+
it('should set canvas dimensions from inputStream.getCanvasSize()', () => {
87+
context.config = {
88+
inputStream: { type: 'ImageStream' },
89+
};
90+
91+
const result = initCanvas(context);
92+
93+
expect(result!.dom.image!.width).to.equal(640);
94+
expect(result!.dom.image!.height).to.equal(480);
95+
});
96+
});
97+
98+
describe('overlay with createOverlay: true (default)', () => {
99+
it('should have dom.overlay as HTMLCanvasElement when createOverlay is true', () => {
100+
context.config = {
101+
inputStream: { type: 'ImageStream' },
102+
canvas: {
103+
createOverlay: true,
104+
},
105+
};
106+
107+
const result = initCanvas(context);
108+
109+
expect(result).to.be.an('object');
110+
expect(result!.dom.overlay).to.be.instanceOf(HTMLCanvasElement);
111+
expect(result!.dom.overlay!.className).to.equal('drawingBuffer');
112+
});
113+
114+
it('should have ctx.overlay as CanvasRenderingContext2D when createOverlay is true', () => {
115+
context.config = {
116+
inputStream: { type: 'ImageStream' },
117+
canvas: {
118+
createOverlay: true,
119+
},
120+
};
121+
122+
const result = initCanvas(context);
123+
124+
expect(result).to.be.an('object');
125+
expect(result!.ctx.overlay).to.be.instanceOf(CanvasRenderingContext2D);
126+
});
127+
128+
it('should create overlay by default when createOverlay is not specified', () => {
129+
context.config = {
130+
inputStream: { type: 'ImageStream' },
131+
// No canvas config - should default to createOverlay: true
132+
};
133+
134+
const result = initCanvas(context);
135+
136+
expect(result).to.be.an('object');
137+
expect(result!.dom.overlay).to.be.instanceOf(HTMLCanvasElement);
138+
expect(result!.ctx.overlay).to.be.instanceOf(CanvasRenderingContext2D);
139+
});
140+
141+
it('should set overlay canvas dimensions from inputStream.getCanvasSize()', () => {
142+
context.config = {
143+
inputStream: { type: 'ImageStream' },
144+
canvas: {
145+
createOverlay: true,
146+
},
147+
};
148+
149+
const result = initCanvas(context);
150+
151+
expect(result!.dom.overlay!.width).to.equal(640);
152+
expect(result!.dom.overlay!.height).to.equal(480);
153+
});
154+
});
155+
156+
describe('overlay with createOverlay: false', () => {
157+
it('should have null dom.overlay when createOverlay is false', () => {
158+
context.config = {
159+
inputStream: { type: 'ImageStream' },
160+
canvas: {
161+
createOverlay: false,
162+
},
163+
};
164+
165+
const result = initCanvas(context);
166+
167+
expect(result).to.be.an('object');
168+
// Image should still be present
169+
expect(result!.dom.image).to.be.instanceOf(HTMLCanvasElement);
170+
// But overlay should be null
171+
expect(result!.dom.overlay).to.be.null;
172+
});
173+
174+
it('should have null ctx.overlay when createOverlay is false', () => {
175+
context.config = {
176+
inputStream: { type: 'ImageStream' },
177+
canvas: {
178+
createOverlay: false,
179+
},
180+
};
181+
182+
const result = initCanvas(context);
183+
184+
expect(result).to.be.an('object');
185+
// Image context should still be present
186+
expect(result!.ctx.image).to.be.instanceOf(CanvasRenderingContext2D);
187+
// But overlay context should be null
188+
expect(result!.ctx.overlay).to.be.null;
189+
});
190+
});
191+
192+
describe('edge cases', () => {
193+
it('should return null when inputStream type is not defined', () => {
194+
context.config = {
195+
inputStream: {},
196+
};
197+
198+
const result = initCanvas(context);
199+
200+
expect(result).to.be.null;
201+
});
202+
203+
it('should reuse existing canvas elements if they exist in the DOM', () => {
204+
// Create existing canvas elements
205+
const existingImageCanvas = document.createElement('canvas');
206+
existingImageCanvas.className = 'imgBuffer';
207+
document.body.appendChild(existingImageCanvas);
208+
createdCanvases.push(existingImageCanvas);
209+
210+
const existingOverlayCanvas = document.createElement('canvas');
211+
existingOverlayCanvas.className = 'drawingBuffer';
212+
document.body.appendChild(existingOverlayCanvas);
213+
createdCanvases.push(existingOverlayCanvas);
214+
215+
context.config = {
216+
inputStream: { type: 'ImageStream' },
217+
canvas: {
218+
createOverlay: true,
219+
},
220+
};
221+
222+
const result = initCanvas(context);
223+
224+
// Should reuse existing canvases
225+
expect(result!.dom.image).to.equal(existingImageCanvas);
226+
expect(result!.dom.overlay).to.equal(existingOverlayCanvas);
227+
});
228+
});
229+
});

src/quagga/test/initCanvas.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,21 @@ describe('src/quagga/initCanvas.ts', () => {
1515
};
1616
});
1717

18-
it('should return container with null overlay when there is no document', () => {
18+
it('should return container with all null properties when there is no document (Node.js)', () => {
1919
// Node.js environment - no document
2020
context.config = {
2121
inputStream: { type: 'ImageStream' },
2222
};
2323

2424
const result = initCanvas(context);
2525

26-
// In Node.js (no document), should return default container
26+
// In Node.js (no document), should return default container with all null values
2727
expect(result).to.be.an('object');
2828
// Since we're in Node.js where document is undefined,
29-
// the overlay should be null (default container)
29+
// all dom and ctx properties should be null
30+
expect(result!.dom.image).to.be.null;
3031
expect(result!.dom.overlay).to.be.null;
32+
expect(result!.ctx.image).to.be.null;
3133
expect(result!.ctx.overlay).to.be.null;
3234
});
3335

0 commit comments

Comments
 (0)