Commit 93df8ac08df9ae38ece2ea49fa50556e30db237d

Authored by marcheing
2 parents 0995444d 191007b5

Merge pull request #228 from mezuro/process_by_branch

Process by branch
.travis.yml
... ... @@ -5,7 +5,7 @@ addons:
5 5 postgresql: "9.3"
6 6  
7 7 before_script:
8   - - git clone https://github.com/mezuro/kalibro_install.git -b v2.3 kalibro_install
  8 + - git clone https://github.com/mezuro/kalibro_install.git -b v2.5 kalibro_install
9 9 - pushd kalibro_install
10 10 # Remove bugged libzmq3 package, see https://github.com/travis-ci/travis-ci/issues/982 and https://github.com/travis-ci/travis-ci/issues/1715 for details
11 11 - sudo apt-get remove libzmq3
... ...
Gemfile
... ... @@ -31,7 +31,7 @@ gem 'jbuilder', '~> 2.2.2'
31 31 gem 'devise', '~> 3.4.0'
32 32  
33 33 # Kalibro integration
34   -gem 'kalibro_client'
  34 +gem 'kalibro_client', '~> 0.3.0'
35 35  
36 36 # PostgreSQL integration
37 37 gem "pg", "~> 0.18.1"
... ... @@ -113,6 +113,9 @@ group :development, :test do
113 113 gem 'i18n_generators'
114 114  
115 115 gem 'sprockets', '~>2.12.3' # spckets 3.0.3 breaks konacha
  116 +
  117 + # Mocks and stubs for javascript tests
  118 + gem 'sinon-rails'
116 119 end
117 120  
118 121 # Acceptance tests
... ...
Gemfile.lock
... ... @@ -156,8 +156,8 @@ GEM
156 156 thor (>= 0.14, < 2.0)
157 157 jquery-ui-rails (5.0.3)
158 158 railties (>= 3.2.16)
159   - json (1.8.2)
160   - kalibro_client (0.1.1)
  159 + json (1.8.3)
  160 + kalibro_client (0.3.0)
161 161 activesupport (>= 2.2.1)
162 162 faraday_middleware (~> 0.9.0)
163 163 konacha (3.3.0)
... ... @@ -190,7 +190,7 @@ GEM
190 190 metaclass (0.0.4)
191 191 mime-types (2.5)
192 192 mini_portile (0.6.2)
193   - minitest (5.6.1)
  193 + minitest (5.7.0)
194 194 mocha (1.1.0)
195 195 metaclass (~> 0.0.1)
196 196 multi_json (1.11.0)
... ... @@ -277,6 +277,8 @@ GEM
277 277 json (~> 1.8)
278 278 simplecov-html (~> 0.10.0)
279 279 simplecov-html (0.10.0)
  280 + sinon-rails (1.15.0)
  281 + railties (>= 3.1)
280 282 spring (1.3.6)
281 283 sprockets (2.12.3)
282 284 hike (~> 1.2)
... ... @@ -349,7 +351,7 @@ DEPENDENCIES
349 351 jbuilder (~> 2.2.2)
350 352 jquery-rails
351 353 jquery-ui-rails (~> 5.0.0)
352   - kalibro_client
  354 + kalibro_client (~> 0.3.0)
353 355 konacha (~> 3.3.0)
354 356 mocha
355 357 pg (~> 0.18.1)
... ... @@ -361,6 +363,7 @@ DEPENDENCIES
361 363 sdoc (~> 0.4.0)
362 364 shoulda-matchers (~> 2.8.0)
363 365 simplecov
  366 + sinon-rails
364 367 spring
365 368 sprockets (~> 2.12.3)
366 369 sqlite3
... ...
app/assets/javascripts/application.js
... ... @@ -16,6 +16,7 @@
16 16 //= require twitter/bootstrap
17 17 //= require turbolinks
18 18 //= require modules
  19 +//= require repository
19 20 //= require Chart
20 21 //= require_tree .
21 22 //= require colorpicker
... ...
app/assets/javascripts/repository.js.coffee
1   -class Module.Repository
2   - constructor: (@project_id, @repository_id) ->
3   -
4   - poll_state: (last_state) ->
5   - $.post '/projects/' + @project_id + '/repositories/' + @repository_id + '/state',
6   - last_state: last_state
7   -
8   - schedule_poll_state: (last_state) ->
9   - context = this
10   - call = () ->
11   - context.poll_state(last_state)
12   -
13   - setTimeout( call, 15000 ) # Delays in 15s the next call
14   -
15   - @set_loader: (loading_html) ->
16   - $('div#processing_information').html(loading_html)
  1 +@Repository = {}
