scandit-sdk
Version:
Scandit Barcode Scanner SDK for the Web
611 lines (566 loc) • 24.3 kB
text/typescript
/* tslint:disable:no-implicit-dependencies no-any */
/**
* BarcodePickerCameraManager tests
*/
import test from "ava";
import * as sinon from "sinon";
import { Camera, CameraAccess } from "..";
import { BarcodePickerCameraManager, ExtendedMediaTrackCapabilities, MeteringMode } from "./barcodePickerCameraManager";
import { BarcodePickerGui } from "./barcodePickerGui";
Object.defineProperty(screen, "width", {
writable: true
});
Object.defineProperty(screen, "height", {
writable: true
});
(<any>screen).width = 100;
(<any>screen).height = 100;
const triggerFatalErrorSpy: sinon.SinonSpy = sinon.spy();
// Speed up times
(<any>BarcodePickerCameraManager).cameraAccessTimeoutMs /= 10;
(<any>BarcodePickerCameraManager).cameraMetadataCheckTimeoutMs /= 10;
(<any>BarcodePickerCameraManager).cameraMetadataCheckIntervalMs /= 10;
(<any>BarcodePickerCameraManager).getCapabilitiesTimeoutMs /= 10;
(<any>BarcodePickerCameraManager).autofocusIntervalMs /= 10;
(<any>BarcodePickerCameraManager).manualToAutofocusResumeTimeoutMs /= 10;
(<any>BarcodePickerCameraManager).manualFocusWaitTimeoutMs /= 10;
async function wait(ms: number): Promise<void> {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
function fakeGetCameras(cameraAmount: number, cameraTypes?: Camera.Type[], cameraLabels?: string[]): void {
if ((<sinon.SinonSpy>CameraAccess.getCameras).restore != null) {
(<sinon.SinonSpy>CameraAccess.getCameras).restore();
}
sinon.stub(CameraAccess, "getCameras").resolves(
// tslint:disable-next-line:prefer-array-literal
Array.from(Array(cameraAmount), (_, index) => {
const cameraType: Camera.Type =
cameraTypes == null || cameraTypes[index] == null ? Camera.Type.BACK : cameraTypes[index];
const label: string =
cameraLabels == null || cameraLabels[index] == null
? `Fake Camera Device (${cameraType})`
: cameraLabels[index];
return {
deviceId: "unknown",
groupId: "1",
kind: "videoinput",
label,
cameraType
};
})
);
}
function fakeAccessCameraStream(facingMode: string, mediaTrackCapabilities?: ExtendedMediaTrackCapabilities): void {
if ((<sinon.SinonSpy>CameraAccess.accessCameraStream).restore != null) {
(<sinon.SinonSpy>CameraAccess.accessCameraStream).restore();
}
sinon.stub(CameraAccess, "accessCameraStream").callsFake(() => {
const mediaStreamTrack: MediaStreamTrack = <any>{
stop: sinon.spy(),
addEventListener: sinon.spy(),
getSettings: () => {
return {
width: 640,
height: 480,
deviceId: "1",
facingMode
};
},
label: ""
};
if (mediaTrackCapabilities != null) {
mediaStreamTrack.getCapabilities = () => {
return mediaTrackCapabilities;
};
}
return Promise.resolve(<any>{
getTracks: () => {
return [mediaStreamTrack];
},
getVideoTracks: () => {
return [mediaStreamTrack];
}
});
});
}
function fakeAccessCameraStreamFailure(error: Error): void {
if ((<sinon.SinonSpy>CameraAccess.accessCameraStream).restore != null) {
(<sinon.SinonSpy>CameraAccess.accessCameraStream).restore();
}
sinon.stub(CameraAccess, "accessCameraStream").rejects(error);
}
function fakeMediaStream(
cameraManager: BarcodePickerCameraManager,
mediaTrackCapabilities?: ExtendedMediaTrackCapabilities
): MediaStream {
const mediaStreamTrack: MediaStreamTrack = <any>{
constraints: {},
stop: sinon.spy(),
// tslint:disable-next-line:no-accessor-field-mismatch
getConstraints(): MediaTrackConstraints {
return this.constraints;
},
applyConstraints: sinon.stub().callsFake(
(mediaTrackConstraints: MediaTrackConstraints): Promise<void> => {
(<any>mediaStreamTrack).constraints = mediaTrackConstraints;
return Promise.resolve();
}
)
};
if (mediaTrackCapabilities != null) {
mediaStreamTrack.getCapabilities = () => {
return mediaTrackCapabilities;
};
}
const mediaStream: MediaStream = <any>{
getVideoTracks: () => {
return [mediaStreamTrack];
}
};
(<any>cameraManager).mediaStream = mediaStream;
(<any>cameraManager).storeStreamCapabilities();
return mediaStream;
}
test("isCameraSwitcherEnabled & setCameraSwitcherEnabled", async t => {
const barcodePickerGui: sinon.SinonStubbedInstance<BarcodePickerGui> = sinon.createStubInstance(BarcodePickerGui);
const cameraManager: BarcodePickerCameraManager = new BarcodePickerCameraManager(triggerFatalErrorSpy, <
BarcodePickerGui
>(<unknown>barcodePickerGui));
cameraManager.setInteractionOptions(false, false, false, false);
t.false(cameraManager.isCameraSwitcherEnabled());
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 0);
fakeGetCameras(1);
await cameraManager.setCameraSwitcherEnabled(true);
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 0);
fakeGetCameras(2);
await cameraManager.setCameraSwitcherEnabled(true);
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 1);
t.deepEqual(barcodePickerGui.setCameraSwitcherVisible.lastCall.args, [true]);
t.true(cameraManager.isCameraSwitcherEnabled());
await cameraManager.setCameraSwitcherEnabled(false);
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 2);
t.deepEqual(barcodePickerGui.setCameraSwitcherVisible.lastCall.args, [false]);
t.false(cameraManager.isCameraSwitcherEnabled());
});
test("isTorchToggleEnabled & setTorchToggleEnabled", t => {
const barcodePickerGui: sinon.SinonStubbedInstance<BarcodePickerGui> = sinon.createStubInstance(BarcodePickerGui);
const cameraManager: BarcodePickerCameraManager = new BarcodePickerCameraManager(triggerFatalErrorSpy, <
BarcodePickerGui
>(<unknown>barcodePickerGui));
cameraManager.setInteractionOptions(false, false, false, false);
t.false(cameraManager.isTorchToggleEnabled());
t.is(barcodePickerGui.setTorchTogglerVisible.callCount, 0);
cameraManager.setTorchToggleEnabled(true);
t.is(barcodePickerGui.setTorchTogglerVisible.callCount, 0);
fakeMediaStream(cameraManager, {
torch: true
});
cameraManager.setTorchToggleEnabled(true);
t.is(barcodePickerGui.setTorchTogglerVisible.callCount, 1);
t.deepEqual(barcodePickerGui.setTorchTogglerVisible.lastCall.args, [true]);
t.true(cameraManager.isTorchToggleEnabled());
cameraManager.setTorchToggleEnabled(false);
t.is(barcodePickerGui.setTorchTogglerVisible.callCount, 2);
t.deepEqual(barcodePickerGui.setTorchTogglerVisible.lastCall.args, [false]);
t.false(cameraManager.isTorchToggleEnabled());
});
test("isTapToFocusEnabled & setTapToFocusEnabled & isPinchToZoomEnabled & setPinchToZoomEnabled", t => {
const barcodePickerGui: sinon.SinonStubbedInstance<BarcodePickerGui> = sinon.createStubInstance(BarcodePickerGui);
const videoElementAddEventListener: sinon.SinonSpy = sinon.spy();
const videoElementRemoveEventListener: sinon.SinonSpy = sinon.spy();
(<any>barcodePickerGui).videoElement = {
addEventListener: videoElementAddEventListener,
removeEventListener: videoElementRemoveEventListener
};
const cameraManager: BarcodePickerCameraManager = new BarcodePickerCameraManager(triggerFatalErrorSpy, <
BarcodePickerGui
>(<unknown>barcodePickerGui));
cameraManager.setInteractionOptions(false, false, false, false);
t.false(cameraManager.isTapToFocusEnabled());
t.false(cameraManager.isPinchToZoomEnabled());
t.is(videoElementAddEventListener.callCount, 0);
cameraManager.setTapToFocusEnabled(true);
cameraManager.setPinchToZoomEnabled(true);
t.is(videoElementAddEventListener.callCount, 0);
fakeMediaStream(cameraManager);
cameraManager.setTapToFocusEnabled(true);
t.is(videoElementAddEventListener.callCount, 2);
t.true(videoElementAddEventListener.calledWith("mousedown"));
t.true(videoElementAddEventListener.calledWith("touchend"));
cameraManager.setPinchToZoomEnabled(true);
t.is(videoElementAddEventListener.callCount, 4);
t.true(videoElementAddEventListener.calledWith("touchstart"));
t.true(videoElementAddEventListener.calledWith("touchmove"));
t.true(cameraManager.isTapToFocusEnabled());
t.true(cameraManager.isPinchToZoomEnabled());
t.is(videoElementRemoveEventListener.callCount, 0);
cameraManager.setTapToFocusEnabled(false);
t.is(videoElementRemoveEventListener.callCount, 2);
t.true(videoElementRemoveEventListener.calledWith("mousedown"));
t.true(videoElementRemoveEventListener.calledWith("touchend"));
cameraManager.setPinchToZoomEnabled(false);
t.is(videoElementRemoveEventListener.callCount, 4);
t.true(videoElementRemoveEventListener.calledWith("touchstart"));
t.true(videoElementRemoveEventListener.calledWith("touchmove"));
t.false(cameraManager.isTapToFocusEnabled());
t.false(cameraManager.isPinchToZoomEnabled());
});
test("setTorchEnabled & toggleTorch", async t => {
const barcodePickerGui: sinon.SinonStubbedInstance<BarcodePickerGui> = sinon.createStubInstance(BarcodePickerGui);
const cameraManager: BarcodePickerCameraManager = new BarcodePickerCameraManager(triggerFatalErrorSpy, <
BarcodePickerGui
>(<unknown>barcodePickerGui));
await cameraManager.setTorchEnabled(true);
const mediaTrackCapabilities: ExtendedMediaTrackCapabilities = {
torch: true
};
const applyConstraintsStub: sinon.SinonStub = <any>(
fakeMediaStream(cameraManager, mediaTrackCapabilities).getVideoTracks()[0].applyConstraints
);
t.true(applyConstraintsStub.notCalled);
await cameraManager.setTorchEnabled(true);
t.true(applyConstraintsStub.calledOnce);
t.true(applyConstraintsStub.calledWith({ advanced: [{ torch: true }] }));
await cameraManager.setTorchEnabled(false);
t.true(applyConstraintsStub.calledTwice);
t.true(applyConstraintsStub.calledWith({ advanced: [{ torch: false }] }));
applyConstraintsStub.resetHistory();
await cameraManager.toggleTorch();
t.true(applyConstraintsStub.calledOnce);
t.true(applyConstraintsStub.calledWith({ advanced: [{ torch: true }] }));
await cameraManager.toggleTorch();
t.true(applyConstraintsStub.calledTwice);
t.true(applyConstraintsStub.calledWith({ advanced: [{ torch: false }] }));
});
test("setZoom", async t => {
const barcodePickerGui: sinon.SinonStubbedInstance<BarcodePickerGui> = sinon.createStubInstance(BarcodePickerGui);
const cameraManager: BarcodePickerCameraManager = new BarcodePickerCameraManager(triggerFatalErrorSpy, <
BarcodePickerGui
>(<unknown>barcodePickerGui));
await cameraManager.setZoom(2);
const mediaTrackCapabilities: ExtendedMediaTrackCapabilities = {
zoom: {
max: 9,
min: 1,
step: 0.1
}
};
const applyConstraintsStub: sinon.SinonStub = <any>(
fakeMediaStream(cameraManager, mediaTrackCapabilities).getVideoTracks()[0].applyConstraints
);
t.true(applyConstraintsStub.notCalled);
await cameraManager.setZoom(0);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 1 }] }]);
await cameraManager.setZoom(1);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 9 }] }]);
await cameraManager.setZoom(0.5);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 5 }] }]);
await cameraManager.setZoom(10);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 9 }] }]);
await cameraManager.setZoom(0.25, 5);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 7 }] }]);
});
test("triggerZoomStart & triggerZoomMove", async t => {
const barcodePickerGui: sinon.SinonStubbedInstance<BarcodePickerGui> = sinon.createStubInstance(BarcodePickerGui);
const cameraManager: BarcodePickerCameraManager = new BarcodePickerCameraManager(triggerFatalErrorSpy, <
BarcodePickerGui
>(<unknown>barcodePickerGui));
const touchStartEvent: any = {
preventDefault: sinon.spy(),
type: "touchstart"
};
const touchStart0xEvent: any = {
...touchStartEvent,
touches: [
{
screenX: 0,
screenY: 0
},
{
screenX: 0,
screenY: 0
}
]
};
const touchStart25xEvent: any = {
...touchStartEvent,
touches: [
{
screenX: 0,
screenY: 0
},
{
screenX: 25,
screenY: 0
}
]
};
const touchStart50xEvent: any = {
...touchStartEvent,
touches: [
{
screenX: 0,
screenY: 0
},
{
screenX: 50,
screenY: 0
}
]
};
(<any>cameraManager).triggerZoomStart({
...touchStartEvent,
touches: [1]
});
(<any>cameraManager).triggerZoomMove({
...touchStartEvent,
touches: [1]
});
(<any>cameraManager).triggerZoomStart(touchStart25xEvent);
const mediaTrackCapabilities: ExtendedMediaTrackCapabilities = {
torch: true,
zoom: {
max: 9,
min: 1,
step: 0.1
}
};
const applyConstraintsStub: sinon.SinonStub = <any>(
fakeMediaStream(cameraManager, mediaTrackCapabilities).getVideoTracks()[0].applyConstraints
);
t.true(applyConstraintsStub.notCalled);
(<any>cameraManager).triggerZoomStart(touchStart0xEvent);
await cameraManager.setTorchEnabled(true);
(<any>cameraManager).triggerZoomStart(touchStart0xEvent);
(<any>cameraManager).triggerZoomMove(touchStart0xEvent);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 1 }] }]);
(<any>cameraManager).triggerZoomMove(touchStart25xEvent);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 5 }] }]);
(<any>cameraManager).triggerZoomMove(touchStart50xEvent);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 9 }] }]);
(<any>cameraManager).triggerZoomStart(touchStart25xEvent);
(<any>cameraManager).triggerZoomMove(touchStart0xEvent);
t.deepEqual(applyConstraintsStub.lastCall.args, [{ advanced: [{ zoom: 5 }] }]);
});
// tslint:disable-next-line:max-func-body-length
test("manual / auto focus", async t => {
const barcodePickerGui: sinon.SinonStubbedInstance<BarcodePickerGui> = sinon.createStubInstance(BarcodePickerGui);
const cameraManager: BarcodePickerCameraManager = new BarcodePickerCameraManager(triggerFatalErrorSpy, <
BarcodePickerGui
>(<unknown>barcodePickerGui));
(<any>cameraManager).triggerManualFocus({
preventDefault: sinon.spy(),
type: "touchend",
touches: [1, 2]
});
(<any>cameraManager).pinchToZoomDistance = 1;
(<any>cameraManager).triggerManualFocus({
preventDefault: sinon.spy(),
type: "mousedown"
});
(<any>cameraManager).triggerManualFocus({
preventDefault: sinon.spy(),
type: "touchend",
touches: []
});
// Trigger manual focus when not supported
let mediaTrackCapabilities: ExtendedMediaTrackCapabilities = {};
let applyConstraintsStub: sinon.SinonStub = <any>(
fakeMediaStream(cameraManager, mediaTrackCapabilities).getVideoTracks()[0].applyConstraints
);
t.true(applyConstraintsStub.notCalled);
(<any>cameraManager).triggerManualFocus();
t.true(applyConstraintsStub.notCalled);
mediaTrackCapabilities = {
focusMode: [MeteringMode.SINGLE_SHOT, MeteringMode.CONTINUOUS] // this is a weird mix
};
applyConstraintsStub = <any>(
fakeMediaStream(cameraManager, mediaTrackCapabilities).getVideoTracks()[0].applyConstraints
);
t.true(applyConstraintsStub.notCalled);
(<any>cameraManager).triggerManualFocus();
t.true(applyConstraintsStub.notCalled);
// Trigger manual focus when single-shot only is supported
mediaTrackCapabilities = {
focusMode: [MeteringMode.SINGLE_SHOT]
};
applyConstraintsStub = <any>(
fakeMediaStream(cameraManager, mediaTrackCapabilities).getVideoTracks()[0].applyConstraints
);
t.true(applyConstraintsStub.notCalled);
(<any>cameraManager).triggerManualFocus();
t.true(applyConstraintsStub.calledOnce);
t.true(applyConstraintsStub.calledWith({ advanced: [{ focusMode: MeteringMode.SINGLE_SHOT }] }));
// Enable background single-shot autofocus
applyConstraintsStub.resetHistory();
(<any>cameraManager).storeStreamCapabilities();
(<any>cameraManager).setupAutofocus();
await wait((<any>BarcodePickerCameraManager).autofocusIntervalMs * 4);
t.true(applyConstraintsStub.callCount >= 2);
t.true(applyConstraintsStub.alwaysCalledWith({ advanced: [{ focusMode: MeteringMode.SINGLE_SHOT }] }));
// Trigger manual focus when single-shot only is supported (while background single-shot autofocus is active)
(<any>cameraManager).triggerManualFocus();
applyConstraintsStub.resetHistory();
// Background single-shot autofocus should be disabled for a while
await wait((<any>BarcodePickerCameraManager).autofocusIntervalMs * 2);
t.true(applyConstraintsStub.notCalled);
await wait((<any>BarcodePickerCameraManager).manualToAutofocusResumeTimeoutMs * 2);
// Background single-shot autofocus should be enabled now
t.true(applyConstraintsStub.called);
t.true(applyConstraintsStub.alwaysCalledWith({ advanced: [{ focusMode: MeteringMode.SINGLE_SHOT }] }));
cameraManager.stopStream();
// Trigger manual focus when all focus modes are supported
mediaTrackCapabilities = {
focusMode: [MeteringMode.SINGLE_SHOT, MeteringMode.CONTINUOUS, MeteringMode.MANUAL]
};
applyConstraintsStub = <any>(
fakeMediaStream(cameraManager, mediaTrackCapabilities).getVideoTracks()[0].applyConstraints
);
(<any>cameraManager).triggerManualFocus();
t.true(applyConstraintsStub.calledOnce);
t.true(applyConstraintsStub.calledWith({ advanced: [{ focusMode: MeteringMode.CONTINUOUS }] }));
applyConstraintsStub.resetHistory();
await wait((<any>BarcodePickerCameraManager).manualFocusWaitTimeoutMs * 2);
t.true(applyConstraintsStub.calledOnce);
t.true(applyConstraintsStub.calledWith({ advanced: [{ focusMode: MeteringMode.MANUAL }] }));
applyConstraintsStub.resetHistory();
await wait((<any>BarcodePickerCameraManager).manualToAutofocusResumeTimeoutMs * 2);
t.true(applyConstraintsStub.calledOnce);
t.true(applyConstraintsStub.calledWith({ advanced: [{ focusMode: MeteringMode.CONTINUOUS }] }));
});
// tslint:disable-next-line:max-func-body-length
test.serial("setupCameras", async t => {
const barcodePickerGui: sinon.SinonStubbedInstance<BarcodePickerGui> = sinon.createStubInstance(BarcodePickerGui);
const videoElementRemoveEventListener: sinon.SinonSpy = sinon.spy();
(<any>barcodePickerGui).videoElement = {
loadedmetadataEventListener: null,
addEventListener(eventType: string, listener: (this: HTMLVideoElement, ev: Event) => any): void {
if (eventType === "loadedmetadata") {
this.loadedmetadataEventListener = listener;
}
},
removeEventListener: videoElementRemoveEventListener,
dispatchEvent: sinon.spy()
};
(<any>barcodePickerGui).videoElement.load = function(): void {
this.loadedmetadataEventListener();
this.videoWidth = 640;
this.videoHeight = 480;
this.currentTime = 0;
this.onloadeddata();
setTimeout(() => {
this.currentTime = 1;
}, (<any>BarcodePickerCameraManager).cameraMetadataCheckIntervalMs * 2);
};
const cameraManager: BarcodePickerCameraManager = new BarcodePickerCameraManager(triggerFatalErrorSpy, <
BarcodePickerGui
>(<unknown>barcodePickerGui));
cameraManager.setInteractionOptions(true, true, true, true);
t.true(cameraManager.isCameraSwitcherEnabled());
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 0);
// Intentionally make optimistic initial back camera access fail
fakeAccessCameraStream("user");
fakeGetCameras(2, [Camera.Type.FRONT, Camera.Type.FRONT]);
await cameraManager.setupCameras();
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 1);
barcodePickerGui.setCameraSwitcherVisible.resetHistory();
t.is((<sinon.SinonSpy>CameraAccess.accessCameraStream).callCount, 2);
t.is((<sinon.SinonSpy>CameraAccess.getCameras).callCount, 1);
cameraManager.selectedCamera = undefined;
fakeAccessCameraStream("user");
fakeGetCameras(2, [Camera.Type.BACK, Camera.Type.FRONT]);
await cameraManager.setupCameras();
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 1);
barcodePickerGui.setCameraSwitcherVisible.resetHistory();
t.is((<sinon.SinonSpy>CameraAccess.accessCameraStream).callCount, 2);
t.is((<sinon.SinonSpy>CameraAccess.getCameras).callCount, 1);
cameraManager.selectedCamera = undefined;
fakeAccessCameraStream("user");
fakeGetCameras(0);
let error: Error = await t.throwsAsync(cameraManager.setupCameras());
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 0);
t.is(error.message, "No camera available");
// Access primary back camera in common triple camera setups
cameraManager.selectedCamera = undefined;
fakeAccessCameraStream("user");
fakeGetCameras(
3,
[Camera.Type.FRONT, Camera.Type.BACK, Camera.Type.BACK],
["", "camera2 2, facing back", "camera2 0, facing back"]
);
await cameraManager.setupCameras();
t.not(cameraManager.selectedCamera, null);
t.is((<Camera>(<unknown>cameraManager.selectedCamera)).label, "camera2 0, facing back");
let mediaTrackCapabilities: ExtendedMediaTrackCapabilities = {
torch: true
};
cameraManager.selectedCamera = undefined;
fakeAccessCameraStream("environment", mediaTrackCapabilities);
fakeGetCameras(1);
barcodePickerGui.setCameraSwitcherVisible.resetHistory();
await cameraManager.setupCameras();
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 0);
t.is((<sinon.SinonSpy>CameraAccess.accessCameraStream).callCount, 2);
t.is((<sinon.SinonSpy>CameraAccess.getCameras).callCount, 1);
await wait((<any>BarcodePickerCameraManager).getCapabilitiesTimeoutMs * 2);
t.deepEqual((<any>cameraManager).mediaTrackCapabilities, mediaTrackCapabilities);
mediaTrackCapabilities = {
torch: false,
focusMode: [MeteringMode.SINGLE_SHOT]
};
cameraManager.selectedCamera = undefined;
fakeAccessCameraStream("environment", mediaTrackCapabilities);
fakeGetCameras(2);
await cameraManager.setupCameras();
t.is(barcodePickerGui.setCameraSwitcherVisible.callCount, 1);
t.is((<sinon.SinonSpy>CameraAccess.accessCameraStream).callCount, 2);
t.is((<sinon.SinonSpy>CameraAccess.getCameras).callCount, 1);
await wait((<any>BarcodePickerCameraManager).getCapabilitiesTimeoutMs * 2);
t.deepEqual((<any>cameraManager).mediaTrackCapabilities, mediaTrackCapabilities);
(<any>barcodePickerGui).videoElement.load = function(): void {
this.loadedmetadataEventListener();
this.videoWidth = 640;
this.videoHeight = 480;
this.currentTime = 0;
this.onloadeddata();
// Intentionally never have valid metadata
};
error = await t.throwsAsync(cameraManager.setupCameras());
t.is(error.message, "Could not initialize camera correctly");
(<any>barcodePickerGui).videoElement.load = function(): void {
this.loadedmetadataEventListener();
// Intentionally never call onloadeddata()
};
error = await t.throwsAsync(cameraManager.setupCameras());
t.is(error.message, "Could not initialize camera correctly");
fakeAccessCameraStreamFailure(new Error("Test error 1"));
fakeGetCameras(1);
cameraManager.selectedCamera = undefined;
error = await t.throwsAsync(cameraManager.setupCameras());
t.is(error.message, "Test error 1");
t.true(
(<sinon.SinonSpy>CameraAccess.accessCameraStream)
.getCall(0)
.calledBefore((<sinon.SinonSpy>CameraAccess.getCameras).firstCall)
);
t.true(
(<sinon.SinonSpy>CameraAccess.accessCameraStream)
.getCall(4)
.calledAfter((<sinon.SinonSpy>CameraAccess.getCameras).firstCall)
);
t.is((<sinon.SinonSpy>CameraAccess.accessCameraStream).callCount, 8); // 2 times 4 calls (resolution fallbacks)
t.is((<sinon.SinonSpy>CameraAccess.getCameras).callCount, 1);
fakeAccessCameraStreamFailure(new Error("Test error 2"));
fakeGetCameras(1);
error = await t.throwsAsync(cameraManager.setupCameras());
t.is(error.message, "Test error 2");
t.true(
(<sinon.SinonSpy>CameraAccess.accessCameraStream)
.getCall(0)
.calledAfter((<sinon.SinonSpy>CameraAccess.getCameras).firstCall)
);
t.is((<sinon.SinonSpy>CameraAccess.accessCameraStream).callCount, 4); // 1 time 4 calls (resolution fallbacks)
t.is((<sinon.SinonSpy>CameraAccess.getCameras).callCount, 1);
});