Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,658 @@
suite('lib/camera-coordinates', function() {
/*jshint maxlen:false*/
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs(['lib/camera-coordinates'], function(cameraCoordinates) {
// alias
self.faceToCamera = cameraCoordinates.faceToCamera;
self.faceToPixels = cameraCoordinates.faceToPixels;
self.areaToCamera = cameraCoordinates.private.areaToCamera;
self.areaToPixels = cameraCoordinates.private.areaToPixels;
self.mirrorAreaCamera = cameraCoordinates.private.mirrorAreaCamera;
self.mirrorAreaPixels = cameraCoordinates.private.mirrorAreaPixels;
self.rotateArea = cameraCoordinates.private.rotateArea;
self.sizeToCamera = cameraCoordinates.private.sizeToCamera;
self.sizeToPixels = cameraCoordinates.private.sizeToPixels;
self.toCamera = cameraCoordinates.private.toCamera;
self.toPixels = cameraCoordinates.private.toPixels;
self.rotatePoint = cameraCoordinates.private.rotatePoint;
self.equalAreas = function(area1, area2) {
var equal = area1.top === area2.top &&
area1.left === area2.left &&
area1.bottom === area2.bottom &&
area1.right === area2.right &&
area1.width === area2.width &&
area1.height === area2.height;
if (!equal) {
console.log('Areas are not equal: ' +
JSON.stringify(area1) + ' /= ' + JSON.stringify(area2));
}
return equal;
};
done();
});
this.viewportWidth = 800;
this.viewportHeight = 600;
});
suite('#faceToCamera', function() {
suite('The face should be converted from pixel coordinates to camera and back to pixels', function() {
test('face is at the center of the viewport', function() {
var face = {
top: 200,
left: 300,
bottom: 400,
right: 600,
height: 200,
width: 300
};
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, facePixelCooordinates));
});
test('face is at the top left quadrant of the viewport', function() {
var face = {
top: 0,
left: 0,
bottom: 230,
right: 230,
height: 230,
width: 230
};
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, facePixelCooordinates));
});
test('face is at the top right quadrant of the viewport', function() {
var face = {
top: 50,
left: 500,
bottom: 80,
right: 530,
height: 30,
width: 30
};
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, facePixelCooordinates));
});
test('face is at the bottom left quadrant of the viewport', function() {
var face = {
top: 400,
left: 200,
bottom: 475,
right: 275,
height: 75,
width: 75
};
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, facePixelCooordinates));
});
test('face is at the bottom right quadrant of the viewport', function() {
var face = {
top: 400,
left: 500,
bottom: 500,
right: 600,
height: 100,
width: 100
};
var faceCameraCoordinates = this.faceToCamera(face, this.viewportWidth, this.viewportHeight, 0);
var facePixelCooordinates = this.faceToPixels(faceCameraCoordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, facePixelCooordinates));
});
});
});
suite('#faceToPixels', function() {
suite('The face should be converted from camera to pixel coordinates and back to camera coordinates', function() {
test('face is at the center of the viewport', function() {
// var face = {
// top: -25,
// left: -25,
// bottom: 25,
// right: 25,
// height: 50,
// width: 50
// };
var face = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, faceCameraCoordinates));
});
test('face is at the top left quadrant of the viewport', function() {
var face = {
top: -1000,
left: -1000,
bottom: -950,
right: -950,
height: 50,
width: 50
};
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, faceCameraCoordinates));
});
test('face is at the top right quadrant of the viewport', function() {
var face = {
top: -500,
left: 500,
bottom: -450,
right: 550,
height: 50,
width: 50
};
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, faceCameraCoordinates));
});
test('face is at the bottom left quadrant of the viewport', function() {
var face = {
top: 500,
left: -500,
bottom: 550,
right: -450,
height: 50,
width: 50
};
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, faceCameraCoordinates));
});
test('face is at the bottom right quadrant of the viewport', function() {
var face = {
top: 500,
left: 500,
bottom: 550,
right: 550,
height: 50,
width: 50
};
var facePixelCooordinates = this.faceToPixels(face, this.viewportWidth, this.viewportHeight, 0);
var faceCameraCoordinates = this.faceToCamera(facePixelCooordinates, this.viewportWidth, this.viewportHeight, 0);
assert.ok(this.equalAreas(face, faceCameraCoordinates));
});
});
});
suite('#areaToCamera', function() {
suite('The area should be converted from pixel coordinates to camera and back to pixels', function() {
test('area is at the center of the viewport', function() {
var area = {
top: 200,
left: 300,
bottom: 400,
right: 600,
height: 200,
width: 300
};
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaPixels));
});
test('area is at the top left quadrant of the viewport', function() {
var area = {
top: 0,
left: 0,
bottom: 230,
right: 230,
height: 230,
width: 230
};
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaPixels));
});
test('area is at the top right quadrant of the viewport', function() {
var area = {
top: 50,
left: 500,
bottom: 80,
right: 530,
height: 30,
width: 30
};
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaPixels));
});
test('area is at the bottom left quadrant of the viewport', function() {
var area = {
top: 400,
left: 200,
bottom: 475,
right: 275,
height: 75,
width: 75
};
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaPixels));
});
test('area is at the bottom right quadrant of the viewport', function() {
var area = {
top: 400,
left: 500,
bottom: 500,
right: 600,
height: 100,
width: 100
};
var areaCamera = this.areaToCamera(area, this.viewportWidth, this.viewportHeight);
var areaPixels = this.areaToPixels(areaCamera, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaPixels));
});
});
});
suite('#areaToPixels', function() {
suite('The area should be converted from camera to pixel coordinates and back to camera coordinates', function() {
test('area is at the center of the viewport', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaCamera));
});
test('area is at the top left quadrant of the viewport', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaCamera));
});
test('area is at the top right quadrant of the viewport', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaCamera));
});
test('area is at the bottom left quadrant of the viewport', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaCamera));
});
test('area is at the bottom right quadrant of the viewport', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var areaPixels = this.areaToPixels(area, this.viewportWidth, this.viewportHeight);
var areaCamera = this.areaToCamera(areaPixels, this.viewportWidth, this.viewportHeight);
assert.ok(this.equalAreas(area, areaCamera));
});
});
});
suite('#mirrorAreaCamera', function() {
suite('The area should be properly mirrored', function() {
test('area is at the center of the viewport', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var mirroredArea = this.mirrorAreaCamera(area);
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
assert.ok(this.equalAreas(area, doubledMirrorArea));
});
test('area is at the top left quadrant of the viewport', function() {
var area = {
top: -100,
left: -100,
bottom: -50,
right: -50,
height: 50,
width: 50
};
var mirroredArea = this.mirrorAreaCamera(area);
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
assert.ok(this.equalAreas(area, doubledMirrorArea));
});
test('area is at the top right quadrant of the viewport', function() {
var area = {
top: -100,
left: 300,
bottom: -50,
right: 350,
height: 50,
width: 50
};
var mirroredArea = this.mirrorAreaCamera(area);
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
assert.ok(this.equalAreas(area, doubledMirrorArea));
});
test('area is at the bottom left quadrant of the viewport', function() {
var area = {
top: 100,
left: -200,
bottom: 200,
right: -100,
height: 100,
width: 100
};
var mirroredArea = this.mirrorAreaCamera(area);
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
assert.ok(this.equalAreas(area, doubledMirrorArea));
});
test('area is at the bottom right quadrant of the viewport', function() {
var area = {
top: 800,
left: 800,
bottom: 900,
right: 900,
height: 100,
width: 100
};
var mirroredArea = this.mirrorAreaCamera(area);
var doubledMirrorArea = this.mirrorAreaCamera(mirroredArea);
assert.ok(this.equalAreas(area, doubledMirrorArea));
});
});
});
suite('#mirrorAreaPixels', function() {
suite('The area should be properly mirrored', function() {
test('area is at the center of the viewport', function() {
var area = {
top: 100,
left: 300,
bottom: 200,
right: 400,
height: 100,
width: 100
};
var mirroredArea = this.mirrorAreaPixels(area, this.viewportWidth);
var doubledMirrorArea = this.mirrorAreaPixels(mirroredArea, this.viewportWidth);
assert.ok(this.equalAreas(area, doubledMirrorArea));
});
});
});
suite('#rotateArea', function() {
suite('The area should be properly rotated', function() {
test('area rotates 0 degrees', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var expectedResult = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var rotatedArea = this.rotateArea(area, 0);
assert.ok(this.equalAreas(rotatedArea, expectedResult));
});
test('area rotates 90 degrees', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var expectedResult = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var rotatedArea = this.rotateArea(area, 90);
assert.ok(this.equalAreas(rotatedArea, expectedResult));
});
test('area rotates 180 degrees', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var expectedResult = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var rotatedArea = this.rotateArea(area, 180);
assert.ok(this.equalAreas(rotatedArea, expectedResult));
});
test('area rotates 270 degrees', function() {
var area = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var expectedResult = {
top: -50,
left: -50,
bottom: 50,
right: 50,
height: 100,
width: 100
};
var rotatedArea = this.rotateArea(area, 270);
assert.ok(this.equalAreas(rotatedArea, expectedResult));
});
});
});
suite('#sizeToCamera', function() {
test('The size should be converted from pixels to camera coordinates and back to pixels', function() {
var width = 50;
var height = 100;
var sizeCamera = this.sizeToCamera(width, height, this.viewportWidth, this.viewportHeight);
var sizePixels = this.sizeToPixels(sizeCamera.width, sizeCamera.height, this.viewportWidth, this.viewportHeight);
assert.ok(sizePixels.width === width);
assert.ok(sizePixels.height === height);
});
});
suite('#sizeToPixels', function() {
test('The size should be converted from camera coordinates to pixels and back to camera', function() {
var width = 50;
var height = 100;
var sizePixels = this.sizeToPixels(width, height, this.viewportWidth, this.viewportHeight);
var sizeCamera = this.sizeToCamera(sizePixels.width, sizePixels.height, this.viewportWidth, this.viewportHeight);
assert.ok(sizeCamera.width === width);
assert.ok(sizeCamera.height === height);
});
});
suite('#toCamera', function() {
test('The coordinate should be transformed from pixels to camera coordinates', function() {
var x = 50;
var y = 100;
var camera = this.toCamera(x, y, this.viewportWidth, this.viewportHeight);
var pixels = this.toPixels(camera.x, camera.y, this.viewportWidth, this.viewportHeight);
assert.ok(pixels.x === x);
assert.ok(pixels.y === y);
});
});
suite('#toPixels', function() {
test('The coordinate should be transformed from camera coordinates to pixels', function() {
var x = 50;
var y = 100;
var pixels = this.toPixels(x, y, this.viewportWidth, this.viewportHeight);
var camera = this.toCamera(pixels.x, pixels.y, this.viewportWidth, this.viewportHeight);
assert.ok(camera.x === x);
assert.ok(camera.y === y);
});
});
suite('#rotatePoint', function() {
test('The point coordinates should rotate 0 degrees', function() {
var x = 50;
var y = 100;
var point = this.rotatePoint(x, y, 0);
assert.ok(x === point.x);
assert.ok(y === point.y);
});
test('The point coordinates should rotate 90 degrees', function() {
var x = 760;
var y = 134;
var point = this.rotatePoint(x, y, 90);
assert.ok(x === point.y);
assert.ok(y === -point.x);
});
test('The point coordinates should rotate 180 degrees', function() {
var x = 34;
var y = 100;
var point = this.rotatePoint(x, y, 180);
assert.ok(x === -point.x);
assert.ok(y === -point.y);
});
test('The point coordinates should rotate 270 degrees', function() {
var x = 200;
var y = 200;
var point = this.rotatePoint(x, y, 270);
assert.ok(x === -point.y);
assert.ok(y === point.x);
});
test('Angle should be clamped to 0 degrees', function() {
var x = 0;
var y = 240;
var point1 = this.rotatePoint(x, y, 0);
var point2 = this.rotatePoint(x, y, -20);
assert.ok(point1.x === point2.x);
assert.ok(point1.y === point2.y);
});
test('Angle should be clamped to 90 degrees', function() {
var x = 123;
var y = -321;
var point1 = this.rotatePoint(x, y, 90);
var point2 = this.rotatePoint(x, y, -260);
assert.ok(point1.x === point2.x);
assert.ok(point1.y === point2.y);
});
test('Angle should be clamped to 90 degrees', function() {
var x = 123;
var y = 321;
var point1 = this.rotatePoint(x, y, 90);
var point2 = this.rotatePoint(x, y, 123);
assert.ok(point1.x === point2.x);
assert.ok(point1.y === point2.y);
});
test('Angle should be clamped to 180 degrees', function() {
var x = -100;
var y = 100;
var point1 = this.rotatePoint(x, y, 180);
var point2 = this.rotatePoint(x, y, 140);
assert.ok(point1.x === point2.x);
assert.ok(point1.y === point2.y);
});
test('Angle should be clamped to 270 degrees', function() {
var x = -500;
var y = -500;
var point1 = this.rotatePoint(x, y, 230);
var point2 = this.rotatePoint(x, y, 270);
assert.ok(point1.x === point2.x);
assert.ok(point1.y === point2.y);
});
});
});

