diff --git a/app/components/build-repo-actions.coffee b/app/components/build-repo-actions.coffee new file mode 100644 index 00000000..5c2c2278 --- /dev/null +++ b/app/components/build-repo-actions.coffee @@ -0,0 +1,9 @@ +`import Ember from 'ember'` +`import RepoActionsItemComponentMixin from 'travis/utils/repo-actions-item-component-mixin'` + +BuildRepoActionsComponent = Ember.Component.extend(RepoActionsItemComponentMixin, + item: Ember.computed.alias('build') + type: 'build' +) + +`export default BuildRepoActionsComponent` diff --git a/app/components/job-repo-actions.coffee b/app/components/job-repo-actions.coffee new file mode 100644 index 00000000..4b6deb59 --- /dev/null +++ b/app/components/job-repo-actions.coffee @@ -0,0 +1,9 @@ +`import Ember from 'ember'` +`import RepoActionsItemComponentMixin from 'travis/utils/repo-actions-item-component-mixin'` + +JobRepoActionsComponent = Ember.Component.extend(RepoActionsItemComponentMixin, + item: Ember.computed.alias('job') + type: 'job' +) + +`export default JobRepoActionsComponent` diff --git a/app/components/repo-actions.coffee b/app/components/repo-actions.coffee new file mode 100644 index 00000000..43d38649 --- /dev/null +++ b/app/components/repo-actions.coffee @@ -0,0 +1,15 @@ +`import Ember from 'ember'` + +RepoActionsComponent = Ember.Component.extend( + displayCodeClimate: (-> + @get('repo.githubLanguage') == 'Ruby' + ).property('repo.githubLanguage') + + actions: + codeClimatePopup: -> + $('.popup').removeClass('display') + $('#code-climate').addClass('display') + return false +) + +`export default RepoActionsComponent` diff --git a/app/models/build.coffee b/app/models/build.coffee index b3b72b6f..39dcca4d 100644 --- a/app/models/build.coffee +++ b/app/models/build.coffee @@ -92,11 +92,13 @@ Build = Model.extend DurationCalculations, @get('jobs').filterBy('canCancel').length ).property('jobs.@each.canCancel') + canRestart: Ember.computed.alias('isFinished') + cancel: (-> Ajax.post "/builds/#{@get('id')}/cancel" ) - requeue: -> + restart: -> Ajax.post "/builds/#{@get('id')}/restart" formattedFinishedAt: (-> diff --git a/app/models/job.coffee b/app/models/job.coffee index ca252075..37f08934 100644 --- a/app/models/job.coffee +++ b/app/models/job.coffee @@ -91,6 +91,8 @@ Job = Model.extend DurationCalculations, !@get('isFinished') ).property('state') + canRestart: Ember.computed.alias('isFinished') + cancel: (-> Ajax.post "/jobs/#{@get('id')}/cancel" ) @@ -103,7 +105,7 @@ Job = Model.extend DurationCalculations, @clearLog() @get('log').fetch() - requeue: -> + restart: -> Ajax.post "/jobs/#{@get('id')}/restart" appendLog: (part) -> diff --git a/app/models/user.coffee b/app/models/user.coffee index 86c3f537..964098f1 100644 --- a/app/models/user.coffee +++ b/app/models/user.coffee @@ -54,6 +54,12 @@ User = Model.extend permissions ).property() + hasAccessToRepo: (repo) -> + id = if repo.get then repo.get('id') else repo + + if permissions = @get('permissions') + permissions.contains parseInt(id) + type: (-> 'user' ).property() diff --git a/app/templates/build.hbs b/app/templates/build.hbs index 136ecc6a..cba093f5 100644 --- a/app/templates/build.hbs +++ b/app/templates/build.hbs @@ -67,8 +67,7 @@ - {{view 'repo-actions'}} - + {{repo-actions build=build repo=build.repo user=auth.currentUser}} diff --git a/app/templates/components/build-repo-actions.hbs b/app/templates/components/build-repo-actions.hbs new file mode 100644 index 00000000..9b00cd3b --- /dev/null +++ b/app/templates/components/build-repo-actions.hbs @@ -0,0 +1,20 @@ +{{#if canCancel}} + {{#if cancelling}} + + {{else}} + + + + {{/if}} +{{/if}} + +{{#if canRestart}} + {{#if restarting}} + + {{else}} + + + + {{/if}} +{{/if}} diff --git a/app/templates/components/code-climate-popup.hbs b/app/templates/components/code-climate-popup.hbs new file mode 100644 index 00000000..372f41ef --- /dev/null +++ b/app/templates/components/code-climate-popup.hbs @@ -0,0 +1,44 @@ + diff --git a/app/templates/components/job-repo-actions.hbs b/app/templates/components/job-repo-actions.hbs new file mode 100644 index 00000000..a02d9fd1 --- /dev/null +++ b/app/templates/components/job-repo-actions.hbs @@ -0,0 +1,19 @@ +{{#if canCancel}} + {{#if cancelling}} + + {{else}} + + + + {{/if}} +{{/if}} + +{{#if canRestart}} + {{#if restarting}} + + {{else}} + + + + {{/if}} +{{/if}} diff --git a/app/templates/components/repo-actions.hbs b/app/templates/components/repo-actions.hbs new file mode 100644 index 00000000..e6cb6c53 --- /dev/null +++ b/app/templates/components/repo-actions.hbs @@ -0,0 +1,33 @@ +
+ + {{! TODO: when `component` helper is available we could just use + with a component name based on type that is passed here }} + {{#if job}} + {{job-repo-actions job=job user=user repo=repo}} + {{else}} + {{build-repo-actions build=build user=user repo=repo}} + {{/if}} + + {{#if displayCodeClimate}} + + + + {{/if}} + +
+ + + + +{{code-climate-popup}} diff --git a/app/templates/job.hbs b/app/templates/job.hbs index 3bcc2155..95bb366e 100644 --- a/app/templates/job.hbs +++ b/app/templates/job.hbs @@ -55,7 +55,7 @@ - {{view 'repo-actions'}} + {{repo-actions job=job repo=job.repo user=auth.currentUser}} diff --git a/app/templates/repos/show/actions.hbs b/app/templates/repos/show/actions.hbs deleted file mode 100644 index fdc68e49..00000000 --- a/app/templates/repos/show/actions.hbs +++ /dev/null @@ -1,55 +0,0 @@ -
- - {{#if view.displayCancelBuild}} - - - - {{/if}} - {{#if view.displayCancelJob}} - - - - {{/if}} - {{#if view.displayRequeueBuild}} - {{#if view.requeueing}} - - {{else}} - - - - {{/if}} - {{/if}} - {{#if view.displayRequeueJob}} - {{#if view.requeueing}} - - {{else}} - - - - {{/if}} - {{/if}} - {{#if view.displayCodeClimate}} - - - - {{/if}} - -
- - - diff --git a/app/templates/repos/show/tools.hbs b/app/templates/repos/show/tools.hbs index 66024bd4..9b990fdb 100644 --- a/app/templates/repos/show/tools.hbs +++ b/app/templates/repos/show/tools.hbs @@ -17,7 +17,6 @@ - - - diff --git a/app/utils/repo-actions-item-component-mixin.coffee b/app/utils/repo-actions-item-component-mixin.coffee new file mode 100644 index 00000000..20d69aa8 --- /dev/null +++ b/app/utils/repo-actions-item-component-mixin.coffee @@ -0,0 +1,50 @@ +`import Ember from 'ember'` + +Mixin = Ember.Mixin.create( + restarting: false + cancelling: false + + userHasPermissionForRepo: (-> + repo = @get('repo') + user = @get('user') + if user && repo + user.hasAccessToRepo(repo) + ).property('user.permissions.[]', 'repo', 'user') + + canCancel: (-> + @get('item.canCancel') && @get('userHasPermissionForRepo') + ).property('userHasPermissionForRepo', 'item.canCancel') + + canRestart: (-> + @get('item.canRestart') && @get('userHasPermissionForRepo') + ).property('userHasPermissionForRepo', 'item.canRestart') + + actions: + restart: -> + return if @get('restarting') + @set('restarting', true) + + onFinished = => + @set('restarting', false) + @get('item').restart().then(onFinished, onFinished) + + cancel: -> + return if @get('cancelling') + @set('cancelling', true) + + type = @get('type') + + @get('item').cancel().then => + @set('cancelling', false) + Travis.flash(success: "#{type.capitalize()} has been successfully canceled.") + , (xhr) => + @set('cancelling', false) + if xhr.status == 422 + Travis.flash(error: "This #{type} can't be canceled") + else if xhr.status == 403 + Travis.flash(error: "You don't have sufficient access to cancel this #{type}") + else + Travis.flash(error: "An error occured when canceling the #{type}") +) + +`export default Mixin` diff --git a/app/views/repo-actions.coffee b/app/views/repo-actions.coffee deleted file mode 100644 index 53b98acf..00000000 --- a/app/views/repo-actions.coffee +++ /dev/null @@ -1,172 +0,0 @@ -`import Ember from 'ember'` -`import { plainTextLog as plainTextLogUrl } from 'travis/utils/urls'` -`import Job from 'travis/models/job'` -`import config from 'travis/config/environment'` -`import BasicView from 'travis/views/basic'` - -View = BasicView.extend - templateName: 'repos/show/actions' - - repoBinding: 'controller.repo' - buildBinding: 'controller.build' - jobBinding: 'controller.job' - tabBinding: 'controller.tab' - currentUserBinding: 'controller.currentUser' - - actions: - requeueBuild: -> - if @get('canRequeueBuild') - @requeue @get('build') - - requeueJob: -> - if @get('canRequeueJob') - @requeue @get('_job') - - cancelBuild: -> - if @get('canCancelBuild') - Travis.flash(notice: 'Build cancellation has been scheduled.') - @get('build').cancel().then -> - Travis.flash(success: 'Build has been successfully canceled.') - , (xhr) -> - if xhr.status == 422 - Travis.flash(error: 'This build can\'t be canceled') - else if xhr.status == 403 - Travis.flash(error: 'You don\'t have sufficient access to cancel this build') - else - Travis.flash(error: 'An error occured when canceling the build') - - - removeLog: -> - @popupCloseAll() - if @get('canRemoveLog') - job = @get('_job') || @get('build.jobs.firstObject') - job.removeLog().then -> - Travis.flash(success: 'Log has been successfully removed.') - , (xhr) -> - if xhr.status == 409 - Travis.flash(error: 'Log can\'t be removed') - else if xhr.status == 401 - Travis.flash(error: 'You don\'t have sufficient access to remove the log') - else - Travis.flash(error: 'An error occured when removing the log') - - cancelJob: -> - if @get('canCancelJob') - Travis.flash(notice: 'Job cancellation has been scheduled.') - @get('_job').cancel().then -> - Travis.flash(success: 'Job has been successfully canceled.') - , (xhr) -> - if xhr.status == 422 - Travis.flash(error: 'This job can\'t be canceled') - else if xhr.status == 403 - Travis.flash(error: 'You don\'t have sufficient access to cancel this job') - else - Travis.flash(error: 'An error occured when canceling the job') - - codeClimatePopup: -> - @popupCloseAll() - console.log('repo-actions view') - @popup('code-climate') - return false - - hasPermission: (-> - if permissions = @get('currentUser.permissions') - permissions.contains parseInt(@get('repo.id')) - ).property('currentUser.permissions.length', 'repo.id') - - hasPushPermission: (-> - if permissions = @get('currentUser.pushPermissions') - permissions.contains parseInt(@get('repo.id')) - ).property('currentUser.pushPermissions.length', 'repo.id') - - displayRequeueBuild: (-> - @get('isBuildTab') && @get('build.isFinished') - ).property('isBuildTab', 'build.isFinished') - - canRequeueBuild: (-> - @get('displayRequeueBuild') && @get('hasPermission') - ).property('displayRequeueBuild', 'hasPermission') - - displayRequeueJob: (-> - @get('isJobTab') && @get('job.isFinished') - ).property('isJobTab', 'job.isFinished') - - canRequeueJob: (-> - @get('displayRequeueJob') && @get('hasPermission') - ).property('displayRequeueJob', 'hasPermission') - - showDownloadLog: (-> - @get('jobIdForLog') && (@get('isJobTab') || @get('isBuildTab')) - ).property('jobIdForLog', 'isJobTab', 'isBuildTab') - - _job: (-> - if id = @get('jobIdForLog') - store =Travis.__container__.lookup('store:main') - store.find('job', id) - store.recordForId('job', id) - ).property('jobIdForLog') - - jobIdForLog: (-> - job = @get('job.id') - unless job - if @get('build.jobs.length') == 1 - job = @get('build.jobs').objectAt?(0).get?('id') - job - ).property('job.id', 'build.jobs.firstObject.id', 'build.jobs.length') - - plainTextLogUrl: (-> - if id = @get('jobIdForLog') - url = plainTextLogUrl(id) - if config.pro - token = @get('job.log.token') || @get('build.jobs.firstObject.log.token') - url += "&access_token=#{token}" - url - ).property('jobIdForLog', 'job.log.token', 'build.jobs.firstObject.log.token') - - canRemoveLog: (-> - @get('displayRemoveLog') - ).property('displayRemoveLog') - - displayRemoveLog: (-> - if job = @get('_job') - (@get('isJobTab') || (@get('isBuildTab') && @get('build.jobs.length') == 1)) && - job.get('canRemoveLog') && @get('hasPermission') - ).property('isJobTab', 'isBuildTab', 'build.jobs.length', '_job.canRemoveLog', 'jobIdForLog', 'canRemoveLog', 'hasPermission') - - canCancelBuild: (-> - @get('displayCancelBuild') && @get('hasPermission') - ).property('displayCancelBuild', 'hasPermission') - - displayCancelBuild: (-> - @get('isBuildTab') && @get('build.canCancel') - ).property('isBuildTab', 'build.canCancel') - - canCancelJob: (-> - @get('displayCancelJob') && @get('hasPermission') - ).property('displayCancelJob', 'hasPermission') - - displayCancelJob: (-> - @get('isJobTab') && @get('job.canCancel') - ).property('isJobTab', 'job.canCancel') - - isJobTab: (-> - @get('tab') == 'job' - ).property('tab', 'repo.id') - - isBuildTab: (-> - ['current', 'build'].indexOf(@get('tab')) > -1 - ).property('tab') - - displayCodeClimate: (-> - @get('repo.githubLanguage') == 'Ruby' - ).property('repo.githubLanguage') - - requeueFinished: -> - @set('requeueing', false) - - requeue: (thing) -> - return if @get('requeueing') - @set('requeueing', true) - thing.requeue().then(this.requeueFinished.bind(this), this.requeueFinished.bind(this)) - -`export default View` diff --git a/tests/acceptance/dashboard-test.coffee b/tests/acceptance/dashboard-test.coffee index 41ede825..f3b2a29a 100644 --- a/tests/acceptance/dashboard-test.coffee +++ b/tests/acceptance/dashboard-test.coffee @@ -63,5 +63,5 @@ test 'visiting /dashboard', -> visit '/dashboard' andThen -> - equal find('.tiles .repo').length, 1, 'there should be one repo displayed on dashboard' - equal find('.tiles .repo').text(), 'travis-web', 'travis-web repository should be displayed' + equal find('.dashboard-active .row').length, 1, 'there should be one repo displayed on dashboard' + equal find('.dashboard-active .row h2').text(), 'travis-web', 'travis-web repository should be displayed' diff --git a/tests/unit/components/build-repo-actions-test.coffee b/tests/unit/components/build-repo-actions-test.coffee new file mode 100644 index 00000000..ac57952c --- /dev/null +++ b/tests/unit/components/build-repo-actions-test.coffee @@ -0,0 +1,58 @@ +`import { test, moduleForComponent } from 'ember-qunit'` + +moduleForComponent 'build-repo-actions', 'BuildRepoActionsComponent', { + # specify the other units that are required for this test + # needs: ['component:foo', 'helper:bar'] +} + +test 'it shows cancel button if canCancel is true', -> + component = @subject(canCancel: true) + @append() + + ok component.$('a[title="Cancel Build"]').length, 'cancel link should be visible' + +test 'it shows restart button if canRestart is true', -> + component = @subject(canRestart: true) + @append() + + ok component.$('a[title="Restart Build"]').length, 'restart link should be visible' + +test 'user can cancel if she has permissions to a repo and build is cancelable', -> + build = Ember.Object.create(canCancel: false, userHasPermissionForRepo: true) + + component = @subject(build: build, userHasPermissionForRepo: false) + ok !component.get('canCancel') + + component.set('userHasPermissionForRepo', true) + ok !component.get('canCancel') + + build.set('canCancel', true) + ok component.get('canCancel') + +test 'user can restart if she has permissions to a repo and job is restartable', -> + build = Ember.Object.create(canRestart: false, userHasPermissionForRepo: true) + + component = @subject(build: build, userHasPermissionForRepo: false) + ok !component.get('canRestart') + + component.set('userHasPermissionForRepo', true) + ok !component.get('canRestart') + + build.set('canRestart', true) + ok component.get('canRestart') + +test 'it properly checks for user permissions for a repo', -> + expect 3 + + repo = Ember.Object.create(id: 44) + user = Ember.Object.extend( + hasAccessToRepo: (repo) -> + ok repo.get('id', 44) + ok true, 'hasAccessToRepo was called' + + false + ).create() + + component = @subject(user: user, repo: repo) + + ok !component.get('userHasPermissionForRepo'), 'user should not have access to a repo' diff --git a/tests/unit/components/job-repo-actions-test.coffee b/tests/unit/components/job-repo-actions-test.coffee new file mode 100644 index 00000000..e5ff256f --- /dev/null +++ b/tests/unit/components/job-repo-actions-test.coffee @@ -0,0 +1,56 @@ +`import { test, moduleForComponent } from 'ember-qunit'` + +moduleForComponent 'job-repo-actions', 'JobRepoActionsComponent', { +} + +test 'it shows cancel button if canCancel is true', -> + component = @subject(canCancel: true) + @append() + + ok component.$('a[title="Cancel Job"]').length, 'cancel link should be visible' + +test 'it shows restart button if canRestart is true', -> + component = @subject(canRestart: true) + @append() + + ok component.$('a[title="Restart Job"]').length, 'restart link should be visible' + +test 'user can cancel if she has permissions to a repo and job is cancelable', -> + job = Ember.Object.create(canCancel: false, userHasPermissionForRepo: true) + + component = @subject(job: job, userHasPermissionForRepo: false) + ok !component.get('canCancel') + + component.set('userHasPermissionForRepo', true) + ok !component.get('canCancel') + + job.set('canCancel', true) + ok component.get('canCancel') + +test 'user can restart if she has permissions to a repo and job is restartable', -> + job = Ember.Object.create(canRestart: false, userHasPermissionForRepo: true) + + component = @subject(job: job, userHasPermissionForRepo: false) + ok !component.get('canRestart') + + component.set('userHasPermissionForRepo', true) + ok !component.get('canRestart') + + job.set('canRestart', true) + ok component.get('canRestart') + +test 'it properly checks for user permissions for a repo', -> + expect 3 + + repo = Ember.Object.create(id: 44) + user = Ember.Object.extend( + hasAccessToRepo: (repo) -> + ok repo.get('id', 44) + ok true, 'hasAccessToRepo was called' + + false + ).create() + + component = @subject(user: user, repo: repo) + + ok !component.get('userHasPermissionForRepo'), 'user should not have access to a repo' diff --git a/tests/unit/components/repo-actions-test.coffee b/tests/unit/components/repo-actions-test.coffee new file mode 100644 index 00000000..c9935252 --- /dev/null +++ b/tests/unit/components/repo-actions-test.coffee @@ -0,0 +1,25 @@ +`import { test, moduleForComponent } from 'ember-qunit'` + +moduleForComponent 'repo-actions', 'RepoActionsComponent', { + # specify the other units that are required for this test + needs: ['component:build-repo-actions', 'component:job-repo-actions'] +} + +test 'it displays code climate if the repo language is ruby', -> + # creates the component instance + repo = Ember.Object.create(githubLanguage: 'Ruby') + + component = @subject(repo: repo) + @append() + + ok component.get('displayCodeClimate'), 'component should try to display code climate' + ok component.$('a[name=code-climate]').length, 'component should render a code climate button' + +test 'it doesn\'t display code climate for other languages', -> + repo = Ember.Object.create(githubLanguage: 'Go') + + component = @subject(repo: repo) + @append() + + ok !component.get('displayCodeClimate'), 'component should not try to display code climate' + ok !component.$('a[name=code-climate]').length, 'component should not render a code climate button'