pulsar/spec/view-registry-spec.coffee
Nathan Sobo 32d393d26f Pause polling when updates are requested, but don’t start polling over
The blinking cursor was ensuring that we never polled in certain cases.
We need to allow the interval to continue polling at a normal pace, but
just avoid doing any work that could delay the next animation frame.
2015-02-20 09:51:11 -07:00

163 lines
5.8 KiB
CoffeeScript

ViewRegistry = require '../src/view-registry'
{View} = require '../src/space-pen-extensions'
describe "ViewRegistry", ->
registry = null
beforeEach ->
registry = new ViewRegistry
describe "::getView(object)", ->
describe "when passed a DOM node", ->
it "returns the given DOM node", ->
node = document.createElement('div')
expect(registry.getView(node)).toBe node
describe "when passed a SpacePen view", ->
it "returns the root node of the view with a .spacePenView property pointing at the SpacePen view", ->
class TestView extends View
@content: -> @div "Hello"
view = new TestView
node = registry.getView(view)
expect(node.textContent).toBe "Hello"
expect(node.spacePenView).toBe view
describe "when passed a model object", ->
describe "when a view provider is registered matching the object's constructor", ->
it "constructs a view element and assigns the model on it", ->
class TestModel
class TestModelSubclass extends TestModel
class TestView
initialize: (@model) -> this
model = new TestModel
registry.addViewProvider TestModel, (model) ->
new TestView().initialize(model)
view = registry.getView(model)
expect(view instanceof TestView).toBe true
expect(view.model).toBe model
subclassModel = new TestModelSubclass
view2 = registry.getView(subclassModel)
expect(view2 instanceof TestView).toBe true
expect(view2.model).toBe subclassModel
describe "when no view provider is registered for the object's constructor", ->
describe "when the object has a .getViewClass() method", ->
it "builds an instance of the view class with the model, then returns its root node with a __spacePenView property pointing at the view", ->
class TestView extends View
@content: (model) -> @div model.name
initialize: (@model) ->
class TestModel
constructor: (@name) ->
getViewClass: -> TestView
model = new TestModel("hello")
node = registry.getView(model)
expect(node.textContent).toBe "hello"
view = node.spacePenView
expect(view instanceof TestView).toBe true
expect(view.model).toBe model
# returns the same DOM node for repeated calls
expect(registry.getView(model)).toBe node
describe "when the object has no .getViewClass() method", ->
it "throws an exception", ->
expect(-> registry.getView(new Object)).toThrow()
describe "::addViewProvider(providerSpec)", ->
it "returns a disposable that can be used to remove the provider", ->
class TestModel
class TestView
initialize: (@model) -> this
disposable = registry.addViewProvider TestModel, (model) ->
new TestView().initialize(model)
expect(registry.getView(new TestModel) instanceof TestView).toBe true
disposable.dispose()
expect(-> registry.getView(new TestModel)).toThrow()
describe "::updateDocument(fn) and ::readDocument(fn)", ->
frameRequests = null
beforeEach ->
frameRequests = []
spyOn(window, 'requestAnimationFrame').andCallFake (fn) -> frameRequests.push(fn)
it "performs all pending writes before all pending reads on the next animation frame", ->
events = []
registry.updateDocument -> events.push('write 1')
registry.readDocument -> events.push('read 1')
registry.readDocument -> events.push('read 2')
registry.updateDocument -> events.push('write 2')
expect(events).toEqual []
expect(frameRequests.length).toBe 1
frameRequests[0]()
expect(events).toEqual ['write 1', 'write 2', 'read 1', 'read 2']
frameRequests = []
events = []
disposable = registry.updateDocument -> events.push('write 3')
registry.updateDocument -> events.push('write 4')
registry.readDocument -> events.push('read 3')
disposable.dispose()
expect(frameRequests.length).toBe 1
frameRequests[0]()
expect(events).toEqual ['write 4', 'read 3']
it "pauses DOM polling when reads or writes are pending", ->
spyOn(window, 'setInterval').andCallFake(fakeSetInterval)
spyOn(window, 'clearInterval').andCallFake(fakeClearInterval)
events = []
registry.pollDocument -> events.push('poll')
registry.updateDocument -> events.push('write')
registry.readDocument -> events.push('read')
advanceClock(registry.documentPollingInterval)
expect(events).toEqual []
frameRequests[0]()
expect(events).toEqual ['write', 'read', 'poll']
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['write', 'read', 'poll', 'poll']
describe "::pollDocument(fn)", ->
it "calls all registered reader functions on an interval until they are disabled via a returned disposable", ->
spyOn(window, 'setInterval').andCallFake(fakeSetInterval)
events = []
disposable1 = registry.pollDocument -> events.push('poll 1')
disposable2 = registry.pollDocument -> events.push('poll 2')
expect(events).toEqual []
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['poll 1', 'poll 2']
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['poll 1', 'poll 2', 'poll 1', 'poll 2']
disposable1.dispose()
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['poll 1', 'poll 2', 'poll 1', 'poll 2', 'poll 2']
disposable2.dispose()
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['poll 1', 'poll 2', 'poll 1', 'poll 2', 'poll 2']