View file

@ -0,0 +1,235 @@
/*jshint maxlen:false*/
/*global req*/
'use strict';
suite('utils/camera-utils', function() {
var CameraUtils;
suiteSetup(function(done) {
var self = this;
this.modules = {};
requirejs([
'lib/camera-utils'
], function(_CameraUtils) {
CameraUtils = self.modules.CameraUtils = _CameraUtils;
done();
});
});
setup(function() {
});
suite('scaleSizeToFitViewport', function() {
setup(function() {
});
test('Should scale image size to *FIT* viewport ' +
'preserving aspect ratio', function() {
var viewportSize, imageSize, outputSize;
// Case where image size exactly matches viewport size
viewportSize = {
width: 720,
height: 1280
};
imageSize = {
width: 720,
height: 1280
};
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 720);
assert.equal(outputSize.height, 1280);
// Case where image size has larger width, smaller height than viewport
viewportSize = {
width: 720,
height: 1280
};
imageSize = {
width: 800,
height: 1280
};
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 720);
assert.equal(outputSize.height, 1152);
// Case where image size has larger height, smaller width than viewport
viewportSize = {
width: 720,
height: 1280
};
imageSize = {
width: 720,
height: 1440
};
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 640);
assert.equal(outputSize.height, 1280);
// Case where image size has larger width, larger height than viewport
// (different aspect ratio)
viewportSize = {
width: 600,
height: 800
};
imageSize = {
width: 720,
height: 1152
};
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 500);
assert.equal(outputSize.height, 800);
// Case where image size has larger width, larger height than viewport
// (same aspect ratio)
viewportSize = {
width: 720,
height: 1280
};
imageSize = {
width: 1080,
height: 1920
};
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 720);
assert.equal(outputSize.height, 1280);
// Case where image size has smaller width, smaller height than viewport
// (different aspect ratio)
viewportSize = {
width: 720,
height: 1152
};
imageSize = {
width: 600,
height: 800
};
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 720);
assert.equal(outputSize.height, 960);
// Case where image size has smaller width, smaller height than viewport
// (same aspect ratio)
viewportSize = {
width: 1080,
height: 1920
};
imageSize = {
width: 720,
height: 1280
};
outputSize = CameraUtils.scaleSizeToFitViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 1080);
assert.equal(outputSize.height, 1920);
});
});
suite('scaleSizeToFillViewport', function() {
setup(function() {});
test('Should scale image size to *FILL* viewport ' +
'preserving aspect ratio', function() {
var viewportSize, imageSize, outputSize;
// Case where image size exactly matches viewport size
viewportSize = {
width: 720,
height: 1280
};
imageSize = {
width: 720,
height: 1280
};
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 720);
assert.equal(outputSize.height, 1280);
// Case where image size has larger width, smaller height than viewport
viewportSize = {
width: 720,
height: 1280
};
imageSize = {
width: 800,
height: 1280
};
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 800);
assert.equal(outputSize.height, 1280);
// Case where image size has larger height, smaller width than viewport
viewportSize = {
width: 720,
height: 1280
};
imageSize = {
width: 720,
height: 1440
};
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 720);
assert.equal(outputSize.height, 1440);
// Case where image size has larger width, larger height than viewport
// (different aspect ratio)
viewportSize = {
width: 600,
height: 800
};
imageSize = {
width: 720,
height: 1152
};
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 600);
assert.equal(outputSize.height, 960);
// Case where image size has larger width, larger height than viewport
// (same aspect ratio)
viewportSize = {
width: 720,
height: 1280
};
imageSize = {
width: 1080,
height: 1920
};
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 720);
assert.equal(outputSize.height, 1280);
// Case where image size has smaller width, smaller height than viewport
// (different aspect ratio)
viewportSize = {
width: 720,
height: 1152
};
imageSize = {
width: 600,
height: 800
};
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 864);
assert.equal(outputSize.height, 1152);
// Case where image size has smaller width, smaller height than viewport
// (same aspect ratio)
viewportSize = {
width: 1080,
height: 1920
};
imageSize = {
width: 720,
height: 1280
};
outputSize = CameraUtils.scaleSizeToFillViewport(viewportSize, imageSize);
assert.equal(outputSize.width, 1080);
assert.equal(outputSize.height, 1920);
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,677 @@
suite('lib/camera/focus', function() {
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs(['lib/camera/focus'], function(Focus) {
self.Focus = Focus;
done();
});
});
setup(function() {
this.sandbox = sinon.sandbox.create();
// Fake mozCamera
this.mozCamera = {
_listeners: {},
capabilities: {
focusModes : ["auto", "infinity", "normal",
"macro", "continuous-picture", "continuous-video" ],
},
autoFocus: sinon.stub().returns({
then: function(onSuccess, onError) {
onSuccess(true);
}
}),
setFocusAreas: sinon.stub(),
setMeteringAreas: sinon.stub(),
stopContinuousFocus: sinon.stub(),
resumeContinuousFocus: sinon.stub(),
startFaceDetection: sinon.spy(),
stopFaceDetection: sinon.spy(),
addEventListener: function(eventname, listener) {
this._listeners[eventname] = listener;
},
removeEventListener: function(eventname, listener) {
if (this._listeners.hasOwnProperty(eventname)) {
if (this._listeners[eventname] === listener) {
delete this._listeners[eventname];
}
}
}
};
this.focus = new this.Focus({});
this.focus.mozCamera = this.mozCamera;
});
suite('Focus#configure()', function() {
setup(function() {
this.focus.configureFocusModes = sinon.spy();
});
test('mozCamera should be assigned \'auto\' mode by default', function() {
this.focus.configure(this.mozCamera);
assert.ok(this.focus.configureFocusModes.called);
assert.equal(this.mozCamera.focusMode, 'auto');
});
test('user preferences override defaults', function() {
this.focus.continuousAutoFocus = false;
this.focus.configure(this.mozCamera, 'continuous-picture');
assert.ok(this.focus.configureFocusModes.called);
assert.equal(this.mozCamera.focusMode, 'auto');
});
test('continuous-picture selected on picture mode if user pref is set to CAF', function() {
this.focus.continuousAutoFocus = true;
this.focus.configure(this.mozCamera, 'picture');
assert.ok(this.focus.configureFocusModes.called);
assert.equal(this.mozCamera.focusMode, 'continuous-picture');
});
test('continuous-video selected on video mode if user pref is set to CAF', function() {
this.focus.continuousAutoFocus = true;
this.focus.configure(this.mozCamera, 'video');
assert.ok(this.focus.configureFocusModes.called);
assert.equal(this.mozCamera.focusMode, 'continuous-video');
});
test('if not valid focus mode is passed the first available is picked', function() {
this.focus.configure(this.mozCamera, 'invalid-focus-mode');
assert.ok(this.focus.configureFocusModes.called);
assert.equal(this.mozCamera.focusMode, 'auto');
});
});
suite('Focus#getMode', function() {
setup(function() {
this.mozCamera.focusMode = 'auto';
});
test('it returns the current focus mode', function() {
this.focus.startFaceDetection();
assert.ok(this.focus.getMode() === 'auto');
});
test('it returns the suspended focus mode', function() {
this.mozCamera.focusMode = 'continuous-picture';
assert.ok(this.focus.getMode() === 'continuous-picture');
});
});
suite('Focus#configureFocusModes()', function() {
setup(function() {
var userPreferences = {
touchFocus: true,
faceDetection: false
};
this.focus = new this.Focus(userPreferences);
this.focus.mozCamera = this.mozCamera;
this.sandbox.stub(this.focus, 'isTouchFocusSupported');
this.sandbox.stub(this.focus, 'isFaceDetectionSupported');
this.sandbox.stub(this.focus, 'startFaceDetection');
this.focus.isTouchFocusSupported.returns(true);
this.focus.isFaceDetectionSupported.returns(true);
});
teardown(function() {
this.sandbox.restore();
});
test('continous autofocus is enabled by default if supported', function() {
this.focus.configureFocusModes();
assert.equal(this.focus.continuousAutoFocus, true);
});
test('continous autofocus is disabled if user preferences desable it', function() {
this.focus.userPreferences.continuousAutoFocus = false;
this.focus.configureFocusModes();
assert.equal(this.focus.continuousAutoFocus, false);
});
test('touch to focus is enabled by default if available', function() {
this.focus.configureFocusModes();
assert.equal(this.focus.touchFocus, true);
});
test('touch to focus is disabled if user preferences disable it', function() {
this.focus.userPreferences.touchFocus = false;
this.focus.configureFocusModes();
assert.equal(this.focus.touchFocus, false);
});
test('touch to focus is disabled if hardware doesn\'t support it', function() {
this.focus.isTouchFocusSupported.returns(false);
this.focus.configureFocusModes();
assert.equal(this.focus.touchFocus, false);
});
test('touch to focus disabled if hardware doesn\'t support it and even if user preferences enable it', function() {
this.focus.userPreferences.touchFocus = true;
this.focus.isTouchFocusSupported.returns(false);
this.focus.configureFocusModes('picture');
assert.equal(this.focus.touchFocus, false);
});
test('face detection is disabled if not supported by HW', function() {
this.focus.userPreferences.faceDetection = true;
this.focus.isFaceDetectionSupported.returns(false);
this.focus.configureFocusModes('picture');
assert.ok(!this.focus.faceDetection);
});
test('face detection is disabled if disabled in user preferences', function() {
this.focus.userPreferences.faceDetection = false;
this.focus.isFaceDetectionSupported.returns(true);
this.focus.configureFocusModes('picture');
assert.ok(!this.focus.faceDetection);
});
test('face detection is disabled in video mode', function() {
this.focus.userPreferences.faceDetection = true;
this.focus.isFaceDetectionSupported.returns(true);
this.focus.configureFocusModes('video');
assert.ok(!this.focus.faceDetection);
});
test('face detection is enabled in picture mode if supported by HW and enabled in user preferences', function() {
this.focus.userPreferences.faceDetection = true;
this.focus.isFaceDetectionSupported.returns(true);
this.focus.configureFocusModes('picture');
assert.ok(this.focus.faceDetection);
});
});
suite('Focus#startFaceDetection', function() {
setup(function() {
this.focus.faceDetection = true;
});
test('mozCamera startFaceDetection is called to start face detection if face detection enabled', function() {
this.focus.startFaceDetection();
assert.ok(this.mozCamera.startFaceDetection.called);
});
test('mozCamera startFaceDetection is not called to start face detection if face detection disabled', function() {
this.focus.faceDetection = false;
this.focus.startFaceDetection();
assert.ok(!this.mozCamera.startFaceDetection.called);
});
});
suite('Focus#clearFaceDetection', function() {
setup(function() {
this.focus.faceDetection = true;
this.sandbox.spy(this.focus, 'focusOnLargestFace');
});
teardown(function() {
this.sandbox.restore();
});
test('focusOnLargestFace is called to start face detection if face detection enabled', function() {
this.focus.clearFaceDetection();
assert.ok(this.focus.focusOnLargestFace.calledWith([]));
});
test('focusOnLargestFace is called with empty list when clearing faces', function() {
this.focus.faceDetection = false;
this.focus.clearFaceDetection();
assert.ok(this.focus.focusOnLargestFace.calledWith([]));
});
});
suite('Focus#stopFaceDetection', function() {
setup(function() {
this.focus.faceDetection = true;
this.focus.mozCamera = this.mozCamera;
this.sandbox.spy(this.focus, 'stopFaceDetection');
this.sandbox.spy(this.focus, 'clearFaceDetection');
});
teardown(function() {
this.sandbox.restore();
});
test('mozCamera stopFaceDetection is called to stop face detection', function() {
this.focus.stopFaceDetection();
assert.ok(this.mozCamera.stopFaceDetection.called);
});
test('mozCamera stopFaceDetection is not called if face detection is not available', function() {
this.mozCamera.stopFaceDetection = false;
this.focus.stopFaceDetection();
assert.ok(!this.mozCamera.stopFaceDetection.called);
assert.ok(this.focus.clearFaceDetection.called);
});
});
suite('Focus#suspendFaceDetection', function() {
setup(function() {
this.clock = sinon.useFakeTimers();
this.focus.faceDetection = true;
this.focus.faceDetectionSuspended = undefined;
this.focus.faceFocused = true;
});
teardown(function() {
this.clock.restore();
});
test('mozCamera suspendFaceDetection resets timers', function() {
this.focus.suspendFaceDetection(500, 300);
this.clock.tick(500);
assert.ok(this.focus.faceDetectionSuspended);
this.clock.tick(300);
assert.ok(!this.focus.faceFocused);
});
});
suite('Focus#resumeContinuousFocus', function() {
setup(function() {
this.focus.mozCamera = this.mozCamera;
this.focus.previousMode = 'auto';
this.mozCamera.focusMode = 'continuous-picture';
});
test('mozCamera resumeContinuousFocus is called', function() {
this.focus.resumeContinuousFocus();
assert.ok(!this.focus.suspendedMode);
assert.ok(this.focus.mozCamera.resumeContinuousFocus.called);
});
});
suite('Focus#onfocus', function() {
setup(function() {
this.sandbox.spy(this.focus, 'updateFocusState');
});
teardown(function() {
this.sandbox.restore();
});
test('should call updateFocusState', function() {
this.focus.configureFocusModes();
var listener = this.mozCamera._listeners['focus'];
assert.ok(listener);
listener({ newState: 'focusing' });
assert.ok(this.focus.updateFocusState.calledWith('focusing'));
listener({ newState: 'focused' });
assert.ok(this.focus.updateFocusState.calledWith('focused'));
listener({ newState: 'unfocused' });
assert.ok(this.focus.updateFocusState.calledWith('fail'));
});
});
suite('Focus#updateFocusState', function() {
setup(function() {
this.sandbox.spy(this.focus, 'onAutoFocusChanged');
});
teardown(function() {
this.sandbox.restore();
});
test('should call onAutoFocusChanged', function() {
this.focus.focusState = 'focused';
this.focus.updateFocusState('focusing');
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
this.focus.updateFocusState('focused');
assert.ok(this.focus.onAutoFocusChanged.calledWith('focused'));
this.focus.updateFocusState('focusing');
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
this.focus.updateFocusState('fail');
assert.ok(this.focus.onAutoFocusChanged.calledWith('fail'));
});
test('should not call onAutoFocusChanged', function() {
this.focus.focusState = 'focused';
this.focus.updateFocusState('focused');
assert.ok(!this.focus.onAutoFocusChanged.called);
this.focus.updateFocusState('fail');
assert.ok(!this.focus.onAutoFocusChanged.called);
this.focus.focusState = 'focusing';
this.focus.updateFocusState('focusing');
assert.ok(!this.focus.onAutoFocusChanged.called);
});
});
suite('Focus#suspendContinuousFocus', function() {
setup(function() {
this.clock = sinon.useFakeTimers();
this.focus.mozCamera = this.mozCamera;
this.sandbox.spy(this.focus, 'resumeContinuousFocus');
this.focus.continuousModeTimer = undefined;
});
teardown(function() {
this.sandbox.restore();
this.clock.restore();
});
test('mozCamera resumeContinuousFocus is called', function() {
this.focus.suspendContinuousFocus(500);
assert.ok(this.focus.continuousModeTimer !== undefined);
this.clock.tick(500);
assert.ok(this.focus.resumeContinuousFocus.called);
});
});
suite('Focus#focusOnLargestFace', function() {
setup(function() {
this.sandbox.spy(this.focus, 'onFacesDetected');
});
teardown(function() {
this.sandbox.restore();
});
test('it should not focus on any face if face detection is suspended', function() {
this.focus.touchFocus = true;
this.focus.faceDetectionSuspended = true;
this.focus.focusOnLargestFace([{
id: 3,
score: 80,
bounds: {
height: 300,
width: 300
}
}]);
assert.ok(this.focus.onFacesDetected.calledWith([]));
});
test('it should focus on the singleton face', function() {
this.focus.touchFocus = true;
this.focus.faceDetectionSuspended = false;
var face = {
id: 3,
score: 80,
bounds: {
height: 300,
width: 300
}
};
this.focus.focusOnLargestFace([face]);
assert.ok(
Object.is(this.focus.onFacesDetected.firstCall.args[0][0], face)
);
});
test('it should focus on the singleton face (event)', function() {
this.focus.touchFocus = true;
this.focus.faceDetectionSuspended = false;
var face = {
id: 3,
score: 80,
bounds: {
height: 300,
width: 300
}
};
var event = { faces: [face] };
this.focus.handleFaceDetectionEvent(event);
assert.ok(
Object.is(this.focus.onFacesDetected.firstCall.args[0][0], face)
);
});
});
suite('Focus#focus()', function() {
setup(function() {
this.focus.focusMode = 'auto';
this.focus.onAutoFocusChanged = sinon.spy();
});
test('mozCamera autoFocus is called if focus mode is auto', function() {
this.mozCamera.focusMode = 'auto';
this.focus.focus(function() {});
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
assert.ok(this.mozCamera.autoFocus.called);
});
test('mozCamera autoFocus is called if focus mode is continuous-picture', function() {
this.mozCamera.focusMode = 'continuous-picture';
this.focus.focus(function() {});
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
assert.ok(this.mozCamera.autoFocus.called);
});
test('mozCamera autoFocus is not called if focus mode is neither auto nor continuous-picture', function() {
this.mozCamera.focusMode = 'infinity';
this.focus.focus(function() {});
assert.ok(!this.focus.onAutoFocusChanged.called);
assert.ok(!this.mozCamera.autoFocus.called);
});
test('mozCamera autoFocus callback is called with error and focus state switches to false', function() {
var onFocused = sinon.spy();
this.mozCamera.autoFocus = sinon.stub();
this.mozCamera.focusMode = 'auto';
this.mozCamera.autoFocus.returns({
then: function(resolve, reject) {
resolve(false);
}
});
this.focus.focus(onFocused);
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
assert.ok(this.mozCamera.autoFocus.called);
assert.ok(onFocused.calledWith('failed'));
assert.ok(!this.focus.focused);
});
test('mozCamera autoFocus callback is called with success and focus state switches to true', function() {
var onFocused = sinon.spy();
this.mozCamera.autoFocus = sinon.stub();
this.mozCamera.focusMode = 'auto';
this.mozCamera.autoFocus.returns({
then: function(resolve, reject) {
resolve(true);
}
});
this.focus.focus(onFocused);
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
assert.ok(this.mozCamera.autoFocus.called);
assert.ok(onFocused.calledWith('focused'));
assert.ok(this.focus.focused);
});
test('with focus mode different than auto or continuous-picture callback is called with no arguments and focus state doesn\'t change', function(done) {
var onFocused = function(result) {
assert.equal(result, undefined);
done();
};
var previousFocusState;
this.focus.focused = true;
previousFocusState = this.focus.focused;
this.mozCamera.autoFocus = sinon.stub();
this.mozCamera.focusMode = 'infinity';
this.focus.focus(onFocused);
assert.ok(!this.focus.onAutoFocusChanged.called);
assert.ok(!this.mozCamera.autoFocus.called);
assert.ok(previousFocusState === this.focus.focused);
});
test('Should call the focus callback with interrupted state if autofocus is interrupted', function() {
var onFocused = sinon.spy();
this.mozCamera.autoFocus = sinon.stub();
this.mozCamera.autoFocus.returns({
then: function(resolve, reject) {
var error = new Error();
error.name = 'NS_ERROR_IN_PROGRESS';
reject(error);
}
});
this.mozCamera.focusMode = 'auto';
this.focus.focused = true;
this.focus.focus(onFocused);
assert.ok(onFocused.calledWith('interrupted'));
assert.ok(this.focus.focused === false);
});
});
suite('Focus#pause()', function() {
setup(function() {
this.focus.stopContinuousFocus = sinon.spy();
this.focus.stopFaceDetection = sinon.spy();
});
test('Should do nothing if already paused', function() {
this.focus.paused = true;
this.focus.pause();
assert.ok(!this.focus.stopContinuousFocus.called);
assert.ok(!this.focus.stopFaceDetection.called);
assert.ok(this.focus.paused);
});
test('Should stop face detection and continuous focus', function() {
this.focus.paused = false;
this.focus.pause();
assert.ok(this.focus.stopContinuousFocus.called);
assert.ok(this.focus.stopFaceDetection.called);
assert.ok(this.focus.paused);
});
});
suite('Focus#reset()', function() {
test('metering areas and focus areas are reset if touch focus enabled', function() {
this.focus.touchFocus = true;
this.focus.resetFocusAreas();
assert.ok(this.focus.mozCamera.setFocusAreas.calledWith([]));
assert.ok(this.focus.mozCamera.setMeteringAreas.calledWith([]));
});
test('metering areas and focus areas are not reset if touch focus disabled', function() {
this.focus.touchFocus = false;
this.focus.resetFocusAreas();
assert.ok(!this.focus.mozCamera.setFocusAreas.called);
assert.ok(!this.focus.mozCamera.setMeteringAreas.called);
});
});
suite('Focus#resume()', function() {
setup(function() {
this.focus.resumeContinuousFocus = sinon.spy();
});
test('should do nothing if not paused', function() {
this.focus.paused = false;
this.focus.resume();
assert.ok(!this.focus.resumeContinuousFocus.called);
assert.ok(!this.focus.paused);
});
test('resumeContinuousFocus called if focus mode is continuous-picture', function() {
this.focus.paused = true;
this.focus.mozCamera.focusMode = 'continuous-picture'
this.focus.resume();
assert.ok(this.focus.resumeContinuousFocus.called);
assert.ok(!this.focus.paused);
});
});
suite('Focus#isTouchFocusSupported()', function() {
setup(function() {
this.focus.mozCamera.capabilities = {};
});
test('it returns false if touch to focus not supported by hardware', function() {
this.focus.mozCamera.capabilities.maxFocusAreas = 0;
this.focus.mozCamera.capabilities.maxMeteringAreas = 0;
assert.ok(!this.focus.isTouchFocusSupported());
});
test('it returns true if touch to focus supported by hardware', function() {
this.focus.mozCamera.capabilities.maxFocusAreas = 4;
this.focus.mozCamera.capabilities.maxMeteringAreas = 4;
assert.ok(this.focus.isTouchFocusSupported());
});
});
suite('Focus#isFaceDetectionSupported()', function() {
test('it returns true if hardware supports face detection', function() {
this.focus.mozCamera.capabilities = {
maxDetectedFaces: 5
};
this.focus.mozCamera.startFaceDetection = sinon.stub();
assert.ok(this.focus.isFaceDetectionSupported() === true);
});
test('it returns false if hardware doesn\'t support face detection', function() {
this.focus.mozCamera.capabilities = {
maxDetectedFaces: 0
};
this.focus.mozCamera.startFaceDetection = undefined;
assert.ok(this.focus.isFaceDetectionSupported() === false);
});
});
suite('Focus#updateFocusArea()', function() {
setup(function() {
this.focus.focus = sinon.spy();
this.focus.stopContinuousFocus = sinon.spy();
this.focus.suspendFaceDetection = sinon.spy();
this.focus.onAutoFocusChanged = sinon.spy();
});
test('it returns without doing anything if touch to focus disabled', function() {
this.focus.touchFocus = false;
this.focus.updateFocusArea();
assert.ok(!this.focus.stopContinuousFocus.called);
assert.ok(!this.focus.suspendFaceDetection.calledWith(10000));
assert.ok(!this.focus.mozCamera.setFocusAreas.called);
assert.ok(!this.focus.mozCamera.setMeteringAreas.called);
assert.ok(!this.focus.focus.called);
assert.ok(!this.focus.onAutoFocusChanged.called);
});
test('it updates focus area if touch to focus is enabled', function() {
this.focus.touchFocus = true;
this.focus.updateFocusArea();
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
assert.ok(this.focus.stopContinuousFocus.called);
assert.ok(this.focus.suspendFaceDetection.calledWith(10000));
assert.ok(this.focus.mozCamera.setFocusAreas.called);
assert.ok(this.focus.mozCamera.setMeteringAreas.called);
assert.ok(this.focus.focus.called);
});
test('Should call focus callback with fail state if updating the focus area fails', function() {
this.focus.touchFocus = true;
var onFocused = sinon.spy();
this.focus.updateFocusArea(null, onFocused);
assert.ok(this.focus.focus.called);
this.focus.focus.callArgWith(0, 'failed');
assert.ok(onFocused.calledWith('failed'));
});
});
suite('Focus#resumeCamera', function() {
setup(function() {
this.sandbox.spy(this.focus, 'onAutoFocusChanged');
});
teardown(function() {
this.sandbox.restore();
});
test('should call onAutoFocusChanged after pause', function() {
this.focus.focusState = 'focusing';
this.focus.pause();
this.focus.updateFocusState('focusing');
assert.ok(this.focus.onAutoFocusChanged.calledWith('focusing'));
});
});
});