... ...
app/assets/javascripts/repository/branch.js.coffee 0 → 100644
... ... @@ -0,0 +1,57 @@
  1 +class Repository.Branch
  2 + constructor: ->
  3 + @names = {}
  4 + @request = null
  5 +
  6 + toggle: ->
  7 + if $("#repository_scm_type").val() != "SVN"
  8 + $("#branches").show()
  9 + @fetch($("#repository_address").val())
  10 + else
  11 + $("#branches").hide()
  12 +
  13 + cancel_request: ->
  14 + if @request != null
  15 + @request.abort()
  16 + @request = null
  17 +
  18 + fill_options: (options, el) ->
  19 + # FIXME: this works only if master is the default branch
  20 + # it can be improved by moving it into KalibroProcessor
  21 + default_branch = "master"
  22 + if default_branch in options
  23 + # Brings the default branch as the first option
  24 + index = options.indexOf(default_branch)
  25 + options.splice(index, 1)
  26 + options.unshift(default_branch)
  27 + for option in options
  28 + do ->
  29 + el.append($("<option></option>")
  30 + .attr("value", option)
  31 + .text(option))
  32 +
  33 + fetch: (address) ->
  34 + @cancel_request()
  35 +
  36 + # Prevent a call with blank address
  37 + if address == ""
  38 + return
  39 +
  40 + branches_select_box = $("#repository_branch")
  41 + branches_select_box.empty() # remove old options
  42 +
  43 + if @names[address]?
  44 + @fill_options(@names[address], branches_select_box)
  45 + return
  46 +
  47 + scm_type = $("#repository_scm_type").val()
  48 +
  49 + context = this
  50 + @request = $.get '/repository_branches',
  51 + {'url': address, 'scm_type': scm_type},
  52 + (data) ->
  53 + unless data["errors"]
  54 + options = data["branches"]
  55 + if options != null
  56 + context.names[address] = options
  57 + context.fill_options(options, branches_select_box)
... ...
app/assets/javascripts/repository/state.js.coffee 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +class Repository.State
  2 + constructor: (@project_id, @repository_id) ->
  3 +
  4 + poll_state: (last_state) ->
  5 + $.post('/projects/' + @project_id + '/repositories/' + @repository_id + '/state',
  6 + last_state: last_state)
  7 +
  8 + schedule_poll_state: (last_state) ->
  9 + context = this
  10 + call = () ->
  11 + context.poll_state(last_state)
  12 +
  13 + setTimeout(call, 15000) # Delays in 15s the next call
  14 +
  15 + @set_loader: (loading_html) ->
  16 + $('div#processing_information').html(loading_html)
0 17 \ No newline at end of file
... ...
app/controllers/repositories_controller.rb
... ... @@ -84,6 +84,15 @@ class RepositoriesController &lt; ApplicationController
84 84 end
85 85 end
86 86  
  87 + def branches
  88 + branch_params = branches_params
  89 + branches_list = Repository.branches(branch_params[:url], branch_params[:scm_type])
  90 +
  91 + respond_to do |format|
  92 + format.json { render json: branches_list }
  93 + end
  94 + end
  95 +
87 96 private
88 97 def set_project_id_repository_types_and_configurations
89 98 @project_id = params[:project_id]
... ... @@ -119,6 +128,10 @@ private
119 128 params[:repository]
120 129 end
121 130  
  131 + def branches_params
  132 + params.permit(:scm_type, :url)
  133 + end
  134 +
122 135 # Code extracted from create action
123 136 def create_and_redir(format)
124 137 if @repository.save
... ...
app/views/repositories/_form.html.erb
... ... @@ -43,7 +43,7 @@
43 43 <div class="form-row">
44 44 <div class="field-container">
45 45 <%= f.label :scm_type, class: 'control-label' %>
46   - <%= f.select( :scm_type, @repository_types, class: 'tooltip-control' ) %>
  46 + <%= f.select( :scm_type, @repository_types, {}, class: 'tooltip-control', onchange: "_repository_branch.toggle();" ) %>
47 47 </div>
48 48 <div class="help-container">
49 49 <p>
... ... @@ -55,7 +55,7 @@
55 55 <div class="form-row">
56 56 <div class="field-container">
57 57 <%= f.label :address, class: 'control-label' %>
58   - <%= f.text_field :address, :required => true, class: 'text-field form-control' %>
  58 + <%= f.text_field :address, :required => true, class: 'text-field form-control', onchange: "_repository_branch.fetch(this.value);" %>
