diff --git a/src/components/qr-code/qr-code.test.ts b/src/components/qr-code/qr-code.test.ts new file mode 100644 index 000000000..cff942eab --- /dev/null +++ b/src/components/qr-code/qr-code.test.ts @@ -0,0 +1,144 @@ +import '../../../dist/shoelace.js'; +import { expect, fixture, html } from '@open-wc/testing'; +import type SlQrCode from './qr-code'; + +const getCanvas = (qrCode: SlQrCode): HTMLCanvasElement => { + const possibleCanvas = qrCode.shadowRoot?.querySelector('.qr-code'); + expect(possibleCanvas).to.exist; + return possibleCanvas!; +}; + +const expectCanvasToHaveAriaLabel = (qrCode: SlQrCode, expectedLabel: string): void => { + const canvas = getCanvas(qrCode); + expect(canvas).to.have.attribute('aria-label', expectedLabel); +}; + +class Color { + r: number; + g: number; + b: number; + alpha: number; + + constructor(r: number, g: number, b: number, alpha: number) { + this.r = r; + this.b = b; + this.g = g; + this.alpha = alpha; + } + + equals(other: Color): boolean { + return ( + other === this || (this.r === other.r && this.b === other.b && this.g === other.g && this.alpha === other.alpha) + ); + } + + toString(): string { + return JSON.stringify(this); + } +} + +interface QrCodeColors { + foreground: Color; + background: Color; +} + +const getColorFromPixel = (colorArray: Uint8ClampedArray, pixelNumber: number): Color => { + const startEntryNumber = pixelNumber * 4; + return new Color( + colorArray[startEntryNumber], + colorArray[startEntryNumber + 1], + colorArray[startEntryNumber + 2], + colorArray[startEntryNumber + 3] + ); +}; + +const getQrCodeColors = (qrCode: SlQrCode): QrCodeColors => { + const canvas = getCanvas(qrCode); + const context = canvas.getContext('2d'); + const imageData = context?.getImageData(0, 0, canvas.width, canvas.height); + expect(imageData).not.to.be.null; + const colorArray = imageData!.data; + const numberOfPixels = imageData!.width * imageData!.height; + const foregroundColor = getColorFromPixel(colorArray, 0); + let backgroundColor: Color | null = null; + for (let pixelNumber = 0; pixelNumber < numberOfPixels; pixelNumber++) { + const currentColor = getColorFromPixel(colorArray, pixelNumber); + if (!currentColor.equals(foregroundColor)) { + backgroundColor = currentColor; + break; + } + } + return { + foreground: foregroundColor, + background: backgroundColor! + }; +}; + +const red = new Color(255, 0, 0, 255); +const white = new Color(255, 255, 255, 255); +const blue = new Color(0, 0, 255, 255); + +const expectQrCodeColorsToBe = (qrCode: SlQrCode, expectedColors: QrCodeColors): void => { + const qrCodeColors = getQrCodeColors(qrCode); + const backgroundMessage = + 'expected background color to be ' + + expectedColors.background.toString() + + ' but got ' + + qrCodeColors.background.toString(); + expect(qrCodeColors.background.equals(expectedColors.background), backgroundMessage).to.be.true; + const foregroundMessage = + 'expected foreground color to be ' + + expectedColors.foreground.toString() + + ' but got ' + + qrCodeColors.foreground.toString(); + expect(qrCodeColors.foreground.equals(expectedColors.foreground), foregroundMessage).to.be.true; +}; + +describe('', () => { + it('should render a component', async () => { + const qrCode = await fixture(html` `); + + expect(qrCode).to.exist; + }); + + it('should be accessible', async () => { + const qrCode = await fixture(html` `); + + await expect(qrCode).to.be.accessible(); + }); + + it('uses the value as label if none given', async () => { + const qrCode = await fixture(html` `); + + expectCanvasToHaveAriaLabel(qrCode, 'test data'); + }); + + it('uses the label if given', async () => { + const qrCode = await fixture(html` `); + + expectCanvasToHaveAriaLabel(qrCode, 'test label'); + }); + + it('sets the correct color for the qr code', async () => { + const qrCode = await fixture(html` `); + + expectQrCodeColorsToBe(qrCode, { foreground: red, background: white }); + }); + + it('sets the correct background for the qr code', async () => { + const qrCode = await fixture( + html` ` + ); + + expectQrCodeColorsToBe(qrCode, { foreground: red, background: blue }); + }); + + it('has the expected size', async () => { + const qrCode = await fixture(html` `); + + const height = qrCode.getBoundingClientRect().height; + const width = qrCode.getBoundingClientRect().width; + expect(height).to.equal(100); + expect(width).to.equal(100); + }); +}); diff --git a/src/components/qr-code/qr-code.ts b/src/components/qr-code/qr-code.ts index 11614d943..875c96f76 100644 --- a/src/components/qr-code/qr-code.ts +++ b/src/components/qr-code/qr-code.ts @@ -58,7 +58,7 @@ export default class SlQrCode extends ShoelaceElement { radius: this.radius, ecLevel: this.errorCorrection, fill: this.fill, - background: null, + background: this.background, // We draw the canvas larger and scale its container down to avoid blurring on high-density displays size: this.size * 2 },