View file

@ -0,0 +1,43 @@
require('/shared/js/format.js');
suite('Format Timer Unit Tests', function() {
/*global asyncStorage*/
'use strict';
var subject;
suiteSetup(function(done) {
requirejs([
'lib/dcf'
], function(dcf) {
subject = dcf;
done();
});
});
suite('#createDCFFilename', function() {
setup(function() {
this.sandbox = sinon.sandbox.create();
var stub = this.sandbox.stub(asyncStorage, 'getItem');
subject.init();
// Trigger the callback
stub.getCall(0).args[1]({
file: 'moz',
dir: '1'
});
});
test('=> pads the filename', function() {
var result;
var storage = {
get: function(filename) {
result = filename;
return {};
}
};
subject.createDCFFilename(storage, 'image');
assert.equal(result, 'DCIM/1MZLLA/IMG_0moz.jpg');
});
});
});

View file

@ -0,0 +1,64 @@
suite('lib/picture-sizes/format-picture-sizes', function() {
/*jshint maxlen:false*/
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs(['lib/format-picture-sizes'], function(formatPictureSizes) {
self.formatPictureSizes = formatPictureSizes;
done();
});
});
setup(function() {
this.sizes = [
{
height: 1536,
width: 2048
},
{
height: 1200,
width: 1600
},
{
height: 240,
width: 320
},
{
height: 480,
width: 640
},
{
height: 288,
width: 352
}
];
this.options = this.formatPictureSizes(this.sizes);
});
test('Should be sorted by resolution', function() {
assert.equal(this.options[0].key, '2048x1536');
assert.equal(this.options[4].key, '320x240');
});
test('Should exlude given keys', function() {
var options = this.formatPictureSizes(this.sizes, { exclude: { keys: ['2048x1536'] }});
var found = options.some(function(size) { return size.key === '2048x1536'; });
assert.isFalse(found);
});
test('Should exlude given aspects', function() {
var options = this.formatPictureSizes(this.sizes, { exclude: { aspects: ['11:9'] }});
var found = options.some(function(size) { return size.data.aspect === '11:9'; });
assert.isFalse(found);
});
test('Should not include sizes above the `maxPixelSize` option', function() {
var maxPixelSize = 307200;
var options = this.formatPictureSizes(this.sizes, { maxPixelSize: maxPixelSize });
var someGreater = options.some(function(item) { return item.pixelSize > maxPixelSize; });
assert.isFalse(someGreater, 'should be none greater than: ' + maxPixelSize);
});
});

