diff options
author | Shinya Maeda <shinya@gitlab.com> | 2018-10-04 09:44:14 +0900 |
---|---|---|
committer | Shinya Maeda <shinya@gitlab.com> | 2018-10-04 09:44:14 +0900 |
commit | 5381985bd012562696637122b1dcd067480a94d3 (patch) | |
tree | 7e05baefc2df32f6ef324cae755de6294c89c92f /spec | |
parent | 9c13a512f4a836d2d0d61dc4fbbc6fd8f76aa474 (diff) | |
parent | dfb9ac3a5f97a4c556bacea78174836fe7d39145 (diff) | |
download | gitlab-ce-5381985bd012562696637122b1dcd067480a94d3.tar.gz |
Merge branch 'master-ce' into scheduled-manual-jobs
Diffstat (limited to 'spec')
73 files changed, 2634 insertions, 1014 deletions
diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb index f979d2f6090..a4904272706 100644 --- a/spec/features/admin/admin_manage_applications_spec.rb +++ b/spec/features/admin/admin_manage_applications_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'admin manage applications' do check :doorkeeper_application_trusted click_on 'Submit' expect(page).to have_content('Application: test') - expect(page).to have_content('Application Id') + expect(page).to have_content('Application ID') expect(page).to have_content('Secret') expect(page).to have_content('Trusted Y') @@ -28,7 +28,7 @@ RSpec.describe 'admin manage applications' do click_on 'Submit' expect(page).to have_content('test_changed') - expect(page).to have_content('Application Id') + expect(page).to have_content('Application ID') expect(page).to have_content('Secret') expect(page).to have_content('Trusted N') diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb index b99c5a7f4e3..0e296ab2109 100644 --- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb @@ -15,6 +15,7 @@ describe 'Dropdown hint', :js do before do project.add_maintainer(user) create(:issue, project: project) + create(:merge_request, source_project: project, target_project: project) end context 'when user not logged in' do @@ -224,4 +225,21 @@ describe 'Dropdown hint', :js do end end end + + context 'merge request page' do + before do + sign_in(user) + visit project_merge_requests_path(project) + filtered_search.click + end + + it 'shows the WIP menu item and opens the WIP options dropdown' do + click_hint('wip') + + expect(page).to have_css(js_dropdown_hint, visible: false) + expect(page).to have_css('#js-dropdown-wip', visible: true) + expect_tokens([{ name: 'wip' }]) + expect_filtered_search_input_empty + end + end end diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index cac8a5068ec..3b37ede8579 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -264,9 +264,9 @@ describe 'GitLab Markdown', :aggregate_failures do @project_wiki = @feat.project_wiki @project_wiki_page = @feat.project_wiki_page - file = Gollum::File.new(@project_wiki.wiki) - expect(file).to receive(:path).and_return('images/example.jpg') - expect(@project_wiki).to receive(:find_file).with('images/example.jpg').and_return(file) + path = 'images/example.jpg' + gitaly_wiki_file = Gitlab::GitalyClient::WikiFile.new(path: path) + expect(@project_wiki).to receive(:find_file).with(path).and_return(Gitlab::Git::WikiFile.new(gitaly_wiki_file)) allow(@project_wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' } @html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki, page_slug: @project_wiki_page.slug }) diff --git a/spec/features/merge_request/user_comments_on_diff_spec.rb b/spec/features/merge_request/user_comments_on_diff_spec.rb index 441b080bee5..00cf368e8c9 100644 --- a/spec/features/merge_request/user_comments_on_diff_spec.rb +++ b/spec/features/merge_request/user_comments_on_diff_spec.rb @@ -28,7 +28,7 @@ describe 'User comments on a diff', :js do click_button('Comment') end - page.within('.files > div:nth-child(3)') do + page.within('.diff-files-holder > div:nth-child(3)') do expect(page).to have_content('Line is wrong') find('.js-btn-vue-toggle-comments').click @@ -49,7 +49,7 @@ describe 'User comments on a diff', :js do wait_for_requests - page.within('.files > div:nth-child(2) .note-body > .note-text') do + page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do expect(page).to have_content('Line is correct') end @@ -63,7 +63,7 @@ describe 'User comments on a diff', :js do wait_for_requests # Hide the comment. - page.within('.files > div:nth-child(3)') do + page.within('.diff-files-holder > div:nth-child(3)') do find('.js-btn-vue-toggle-comments').click expect(page).not_to have_content('Line is wrong') @@ -71,21 +71,21 @@ describe 'User comments on a diff', :js do # At this moment a user should see only one comment. # The other one should be hidden. - page.within('.files > div:nth-child(2) .note-body > .note-text') do + page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do expect(page).to have_content('Line is correct') end # Show the comment. - page.within('.files > div:nth-child(3)') do + page.within('.diff-files-holder > div:nth-child(3)') do find('.js-btn-vue-toggle-comments').click end # Now both the comments should be shown. - page.within('.files > div:nth-child(3) .note-body > .note-text') do + page.within('.diff-files-holder > div:nth-child(3) .note-body > .note-text') do expect(page).to have_content('Line is wrong') end - page.within('.files > div:nth-child(2) .note-body > .note-text') do + page.within('.diff-files-holder > div:nth-child(2) .note-body > .note-text') do expect(page).to have_content('Line is correct') end @@ -95,11 +95,11 @@ describe 'User comments on a diff', :js do wait_for_requests - page.within('.files > div:nth-child(3) .parallel .note-body > .note-text') do + page.within('.diff-files-holder > div:nth-child(3) .parallel .note-body > .note-text') do expect(page).to have_content('Line is wrong') end - page.within('.files > div:nth-child(2) .parallel .note-body > .note-text') do + page.within('.diff-files-holder > div:nth-child(2) .parallel .note-body > .note-text') do expect(page).to have_content('Line is correct') end end diff --git a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb index 428eb414274..d3da8cc6752 100644 --- a/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb +++ b/spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb @@ -81,6 +81,8 @@ describe 'Merge request > User sees avatars on diff notes', :js do visit diffs_project_merge_request_path(project, merge_request, view: view) wait_for_requests + + find('.js-toggle-tree-list').click end it 'shows note avatar' do diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb index f42b4dcbb47..92db4f44098 100644 --- a/spec/features/merge_request/user_sees_versions_spec.rb +++ b/spec/features/merge_request/user_sees_versions_spec.rb @@ -110,7 +110,8 @@ describe 'Merge request > User sees versions', :js do diff_id: merge_request_diff3.id, start_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' ) - expect(page).to have_content '4 changed files with 15 additions and 6 deletions' + expect(page).to have_content '4 changed files' + expect(page).to have_content '15 additions 6 deletions' expect(page).to have_content 'Not all comments are displayed' position = Gitlab::Diff::Position.new( @@ -131,7 +132,8 @@ describe 'Merge request > User sees versions', :js do end it 'show diff between new and old version' do - expect(page).to have_content '4 changed files with 15 additions and 6 deletions' + expect(page).to have_content '4 changed files' + expect(page).to have_content '15 additions 6 deletions' end it 'returns to latest version when "Show latest version" button is clicked' do @@ -158,7 +160,7 @@ describe 'Merge request > User sees versions', :js do it 'has 0 chages between versions' do page.within '.mr-version-compare-dropdown' do - expect(find('.dropdown-toggle')).to have_content 'version 1' + expect(find('.dropdown-menu-toggle')).to have_content 'version 1' end page.within '.mr-version-dropdown' do @@ -179,7 +181,7 @@ describe 'Merge request > User sees versions', :js do it 'sets the compared versions to be the same' do page.within '.mr-version-compare-dropdown' do - expect(find('.dropdown-toggle')).to have_content 'version 2' + expect(find('.dropdown-menu-toggle')).to have_content 'version 2' end page.within '.mr-version-dropdown' do diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb index b1bfe9e5de3..7f95a1282f9 100644 --- a/spec/features/merge_request/user_views_diffs_spec.rb +++ b/spec/features/merge_request/user_views_diffs_spec.rb @@ -10,6 +10,8 @@ describe 'User views diffs', :js do visit(diffs_project_merge_request_path(project, merge_request)) wait_for_requests + + find('.js-toggle-tree-list').click end shared_examples 'unfold diffs' do diff --git a/spec/features/profiles/user_manages_applications_spec.rb b/spec/features/profiles/user_manages_applications_spec.rb index 387584fef62..34aaab240cc 100644 --- a/spec/features/profiles/user_manages_applications_spec.rb +++ b/spec/features/profiles/user_manages_applications_spec.rb @@ -16,7 +16,7 @@ describe 'User manages applications' do click_on 'Save application' expect(page).to have_content 'Application: test' - expect(page).to have_content 'Application Id' + expect(page).to have_content 'Application ID' expect(page).to have_content 'Secret' click_on 'Edit' @@ -26,7 +26,7 @@ describe 'User manages applications' do click_on 'Save application' expect(page).to have_content 'test_changed' - expect(page).to have_content 'Application Id' + expect(page).to have_content 'Application ID' expect(page).to have_content 'Secret' visit applications_profile_path diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb index 4c5dda29fee..70e0879dd81 100644 --- a/spec/features/projects/environments/environment_spec.rb +++ b/spec/features/projects/environments/environment_spec.rb @@ -60,7 +60,7 @@ describe 'Environment' do context 'with manual action' do let(:action) do create(:ci_build, :manual, pipeline: pipeline, - name: 'deploy to production') + name: 'deploy to production', environment: environment.name) end context 'when user has ability to trigger deployment' do @@ -73,12 +73,16 @@ describe 'Environment' do expect(page).to have_link(action.name.humanize) end - it 'does allow to play manual action' do + it 'does allow to play manual action', :js do expect(action).to be_manual + find('button.dropdown').click + expect { click_link(action.name.humanize) } .not_to change { Ci::Pipeline.count } + wait_for_all_requests + expect(page).to have_content(action.name) expect(action.reload).to be_pending end @@ -165,10 +169,10 @@ describe 'Environment' do name: action.ref, project: project) end - it 'allows to stop environment' do + it 'allows to stop environment', :js do click_button('Stop') click_button('Stop environment') # confirm modal - + wait_for_all_requests expect(page).to have_content('close_app') end end diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index a2b96514d64..f76f9ba7577 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -12,7 +12,7 @@ describe 'Import/Export - project export integration test', :js do let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } - let(:sensitive_words) { %w[pass secret token key] } + let(:sensitive_words) { %w[pass secret token key encrypted] } let(:safe_list) do { token: [ProjectHook, Ci::Trigger, CommitStatus], diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 2d791947ee9..fc7b78ac21f 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -16,7 +16,9 @@ describe 'User browses a job', :js do visit(project_job_path(project, build)) end - it 'erases the job log' do + it 'erases the job log', :js do + wait_for_requests + expect(page).to have_content("Job ##{build.id}") expect(page).to have_css('#build-trace') @@ -29,18 +31,17 @@ describe 'User browses a job', :js do expect(build.artifacts_file.exists?).to be_falsy expect(build.artifacts_metadata.exists?).to be_falsy - page.within('.erased') do - expect(page).to have_content('Job has been erased') - end + expect(page).to have_content('Job has been erased') end context 'with a failed job' do let!(:build) { create(:ci_build, :failed, :trace_artifact, pipeline: pipeline) } it 'displays the failure reason' do + wait_for_all_requests within('.builds-container') do build_link = first('.build-job > a') - expect(build_link['data-title']).to eq('test - failed - (unknown failure)') + expect(build_link['data-original-title']).to eq('test - failed - (unknown failure)') end end end @@ -49,9 +50,10 @@ describe 'User browses a job', :js do let!(:build) { create(:ci_build, :failed, :retried, :trace_artifact, pipeline: pipeline) } it 'displays the failure reason and retried label' do + wait_for_all_requests within('.builds-container') do build_link = first('.build-job > a') - expect(build_link['data-title']).to eq('test - failed - (unknown failure) (retried)') + expect(build_link['data-original-title']).to eq('test - failed - (unknown failure) (retried)') end end end diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 5b767c96910..45fc492f23f 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -134,23 +134,25 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do expect(page).to have_content pipeline.commit.title end - it 'shows active job' do + it 'shows active job', :js do visit project_job_path(project, job) + wait_for_requests expect(page).to have_selector('.build-job.active') end end - context 'sidebar' do + context 'sidebar', :js do let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') } before do visit project_job_path(project, job) + wait_for_requests end it 'renders escaped tooltip name' do page.within('aside.right-sidebar') do - expect(find('.active.build-job a')['data-title']).to eq('<img src="x"> - passed') + expect(find('.active.build-job a')['data-original-title']).to eq('<img src=x onerror=alert(document.domain)> - passed') end end end @@ -367,39 +369,167 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do end end - context 'when job starts environment' do - let(:environment) { create(:environment, project: project) } - let(:pipeline) { create(:ci_pipeline, project: project) } + context 'when job starts environment', :js do + let(:environment) { create(:environment, name: 'production', project: project) } - context 'job is successfull and has deployment' do - let(:deployment) { create(:deployment) } - let(:job) { create(:ci_build, :success, :trace_artifact, environment: environment.name, deployments: [deployment], pipeline: pipeline) } + context 'job is successful and has deployment' do + let(:build) { create(:ci_build, :success, :trace_live, environment: environment.name, pipeline: pipeline) } + let!(:deployment) { create(:deployment, environment: environment, project: environment.project, deployable: build) } - it 'shows a link for the job' do - visit project_job_path(project, job) + before do + visit project_job_path(project, build) + wait_for_requests + # scroll to the top of the page first + execute_script "window.scrollTo(0,0)" + end + it 'shows a link for the job' do expect(page).to have_link environment.name end + + it 'shows deployment message' do + expect(page).to have_content 'This job is the most recent deployment' + expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}") + end end context 'job is complete and not successful' do - let(:job) { create(:ci_build, :failed, :trace_artifact, environment: environment.name, pipeline: pipeline) } + let(:build) { create(:ci_build, :failed, :trace_artifact, environment: environment.name, pipeline: pipeline) } it 'shows a link for the job' do - visit project_job_path(project, job) + visit project_job_path(project, build) + wait_for_requests + # scroll to the top of the page first + execute_script "window.scrollTo(0,0)" expect(page).to have_link environment.name + expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}") end end - context 'job creates a new deployment' do - let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) } - let(:job) { create(:ci_build, :success, :trace_artifact, environment: environment.name, pipeline: pipeline) } + context 'deployment still not finished' do + let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) } it 'shows a link to latest deployment' do - visit project_job_path(project, job) + visit project_job_path(project, build) + wait_for_all_requests + # scroll to the top of the page first + execute_script "window.scrollTo(0,0)" + + expect(page).to have_link environment.name + expect(page).to have_content 'This job is creating a deployment' + expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}") + end + end + end + + describe 'environment info in job view', :js do + before do + visit project_job_path(project, job) + wait_for_requests + # scroll to the top of the page first + execute_script "window.scrollTo(0,0)" + end + + context 'job with outdated deployment' do + let(:job) { create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) } + let(:second_build) { create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) } + let(:environment) { create(:environment, name: 'staging', project: project) } + let!(:first_deployment) { create(:deployment, environment: environment, deployable: job) } + let!(:second_deployment) { create(:deployment, environment: environment, deployable: second_build) } + + it 'shows deployment message' do + expected_text = 'This job is an out-of-date deployment ' \ + "to staging. View the most recent deployment ##{second_deployment.iid}." + + expect(page).to have_css('.environment-information', text: expected_text) + end + + it 'renders a link to the most recent deployment' do + expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}") + expect(find('.js-job-deployment-link')['href']).to include(second_deployment.deployable.project.path, second_deployment.deployable_id.to_s) + end + end + + context 'job failed to deploy' do + let(:job) { create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline) } + let!(:environment) { create(:environment, name: 'staging', project: project) } + + it 'shows deployment message' do + expected_text = 'The deployment of this job to staging did not succeed.' + + expect(page).to have_css( + '.environment-information', text: expected_text) + end + end + + context 'job will deploy' do + let(:job) { create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline) } + + context 'when environment exists' do + let!(:environment) { create(:environment, name: 'staging', project: project) } + + it 'shows deployment message' do + expected_text = 'This job is creating a deployment to staging' + + expect(page).to have_css( + '.environment-information', text: expected_text) + expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}") + end + + context 'when it has deployment' do + let!(:deployment) { create(:deployment, environment: environment) } + + it 'shows that deployment will be overwritten' do + expected_text = 'This job is creating a deployment to staging' + + expect(page).to have_css( + '.environment-information', text: expected_text) + expect(page).to have_css( + '.environment-information', text: 'latest deployment') + expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}") + end + end + end + + context 'when environment does not exist' do + let!(:environment) { create(:environment, name: 'staging', project: project) } + + it 'shows deployment message' do + expected_text = 'This job is creating a deployment to staging' + + expect(page).to have_css( + '.environment-information', text: expected_text) + expect(page).not_to have_css( + '.environment-information', text: 'latest deployment') + expect(find('.js-environment-link')['href']).to match("environments/#{environment.id}") + end + end + end + + context 'job that failed to deploy and environment has not been created' do + let(:job) { create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline) } + let!(:environment) { create(:environment, name: 'staging', project: project) } + + it 'shows deployment message' do + expected_text = 'The deployment of this job to staging did not succeed' + + expect(page).to have_css( + '.environment-information', text: expected_text) + end + end + + context 'job that will deploy and environment has not been created' do + let(:job) { create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline) } + let!(:environment) { create(:environment, name: 'staging', project: project) } + + it 'shows deployment message' do + expected_text = 'This job is creating a deployment to staging' - expect(page).to have_link('latest deployment') + expect(page).to have_css( + '.environment-information', text: expected_text) + expect(page).not_to have_css( + '.environment-information', text: 'latest deployment') end end end diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb index 747406efc8b..9a4ce426e69 100644 --- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb @@ -83,12 +83,13 @@ describe 'User views a wiki page' do end it 'shows a file stored in a page' do - gollum_file_double = double('Gollum::File', - mime_type: 'image/jpeg', - name: 'images/image.jpg', - path: 'images/image.jpg', - raw_data: '') - wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double) + raw_file = Gitlab::GitalyClient::WikiFile.new( + mime_type: 'image/jpeg', + name: 'images/image.jpg', + path: 'images/image.jpg', + raw_data: '' + ) + wiki_file = Gitlab::Git::WikiFile.new(raw_file) allow(wiki_file).to receive(:mime_type).and_return('image/jpeg') allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file) diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 35d0eeda8f6..33d01697c75 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -16,12 +16,18 @@ describe MergeRequestsFinder do p end let(:project4) { create(:project, :public, group: subgroup) } + let(:project5) { create(:project, :public, group: subgroup) } + let(:project6) { create(:project, :public, group: subgroup) } let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) } let!(:merge_request2) { create(:merge_request, :conflict, author: user, source_project: project2, target_project: project1, state: 'closed') } - let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, state: 'locked') } - let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) } - let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4) } + let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, state: 'locked', title: 'thing WIP thing') } + let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3, title: 'WIP thing') } + let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4, title: '[WIP]') } + let!(:merge_request6) { create(:merge_request, :simple, author: user, source_project: project5, target_project: project5, title: 'WIP: thing') } + let!(:merge_request7) { create(:merge_request, :simple, author: user, source_project: project6, target_project: project6, title: 'wip thing') } + let!(:merge_request8) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project1, title: '[wip] thing') } + let!(:merge_request9) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2, title: 'wip: thing') } before do project1.add_maintainer(user) @@ -29,19 +35,21 @@ describe MergeRequestsFinder do project3.add_developer(user) project2.add_developer(user2) project4.add_developer(user) + project5.add_developer(user) + project6.add_developer(user) end describe "#execute" do it 'filters by scope' do params = { scope: 'authored', state: 'opened' } merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(3) + expect(merge_requests.size).to eq(7) end it 'filters by project' do params = { project_id: project1.id, scope: 'authored', state: 'opened' } merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(1) + expect(merge_requests.size).to eq(2) end it 'filters by group' do @@ -49,7 +57,7 @@ describe MergeRequestsFinder do merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(2) + expect(merge_requests.size).to eq(3) end it 'filters by group including subgroups', :nested_groups do @@ -57,13 +65,13 @@ describe MergeRequestsFinder do merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(3) + expect(merge_requests.size).to eq(6) end it 'filters by non_archived' do params = { non_archived: true } merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(4) + expect(merge_requests.size).to eq(8) end it 'filters by iid' do @@ -98,6 +106,36 @@ describe MergeRequestsFinder do expect(merge_requests).to contain_exactly(merge_request3) end + it 'filters by wip' do + params = { wip: 'yes' } + + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request4, merge_request5, merge_request6, merge_request7, merge_request8, merge_request9) + end + + it 'filters by not wip' do + params = { wip: 'no' } + + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3) + end + + it 'returns all items if no valid wip param exists' do + params = { wip: '' } + + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3, merge_request4, merge_request5, merge_request6, merge_request7, merge_request8, merge_request9) + end + + it 'adds wip to scalar params' do + scalar_params = described_class.scalar_params + + expect(scalar_params).to include(:wip, :assignee_id) + end + context 'filtering by group milestone' do let!(:group) { create(:group, :public) } let(:group_milestone) { create(:milestone, group: group) } @@ -207,7 +245,7 @@ describe MergeRequestsFinder do it 'returns the number of rows for the default state' do finder = described_class.new(user) - expect(finder.row_count).to eq(3) + expect(finder.row_count).to eq(7) end it 'returns the number of rows for a given state' do diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js index ee37821ad08..1245e3e099a 100644 --- a/spec/javascripts/boards/board_new_issue_spec.js +++ b/spec/javascripts/boards/board_new_issue_spec.js @@ -69,7 +69,6 @@ describe('Issue boards new issue form', () => { vm.$el.querySelector('.btn-success').click(); expect(vm.submit.calls.count()).toBe(1); - expect(vm.$refs['submit-button']).toBe(vm.$el.querySelector('.btn-success')); }) .then(done) .catch(done.fail); diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js index cf7d8df5405..a3a714678af 100644 --- a/spec/javascripts/diffs/components/app_spec.js +++ b/spec/javascripts/diffs/components/app_spec.js @@ -44,7 +44,8 @@ describe('diffs/components/app', () => { it('shows comments message, with commit', done => { vm.$store.state.diffs.commit = getDiffWithCommit().commit; - vm.$nextTick() + vm + .$nextTick() .then(() => { expect(vm.$el).toContainText('Only comments from the following commit are shown below'); expect(vm.$el).toContainElement('.blob-commit-info'); @@ -55,10 +56,14 @@ describe('diffs/components/app', () => { it('shows comments message, with old mergeRequestDiff', done => { vm.$store.state.diffs.mergeRequestDiff = { latest: false }; + vm.$store.state.diffs.targetBranch = 'master'; - vm.$nextTick() + vm + .$nextTick() .then(() => { - expect(vm.$el).toContainText("Not all comments are displayed because you're viewing an old version of the diff."); + expect(vm.$el).toContainText( + "Not all comments are displayed because you're viewing an old version of the diff.", + ); }) .then(done) .catch(done.fail); @@ -67,9 +72,12 @@ describe('diffs/components/app', () => { it('shows comments message, with startVersion', done => { vm.$store.state.diffs.startVersion = 'test'; - vm.$nextTick() + vm + .$nextTick() .then(() => { - expect(vm.$el).toContainText("Not all comments are displayed because you're comparing two versions of the diff."); + expect(vm.$el).toContainText( + "Not all comments are displayed because you're comparing two versions of the diff.", + ); }) .then(done) .catch(done.fail); diff --git a/spec/javascripts/diffs/components/changed_files_spec.js b/spec/javascripts/diffs/components/changed_files_spec.js deleted file mode 100644 index 7f21273a991..00000000000 --- a/spec/javascripts/diffs/components/changed_files_spec.js +++ /dev/null @@ -1,105 +0,0 @@ -import Vue from 'vue'; -import Vuex from 'vuex'; -import { mountComponentWithStore } from 'spec/helpers'; -import diffsModule from '~/diffs/store/modules'; -import changedFiles from '~/diffs/components/changed_files.vue'; - -describe('ChangedFiles', () => { - const Component = Vue.extend(changedFiles); - const store = new Vuex.Store({ - modules: { - diffs: diffsModule(), - }, - }); - - let vm; - - beforeEach(() => { - setFixtures(` - <div id="dummy-element"></div> - <div class="js-tabs-affix"></div> - `); - - const props = { - diffFiles: [ - { - addedLines: 10, - removedLines: 20, - blob: { - path: 'some/code.txt', - }, - filePath: 'some/code.txt', - }, - ], - }; - - vm = mountComponentWithStore(Component, { props, store }); - }); - - describe('with single file added', () => { - it('shows files changes', () => { - expect(vm.$el).toContainText('1 changed file'); - }); - - it('shows file additions and deletions', () => { - expect(vm.$el).toContainText('10 additions'); - expect(vm.$el).toContainText('20 deletions'); - }); - }); - - describe('diff view mode buttons', () => { - let inlineButton; - let parallelButton; - - beforeEach(() => { - inlineButton = vm.$el.querySelector('.js-inline-diff-button'); - parallelButton = vm.$el.querySelector('.js-parallel-diff-button'); - }); - - it('should have Inline and Side-by-side buttons', () => { - expect(inlineButton).toBeDefined(); - expect(parallelButton).toBeDefined(); - }); - - it('should add active class to Inline button', done => { - vm.$store.state.diffs.diffViewType = 'inline'; - - vm.$nextTick(() => { - expect(inlineButton.classList.contains('active')).toEqual(true); - expect(parallelButton.classList.contains('active')).toEqual(false); - - done(); - }); - }); - - it('should toggle active state of buttons when diff view type changed', done => { - vm.$store.state.diffs.diffViewType = 'parallel'; - - vm.$nextTick(() => { - expect(inlineButton.classList.contains('active')).toEqual(false); - expect(parallelButton.classList.contains('active')).toEqual(true); - - done(); - }); - }); - - describe('clicking them', () => { - it('should toggle the diff view type', done => { - parallelButton.click(); - - vm.$nextTick(() => { - expect(inlineButton.classList.contains('active')).toEqual(false); - expect(parallelButton.classList.contains('active')).toEqual(true); - - inlineButton.click(); - - vm.$nextTick(() => { - expect(inlineButton.classList.contains('active')).toEqual(true); - expect(parallelButton.classList.contains('active')).toEqual(false); - done(); - }); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/diffs/components/file_row_stats_spec.js b/spec/javascripts/diffs/components/file_row_stats_spec.js new file mode 100644 index 00000000000..a8a7f3f1d82 --- /dev/null +++ b/spec/javascripts/diffs/components/file_row_stats_spec.js @@ -0,0 +1,33 @@ +import Vue from 'vue'; +import FileRowStats from '~/diffs/components/file_row_stats.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +describe('Diff file row stats', () => { + let Component; + let vm; + + beforeAll(() => { + Component = Vue.extend(FileRowStats); + }); + + beforeEach(() => { + vm = mountComponent(Component, { + file: { + addedLines: 20, + removedLines: 10, + }, + }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders added lines count', () => { + expect(vm.$el.querySelector('.cgreen').textContent).toContain('+20'); + }); + + it('renders removed lines count', () => { + expect(vm.$el.querySelector('.cred').textContent).toContain('-10'); + }); +}); diff --git a/spec/javascripts/diffs/components/tree_list_spec.js b/spec/javascripts/diffs/components/tree_list_spec.js new file mode 100644 index 00000000000..08e25d2004e --- /dev/null +++ b/spec/javascripts/diffs/components/tree_list_spec.js @@ -0,0 +1,120 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import TreeList from '~/diffs/components/tree_list.vue'; +import createStore from '~/diffs/store/modules'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; + +describe('Diffs tree list component', () => { + let Component; + let vm; + + beforeAll(() => { + Component = Vue.extend(TreeList); + }); + + beforeEach(() => { + Vue.use(Vuex); + + const store = new Vuex.Store({ + modules: { + diffs: createStore(), + }, + }); + + // Setup initial state + store.state.diffs.addedLines = 10; + store.state.diffs.removedLines = 20; + store.state.diffs.diffFiles.push('test'); + + vm = mountComponentWithStore(Component, { store }); + }); + + afterEach(() => { + vm.$destroy(); + }); + + it('renders diff stats', () => { + expect(vm.$el.textContent).toContain('1 changed file'); + expect(vm.$el.textContent).toContain('10 additions'); + expect(vm.$el.textContent).toContain('20 deletions'); + }); + + it('renders empty text', () => { + expect(vm.$el.textContent).toContain('No files found'); + }); + + describe('with files', () => { + beforeEach(done => { + Object.assign(vm.$store.state.diffs.treeEntries, { + 'index.js': { + addedLines: 0, + changed: true, + deleted: false, + fileHash: 'test', + key: 'index.js', + name: 'index.js', + path: 'index.js', + removedLines: 0, + tempFile: true, + type: 'blob', + }, + app: { + key: 'app', + path: 'app', + name: 'app', + type: 'tree', + tree: [], + }, + }); + vm.$store.state.diffs.tree = [ + vm.$store.state.diffs.treeEntries['index.js'], + vm.$store.state.diffs.treeEntries.app, + ]; + + vm.$nextTick(done); + }); + + it('renders tree', () => { + expect(vm.$el.querySelectorAll('.file-row').length).toBe(2); + expect(vm.$el.querySelectorAll('.file-row')[0].textContent).toContain('index.js'); + expect(vm.$el.querySelectorAll('.file-row')[1].textContent).toContain('app'); + }); + + it('filters tree list to blobs matching search', done => { + vm.search = 'index'; + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.file-row').length).toBe(1); + expect(vm.$el.querySelectorAll('.file-row')[0].textContent).toContain('index.js'); + + done(); + }); + }); + + it('calls toggleTreeOpen when clicking folder', () => { + spyOn(vm.$store, 'dispatch').and.stub(); + + vm.$el.querySelectorAll('.file-row')[1].click(); + + expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/toggleTreeOpen', 'app'); + }); + + it('calls scrollToFile when clicking blob', () => { + spyOn(vm.$store, 'dispatch').and.stub(); + + vm.$el.querySelector('.file-row').click(); + + expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/scrollToFile', 'index.js'); + }); + }); + + describe('clearSearch', () => { + it('resets search', () => { + vm.search = 'test'; + + vm.$el.querySelector('.tree-list-clear-icon').click(); + + expect(vm.search).toBe(''); + }); + }); +}); diff --git a/spec/javascripts/diffs/mock_data/diff_file.js b/spec/javascripts/diffs/mock_data/diff_file.js index 372b8f066cf..2aa2f8f3528 100644 --- a/spec/javascripts/diffs/mock_data/diff_file.js +++ b/spec/javascripts/diffs/mock_data/diff_file.js @@ -33,7 +33,7 @@ export default { contentSha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13', storedExternally: null, externalStorage: null, - oldPathHtml: ['CHANGELOG', 'CHANGELOG'], + oldPathHtml: 'CHANGELOG', newPathHtml: 'CHANGELOG', editPath: '/gitlab-org/gitlab-test/edit/spooky-stuff/CHANGELOG', viewPath: '/gitlab-org/gitlab-test/blob/spooky-stuff/CHANGELOG', diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js index 05b39bad6ea..aacad7a479b 100644 --- a/spec/javascripts/diffs/store/actions_spec.js +++ b/spec/javascripts/diffs/store/actions_spec.js @@ -22,6 +22,9 @@ import actions, { expandAllFiles, toggleFileDiscussions, saveDiffDiscussion, + toggleTreeOpen, + scrollToFile, + toggleShowTreeList, } from '~/diffs/store/actions'; import * as types from '~/diffs/store/mutation_types'; import { reduceDiscussionsToLineCodes } from '~/notes/stores/utils'; @@ -608,4 +611,88 @@ describe('DiffsStoreActions', () => { .catch(done.fail); }); }); + + describe('toggleTreeOpen', () => { + it('commits TOGGLE_FOLDER_OPEN', done => { + testAction( + toggleTreeOpen, + 'path', + {}, + [{ type: types.TOGGLE_FOLDER_OPEN, payload: 'path' }], + [], + done, + ); + }); + }); + + describe('scrollToFile', () => { + let commit; + + beforeEach(() => { + commit = jasmine.createSpy(); + jasmine.clock().install(); + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); + + it('updates location hash', () => { + const state = { + treeEntries: { + path: { + fileHash: 'test', + }, + }, + }; + + scrollToFile({ state, commit }, 'path'); + + expect(document.location.hash).toBe('#test'); + }); + + it('commits UPDATE_CURRENT_DIFF_FILE_ID', () => { + const state = { + treeEntries: { + path: { + fileHash: 'test', + }, + }, + }; + + scrollToFile({ state, commit }, 'path'); + + expect(commit).toHaveBeenCalledWith(types.UPDATE_CURRENT_DIFF_FILE_ID, 'test'); + }); + + it('resets currentDiffId after timeout', () => { + const state = { + treeEntries: { + path: { + fileHash: 'test', + }, + }, + }; + + scrollToFile({ state, commit }, 'path'); + + jasmine.clock().tick(1000); + + expect(commit.calls.argsFor(1)).toEqual([types.UPDATE_CURRENT_DIFF_FILE_ID, '']); + }); + }); + + describe('toggleShowTreeList', () => { + it('commits toggle', done => { + testAction(toggleShowTreeList, null, {}, [{ type: types.TOGGLE_SHOW_TREE_LIST }], [], done); + }); + + it('updates localStorage', () => { + spyOn(localStorage, 'setItem'); + + toggleShowTreeList({ commit() {}, state: { showTreeList: true } }); + + expect(localStorage.setItem).toHaveBeenCalledWith('mr_tree_show', true); + }); + }); }); diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js index 4747e437c4e..cfeaaec6980 100644 --- a/spec/javascripts/diffs/store/getters_spec.js +++ b/spec/javascripts/diffs/store/getters_spec.js @@ -291,4 +291,31 @@ describe('Diffs Module Getters', () => { expect(getters.getDiffFileByHash(localState)('123')).toBeUndefined(); }); }); + + describe('allBlobs', () => { + it('returns an array of blobs', () => { + localState.treeEntries = { + file: { + type: 'blob', + }, + tree: { + type: 'tree', + }, + }; + + expect(getters.allBlobs(localState)).toEqual([ + { + type: 'blob', + }, + ]); + }); + }); + + describe('diffFilesLength', () => { + it('returns length of diff files', () => { + localState.diffFiles.push('test', 'test 2'); + + expect(getters.diffFilesLength(localState)).toBe(2); + }); + }); }); diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js index 9a5d8dfbd15..cc8d5dc4bac 100644 --- a/spec/javascripts/diffs/store/mutations_spec.js +++ b/spec/javascripts/diffs/store/mutations_spec.js @@ -1,3 +1,4 @@ +import createState from '~/diffs/store/modules/diff_state'; import mutations from '~/diffs/store/mutations'; import * as types from '~/diffs/store/mutation_types'; import { INLINE_DIFF_VIEW_TYPE } from '~/diffs/constants'; @@ -356,4 +357,44 @@ describe('DiffsStoreMutations', () => { expect(state.diffFiles[0].highlightedDiffLines[0].discussions.length).toEqual(0); }); }); + + describe('TOGGLE_FOLDER_OPEN', () => { + it('toggles entry opened prop', () => { + const state = { + treeEntries: { + path: { + opened: false, + }, + }, + }; + + mutations[types.TOGGLE_FOLDER_OPEN](state, 'path'); + + expect(state.treeEntries.path.opened).toBe(true); + }); + }); + + describe('TOGGLE_SHOW_TREE_LIST', () => { + it('toggles showTreeList', () => { + const state = createState(); + + mutations[types.TOGGLE_SHOW_TREE_LIST](state); + + expect(state.showTreeList).toBe(false, 'Failed to toggle showTreeList to false'); + + mutations[types.TOGGLE_SHOW_TREE_LIST](state); + + expect(state.showTreeList).toBe(true, 'Failed to toggle showTreeList to true'); + }); + }); + + describe('UPDATE_CURRENT_DIFF_FILE_ID', () => { + it('updates currentDiffFileId', () => { + const state = createState(); + + mutations[types.UPDATE_CURRENT_DIFF_FILE_ID](state, 'somefileid'); + + expect(state.currentDiffFileId).toBe('somefileid'); + }); + }); }); diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js index 897cd1483aa..e660f94c72e 100644 --- a/spec/javascripts/diffs/store/utils_spec.js +++ b/spec/javascripts/diffs/store/utils_spec.js @@ -421,4 +421,113 @@ describe('DiffsStoreUtils', () => { ).toBe(false); }); }); + + describe('generateTreeList', () => { + let files; + + beforeAll(() => { + files = [ + { + newPath: 'app/index.js', + deletedFile: false, + newFile: false, + removedLines: 10, + addedLines: 0, + fileHash: 'test', + }, + { + newPath: 'app/test/index.js', + deletedFile: false, + newFile: true, + removedLines: 0, + addedLines: 0, + fileHash: 'test', + }, + { + newPath: 'package.json', + deletedFile: true, + newFile: false, + removedLines: 0, + addedLines: 0, + fileHash: 'test', + }, + ]; + }); + + it('creates a tree of files', () => { + const { tree } = utils.generateTreeList(files); + + expect(tree).toEqual([ + { + key: 'app', + path: 'app', + name: 'app', + type: 'tree', + tree: [ + { + addedLines: 0, + changed: true, + deleted: false, + fileHash: 'test', + key: 'app/index.js', + name: 'index.js', + path: 'app/index.js', + removedLines: 10, + tempFile: false, + type: 'blob', + tree: [], + }, + { + key: 'app/test', + path: 'app/test', + name: 'test', + type: 'tree', + opened: true, + tree: [ + { + addedLines: 0, + changed: true, + deleted: false, + fileHash: 'test', + key: 'app/test/index.js', + name: 'index.js', + path: 'app/test/index.js', + removedLines: 0, + tempFile: true, + type: 'blob', + tree: [], + }, + ], + }, + ], + opened: true, + }, + { + key: 'package.json', + path: 'package.json', + name: 'package.json', + type: 'blob', + changed: true, + tempFile: false, + deleted: true, + fileHash: 'test', + addedLines: 0, + removedLines: 0, + tree: [], + }, + ]); + }); + + it('creates flat list of blobs & folders', () => { + const { treeEntries } = utils.generateTreeList(files); + + expect(Object.keys(treeEntries)).toEqual([ + 'app', + 'app/index.js', + 'app/test', + 'app/test/index.js', + 'package.json', + ]); + }); + }); }); diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js index 8792e99d461..68bbbf838da 100644 --- a/spec/javascripts/filtered_search/dropdown_utils_spec.js +++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js @@ -288,13 +288,13 @@ describe('Dropdown Utils', () => { describe('setDataValueIfSelected', () => { beforeEach(() => { - spyOn(FilteredSearchDropdownManager, 'addWordToInput') - .and.callFake(() => {}); + spyOn(FilteredSearchDropdownManager, 'addWordToInput').and.callFake(() => {}); }); it('calls addWordToInput when dataValue exists', () => { const selected = { getAttribute: () => 'value', + hasAttribute: () => false, }; DropdownUtils.setDataValueIfSelected(null, selected); @@ -304,6 +304,7 @@ describe('Dropdown Utils', () => { it('returns true when dataValue exists', () => { const selected = { getAttribute: () => 'value', + hasAttribute: () => false, }; const result = DropdownUtils.setDataValueIfSelected(null, selected); diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js index 756a654765b..53a6d1d62b0 100644 --- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js @@ -240,13 +240,17 @@ describe('Filtered Search Visual Tokens', () => { beforeEach(() => { setFixtures(` <div class="test-area"> - ${subject.createVisualTokenElementHTML()} + ${subject.createVisualTokenElementHTML('custom-token')} </div> `); tokenElement = document.querySelector('.test-area').firstElementChild; }); + it('should add class name to token element', () => { + expect(document.querySelector('.test-area .custom-token')).toBeDefined(); + }); + it('contains name div', () => { expect(tokenElement.querySelector('.name')).toEqual(jasmine.anything()); }); @@ -280,7 +284,7 @@ describe('Filtered Search Visual Tokens', () => { describe('addVisualTokenElement', () => { it('renders search visual tokens', () => { - subject.addVisualTokenElement('search term', null, true); + subject.addVisualTokenElement('search term', null, { isSearchTerm: true }); const token = tokensContainer.querySelector('.js-visual-token'); expect(token.classList.contains('filtered-search-term')).toEqual(true); diff --git a/spec/javascripts/ide/components/file_row_extra_spec.js b/spec/javascripts/ide/components/file_row_extra_spec.js index 60dabe28045..c93a939ad71 100644 --- a/spec/javascripts/ide/components/file_row_extra_spec.js +++ b/spec/javascripts/ide/components/file_row_extra_spec.js @@ -107,14 +107,14 @@ describe('IDE extra file row component', () => { describe('changes file icon', () => { it('hides when file is not changed', () => { - expect(vm.$el.querySelector('.ide-file-changed-icon')).toBe(null); + expect(vm.$el.querySelector('.file-changed-icon')).toBe(null); }); it('shows when file is changed', done => { vm.file.changed = true; vm.$nextTick(() => { - expect(vm.$el.querySelector('.ide-file-changed-icon')).not.toBe(null); + expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null); done(); }); @@ -124,7 +124,7 @@ describe('IDE extra file row component', () => { vm.file.staged = true; vm.$nextTick(() => { - expect(vm.$el.querySelector('.ide-file-changed-icon')).not.toBe(null); + expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null); done(); }); @@ -134,7 +134,7 @@ describe('IDE extra file row component', () => { vm.file.tempFile = true; vm.$nextTick(() => { - expect(vm.$el.querySelector('.ide-file-changed-icon')).not.toBe(null); + expect(vm.$el.querySelector('.file-changed-icon')).not.toBe(null); done(); }); diff --git a/spec/javascripts/ide/components/repo_tab_spec.js b/spec/javascripts/ide/components/repo_tab_spec.js index 278a0753322..3b52f279bf2 100644 --- a/spec/javascripts/ide/components/repo_tab_spec.js +++ b/spec/javascripts/ide/components/repo_tab_spec.js @@ -93,13 +93,13 @@ describe('RepoTab', () => { Vue.nextTick() .then(() => { - expect(vm.$el.querySelector('.ide-file-modified')).toBeNull(); + expect(vm.$el.querySelector('.file-modified')).toBeNull(); vm.$el.dispatchEvent(new Event('mouseout')); }) .then(Vue.nextTick) .then(() => { - expect(vm.$el.querySelector('.ide-file-modified')).not.toBeNull(); + expect(vm.$el.querySelector('.file-modified')).not.toBeNull(); done(); }) diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js index 2fcb5566ebc..d6b5dec9e47 100644 --- a/spec/javascripts/job_spec.js +++ b/spec/javascripts/job_spec.js @@ -57,25 +57,6 @@ describe('Job', () => { expect(job.buildStage).toBe('test'); expect(job.state).toBe(''); }); - - it('only shows the jobs matching the current stage', () => { - expect($('.build-job[data-stage="build"]').is(':visible')).toBe(false); - expect($('.build-job[data-stage="test"]').is(':visible')).toBe(true); - expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false); - }); - - it('selects the current stage in the build dropdown menu', () => { - expect($('.stage-selection').text()).toBe('test'); - }); - - it('updates the jobs when the build dropdown changes', () => { - $('.stage-item:contains("build")').click(); - - expect($('.stage-selection').text()).toBe('build'); - expect($('.build-job[data-stage="build"]').is(':visible')).toBe(true); - expect($('.build-job[data-stage="test"]').is(':visible')).toBe(false); - expect($('.build-job[data-stage="deploy"]').is(':visible')).toBe(false); - }); }); describe('running build', () => { diff --git a/spec/javascripts/jobs/components/environments_block_spec.js b/spec/javascripts/jobs/components/environments_block_spec.js index 015c26be9fc..7d836129b13 100644 --- a/spec/javascripts/jobs/components/environments_block_spec.js +++ b/spec/javascripts/jobs/components/environments_block_spec.js @@ -5,19 +5,16 @@ import mountComponent from '../../helpers/vue_mount_component_helper'; describe('Environments block', () => { const Component = Vue.extend(component); let vm; - const icon = { + const status = { group: 'success', icon: 'status_success', label: 'passed', text: 'passed', tooltip: 'passed', }; - const deployment = { - path: 'deployment', - name: 'deployment name', - }; + const environment = { - path: '/environment', + environment_path: '/environment', name: 'environment', }; @@ -25,15 +22,14 @@ describe('Environments block', () => { vm.$destroy(); }); - describe('with latest deployment', () => { + describe('with last deployment', () => { it('renders info for most recent deployment', () => { vm = mountComponent(Component, { deploymentStatus: { - status: 'latest', - icon, - deployment, + status: 'last', environment, }, + iconStatus: status, }); expect(vm.$el.textContent.trim()).toEqual( @@ -48,17 +44,17 @@ describe('Environments block', () => { vm = mountComponent(Component, { deploymentStatus: { status: 'out_of_date', - icon, - deployment, environment: Object.assign({}, environment, { - last_deployment: { name: 'deployment', path: 'last_deployment' }, + last_deployment: { iid: 'deployment', deployable: { build_path: 'bar' } }, }), }, + iconStatus: status, }); expect(vm.$el.textContent.trim()).toEqual( - 'This job is an out-of-date deployment to environment. View the most recent deployment deployment.', + 'This job is an out-of-date deployment to environment. View the most recent deployment #deployment.', ); + expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('bar'); }); }); @@ -67,10 +63,9 @@ describe('Environments block', () => { vm = mountComponent(Component, { deploymentStatus: { status: 'out_of_date', - icon, - deployment: null, environment, }, + iconStatus: status, }); expect(vm.$el.textContent.trim()).toEqual( @@ -85,10 +80,9 @@ describe('Environments block', () => { vm = mountComponent(Component, { deploymentStatus: { status: 'failed', - icon, - deployment: null, environment, }, + iconStatus: status, }); expect(vm.$el.textContent.trim()).toEqual( @@ -99,21 +93,24 @@ describe('Environments block', () => { describe('creating deployment', () => { describe('with last deployment', () => { - it('renders info about creating deployment and overriding lastest deployment', () => { + it('renders info about creating deployment and overriding latest deployment', () => { vm = mountComponent(Component, { deploymentStatus: { status: 'creating', - icon, - deployment, environment: Object.assign({}, environment, { - last_deployment: { name: 'deployment', path: 'last_deployment' }, + last_deployment: { + iid: 'deployment', + deployable: { build_path: 'foo' }, + }, }), }, + iconStatus: status, }); expect(vm.$el.textContent.trim()).toEqual( - 'This job is creating a deployment to environment and will overwrite the last deployment.', + 'This job is creating a deployment to environment and will overwrite the latest deployment.', ); + expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('foo'); }); }); @@ -122,10 +119,9 @@ describe('Environments block', () => { vm = mountComponent(Component, { deploymentStatus: { status: 'creating', - icon, - deployment: null, environment, }, + iconStatus: status, }); expect(vm.$el.textContent.trim()).toEqual( @@ -133,5 +129,18 @@ describe('Environments block', () => { ); }); }); + + describe('without environment', () => { + it('does not render environment link', () => { + vm = mountComponent(Component, { + deploymentStatus: { + status: 'creating', + environment: null, + }, + iconStatus: status, + }); + expect(vm.$el.querySelector('.js-environment-link')).toBeNull(); + }); + }); }); }); diff --git a/spec/javascripts/jobs/components/header_spec.js b/spec/javascripts/jobs/components/header_spec.js deleted file mode 100644 index e21e2c6d6e3..00000000000 --- a/spec/javascripts/jobs/components/header_spec.js +++ /dev/null @@ -1,98 +0,0 @@ -import Vue from 'vue'; -import headerComponent from '~/jobs/components/header.vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; - -describe('Job details header', () => { - let HeaderComponent; - let vm; - let props; - - beforeEach(() => { - HeaderComponent = Vue.extend(headerComponent); - - const threeWeeksAgo = new Date(); - threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21); - - const twoDaysAgo = new Date(); - twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); - - props = { - job: { - status: { - group: 'failed', - icon: 'status_failed', - label: 'failed', - text: 'failed', - details_path: 'path', - }, - id: 123, - created_at: threeWeeksAgo.toISOString(), - user: { - web_url: 'path', - name: 'Foo', - username: 'foobar', - email: 'foo@bar.com', - avatar_url: 'link', - }, - started: twoDaysAgo.toISOString(), - new_issue_path: 'path', - }, - isLoading: false, - }; - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('job reason', () => { - it('should not render the reason when reason is absent', () => { - vm = mountComponent(HeaderComponent, props); - - expect(vm.shouldRenderReason).toBe(false); - }); - - it('should render the reason when reason is present', () => { - props.job.callout_message = 'There is an unknown failure, please try again'; - - vm = mountComponent(HeaderComponent, props); - - expect(vm.shouldRenderReason).toBe(true); - }); - }); - - describe('triggered job', () => { - beforeEach(() => { - vm = mountComponent(HeaderComponent, props); - }); - - it('should render provided job information', () => { - expect( - vm.$el - .querySelector('.header-main-content') - .textContent.replace(/\s+/g, ' ') - .trim(), - ).toEqual('failed Job #123 triggered 2 days ago by Foo'); - }); - - it('should render new issue link', () => { - expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual( - props.job.new_issue_path, - ); - }); - }); - - describe('created job', () => { - it('should render created key', () => { - props.job.started = false; - vm = mountComponent(HeaderComponent, props); - - expect( - vm.$el - .querySelector('.header-main-content') - .textContent.replace(/\s+/g, ' ') - .trim(), - ).toEqual('failed Job #123 created 3 weeks ago by Foo'); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js new file mode 100644 index 00000000000..c31fa6f9887 --- /dev/null +++ b/spec/javascripts/jobs/components/job_app_spec.js @@ -0,0 +1,185 @@ +import Vue from 'vue'; +import jobApp from '~/jobs/components/job_app.vue'; +import createStore from '~/jobs/store'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; + +describe('Job App ', () => { + const Component = Vue.extend(jobApp); + let store; + let vm; + + const threeWeeksAgo = new Date(); + threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21); + + const twoDaysAgo = new Date(); + twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); + + const job = { + status: { + group: 'failed', + icon: 'status_failed', + label: 'failed', + text: 'failed', + details_path: 'path', + }, + id: 123, + created_at: threeWeeksAgo.toISOString(), + user: { + web_url: 'path', + name: 'Foo', + username: 'foobar', + email: 'foo@bar.com', + avatar_url: 'link', + }, + started: twoDaysAgo.toISOString(), + new_issue_path: 'path', + runners: { + available: false, + }, + tags: ['docker'], + }; + + const props = { + runnerHelpUrl: 'help/runners', + }; + + beforeEach(() => { + store = createStore(); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('Header section', () => { + describe('job callout message', () => { + it('should not render the reason when reason is absent', () => { + store.dispatch('receiveJobSuccess', job); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + expect(vm.shouldRenderCalloutMessage).toBe(false); + }); + + it('should render the reason when reason is present', () => { + store.dispatch( + 'receiveJobSuccess', + Object.assign({}, job, { + callout_message: 'There is an unknown failure, please try again', + }), + ); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + expect(vm.shouldRenderCalloutMessage).toBe(true); + }); + }); + + describe('triggered job', () => { + beforeEach(() => { + store.dispatch('receiveJobSuccess', job); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + }); + + it('should render provided job information', () => { + expect( + vm.$el + .querySelector('.header-main-content') + .textContent.replace(/\s+/g, ' ') + .trim(), + ).toEqual('failed Job #123 triggered 2 days ago by Foo'); + }); + + it('should render new issue link', () => { + expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual( + job.new_issue_path, + ); + }); + }); + + describe('created job', () => { + it('should render created key', () => { + store.dispatch('receiveJobSuccess', Object.assign({}, job, { started: false })); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + expect( + vm.$el + .querySelector('.header-main-content') + .textContent.replace(/\s+/g, ' ') + .trim(), + ).toEqual('failed Job #123 created 3 weeks ago by Foo'); + }); + }); + }); + + describe('stuck block', () => { + it('renders stuck block when there are no runners', () => { + store.dispatch( + 'receiveJobSuccess', + Object.assign({}, job, { + status: { + group: 'pending', + icon: 'status_pending', + label: 'pending', + text: 'pending', + details_path: 'path', + }, + }), + ); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + expect(vm.$el.querySelector('.js-job-stuck')).not.toBeNull(); + }); + + it('renders tags in stuck block when there are no runners', () => { + store.dispatch( + 'receiveJobSuccess', + Object.assign({}, job, { + status: { + group: 'pending', + icon: 'status_pending', + label: 'pending', + text: 'pending', + details_path: 'path', + }, + }), + ); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(job.tags[0]); + }); + + it(' does not renders stuck block when there are no runners', () => { + store.dispatch('receiveJobSuccess', Object.assign({}, job, { runners: { available: true } })); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + expect(vm.$el.querySelector('.js-job-stuck')).toBeNull(); + }); + }); +}); diff --git a/spec/javascripts/jobs/components/jobs_container_spec.js b/spec/javascripts/jobs/components/jobs_container_spec.js index f3f8ff0d031..fa3a2c4c266 100644 --- a/spec/javascripts/jobs/components/jobs_container_spec.js +++ b/spec/javascripts/jobs/components/jobs_container_spec.js @@ -2,7 +2,7 @@ import Vue from 'vue'; import component from '~/jobs/components/jobs_container.vue'; import mountComponent from '../../helpers/vue_mount_component_helper'; -describe('Artifacts block', () => { +describe('Jobs List block', () => { const Component = Vue.extend(component); let vm; @@ -16,8 +16,7 @@ describe('Artifacts block', () => { text: 'passed', tooltip: 'passed', }, - path: 'job/233432756', - id: '233432756', + id: 233432756, tooltip: 'build - passed', retried: true, }; @@ -33,8 +32,7 @@ describe('Artifacts block', () => { text: 'passed', tooltip: 'passed', }, - path: 'job/2322756', - id: '2322756', + id: 2322756, tooltip: 'build - passed', active: true, }; @@ -50,8 +48,7 @@ describe('Artifacts block', () => { text: 'passed', tooltip: 'passed', }, - path: 'job/232153', - id: '232153', + id: 232153, tooltip: 'build - passed', }; @@ -62,14 +59,16 @@ describe('Artifacts block', () => { it('renders list of jobs', () => { vm = mountComponent(Component, { jobs: [job, retried, active], + jobId: 12313, }); expect(vm.$el.querySelectorAll('a').length).toEqual(3); }); - it('renders arrow right when job is active', () => { + it('renders arrow right when job id matches `jobId`', () => { vm = mountComponent(Component, { jobs: [active], + jobId: active.id, }); expect(vm.$el.querySelector('a .js-arrow-right')).not.toBeNull(); @@ -78,6 +77,7 @@ describe('Artifacts block', () => { it('does not render arrow right when job is not active', () => { vm = mountComponent(Component, { jobs: [job], + jobId: active.id, }); expect(vm.$el.querySelector('a .js-arrow-right')).toBeNull(); @@ -86,6 +86,7 @@ describe('Artifacts block', () => { it('renders job name when present', () => { vm = mountComponent(Component, { jobs: [job], + jobId: active.id, }); expect(vm.$el.querySelector('a').textContent.trim()).toContain(job.name); @@ -95,6 +96,7 @@ describe('Artifacts block', () => { it('renders job id when job name is not available', () => { vm = mountComponent(Component, { jobs: [retried], + jobId: active.id, }); expect(vm.$el.querySelector('a').textContent.trim()).toContain(retried.id); @@ -103,14 +105,16 @@ describe('Artifacts block', () => { it('links to the job page', () => { vm = mountComponent(Component, { jobs: [job], + jobId: active.id, }); - expect(vm.$el.querySelector('a').getAttribute('href')).toEqual(job.path); + expect(vm.$el.querySelector('a').getAttribute('href')).toEqual(job.status.details_path); }); it('renders retry icon when job was retried', () => { vm = mountComponent(Component, { jobs: [retried], + jobId: active.id, }); expect(vm.$el.querySelector('.js-retry-icon')).not.toBeNull(); @@ -119,6 +123,7 @@ describe('Artifacts block', () => { it('does not render retry icon when job was not retried', () => { vm = mountComponent(Component, { jobs: [job], + jobId: active.id, }); expect(vm.$el.querySelector('.js-retry-icon')).toBeNull(); diff --git a/spec/javascripts/jobs/components/sidebar_details_block_spec.js b/spec/javascripts/jobs/components/sidebar_details_block_spec.js deleted file mode 100644 index ba19534dac2..00000000000 --- a/spec/javascripts/jobs/components/sidebar_details_block_spec.js +++ /dev/null @@ -1,139 +0,0 @@ -import Vue from 'vue'; -import sidebarDetailsBlock from '~/jobs/components/sidebar_details_block.vue'; -import job from '../mock_data'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('Sidebar details block', () => { - let SidebarComponent; - let vm; - - function trimWhitespace(element) { - return element.textContent.replace(/\s+/g, ' ').trim(); - } - - beforeEach(() => { - SidebarComponent = Vue.extend(sidebarDetailsBlock); - }); - - afterEach(() => { - vm.$destroy(); - }); - - describe('when it is loading', () => { - it('should render a loading spinner', () => { - vm = mountComponent(SidebarComponent, { - job: {}, - isLoading: true, - }); - expect(vm.$el.querySelector('.fa-spinner')).toBeDefined(); - }); - }); - - describe('when there is no retry path retry', () => { - it('should not render a retry button', () => { - vm = mountComponent(SidebarComponent, { - job: {}, - isLoading: false, - }); - - expect(vm.$el.querySelector('.js-retry-job')).toBeNull(); - }); - }); - - describe('without terminal path', () => { - it('does not render terminal link', () => { - vm = mountComponent(SidebarComponent, { - job, - isLoading: false, - }); - - expect(vm.$el.querySelector('.js-terminal-link')).toBeNull(); - }); - }); - - describe('with terminal path', () => { - it('renders terminal link', () => { - vm = mountComponent(SidebarComponent, { - job, - isLoading: false, - terminalPath: 'job/43123/terminal', - }); - - expect(vm.$el.querySelector('.js-terminal-link')).not.toBeNull(); - }); - }); - - beforeEach(() => { - vm = mountComponent(SidebarComponent, { - job, - isLoading: false, - }); - }); - - describe('actions', () => { - it('should render link to new issue', () => { - expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual( - job.new_issue_path, - ); - expect(vm.$el.querySelector('.js-new-issue').textContent.trim()).toEqual('New issue'); - }); - - it('should render link to retry job', () => { - expect(vm.$el.querySelector('.js-retry-job').getAttribute('href')).toEqual(job.retry_path); - }); - - it('should render link to cancel job', () => { - expect(vm.$el.querySelector('.js-cancel-job').getAttribute('href')).toEqual(job.cancel_path); - }); - }); - - describe('information', () => { - it('should render merge request link', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-mr'))).toEqual('Merge Request: !2'); - - expect(vm.$el.querySelector('.js-job-mr a').getAttribute('href')).toEqual( - job.merge_request.path, - ); - }); - - it('should render job duration', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-duration'))).toEqual( - 'Duration: 6 seconds', - ); - }); - - it('should render erased date', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-erased'))).toEqual('Erased: 3 weeks ago'); - }); - - it('should render finished date', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-finished'))).toEqual( - 'Finished: 3 weeks ago', - ); - }); - - it('should render queued date', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-queued'))).toEqual('Queued: 9 seconds'); - }); - - it('should render runner ID', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-runner'))).toEqual( - 'Runner: local ci runner (#1)', - ); - }); - - it('should render timeout information', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-timeout'))).toEqual( - 'Timeout: 1m 40s (from runner)', - ); - }); - - it('should render coverage', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-coverage'))).toEqual('Coverage: 20%'); - }); - - it('should render tags', () => { - expect(trimWhitespace(vm.$el.querySelector('.js-job-tags'))).toEqual('Tags: tag'); - }); - }); -}); diff --git a/spec/javascripts/jobs/components/sidebar_spec.js b/spec/javascripts/jobs/components/sidebar_spec.js new file mode 100644 index 00000000000..2f5c4245ced --- /dev/null +++ b/spec/javascripts/jobs/components/sidebar_spec.js @@ -0,0 +1,196 @@ +import Vue from 'vue'; +import sidebarDetailsBlock from '~/jobs/components/sidebar.vue'; +import createStore from '~/jobs/store'; +import job, { stages, jobsInStage } from '../mock_data'; +import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper'; +import { trimText } from '../../helpers/vue_component_helper'; + +describe('Sidebar details block', () => { + const SidebarComponent = Vue.extend(sidebarDetailsBlock); + let vm; + let store; + + beforeEach(() => { + store = createStore(); + }); + + afterEach(() => { + vm.$destroy(); + }); + + describe('when it is loading', () => { + it('should render a loading spinner', () => { + store.dispatch('requestJob'); + vm = mountComponentWithStore(SidebarComponent, { store }); + + expect(vm.$el.querySelector('.fa-spinner')).toBeDefined(); + }); + }); + + describe('when there is no retry path retry', () => { + it('should not render a retry button', () => { + const copy = Object.assign({}, job); + delete copy.retry_path; + + store.dispatch('receiveJobSuccess', copy); + vm = mountComponentWithStore(SidebarComponent, { + store, + }); + + expect(vm.$el.querySelector('.js-retry-job')).toBeNull(); + }); + }); + + describe('without terminal path', () => { + it('does not render terminal link', () => { + store.dispatch('receiveJobSuccess', job); + vm = mountComponentWithStore(SidebarComponent, { store }); + + expect(vm.$el.querySelector('.js-terminal-link')).toBeNull(); + }); + }); + + describe('with terminal path', () => { + it('renders terminal link', () => { + store.dispatch('receiveJobSuccess', job); + vm = mountComponentWithStore(SidebarComponent, { + store, + props: { + terminalPath: 'job/43123/terminal', + }, + }); + + expect(vm.$el.querySelector('.js-terminal-link')).not.toBeNull(); + }); + }); + + beforeEach(() => { + store.dispatch('receiveJobSuccess', job); + vm = mountComponentWithStore(SidebarComponent, { store }); + }); + + describe('actions', () => { + it('should render link to new issue', () => { + expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual( + job.new_issue_path, + ); + expect(vm.$el.querySelector('.js-new-issue').textContent.trim()).toEqual('New issue'); + }); + + it('should render link to retry job', () => { + expect(vm.$el.querySelector('.js-retry-job').getAttribute('href')).toEqual(job.retry_path); + }); + + it('should render link to cancel job', () => { + expect(vm.$el.querySelector('.js-cancel-job').getAttribute('href')).toEqual(job.cancel_path); + }); + }); + + describe('information', () => { + it('should render merge request link', () => { + expect(trimText(vm.$el.querySelector('.js-job-mr').textContent)).toEqual('Merge Request: !2'); + + expect(vm.$el.querySelector('.js-job-mr a').getAttribute('href')).toEqual( + job.merge_request.path, + ); + }); + + it('should render job duration', () => { + expect(trimText(vm.$el.querySelector('.js-job-duration').textContent)).toEqual( + 'Duration: 6 seconds', + ); + }); + + it('should render erased date', () => { + expect(trimText(vm.$el.querySelector('.js-job-erased').textContent)).toEqual( + 'Erased: 3 weeks ago', + ); + }); + + it('should render finished date', () => { + expect(trimText(vm.$el.querySelector('.js-job-finished').textContent)).toEqual( + 'Finished: 3 weeks ago', + ); + }); + + it('should render queued date', () => { + expect(trimText(vm.$el.querySelector('.js-job-queued').textContent)).toEqual( + 'Queued: 9 seconds', + ); + }); + + it('should render runner ID', () => { + expect(trimText(vm.$el.querySelector('.js-job-runner').textContent)).toEqual( + 'Runner: local ci runner (#1)', + ); + }); + + it('should render timeout information', () => { + expect(trimText(vm.$el.querySelector('.js-job-timeout').textContent)).toEqual( + 'Timeout: 1m 40s (from runner)', + ); + }); + + it('should render coverage', () => { + expect(trimText(vm.$el.querySelector('.js-job-coverage').textContent)).toEqual( + 'Coverage: 20%', + ); + }); + + it('should render tags', () => { + expect(trimText(vm.$el.querySelector('.js-job-tags').textContent)).toEqual('Tags: tag'); + }); + }); + + describe('stages dropdown', () => { + beforeEach(() => { + store.dispatch('receiveJobSuccess', job); + }); + + describe('while fetching stages', () => { + it('renders dropdown with More label', () => { + vm = mountComponentWithStore(SidebarComponent, { store }); + + expect(vm.$el.querySelector('.js-selected-stage').textContent.trim()).toEqual('More'); + }); + }); + + describe('with stages', () => { + beforeEach(() => { + store.dispatch('receiveStagesSuccess', stages); + vm = mountComponentWithStore(SidebarComponent, { store }); + }); + + it('renders first stage as selected', () => { + expect(vm.$el.querySelector('.js-selected-stage').textContent.trim()).toEqual( + stages[0].name, + ); + }); + }); + + describe('without jobs for stages', () => { + beforeEach(() => { + store.dispatch('receiveJobSuccess', job); + store.dispatch('receiveStagesSuccess', stages); + vm = mountComponentWithStore(SidebarComponent, { store }); + }); + + it('does not render job container', () => { + expect(vm.$el.querySelector('.js-jobs-container')).toBeNull(); + }); + }); + + describe('with jobs for stages', () => { + beforeEach(() => { + store.dispatch('receiveJobSuccess', job); + store.dispatch('receiveStagesSuccess', stages); + store.dispatch('receiveJobsForStageSuccess', jobsInStage.latest_statuses); + vm = mountComponentWithStore(SidebarComponent, { store }); + }); + + it('renders list of jobs', () => { + expect(vm.$el.querySelector('.js-jobs-container')).not.toBeNull(); + }); + }); + }); +}); diff --git a/spec/javascripts/jobs/components/stages_dropdown_spec.js b/spec/javascripts/jobs/components/stages_dropdown_spec.js index 402289345aa..aa6cc0f1b1a 100644 --- a/spec/javascripts/jobs/components/stages_dropdown_spec.js +++ b/spec/javascripts/jobs/components/stages_dropdown_spec.js @@ -8,10 +8,25 @@ describe('Artifacts block', () => { beforeEach(() => { vm = mountComponent(Component, { - pipelineId: 28029444, - pipelinePath: 'pipeline/28029444', - pipelineRef: '50101-truncated-job-information', - pipelineRefPath: 'commits/50101-truncated-job-information', + pipeline: { + id: 28029444, + details: { + status: { + details_path: '/gitlab-org/gitlab-ce/pipelines/28029444', + group: 'success', + has_details: true, + icon: 'status_success', + label: 'passed', + text: 'passed', + tooltip: 'passed', + }, + }, + path: 'pipeline/28029444', + }, + ref: { + path: 'commits/50101-truncated-job-information', + name: '50101-truncated-job-information', + }, stages: [ { name: 'build', @@ -20,15 +35,6 @@ describe('Artifacts block', () => { name: 'test', }, ], - pipelineStatus: { - details_path: '/gitlab-org/gitlab-ce/pipelines/28029444', - group: 'success', - has_details: true, - icon: 'status_success', - label: 'passed', - text: 'passed', - tooltip: 'passed', - }, }); }); diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js index 8fdd9b309b7..4269b42e8b6 100644 --- a/spec/javascripts/jobs/mock_data.js +++ b/spec/javascripts/jobs/mock_data.js @@ -20,7 +20,8 @@ export default { group: 'success', has_details: true, details_path: '/root/ci-mock/-/jobs/4757', - favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', + favicon: + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { icon: 'retry', title: 'Retry', @@ -37,7 +38,8 @@ export default { username: 'root', id: 1, state: 'active', - avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', web_url: 'http://localhost:3000/root', }, erase_path: '/root/ci-mock/-/jobs/4757/erase', @@ -54,7 +56,8 @@ export default { username: 'root', id: 1, state: 'active', - avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', web_url: 'http://localhost:3000/root', }, active: false, @@ -78,7 +81,8 @@ export default { group: 'success', has_details: true, details_path: '/root/ci-mock/pipelines/140', - favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', + favicon: + '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', }, duration: 6, finished_at: '2017-06-01T17:32:00.042Z', @@ -107,11 +111,14 @@ export default { username: 'root', id: 1, state: 'active', - avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', web_url: 'http://localhost:3000/root', }, - author_gravatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - commit_url: 'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6', + author_gravatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', + commit_url: + 'http://localhost:3000/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6', commit_path: '/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6', }, }, @@ -125,3 +132,1029 @@ export default { }, raw_path: '/root/ci-mock/builds/4757/raw', }; + +export const stages = [ + { + name: 'build', + title: 'build: running', + groups: [ + { + name: 'build:linux', + size: 1, + status: { + icon: 'status_pending', + text: 'pending', + label: 'pending', + group: 'pending', + tooltip: 'pending', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/1180', + illustration: { + image: 'illustrations/pending_job_empty.svg', + size: 'svg-430', + title: 'This job has not started yet', + content: 'This job is in pending state and is waiting to be picked by a runner', + }, + favicon: + '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png', + action: { + icon: 'cancel', + title: 'Cancel', + path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel', + method: 'post', + }, + }, + jobs: [ + { + id: 1180, + name: 'build:linux', + started: false, + build_path: '/gitlab-org/gitlab-shell/-/jobs/1180', + cancel_path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel', + playable: false, + created_at: '2018-09-28T11:09:57.229Z', + updated_at: '2018-09-28T11:09:57.503Z', + status: { + icon: 'status_pending', + text: 'pending', + label: 'pending', + group: 'pending', + tooltip: 'pending', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/1180', + illustration: { + image: 'illustrations/pending_job_empty.svg', + size: 'svg-430', + title: 'This job has not started yet', + content: 'This job is in pending state and is waiting to be picked by a runner', + }, + favicon: + '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png', + action: { + icon: 'cancel', + title: 'Cancel', + path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel', + method: 'post', + }, + }, + }, + ], + }, + { + name: 'build:osx', + size: 1, + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/444', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/444/retry', + method: 'post', + }, + }, + jobs: [ + { + id: 444, + name: 'build:osx', + started: '2018-05-18T05:32:20.655Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/444', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/444/retry', + playable: false, + created_at: '2018-05-18T15:32:54.364Z', + updated_at: '2018-05-18T15:32:54.364Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/444', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/444/retry', + method: 'post', + }, + }, + }, + ], + }, + ], + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + tooltip: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/pipelines/27#build', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png', + }, + path: '/gitlab-org/gitlab-shell/pipelines/27#build', + dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build', + }, + { + name: 'test', + title: 'test: passed with warnings', + groups: [ + { + name: 'jenkins', + size: 1, + status: { + icon: 'status_success', + text: 'passed', + label: null, + group: 'success', + tooltip: null, + has_details: false, + details_path: null, + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + jobs: [ + { + id: 459, + name: 'jenkins', + started: '2018-05-18T09:32:20.658Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/459', + playable: false, + created_at: '2018-05-18T15:32:55.330Z', + updated_at: '2018-05-18T15:32:55.330Z', + status: { + icon: 'status_success', + text: 'passed', + label: null, + group: 'success', + tooltip: null, + has_details: false, + details_path: null, + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + }, + ], + }, + { + name: 'rspec:linux', + size: 3, + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: false, + details_path: null, + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + jobs: [ + { + id: 445, + name: 'rspec:linux 0 3', + started: '2018-05-18T07:32:20.655Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/445', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/445/retry', + playable: false, + created_at: '2018-05-18T15:32:54.425Z', + updated_at: '2018-05-18T15:32:54.425Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/445', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/445/retry', + method: 'post', + }, + }, + }, + { + id: 446, + name: 'rspec:linux 1 3', + started: '2018-05-18T07:32:20.655Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/446', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/446/retry', + playable: false, + created_at: '2018-05-18T15:32:54.506Z', + updated_at: '2018-05-18T15:32:54.506Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/446', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/446/retry', + method: 'post', + }, + }, + }, + { + id: 447, + name: 'rspec:linux 2 3', + started: '2018-05-18T07:32:20.656Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/447', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/447/retry', + playable: false, + created_at: '2018-05-18T15:32:54.572Z', + updated_at: '2018-05-18T15:32:54.572Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/447', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/447/retry', + method: 'post', + }, + }, + }, + ], + }, + { + name: 'rspec:osx', + size: 1, + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/452', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/452/retry', + method: 'post', + }, + }, + jobs: [ + { + id: 452, + name: 'rspec:osx', + started: '2018-05-18T07:32:20.657Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/452', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/452/retry', + playable: false, + created_at: '2018-05-18T15:32:54.920Z', + updated_at: '2018-05-18T15:32:54.920Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/452', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/452/retry', + method: 'post', + }, + }, + }, + ], + }, + { + name: 'rspec:windows', + size: 3, + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: false, + details_path: null, + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + jobs: [ + { + id: 448, + name: 'rspec:windows 0 3', + started: '2018-05-18T07:32:20.656Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/448', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/448/retry', + playable: false, + created_at: '2018-05-18T15:32:54.639Z', + updated_at: '2018-05-18T15:32:54.639Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/448', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/448/retry', + method: 'post', + }, + }, + }, + { + id: 449, + name: 'rspec:windows 1 3', + started: '2018-05-18T07:32:20.656Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/449', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/449/retry', + playable: false, + created_at: '2018-05-18T15:32:54.703Z', + updated_at: '2018-05-18T15:32:54.703Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/449', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/449/retry', + method: 'post', + }, + }, + }, + { + id: 451, + name: 'rspec:windows 2 3', + started: '2018-05-18T07:32:20.657Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/451', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/451/retry', + playable: false, + created_at: '2018-05-18T15:32:54.853Z', + updated_at: '2018-05-18T15:32:54.853Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/451', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/451/retry', + method: 'post', + }, + }, + }, + ], + }, + { + name: 'spinach:linux', + size: 1, + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/453', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/453/retry', + method: 'post', + }, + }, + jobs: [ + { + id: 453, + name: 'spinach:linux', + started: '2018-05-18T07:32:20.657Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/453', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/453/retry', + playable: false, + created_at: '2018-05-18T15:32:54.993Z', + updated_at: '2018-05-18T15:32:54.993Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/453', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/453/retry', + method: 'post', + }, + }, + }, + ], + }, + { + name: 'spinach:osx', + size: 1, + status: { + icon: 'status_warning', + text: 'failed', + label: 'failed (allowed to fail)', + group: 'failed_with_warnings', + tooltip: 'failed - (unknown failure) (allowed to fail)', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/454', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/454/retry', + method: 'post', + }, + }, + jobs: [ + { + id: 454, + name: 'spinach:osx', + started: '2018-05-18T07:32:20.657Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/454', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/454/retry', + playable: false, + created_at: '2018-05-18T15:32:55.053Z', + updated_at: '2018-05-18T15:32:55.053Z', + status: { + icon: 'status_warning', + text: 'failed', + label: 'failed (allowed to fail)', + group: 'failed_with_warnings', + tooltip: 'failed - (unknown failure) (allowed to fail)', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/454', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/454/retry', + method: 'post', + }, + }, + callout_message: 'There is an unknown failure, please try again', + recoverable: true, + }, + ], + }, + ], + status: { + icon: 'status_warning', + text: 'passed', + label: 'passed with warnings', + group: 'success_with_warnings', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/pipelines/27#test', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + }, + path: '/gitlab-org/gitlab-shell/pipelines/27#test', + dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=test', + }, + { + name: 'deploy', + title: 'deploy: running', + groups: [ + { + name: 'production', + size: 1, + status: { + icon: 'status_created', + text: 'created', + label: 'created', + group: 'created', + tooltip: 'created', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/457', + illustration: { + image: 'illustrations/job_not_triggered.svg', + size: 'svg-306', + title: 'This job has not been triggered yet', + content: + 'This job depends on upstream jobs that need to succeed in order for this job to be triggered', + }, + favicon: + '/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png', + action: { + icon: 'cancel', + title: 'Cancel', + path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel', + method: 'post', + }, + }, + jobs: [ + { + id: 457, + name: 'production', + started: false, + build_path: '/gitlab-org/gitlab-shell/-/jobs/457', + cancel_path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel', + playable: false, + created_at: '2018-05-18T15:32:55.259Z', + updated_at: '2018-09-28T11:09:57.454Z', + status: { + icon: 'status_created', + text: 'created', + label: 'created', + group: 'created', + tooltip: 'created', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/457', + illustration: { + image: 'illustrations/job_not_triggered.svg', + size: 'svg-306', + title: 'This job has not been triggered yet', + content: + 'This job depends on upstream jobs that need to succeed in order for this job to be triggered', + }, + favicon: + '/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png', + action: { + icon: 'cancel', + title: 'Cancel', + path: '/gitlab-org/gitlab-shell/-/jobs/457/cancel', + method: 'post', + }, + }, + }, + ], + }, + { + name: 'staging', + size: 1, + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/455', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/455/retry', + method: 'post', + }, + }, + jobs: [ + { + id: 455, + name: 'staging', + started: '2018-05-18T09:32:20.658Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/455', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/455/retry', + playable: false, + created_at: '2018-05-18T15:32:55.119Z', + updated_at: '2018-05-18T15:32:55.119Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/455', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/455/retry', + method: 'post', + }, + }, + }, + ], + }, + { + name: 'stop staging', + size: 1, + status: { + icon: 'status_created', + text: 'created', + label: 'created', + group: 'created', + tooltip: 'created', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/456', + illustration: { + image: 'illustrations/job_not_triggered.svg', + size: 'svg-306', + title: 'This job has not been triggered yet', + content: + 'This job depends on upstream jobs that need to succeed in order for this job to be triggered', + }, + favicon: + '/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png', + action: { + icon: 'cancel', + title: 'Cancel', + path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel', + method: 'post', + }, + }, + jobs: [ + { + id: 456, + name: 'stop staging', + started: false, + build_path: '/gitlab-org/gitlab-shell/-/jobs/456', + cancel_path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel', + playable: false, + created_at: '2018-05-18T15:32:55.205Z', + updated_at: '2018-09-28T11:09:57.396Z', + status: { + icon: 'status_created', + text: 'created', + label: 'created', + group: 'created', + tooltip: 'created', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/456', + illustration: { + image: 'illustrations/job_not_triggered.svg', + size: 'svg-306', + title: 'This job has not been triggered yet', + content: + 'This job depends on upstream jobs that need to succeed in order for this job to be triggered', + }, + favicon: + '/assets/ci_favicons/favicon_status_created-4b975aa976d24e5a3ea7cd9a5713e6ce2cd9afd08b910415e96675de35f64955.png', + action: { + icon: 'cancel', + title: 'Cancel', + path: '/gitlab-org/gitlab-shell/-/jobs/456/cancel', + method: 'post', + }, + }, + }, + ], + }, + ], + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + tooltip: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/pipelines/27#deploy', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png', + }, + path: '/gitlab-org/gitlab-shell/pipelines/27#deploy', + dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=deploy', + }, + { + name: 'notify', + title: 'notify: manual action', + groups: [ + { + name: 'slack', + size: 1, + status: { + icon: 'status_manual', + text: 'manual', + label: 'manual play action', + group: 'manual', + tooltip: 'manual action', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/458', + illustration: { + image: 'illustrations/manual_action.svg', + size: 'svg-394', + title: 'This job requires a manual action', + content: + 'This job depends on a user to trigger its process. Often they are used to deploy code to production environments', + }, + favicon: + '/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png', + action: { + icon: 'play', + title: 'Play', + path: '/gitlab-org/gitlab-shell/-/jobs/458/play', + method: 'post', + }, + }, + jobs: [ + { + id: 458, + name: 'slack', + started: null, + build_path: '/gitlab-org/gitlab-shell/-/jobs/458', + play_path: '/gitlab-org/gitlab-shell/-/jobs/458/play', + playable: true, + created_at: '2018-05-18T15:32:55.303Z', + updated_at: '2018-05-18T15:34:08.535Z', + status: { + icon: 'status_manual', + text: 'manual', + label: 'manual play action', + group: 'manual', + tooltip: 'manual action', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/458', + illustration: { + image: 'illustrations/manual_action.svg', + size: 'svg-394', + title: 'This job requires a manual action', + content: + 'This job depends on a user to trigger its process. Often they are used to deploy code to production environments', + }, + favicon: + '/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png', + action: { + icon: 'play', + title: 'Play', + path: '/gitlab-org/gitlab-shell/-/jobs/458/play', + method: 'post', + }, + }, + }, + ], + }, + ], + status: { + icon: 'status_manual', + text: 'manual', + label: 'manual action', + group: 'manual', + tooltip: 'manual action', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/pipelines/27#notify', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png', + }, + path: '/gitlab-org/gitlab-shell/pipelines/27#notify', + dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=notify', + }, +]; + +export const jobsInStage = { + name: 'build', + title: 'build: running', + latest_statuses: [ + { + id: 1180, + name: 'build:linux', + started: false, + build_path: '/gitlab-org/gitlab-shell/-/jobs/1180', + cancel_path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel', + playable: false, + created_at: '2018-09-28T11:09:57.229Z', + updated_at: '2018-09-28T11:09:57.503Z', + status: { + icon: 'status_pending', + text: 'pending', + label: 'pending', + group: 'pending', + tooltip: 'pending', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/1180', + illustration: { + image: 'illustrations/pending_job_empty.svg', + size: 'svg-430', + title: 'This job has not started yet', + content: 'This job is in pending state and is waiting to be picked by a runner', + }, + favicon: + '/assets/ci_favicons/favicon_status_pending-5bdf338420e5221ca24353b6bff1c9367189588750632e9a871b7af09ff6a2ae.png', + action: { + icon: 'cancel', + title: 'Cancel', + path: '/gitlab-org/gitlab-shell/-/jobs/1180/cancel', + method: 'post', + }, + }, + }, + { + id: 444, + name: 'build:osx', + started: '2018-05-18T05:32:20.655Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/444', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/444/retry', + playable: false, + created_at: '2018-05-18T15:32:54.364Z', + updated_at: '2018-05-18T15:32:54.364Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/444', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/444/retry', + method: 'post', + }, + }, + }, + ], + retried: [ + { + id: 443, + name: 'build:linux', + started: '2018-05-18T06:32:20.655Z', + build_path: '/gitlab-org/gitlab-shell/-/jobs/443', + retry_path: '/gitlab-org/gitlab-shell/-/jobs/443/retry', + playable: false, + created_at: '2018-05-18T15:32:54.296Z', + updated_at: '2018-05-18T15:32:54.296Z', + status: { + icon: 'status_success', + text: 'passed', + label: 'passed', + group: 'success', + tooltip: 'passed (retried)', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/-/jobs/443', + illustration: { + image: 'illustrations/skipped-job_empty.svg', + size: 'svg-430', + title: 'This job does not have a trace.', + }, + favicon: + '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png', + action: { + icon: 'retry', + title: 'Retry', + path: '/gitlab-org/gitlab-shell/-/jobs/443/retry', + method: 'post', + }, + }, + }, + ], + status: { + icon: 'status_running', + text: 'running', + label: 'running', + group: 'running', + tooltip: 'running', + has_details: true, + details_path: '/gitlab-org/gitlab-shell/pipelines/27#build', + illustration: null, + favicon: + '/assets/ci_favicons/favicon_status_running-9c635b2419a8e1ec991c993061b89cc5aefc0743bb238ecd0c381e7741a70e8c.png', + }, + path: '/gitlab-org/gitlab-shell/pipelines/27#build', + dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build', +}; diff --git a/spec/javascripts/jobs/store/actions_spec.js b/spec/javascripts/jobs/store/actions_spec.js index 5042718dfa0..5ab1f75d0d6 100644 --- a/spec/javascripts/jobs/store/actions_spec.js +++ b/spec/javascripts/jobs/store/actions_spec.js @@ -27,7 +27,6 @@ import { receiveStagesSuccess, receiveStagesError, requestJobsForStage, - setSelectedStage, fetchJobsForStage, receiveJobsForStageSuccess, receiveJobsForStageError, @@ -236,7 +235,8 @@ describe('Job State actions', () => { }, { payload: { - html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :', complete: true, + html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :', + complete: true, }, type: 'receiveTraceSuccess', }, @@ -421,7 +421,9 @@ describe('Job State actions', () => { let mock; beforeEach(() => { - mockedState.stagesEndpoint = `${TEST_HOST}/endpoint.json`; + mockedState.job.pipeline = { + path: `${TEST_HOST}/endpoint.json/stages`, + }; mock = new MockAdapter(axios); }); @@ -430,8 +432,10 @@ describe('Job State actions', () => { }); describe('success', () => { - it('dispatches requestStages and receiveStagesSuccess ', done => { - mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [{ id: 121212, name: 'build' }]); + it('dispatches requestStages and receiveStagesSuccess, fetchJobsForStage ', done => { + mock + .onGet(`${TEST_HOST}/endpoint.json/stages`) + .replyOnce(200, { details: { stages: [{ id: 121212, name: 'build' }] } }); testAction( fetchStages, @@ -446,6 +450,10 @@ describe('Job State actions', () => { payload: [{ id: 121212, name: 'build' }], type: 'receiveStagesSuccess', }, + { + payload: { id: 121212, name: 'build' }, + type: 'fetchJobsForStage', + }, ], done, ); @@ -516,24 +524,10 @@ describe('Job State actions', () => { }); }); - describe('setSelectedStage', () => { - it('should commit SET_SELECTED_STAGE mutation ', done => { - testAction( - setSelectedStage, - { name: 'build' }, - mockedState, - [{ type: types.SET_SELECTED_STAGE, payload: { name: 'build' } }], - [], - done, - ); - }); - }); - describe('fetchJobsForStage', () => { let mock; beforeEach(() => { - mockedState.stageJobsEndpoint = `${TEST_HOST}/endpoint.json`; mock = new MockAdapter(axios); }); @@ -542,20 +536,18 @@ describe('Job State actions', () => { }); describe('success', () => { - it('dispatches setSelectedStage, requestJobsForStage and receiveJobsForStageSuccess ', done => { - mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [{ id: 121212, name: 'build' }]); + it('dispatches requestJobsForStage and receiveJobsForStageSuccess ', done => { + mock + .onGet(`${TEST_HOST}/jobs.json`) + .replyOnce(200, { latest_statuses: [{ id: 121212, name: 'build' }], retried: [] }); testAction( fetchJobsForStage, - null, + { dropdown_path: `${TEST_HOST}/jobs.json` }, mockedState, [], [ { - type: 'setSelectedStage', - payload: null, - }, - { type: 'requestJobsForStage', }, { @@ -570,21 +562,17 @@ describe('Job State actions', () => { describe('error', () => { beforeEach(() => { - mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500); + mock.onGet(`${TEST_HOST}/jobs.json`).reply(500); }); - it('dispatches setSelectedStage, requestJobsForStage and receiveJobsForStageError', done => { + it('dispatches requestJobsForStage and receiveJobsForStageError', done => { testAction( fetchJobsForStage, - null, + { dropdown_path: `${TEST_HOST}/jobs.json` }, mockedState, [], [ { - payload: null, - type: 'setSelectedStage', - }, - { type: 'requestJobsForStage', }, { diff --git a/spec/javascripts/jobs/store/getters_spec.js b/spec/javascripts/jobs/store/getters_spec.js new file mode 100644 index 00000000000..63ef4135d83 --- /dev/null +++ b/spec/javascripts/jobs/store/getters_spec.js @@ -0,0 +1,121 @@ +import * as getters from '~/jobs/store/getters'; +import state from '~/jobs/store/state'; + +describe('Job Store Getters', () => { + let localState; + + beforeEach(() => { + localState = state(); + }); + + describe('headerActions', () => { + describe('with new issue path', () => { + it('returns an array with action to create a new issue', () => { + localState.job.new_issue_path = 'issues/new'; + + expect(getters.headerActions(localState)).toEqual([ + { + label: 'New issue', + path: localState.job.new_issue_path, + cssClass: + 'js-new-issue btn btn-success btn-inverted d-none d-md-block d-lg-block d-xl-block', + type: 'link', + }, + ]); + }); + }); + + describe('without new issue path', () => { + it('returns an empty array', () => { + expect(getters.headerActions(localState)).toEqual([]); + }); + }); + }); + + describe('headerTime', () => { + describe('when the job has started key', () => { + it('returns started key', () => { + const started = '2018-08-31T16:20:49.023Z'; + localState.job.started = started; + + expect(getters.headerTime(localState)).toEqual(started); + }); + }); + + describe('when the job does not have started key', () => { + it('returns created_at key', () => { + const created = '2018-08-31T16:20:49.023Z'; + localState.job.created_at = created; + expect(getters.headerTime(localState)).toEqual(created); + }); + }); + }); + + describe('shouldRenderCalloutMessage', () => { + describe('with status and callout message', () => { + it('returns true', () => { + localState.job.callout_message = 'Callout message'; + localState.job.status = { icon: 'passed' }; + + expect(getters.shouldRenderCalloutMessage(localState)).toEqual(true); + }); + }); + + describe('without status & with callout message', () => { + it('returns false', () => { + localState.job.callout_message = 'Callout message'; + expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false); + }); + }); + + describe('with status & without callout message', () => { + it('returns false', () => { + localState.job.status = { icon: 'passed' }; + + expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false); + }); + }); + }); + + describe('jobHasStarted', () => { + describe('when started equals false', () => { + it('returns false', () => { + localState.job.started = false; + expect(getters.jobHasStarted(localState)).toEqual(false); + }); + }); + + describe('when started equals string', () => { + it('returns true', () => { + localState.job.started = '2018-08-31T16:20:49.023Z'; + expect(getters.jobHasStarted(localState)).toEqual(true); + }); + }); + }); + + describe('hasEnvironment', () => { + describe('without `deployment_status`', () => { + it('returns false', () => { + expect(getters.hasEnvironment(localState)).toEqual(false); + }); + }); + describe('with an empty object for `deployment_status`', () => { + it('returns false', () => { + localState.job.deployment_status = {}; + expect(getters.hasEnvironment(localState)).toEqual(false); + }); + }); + describe('when `deployment_status` is defined and not empty', () => { + it('returns true', () => { + localState.job.deployment_status = { + status: 'creating', + environment: { + last_deployment: {}, + }, + }; + + expect(getters.hasEnvironment(localState)).toEqual(true); + }); + }); + }); +}); diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js index 2a01bd85520..40b5f009ceb 100644 --- a/spec/javascripts/notes/components/noteable_discussion_spec.js +++ b/spec/javascripts/notes/components/noteable_discussion_spec.js @@ -133,4 +133,29 @@ describe('noteable_discussion component', () => { }); }); }); + + describe('componentData', () => { + it('should return first note object for placeholder note', () => { + const data = { + isPlaceholderNote: true, + notes: [ + { body: 'hello world!' }, + ], + }; + + const note = vm.componentData(data); + expect(note).toEqual(data.notes[0]); + }); + + it('should return given note for nonplaceholder notes', () => { + const data = { + notes: [ + { id: 12 }, + ], + }; + + const note = vm.componentData(data); + expect(note).toEqual(data); + }); + }); }); diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js index 9dff52a9d49..0e30759c41d 100644 --- a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js +++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js @@ -8,7 +8,10 @@ describe('Issuable Time Tracker', () => { let initialData; let vm; - const initTimeTrackingComponent = opts => { + const initTimeTrackingComponent = ({ timeEstimate, + timeSpent, + timeEstimateHumanReadable, + timeSpentHumanReadable }) => { setFixtures(` <div> <div id="mock-container"></div> @@ -16,10 +19,10 @@ describe('Issuable Time Tracker', () => { `); initialData = { - time_estimate: opts.timeEstimate, - time_spent: opts.timeSpent, - human_time_estimate: opts.timeEstimateHumanReadable, - human_time_spent: opts.timeSpentHumanReadable, + timeEstimate, + timeSpent, + humanTimeEstimate: timeEstimateHumanReadable, + humanTimeSpent: timeSpentHumanReadable, rootPath: '/', }; @@ -43,8 +46,8 @@ describe('Issuable Time Tracker', () => { describe('Initialization', () => { beforeEach(() => { initTimeTrackingComponent({ - timeEstimate: 100000, - timeSpent: 5000, + timeEstimate: 10000, // 2h 46m + timeSpent: 5000, // 1h 23m timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '1h 23m', }); @@ -56,14 +59,14 @@ describe('Issuable Time Tracker', () => { it('should correctly set timeEstimate', done => { Vue.nextTick(() => { - expect(vm.timeEstimate).toBe(initialData.time_estimate); + expect(vm.timeEstimate).toBe(initialData.timeEstimate); done(); }); }); it('should correctly set time_spent', done => { Vue.nextTick(() => { - expect(vm.timeSpent).toBe(initialData.time_spent); + expect(vm.timeSpent).toBe(initialData.timeSpent); done(); }); }); @@ -74,8 +77,8 @@ describe('Issuable Time Tracker', () => { describe('Comparison pane', () => { beforeEach(() => { initTimeTrackingComponent({ - timeEstimate: 100000, - timeSpent: 5000, + timeEstimate: 100000, // 1d 3h + timeSpent: 5000, // 1h 23m timeEstimateHumanReadable: '', timeSpentHumanReadable: '', }); @@ -106,8 +109,8 @@ describe('Issuable Time Tracker', () => { }); it('should display the remaining meter with the correct background color when over estimate', done => { - vm.time_estimate = 100000; - vm.time_spent = 20000000; + vm.timeEstimate = 10000; // 2h 46m + vm.timeSpent = 20000000; // 231 days Vue.nextTick(() => { expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]')).not.toBeNull(); done(); @@ -119,7 +122,7 @@ describe('Issuable Time Tracker', () => { describe('Estimate only pane', () => { beforeEach(() => { initTimeTrackingComponent({ - timeEstimate: 100000, + timeEstimate: 10000, // 2h 46m timeSpent: 0, timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '', @@ -142,7 +145,7 @@ describe('Issuable Time Tracker', () => { beforeEach(() => { initTimeTrackingComponent({ timeEstimate: 0, - timeSpent: 5000, + timeSpent: 5000, // 1h 23m timeEstimateHumanReadable: '2h 46m', timeSpentHumanReadable: '1h 23m', }); diff --git a/spec/javascripts/ide/components/changed_file_icon_spec.js b/spec/javascripts/vue_shared/components/changed_file_icon_spec.js index 7308219f705..5b1038840c7 100644 --- a/spec/javascripts/ide/components/changed_file_icon_spec.js +++ b/spec/javascripts/vue_shared/components/changed_file_icon_spec.js @@ -1,8 +1,8 @@ import Vue from 'vue'; -import changedFileIcon from '~/ide/components/changed_file_icon.vue'; +import changedFileIcon from '~/vue_shared/components/changed_file_icon.vue'; import createComponent from 'spec/helpers/vue_mount_component_helper'; -describe('IDE changed file icon', () => { +describe('Changed file icon', () => { let vm; beforeEach(() => { @@ -33,14 +33,14 @@ describe('IDE changed file icon', () => { }); describe('changedIconClass', () => { - it('includes ide-file-modified when not a temp file', () => { - expect(vm.changedIconClass).toContain('ide-file-modified'); + it('includes file-modified when not a temp file', () => { + expect(vm.changedIconClass).toContain('file-modified'); }); - it('includes ide-file-addition when a temp file', () => { + it('includes file-addition when a temp file', () => { vm.file.tempFile = true; - expect(vm.changedIconClass).toContain('ide-file-addition'); + expect(vm.changedIconClass).toContain('file-addition'); }); }); }); diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb index 0735ebd6dcb..5dce3fcbcb6 100644 --- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb +++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :migration, schema: 20171114162227 do + include GitHelpers + let(:merge_request_diffs) { table(:merge_request_diffs) } let(:merge_requests) { table(:merge_requests) } @@ -9,11 +11,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m let(:merge_request) { merge_requests.create!(iid: 1, target_project_id: project.id, source_project_id: project.id, target_branch: 'feature', source_branch: 'master').becomes(MergeRequest) } let(:merge_request_diff) { MergeRequest.find(merge_request.id).create_merge_request_diff } let(:updated_merge_request_diff) { MergeRequestDiff.find(merge_request_diff.id) } - let(:rugged) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end - end + let(:rugged) { rugged_repo(project.repository) } before do allow_any_instance_of(MergeRequestDiff) diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb index 9095ffbfd52..1bd077ddbdf 100644 --- a/spec/lib/gitlab/conflict/file_spec.rb +++ b/spec/lib/gitlab/conflict/file_spec.rb @@ -1,9 +1,11 @@ require 'spec_helper' describe Gitlab::Conflict::File do + include GitHelpers + let(:project) { create(:project, :repository) } let(:repository) { project.repository } - let(:rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } } + let(:rugged) { rugged_repo(repository) } let(:their_commit) { rugged.branches['conflict-start'].target } let(:our_commit) { rugged.branches['conflict-resolvable'].target } let(:merge_request) { create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project) } diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index ea49502ae2e..b243f0dacae 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -4,6 +4,9 @@ require "spec_helper" describe Gitlab::Git::Blob, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } + let(:rugged) do + Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH)) + end describe 'initialize' do let(:blob) { Gitlab::Git::Blob.new(name: 'test') } @@ -139,9 +142,7 @@ describe Gitlab::Git::Blob, :seed_helper do it 'limits the size of a large file' do blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1 buffer = Array.new(blob_size, 0) - rugged_blob = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - Rugged::Blob.from_buffer(repository.rugged, buffer.join('')) - end + rugged_blob = Rugged::Blob.from_buffer(rugged, buffer.join('')) blob = Gitlab::Git::Blob.raw(repository, rugged_blob) expect(blob.size).to eq(blob_size) @@ -156,9 +157,7 @@ describe Gitlab::Git::Blob, :seed_helper do context 'when sha references a tree' do it 'returns nil' do - tree = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.rev_parse('master^{tree}') - end + tree = rugged.rev_parse('master^{tree}') blob = Gitlab::Git::Blob.raw(repository, tree.oid) @@ -262,11 +261,7 @@ describe Gitlab::Git::Blob, :seed_helper do end describe '.batch_lfs_pointers' do - let(:tree_object) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.rev_parse('master^{tree}') - end - end + let(:tree_object) { rugged.rev_parse('master^{tree}') } let(:non_lfs_blob) do Gitlab::Git::Blob.find( diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb index 79ccbb79966..0df282d0ae3 100644 --- a/spec/lib/gitlab/git/branch_spec.rb +++ b/spec/lib/gitlab/git/branch_spec.rb @@ -3,9 +3,7 @@ require "spec_helper" describe Gitlab::Git::Branch, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:rugged) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged - end + Rugged::Repository.new(File.join(TestEnv.repos_path, repository.relative_path)) end subject { repository.branches } @@ -74,9 +72,7 @@ describe Gitlab::Git::Branch, :seed_helper do Gitlab::Git.committer_hash(email: user.email, name: user.name) end let(:params) do - parents = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - [repository.rugged.head.target] - end + parents = [rugged.head.target] tree = parents.first.tree { diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 2718a3c5e49..9ef27081f98 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -1,19 +1,17 @@ require "spec_helper" describe Gitlab::Git::Commit, :seed_helper do + include GitHelpers + let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } - let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) } - let(:rugged_commit) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.lookup(SeedRepo::Commit::ID) - end + let(:rugged_repo) do + Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH)) end + let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) } + let(:rugged_commit) { rugged_repo.lookup(SeedRepo::Commit::ID) } + describe "Commit info" do before do - repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - end - @committer = { email: 'mike@smith.com', name: "Mike Smith", @@ -26,12 +24,12 @@ describe Gitlab::Git::Commit, :seed_helper do time: Time.now } - @parents = [repo.head.target] + @parents = [rugged_repo.head.target] @gitlab_parents = @parents.map { |c| described_class.find(repository, c.oid) } @tree = @parents.first.tree sha = Rugged::Commit.create( - repo, + rugged_repo, author: @author, committer: @committer, tree: @tree, @@ -40,7 +38,7 @@ describe Gitlab::Git::Commit, :seed_helper do update_ref: "HEAD" ) - @raw_commit = repo.lookup(sha) + @raw_commit = rugged_repo.lookup(sha) @commit = described_class.find(repository, sha) end @@ -61,10 +59,7 @@ describe Gitlab::Git::Commit, :seed_helper do after do # Erase the new commit so other tests get the original repo - repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - end - repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) + rugged_repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) end end @@ -120,9 +115,7 @@ describe Gitlab::Git::Commit, :seed_helper do describe '.find' do it "should return first head commit if without params" do expect(described_class.last(repository).id).to eq( - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.head.target.oid - end + rugged_repo.head.target.oid ) end diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb index 27d803e0117..8a4415506c4 100644 --- a/spec/lib/gitlab/git/diff_spec.rb +++ b/spec/lib/gitlab/git/diff_spec.rb @@ -64,9 +64,22 @@ EOT end end - context 'using a Rugged::Patch' do + context 'using a GitalyClient::Diff' do + let(:gitaly_diff) do + Gitlab::GitalyClient::Diff.new( + to_path: ".gitmodules", + from_path: ".gitmodules", + old_mode: 0100644, + new_mode: 0100644, + from_id: '357406f3075a57708d0163752905cc1576fceacc', + to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0', + patch: raw_patch + ) + end + let(:diff) { described_class.new(gitaly_diff) } + context 'with a small diff' do - let(:diff) { described_class.new(gitaly_diff) } + let(:raw_patch) { @raw_diff_hash[:diff] } it 'initializes the diff' do expect(diff.to_hash).to eq(@raw_diff_hash) @@ -78,16 +91,17 @@ EOT end context 'using a diff that is too large' do - it 'prunes the diff' do - gitaly_diff.too_large = true - diff = described_class.new(gitaly_diff) + let(:raw_patch) { 'a' * 204800 } + it 'prunes the diff' do expect(diff.diff).to be_empty expect(diff).to be_too_large end end context 'using a collapsable diff that is too large' do + let(:raw_patch) { 'a' * 204800 } + it 'prunes the diff as a large diff instead of as a collapsed diff' do gitaly_diff.too_large = true diff = described_class.new(gitaly_diff, expanded: false) @@ -97,43 +111,6 @@ EOT expect(diff).not_to be_collapsed end end - end - - context 'using a GitalyClient::Diff' do - let(:diff) do - described_class.new( - Gitlab::GitalyClient::Diff.new( - to_path: ".gitmodules", - from_path: ".gitmodules", - old_mode: 0100644, - new_mode: 0100644, - from_id: '357406f3075a57708d0163752905cc1576fceacc', - to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0', - patch: raw_patch - ) - ) - end - - context 'with a small diff' do - let(:raw_patch) { @raw_diff_hash[:diff] } - - it 'initializes the diff' do - expect(diff.to_hash).to eq(@raw_diff_hash) - end - - it 'does not prune the diff' do - expect(diff).not_to be_too_large - end - end - - context 'using a diff that is too large' do - let(:raw_patch) { 'a' * 204800 } - - it 'prunes the diff' do - expect(diff.diff).to be_empty - expect(diff).to be_too_large - end - end context 'when the patch passed is not UTF-8-encoded' do let(:raw_patch) { @raw_diff_hash[:diff].encode(Encoding::ASCII_8BIT) } diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index d02536a2fb4..51eb997a325 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -19,7 +19,10 @@ describe Gitlab::Git::Repository, :seed_helper do end end + let(:mutable_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } + let(:repository_path) { File.join(TestEnv.repos_path, repository.relative_path) } + let(:repository_rugged) { Rugged::Repository.new(repository_path) } let(:storage_path) { TestEnv.repos_path } let(:user) { build(:user) } @@ -71,7 +74,6 @@ describe Gitlab::Git::Repository, :seed_helper do describe "Respond to" do subject { repository } - it { is_expected.to respond_to(:rugged) } it { is_expected.to respond_to(:root_ref) } it { is_expected.to respond_to(:tags) } end @@ -91,57 +93,6 @@ describe Gitlab::Git::Repository, :seed_helper do end end - describe "#rugged" do - describe 'when storage is broken', :broken_storage do - it 'raises a storage exception when storage is not available' do - broken_repo = described_class.new('broken', 'a/path.git', '') - - expect { broken_repo.rugged }.to raise_error(Gitlab::Git::Storage::Inaccessible) - end - end - - it 'raises a no repository exception when there is no repo' do - broken_repo = described_class.new('default', 'a/path.git', '') - - expect do - Gitlab::GitalyClient::StorageSettings.allow_disk_access { broken_repo.rugged } - end.to raise_error(Gitlab::Git::Repository::NoRepository) - end - - describe 'alternates keyword argument' do - context 'with no Git env stored' do - before do - allow(Gitlab::Git::HookEnv).to receive(:all).and_return({}) - end - - it "is passed an empty array" do - expect(Rugged::Repository).to receive(:new).with(repository_path, alternates: []) - - repository_rugged - end - end - - context 'with absolute and relative Git object dir envvars stored' do - before do - allow(Gitlab::Git::HookEnv).to receive(:all).and_return({ - 'GIT_OBJECT_DIRECTORY_RELATIVE' => './objects/foo', - 'GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE' => ['./objects/bar', './objects/baz'], - 'GIT_OBJECT_DIRECTORY' => 'ignored', - 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => %w[ignored ignored], - 'GIT_OTHER' => 'another_env' - }) - end - - it "is passed the relative object dir envvars after being converted to absolute ones" do - alternates = %w[foo bar baz].map { |d| File.join(repository_path, './objects', d) } - expect(Rugged::Repository).to receive(:new).with(repository_path, alternates: alternates) - - repository_rugged - end - end - end - end - describe '#branch_names' do subject { repository.branch_names } @@ -284,7 +235,6 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#submodule_url_for' do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:ref) { 'master' } def submodule_url(path) @@ -336,7 +286,7 @@ describe Gitlab::Git::Repository, :seed_helper do it { expect(repository.has_local_branches?).to eq(true) } context 'mutable' do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + let(:repository) { mutable_repository } after do ensure_seeds @@ -369,7 +319,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe "#delete_branch" do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + let(:repository) { mutable_repository } after do ensure_seeds @@ -393,7 +343,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe "#create_branch" do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + let(:repository) { mutable_repository } after do ensure_seeds @@ -418,39 +368,33 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#delete_refs' do - let(:repo) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } - - def repo_rugged - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repo.rugged - end - end + let(:repository) { mutable_repository } after do ensure_seeds end it 'deletes the ref' do - repo.delete_refs('refs/heads/feature') + repository.delete_refs('refs/heads/feature') - expect(repo_rugged.references['refs/heads/feature']).to be_nil + expect(repository_rugged.references['refs/heads/feature']).to be_nil end it 'deletes all refs' do refs = %w[refs/heads/wip refs/tags/v1.1.0] - repo.delete_refs(*refs) + repository.delete_refs(*refs) refs.each do |ref| - expect(repo_rugged.references[ref]).to be_nil + expect(repository_rugged.references[ref]).to be_nil end end it 'does not fail when deleting an empty list of refs' do - expect { repo.delete_refs(*[]) }.not_to raise_error + expect { repository.delete_refs(*[]) }.not_to raise_error end it 'raises an error if it failed' do - expect { repo.delete_refs('refs\heads\fix') }.to raise_error(Gitlab::Git::Repository::GitError) + expect { repository.delete_refs('refs\heads\fix') }.to raise_error(Gitlab::Git::Repository::GitError) end end @@ -528,9 +472,7 @@ describe Gitlab::Git::Repository, :seed_helper do end def new_repository_path - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - new_repository.path - end + File.join(TestEnv.repos_path, new_repository.relative_path) end end @@ -577,18 +519,16 @@ describe Gitlab::Git::Repository, :seed_helper do Gitlab::Git::Commit.find(repository, @rename_commit_id) end - before(:context) do + before do # Add new commits so that there's a renamed file in the commit history - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - @commit_with_old_name_id = new_commit_edit_old_file(repo).oid - @rename_commit_id = new_commit_move_file(repo).oid - @commit_with_new_name_id = new_commit_edit_new_file(repo).oid + @commit_with_old_name_id = new_commit_edit_old_file(repository_rugged).oid + @rename_commit_id = new_commit_move_file(repository_rugged).oid + @commit_with_new_name_id = new_commit_edit_new_file(repository_rugged).oid end - after(:context) do + after do # Erase our commits so other tests get the original repo - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) + repository_rugged.references.update("refs/heads/master", SeedRepo::LastCommit::ID) end context "where 'follow' == true" do @@ -1010,12 +950,10 @@ describe Gitlab::Git::Repository, :seed_helper do subject { repository.branches } context 'with local and remote branches' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } before do - create_remote_branch(repository, 'joe', 'remote_branch', 'master') + create_remote_branch('joe', 'remote_branch', 'master') repository.create_branch('local_branch', 'master') end @@ -1038,12 +976,10 @@ describe Gitlab::Git::Repository, :seed_helper do end context 'with local and remote branches' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } before do - create_remote_branch(repository, 'joe', 'remote_branch', 'master') + create_remote_branch('joe', 'remote_branch', 'master') repository.create_branch('local_branch', 'master') end @@ -1303,24 +1239,24 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#local_branches' do - before(:all) do - @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') + let(:repository) { mutable_repository } + + before do + create_remote_branch('joe', 'remote_branch', 'master') + repository.create_branch('local_branch', 'master') end - after(:all) do + after do ensure_seeds end it 'returns the local branches' do - create_remote_branch(@repo, 'joe', 'remote_branch', 'master') - @repo.create_branch('local_branch', 'master') - - expect(@repo.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) - expect(@repo.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true) + expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) + expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true) end it 'returns a Branch with UTF-8 fields' do - branches = @repo.local_branches.to_a + branches = repository.local_branches.to_a expect(branches.size).to be > 0 branches.each do |branch| expect(branch.name).to be_utf8 @@ -1331,11 +1267,11 @@ describe Gitlab::Git::Repository, :seed_helper do it 'gets the branches from GitalyClient' do expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:local_branches) .and_return([]) - @repo.local_branches + repository.local_branches end it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :local_branches do - subject { @repo.local_branches } + subject { repository.local_branches } end end @@ -1391,8 +1327,7 @@ describe Gitlab::Git::Repository, :seed_helper do describe '#fetch_source_branch!' do let(:local_ref) { 'refs/merge-requests/1/head' } - let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } - let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + let(:source_repository) { mutable_repository } after do ensure_seeds @@ -1401,7 +1336,8 @@ describe Gitlab::Git::Repository, :seed_helper do context 'when the branch exists' do context 'when the commit does not exist locally' do let(:source_branch) { 'new-branch-for-fetch-source-branch' } - let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } } + let(:source_path) { File.join(TestEnv.repos_path, source_repository.relative_path) } + let(:source_rugged) { Rugged::Repository.new(source_path) } let(:new_oid) { new_commit_edit_old_file(source_rugged).oid } before do @@ -1513,8 +1449,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#set_config' do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } - let(:rugged) { repository_rugged } + let(:repository) { mutable_repository } let(:entries) do { 'test.foo1' => 'bla bla', @@ -1526,19 +1461,18 @@ describe Gitlab::Git::Repository, :seed_helper do it 'can set config settings' do expect(repository.set_config(entries)).to be_nil - expect(rugged.config['test.foo1']).to eq('bla bla') - expect(rugged.config['test.foo2']).to eq('1234') - expect(rugged.config['test.foo3']).to eq('true') + expect(repository_rugged.config['test.foo1']).to eq('bla bla') + expect(repository_rugged.config['test.foo2']).to eq('1234') + expect(repository_rugged.config['test.foo3']).to eq('true') end after do - entries.keys.each { |k| rugged.config.delete(k) } + entries.keys.each { |k| repository_rugged.config.delete(k) } end end describe '#delete_config' do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } - let(:rugged) { repository_rugged } + let(:repository) { mutable_repository } let(:entries) do { 'test.foo1' => 'bla bla', @@ -1549,21 +1483,19 @@ describe Gitlab::Git::Repository, :seed_helper do it 'can delete config settings' do entries.each do |key, value| - rugged.config[key] = value + repository_rugged.config[key] = value end expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil - config_keys = rugged.config.each_key.to_a + config_keys = repository_rugged.config.each_key.to_a expect(config_keys).not_to include('test.foo1') expect(config_keys).not_to include('test.foo2') end end describe '#merge' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } let(:source_sha) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' } let(:target_branch) { 'test-merge-target-branch' } @@ -1602,9 +1534,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#ff_merge' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' } let(:source_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' } let(:target_branch) { 'test-ff-target-branch' } @@ -1667,9 +1597,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#delete_all_refs_except' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } before do repository.write_ref("refs/delete/a", "0b4bc9a49b562e85de7cc9e834518ea6828729b9") @@ -1693,12 +1621,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe 'remotes' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end - let(:rugged) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } - end + let(:repository) { mutable_repository } let(:remote_name) { 'my-remote' } let(:url) { 'http://my-repo.git' } @@ -1711,26 +1634,26 @@ describe Gitlab::Git::Repository, :seed_helper do it 'added the remote' do begin - rugged.remotes.delete(remote_name) + repository_rugged.remotes.delete(remote_name) rescue Rugged::ConfigError end repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap) - expect(rugged.remotes[remote_name]).not_to be_nil - expect(rugged.config["remote.#{remote_name}.mirror"]).to eq('true') - expect(rugged.config["remote.#{remote_name}.prune"]).to eq('true') - expect(rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap) + expect(repository_rugged.remotes[remote_name]).not_to be_nil + expect(repository_rugged.config["remote.#{remote_name}.mirror"]).to eq('true') + expect(repository_rugged.config["remote.#{remote_name}.prune"]).to eq('true') + expect(repository_rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap) end end describe '#remove_remote' do it 'removes the remote' do - rugged.remotes.create(remote_name, url) + repository_rugged.remotes.create(remote_name, url) repository.remove_remote(remote_name) - expect(rugged.remotes[remote_name]).to be_nil + expect(repository_rugged.remotes[remote_name]).to be_nil end end end @@ -1901,13 +1824,11 @@ describe Gitlab::Git::Repository, :seed_helper do end context 'when the diff contains a rename' do - let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged } - let(:end_sha) { new_commit_move_file(repo).oid } + let(:end_sha) { new_commit_move_file(repository_rugged).oid } after do # Erase our commits so other tests get the original repo - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID) + repository_rugged.references.update('refs/heads/master', SeedRepo::LastCommit::ID) end it 'does not include the renamed file in the sparse checkout' do @@ -1954,10 +1875,9 @@ describe Gitlab::Git::Repository, :seed_helper do end end - def create_remote_branch(repository, remote_name, branch_name, source_branch_name) + def create_remote_branch(remote_name, branch_name, source_branch_name) source_branch = repository.branches.find { |branch| branch.name == source_branch_name } - rugged = repository_rugged - rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha) + repository_rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha) end # Build the options hash that's passed to Rugged::Commit#create @@ -2035,16 +1955,4 @@ describe Gitlab::Git::Repository, :seed_helper do line.split("\t").last end end - - def repository_rugged - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged - end - end - - def repository_path - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.path - end - end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index dbd64c4bec0..e7da5565c26 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Gitlab::GitAccess do include TermsHelper + include GitHelpers let(:user) { create(:user) } @@ -736,21 +737,19 @@ describe Gitlab::GitAccess do def merge_into_protected_branch @protected_branch_merge_commit ||= begin - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.add_branch(user, unprotected_branch, 'feature') - rugged = project.repository.rugged - target_branch = rugged.rev_parse('feature') - source_branch = project.repository.create_file( - user, - 'filename', - 'This is the file content', - message: 'This is a good commit message', - branch_name: unprotected_branch) - author = { email: "email@example.com", time: Time.now, name: "Example Git User" } - - merge_index = rugged.merge_commits(target_branch, source_branch) - Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged)) - end + project.repository.add_branch(user, unprotected_branch, 'feature') + rugged = rugged_repo(project.repository) + target_branch = rugged.rev_parse('feature') + source_branch = project.repository.create_file( + user, + 'filename', + 'This is the file content', + message: 'This is a good commit message', + branch_name: unprotected_branch) + author = { email: "email@example.com", time: Time.now, name: "Example Git User" } + + merge_index = rugged.merge_commits(target_branch, source_branch) + Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged)) end end diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb index 5f67fe6b952..d82c9c28da0 100644 --- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb @@ -39,6 +39,10 @@ describe Gitlab::GitalyClient::WikiService do expect(wiki_page.title).to eq('My Page') expect(wiki_page.raw_data).to eq('ab') expect(wiki_page_version.format).to eq('markdown') + + expect(wiki_page.title).to be_utf8 + expect(wiki_page.path).to be_utf8 + expect(wiki_page.name).to be_utf8 end end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 1ec1fe10744..d669c42ab4a 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -46,6 +46,7 @@ describe Gitlab::UsageData do git database avg_cycle_analytics + web_ide_commits )) end diff --git a/spec/lib/gitlab/web_ide_commits_counter_spec.rb b/spec/lib/gitlab/web_ide_commits_counter_spec.rb new file mode 100644 index 00000000000..c51889a1c63 --- /dev/null +++ b/spec/lib/gitlab/web_ide_commits_counter_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::WebIdeCommitsCounter, :clean_gitlab_redis_shared_state do + describe '.increment' do + it 'increments the web ide commits counter by 1' do + expect do + described_class.increment + end.to change { described_class.total_count }.from(0).to(1) + end + end + + describe '.total_count' do + it 'returns the total amount of web ide commits' do + expect(described_class.total_count).to eq(0) + end + end +end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 48f4e53b93e..666d7e69f89 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -746,7 +746,7 @@ describe MergeRequest do end describe "#wipless_title" do - ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix| + ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', '[WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix| it "removes the '#{wip_prefix}' prefix" do wipless_title = subject.title subject.title = "#{wip_prefix}#{subject.title}" diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 22dc81acfda..8913644a3ce 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Namespace do include ProjectForksHelper + include GitHelpers let!(:namespace) { create(:namespace) } let(:gitlab_shell) { Gitlab::Shell.new } @@ -361,9 +362,7 @@ describe Namespace do end def project_rugged(project) - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged_repo(project.repository) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d15ba89efb5..8b71919544e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Project do include ProjectForksHelper + include GitHelpers describe 'associations' do it { is_expected.to belong_to(:group) } @@ -4039,8 +4040,6 @@ describe Project do end def rugged_config - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged.config - end + rugged_repo(project.repository).config end end diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index 269d5deca20..3d316fb3c5b 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' describe RemoteMirror do + include GitHelpers + describe 'URL validation' do context 'with a valid URL' do it 'should be valid' do @@ -74,9 +76,7 @@ describe RemoteMirror do mirror.update_attribute(:url, 'http://foo:baz@test.com') - config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repo.raw_repository.rugged.config - end + config = rugged_repo(repo).config expect(config["remote.#{mirror.remote_name}.url"]).to eq('http://foo:baz@test.com') end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 784d17e271e..77e549d9528 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' describe Repository do include RepoHelpers + include GitHelpers + TestBlob = Struct.new(:path) let(:project) { create(:project, :repository) } @@ -137,9 +139,7 @@ describe Repository do options = { message: 'test tag message\n', tagger: { name: 'John Smith', email: 'john@gmail.com' } } - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options) - end + rugged_repo(repository).tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options) double_first = double(committed_date: Time.now - 1.second) double_last = double(committed_date: Time.now) @@ -151,9 +151,7 @@ describe Repository do it { is_expected.to eq(['v1.1.0', 'v1.0.0', annotated_tag_name]) } after do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.tags.delete(annotated_tag_name) - end + rugged_repo(repository).tags.delete(annotated_tag_name) end end end @@ -1678,10 +1676,7 @@ describe Repository do it 'returns the number of branches' do expect(repository.branch_count).to be_an(Integer) - # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync - rugged_count = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.raw_repository.rugged.branches.count - end + rugged_count = rugged_repo(repository).branches.count expect(repository.branch_count).to eq(rugged_count) end @@ -1691,10 +1686,7 @@ describe Repository do it 'returns the number of tags' do expect(repository.tag_count).to be_an(Integer) - # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync - rugged_count = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.raw_repository.rugged.tags.count - end + rugged_count = rugged_repo(repository).tags.count expect(repository.tag_count).to eq(rugged_count) end @@ -2184,9 +2176,7 @@ describe Repository do end def create_remote_branch(remote_name, branch_name, target) - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged - end + rugged = rugged_repo(repository) rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 63850939be1..b4732ec0cd5 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -77,7 +77,7 @@ describe WikiPage do end describe "#initialize" do - context "when initialized with an existing gollum page" do + context "when initialized with an existing page" do before do create_page("test page", "test content") @page = wiki.wiki.page(title: "test page") diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index f3fb88474a4..06ccf383362 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -278,6 +278,12 @@ describe API::Commits do } end + it 'does not increment the usage counters using access token authentication' do + expect(::Gitlab::WebIdeCommitsCounter).not_to receive(:increment) + + post api(url, user), valid_c_params + end + it 'a new file in project repo' do post api(url, user), valid_c_params diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index e987eee6e91..07d19e3ad29 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -81,6 +81,35 @@ describe API::MergeRequests do let(:user2) { create(:user) } it 'returns an array of all merge requests except unauthorized ones' do + get api('/merge_requests', user), scope: :all + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request_locked.id, merge_request2.id) + end + + it "returns an array of no merge_requests when wip=yes" do + get api("/merge_requests", user), wip: 'yes' + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it "returns an array of no merge_requests when wip=no" do + get api("/merge_requests", user), wip: 'no' + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request_locked.id, merge_request2.id) + end + + it 'does not return unauthorized merge requests' do private_project = create(:project, :private) merge_request3 = create(:merge_request, :simple, source_project: private_project, target_project: private_project, source_branch: 'other-branch') @@ -244,6 +273,15 @@ describe API::MergeRequests do expect(response).to have_gitlab_http_status(404) end + it "returns an array of no merge_requests when wip=yes" do + get api("/projects/#{project.id}/merge_requests", user), wip: 'yes' + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + it 'returns merge_request by "iids" array' do get api(endpoint_path, user), iids: [merge_request.iid, merge_request_closed.iid] diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index 92159e1e372..2699f6e7bcd 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe GitTagPushService do include RepoHelpers + include GitHelpers let(:user) { create(:user) } let(:project) { create(:project, :repository) } @@ -118,9 +119,7 @@ describe GitTagPushService do before do # Create the lightweight tag - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.raw_repository.rugged.tags.create(tag_name, newrev) - end + rugged_repo(project.repository).tags.create(tag_name, newrev) # Clear tag list cache project.repository.expire_tags_cache diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb index 8ab09412f55..53bce15735c 100644 --- a/spec/services/merge_requests/squash_service_spec.rb +++ b/spec/services/merge_requests/squash_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe MergeRequests::SquashService do + include GitHelpers + let(:service) { described_class.new(project, user, {}) } let(:user) { project.owner } let(:project) { create(:project, :repository) } @@ -63,9 +65,7 @@ describe MergeRequests::SquashService do end it 'has the same diff as the merge request, but a different SHA' do - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged = rugged_repo(project.repository) mr_diff = rugged.diff(merge_request.diff_base_sha, merge_request.diff_head_sha) squash_diff = rugged.diff(merge_request.diff_start_sha, squash_sha) diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb index cd52bc88f4c..4dd6c6dab86 100644 --- a/spec/services/projects/after_import_service_spec.rb +++ b/spec/services/projects/after_import_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::AfterImportService do + include GitHelpers + subject { described_class.new(project) } let(:project) { create(:project, :repository) } @@ -53,7 +55,7 @@ describe Projects::AfterImportService do end def rugged - Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } + rugged_repo(repository) end end end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index bb3f1501f0e..a80c8a7fe51 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::CreateService, '#execute' do + include GitHelpers + let(:gitlab_shell) { Gitlab::Shell.new } let(:user) { create :user } let(:opts) do @@ -295,9 +297,7 @@ describe Projects::CreateService, '#execute' do it 'writes project full path to .git/config' do project = create_project(user, opts) - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged = rugged_repo(project.repository) expect(rugged.config['gitlab.fullpath']).to eq project.full_path end diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb index 5f67c325223..0e82194e9ee 100644 --- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb +++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::HashedStorage::MigrateRepositoryService do + include GitHelpers + let(:gitlab_shell) { Gitlab::Shell.new } let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) } let(:legacy_storage) { Storage::LegacyProject.new(project) } @@ -38,9 +40,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do it 'writes project full path to .git/config' do service.execute - rugged_config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged.config['gitlab.fullpath'] - end + rugged_config = rugged_repo(project.repository).config['gitlab.fullpath'] expect(rugged_config).to eq project.full_path end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 92c5ac7354a..1411723fb9e 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::TransferService do + include GitHelpers + let(:gitlab_shell) { Gitlab::Shell.new } let(:user) { create(:user) } let(:group) { create(:group) } @@ -291,8 +293,6 @@ describe Projects::TransferService do end def rugged_config - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged.config - end + rugged_repo(project.repository).config end end diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index e0fceae88de..83035788a56 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -1,4 +1,6 @@ module CycleAnalyticsHelpers + include GitHelpers + def create_commit_referencing_issue(issue, branch_name: generate(:branch)) project.repository.add_branch(user, branch_name, 'master') create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name) @@ -9,7 +11,7 @@ module CycleAnalyticsHelpers oldrev = repository.commit(branch_name)&.sha || Gitlab::Git::BLANK_SHA if Timecop.frozen? && Gitlab::GitalyClient.feature_enabled?(:operation_user_commit_files) - mock_gitaly_multi_action_dates(repository.raw, commit_time) + mock_gitaly_multi_action_dates(repository, commit_time) end commit_shas = Array.new(count) do |index| @@ -118,18 +120,15 @@ module CycleAnalyticsHelpers protected: false) end - def mock_gitaly_multi_action_dates(raw_repository, commit_time) - allow(raw_repository).to receive(:multi_action).and_wrap_original do |m, *args| + def mock_gitaly_multi_action_dates(repository, commit_time) + allow(repository.raw).to receive(:multi_action).and_wrap_original do |m, *args| new_date = commit_time || Time.now branch_update = m.call(*args) if branch_update.newrev _, opts = args - commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - rugged = raw_repository.rugged - rugged.rev_parse(branch_update.newrev) - end + commit = rugged_repo(repository).rev_parse(branch_update.newrev) branch_update.newrev = commit.amend( update_ref: "#{Gitlab::Git::BRANCH_REF_PREFIX}#{opts[:branch_name]}", diff --git a/spec/support/helpers/git_helpers.rb b/spec/support/helpers/git_helpers.rb index fc92bc38561..99a7c39852e 100644 --- a/spec/support/helpers/git_helpers.rb +++ b/spec/support/helpers/git_helpers.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true module GitHelpers + def rugged_repo(repository) + path = File.join(TestEnv.repos_path, repository.disk_path + '.git') + + Rugged::Repository.new(path) + end + def project_hook_exists?(project) Gitlab::GitalyClient::StorageSettings.allow_disk_access do project_path = project.repository.raw_repository.path diff --git a/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb b/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb index 93c21a99e59..1190863d88e 100644 --- a/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb +++ b/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb @@ -33,6 +33,14 @@ shared_examples "builds correct paths" do |**patterns| it_behaves_like "matches the method pattern", :upload_path end + describe "#relative_path" do + it 'is relative' do + skip 'Path not set, skipping.' unless subject.path + + expect(Pathname.new(subject.relative_path)).to be_relative + end + end + describe ".absolute_path" do it_behaves_like "matches the method pattern", :absolute_path do let(:target) { subject.class } diff --git a/spec/views/projects/jobs/show.html.haml_spec.rb b/spec/views/projects/jobs/show.html.haml_spec.rb index 496646dc623..e06a9ecb98b 100644 --- a/spec/views/projects/jobs/show.html.haml_spec.rb +++ b/spec/views/projects/jobs/show.html.haml_spec.rb @@ -18,161 +18,6 @@ describe 'projects/jobs/show' do allow(view).to receive(:can?).and_return(true) end - describe 'environment info in job view' do - context 'job with latest deployment' do - let(:build) do - create(:ci_build, :success, :trace_artifact, environment: 'staging') - end - - before do - create(:environment, name: 'staging') - create(:deployment, deployable: build) - end - - it 'shows deployment message' do - expected_text = 'This job is the most recent deployment' - render - - expect(rendered).to have_css( - '.environment-information', text: expected_text) - end - end - - context 'job with outdated deployment' do - let(:build) do - create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) - end - - let(:second_build) do - create(:ci_build, :success, :trace_artifact, environment: 'staging', pipeline: pipeline) - end - - let(:environment) do - create(:environment, name: 'staging', project: project) - end - - let!(:first_deployment) do - create(:deployment, environment: environment, deployable: build) - end - - let!(:second_deployment) do - create(:deployment, environment: environment, deployable: second_build) - end - - it 'shows deployment message' do - expected_text = 'This job is an out-of-date deployment ' \ - "to staging.\nView the most recent deployment ##{second_deployment.iid}." - render - - expect(rendered).to have_css('.environment-information', text: expected_text) - end - end - - context 'job failed to deploy' do - let(:build) do - create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline) - end - - let!(:environment) do - create(:environment, name: 'staging', project: project) - end - - it 'shows deployment message' do - expected_text = 'The deployment of this job to staging did not succeed.' - render - - expect(rendered).to have_css( - '.environment-information', text: expected_text) - end - end - - context 'job will deploy' do - let(:build) do - create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline) - end - - context 'when environment exists' do - let!(:environment) do - create(:environment, name: 'staging', project: project) - end - - it 'shows deployment message' do - expected_text = 'This job is creating a deployment to staging' - render - - expect(rendered).to have_css( - '.environment-information', text: expected_text) - end - - context 'when it has deployment' do - let!(:deployment) do - create(:deployment, environment: environment) - end - - it 'shows that deployment will be overwritten' do - expected_text = 'This job is creating a deployment to staging' - render - - expect(rendered).to have_css( - '.environment-information', text: expected_text) - expect(rendered).to have_css( - '.environment-information', text: 'latest deployment') - end - end - end - - context 'when environment does not exist' do - it 'shows deployment message' do - expected_text = 'This job is creating a deployment to staging' - render - - expect(rendered).to have_css( - '.environment-information', text: expected_text) - expect(rendered).not_to have_css( - '.environment-information', text: 'latest deployment') - end - end - end - - context 'job that failed to deploy and environment has not been created' do - let(:build) do - create(:ci_build, :failed, :trace_artifact, environment: 'staging', pipeline: pipeline) - end - - let!(:environment) do - create(:environment, name: 'staging', project: project) - end - - it 'shows deployment message' do - expected_text = 'The deployment of this job to staging did not succeed' - render - - expect(rendered).to have_css( - '.environment-information', text: expected_text) - end - end - - context 'job that will deploy and environment has not been created' do - let(:build) do - create(:ci_build, :running, :trace_live, environment: 'staging', pipeline: pipeline) - end - - let!(:environment) do - create(:environment, name: 'staging', project: project) - end - - it 'shows deployment message' do - expected_text = 'This job is creating a deployment to staging' - render - - expect(rendered).to have_css( - '.environment-information', text: expected_text) - expect(rendered).not_to have_css( - '.environment-information', text: 'latest deployment') - end - end - end - context 'when job is running' do let(:build) { create(:ci_build, :trace_live, :running, pipeline: pipeline) } diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 30e67e67e0e..a159f24f876 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -3,6 +3,8 @@ require 'fileutils' require 'spec_helper' describe GitGarbageCollectWorker do + include GitHelpers + let(:project) { create(:project, :repository) } let(:shell) { Gitlab::Shell.new } let!(:lease_uuid) { SecureRandom.uuid } @@ -197,9 +199,7 @@ describe GitGarbageCollectWorker do # Create a new commit on a random new branch def create_objects(project) - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged = rugged_repo(project.repository) old_commit = rugged.branches.first.target new_commit_sha = Rugged::Commit.create( rugged, diff --git a/spec/workers/repository_remove_remote_worker_spec.rb b/spec/workers/repository_remove_remote_worker_spec.rb index a653f6f926c..6ddb653d142 100644 --- a/spec/workers/repository_remove_remote_worker_spec.rb +++ b/spec/workers/repository_remove_remote_worker_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe RepositoryRemoveRemoteWorker do include ExclusiveLeaseHelpers + include GitHelpers describe '#perform' do let!(:project) { create(:project, :repository) } @@ -50,9 +51,7 @@ describe RepositoryRemoveRemoteWorker do end def create_remote_branch(remote_name, branch_name, target) - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged = rugged_repo(project.repository) rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) end |