59 59 </div>
60 60 <div class="help-container">
61 61 <p>
... ... @@ -64,6 +64,20 @@
64 64 </div>
65 65 </div>
66 66  
  67 + <div id="branches">
  68 + <div class="form-row">
  69 + <div class="field-container">
  70 + <%= f.label :branch, class: 'control-label' %>
  71 + <%= f.select :branch, []%>
  72 + </div>
  73 + <div class="help-container">
  74 + <p>
  75 + <%= t('activemodel.hints.repository.address') %>
  76 + </p>
  77 + </div>
  78 + </div>
  79 + </div>
  80 +
67 81 <div class="form-row">
68 82 <div class="field-container">
69 83 <%= f.label :period, class: 'control-label' %>
... ... @@ -94,3 +108,11 @@
94 108 <%= f.submit t('save'), class: 'btn btn-primary' %>
95 109 <%= link_to t('back'), project_path(@project_id), class: 'btn btn-default' %>
96 110 </div>
  111 +
  112 +<script type="text/javascript">
  113 + var _repository_branch = new Repository.Branch;
  114 +
  115 + $(document).on('page:load ready', function() {
  116 + _repository_branch.toggle();
  117 + });
  118 +</script>
... ...
app/views/repositories/reload_processing.js.erb
... ... @@ -7,6 +7,6 @@ current_path = current_path_splited.join(&quot;/&quot;);
7 7 // This if prevents it from updating the repository's state after the user leaves its page
8 8 if(current_path == '<%= project_repository_path(project_id: @repository.project_id, id: @repository, locale: nil) %>'){
9 9 $('div#processing_information').html('<%= escape_javascript(render partial: "processing_information") %>');
10   - repository = new Module.Repository(<%= @repository.project_id %>, <%= @repository.id %>)
  10 + repository = new Repository.State(<%= @repository.project_id %>, <%= @repository.id %>)
11 11 repository.schedule_poll_state('<%= @processing.state %>')
12 12 }
... ...
app/views/repositories/show.html.erb
... ... @@ -23,6 +23,11 @@
23 23 </p>
24 24  
25 25 <p>
  26 + <strong><%= Repository.human_attribute_name('branch') %>:</strong>
  27 + <%= @repository.branch %>
  28 +</p>
  29 +
  30 +<p>
26 31 <strong><%= Repository.human_attribute_name('period') %>:</strong>
27 32 <%= periodicity_option(@repository.period) %>
28 33 </p>
... ... @@ -42,7 +47,7 @@
42 47 <%= select_tag(:month, options_for_select(month_options), :style => "width:55px; margin-top:5px") %>
43 48 <%= label_tag :year, t("year") %>:
44 49 <%= select_tag(:year, options_for_select(year_options), :style => "width:70px; margin-top:5px") %>
45   - <%= submit_tag(t('search'), class: 'btn btn-info', style: 'margin-bottom:5px', onClick: "Module.Repository.set_loader('#{image_tag 'loader.gif'} Loading data. Please, wait.')") %>
  50 + <%= submit_tag(t('search'), class: 'btn btn-info', style: 'margin-bottom:5px', onClick: "Repository.State.set_loader('#{image_tag 'loader.gif'} Loading data. Please, wait.')") %>
46 51 </p>
47 52 <% end %>
48 53  
... ... @@ -66,7 +71,7 @@
66 71 </div>
67 72 <script type="text/javascript">
68 73 $(document).ready(function () {
69   - (new Module.Repository(<%= @repository.project_id %>, <%= @repository.id %>)).poll_state('')
  74 + (new Repository.State(<%= @repository.project_id %>, <%= @repository.id %>)).poll_state('')
70 75 });
71 76  
72 77 //Loads the accordion
... ...
config/locales/views/repository/en.yml
... ... @@ -12,6 +12,7 @@ en:
12 12 scm_type: "Type"
13 13 address: "Address"
14 14 period: "Process Period"
  15 + branch: "Branch"
15 16 hints:
16 17 repository:
17 18 name: "The name of your Repository."
... ... @@ -21,6 +22,7 @@ en:
21 22 address: "The URL where the Repository is located."
22 23 period: "Select how often the Repository will be reprocessed."
23 24 kalibro_configuration: "A %{configuration_href} defines all the metrics to be calculated in the source code. Choose your weapon!"
  25 + branch: "The branch to be analyzed."
24 26 errors:
25 27 repository:
26 28 no_metric_results: "Repository process returned with error. There are no metric results."
... ... @@ -33,4 +35,4 @@ en:
33 35 modules_tree: "Modules Tree"
34 36 metric_results: "Metric Results"
35 37 loading: "Loading data. Please, wait."
36   - date_processing: "Retrieve the closest processing information from"
37 38 \ No newline at end of file
  39 + date_processing: "Retrieve the closest processing information from"