View file

@ -0,0 +1,155 @@
suite('lib/format-recorder-profiles', function() {
/*jshint maxlen:false*/
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs(['lib/format-recorder-profiles'], function(formatRecorderProfiles) {
self.formatRecorderProfiles = formatRecorderProfiles;
done();
});
});
setup(function() {
this.profiles = {
'low': {
format: '3gp',
video: {
codec: 'mpeg4sp',
bitrate: 128000,
framerate: 15,
width: 320,
height: 240
},
audio: {
codec: 'amrnb',
bitrate: 12200,
samplerate: 8000,
channels: 1
}
},
'high': {
format: 'mp4',
video: {
codec: 'h264',
bitrate: 12000000,
framerate: 30,
width: 1920,
height: 1080
},
audio: {
codec: 'aac',
bitrate: 96000,
samplerate: 48000,
channels: 1
}
},
'cif': {
format: 'mp4',
video: {
codec: 'h264',
bitrate: 1200000,
framerate: 30,
width: 352,
height: 288
},
audio: {
codec: 'aac',
bitrate: 96000,
samplerate: 48000,
channels: 1
},
key: 'cif'
},
'480p': {
format: 'mp4',
video: {
codec: 'h264',
bitrate: 5000000,
framerate: 30,
width: 720,
height: 480
},
audio: {
codec: 'aac',
bitrate: 96000,
samplerate: 48000,
channels: 1
}
},
'720p': {
format: 'mp4',
video: {
codec: 'h264',
bitrate: 8000000,
framerate: 30,
width: 1280,
height: 720
},
audio: {
codec: 'aac',
bitrate: 96000,
samplerate: 48000,
channels: 1
}
},
'1080p': {
format: 'mp4',
video: {
codec: 'h264',
bitrate: 12000000,
framerate: 30,
width: 1920,
height: 1080
},
audio: {
codec: 'aac',
bitrate: 96000,
samplerate: 48000,
channels: 1
}
}
};
this.profiles.default = this.profiles['720p'];
this.result = this.formatRecorderProfiles(this.profiles, {
exclude: ['high', 'low', 'default']
});
});
test('Should have formatted title string', function() {
var items = this.result;
assert.equal(items[0].title, '1080p 1920x1080 16:9');
assert.equal(items[1].title, '720p 1280x720 16:9');
assert.equal(items[2].title, '480p 720x480 3:2');
assert.equal(items[3].title, 'cif 352x288 11:9');
});
test('Should be sorted by pixel count', function() {
var items = this.result;
var first = items[0];
var last = items[items.length - 1];
assert.equal(first.key, '1080p');
assert.equal(last.key, 'cif');
});
test('Should exclude given keys', function() {
var exclude = ['default', 'low', 'high', 'other'];
var items = this.formatRecorderProfiles(this.profiles, { exclude: exclude });
var found = items.some(function(item) { return exclude.indexOf(item.key) > -1; });
assert.isFalse(found);
assert.equal(items.length, 4);
});
test('Should store the raw profile', function() {
assert.equal(this.result[0].raw, this.profiles['1080p']);
});
test('Should store the `key`', function() {
assert.equal(this.result[0].key, '1080p');
});
});

