DSDSServer2024/build/build.20240805/static/js/Cesium-1.53/Specs/Widgets/Animation/AnimationViewModelSpec.js

627 lines
26 KiB
JavaScript

defineSuite([
'Widgets/Animation/AnimationViewModel',
'Core/ClockRange',
'Core/ClockStep',
'Core/JulianDate',
'Widgets/ClockViewModel'
], function(
AnimationViewModel,
ClockRange,
ClockStep,
JulianDate,
ClockViewModel) {
'use strict';
var clockViewModel;
beforeEach(function() {
clockViewModel = new ClockViewModel();
});
function verifyPausedState(viewModel) {
expect(viewModel.pauseViewModel.toggled).toEqual(true);
expect(viewModel.playReverseViewModel.toggled).toEqual(false);
expect(viewModel.playForwardViewModel.toggled).toEqual(false);
expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
}
function verifyForwardState(viewModel) {
expect(viewModel.pauseViewModel.toggled).toEqual(false);
expect(viewModel.playReverseViewModel.toggled).toEqual(false);
expect(viewModel.playForwardViewModel.toggled).toEqual(true);
expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
}
function verifyReverseState(viewModel) {
expect(viewModel.pauseViewModel.toggled).toEqual(false);
expect(viewModel.playReverseViewModel.toggled).toEqual(true);
expect(viewModel.playForwardViewModel.toggled).toEqual(false);
expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
}
function verifyRealtimeState(viewModel) {
expect(viewModel.pauseViewModel.toggled).toEqual(false);
expect(viewModel.playReverseViewModel.toggled).toEqual(false);
expect(viewModel.playForwardViewModel.toggled).toEqual(false);
expect(viewModel.playRealtimeViewModel.toggled).toEqual(true);
expect(viewModel.shuttleRingAngle).toEqual(AnimationViewModel._realtimeShuttleRingAngle);
}
it('constructor sets expected properties', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
expect(animationViewModel.clockViewModel).toBe(clockViewModel);
});
it('setTimeFormatter overrides the default formatter', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
var expectedString = 'My Time';
var myCustomFormatter = function(date) {
expect(date).toEqual(clockViewModel.currentTime);
return expectedString;
};
animationViewModel.timeFormatter = myCustomFormatter;
expect(animationViewModel.timeLabel).toEqual(expectedString);
expect(animationViewModel.timeFormatter).toEqual(myCustomFormatter);
});
it('defaultTimeFormatter produces expected result', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
var date = JulianDate.fromIso8601('2012-03-05T06:07:08.89Z');
clockViewModel.multiplier = 1;
var expectedResult = '06:07:08 UTC';
var result = animationViewModel.timeFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
clockViewModel.multiplier = -1;
expectedResult = '06:07:08 UTC';
result = animationViewModel.timeFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
clockViewModel.multiplier = -0.5;
expectedResult = '06:07:08.890';
result = animationViewModel.timeFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
clockViewModel.multiplier = 0.5;
expectedResult = '06:07:08.890';
result = animationViewModel.timeFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
});
it('setDateFormatter overrides the default formatter', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
var expectedString = 'My Date';
var myCustomFormatter = function(date) {
expect(date).toEqual(clockViewModel.currentTime);
return expectedString;
};
animationViewModel.dateFormatter = myCustomFormatter;
expect(animationViewModel.dateLabel).toEqual(expectedString);
expect(animationViewModel.dateFormatter).toEqual(myCustomFormatter);
});
it('defaultDateFormatter produces expected result', function() {
var animationViewModel = new AnimationViewModel(new ClockViewModel());
var date = JulianDate.fromIso8601('2012-01-05T06:07:08.89Z');
var expectedResult = 'Jan 5 2012';
var result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-02-05T06:07:08.89Z');
expectedResult = 'Feb 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-03-05T06:07:08.89Z');
expectedResult = 'Mar 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-04-05T06:07:08.89Z');
expectedResult = 'Apr 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-05-05T06:07:08.89Z');
expectedResult = 'May 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-06-05T06:07:08.89Z');
expectedResult = 'Jun 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-07-05T06:07:08.89Z');
expectedResult = 'Jul 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-08-05T06:07:08.89Z');
expectedResult = 'Aug 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-09-05T06:07:08.89Z');
expectedResult = 'Sep 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-10-05T06:07:08.89Z');
expectedResult = 'Oct 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-11-05T06:07:08.89Z');
expectedResult = 'Nov 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
date = JulianDate.fromIso8601('2012-12-05T06:07:08.89Z');
expectedResult = 'Dec 5 2012';
result = animationViewModel.dateFormatter(date, animationViewModel);
expect(result).toEqual(expectedResult);
});
it('correctly formats speed label', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
var expectedString;
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.multiplier = 123.1;
expectedString = '123.1x';
expect(animationViewModel.multiplierLabel).toEqual(expectedString);
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.multiplier = 123.12;
expectedString = '123.12x';
expect(animationViewModel.multiplierLabel).toEqual(expectedString);
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.multiplier = 123.123;
expectedString = '123.123x';
expect(animationViewModel.multiplierLabel).toEqual(expectedString);
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.multiplier = 123.1236;
expectedString = '123.124x';
expect(animationViewModel.multiplierLabel).toEqual(expectedString);
clockViewModel.clockStep = ClockStep.SYSTEM_CLOCK;
expectedString = 'Today';
expect(animationViewModel.multiplierLabel).toEqual(expectedString);
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.multiplier = 15;
expectedString = '15x';
expect(animationViewModel.multiplierLabel).toEqual(expectedString);
});
it('pause button restores current state', function() {
clockViewModel.startTime = JulianDate.fromIso8601('2012-01-01T00:00:00');
clockViewModel.stopTime = JulianDate.fromIso8601('2012-01-02T00:00:00');
clockViewModel.currentTime = JulianDate.fromIso8601('2012-01-01T12:00:00');
clockViewModel.multiplier = 1;
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.clockRange = ClockRange.UNBOUNDED;
clockViewModel.shouldAnimate = false;
var viewModel = new AnimationViewModel(clockViewModel);
//Starts out paused
verifyPausedState(viewModel);
//Toggling paused restores state when animating forward
viewModel.pauseViewModel.command();
verifyForwardState(viewModel);
//Executing paused command restores paused state
viewModel.pauseViewModel.command();
verifyPausedState(viewModel);
//Setting the multiplier to negative and unpausing animates backward
clockViewModel.multiplier = -1;
viewModel.pauseViewModel.command();
verifyReverseState(viewModel);
});
it('animating forwards negates the multiplier if it is negative', function() {
var viewModel = new AnimationViewModel(clockViewModel);
var multiplier = -100;
clockViewModel.multiplier = multiplier;
viewModel.playForwardViewModel.command();
expect(clockViewModel.multiplier).toEqual(-multiplier);
});
it('animating backwards negates the multiplier if it is positive', function() {
var viewModel = new AnimationViewModel(clockViewModel);
var multiplier = 100;
clockViewModel.multiplier = multiplier;
viewModel.playReverseViewModel.command();
expect(clockViewModel.multiplier).toEqual(-multiplier);
});
it('animating backwards pauses with a bounded startTime', function() {
var centerTime = JulianDate.fromIso8601('2012-01-01T12:00:00');
clockViewModel.startTime = JulianDate.fromIso8601('2012-01-01T00:00:00');
clockViewModel.stopTime = JulianDate.fromIso8601('2012-01-02T00:00:00');
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.currentTime = centerTime;
clockViewModel.shouldAnimate = false;
var viewModel = new AnimationViewModel(clockViewModel);
verifyPausedState(viewModel);
//Play in reverse while clamped
clockViewModel.multiplier = -1;
clockViewModel.clockRange = ClockRange.CLAMPED;
viewModel.playReverseViewModel.command();
verifyReverseState(viewModel);
//Set current time to start time
clockViewModel.currentTime = clockViewModel.startTime;
//Should now be paused
verifyPausedState(viewModel);
//Animate in reverse again.
clockViewModel.currentTime = centerTime;
clockViewModel.clockRange = ClockRange.LOOP_STOP;
viewModel.playReverseViewModel.command();
verifyReverseState(viewModel);
//Set current time to start time
clockViewModel.currentTime = clockViewModel.startTime;
//Should now be paused
verifyPausedState(viewModel);
//Reversing in start state while bounded should have no effect
viewModel.playReverseViewModel.command();
verifyPausedState(viewModel);
//Set to unbounded and reversing should be okay
clockViewModel.clockRange = ClockRange.UNBOUNDED;
viewModel.playReverseViewModel.command();
verifyReverseState(viewModel);
});
it('dragging shuttle ring does not pause with bounded start or stop Time', function() {
var centerTime = JulianDate.fromIso8601('2012-01-01T12:00:00');
clockViewModel.startTime = JulianDate.fromIso8601('2012-01-01T00:00:00');
clockViewModel.stopTime = JulianDate.fromIso8601('2012-01-02T00:00:00');
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.clockRange = ClockRange.CLAMPED;
clockViewModel.multiplier = 1;
var viewModel = new AnimationViewModel(clockViewModel);
verifyPausedState(viewModel);
//Play forward while clamped
clockViewModel.currentTime = centerTime;
viewModel.playForwardViewModel.command();
verifyForwardState(viewModel);
//Set current time to stop time, which won't stop while dragging
viewModel.shuttleRingDragging = true;
clockViewModel.currentTime = clockViewModel.stopTime;
verifyForwardState(viewModel);
//Drag complete stops.
viewModel.shuttleRingDragging = false;
verifyPausedState(viewModel);
//Do the same thing with start time
clockViewModel.currentTime = centerTime;
viewModel.playReverseViewModel.command();
verifyReverseState(viewModel);
viewModel.shuttleRingDragging = true;
clockViewModel.currentTime = clockViewModel.startTime;
verifyReverseState(viewModel);
//Drag complete stops.
viewModel.shuttleRingDragging = false;
verifyPausedState(viewModel);
});
it('animating forward pauses with a bounded stopTime', function() {
var centerTime = JulianDate.fromIso8601('2012-01-01T12:00:00');
clockViewModel.startTime = JulianDate.fromIso8601('2012-01-01T00:00:00');
clockViewModel.stopTime = JulianDate.fromIso8601('2012-01-02T00:00:00');
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.currentTime = centerTime;
clockViewModel.shouldAnimate = false;
var viewModel = new AnimationViewModel(clockViewModel);
verifyPausedState(viewModel);
//Play forward while clamped
clockViewModel.multiplier = 1;
clockViewModel.clockRange = ClockRange.CLAMPED;
viewModel.playForwardViewModel.command();
verifyForwardState(viewModel);
//Set current time to stop time
clockViewModel.currentTime = clockViewModel.stopTime;
//Should now be paused
verifyPausedState(viewModel);
//Playing in stop state while bounded should have no effect
viewModel.playForwardViewModel.command();
verifyPausedState(viewModel);
//Set to unbounded and playing should be okay
clockViewModel.clockRange = ClockRange.UNBOUNDED;
viewModel.playForwardViewModel.command();
verifyForwardState(viewModel);
});
it('slower has no effect if at the slowest speed', function() {
var viewModel = new AnimationViewModel(clockViewModel);
viewModel.setShuttleRingTicks([0.0, 1.0, 2.0]);
var slowestMultiplier = -2;
clockViewModel.multiplier = slowestMultiplier;
viewModel.slower();
expect(clockViewModel.multiplier).toEqual(slowestMultiplier);
});
it('faster has no effect if at the faster speed', function() {
var viewModel = new AnimationViewModel(clockViewModel);
viewModel.setShuttleRingTicks([0.0, 1.0, 2.0]);
var fastestMultiplier = 2;
clockViewModel.multiplier = fastestMultiplier;
viewModel.faster();
expect(clockViewModel.multiplier).toEqual(fastestMultiplier);
});
it('slower and faster cycle through defined multipliers', function() {
var viewModel = new AnimationViewModel(clockViewModel);
var i = 0;
var multipliers = viewModel.getShuttleRingTicks();
var length = multipliers.length;
//Start at slowest speed
clockViewModel.multiplier = multipliers[0];
//Cycle through them all with faster
for (i = 1; i < length; i++) {
viewModel.faster();
expect(clockViewModel.multiplier).toEqual(multipliers[i]);
}
//We should be at the fastest time now.
expect(clockViewModel.multiplier).toEqual(multipliers[length - 1]);
//Cycle through them all with slower
for (i = length - 2; i >= 0; i--) {
viewModel.slower();
expect(clockViewModel.multiplier).toEqual(multipliers[i]);
}
//We should be at the slowest time now.
expect(clockViewModel.multiplier).toEqual(multipliers[0]);
});
it('Realtime canExecute and tooltip depends on clock settings', function() {
var viewModel = new AnimationViewModel(clockViewModel);
//UNBOUNDED but available when start/stop time does not include realtime
clockViewModel.systemTime = JulianDate.now();
clockViewModel.clockRange = ClockRange.UNBOUNDED;
clockViewModel.startTime = JulianDate.addSeconds(clockViewModel.systemTime, -60, new JulianDate());
clockViewModel.stopTime = JulianDate.addSeconds(clockViewModel.systemTime, -30, new JulianDate());
expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(true);
expect(viewModel.playRealtimeViewModel.tooltip).toEqual('Today (real-time)');
//CLAMPED but unavailable when start/stop time does not include realtime
clockViewModel.clockRange = ClockRange.CLAMPED;
clockViewModel.startTime = JulianDate.addSeconds(clockViewModel.systemTime, -60, new JulianDate());
clockViewModel.stopTime = JulianDate.addSeconds(clockViewModel.systemTime, -30, new JulianDate());
expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(false);
expect(viewModel.playRealtimeViewModel.tooltip).toEqual('Current time not in range');
//CLAMPED but available when start/stop time includes realtime
clockViewModel.clockRange = ClockRange.CLAMPED;
clockViewModel.startTime = JulianDate.addSeconds(clockViewModel.systemTime, -60, new JulianDate());
clockViewModel.stopTime = JulianDate.addSeconds(clockViewModel.systemTime, 60, new JulianDate());
expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(true);
expect(viewModel.playRealtimeViewModel.tooltip).toEqual('Today (real-time)');
//LOOP_STOP but unavailable when start/stop time does not include realtime
clockViewModel.clockRange = ClockRange.LOOP_STOP;
clockViewModel.startTime = JulianDate.addSeconds(clockViewModel.systemTime, -60, new JulianDate());
clockViewModel.stopTime = JulianDate.addSeconds(clockViewModel.systemTime, -30, new JulianDate());
expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(false);
expect(viewModel.playRealtimeViewModel.tooltip).toEqual('Current time not in range');
//LOOP_STOP but available when start/stop time includes realtime
clockViewModel.clockRange = ClockRange.LOOP_STOP;
clockViewModel.startTime = JulianDate.addSeconds(clockViewModel.systemTime, -60, new JulianDate());
clockViewModel.stopTime = JulianDate.addSeconds(clockViewModel.systemTime, 60, new JulianDate());
expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(true);
expect(viewModel.playRealtimeViewModel.tooltip).toEqual('Today (real-time)');
});
it('User action breaks out of realtime mode', function() {
var viewModel = new AnimationViewModel(clockViewModel);
clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
clockViewModel.clockRange = ClockRange.UNBOUNDED;
viewModel.playRealtimeViewModel.command();
verifyRealtimeState(viewModel);
expect(clockViewModel.multiplier).toEqual(1);
//Pausing breaks realtime state
viewModel.pauseViewModel.command();
verifyPausedState(viewModel);
expect(clockViewModel.multiplier).toEqual(1);
viewModel.playRealtimeViewModel.command();
verifyRealtimeState(viewModel);
//Reverse breaks realtime state
viewModel.playReverseViewModel.command();
verifyReverseState(viewModel);
expect(clockViewModel.multiplier).toEqual(-1);
viewModel.playRealtimeViewModel.command();
verifyRealtimeState(viewModel);
//Play does not break realtime state
viewModel.playForwardViewModel.command();
verifyRealtimeState(viewModel);
expect(clockViewModel.multiplier).toEqual(1);
viewModel.playRealtimeViewModel.command();
verifyRealtimeState(viewModel);
//Shuttle ring change breaks realtime state
viewModel.shuttleRingAngle = viewModel.shuttleRingAngle + 1;
verifyForwardState(viewModel);
});
it('real time mode toggles off but not back on when shouldAnimate changes', function() {
var viewModel = new AnimationViewModel(clockViewModel);
viewModel.playRealtimeViewModel.command();
verifyRealtimeState(viewModel);
clockViewModel.shouldAnimate = false;
expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
clockViewModel.shouldAnimate = true;
expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
});
it('Shuttle ring angles set expected multipliers', function() {
var viewModel = new AnimationViewModel(clockViewModel);
var shuttleRingTicks = viewModel.getShuttleRingTicks();
var maxMultiplier = shuttleRingTicks[shuttleRingTicks.length - 1];
var minMultiplier = -maxMultiplier;
//Max angle should produce max speed
viewModel.shuttleRingAngle = AnimationViewModel._maxShuttleRingAngle;
expect(clockViewModel.multiplier).toEqual(maxMultiplier);
//Min angle should produce min speed
viewModel.shuttleRingAngle = -AnimationViewModel._maxShuttleRingAngle;
expect(clockViewModel.multiplier).toEqual(minMultiplier);
//AnimationViewModel._realtimeShuttleRingAngle degrees is always 1x
viewModel.shuttleRingAngle = AnimationViewModel._realtimeShuttleRingAngle;
expect(clockViewModel.multiplier).toEqual(1);
viewModel.shuttleRingAngle = -AnimationViewModel._realtimeShuttleRingAngle;
expect(clockViewModel.multiplier).toEqual(-1);
//For large values, the shuttleRingAngle should always round to the first two digits.
viewModel.shuttleRingAngle = 45.0;
expect(clockViewModel.multiplier).toEqual(85.0);
viewModel.shuttleRingAngle = -90.0;
expect(clockViewModel.multiplier).toEqual(-66000.0);
viewModel.shuttleRingAngle = 0.0;
expect(clockViewModel.multiplier).toEqual(0.0);
});
it('Shuttle ring angles set expected multipliers when snapping to ticks', function() {
var viewModel = new AnimationViewModel(clockViewModel);
viewModel.snapToTicks = true;
var shuttleRingTicks = viewModel.getShuttleRingTicks();
var maxMultiplier = shuttleRingTicks[shuttleRingTicks.length - 1];
var minMultiplier = -maxMultiplier;
//Max angle should produce max speed
viewModel.shuttleRingAngle = AnimationViewModel._maxShuttleRingAngle;
expect(clockViewModel.multiplier).toEqual(maxMultiplier);
//Min angle should produce min speed
viewModel.shuttleRingAngle = -AnimationViewModel._maxShuttleRingAngle;
expect(clockViewModel.multiplier).toEqual(minMultiplier);
//AnimationViewModel._realtimeShuttleRingAngle degrees is always 1x
viewModel.shuttleRingAngle = AnimationViewModel._realtimeShuttleRingAngle;
expect(clockViewModel.multiplier).toEqual(1);
viewModel.shuttleRingAngle = -AnimationViewModel._realtimeShuttleRingAngle;
expect(clockViewModel.multiplier).toEqual(-1);
//For large values, the shuttleRingAngle should always round to the first two digits.
viewModel.shuttleRingAngle = 45.0;
expect(clockViewModel.multiplier).toEqual(120.0);
viewModel.shuttleRingAngle = -90.0;
expect(clockViewModel.multiplier).toEqual(-43200.0);
viewModel.shuttleRingAngle = 0.0;
expect(clockViewModel.multiplier).toEqual(AnimationViewModel.defaultTicks[0]);
});
it('throws when constructed without arguments', function() {
expect(function() {
return new AnimationViewModel();
}).toThrowDeveloperError();
});
it('setting timeFormatter throws with non-function', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
expect(function() {
animationViewModel.timeFormatter = {};
}).toThrowDeveloperError();
});
it('setting dateFormatter throws with non-function', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
expect(function() {
animationViewModel.dateFormatter = {};
}).toThrowDeveloperError();
});
it('setting shuttleRingTicks throws with undefined', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
expect(function() {
animationViewModel.setShuttleRingTicks(undefined);
}).toThrowDeveloperError();
});
it('returns a copy of shuttleRingTicks when getting', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
var originalTicks = [0.0, 1.0, 2.0];
animationViewModel.setShuttleRingTicks(originalTicks);
var ticks = animationViewModel.getShuttleRingTicks();
ticks.push(99);
ticks[0] = -99;
expect(animationViewModel.getShuttleRingTicks()).toEqual(originalTicks);
});
it('sorts shuttleRingTicks when setting', function() {
var animationViewModel = new AnimationViewModel(clockViewModel);
var ticks = [4.0, 0.0, 8.0, 2.0];
animationViewModel.setShuttleRingTicks(ticks);
expect(animationViewModel.getShuttleRingTicks()).toEqual([0.0, 2.0, 4.0, 8.0]);
});
});