... ...
config/locales/views/repository/pt.yml
... ... @@ -12,6 +12,7 @@ pt:
12 12 scm_type: "Tipo"
13 13 address: "Endereço"
14 14 period: "Período"
  15 + branch: "Ramo"
15 16 hints:
16 17 repository:
17 18 name: "O nome do seu Repositório."
... ... @@ -21,6 +22,7 @@ pt:
21 22 address: "A URL onde o respositório está acessível."
22 23 period: "Selecione o quão frequentemente o repositório será reprocessado."
23 24 kalibro_configuration: "Uma %{configuration_href} define todas as métricas a serem extraídas do códifo-fonte."
  25 + branch: "O ramo que será analisado."
24 26 errors:
25 27 repository:
26 28 no_metric_results: "O processamento do Repósitório retornou um erro. Não há Resultados de Métrica."
... ... @@ -33,4 +35,4 @@ pt:
33 35 modules_tree: "Árvore de Módulos"
34 36 metric_results: "Resultados de Métrica"
35 37 loading: "Carregando os dados. Por favor, aguarde."
36   - date_processing: "Obtenha a informação de processamento mais próxima a"
37 38 \ No newline at end of file
  39 + date_processing: "Obtenha a informação de processamento mais próxima a"
... ...
config/routes.rb
... ... @@ -12,6 +12,8 @@ Rails.application.routes.draw do
12 12 get '/repositories/:id/process' => 'repositories#process_repository', as: :repository_process
13 13 end
14 14  
  15 + get '/repository_branches' => 'repositories#branches', as: :repository_branches
  16 +
15 17 resources :kalibro_configurations do
16 18 get '/metric_configurations/choose_metric' => 'metric_configurations#choose_metric', as: :choose_metric
17 19 resources :metric_configurations, except: [:update, :new] do
... ...
features/repository/create.feature
... ... @@ -13,8 +13,12 @@ Scenario: repository creation
13 13 And I fill the Name field with "Kalibro"
14 14 And I fill the Description field with "Description"
15 15 And I set the select field "License" as "ISC License (ISC)"
16   - And I set the select field "Type" as "GIT"
17   - And I fill the Address field with "https://github.com/mezuro/kalibro_gem.git"
  16 + When I set the select field "Type" as "SVN"
  17 + Then I should not see "Branch"
  18 + When I set the select field "Type" as "GIT"
  19 + Then I should see "Branch"
  20 + Given I fill the Address field with "https://github.com/mezuro/kalibro_client.git"
  21 + And I set the select field "Branch" as "master"
18 22 And I set the select field "Process Period" as "1 day"
19 23 And I set the select field "Configuration" as "Java"
20 24 When I press the Save button
... ... @@ -48,8 +52,8 @@ Scenario: repository creation with name already taken
48 52 And I fill the Name field with "KalibroEntities"
49 53 And I fill the Description field with "Description"
50 54 And I set the select field "License" as "ISC License (ISC)"
  55 + And I fill the Address field with "https://github.com/mezuro/kalibro_client.git"
51 56 And I set the select field "Type" as "GIT"
52   - And I fill the Address field with "https://github.com/mezuro/kalibro_gem.git"
53 57 And I set the select field "Process Period" as "1 day"
54 58 And I set the select field "Configuration" as "Java"
55 59 When I press the Save button
... ... @@ -65,8 +69,8 @@ Scenario: Repository name with whitespaces
65 69 And I am at the New Repository page
66 70 And I fill the Name field with " Kalibro Entities "
67 71 And I set the select field "License" as "ISC License (ISC)"
  72 + And I fill the Address field with "https://github.com/mezuro/kalibro_client.git"