View file

@ -0,0 +1,27 @@
/*
Formattimer Test.
*/
'use strict';
require('/shared/js/format.js');
suite('Format Timer Unit Tests', function() {
var subject;
suiteSetup(function(done) {
requirejs([
'lib/format-timer'
], function(formattimer) {
subject = formattimer;
done();
});
});
suite('#exports', function() {
test('=> pads minutes and seconds', function() {
assert.equal(subject(0), '00:00');
assert.equal(subject(60000), '01:00');
assert.equal(subject(1000000), '16:40');
});
});
});

View file

@ -0,0 +1,74 @@
/*jshint maxlen:false*/
'use strict';
suite('geolocation', function() {
var GeoLocation;
suiteSetup(function(done) {
requirejs(['lib/geo-location'], function(geolocation) {
GeoLocation = geolocation;
done();
});
});
setup(function() {
this.geolocation = new GeoLocation();
sinon.stub(navigator.geolocation, 'watchPosition');
sinon.stub(navigator.geolocation, 'clearWatch');
});
teardown(function() {
navigator.geolocation.watchPosition.restore();
navigator.geolocation.clearWatch.restore();
});
suite('GeoLocation#watch()', function() {
test('Should begin watching geolocation position if' +
'no watcher currently in place', function() {
this.geolocation.watch();
assert.isTrue(navigator.geolocation.watchPosition.calledOnce);
});
test('Should begin watching geolocation position if' +
'no watcher currently in place', function() {
this.geolocation.watcher = 1;
this.geolocation.watch();
assert.isFalse(navigator.geolocation.watchPosition.called);
});
});
suite('GeoLocation#stopWatching()', function() {
test('Should call navigator.geolocation.clearWatch passing' +
'a reference to the watcher', function() {
this.geolocation.watcher = 1;
this.geolocation.stopWatching();
assert.isTrue(navigator.geolocation.clearWatch.calledWith(1));
});
test('Should unset the watcher', function() {
this.geolocation.watcher = 1;
this.geolocation.stopWatching();
assert.ok(!this.geolocation.watcher);
});
});
suite('GeoLocation#setPosition()', function() {
test('Should store passed geolocation object on instance', function() {
this.geolocation.setPosition({
timestamp: 1234,
coords: {
latitude: 1,
longitude: 2,
altitude: 3
}
});
var position = this.geolocation.position;
assert.ok(position.timestamp === 1234);
assert.ok(position.latitude === 1);
assert.ok(position.longitude === 2);
assert.ok(position.altitude === 3);
});
});
});

View file

@ -0,0 +1,191 @@
suite('lib/pinch', function() {
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs([
'lib/pinch'
], function(Pinch) {
self.Pinch = Pinch;
done();
});
});
setup(function() {
this.el = {
addEventListener: sinon.spy(),
removeEventListener: sinon.spy()
};
this.pinch = new this.Pinch(this.el);
this.pinch.emit = sinon.spy();
});
suite('Pinch()', function() {
test('Should attach to specified element', function() {
assert.isTrue(this.el.addEventListener.called);
});
});
suite('Pinch#attach()', function() {
test('Should attach touchstart to specified element', function() {
this.pinch.attach(this.el);
assert.isTrue(this.el.addEventListener.calledWith('touchstart',
this.pinch.onTouchStart));
});
});
suite('Pinch#detach()', function() {
test('Should detach touchstart from specified element', function() {
this.pinch.detach(this.el);
assert.isTrue(this.el.removeEventListener.calledWith('touchstart',
this.pinch.onTouchStart));
});
});
suite('Pinch#onTouchStart()', function() {
setup(function() {
this.pinch.isPinching = false;
});
test('Should NOT emit `started` for one touch', function() {
this.pinch.onTouchStart({
touches: [{}]
});
assert.isFalse(this.pinch.emit.calledWith('started'));
});
test('Should emit `started` for two touches', function() {
this.pinch.onTouchStart({
touches: [{}]
});
this.pinch.onTouchStart({
touches: [{}, {}]
});
assert.isTrue(this.pinch.isPinching);
assert.isTrue(this.pinch.emit.calledWith('started'));
});
test('Should remember last two touches for two touches', function() {
var touchA = { identifier: 0 };
var touchB = { identifier: 1 };
this.pinch.onTouchStart({
touches: [touchA, touchB]
});
assert.equal(this.pinch.lastTouchA, touchA);
assert.equal(this.pinch.lastTouchB, touchB);
});
});
suite('Pinch#onTouchMove()', function() {
setup(function() {
this.pinch.isPinching = false;
});
test('Should NOT emit `change` for one touch', function() {
this.pinch.onTouchStart({
touches: [{}]
});
this.pinch.onTouchMove({
touches: [{}]
});
assert.isFalse(this.pinch.emit.calledWith('changed'));
});
test('Should emit `change` for two touches', function() {
this.pinch.onTouchStart({
touches: [{}]
});
this.pinch.onTouchStart({
touches: [{}, {}]
});
this.pinch.onTouchMove({
touches: [{}, {}]
});
assert.isTrue(this.pinch.emit.calledWith('changed'));
});
test('Should track touch "A" for two touches', function() {
var touchA1 = { identifier: 0 };
var touchA2 = { identifier: 0 };
this.pinch.onTouchStart({
touches: [touchA1, {}]
});
assert.equal(this.pinch.lastTouchA, touchA1);
this.pinch.onTouchMove({
touches: [touchA2, {}]
});
assert.equal(this.pinch.lastTouchA, touchA2);
});
test('Should track touch "B" for two touches', function() {
var touchB1 = { identifier: 1 };
var touchB2 = { identifier: 1 };
this.pinch.onTouchStart({
touches: [{}, touchB1]
});
assert.equal(this.pinch.lastTouchB, touchB1);
this.pinch.onTouchMove({
touches: [{}, touchB2]
});
assert.equal(this.pinch.lastTouchB, touchB2);
});
test('Should calculate delta for two touches', function() {
var touchA1 = { identifier: 0, pageX: -1, pageY: -1 };
var touchA2 = { identifier: 0, pageX: -2, pageY: -2 };
var touchB1 = { identifier: 1, pageX: 1, pageY: 1 };
var touchB2 = { identifier: 1, pageX: 2, pageY: 2 };
this.pinch.onTouchStart({
touches: [touchA1, touchB1]
});
this.pinch.onTouchMove({
touches: [touchA2, touchB2]
});
assert.equal(this.pinch.emit.args[1][1].toFixed(4), '2.8284');
});
});
suite('Pinch#onTouchEnd()', function() {
setup(function() {
this.pinch.isPinching = false;
});
test('Should NOT emit `ended` for one touch', function() {
this.pinch.onTouchStart({
touches: [{}]
});
this.pinch.onTouchEnd({
touches: []
});
assert.isFalse(this.pinch.emit.calledWith('ended'));
});
test('Should emit `ended` for two touches', function() {
this.pinch.onTouchStart({
touches: [{}, {}]
});
this.pinch.onTouchEnd({
touches: [{}]
});
assert.isFalse(this.pinch.isPinching);
assert.isTrue(this.pinch.emit.calledWith('ended'));
});
});
});

