From a4a75912b0ce38c0fffebce2b904cb5dc6b5d015 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 27 Apr 2015 12:31:48 +0200 Subject: [PATCH 1/9] Implement ajax polling for things that are visible on the screen We sometimes miss pusher updates, which started to be more common lately. Until we investigate what's going on, this should be a good workaround for keeping UI in sync with the DB. --- app/components/running-jobs-item.coffee | 8 +++ app/controllers/running-jobs.coffee | 3 +- app/mixins/polling.coffee | 63 +++++++++++++++++++ app/routes/abstract-builds.coffee | 1 + app/services/polling.coffee | 49 +++++++++++++++ .../components/running-jobs-item.hbs | 1 + app/templates/running-jobs.hbs | 24 +------ app/views/build.coffee | 5 +- app/views/builds.coffee | 18 ++++++ app/views/job.coffee | 5 +- app/views/repos-list.coffee | 5 +- app/views/running-jobs.coffee | 8 +++ .../components/running-jobs-item-test.coffee | 17 +++++ 13 files changed, 180 insertions(+), 27 deletions(-) create mode 100644 app/components/running-jobs-item.coffee create mode 100644 app/mixins/polling.coffee create mode 100644 app/services/polling.coffee create mode 100644 app/templates/components/running-jobs-item.hbs create mode 100644 app/views/builds.coffee create mode 100644 app/views/running-jobs.coffee create mode 100644 tests/unit/components/running-jobs-item-test.coffee diff --git a/app/components/running-jobs-item.coffee b/app/components/running-jobs-item.coffee new file mode 100644 index 00000000..fb5fe7c7 --- /dev/null +++ b/app/components/running-jobs-item.coffee @@ -0,0 +1,8 @@ +`import Ember from 'ember'` +`import Polling from 'travis/mixins/polling'` + +RunningJobsItemComponent = Ember.Component.extend(Polling, + pollModels: 'job' +) + +`export default RunningJobsItemComponent` diff --git a/app/controllers/running-jobs.coffee b/app/controllers/running-jobs.coffee index 10b09e45..1058b304 100644 --- a/app/controllers/running-jobs.coffee +++ b/app/controllers/running-jobs.coffee @@ -12,7 +12,8 @@ Controller = Ember.ArrayController.extend isLoaded: false content: (-> - result = @store.filter('job', { state: 'started' }, (job) -> + # TODO: this should also query for received jobs + result = @store.filter('job', {}, (job) -> ['started', 'received'].indexOf(job.get('state')) != -1 ) result.then => diff --git a/app/mixins/polling.coffee b/app/mixins/polling.coffee new file mode 100644 index 00000000..b55b7c1b --- /dev/null +++ b/app/mixins/polling.coffee @@ -0,0 +1,63 @@ +`import Ember from 'ember'` + +mixin = Ember.Mixin.create + polling: Ember.inject.service() + + didInsertElement: -> + @_super.apply(this, arguments) + + @startPolling() + + willDestroyElement: -> + @_super.apply(this, arguments) + + @stopPolling() + + willDestroy: -> + @_super.apply(this, arguments) + + @stopPolling() + + pollModelDidChange: (sender, key, value) -> + @pollModel(key) + + pollModelWillChange: (sender, key, value) -> + @stopPollingModel(key) + + pollModel: (property) -> + model = @get(property) + + @get('polling').startPolling(model) + + stopPollingModel: (property) -> + model = @get(property) + + @get('polling').stopPolling(model) + + startPolling: -> + pollModels = @get('pollModels') + + if pollModels + pollModels = [pollModels] unless pollModels.forEeach + + pollModels.forEach (property) => + @pollModel(property) + @addObserver(property, this, 'pollModelDidChange') + Ember.addBeforeObserver(this, property, this, 'pollModelWillChange') + + @get('polling').startPollingHook(this) if @pollHook + + stopPolling: -> + pollModels = @get('pollModels') + return unless pollModels + + pollModels = [pollModels] unless pollModels.forEeach + + pollModels.forEach (property) => + @stopPollingModel(property) + @removeObserver(property, this, 'pollModelDidChange') + Ember.removeBeforeObserver(this, property, this, 'pollModelWillChange') + + @get('polling').stopPollingHook(this) + +`export default mixin` diff --git a/app/routes/abstract-builds.coffee b/app/routes/abstract-builds.coffee index 1a79b7ec..d64f93e3 100644 --- a/app/routes/abstract-builds.coffee +++ b/app/routes/abstract-builds.coffee @@ -11,6 +11,7 @@ Route = TravisRoute.extend @controllerFor('repo').activate(@get('contentType')) @contentDidChange() @controllerFor('repo').addObserver(@get('path'), this, 'contentDidChange') + @controllerFor('build').set('contentType', @get('contentType')) deactivate: -> @controllerFor('repo').removeObserver(@get('path'), this, 'contentDidChange') diff --git a/app/services/polling.coffee b/app/services/polling.coffee new file mode 100644 index 00000000..a9b916b8 --- /dev/null +++ b/app/services/polling.coffee @@ -0,0 +1,49 @@ +`import Ember from 'ember'` + +service = Ember.Object.extend + init: -> + @_super.apply(this, arguments) + + @set('watchedModels', []) + @set('sources', []) + + interval = setInterval => + @poll() + , 30000 + + @set('interval', interval) + + willDestroy: -> + @_super.apply(this, arguments) + + clearInterval(@get('interval')) + + startPollingHook: (source) -> + sources = @get('sources') + unless sources.contains(source) + sources.pushObject(source) + + stopPollingHook: (source) -> + sources = @get('sources') + sources.removeObject(source) + + startPolling: (model) -> + watchedModels = @get('watchedModels') + unless watchedModels.contains(model) + watchedModels.pushObject(model) + + stopPolling: (model) -> + watchedModels = @get('watchedModels') + watchedModels.removeObject(model) + + poll: -> + @get('watchedModels').forEach (model) -> + model.reload() + + @get('sources').forEach (source) => + if source.get('destroyed') + @get('sources').removeObject(source) + else + source.pollHook() + +`export default service` diff --git a/app/templates/components/running-jobs-item.hbs b/app/templates/components/running-jobs-item.hbs new file mode 100644 index 00000000..889d9eea --- /dev/null +++ b/app/templates/components/running-jobs-item.hbs @@ -0,0 +1 @@ +{{yield}} diff --git a/app/templates/running-jobs.hbs b/app/templates/running-jobs.hbs index 0c12c853..c99fd7e3 100644 --- a/app/templates/running-jobs.hbs +++ b/app/templates/running-jobs.hbs @@ -1,29 +1,7 @@ {{#if isLoaded}} {{#if controller.length}} {{#each job in controller}} -
- {{#if job.repo.slug}} - - {{#link-to "job" job.repo job}}{{job.repo.slug}}{{/link-to}} - {{/if}} - -

- - {{#if job.repo.slug}} - {{#link-to "job" job.repo job}}{{job.number}}{{/link-to}} - {{/if}} -

- -

- - Duration: - - {{format-duration job.duration}} - -

- -
- + {{running-jobs-item job=job}} {{/each}} {{else}}
There are no jobs running
diff --git a/app/views/build.coffee b/app/views/build.coffee index 10792cdb..124b8055 100644 --- a/app/views/build.coffee +++ b/app/views/build.coffee @@ -1,10 +1,13 @@ `import { colorForState } from 'travis/utils/helpers'` `import BasicView from 'travis/views/basic'` +`import Polling from 'travis/mixins/polling'` -View = BasicView.extend +View = BasicView.extend Polling, classNameBindings: ['color'] buildBinding: 'controller.build' + pollModels: 'controller.build' + color: (-> colorForState(@get('build.state')) ).property('build.state') diff --git a/app/views/builds.coffee b/app/views/builds.coffee new file mode 100644 index 00000000..cdee99f0 --- /dev/null +++ b/app/views/builds.coffee @@ -0,0 +1,18 @@ +`import BasicView from 'travis/views/basic'` +`import Polling from 'travis/mixins/polling'` + +View = BasicView.extend Polling, + pollHook: (store) -> + contentType = @get('controller.contentType') + repositoryId = @get('controller.repo.id') + store = @get('controller.store') + + if contentType == 'builds' + store.find('build', { event_type: 'push', repository_id: repositoryId }) + else if contentType == 'pull_requests' + store.filter('build', { event_type: 'pull_request', repository_id: repositoryId }) + else + store.find 'build', repository_id: repositoryId, branches: true + + +`export default View` diff --git a/app/views/job.coffee b/app/views/job.coffee index 8eaeeafb..622b0bcd 100644 --- a/app/views/job.coffee +++ b/app/views/job.coffee @@ -1,8 +1,11 @@ `import Ember from 'ember'` `import { colorForState } from 'travis/utils/helpers'` `import { githubCommit, gravatarImage } from 'travis/utils/urls'` +`import Polling from 'travis/mixins/polling'` + +View = Ember.View.extend Polling, + pollModels: 'controller.job' -View = Ember.View.extend repoBinding: 'controller.repo' jobBinding: 'controller.job' commitBinding: 'job.commit' diff --git a/app/views/repos-list.coffee b/app/views/repos-list.coffee index 65239f90..5d4fba03 100644 --- a/app/views/repos-list.coffee +++ b/app/views/repos-list.coffee @@ -1,5 +1,6 @@ `import Ember from 'ember'` `import { colorForState } from 'travis/utils/helpers'` +`import Polling from 'travis/mixins/polling'` View = Ember.CollectionView.extend elementId: '' @@ -8,7 +9,9 @@ View = Ember.CollectionView.extend emptyView: Ember.View.extend templateName: 'repos-list/empty' - itemViewClass: Ember.View.extend + itemViewClass: Ember.View.extend Polling, + pollModels: 'repo' + repoBinding: 'content' classNames: ['repo'] classNameBindings: ['color', 'selected'] diff --git a/app/views/running-jobs.coffee b/app/views/running-jobs.coffee new file mode 100644 index 00000000..11c9a768 --- /dev/null +++ b/app/views/running-jobs.coffee @@ -0,0 +1,8 @@ +`import BasicView from 'travis/views/basic'` +`import Polling from 'travis/mixins/polling'` + +View = BasicView.extend Polling, + pollHook: (store) -> + @get('controller.store').find('job', {}) + +`export default View` diff --git a/tests/unit/components/running-jobs-item-test.coffee b/tests/unit/components/running-jobs-item-test.coffee new file mode 100644 index 00000000..6a10536f --- /dev/null +++ b/tests/unit/components/running-jobs-item-test.coffee @@ -0,0 +1,17 @@ +`import { test, moduleForComponent } from 'ember-qunit'` + +moduleForComponent 'running-jobs-item', { + # specify the other units that are required for this test + # needs: ['component:foo', 'helper:bar'] +} + +test 'it renders', (assert) -> + assert.expect 2 + + # creates the component instance + component = @subject() + assert.equal component._state, 'preRender' + + # renders the component to the page + @render() + assert.equal component._state, 'inDOM' From 193a005434655880bcfee98c5a5ce837beb0bb42 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 30 Apr 2015 12:36:55 +0200 Subject: [PATCH 2/9] Remove some deprecations --- app/controllers/builds-item.coffee | 6 +++--- app/views/repo-show-tools.coffee | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/builds-item.coffee b/app/controllers/builds-item.coffee index 3db4ba54..50cc588a 100644 --- a/app/controllers/builds-item.coffee +++ b/app/controllers/builds-item.coffee @@ -6,15 +6,15 @@ Controller = Ember.ObjectController.extend(GithubUrlProperties, needs: ['builds'] isPullRequestsListBinding: 'controllers.builds.isPullRequestsList' - buildBinding: 'content' + buildBinding: 'model' color: (-> colorForState(@get('build.state')) ).property('build.state') urlAuthorGravatarImage: (-> - gravatarImage(@get('commit.authorEmail'), 40) - ).property('commit.authorEmail') + gravatarImage(@get('build.commit.authorEmail'), 40) + ).property('build.commit.authorEmail') ) `export default Controller` diff --git a/app/views/repo-show-tools.coffee b/app/views/repo-show-tools.coffee index 57b153f2..2f19aecd 100644 --- a/app/views/repo-show-tools.coffee +++ b/app/views/repo-show-tools.coffee @@ -9,7 +9,7 @@ View = BasicView.extend buildBinding: 'controller.build' jobBinding: 'controller.job' tabBinding: 'controller.tab' - currentUserBinding: 'controller.currentUser' + currentUserBinding: 'controller.currentUser.model' slugBinding: 'controller.repo.slug' From 984dd2328e6167b20fad46c53a6df28d1e69cde6 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 30 Apr 2015 12:43:53 +0200 Subject: [PATCH 3/9] Also observe current repository for ajax polling --- app/views/repo.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/repo.coffee b/app/views/repo.coffee index 64089307..01815ca1 100644 --- a/app/views/repo.coffee +++ b/app/views/repo.coffee @@ -2,6 +2,7 @@ `import StatusImagesView from 'travis/views/status-images'` `import BasicView from 'travis/views/basic'` `import config from 'travis/config/environment'` +`import Polling from 'travis/mixins/polling'` View = BasicView.extend reposBinding: 'controllers.repos' @@ -10,6 +11,8 @@ View = BasicView.extend jobBinding: 'controller.job' tabBinding: 'controller.tab' + pollModels: 'controller.repo' + classNameBindings: ['controller.isLoading:loading'] isEmpty: (-> From 29c95a41807c6311dcbbaf7e7b83eccb7857de9f Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 30 Apr 2015 12:57:02 +0200 Subject: [PATCH 4/9] Poll for build on job tab Build has also job's data, so it will get more info with not much overhead. --- app/views/job.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/job.coffee b/app/views/job.coffee index 622b0bcd..b4c6a092 100644 --- a/app/views/job.coffee +++ b/app/views/job.coffee @@ -4,7 +4,7 @@ `import Polling from 'travis/mixins/polling'` View = Ember.View.extend Polling, - pollModels: 'controller.job' + pollModels: 'controller.job.build' repoBinding: 'controller.repo' jobBinding: 'controller.job' From f8390b8f82d7ad793c4f357bfbc37f24c4d9b1df Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 30 Apr 2015 13:04:22 +0200 Subject: [PATCH 5/9] Fix tests --- app/views/repo.coffee | 2 +- tests/unit/components/running-jobs-item-test.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/repo.coffee b/app/views/repo.coffee index 01815ca1..90a3db2c 100644 --- a/app/views/repo.coffee +++ b/app/views/repo.coffee @@ -4,7 +4,7 @@ `import config from 'travis/config/environment'` `import Polling from 'travis/mixins/polling'` -View = BasicView.extend +View = BasicView.extend Polling, reposBinding: 'controllers.repos' repoBinding: 'controller.repo' buildBinding: 'controller.build' diff --git a/tests/unit/components/running-jobs-item-test.coffee b/tests/unit/components/running-jobs-item-test.coffee index 6a10536f..69a8afd9 100644 --- a/tests/unit/components/running-jobs-item-test.coffee +++ b/tests/unit/components/running-jobs-item-test.coffee @@ -2,7 +2,7 @@ moduleForComponent 'running-jobs-item', { # specify the other units that are required for this test - # needs: ['component:foo', 'helper:bar'] + needs: ['mixin:polling', 'service:polling'] } test 'it renders', (assert) -> From b27977baaa0eb08838640c0b0c2032bab020a93a Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 30 Apr 2015 14:04:07 +0200 Subject: [PATCH 6/9] Make polling play nice with promises --- app/mixins/polling.coffee | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/mixins/polling.coffee b/app/mixins/polling.coffee index b55b7c1b..6e8638b7 100644 --- a/app/mixins/polling.coffee +++ b/app/mixins/polling.coffee @@ -25,9 +25,15 @@ mixin = Ember.Mixin.create @stopPollingModel(key) pollModel: (property) -> - model = @get(property) + addToPolling = (model) => + @get('polling').startPolling(model) - @get('polling').startPolling(model) + if model = @get(property) + if model.then + model.then (resolved) -> + addToPolling(resolved) + else + addToPolling(model) stopPollingModel: (property) -> model = @get(property) From a85f0ebc9fa6d3c4da2fd38bea6f2ab59e0da837 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 4 May 2015 14:34:53 +0200 Subject: [PATCH 7/9] Add tests for polling service and mixin --- app/mixins/polling.coffee | 14 +- app/services/polling.coffee | 8 +- tests/unit/mixins/polling-test.coffee | 105 +++++++++++++++ tests/unit/services/polling-test.coffee | 168 ++++++++++++++++++++++++ 4 files changed, 282 insertions(+), 13 deletions(-) create mode 100644 tests/unit/mixins/polling-test.coffee create mode 100644 tests/unit/services/polling-test.coffee diff --git a/app/mixins/polling.coffee b/app/mixins/polling.coffee index 6e8638b7..83cdca8d 100644 --- a/app/mixins/polling.coffee +++ b/app/mixins/polling.coffee @@ -13,11 +13,6 @@ mixin = Ember.Mixin.create @stopPolling() - willDestroy: -> - @_super.apply(this, arguments) - - @stopPolling() - pollModelDidChange: (sender, key, value) -> @pollModel(key) @@ -36,15 +31,14 @@ mixin = Ember.Mixin.create addToPolling(model) stopPollingModel: (property) -> - model = @get(property) - - @get('polling').stopPolling(model) + if model = @get(property) + @get('polling').stopPolling(model) startPolling: -> pollModels = @get('pollModels') if pollModels - pollModels = [pollModels] unless pollModels.forEeach + pollModels = [pollModels] unless Ember.isArray(pollModels) pollModels.forEach (property) => @pollModel(property) @@ -57,7 +51,7 @@ mixin = Ember.Mixin.create pollModels = @get('pollModels') return unless pollModels - pollModels = [pollModels] unless pollModels.forEeach + pollModels = [pollModels] unless Ember.isArray(pollModels) pollModels.forEach (property) => @stopPollingModel(property) diff --git a/app/services/polling.coffee b/app/services/polling.coffee index a9b916b8..f4a3dd1c 100644 --- a/app/services/polling.coffee +++ b/app/services/polling.coffee @@ -1,6 +1,8 @@ `import Ember from 'ember'` -service = Ember.Object.extend +service = Ember.Service.extend + pollingInterval: 30000 + init: -> @_super.apply(this, arguments) @@ -9,7 +11,7 @@ service = Ember.Object.extend interval = setInterval => @poll() - , 30000 + , @get('pollingInterval') @set('interval', interval) @@ -41,7 +43,7 @@ service = Ember.Object.extend model.reload() @get('sources').forEach (source) => - if source.get('destroyed') + if Ember.get(source, 'isDestroyed') @get('sources').removeObject(source) else source.pollHook() diff --git a/tests/unit/mixins/polling-test.coffee b/tests/unit/mixins/polling-test.coffee new file mode 100644 index 00000000..7b127a8e --- /dev/null +++ b/tests/unit/mixins/polling-test.coffee @@ -0,0 +1,105 @@ +`import { test, moduleForComponent } from 'ember-qunit'` +`import Polling from 'travis/mixins/polling'` + +hookRuns = 0 +pollingChangesHistory = [] + +# define component just for testing +define('travis/components/polling-test', [], -> + PollingService = Ember.Object.extend( + startPolling: (model) -> + pollingChangesHistory.push(type: 'start', model: model) + + stopPolling: (model) -> + pollingChangesHistory.push(type: 'stop', model: model) + + startPollingHook: (source) -> + pollingChangesHistory.push(type: 'start-hook', source: source+'') + + stopPollingHook: (source) -> + pollingChangesHistory.push(type: 'stop-hook', source: source+'') + ) + + Ember.Component.extend(Polling, + init: -> + @_super.apply this, arguments + + @set('polling', PollingService.create()) + + pollModels: ['model1', 'model2'], + pollHook: -> + hookRuns += 1 + + toString: -> + '' + ) +) + + +# I want to test this mixin in context of component, so I'm using +# modelForComponent +moduleForComponent 'polling-test', 'PollingTestComponent', { + # specify the other units that are required for this test + needs: [] + + setup: -> + hookRuns = 0 + pollingChangesHistory = [] +} + +test 'it works even if one of the model is null', -> + component = @subject(model1: { name: 'model1' }) + @append() + + Ember.run -> + component.destroy() + + expected = [ + { type: 'start', model: { name: 'model1' } }, + { type: 'start-hook', source: '' } + { type: 'stop', model: { name: 'model1' } }, + { type: 'stop-hook', source: '' } + ] + + deepEqual pollingChangesHistory, expected + +test 'it polls for both models if they are present', -> + component = @subject(model1: { name: 'model1' }, model2: { name: 'model2' }) + @append() + + Ember.run -> + component.destroy() + + expected = [ + { type: 'start', model: { name: 'model1' } }, + { type: 'start', model: { name: 'model2' } }, + { type: 'start-hook', source: '' } + { type: 'stop', model: { name: 'model1' } }, + { type: 'stop', model: { name: 'model2' } }, + { type: 'stop-hook', source: '' } + ] + + deepEqual pollingChangesHistory, expected + +test 'it detects model changes', -> + component = @subject(model1: { name: 'foo' }) + @append() + + Ember.run -> + component.set('model1', { name: 'bar' }) + + Ember.run -> + component.destroy() + + expected = [ + { type: 'start', model: { name: 'foo' } }, + { type: 'start-hook', source: '' } + { type: 'stop', model: { name: 'foo' } }, + { type: 'start', model: { name: 'bar' } }, + { type: 'stop', model: { name: 'bar' } }, + { type: 'stop-hook', source: '' } + ] + + deepEqual pollingChangesHistory, expected + + diff --git a/tests/unit/services/polling-test.coffee b/tests/unit/services/polling-test.coffee new file mode 100644 index 00000000..57bab758 --- /dev/null +++ b/tests/unit/services/polling-test.coffee @@ -0,0 +1,168 @@ +`import Ember from 'ember'` +`import Polling from 'travis/services/polling'` + +service = null + +module 'PollingService', + teardown: -> + unless service.get('isDestroyed') + Ember.run -> + service.destroy() + +test 'polls for each of the models', -> + expect(3) + + history = [] + + service = Polling.create( + pollingInterval: 10 + ) + + model1 = { + reload: -> + ok(true) + history.push 'model1' + } + + model2 = { + reload: -> + ok(true) + history.push 'model2' + } + + service.startPolling(model1) + service.startPolling(model2) + + stop() + + setTimeout -> + start() + + deepEqual history, ['model1', 'model2'] + + Ember.run -> + service.destroy() + , 15 + +test 'it will stop running any reloads after it is destroyed', -> + expect(1) + + service = Polling.create( + pollingInterval: 10 + ) + + model = { + reload: -> + ok(true) + } + + service.startPolling(model) + + stop() + + setTimeout -> + Ember.run -> + service.destroy() + , 15 + + setTimeout -> + start() + , 30 + +test 'it stops reloading models after they were removed from polling', -> + expect(4) + + history = [] + + service = Polling.create( + pollingInterval: 10 + ) + + model1 = { + reload: -> + ok(true) + history.push 'model1' + } + + model2 = { + reload: -> + ok(true) + history.push 'model2' + } + + service.startPolling(model1) + service.startPolling(model2) + + stop() + + setTimeout -> + service.stopPolling(model2) + + setTimeout -> + Ember.run -> + service.destroy() + + start() + + deepEqual history, ['model1', 'model2', 'model1'] + , 10 + , 12 + +test 'it runs a hook on each interval', -> + expect(1) + + history = [] + + service = Polling.create( + pollingInterval: 10 + ) + + source = { + pollHook: -> + ok(true) + } + + service.startPollingHook(source) + + stop() + + setTimeout -> + service.stopPollingHook(source) + + setTimeout -> + Ember.run -> + service.destroy() + + start() + , 10 + , 12 + +test 'it will not run pollHook if the source is destroyed', -> + expect(1) + + history = [] + + service = Polling.create( + pollingInterval: 10 + ) + + source = Ember.Object.extend( + pollHook: -> + ok(true) + ).create() + + service.startPollingHook(source) + + stop() + + setTimeout -> + Ember.run -> + source.destroy() + + setTimeout -> + Ember.run -> + service.destroy() + + start() + , 30 + , 12 From e2db99c7fd82ed9dc9a857b60c7b8cf461248ac9 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Tue, 5 May 2015 10:06:18 +0200 Subject: [PATCH 8/9] Make polling tests more reliable with longer timeouts I was using a very short polling interval of 10ms for testing polling mixin. This lets a little room for any timing problems, which may happen with setTimeout or setInterval. Increasing it to 20ms hopefully can make it more reliable. --- tests/unit/services/polling-test.coffee | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/unit/services/polling-test.coffee b/tests/unit/services/polling-test.coffee index 57bab758..6bbe1ac9 100644 --- a/tests/unit/services/polling-test.coffee +++ b/tests/unit/services/polling-test.coffee @@ -15,7 +15,7 @@ test 'polls for each of the models', -> history = [] service = Polling.create( - pollingInterval: 10 + pollingInterval: 20 ) model1 = { @@ -42,13 +42,13 @@ test 'polls for each of the models', -> Ember.run -> service.destroy() - , 15 + , 30 test 'it will stop running any reloads after it is destroyed', -> expect(1) service = Polling.create( - pollingInterval: 10 + pollingInterval: 20 ) model = { @@ -63,11 +63,11 @@ test 'it will stop running any reloads after it is destroyed', -> setTimeout -> Ember.run -> service.destroy() - , 15 + , 30 setTimeout -> start() - , 30 + , 50 test 'it stops reloading models after they were removed from polling', -> expect(4) @@ -75,7 +75,7 @@ test 'it stops reloading models after they were removed from polling', -> history = [] service = Polling.create( - pollingInterval: 10 + pollingInterval: 30 ) model1 = { @@ -105,8 +105,8 @@ test 'it stops reloading models after they were removed from polling', -> start() deepEqual history, ['model1', 'model2', 'model1'] - , 10 - , 12 + , 30 + , 40 test 'it runs a hook on each interval', -> expect(1) @@ -114,7 +114,7 @@ test 'it runs a hook on each interval', -> history = [] service = Polling.create( - pollingInterval: 10 + pollingInterval: 20 ) source = { @@ -135,7 +135,7 @@ test 'it runs a hook on each interval', -> start() , 10 - , 12 + , 30 test 'it will not run pollHook if the source is destroyed', -> expect(1) @@ -143,7 +143,7 @@ test 'it will not run pollHook if the source is destroyed', -> history = [] service = Polling.create( - pollingInterval: 10 + pollingInterval: 20 ) source = Ember.Object.extend( @@ -164,5 +164,5 @@ test 'it will not run pollHook if the source is destroyed', -> service.destroy() start() - , 30 - , 12 + , 35 + , 30 From 22ac3011499be9c7db71693c5755f89611e339ac Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Tue, 5 May 2015 11:48:43 +0200 Subject: [PATCH 9/9] Fix hook error styling --- app/styles/app/layouts/profile.sass | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/styles/app/layouts/profile.sass b/app/styles/app/layouts/profile.sass index afb6d0be..ceae5ea1 100644 --- a/app/styles/app/layouts/profile.sass +++ b/app/styles/app/layouts/profile.sass @@ -195,11 +195,15 @@ p.profile-user-last .hooks-error width: 100%; padding: 0 $column-gutter/2; + margin-top: 3.3rem; p position: relative padding: $column-gutter/2 $column-gutter*2 $column-gutter/2 $column-gutter/2; color: #de4248 background-color: #f1b6ad + a + color: #de4248 + text-decoration: underline &:after content: "" position: absolute