68 73 And I set the select field "Type" as "GIT"
69   - And I fill the Address field with "https://github.com/mezuro/kalibro_gem.git"
70 74 And I set the select field "Process Period" as "1 day"
71 75 And I set the select field "Configuration" as "Java"
72 76 When I press the Save button
... ...
spec/controllers/repositories_controller_spec.rb
... ... @@ -347,4 +347,67 @@ describe RepositoriesController, :type =&gt; :controller do
347 347 end
348 348 it { is_expected.to redirect_to(project_repository_path(repository.project_id, repository.id)) }
349 349 end
  350 +
  351 + describe 'branches' do
  352 + let(:url) { "dummy-url" }
  353 + let(:scm_type) { "GIT" }
  354 +
  355 + context 'valid parameters' do
  356 + let!(:branches) { ['branch1', 'branch2'] }
  357 +
  358 + before :each do
  359 + sign_in FactoryGirl.create(:user)
  360 + Repository.expects(:branches).with(url, scm_type).returns(branches: branches)
  361 + get :branches, url: url, scm_type: scm_type, format: :json
  362 + end
  363 +
  364 + it 'is expected to return a list of branches' do
  365 + expect(JSON.parse(response.body)).to eq(JSON.parse({branches: branches}.to_json))
  366 + end
  367 +
  368 + it { is_expected.to respond_with(:success) }
  369 + end
  370 +
  371 + context 'invalid parameters' do
  372 + before :each do
  373 + sign_in FactoryGirl.create(:user)
  374 + Repository.expects(:branches).with(url, scm_type).returns(errors: ['Error'])
  375 + get :branches, url: url, scm_type: scm_type, format: :json
  376 + end
  377 +
  378 + it 'is expected to return an empty list' do
  379 + expect(JSON.parse(response.body)).to eq(JSON.parse({errors: ['Error']}.to_json))
  380 + end
  381 +
  382 + it { is_expected.to respond_with(:success) }
  383 + end
  384 + end
  385 +
  386 + describe 'branches_params' do
  387 + let!(:url) { 'dummy-url' }
  388 + let!(:scm_type) { 'GIT' }
  389 + let(:parameters) { ActionController::Parameters.new(scm_type: scm_type, url: url) }
  390 +
  391 + context 'valid parameters' do
  392 + it 'should return a hash with the permitted parameters' do
  393 + subject.params = parameters
  394 + expect(subject.params.permitted?).to be_falsey
  395 + result = subject.send(:branches_params)
  396 + expect(result).to eq(parameters)
  397 + expect(result.permitted?).to be_truthy
  398 + end
  399 + end
  400 +
  401 + context 'invalid parameters' do
  402 + let(:invalid_parameters) { ActionController::Parameters.new(scm_type: scm_type, url: url, something_evil: 'fizzbuzz') }
  403 +
  404 + it 'should return a valid parameters hash' do
  405 + subject.params = invalid_parameters
  406 + expect(subject.params.permitted?).to be_falsey
  407 + result = subject.send(:branches_params)
  408 + expect(result).to eq(parameters)
  409 + expect(result.permitted?).to be_truthy
  410 + end
  411 + end
  412 + end
350 413 end
... ...
spec/javascripts/repository/branch_spec.js.coffee 0 → 100644
... ... @@ -0,0 +1,38 @@
  1 +#= require jquery
  2 +#= require repository
  3 +#= require repository/branch
  4 +#= require sinon
  5 +
  6 +describe "Branch#constructor", ->
  7 + it "should construct a branch", ->
  8 + subject = new Repository.Branch()
  9 + subject.names.should.deep.equal({})
  10 + assert.isNull(subject.request)
  11 +
  12 +describe "toggle", ->
  13 + before ->
  14 + @subject = new Repository.Branch()
  15 +
  16 + @combo_box = sinon.stub()
  17 + $ = sinon.stub(window, "$")
  18 + $.withArgs("#branches").returns(@combo_box)
  19 +
  20 + context "scm_type = SVN", ->
  21 + before ->
  22 + @combo_box.hide = sinon.spy()
  23 + $.withArgs("#repository_scm_type").returns({val: -> "SVN"})
  24 +
  25 + it "should hide the branches combo box", ->
  26 + @subject.toggle()
  27 + assert.isTrue(@combo_box.hide.calledOnce)
  28 +
  29 + context "scm_type != SVN", ->
  30 + before ->
  31 + @combo_box.show = sinon.spy()
  32 + $.withArgs("#repository_address").returns({val: -> "https://github.com/mezuro/prezento.git"})
  33 + $.withArgs("#repository_scm_type").returns({val: -> "GIT"})
  34 + sinon.stub(@subject, "fetch").withArgs("https://github.com/mezuro/prezento.git")
  35 +
  36 + it "should show the branches combo box", ->
  37 + @subject.toggle()
  38 + assert.isTrue(@combo_box.show.calledOnce)
... ...
spec/routing/repositories_routing_spec.rb
... ... @@ -24,5 +24,7 @@ describe RepositoriesController, :type =&gt; :routing do
24 24 to(controller: :repositories, action: :state_with_date, project_id: 1, id: 1) }
25 25 it { is_expected.to route(:get, '/projects/1/repositories/1/process').
26 26 to(controller: :repositories, action: :process_repository, project_id: 1, id: 1) }
  27 + it { is_expected.to route(:get, '/repository_branches').
  28 + to(controller: :repositories, action: :branches) }
27 29 end
28 30 end
... ...