View file

@ -0,0 +1,156 @@
suite('lib/setting-alias', function() {
/*jshint maxlen:false*/
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs([
'lib/setting',
'lib/setting-alias',
], function(Setting, SettingAlias) {
self.Setting = Setting;
self.SettingAlias = SettingAlias;
done();
});
});
setup(function() {
var self = this;
// We use this object to store state
// as nested Suites and tests' context
// inherits from this context.
this.state = {};
this.state.value = 'a';
this.settings = {
a: new this.Setting({ key: 'a' }),
b: new this.Setting({ key: 'b' })
};
this.alias = new this.SettingAlias({
key: 'my-alias',
settings: this.settings,
get: function() {
return this.settings[self.state.value];
}
});
});
suite('SettingAlias()', function() {
test('It stores `key` and `settings` options', function() {
assert.equal(this.alias.key, 'my-alias');
assert.equal(this.alias.settings, this.settings);
});
});
suite('SettingAlias#current()', function() {
test('It returns the current setting', function() {
assert.equal(this.alias.current().key, 'a');
this.state.value = 'b';
assert.equal(this.alias.current().key, 'b');
});
});
suite('SettingAlias#on()', function() {
test('Should fire callback when *current* setting emits event', function() {
var callback = sinon.spy();
this.alias.on('event', callback);
this.settings.a.fire('event');
this.settings.b.fire('event');
sinon.assert.calledOnce(callback);
});
test('Should pass arguments to the callback', function() {
var callback = sinon.spy();
this.alias.on('event', callback);
this.settings.a.fire('event', 'a', 'b');
assert.ok(callback.calledWith('a', 'b'));
});
});
suite('SettingAlias#off()', function() {
test('it calls the callback once', function() {
var callback = sinon.spy();
this.alias.on('event', callback);
this.settings.a.fire('event');
sinon.assert.calledOnce(callback);
});
test('It only removes the listeners that match the callback given', function() {
var callback1 = sinon.spy();
var callback2 = sinon.stub();
this.alias.on('event', callback1);
this.alias.on('event', callback2);
this.settings.a.fire('event');
sinon.assert.calledOnce(callback1);
sinon.assert.calledOnce(callback2);
callback1.reset();
callback2.reset();
this.alias.off('event', callback1);
this.settings.a.fire('event');
sinon.assert.calledOnce(callback2);
sinon.assert.notCalled(callback1);
});
});
suite('SettingAlias#get()', function() {
test('It calls the underlying Setting\'s `.get()` method', function() {
assert.equal(this.alias.get('key'), 'a');
this.state.value = 'b';
assert.equal(this.alias.get('key'), 'b');
});
});
test('It forwards the setting mathod calls onto the current setting', function() {
sinon.stub(this.settings.a);
sinon.stub(this.settings.b);
this.alias.filterOptions();
this.alias.resetOptions();
this.alias.supported();
this.alias.selected();
this.alias.select();
this.alias.next();
this.alias.get();
this.alias.set();
sinon.assert.calledOnce(this.settings.a.filterOptions);
sinon.assert.calledOnce(this.settings.a.resetOptions);
sinon.assert.calledOnce(this.settings.a.supported);
sinon.assert.calledOnce(this.settings.a.selected);
sinon.assert.calledOnce(this.settings.a.select);
sinon.assert.calledOnce(this.settings.a.next);
sinon.assert.calledOnce(this.settings.a.get);
sinon.assert.calledOnce(this.settings.a.set);
// Change state
this.state.value = 'b';
this.alias.filterOptions();
this.alias.resetOptions();
this.alias.supported();
this.alias.selected();
this.alias.select();
this.alias.next();
this.alias.get();
this.alias.set();
sinon.assert.calledOnce(this.settings.b.filterOptions);
sinon.assert.calledOnce(this.settings.b.resetOptions);
sinon.assert.calledOnce(this.settings.b.supported);
sinon.assert.calledOnce(this.settings.b.selected);
sinon.assert.calledOnce(this.settings.b.select);
sinon.assert.calledOnce(this.settings.b.next);
sinon.assert.calledOnce(this.settings.b.get);
sinon.assert.calledOnce(this.settings.b.set);
});
});

View file

@ -0,0 +1,409 @@
suite('lib/setting', function() {
/*jshint maxlen:false*/
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs(['lib/setting'], function(Setting) {
self.Setting = Setting;
done();
});
});
setup(function() {
this.storage = {
getItem: sinon.stub(),
setItem: sinon.stub()
};
});
suite('Setting()', function() {
setup(function() {
this.sandbox = sinon.sandbox.create();
});
teardown(function() {
this.sandbox.restore();
});
test('Should store the key on self', function() {
var setting = new this.Setting({ key: 'mykey', options: [] });
assert.ok(setting.key === 'mykey');
});
test('Should save when selection is change, only if marked as `persistent`', function() {
var on = this.sandbox.spy(this.Setting.prototype, 'on');
var settings1 = new this.Setting({
key: 'key',
persistent: true,
options: []
});
assert.ok(on.calledWith('change:selected', settings1.save));
on.reset();
var settings2 = new this.Setting({ key: 'key', options: [] });
assert.ok(!on.calledWith('change:selected', settings2.save));
});
test('Should default to selecting first option if ' +
'`selected` is undefined', function() {
var setting = new this.Setting({
key: 'key',
options: [
{ key: 'a' },
{ key: 'b' }
]
});
assert.ok(setting.get('selected') === 'a');
});
test('Should flag `options.defined` if options initially defined', function() {
var setting = new this.Setting({
key: 'key',
options: [
{ key: 'a' },
{ key: 'b' }
]
});
assert.isTrue(setting.options.defined);
setting = new this.Setting({
key: 'key',
options: []
});
assert.isFalse(setting.options.defined);
});
});
suite('Setting#select()', function() {
setup(function() {
this.setting = new this.Setting({
key: 'key',
options: [
{ key: 'a' },
{ key: 'b' }
]
});
});
test('Should select the first option if no key match', function() {
this.setting.select('b');
assert.ok(this.setting.get('selected') === 'b');
// Fallback to first
this.setting.select('c');
assert.ok(this.setting.get('selected') === 'a');
this.setting.select(3);
assert.ok(this.setting.get('selected') === 'a');
});
});
suite('Setting#resetOptions()', function() {
setup(function() {
this.setting = new this.Setting({
selected: 'b',
options: [
{ key: 'a' },
{ key: 'b' },
{ key: 'c' }
]
});
});
test('Should update the `selected` option to first if no new options match selected', function() {
this.setting.resetOptions([
{ key: 'd' },
{ key: 'e' },
{ key: 'f' }
]);
assert.equal(this.setting.selected('key'), 'd');
});
test('Should maintain the selected key if a key of new options matches', function() {
this.setting.resetOptions([
{ key: 'c' },
{ key: 'b' },
{ key: 'a' }
]);
assert.equal(this.setting.selected('key'), 'b');
});
test('Should maintain the order', function() {
var options;
this.setting.resetOptions([
{ key: 'a' },
{ key: 'b' },
{ key: 'c' }
]);
options = this.setting.get('options');
assert.equal(options[0].key, 'a');
assert.equal(options[1].key, 'b');
assert.equal(options[2].key, 'c');
this.setting.resetOptions([
{ key: 'c' },
{ key: 'b' },
{ key: 'a' }
]);
options = this.setting.get('options');
assert.equal(options[0].key, 'c');
assert.equal(options[1].key, 'b');
assert.equal(options[2].key, 'a');
});
test('Should not fire a `change` event', function() {
var spy = sinon.spy();
this.setting.on('change', spy);
this.setting.resetOptions([{ key: 'c' }]);
assert.isFalse(spy.called);
});
});
suite('Setting#filterOptions()', function() {
setup(function() {
this.setting = new this.Setting({
key: 'key',
selected: 'b',
options: [
{ key: 'a' },
{ key: 'b' },
{ key: 'c' }
]
});
});
test('Should filter down the available options', function() {
this.setting.filterOptions(['a', 'b']);
var available = this.setting.get('options');
var found = available.some(function(option) { return option.key === 'c'; });
assert.isFalse(found, 'c should not longer be present');
});
test('Should have no available options if argument is falsy', function() {
var available;
this.setting.filterOptions(null);
available = this.setting.get('options');
assert.equal(available.length, 0);
this.setting.filterOptions(undefined);
available = this.setting.get('options');
assert.equal(available.length, 0);
this.setting.filterOptions(false);
available = this.setting.get('options');
assert.equal(available.length, 0);
});
test('Should always filter the original config', function() {
var available;
this.setting.filterOptions(['a', 'b']);
available = this.setting.get('options');
assert.equal(available.length, 2);
assert.equal(available[0].key, 'a');
assert.equal(available[1].key, 'b');
this.setting.filterOptions(['c']);
available = this.setting.get('options');
assert.equal(available.length, 1);
assert.equal(available[0].key, 'c');
});
test('Should maintain `selected` if in filter list', function() {
this.setting.filterOptions(['c', 'b']);
assert.equal(this.setting.selected('key'), 'b');
});
test('Should update the `selected` match first if not in filter list', function() {
this.setting.filterOptions(['c', 'a']);
assert.equal(this.setting.selected('key'), 'a');
});
test('Should fire a `change` event', function() {
var spy = sinon.spy();
this.setting.on('change', spy);
this.setting.filterOptions(['c']);
assert.isTrue(spy.called);
});
test('Should cope with keys that aren\'t defined', function() {
this.setting.filterOptions(['a', 'c', 'jibberish']);
var options = this.setting.get('options');
assert.equal(options.length, 2);
assert.equal(options[0].key, 'a');
assert.equal(options[1].key, 'c');
});
});
suite('Setting#selected()', function() {
setup(function() {
this.setting = new this.Setting({
key: 'key',
options: [
{ key: 'a', title: 'i am a' },
{ key: 'b', title: 'i am b' },
{ key: 'c', title: 'i am c' }
]
});
});
test('Should return the currently selected option', function() {
var selected = this.setting.selected();
assert.ok(selected.title === 'i am a');
this.setting.select('b');
selected = this.setting.selected();
assert.ok(selected.title === 'i am b');
});
test('Should return the given key from the selected option', function() {
var title = this.setting.selected('title');
assert.ok(title === 'i am a');
this.setting.select('b');
var key = this.setting.selected('key');
assert.ok(key === 'b');
});
test('Should return `undefined` if no available options', function() {
this.setting.filterOptions([]);
assert.equal(this.setting.selected(), undefined);
assert.equal(this.setting.selected('key'), undefined);
});
});
suite('Setting#next()', function() {
setup(function() {
this.setting = new this.Setting({
key: 'key',
options: [
{ key: 'a' },
{ key: 'b' },
{ key: 'c' }
]
});
});
test('Should set the `selected` value to the next option', function() {
this.setting.next();
assert.ok(this.setting.selected().key === 'b');
this.setting.next();
assert.ok(this.setting.selected().key === 'c');
this.setting.next();
assert.ok(this.setting.selected().key === 'a');
});
});
suite('Setting#supported()', function() {
setup(function() {
this.config = {
title: 'My title',
options: [{ key: 'a' }, { key: 'b' }]
};
});
test('Should return false if disabled', function() {
var setting;
this.config.disabled = true;
setting = new this.Setting(this.config);
assert.isFalse(setting.supported());
this.config.disabled = false;
setting = new this.Setting(this.config);
assert.isTrue(setting.supported());
delete this.config.disabled;
setting = new this.Setting(this.config);
assert.isTrue(setting.supported());
});
test('Should return true if not disabled and has options', function() {
var setting;
setting = new this.Setting(this.config);
assert.isTrue(setting.supported());
this.config.options = [];
setting = new this.Setting(this.config);
assert.isFalse(setting.supported());
});
});
suite('Setting#fetch()', function() {
setup(function() {
this.setting = new this.Setting({
key: 'foo',
storage: this.storage
});
});
test('Should `select` silently the given key if one is in storage', function() {
sinon.spy(this.setting, 'selected');
this.setting.fetch();
assert.isFalse(this.setting.selected.called);
this.storage.getItem
.withArgs('setting:' + this.setting.key)
.returns('the-key');
this.setting.fetch();
assert.isFalse(this.setting.selected.calledWith('the-key'));
});
test('Should not fire a `change` event', function() {
var spy = sinon.spy();
this.storage.getItem
.withArgs('setting:' + this.setting.key)
.returns('the-key');
this.setting.on('change', spy);
this.setting.fetch();
assert.isFalse(spy.called);
});
});
suite('Setting#save()', function() {
setup(function() {
this.setting = new this.Setting({
key: 'foo',
storage: this.storage,
options: [
{ key: 'a' },
{ key: 'b' }
]
});
});
test('Should call `setItem` with the current selected option key', function() {
var key = 'setting:' + this.setting.key;
var value = this.setting.selected('key');
this.setting.save();
assert.isTrue(this.storage.setItem.calledWith(key, value));
});
});
});

