[Plugins] Bring over timeline, clock plugins
WTD-1239
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/
|
||||
|
||||
define(
|
||||
['../../../src/controllers/drag/TimelineDragHandleFactory'],
|
||||
function (TimelineDragHandleFactory) {
|
||||
'use strict';
|
||||
|
||||
describe("A Timeline drag handle factory", function () {
|
||||
var mockDragHandler,
|
||||
mockSnapHandler,
|
||||
mockDomainObject,
|
||||
mockType,
|
||||
testType,
|
||||
factory;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDragHandler = jasmine.createSpyObj(
|
||||
'dragHandler',
|
||||
[ 'start' ]
|
||||
);
|
||||
mockSnapHandler = jasmine.createSpyObj(
|
||||
'snapHandler',
|
||||
[ 'snap' ]
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
[ 'getCapability', 'getId' ]
|
||||
);
|
||||
mockType = jasmine.createSpyObj(
|
||||
'type',
|
||||
[ 'instanceOf' ]
|
||||
);
|
||||
|
||||
mockDomainObject.getId.andReturn('test-id');
|
||||
mockDomainObject.getCapability.andReturn(mockType);
|
||||
mockType.instanceOf.andCallFake(function (t) {
|
||||
return t === testType;
|
||||
});
|
||||
|
||||
factory = new TimelineDragHandleFactory(
|
||||
mockDragHandler,
|
||||
mockSnapHandler
|
||||
);
|
||||
});
|
||||
|
||||
it("inspects an object's type capability", function () {
|
||||
factory.handles(mockDomainObject);
|
||||
expect(mockDomainObject.getCapability)
|
||||
.toHaveBeenCalledWith('type');
|
||||
});
|
||||
|
||||
it("provides three handles for activities", function () {
|
||||
testType = "warp.activity";
|
||||
expect(factory.handles(mockDomainObject).length)
|
||||
.toEqual(3);
|
||||
});
|
||||
|
||||
it("provides two handles for timelines", function () {
|
||||
testType = "warp.timeline";
|
||||
expect(factory.handles(mockDomainObject).length)
|
||||
.toEqual(2);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,209 @@
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/
|
||||
|
||||
define(
|
||||
['../../../src/controllers/drag/TimelineDragHandler'],
|
||||
function (TimelineDragHandler) {
|
||||
'use strict';
|
||||
|
||||
describe("A Timeline drag handler", function () {
|
||||
var mockLoader,
|
||||
mockSelection,
|
||||
testConfiguration,
|
||||
mockDomainObject,
|
||||
mockDomainObjects,
|
||||
mockTimespans,
|
||||
mockMutations,
|
||||
mockPersists,
|
||||
mockCallback,
|
||||
handler;
|
||||
|
||||
function asPromise(value) {
|
||||
return (value || {}).then ? value : {
|
||||
then: function (callback) {
|
||||
return asPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function subgraph(domainObject, objects) {
|
||||
function lookupSubgraph(id) {
|
||||
return subgraph(objects[id], objects);
|
||||
}
|
||||
return {
|
||||
domainObject: domainObject,
|
||||
composition: (domainObject.getModel().composition || [])
|
||||
.map(lookupSubgraph)
|
||||
};
|
||||
}
|
||||
|
||||
function makeMockDomainObject(id, composition) {
|
||||
var mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject-' + id,
|
||||
['getId', 'getModel', 'getCapability', 'useCapability']
|
||||
);
|
||||
|
||||
mockDomainObject.getId.andReturn(id);
|
||||
mockDomainObject.getModel.andReturn({ composition: composition });
|
||||
mockDomainObject.useCapability.andReturn(asPromise(mockTimespans[id]));
|
||||
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||
return {
|
||||
persistence: mockPersists[id],
|
||||
mutation: mockMutations[id]
|
||||
}[c];
|
||||
});
|
||||
|
||||
return mockDomainObject;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimespans = {};
|
||||
mockPersists = {};
|
||||
mockMutations = {};
|
||||
['a', 'b', 'c', 'd', 'e', 'f'].forEach(function (id, index) {
|
||||
mockTimespans[id] = jasmine.createSpyObj(
|
||||
'timespan-' + id,
|
||||
[ 'getStart', 'getEnd', 'getDuration', 'setStart', 'setEnd', 'setDuration' ]
|
||||
);
|
||||
mockPersists[id] = jasmine.createSpyObj(
|
||||
'persistence-' + id,
|
||||
[ 'persist' ]
|
||||
);
|
||||
mockMutations[id] = jasmine.createSpyObj(
|
||||
'mutation-' + id,
|
||||
[ 'mutate' ]
|
||||
);
|
||||
mockTimespans[id].getStart.andReturn(index * 1000);
|
||||
mockTimespans[id].getDuration.andReturn(4000 + index);
|
||||
mockTimespans[id].getEnd.andReturn(4000 + index + index * 1000);
|
||||
});
|
||||
|
||||
mockLoader = jasmine.createSpyObj('objectLoader', ['load']);
|
||||
mockDomainObject = makeMockDomainObject('a', ['b', 'c']);
|
||||
mockDomainObjects = {
|
||||
a: mockDomainObject,
|
||||
b: makeMockDomainObject('b', ['d']),
|
||||
c: makeMockDomainObject('c', ['e', 'f']),
|
||||
d: makeMockDomainObject('d', []),
|
||||
e: makeMockDomainObject('e', []),
|
||||
f: makeMockDomainObject('f', [])
|
||||
};
|
||||
mockSelection = jasmine.createSpyObj('selection', ['get', 'select']);
|
||||
mockCallback = jasmine.createSpy('callback');
|
||||
|
||||
testConfiguration = {};
|
||||
|
||||
mockLoader.load.andReturn(asPromise(
|
||||
subgraph(mockDomainObject, mockDomainObjects)
|
||||
));
|
||||
|
||||
handler = new TimelineDragHandler(
|
||||
mockDomainObject,
|
||||
mockLoader
|
||||
);
|
||||
});
|
||||
|
||||
it("uses the loader to find subgraph", function () {
|
||||
expect(mockLoader.load).toHaveBeenCalledWith(
|
||||
mockDomainObject,
|
||||
'timespan'
|
||||
);
|
||||
});
|
||||
|
||||
it("reports available object identifiers", function () {
|
||||
expect(handler.ids())
|
||||
.toEqual(Object.keys(mockDomainObjects).sort());
|
||||
});
|
||||
|
||||
it("exposes start/end/duration from timespan capabilities", function () {
|
||||
expect(handler.start('a')).toEqual(0);
|
||||
expect(handler.start('b')).toEqual(1000);
|
||||
expect(handler.start('c')).toEqual(2000);
|
||||
expect(handler.duration('a')).toEqual(4000);
|
||||
expect(handler.duration('b')).toEqual(4001);
|
||||
expect(handler.duration('c')).toEqual(4002);
|
||||
expect(handler.end('a')).toEqual(4000);
|
||||
expect(handler.end('b')).toEqual(5001);
|
||||
expect(handler.end('c')).toEqual(6002);
|
||||
});
|
||||
|
||||
it("accepts objects instead of identifiers for start/end/duration calls", function () {
|
||||
Object.keys(mockDomainObjects).forEach(function (id) {
|
||||
expect(handler.start(mockDomainObjects[id])).toEqual(handler.start(id));
|
||||
expect(handler.duration(mockDomainObjects[id])).toEqual(handler.duration(id));
|
||||
expect(handler.end(mockDomainObjects[id])).toEqual(handler.end(id));
|
||||
});
|
||||
});
|
||||
|
||||
it("mutates objects", function () {
|
||||
handler.start('a', 123);
|
||||
expect(mockTimespans.a.setStart).toHaveBeenCalledWith(123);
|
||||
handler.duration('b', 42);
|
||||
expect(mockTimespans.b.setDuration).toHaveBeenCalledWith(42);
|
||||
handler.end('c', 12321);
|
||||
expect(mockTimespans.c.setEnd).toHaveBeenCalledWith(12321);
|
||||
});
|
||||
|
||||
it("disallows negative starts, durations", function () {
|
||||
handler.start('a', -100);
|
||||
handler.duration('b', -1000);
|
||||
expect(mockTimespans.a.setStart).toHaveBeenCalledWith(0);
|
||||
expect(mockTimespans.b.setDuration).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it("disallows starts greater than ends violations", function () {
|
||||
handler.start('a', 5000);
|
||||
handler.end('b', 500);
|
||||
expect(mockTimespans.a.setStart).toHaveBeenCalledWith(4000); // end time
|
||||
expect(mockTimespans.b.setEnd).toHaveBeenCalledWith(1000); // start time
|
||||
});
|
||||
|
||||
it("moves objects in groups", function () {
|
||||
handler.move('b', 42);
|
||||
expect(mockTimespans.b.setStart).toHaveBeenCalledWith(1042);
|
||||
expect(mockTimespans.b.setEnd).toHaveBeenCalledWith(5043);
|
||||
expect(mockTimespans.d.setStart).toHaveBeenCalledWith(3042);
|
||||
expect(mockTimespans.d.setEnd).toHaveBeenCalledWith(7045);
|
||||
// Verify no other interactions
|
||||
['a', 'c', 'e', 'f'].forEach(function (id) {
|
||||
expect(mockTimespans[id].setStart).not.toHaveBeenCalled();
|
||||
expect(mockTimespans[id].setEnd).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("moves whole subtrees", function () {
|
||||
handler.move('a', 12321);
|
||||
// We verify the math in the previous test, so just verify
|
||||
// that the whole tree is effected here.
|
||||
Object.keys(mockTimespans).forEach(function (id) {
|
||||
expect(mockTimespans[id].setStart).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("prevents bulk moves past 0", function () {
|
||||
// Have a start later; new lowest start is b, at 1000
|
||||
mockTimespans.a.getStart.andReturn(10000);
|
||||
handler.move('a', -10000);
|
||||
// Verify that move was stopped at 0, for b, even though
|
||||
// move was initiated at a
|
||||
expect(mockTimespans.a.setStart).toHaveBeenCalledWith(9000);
|
||||
expect(mockTimespans.b.setStart).toHaveBeenCalledWith(0);
|
||||
expect(mockTimespans.c.setStart).toHaveBeenCalledWith(1000);
|
||||
});
|
||||
|
||||
it("persists mutated objects", function () {
|
||||
handler.start('a', 20);
|
||||
handler.end('b', 50);
|
||||
handler.duration('c', 30);
|
||||
handler.persist();
|
||||
expect(mockPersists.a.persist).toHaveBeenCalled();
|
||||
expect(mockPersists.b.persist).toHaveBeenCalled();
|
||||
expect(mockPersists.c.persist).toHaveBeenCalled();
|
||||
expect(mockPersists.d.persist).not.toHaveBeenCalled();
|
||||
expect(mockPersists.e.persist).not.toHaveBeenCalled();
|
||||
expect(mockPersists.f.persist).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,53 @@
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/
|
||||
|
||||
|
||||
define(
|
||||
['../../../src/controllers/drag/TimelineDragPopulator'],
|
||||
function (TimelineDragPopulator) {
|
||||
"use strict";
|
||||
|
||||
describe("The timeline drag populator", function () {
|
||||
var mockObjectLoader,
|
||||
mockPromise,
|
||||
mockSwimlane,
|
||||
mockDomainObject,
|
||||
populator;
|
||||
|
||||
beforeEach(function () {
|
||||
mockObjectLoader = jasmine.createSpyObj("objectLoader", ["load"]);
|
||||
mockPromise = jasmine.createSpyObj("promise", ["then"]);
|
||||
mockSwimlane = jasmine.createSpyObj("swimlane", ["color"]);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getCapability", "getId"]
|
||||
);
|
||||
|
||||
mockSwimlane.domainObject = mockDomainObject;
|
||||
mockObjectLoader.load.andReturn(mockPromise);
|
||||
|
||||
populator = new TimelineDragPopulator(mockObjectLoader);
|
||||
});
|
||||
|
||||
it("loads timespans for the represented object's subgraph", function () {
|
||||
populator.populate(mockDomainObject);
|
||||
expect(mockObjectLoader.load).toHaveBeenCalledWith(
|
||||
mockDomainObject,
|
||||
'timespan'
|
||||
);
|
||||
});
|
||||
|
||||
it("updates handles for selections", function () {
|
||||
// Ensure we have a represented object context
|
||||
populator.populate(mockDomainObject);
|
||||
// Initially, no selection and no handles
|
||||
expect(populator.get()).toEqual([]);
|
||||
// Select the swimlane
|
||||
populator.select(mockSwimlane);
|
||||
// We should have handles now
|
||||
expect(populator.get().length).toEqual(3);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,96 @@
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/
|
||||
|
||||
define(
|
||||
['../../../src/controllers/drag/TimelineEndHandle', '../../../src/TimelineConstants'],
|
||||
function (TimelineEndHandle, TimelineConstants) {
|
||||
'use strict';
|
||||
|
||||
describe("A Timeline end drag handle", function () {
|
||||
var mockDragHandler,
|
||||
mockSnapHandler,
|
||||
mockZoomController,
|
||||
handle;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDragHandler = jasmine.createSpyObj(
|
||||
'dragHandler',
|
||||
[ 'end', 'persist' ]
|
||||
);
|
||||
mockSnapHandler = jasmine.createSpyObj(
|
||||
'snapHandler',
|
||||
[ 'snap' ]
|
||||
);
|
||||
mockZoomController = jasmine.createSpyObj(
|
||||
'zoom',
|
||||
[ 'toMillis', 'toPixels' ]
|
||||
);
|
||||
|
||||
mockDragHandler.end.andReturn(12321);
|
||||
|
||||
// Echo back the value from snapper for most tests
|
||||
mockSnapHandler.snap.andCallFake(function (ts) {
|
||||
return ts;
|
||||
});
|
||||
|
||||
// Double pixels to get millis, for test purposes
|
||||
mockZoomController.toMillis.andCallFake(function (px) {
|
||||
return px * 2;
|
||||
});
|
||||
|
||||
mockZoomController.toPixels.andCallFake(function (ms) {
|
||||
return ms / 2;
|
||||
});
|
||||
|
||||
handle = new TimelineEndHandle(
|
||||
'test-id',
|
||||
mockDragHandler,
|
||||
mockSnapHandler
|
||||
);
|
||||
});
|
||||
|
||||
it("provides a style for templates", function () {
|
||||
var w = TimelineConstants.HANDLE_WIDTH;
|
||||
expect(handle.style(mockZoomController)).toEqual({
|
||||
// Left should be adjusted by zoom controller
|
||||
left: (12321 / 2) - w + 'px',
|
||||
// Width should match the defined constant
|
||||
width: w + 'px'
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards drags to the drag handler", function () {
|
||||
handle.begin();
|
||||
handle.drag(100, mockZoomController);
|
||||
// Should have been interpreted as a +200 ms change
|
||||
expect(mockDragHandler.end).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
12521
|
||||
);
|
||||
});
|
||||
|
||||
it("snaps drags to other end points", function () {
|
||||
mockSnapHandler.snap.andReturn(42);
|
||||
handle.begin();
|
||||
handle.drag(-10, mockZoomController);
|
||||
// Should have used snap-to timestamp
|
||||
expect(mockDragHandler.end).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
42
|
||||
);
|
||||
});
|
||||
|
||||
it("persists when a move is complete", function () {
|
||||
// Simulate normal drag cycle
|
||||
handle.begin();
|
||||
handle.drag(100, mockZoomController);
|
||||
// Should not have persisted yet
|
||||
expect(mockDragHandler.persist).not.toHaveBeenCalled();
|
||||
// Finish the drag
|
||||
handle.finish();
|
||||
// Now it should have persisted
|
||||
expect(mockDragHandler.persist).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,163 @@
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/
|
||||
|
||||
define(
|
||||
['../../../src/controllers/drag/TimelineMoveHandle', '../../../src/TimelineConstants'],
|
||||
function (TimelineMoveHandle, TimelineConstants) {
|
||||
'use strict';
|
||||
|
||||
describe("A Timeline move drag handle", function () {
|
||||
var mockDragHandler,
|
||||
mockSnapHandler,
|
||||
mockZoomController,
|
||||
handle;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDragHandler = jasmine.createSpyObj(
|
||||
'dragHandler',
|
||||
[ 'start', 'duration', 'end', 'move', 'persist' ]
|
||||
);
|
||||
mockSnapHandler = jasmine.createSpyObj(
|
||||
'snapHandler',
|
||||
[ 'snap' ]
|
||||
);
|
||||
mockZoomController = jasmine.createSpyObj(
|
||||
'zoom',
|
||||
[ 'toMillis', 'toPixels' ]
|
||||
);
|
||||
|
||||
mockDragHandler.start.andReturn(12321);
|
||||
mockDragHandler.duration.andReturn(4200);
|
||||
mockDragHandler.end.andReturn(12321 + 4200);
|
||||
|
||||
// Echo back the value from snapper for most tests
|
||||
mockSnapHandler.snap.andCallFake(function (ts) {
|
||||
return ts;
|
||||
});
|
||||
|
||||
// Double pixels to get millis, for test purposes
|
||||
mockZoomController.toMillis.andCallFake(function (px) {
|
||||
return px * 2;
|
||||
});
|
||||
|
||||
mockZoomController.toPixels.andCallFake(function (ms) {
|
||||
return ms / 2;
|
||||
});
|
||||
|
||||
handle = new TimelineMoveHandle(
|
||||
'test-id',
|
||||
mockDragHandler,
|
||||
mockSnapHandler
|
||||
);
|
||||
});
|
||||
|
||||
it("provides a style for templates", function () {
|
||||
var w = TimelineConstants.HANDLE_WIDTH;
|
||||
expect(handle.style(mockZoomController)).toEqual({
|
||||
// Left should be adjusted by zoom controller
|
||||
left: (12321 / 2) + w + 'px',
|
||||
// Width should be duration minus end points
|
||||
width: 2100 - (w * 2) + 'px'
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards drags to the drag handler", function () {
|
||||
handle.begin();
|
||||
handle.drag(100, mockZoomController);
|
||||
// Should have been interpreted as a +200 ms change
|
||||
expect(mockDragHandler.move).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
200
|
||||
);
|
||||
});
|
||||
|
||||
it("tracks drags incrementally", function () {
|
||||
handle.begin();
|
||||
|
||||
handle.drag(100, mockZoomController);
|
||||
// Should have been interpreted as a +200 ms change...
|
||||
expect(mockDragHandler.move).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
200
|
||||
);
|
||||
|
||||
// Reflect the change from the drag handler
|
||||
mockDragHandler.start.andReturn(12521);
|
||||
mockDragHandler.end.andReturn(12521 + 4200);
|
||||
|
||||
// ....followed by a +100 ms change.
|
||||
handle.drag(150, mockZoomController);
|
||||
expect(mockDragHandler.move).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
100
|
||||
);
|
||||
});
|
||||
|
||||
it("snaps drags to other end points", function () {
|
||||
mockSnapHandler.snap.andCallFake(function (ts) {
|
||||
return ts + 10;
|
||||
});
|
||||
handle.begin();
|
||||
handle.drag(100, mockZoomController);
|
||||
// Should have used snap-to timestamp, which was 10
|
||||
// ms greater than the provided one
|
||||
expect(mockDragHandler.move).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
210
|
||||
);
|
||||
});
|
||||
|
||||
it("considers snaps for both endpoints", function () {
|
||||
handle.begin();
|
||||
expect(mockSnapHandler.snap).not.toHaveBeenCalled();
|
||||
handle.drag(100, mockZoomController);
|
||||
expect(mockSnapHandler.snap.calls.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("chooses the closest snap-to location", function () {
|
||||
// Use a toggle to give snapped timestamps that are
|
||||
// different distances away from the original.
|
||||
// The move handle needs to choose the closest snap-to,
|
||||
// regardless of whether it is the start/end (which
|
||||
// will vary based on the initial state of this toggle.)
|
||||
var toggle = false;
|
||||
mockSnapHandler.snap.andCallFake(function (ts) {
|
||||
toggle = !toggle;
|
||||
return ts + (toggle ? -5 : 10);
|
||||
});
|
||||
handle.begin();
|
||||
handle.drag(100, mockZoomController);
|
||||
expect(mockDragHandler.move).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
195 // Chose the -5
|
||||
);
|
||||
|
||||
// Reflect the change from the drag handler
|
||||
mockDragHandler.start.andReturn(12521 - 5);
|
||||
mockDragHandler.end.andReturn(12521 + 4200 - 5);
|
||||
|
||||
toggle = true; // Change going-in state
|
||||
handle.drag(300, mockZoomController);
|
||||
// Note that the -5 offset is shown in the current state,
|
||||
// so snapping to the -5 implies that the full 400ms will
|
||||
// be moved (again, relative to dragHandler's reported state)
|
||||
expect(mockDragHandler.move).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
400 // Still chose the -5
|
||||
);
|
||||
});
|
||||
|
||||
it("persists when a move is complete", function () {
|
||||
// Simulate normal drag cycle
|
||||
handle.begin();
|
||||
handle.drag(100, mockZoomController);
|
||||
// Should not have persisted yet
|
||||
expect(mockDragHandler.persist).not.toHaveBeenCalled();
|
||||
// Finish the drag
|
||||
handle.finish();
|
||||
// Now it should have persisted
|
||||
expect(mockDragHandler.persist).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,60 @@
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/
|
||||
|
||||
define(
|
||||
['../../../src/controllers/drag/TimelineSnapHandler'],
|
||||
function (TimelineSnapHandler) {
|
||||
'use strict';
|
||||
|
||||
describe("A Timeline snap handler", function () {
|
||||
var mockDragHandler,
|
||||
handler;
|
||||
|
||||
beforeEach(function () {
|
||||
var starts = { a: 1000, b: 2000, c: 2500, d: 2600 },
|
||||
ends = { a: 2050, b: 3000, c: 2700, d: 10000 };
|
||||
|
||||
mockDragHandler = jasmine.createSpyObj(
|
||||
'dragHandler',
|
||||
[ 'start', 'end', 'ids' ]
|
||||
);
|
||||
|
||||
mockDragHandler.ids.andReturn(['a', 'b', 'c', 'd']);
|
||||
mockDragHandler.start.andCallFake(function (id) {
|
||||
return starts[id];
|
||||
});
|
||||
mockDragHandler.end.andCallFake(function (id) {
|
||||
return ends[id];
|
||||
});
|
||||
|
||||
handler = new TimelineSnapHandler(mockDragHandler);
|
||||
});
|
||||
|
||||
it("provides a preferred snap location within tolerance", function () {
|
||||
expect(handler.snap(2511, 15, 'a')).toEqual(2500); // c's start
|
||||
expect(handler.snap(2488, 15, 'a')).toEqual(2500); // c's start
|
||||
expect(handler.snap(10, 1000, 'b')).toEqual(1000); // a's start
|
||||
expect(handler.snap(2711, 20, 'd')).toEqual(2700); // c's end
|
||||
});
|
||||
|
||||
it("excludes provided id from snapping", function () {
|
||||
// Don't want objects to snap to themselves, so we need
|
||||
// this exclusion.
|
||||
expect(handler.snap(2010, 50, 'b')).toEqual(2050); // a's end
|
||||
// Verify that b's start would have been used had the
|
||||
// id not been provided
|
||||
expect(handler.snap(2010, 50, 'd')).toEqual(2000);
|
||||
});
|
||||
|
||||
it("snaps to the closest point, when multiple match", function () {
|
||||
// 2600 and 2700 (plus others) are both in range here
|
||||
expect(handler.snap(2651, 1000, 'a')).toEqual(2700);
|
||||
});
|
||||
|
||||
it("does not snap if no points are within tolerance", function () {
|
||||
// Closest are 1000 and 2000, which are well outside of tolerance
|
||||
expect(handler.snap(1503, 100, 'd')).toEqual(1503);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,95 @@
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,window,afterEach*/
|
||||
|
||||
define(
|
||||
['../../../src/controllers/drag/TimelineStartHandle', '../../../src/TimelineConstants'],
|
||||
function (TimelineStartHandle, TimelineConstants) {
|
||||
'use strict';
|
||||
|
||||
describe("A Timeline start drag handle", function () {
|
||||
var mockDragHandler,
|
||||
mockSnapHandler,
|
||||
mockZoomController,
|
||||
handle;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDragHandler = jasmine.createSpyObj(
|
||||
'dragHandler',
|
||||
[ 'start', 'persist' ]
|
||||
);
|
||||
mockSnapHandler = jasmine.createSpyObj(
|
||||
'snapHandler',
|
||||
[ 'snap' ]
|
||||
);
|
||||
mockZoomController = jasmine.createSpyObj(
|
||||
'zoom',
|
||||
[ 'toMillis', 'toPixels' ]
|
||||
);
|
||||
|
||||
mockDragHandler.start.andReturn(12321);
|
||||
|
||||
// Echo back the value from snapper for most tests
|
||||
mockSnapHandler.snap.andCallFake(function (ts) {
|
||||
return ts;
|
||||
});
|
||||
|
||||
// Double pixels to get millis, for test purposes
|
||||
mockZoomController.toMillis.andCallFake(function (px) {
|
||||
return px * 2;
|
||||
});
|
||||
|
||||
mockZoomController.toPixels.andCallFake(function (ms) {
|
||||
return ms / 2;
|
||||
});
|
||||
|
||||
handle = new TimelineStartHandle(
|
||||
'test-id',
|
||||
mockDragHandler,
|
||||
mockSnapHandler
|
||||
);
|
||||
});
|
||||
|
||||
it("provides a style for templates", function () {
|
||||
expect(handle.style(mockZoomController)).toEqual({
|
||||
// Left should be adjusted by zoom controller
|
||||
left: (12321 / 2) + 'px',
|
||||
// Width should match the defined constant
|
||||
width: TimelineConstants.HANDLE_WIDTH + 'px'
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards drags to the drag handler", function () {
|
||||
handle.begin();
|
||||
handle.drag(100, mockZoomController);
|
||||
// Should have been interpreted as a +200 ms change
|
||||
expect(mockDragHandler.start).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
12521
|
||||
);
|
||||
});
|
||||
|
||||
it("snaps drags to other end points", function () {
|
||||
mockSnapHandler.snap.andReturn(42);
|
||||
handle.begin();
|
||||
handle.drag(-10, mockZoomController);
|
||||
// Should have used snap-to timestamp
|
||||
expect(mockDragHandler.start).toHaveBeenCalledWith(
|
||||
"test-id",
|
||||
42
|
||||
);
|
||||
});
|
||||
|
||||
it("persists when a move is complete", function () {
|
||||
// Simulate normal drag cycle
|
||||
handle.begin();
|
||||
handle.drag(100, mockZoomController);
|
||||
// Should not have persisted yet
|
||||
expect(mockDragHandler.persist).not.toHaveBeenCalled();
|
||||
// Finish the drag
|
||||
handle.finish();
|
||||
// Now it should have persisted
|
||||
expect(mockDragHandler.persist).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user