View file

@ -0,0 +1,100 @@
suite('lib/settings', function() {
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs([
'lib/settings',
'lib/setting'
], function(Settings, Setting) {
self.Settings = Settings;
self.Setting = Setting;
done();
});
});
setup(function() {
this.settings = new this.Settings({
setting1: {
some: 'stuff',
options: [
{ key: 'a' },
{ key: 'b' },
{ key: 'c' }
],
persistent: true
},
setting2: { some: 'stuff' },
setting3: { some: 'stuff' }
});
});
suite('Settings()', function() {
test('Should store each setting on self by key', function() {
assert.isTrue(this.settings.setting1 instanceof this.Setting);
assert.isTrue(this.settings.setting2 instanceof this.Setting);
assert.isTrue(this.settings.setting3 instanceof this.Setting);
});
});
suite('Settings#fetch()', function() {
setup(function() {
sinon.stub(this.settings.setting1, 'fetch');
sinon.stub(this.settings.setting2, 'fetch');
sinon.stub(this.settings.setting3, 'fetch');
});
test('Should call fetch() on each setting', function() {
this.settings.fetch();
assert.isTrue(this.settings.setting1.fetch.called);
assert.isTrue(this.settings.setting2.fetch.called);
assert.isTrue(this.settings.setting3.fetch.called);
});
});
suite('Settings#alias()', function() {
setup(function() {
sinon.spy(this.settings, 'SettingAlias');
this.settings.alias({
key: 'myAlias',
settings: {},
get: function() {}
});
});
teardown(function() {
this.settings.SettingAlias.restore();
});
test('Should call SettingAlias', function() {
assert.isTrue(this.settings.SettingAlias.called);
});
test('Should store the alias on self by key', function() {
assert.isDefined(this.settings.myAlias);
});
test('Should pass in the given key', function() {
var options = this.settings.SettingAlias.args[0][0];
assert.equal(options.key, 'myAlias');
});
});
suite('Settings#dontSave()', function() {
test('Should remove any save event handlers', function() {
var setting1 = this.settings.setting1;
var setting2 = this.settings.setting2;
var setting3 = this.settings.setting3;
sinon.spy(setting1, 'off');
sinon.spy(setting2, 'off');
sinon.spy(setting3, 'off');
this.settings.dontSave();
assert.isTrue(setting1.off.calledWith('change:selected', setting1.save));
assert.isTrue(setting2.off.calledWith('change:selected', setting2.save));
assert.isTrue(setting3.off.calledWith('change:selected', setting3.save));
});
});
});

View file

@ -0,0 +1,230 @@
require('/shared/test/unit/mocks/mock_audio.js')
var mocksHelperForSounds = new MocksHelper([
'Audio'
]).init();
suite('Sounds', function() {
/*jshint maxlen:false*/
'use strict';
/**
* Locals
*/
var Sounds;
var list = [
{
name: 'shutter',
url: 'resources/sounds/shutter.opus',
setting: 'camera.sound.enabled'
},
{
name: 'countdown',
url: 'resources/sounds/countdown.opus',
setting: 'camera.sound.enabled'
},
{
name: 'recordingStart',
url: 'resources/sounds/camcorder_start.opus',
setting: 'camera.sound.enabled'
},
{
name: 'recordingEnd',
url: 'resources/sounds/camcorder_end.opus',
setting: 'camera.sound.enabled'
}
];
mocksHelperForSounds.attachTestHelpers();
suiteSetup(function(done) {
requirejs(['lib/sounds'], function(sounds) {
Sounds = sounds;
done();
});
});
setup(function() {
this.sandbox = sinon.sandbox.create();
this.clock = sinon.useFakeTimers();
this.sounds = new Sounds();
// A sound to pass to APIs
this.mockSound = {
name: 'camera',
url: 'resources/sounds/shutter.opus',
setting: 'camera.sound.enabled'
};
// Keep reference of
// things we want to restore
this.backup = {
mozSettings: navigator.mozSettings
};
});
teardown(function() {
navigator.mozSettings = this.backup.mozSettings;
this.sandbox.restore();
this.clock.restore();
});
suite('Sounds()', function() {
test('Should have an empty items object', function() {
var items = this.sounds.items;
assert.ok(this.sounds.hasOwnProperty('items'));
assert.ok(typeof items === 'object');
assert.ok(Object.keys(items).length === 0);
});
});
suite('Sounds#add()', function() {
setup(function() {
// Stub `isEnabled` and call the callback with `true`
this.sandbox.stub(this.sounds, 'isEnabled');
this.sounds.isEnabled.callsArgWith(1, true);
// Simple stubs
this.sandbox.stub(this.sounds, 'setEnabled');
this.sandbox.stub(this.sounds, 'observeSetting');
});
test('Should add each of the sounds to the this.items hash', function() {
this.sounds.add(list[0]);
this.sounds.add(list[1]);
this.sounds.add(list[2]);
this.sounds.add(list[3]);
assert.ok(this.sounds.items.shutter);
assert.ok(this.sounds.items.countdown);
assert.ok(this.sounds.items.recordingStart);
assert.ok(this.sounds.items.recordingEnd);
});
test('Should call `isEnabled`', function() {
this.sounds.add(list[0]);
assert.ok(this.sounds.isEnabled.called);
});
test('Should call `setEnabled` with the result of `isEnabled`', function() {
var setEnabled = this.sounds.setEnabled;
this.sounds.add(list[0]);
assert.ok(setEnabled.calledWith(this.sounds.items.shutter, true));
});
});
suite('Sounds#isEnabled()', function() {
setup(function() {
var self = this;
// Mock object that mimicks
// mozSettings get API. Inside
// tests set this.mozSettingsGetResult
// define the result of the mock call.
navigator.mozSettings = {
createLock: function() { return this; },
get: function(key) {
var mozSettings = this;
setTimeout(function() {
var result = {};
result[key] = 'the-result';
mozSettings.onsuccess({
target: {
result: result
}
});
});
return this;
}
};
});
test('Should not error if navigator.mozSettings is undefined', function() {
navigator.mozSettings = undefined;
this.sounds.isEnabled(this.mockSound);
});
test('Should return the result from mozSettings API', function() {
this.sounds.isEnabled(this.mockSound, function(result) {
assert.ok(result === 'the-result');
});
// Move time forwards
// so the callback fires
this.clock.tick(1);
});
});
suite('Sounds#observeSetting()', function() {
setup(function() {
this.sandbox.stub(this.sounds, 'setEnabled');
navigator.mozSettings = {
addObserver: function(key, callback) {
this.callback = callback;
}
};
});
test('Should not error if navigator.mozSettings is undefined', function() {
navigator.mozSettings = undefined;
this.sounds.observeSetting(this.mockSound);
});
test('Should call setEnabled with the value passed' +
'to the observe callback', function() {
this.sounds.observeSetting(this.mockSound);
// Manually call the callback
// checking that `setEnabled` was
// called with the expected arguments
navigator.mozSettings.callback({ settingValue: true });
assert.ok(this.sounds.setEnabled.calledWith(this.mockSound, true));
});
});
suite('Sounds#createAudio()', function() {
test('Should return an Audio object with the given src', function() {
var url = this.mockSound.url;
var audio = this.sounds.createAudio(url);
assert.ok(audio instanceof window.Audio);
assert.ok(~audio.src.indexOf(url));
});
test('Should set the mozAudioChannel type', function() {
var audio = this.sounds.createAudio(this.mockSound.url);
assert.ok(audio.mozAudioChannelType === 'notification');
});
});
suite('Sounds#playSound()', function() {
setup(function() {
this.mockSound.audio = {
play: sinon.spy(),
cloneNode: function() { return this; }
};
});
test('Should *not* play the sound if it\'s not enabled', function() {
this.sounds.playSound(this.mockSound);
assert.ok(this.mockSound.audio.play.notCalled);
});
test('Should play the sound if it\'s enabled', function() {
this.mockSound.enabled = true;
this.sounds.playSound(this.mockSound);
assert.ok(this.mockSound.audio.play.called);
});
test('Should throw exception if sound is undefined', function() {
try {
this.sounds.playSound();
} catch (e) {
assert.ok('correctly threw exception');
return;
}
assert.ok(false);
});
});
});

View file

@ -0,0 +1,214 @@
suite('lib/storage', function() {
/*jshint maxlen:false*/
'use strict';
suiteSetup(function(done) {
var self = this;
requirejs([
'lib/storage'
], function(Storage) {
self.Storage = Storage;
done();
});
});
setup(function() {
this.clock = sinon.useFakeTimers();
this.sandbox = sinon.sandbox.create();
this.video = {};
this.video.addEventListener = sinon.spy();
this.video.delete = sinon.stub().returns(this.video);
this.picture = {};
this.picture.addEventListener = sinon.spy();
this.picture.removeEventListener = sinon.spy();
this.picture.delete = sinon.stub().returns(this.picture);
// Stub getDeviceStorage
if (!navigator.getDeviceStorage) { navigator.getDeviceStorage = function() {}; }
this.sandbox.stub(navigator, 'getDeviceStorage');
navigator.getDeviceStorage
.withArgs('pictures')
.returns(this.picture);
navigator.getDeviceStorage
.withArgs('videos')
.returns(this.video);
// Stub getDeviceStorages
if (!navigator.getDeviceStorages) { navigator.getDeviceStorages = function() {}; }
this.sandbox.stub(navigator, 'getDeviceStorages')
.withArgs('pictures').returns([this.picture])
.withArgs('videos').returns([this.video]);
// Stub mozSettings
navigator.mozSettings = {
addObserver: sinon.stub()
};
var options = {
dcf: {
init: sinon.spy(),
createDCFFilename: sinon.stub().callsArgWith(2, 'filename.file')
},
require: sinon.stub().callsArg(1)
};
// The test instance
this.storage = new this.Storage(options);
// Storage is a singleton. This forces reconfiguration for each suite
this.storage.configure();
});
teardown(function() {
this.clock.restore();
this.sandbox.restore();
});
suite('Storage()', function() {
test('Should listen for change events', function() {
assert.isTrue(this.picture.addEventListener.calledWith('change'));
assert.isTrue(navigator.mozSettings.addObserver.called);
});
test('Should set state when receiving change events with available reason', function() {
this.sandbox.stub(this.storage, 'setState');
this.sandbox.stub(this.storage, 'check');
this.storage.onStorageChange({
reason: 'available'
});
assert.isTrue(this.storage.setState.called);
assert.isTrue(this.storage.check.called);
});
test('Should set state when receiving change events with unavailable reason', function() {
this.sandbox.stub(this.storage, 'setState');
this.sandbox.stub(this.storage, 'check');
this.storage.onStorageChange({
reason: 'unavailable'
});
assert.isTrue(this.storage.setState.called);
assert.isTrue(this.storage.check.called);
});
test('Should set state when receiving change events with shared reason', function() {
this.sandbox.stub(this.storage, 'setState');
this.sandbox.stub(this.storage, 'check');
this.storage.onStorageChange({
reason: 'shared'
});
assert.isTrue(this.storage.setState.called);
assert.isTrue(this.storage.check.called);
});
test('Should not set state when receiving change events with unexpected reason', function() {
this.sandbox.stub(this.storage, 'setState');
this.sandbox.stub(this.storage, 'check');
this.storage.onStorageChange({
reason: 'foo'
});
assert.isFalse(this.storage.setState.called);
assert.isTrue(this.storage.check.called);
});
});
suite('Storage#addPicture()', function() {
setup(function() {
this.picture.addNamed = sinon.spy(function() { return this.addNamed.req; });
this.picture.addNamed.req = { result: '/path/to/picture.jpg' };
this.picture.get = sinon.spy(function() { return this.get.req; });
this.picture.get.req = { result: 'memory-backed-blob' };
this.callback = sinon.spy();
// Happy path
this.storage.addPicture('fake-blob', this.callback);
this.picture.addNamed.req.onsuccess({ target: this.picture.addNamed.req });
this.picture.get.req.onsuccess({ target: this.picture.get.req });
});
test('Should create a filename if one not given', function() {
assert.isTrue(this.storage.dcf.createDCFFilename.calledWith(this.picture, 'image'));
});
test('Should add the given blob to picture storage', function() {
assert.isTrue(this.picture.addNamed.calledWith('fake-blob', 'filename.file'));
});
test('Should refetch the memory-backed-blob', function() {
assert.isTrue(this.picture.get.calledWith('filename.file'));
});
test('Should callback passing the relative path, absolute path and memory-backed-blob', function() {
assert.isTrue(this.callback.calledWith(
null,
'filename.file',
'/path/to/picture.jpg',
'memory-backed-blob'));
});
});
suite('Settings#isSpace()', function() {
setup(function() {
var picture = this.picture;
var self = this;
this.event = { target: { result: 100 } };
this.picture.freeSpace = function() {
setTimeout(function() { picture.onsuccess(self.event); });
return picture;
};
});
test('Should return false if space < `maxFileSize` value', function() {
this.storage.maxFileSize = 50;
this.storage.isSpace(function(result) {
assert.isTrue(result);
});
this.clock.tick(1);
});
test('Should return true if space > `maxFileSize` value', function() {
this.storage.maxFileSize = 200;
this.storage.isSpace(function(result) {
assert.isFalse(result);
});
this.clock.tick(1);
});
});
suite('Settings#deletePicture()', function() {
test('Should call the deviceStorageAPI', function() {
this.storage.deletePicture('path/to/picture.jpg');
assert.isTrue(this.picture.delete.calledWith('path/to/picture.jpg'));
});
test('Should work when called out of context', function() {
var fn = this.storage.deletePicture;
fn('path/to/picture.jpg');
assert.isTrue(this.picture.delete.calledWith('path/to/picture.jpg'));
});
});
suite('Settings#deleteVideo()', function() {
test('Should call the deviceStorageAPI', function() {
this.storage.deleteVideo('path/video.3gp');
assert.isTrue(this.video.delete.calledWith('path/video.3gp'));
});
test('Should delete matching poster image', function() {
this.storage.deleteVideo('path/video.3gp');
assert.isTrue(this.picture.delete.calledWith('path/video.jpg'));
});
test('Should work when called out of context', function() {
var fn = this.storage.deleteVideo;
fn('path/video.3gp');
assert.isTrue(this.video.delete.calledWith('path/video.3gp'));
});
});
});

View file

@ -0,0 +1,67 @@
/*jshint maxlen:false*/
/*global req*/
'use strict';
suite('utils/string-utils', function() {
var StringUtils;
suiteSetup(function(done) {
var self = this;
this.modules = {};
requirejs([
'lib/string-utils'
], function(_StringUtils) {
StringUtils = self.modules.StringUtils = _StringUtils;
done();
});
});
setup(function() {
});
suite('toCamelCase', function() {
setup(function() {
});
test('Should convert hyphenated string to camel-case', function() {
var sourceStr = 'this-string-should-be-camel-cased';
var outputStr = StringUtils.toCamelCase(sourceStr);
assert.equal(outputStr, 'thisStringShouldBeCamelCased');
});
test('Should convert snake-case string to camel-case', function() {
var sourceStr = 'THIS_STRING_SHOULD_BE_CAMEL_CASED';
var outputStr = StringUtils.toCamelCase(sourceStr);
assert.equal(outputStr, 'thisStringShouldBeCamelCased');
});
});
suite('toHyphenate', function() {
setup(function() {
});
test('Should convert camel-case string to hyphenated', function() {
var sourceStr = 'thisStringShouldBeHyphenated';
var outputStr = StringUtils.toHyphenate(sourceStr);
assert.equal(outputStr, 'this-string-should-be-hyphenated');
});
});
suite('lastPathComponent', function() {
setup(function() {});
test('Should get the last path component', function() {
assert.equal(StringUtils.lastPathComponent('this/is/a/path'), 'path');
assert.equal(StringUtils.lastPathComponent('abc'), 'abc');
});
});
});