diff options
Diffstat (limited to 'spec/features')
52 files changed, 1448 insertions, 408 deletions
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb new file mode 100644 index 00000000000..f6d625fa7f6 --- /dev/null +++ b/spec/features/admin/admin_groups_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +feature 'Admin Groups', feature: true do + let(:internal) { Gitlab::VisibilityLevel::INTERNAL } + + before do + login_as(:admin) + + stub_application_setting(default_group_visibility: internal) + end + + describe 'create a group' do + scenario 'shows the visibility level radio populated with the default value' do + visit new_admin_group_path + + expect_selected_visibility(internal) + end + end + + describe 'group edit' do + scenario 'shows the visibility level radio populated with the group visibility_level value' do + group = create(:group, :private) + + visit edit_admin_group_path(group) + + expect_selected_visibility(group.visibility_level) + end + end + + def expect_selected_visibility(level) + selector = "#group_visibility_level_#{level}[checked=checked]" + + expect(page).to have_selector(selector, count: 1) + end +end diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index a92075fec8f..4aa84fb65d9 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -380,6 +380,25 @@ describe 'Issue Boards', feature: true, js: true do wait_for_board_cards(1, 5) end + + it 'creates new list from a new label' do + click_button 'Create new list' + + wait_for_ajax + + click_link 'Create new label' + + fill_in('new_label_name', with: 'Testing New Label') + + first('.suggest-colors a').click + + click_button 'Create' + + wait_for_ajax + wait_for_vue_resource + + expect(page).to have_selector('.board', count: 5) + end end end @@ -640,6 +659,10 @@ describe 'Issue Boards', feature: true, js: true do wait_for_vue_resource end + it 'displays lists' do + expect(page).to have_selector('.board') + end + it 'does not show create new list' do expect(page).not_to have_selector('.js-new-board-list') end diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb index 760a8967123..a03cd6fbf2d 100644 --- a/spec/features/boards/new_issue_spec.rb +++ b/spec/features/boards/new_issue_spec.rb @@ -46,7 +46,7 @@ describe 'Issue Boards new issue', feature: true, js: true do click_button 'Cancel' - expect(page).to have_selector('.board-new-issue-form', visible: false) + expect(page).not_to have_selector('.board-new-issue-form') end end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 338c53f08a6..44646ffc602 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -12,11 +12,15 @@ describe 'Commits' do end let!(:pipeline) do - FactoryGirl.create :ci_pipeline, project: project, sha: project.commit.sha + create(:ci_pipeline, + project: project, + ref: project.default_branch, + sha: project.commit.sha, + status: :success) end context 'commit status is Generic Commit Status' do - let!(:status) { FactoryGirl.create :generic_commit_status, pipeline: pipeline } + let!(:status) { create(:generic_commit_status, pipeline: pipeline) } before do project.team << [@user, :reporter] @@ -39,7 +43,7 @@ describe 'Commits' do end context 'commit status is Ci Build' do - let!(:build) { FactoryGirl.create :ci_build, pipeline: pipeline } + let!(:build) { create(:ci_build, pipeline: pipeline) } let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } context 'when logged as developer' do @@ -48,13 +52,22 @@ describe 'Commits' do end describe 'Project commits' do + let!(:pipeline_from_other_branch) do + create(:ci_pipeline, + project: project, + ref: 'fix', + sha: project.commit.sha, + status: :failed) + end + before do visit namespace_project_commits_path(project.namespace, project, :master) end - it 'shows build status' do + it 'shows correct build status from default branch' do page.within("//li[@id='commit-#{pipeline.short_sha}']") do - expect(page).to have_css(".ci-status-link") + expect(page).to have_css('.ci-status-link') + expect(page).to have_css('.ci-status-icon-success') end end end diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb new file mode 100644 index 00000000000..41dcfe439c2 --- /dev/null +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe 'Navigation bar counter', feature: true, js: true, caching: true do + let(:user) { create(:user) } + let(:project) { create(:empty_project, namespace: user.namespace) } + let(:issue) { create(:issue, project: project) } + let(:merge_request) { create(:merge_request, source_project: project) } + + before do + issue.update(assignee: user) + merge_request.update(assignee: user) + login_as(user) + end + + it 'reflects dashboard issues count' do + visit issues_dashboard_path + + expect_counters('issues', '1') + + issue.update(assignee: nil) + visit issues_dashboard_path + + expect_counters('issues', '1') + end + + it 'reflects dashboard merge requests count' do + visit merge_requests_dashboard_path + + expect_counters('merge_requests', '1') + + merge_request.update(assignee: nil) + visit merge_requests_dashboard_path + + expect_counters('merge_requests', '1') + end + + def expect_counters(issuable_type, count) + dashboard_count = find('li.active span.badge') + nav_count = find(".dashboard-shortcuts-#{issuable_type} span.count") + + expect(nav_count).to have_content(count) + expect(dashboard_count).to have_content(count) + end +end diff --git a/spec/features/environment_spec.rb b/spec/features/environment_spec.rb new file mode 100644 index 00000000000..0c1939fd885 --- /dev/null +++ b/spec/features/environment_spec.rb @@ -0,0 +1,161 @@ +require 'spec_helper' + +feature 'Environment', :feature do + given(:project) { create(:empty_project) } + given(:user) { create(:user) } + given(:role) { :developer } + + background do + login_as(user) + project.team << [user, role] + end + + feature 'environment details page' do + given!(:environment) { create(:environment, project: project) } + given!(:deployment) { } + given!(:manual) { } + + before do + visit_environment(environment) + end + + context 'without deployments' do + scenario 'does show no deployments' do + expect(page).to have_content('You don\'t have any deployments right now.') + end + end + + context 'with deployments' do + context 'when there is no related deployable' do + given(:deployment) do + create(:deployment, environment: environment, deployable: nil) + end + + scenario 'does show deployment SHA' do + expect(page).to have_link(deployment.short_sha) + end + + scenario 'does not show a re-deploy button for deployment without build' do + expect(page).not_to have_link('Re-deploy') + end + end + + context 'with related deployable present' do + given(:pipeline) { create(:ci_pipeline, project: project) } + given(:build) { create(:ci_build, pipeline: pipeline) } + + given(:deployment) do + create(:deployment, environment: environment, deployable: build) + end + + scenario 'does show build name' do + expect(page).to have_link("#{build.name} (##{build.id})") + end + + scenario 'does show re-deploy button' do + expect(page).to have_link('Re-deploy') + end + + scenario 'does not show stop button' do + expect(page).not_to have_link('Stop') + end + + context 'with manual action' do + given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production') } + + scenario 'does show a play button' do + expect(page).to have_link(manual.name.humanize) + end + + scenario 'does allow to play manual action' do + expect(manual).to be_skipped + expect{ click_link(manual.name.humanize) }.not_to change { Ci::Pipeline.count } + expect(page).to have_content(manual.name) + expect(manual.reload).to be_pending + end + + context 'with external_url' do + given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') } + given(:build) { create(:ci_build, pipeline: pipeline) } + given(:deployment) { create(:deployment, environment: environment, deployable: build) } + + scenario 'does show an external link button' do + expect(page).to have_link(nil, href: environment.external_url) + end + end + + context 'with stop action' do + given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') } + given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') } + + scenario 'does show stop button' do + expect(page).to have_link('Stop') + end + + scenario 'does allow to stop environment' do + click_link('Stop') + + expect(page).to have_content('close_app') + end + + context 'for reporter' do + let(:role) { :reporter } + + scenario 'does not show stop button' do + expect(page).not_to have_link('Stop') + end + end + end + end + end + end + end + + feature 'auto-close environment when branch is deleted' do + given(:project) { create(:project) } + + given!(:environment) do + create(:environment, :with_review_app, project: project, + ref: 'feature') + end + + scenario 'user visits environment page' do + visit_environment(environment) + + expect(page).to have_link('Stop') + end + + scenario 'user deletes the branch with running environment' do + visit namespace_project_branches_path(project.namespace, project) + + remove_branch_with_hooks(project, user, 'feature') do + page.within('.js-branch-feature') { find('a.btn-remove').click } + end + + visit_environment(environment) + + expect(page).to have_no_link('Stop') + end + + ## + # This is a workaround for problem described in #24543 + # + def remove_branch_with_hooks(project, user, branch) + params = { + oldrev: project.commit(branch).id, + newrev: Gitlab::Git::BLANK_SHA, + ref: "refs/heads/#{branch}" + } + + yield + + GitPushService.new(project, user, params).execute + end + end + + def visit_environment(environment) + visit namespace_project_environment_path(environment.project.namespace, + environment.project, + environment) + end +end diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb index b565586ee14..c7fe622c477 100644 --- a/spec/features/environments_spec.rb +++ b/spec/features/environments_spec.rb @@ -1,228 +1,147 @@ require 'spec_helper' -feature 'Environments', feature: true do +feature 'Environments page', :feature, :js do given(:project) { create(:empty_project) } given(:user) { create(:user) } given(:role) { :developer } background do - login_as(user) project.team << [user, role] + login_as(user) end - describe 'when showing environments' do - given!(:environment) { } - given!(:deployment) { } - given!(:manual) { } - - before do - visit namespace_project_environments_path(project.namespace, project) - end + given!(:environment) { } + given!(:deployment) { } + given!(:manual) { } - context 'shows two tabs' do - scenario 'shows "Available" and "Stopped" tab with links' do - expect(page).to have_link('Available') - expect(page).to have_link('Stopped') - end - end - - context 'without environments' do - scenario 'does show no environments' do - expect(page).to have_content('You don\'t have any environments right now.') - end + before do + visit_environments(project) + end - scenario 'does show 0 as counter for environments in both tabs' do - expect(page.find('.js-available-environments-count').text).to eq('0') - expect(page.find('.js-stopped-environments-count').text).to eq('0') - end + describe 'page tabs' do + scenario 'shows "Available" and "Stopped" tab with links' do + expect(page).to have_link('Available') + expect(page).to have_link('Stopped') end + end - context 'with environments' do - given(:environment) { create(:environment, project: project) } - - scenario 'does show environment name' do - expect(page).to have_link(environment.name) - end - - scenario 'does show number of available and stopped environments' do - expect(page.find('.js-available-environments-count').text).to eq('1') - expect(page.find('.js-stopped-environments-count').text).to eq('0') - end - - context 'without deployments' do - scenario 'does show no deployments' do - expect(page).to have_content('No deployments yet') - end - end - - context 'with deployments' do - given(:deployment) { create(:deployment, environment: environment) } - - scenario 'does show deployment SHA' do - expect(page).to have_link(deployment.short_sha) - end - - scenario 'does show deployment internal id' do - expect(page).to have_content(deployment.iid) - end - - context 'with build and manual actions' do - given(:pipeline) { create(:ci_pipeline, project: project) } - given(:build) { create(:ci_build, pipeline: pipeline) } - given(:deployment) { create(:deployment, environment: environment, deployable: build) } - given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production') } - - scenario 'does show a play button' do - expect(page).to have_link(manual.name.humanize) - end - - scenario 'does allow to play manual action' do - expect(manual).to be_skipped - expect{ click_link(manual.name.humanize) }.not_to change { Ci::Pipeline.count } - expect(page).to have_content(manual.name) - expect(manual.reload).to be_pending - end - - scenario 'does show build name and id' do - expect(page).to have_link("#{build.name} (##{build.id})") - end - - scenario 'does not show stop button' do - expect(page).not_to have_selector('.stop-env-link') - end - - scenario 'does not show external link button' do - expect(page).not_to have_css('external-url') - end - - context 'with external_url' do - given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') } - given(:build) { create(:ci_build, pipeline: pipeline) } - given(:deployment) { create(:deployment, environment: environment, deployable: build) } - - scenario 'does show an external link button' do - expect(page).to have_link(nil, href: environment.external_url) - end - end - - context 'with stop action' do - given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') } - given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') } - - scenario 'does show stop button' do - expect(page).to have_selector('.stop-env-link') - end - - scenario 'starts build when stop button clicked' do - first('.stop-env-link').click - - expect(page).to have_content('close_app') - end - - context 'for reporter' do - let(:role) { :reporter } - - scenario 'does not show stop button' do - expect(page).not_to have_selector('.stop-env-link') - end - end - end - end - end + context 'without environments' do + scenario 'does show no environments' do + expect(page).to have_content('You don\'t have any environments right now.') end - scenario 'does have a New environment button' do - expect(page).to have_link('New environment') + scenario 'does show 0 as counter for environments in both tabs' do + expect(page.find('.js-available-environments-count').text).to eq('0') + expect(page.find('.js-stopped-environments-count').text).to eq('0') end end describe 'when showing the environment' do given(:environment) { create(:environment, project: project) } - given!(:deployment) { } - given!(:manual) { } - before do - visit namespace_project_environment_path(project.namespace, project, environment) + scenario 'does show environment name' do + expect(page).to have_link(environment.name) + end + + scenario 'does show number of available and stopped environments' do + expect(page.find('.js-available-environments-count').text).to eq('1') + expect(page.find('.js-stopped-environments-count').text).to eq('0') end context 'without deployments' do scenario 'does show no deployments' do - expect(page).to have_content('You don\'t have any deployments right now.') + expect(page).to have_content('No deployments yet') end end context 'with deployments' do - given(:deployment) { create(:deployment, environment: environment) } + given(:project) { create(:project) } + + given(:deployment) do + create(:deployment, environment: environment, + sha: project.commit.id) + end scenario 'does show deployment SHA' do expect(page).to have_link(deployment.short_sha) end - scenario 'does not show a re-deploy button for deployment without build' do - expect(page).not_to have_link('Re-deploy') + scenario 'does show deployment internal id' do + expect(page).to have_content(deployment.iid) end - context 'with build' do + context 'with build and manual actions' do given(:pipeline) { create(:ci_pipeline, project: project) } given(:build) { create(:ci_build, pipeline: pipeline) } - given(:deployment) { create(:deployment, environment: environment, deployable: build) } - scenario 'does show build name' do - expect(page).to have_link("#{build.name} (##{build.id})") + given(:manual) do + create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production') end - scenario 'does show re-deploy button' do - expect(page).to have_link('Re-deploy') + given(:deployment) do + create(:deployment, environment: environment, + deployable: build, + sha: project.commit.id) end - scenario 'does not show stop button' do - expect(page).not_to have_link('Stop') + scenario 'does show a play button' do + find('.dropdown-play-icon-container').click + expect(page).to have_content(manual.name.humanize) end - context 'with manual action' do - given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy to production') } + scenario 'does allow to play manual action', js: true do + expect(manual).to be_skipped - scenario 'does show a play button' do - expect(page).to have_link(manual.name.humanize) - end + find('.dropdown-play-icon-container').click + expect(page).to have_content(manual.name.humanize) - scenario 'does allow to play manual action' do - expect(manual).to be_skipped - expect{ click_link(manual.name.humanize) }.not_to change { Ci::Pipeline.count } - expect(page).to have_content(manual.name) - expect(manual.reload).to be_pending - end + expect { click_link(manual.name.humanize) } + .not_to change { Ci::Pipeline.count } - context 'with external_url' do - given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') } - given(:build) { create(:ci_build, pipeline: pipeline) } - given(:deployment) { create(:deployment, environment: environment, deployable: build) } + expect(manual.reload).to be_pending + end - scenario 'does show an external link button' do - expect(page).to have_link(nil, href: environment.external_url) - end + scenario 'does show build name and id' do + expect(page).to have_link("#{build.name} ##{build.id}") + end + + scenario 'does not show stop button' do + expect(page).not_to have_selector('.stop-env-link') + end + + scenario 'does not show external link button' do + expect(page).not_to have_css('external-url') + end + + context 'with external_url' do + given(:environment) { create(:environment, project: project, external_url: 'https://git.gitlab.com') } + given(:build) { create(:ci_build, pipeline: pipeline) } + given(:deployment) { create(:deployment, environment: environment, deployable: build) } + + scenario 'does show an external link button' do + expect(page).to have_link(nil, href: environment.external_url) end + end - context 'with stop action' do - given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') } - given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') } + context 'with stop action' do + given(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'close_app') } + given(:deployment) { create(:deployment, environment: environment, deployable: build, on_stop: 'close_app') } - scenario 'does show stop button' do - expect(page).to have_link('Stop') - end + scenario 'does show stop button' do + expect(page).to have_selector('.stop-env-link') + end - scenario 'does allow to stop environment' do - click_link('Stop') + scenario 'starts build when stop button clicked' do + find('.stop-env-link').click - expect(page).to have_content('close_app') - end + expect(page).to have_content('close_app') + end - context 'for reporter' do - let(:role) { :reporter } + context 'for reporter' do + let(:role) { :reporter } - scenario 'does not show stop button' do - expect(page).not_to have_link('Stop') - end + scenario 'does not show stop button' do + expect(page).not_to have_selector('.stop-env-link') end end end @@ -230,9 +149,13 @@ feature 'Environments', feature: true do end end + scenario 'does have a New environment button' do + expect(page).to have_link('New environment') + end + describe 'when creating a new environment' do before do - visit namespace_project_environments_path(project.namespace, project) + visit_environments(project) end context 'when logged as developer' do @@ -271,4 +194,8 @@ feature 'Environments', feature: true do end end end + + def visit_environments(project) + visit namespace_project_environments_path(project.namespace, project) + end end diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index 6c938bdead8..3934c936f20 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -182,6 +182,20 @@ feature 'Expand and collapse diffs', js: true, feature: true do end end end + + context 'expanding a diff when symlink was converted to a regular file' do + let(:branch) { 'symlink-expand-diff' } + + it 'shows the content of the regular file' do + expect(page).to have_content('This diff is collapsed') + expect(page).to have_no_content('No longer a symlink') + + find('.click-to-expand').click + wait_for_ajax + + expect(page).to have_content('No longer a symlink') + end + end end context 'visiting a commit without collapsed diffs' do diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb new file mode 100644 index 00000000000..f6409e00f22 --- /dev/null +++ b/spec/features/global_search_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +feature 'Global search', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + project.team << [user, :master] + login_with(user) + end + + describe 'I search through the issues and I see pagination' do + before do + allow_any_instance_of(Gitlab::SearchResults).to receive(:per_page).and_return(1) + create_list(:issue, 2, project: project, title: 'initial') + end + + it "has a pagination" do + visit dashboard_projects_path + + fill_in "search", with: "initial" + click_button "Go" + + select_filter("Issues") + expect(page).to have_selector('.gl-pagination .page', count: 2) + end + end +end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb new file mode 100644 index 00000000000..476eca17a9d --- /dev/null +++ b/spec/features/groups/issues_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper' + +feature 'Group issues page', feature: true do + let(:path) { issues_group_path(group) } + let(:issuable) { create(:issue, project: project, title: "this is my created issuable")} + + include_examples 'project features apply to issuables', Issue +end diff --git a/spec/features/groups/members/owner_manages_access_requests_spec.rb b/spec/features/groups/members/owner_manages_access_requests_spec.rb index d811b05b0c3..dbe150823ba 100644 --- a/spec/features/groups/members/owner_manages_access_requests_spec.rb +++ b/spec/features/groups/members/owner_manages_access_requests_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Groups > Members > Owner manages access requests', feature: true do let(:user) { create(:user) } let(:owner) { create(:user) } - let(:group) { create(:group, :public) } + let(:group) { create(:group, :public, :access_requestable) } background do group.request_access(user) diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb index b3baa2ab57c..d8c9c487996 100644 --- a/spec/features/groups/members/user_requests_access_spec.rb +++ b/spec/features/groups/members/user_requests_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Groups > Members > User requests access', feature: true do let(:user) { create(:user) } let(:owner) { create(:user) } - let(:group) { create(:group, :public) } + let(:group) { create(:group, :public, :access_requestable) } let!(:project) { create(:project, :private, namespace: group) } background do diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb new file mode 100644 index 00000000000..a2791b57544 --- /dev/null +++ b/spec/features/groups/merge_requests_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper' + +feature 'Group merge requests page', feature: true do + let(:path) { merge_requests_group_path(group) } + let(:issuable) { create(:merge_request, source_project: project, target_project: project, title: "this is my created issuable")} + + include_examples 'project features apply to issuables', MergeRequest +end diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index 13bfe90302c..4b19886274e 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -80,7 +80,7 @@ feature 'Group', feature: true do visit path - expect(page).to have_css('.description > p > strong') + expect(page).to have_css('.group-home-desc > p > strong') end it 'passes through html-pipeline' do @@ -88,7 +88,7 @@ feature 'Group', feature: true do visit path - expect(page).to have_css('.description > p > img') + expect(page).to have_css('.group-home-desc > p > img') end it 'sanitizes unwanted tags' do @@ -96,7 +96,7 @@ feature 'Group', feature: true do visit path - expect(page).not_to have_css('.description h1') + expect(page).not_to have_css('.group-home-desc h1') end it 'permits `rel` attribute on links' do @@ -104,7 +104,7 @@ feature 'Group', feature: true do visit path - expect(page).to have_css('.description a[rel]') + expect(page).to have_css('.group-home-desc a[rel]') end end end diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb index ef00f209998..efb53026449 100644 --- a/spec/features/issues/award_emoji_spec.rb +++ b/spec/features/issues/award_emoji_spec.rb @@ -3,72 +3,83 @@ require 'rails_helper' describe 'Awards Emoji', feature: true do include WaitForAjax - let!(:project) { create(:project) } + let!(:project) { create(:project, :public) } let!(:user) { create(:user) } - - before do - project.team << [user, :master] - login_as(user) + let(:issue) do + create(:issue, + assignee: @user, + project: project) end - describe 'Click award emoji from issue#show' do - let!(:issue) do - create(:issue, - assignee: @user, - project: project) - end - - let!(:note) { create(:note_on_issue, noteable: issue, project: issue.project, note: "Hello world") } - + context 'authorized user' do before do - visit namespace_project_issue_path(project.namespace, project, issue) + project.team << [user, :master] + login_as(user) end - it 'increments the thumbsdown emoji', js: true do - find('[data-emoji="thumbsdown"]').click - wait_for_ajax - expect(thumbsdown_emoji).to have_text("1") - end + describe 'Click award emoji from issue#show' do + let!(:note) { create(:note_on_issue, noteable: issue, project: issue.project, note: "Hello world") } - context 'click the thumbsup emoji' do - it 'increments the thumbsup emoji', js: true do - find('[data-emoji="thumbsup"]').click - wait_for_ajax - expect(thumbsup_emoji).to have_text("1") + before do + visit namespace_project_issue_path(project.namespace, project, issue) end - it 'decrements the thumbsdown emoji', js: true do - expect(thumbsdown_emoji).to have_text("0") - end - end - - context 'click the thumbsdown emoji' do it 'increments the thumbsdown emoji', js: true do find('[data-emoji="thumbsdown"]').click wait_for_ajax expect(thumbsdown_emoji).to have_text("1") end - it 'decrements the thumbsup emoji', js: true do - expect(thumbsup_emoji).to have_text("0") + context 'click the thumbsup emoji' do + it 'increments the thumbsup emoji', js: true do + find('[data-emoji="thumbsup"]').click + wait_for_ajax + expect(thumbsup_emoji).to have_text("1") + end + + it 'decrements the thumbsdown emoji', js: true do + expect(thumbsdown_emoji).to have_text("0") + end end - end - it 'toggles the smiley emoji on a note', js: true do - toggle_smiley_emoji(true) + context 'click the thumbsdown emoji' do + it 'increments the thumbsdown emoji', js: true do + find('[data-emoji="thumbsdown"]').click + wait_for_ajax + expect(thumbsdown_emoji).to have_text("1") + end - within('.note-awards') do - expect(find(emoji_counter)).to have_text("1") + it 'decrements the thumbsup emoji', js: true do + expect(thumbsup_emoji).to have_text("0") + end end - toggle_smiley_emoji(false) + it 'toggles the smiley emoji on a note', js: true do + toggle_smiley_emoji(true) + + within('.note-awards') do + expect(find(emoji_counter)).to have_text("1") + end + + toggle_smiley_emoji(false) - within('.note-awards') do - expect(page).not_to have_selector(emoji_counter) + within('.note-awards') do + expect(page).not_to have_selector(emoji_counter) + end end end end + context 'unauthorized user', js: true do + before do + visit namespace_project_issue_path(project.namespace, project, issue) + end + + it 'has disabled emoji button' do + expect(first('.award-control')[:disabled]).to be(true) + end + end + def thumbsup_emoji page.all(emoji_counter).first end diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb index 2798db92f0f..0d19563d628 100644 --- a/spec/features/issues/filter_issues_spec.rb +++ b/spec/features/issues/filter_issues_spec.rb @@ -3,8 +3,8 @@ require 'rails_helper' describe 'Filter issues', feature: true do include WaitForAjax - let!(:project) { create(:project) } let!(:group) { create(:group) } + let!(:project) { create(:project, group: group) } let!(:user) { create(:user)} let!(:milestone) { create(:milestone, project: project) } let!(:label) { create(:label, project: project) } @@ -127,7 +127,7 @@ describe 'Filter issues', feature: true do expect(page).to have_content wontfix.title end - find('body').click + find('.dropdown-menu-close-icon').click expect(find('.filtered-labels')).to have_content(wontfix.title) @@ -135,7 +135,7 @@ describe 'Filter issues', feature: true do wait_for_ajax find('.dropdown-menu-labels a', text: label.title).click - find('body').click + find('.dropdown-menu-close-icon').click expect(find('.filtered-labels')).to have_content(wontfix.title) expect(find('.filtered-labels')).to have_content(label.title) @@ -150,8 +150,8 @@ describe 'Filter issues', feature: true do it "selects and unselects `won't fix`" do find('.dropdown-menu-labels a', text: wontfix.title).click find('.dropdown-menu-labels a', text: wontfix.title).click - # Close label dropdown to load - find('body').click + + find('.dropdown-menu-close-icon').click expect(page).not_to have_css('.filtered-labels') end end diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index 4b1aec8bf71..bc068b5e7e0 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -1,7 +1,9 @@ require 'rails_helper' feature 'Issue Sidebar', feature: true do - let(:project) { create(:project) } + include WaitForAjax + + let(:project) { create(:project, :public) } let(:issue) { create(:issue, project: project) } let!(:user) { create(:user)} @@ -10,6 +12,37 @@ feature 'Issue Sidebar', feature: true do login_as(user) end + context 'assignee', js: true do + let(:user2) { create(:user) } + let(:issue2) { create(:issue, project: project, author: user2) } + + before do + project.team << [user, :developer] + visit_issue(project, issue2) + + find('.block.assignee .edit-link').click + + wait_for_ajax + end + + it 'shows author in assignee dropdown' do + page.within '.dropdown-menu-user' do + expect(page).to have_content(user2.name) + end + end + + it 'shows author when filtering assignee dropdown' do + page.within '.dropdown-menu-user' do + find('.dropdown-input-field').native.send_keys user2.name + sleep 1 # Required to wait for end of input delay + + wait_for_ajax + + expect(page).to have_content(user2.name) + end + end + end + context 'as a allowed user' do before do project.team << [user, :developer] diff --git a/spec/features/issues/new_branch_button_spec.rb b/spec/features/issues/new_branch_button_spec.rb index fb0c4704285..ab901e74617 100644 --- a/spec/features/issues/new_branch_button_spec.rb +++ b/spec/features/issues/new_branch_button_spec.rb @@ -18,22 +18,24 @@ feature 'Start new branch from an issue', feature: true do end context "when there is a referenced merge request" do - let(:note) do - create(:note, :on_issue, :system, project: project, + let!(:note) do + create(:note, :on_issue, :system, project: project, noteable: issue, note: "Mentioned in !#{referenced_mr.iid}") end + let(:referenced_mr) do create(:merge_request, :simple, source_project: project, target_project: project, description: "Fixes ##{issue.iid}", author: user) end before do - issue.notes << note + referenced_mr.cache_merge_request_closes_issues!(user) visit namespace_project_issue_path(project.namespace, project, issue) end it "hides the new branch button", js: true do + expect(page).to have_css('#new-branch .unavailable') expect(page).not_to have_css('#new-branch .available') expect(page).to have_content /1 Related Merge Request/ end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index b504329656f..5c958455604 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -3,6 +3,7 @@ require 'spec_helper' describe 'Issues', feature: true do include IssueHelpers include SortingHelper + include WaitForAjax let(:project) { create(:project) } @@ -368,6 +369,28 @@ describe 'Issues', feature: true do end end + describe 'when I want to reset my incoming email token' do + let(:project1) { create(:project, namespace: @user.namespace) } + let(:issue) { create(:issue, project: project1) } + + before do + allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true) + project1.team << [@user, :master] + project1.issues << issue + visit namespace_project_issues_path(@user.namespace, project1) + end + + it 'changes incoming email address token', js: true do + find('.issue-email-modal-btn').click + previous_token = find('input#issue_email').value + + find('.incoming-email-token-reset').click + wait_for_ajax + + expect(find('input#issue_email').value).not_to eq(previous_token) + end + end + describe 'update labels from issue#show', js: true do let(:issue) { create(:issue, project: project, author: @user, assignee: @user) } let!(:label) { create(:label, project: project) } @@ -553,9 +576,12 @@ describe 'Issues', feature: true do end end - xdescribe 'new issue by email' do + describe 'new issue by email' do shared_examples 'show the email in the modal' do + let(:issue) { create(:issue, project: project) } + before do + project.issues << issue stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab") visit namespace_project_issues_path(project.namespace, project) diff --git a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb new file mode 100644 index 00000000000..7f11db3c417 --- /dev/null +++ b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +feature 'Check if mergeable with unresolved discussions', js: true, feature: true do + let(:user) { create(:user) } + let(:project) { create(:project) } + let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) } + + before do + login_as user + project.team << [user, :master] + end + + context 'when project.only_allow_merge_if_all_discussions_are_resolved == true' do + before do + project.update_column(:only_allow_merge_if_all_discussions_are_resolved, true) + end + + context 'with unresolved discussions' do + it 'does not allow to merge' do + visit_merge_request(merge_request) + + expect(page).not_to have_button 'Accept Merge Request' + expect(page).to have_content('This merge request has unresolved discussions') + end + end + + context 'with all discussions resolved' do + before do + merge_request.discussions.each { |d| d.resolve!(user) } + end + + it 'allows MR to be merged' do + visit_merge_request(merge_request) + + expect(page).to have_button 'Accept Merge Request' + end + end + end + + context 'when project.only_allow_merge_if_all_discussions_are_resolved == false' do + before do + project.update_column(:only_allow_merge_if_all_discussions_are_resolved, false) + end + + context 'with unresolved discussions' do + it 'does not allow to merge' do + visit_merge_request(merge_request) + + expect(page).to have_button 'Accept Merge Request' + end + end + + context 'with all discussions resolved' do + before do + merge_request.discussions.each { |d| d.resolve!(user) } + end + + it 'allows MR to be merged' do + visit_merge_request(merge_request) + + expect(page).to have_button 'Accept Merge Request' + end + end + end + + def visit_merge_request(merge_request) + visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) + end +end diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index c68e1ea4af9..702869b6e8b 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -67,4 +67,21 @@ feature 'Create New Merge Request', feature: true, js: true do expect(page).to have_content('Source branch "non-exist-source" does not exist') expect(page).to have_content('Target branch "non-exist-target" does not exist') end + + context 'when a branch contains commits that both delete and add the same image' do + it 'renders the diff successfully' do + visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_branch: 'master', source_branch: 'deleted-image-test' }) + + click_link "Changes" + + expect(page).to have_content "6049019_460s.jpg" + end + end + + # Isolates a regression (see #24627) + it 'does not show error messages on initial form' do + visit new_namespace_project_merge_request_path(project.namespace, project) + expect(page).not_to have_selector('#error_explanation') + expect(page).not_to have_content('The form contains the following error') + end end diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb new file mode 100644 index 00000000000..778b3a90cf3 --- /dev/null +++ b/spec/features/merge_requests/deleted_source_branch_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe 'Deleted source branch', feature: true, js: true do + let(:user) { create(:user) } + let(:merge_request) { create(:merge_request) } + + before do + login_as user + merge_request.project.team << [user, :master] + merge_request.update!(source_branch: 'this-branch-does-not-exist') + visit namespace_project_merge_request_path( + merge_request.project.namespace, + merge_request.project, merge_request + ) + end + + it 'shows a message about missing source branch' do + expect(page).to have_content( + 'Source branch this-branch-does-not-exist does not exist' + ) + end + + it 'hides Discussion, Commits and Changes tabs' do + within '.merge-request-details' do + expect(page).to have_no_content('Discussion') + expect(page).to have_no_content('Commits') + expect(page).to have_no_content('Changes') + end + end +end diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb index 5e6d8467217..eab64bd4b54 100644 --- a/spec/features/merge_requests/diff_notes_resolve_spec.rb +++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb @@ -69,8 +69,6 @@ feature 'Diff notes resolve', feature: true, js: true do page.within '.diff-content .note' do expect(page).to have_selector('.line-resolve-btn.is-active') - - expect(find('.line-resolve-btn')['data-original-title']).to eq("Resolved by #{user.name}") end page.within '.line-resolve-all-container' do @@ -203,7 +201,7 @@ feature 'Diff notes resolve', feature: true, js: true do expect(first('.line-resolve-btn')['data-original-title']).to eq("Resolved by #{user.name}") end - expect(page).to have_content('Last updated') + expect(page).not_to have_content('Last updated') page.within '.line-resolve-all-container' do expect(page).to have_content('0/1 discussion resolved') diff --git a/spec/features/merge_requests/merge_request_versions_spec.rb b/spec/features/merge_requests/merge_request_versions_spec.rb index 23cee891bac..09451f41de4 100644 --- a/spec/features/merge_requests/merge_request_versions_spec.rb +++ b/spec/features/merge_requests/merge_request_versions_spec.rb @@ -3,11 +3,12 @@ require 'spec_helper' feature 'Merge Request versions', js: true, feature: true do let(:merge_request) { create(:merge_request, importing: true) } let(:project) { merge_request.source_project } + let!(:merge_request_diff1) { merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } + let!(:merge_request_diff2) { merge_request.merge_request_diffs.create(head_commit_sha: nil) } + let!(:merge_request_diff3) { merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') } before do login_as :admin - merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') - merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request) end @@ -53,7 +54,7 @@ feature 'Merge Request versions', js: true, feature: true do project.namespace, project, merge_request.iid, - diff_id: 2, + diff_id: merge_request_diff3.id, start_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' ) end diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb index c3c3ab33872..8eceaad2457 100644 --- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -73,7 +73,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do expect(page).to have_button "Merge When Build Succeeds" visit_merge_request(merge_request) # refresh the page - expect(page).to have_content "Canceled the automatic merge" + expect(page).to have_content "canceled the automatic merge" end it "allows the user to remove the source branch" do @@ -101,7 +101,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do expect(page).not_to have_link "Merge When Build Succeeds" end end - + def visit_merge_request(merge_request) visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) end diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb index 80e8b8fc642..1ec3103feef 100644 --- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds.rb +++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' feature 'Only allow merge requests to be merged if the build succeeds', feature: true do - let(:project) { create(:project, :public) } - let(:merge_request) { create(:merge_request_with_diffs, source_project: project) } + let(:merge_request) { create(:merge_request_with_diffs) } + let(:project) { merge_request.target_project } before do login_as merge_request.author @@ -19,7 +19,13 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature: end context 'when project has CI enabled' do - let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch) } + given!(:pipeline) do + create(:ci_empty_pipeline, + project: project, + sha: merge_request.diff_head_sha, + ref: merge_request.source_branch, + status: status) + end context 'when merge requests can only be merged if the build succeeds' do before do @@ -27,7 +33,7 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature: end context 'when CI is running' do - before { pipeline.update_column(:status, :running) } + given(:status) { :running } it 'does not allow to merge immediately' do visit_merge_request(merge_request) @@ -38,7 +44,18 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature: end context 'when CI failed' do - before { pipeline.update_column(:status, :failed) } + given(:status) { :failed } + + it 'does not allow MR to be merged' do + visit_merge_request(merge_request) + + expect(page).not_to have_button 'Accept Merge Request' + expect(page).to have_content('Please retry the build or push a new commit to fix the failure.') + end + end + + context 'when CI canceled' do + given(:status) { :canceled } it 'does not allow MR to be merged' do visit_merge_request(merge_request) @@ -49,7 +66,17 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature: end context 'when CI succeeded' do - before { pipeline.update_column(:status, :success) } + given(:status) { :success } + + it 'allows MR to be merged' do + visit_merge_request(merge_request) + + expect(page).to have_button 'Accept Merge Request' + end + end + + context 'when CI skipped' do + given(:status) { :skipped } it 'allows MR to be merged' do visit_merge_request(merge_request) @@ -65,7 +92,7 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature: end context 'when CI is running' do - before { pipeline.update_column(:status, :running) } + given(:status) { :running } it 'allows MR to be merged immediately', js: true do visit_merge_request(merge_request) @@ -78,7 +105,7 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature: end context 'when CI failed' do - before { pipeline.update_column(:status, :failed) } + given(:status) { :failed } it 'allows MR to be merged' do visit_merge_request(merge_request) @@ -88,7 +115,7 @@ feature 'Only allow merge requests to be merged if the build succeeds', feature: end context 'when CI succeeded' do - before { pipeline.update_column(:status, :success) } + given(:status) { :success } it 'allows MR to be merged' do visit_merge_request(merge_request) diff --git a/spec/features/merge_requests/toggle_whitespace_changes.rb b/spec/features/merge_requests/toggle_whitespace_changes_spec.rb index 0f98737b700..0f98737b700 100644 --- a/spec/features/merge_requests/toggle_whitespace_changes.rb +++ b/spec/features/merge_requests/toggle_whitespace_changes_spec.rb diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index c3d8c349ca4..7a562b5e03d 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -32,4 +32,33 @@ describe 'Profile account page', feature: true do expect(current_path).to eq(profile_account_path) end end + + describe 'when I reset private token' do + before do + visit profile_account_path + end + + it 'resets private token' do + previous_token = find("#private-token").value + + click_link('Reset private token') + + expect(find('#private-token').value).not_to eq(previous_token) + end + end + + describe 'when I reset incoming email token' do + before do + allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true) + visit profile_account_path + end + + it 'resets incoming email token' do + previous_token = find('#incoming-email-token').value + + click_link('Reset incoming email token') + + expect(find('#incoming-email-token').value).not_to eq(previous_token) + end + end end diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb new file mode 100644 index 00000000000..6f6f7029c0b --- /dev/null +++ b/spec/features/profiles/chat_names_spec.rb @@ -0,0 +1,77 @@ +require 'rails_helper' + +feature 'Profile > Chat', feature: true do + given(:user) { create(:user) } + given(:service) { create(:service) } + + before do + login_as(user) + end + + describe 'uses authorization link' do + given(:params) do + { team_id: 'T00', team_domain: 'my_chat_team', user_id: 'U01', user_name: 'my_chat_user' } + end + given!(:authorize_url) { ChatNames::AuthorizeUserService.new(service, params).execute } + given(:authorize_path) { URI.parse(authorize_url).request_uri } + + before do + visit authorize_path + end + + context 'clicks authorize' do + before do + click_button 'Authorize' + end + + scenario 'goes to list of chat names and see chat account' do + expect(page.current_path).to eq(profile_chat_names_path) + expect(page).to have_content('my_chat_team') + expect(page).to have_content('my_chat_user') + end + + scenario 'second use of link is denied' do + visit authorize_path + + expect(page).to have_http_status(:not_found) + end + end + + context 'clicks deny' do + before do + click_button 'Deny' + end + + scenario 'goes to list of chat names and do not see chat account' do + expect(page.current_path).to eq(profile_chat_names_path) + expect(page).not_to have_content('my_chat_team') + expect(page).not_to have_content('my_chat_user') + end + + scenario 'second use of link is denied' do + visit authorize_path + + expect(page).to have_http_status(:not_found) + end + end + end + + describe 'visits chat accounts' do + given!(:chat_name) { create(:chat_name, user: user, service: service) } + + before do + visit profile_chat_names_path + end + + scenario 'sees chat user' do + expect(page).to have_content(chat_name.team_domain) + expect(page).to have_content(chat_name.chat_name) + end + + scenario 'removes chat account' do + click_link 'Remove' + + expect(page).to have_content("You don't have any active chat names.") + end + end +end diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb index a8022a5361f..a0ccc472d11 100644 --- a/spec/features/projects/builds_spec.rb +++ b/spec/features/projects/builds_spec.rb @@ -1,52 +1,59 @@ require 'spec_helper' require 'tempfile' -describe "Builds" do - let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } +feature 'Builds', :feature do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:pipeline) { create(:ci_pipeline, project: project) } + + let(:build) { create(:ci_build, :trace, pipeline: pipeline) } + let(:build2) { create(:ci_build) } + + let(:artifacts_file) do + fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') + end before do - login_as(:user) - @commit = FactoryGirl.create :ci_pipeline - @build = FactoryGirl.create :ci_build, :trace, pipeline: @commit - @build2 = FactoryGirl.create :ci_build - @project = @commit.project - @project.team << [@user, :developer] + project.team << [user, :developer] + login_as(user) end describe "GET /:project/builds" do + let!(:build) { create(:ci_build, pipeline: pipeline) } + context "Pending scope" do before do - visit namespace_project_builds_path(@project.namespace, @project, scope: :pending) + visit namespace_project_builds_path(project.namespace, project, scope: :pending) end it "shows Pending tab builds" do expect(page).to have_link 'Cancel running' expect(page).to have_selector('.nav-links li.active', text: 'Pending') - expect(page).to have_content @build.short_sha - expect(page).to have_content @build.ref - expect(page).to have_content @build.name + expect(page).to have_content build.short_sha + expect(page).to have_content build.ref + expect(page).to have_content build.name end end context "Running scope" do before do - @build.run! - visit namespace_project_builds_path(@project.namespace, @project, scope: :running) + build.run! + visit namespace_project_builds_path(project.namespace, project, scope: :running) end it "shows Running tab builds" do expect(page).to have_selector('.nav-links li.active', text: 'Running') expect(page).to have_link 'Cancel running' - expect(page).to have_content @build.short_sha - expect(page).to have_content @build.ref - expect(page).to have_content @build.name + expect(page).to have_content build.short_sha + expect(page).to have_content build.ref + expect(page).to have_content build.name end end context "Finished scope" do before do - @build.run! - visit namespace_project_builds_path(@project.namespace, @project, scope: :finished) + build.run! + visit namespace_project_builds_path(project.namespace, project, scope: :finished) end it "shows Finished tab builds" do @@ -58,15 +65,15 @@ describe "Builds" do context "All builds" do before do - @project.builds.running_or_pending.each(&:success) - visit namespace_project_builds_path(@project.namespace, @project) + project.builds.running_or_pending.each(&:success) + visit namespace_project_builds_path(project.namespace, project) end it "shows All tab builds" do expect(page).to have_selector('.nav-links li.active', text: 'All') - expect(page).to have_content @build.short_sha - expect(page).to have_content @build.ref - expect(page).to have_content @build.name + expect(page).to have_content build.short_sha + expect(page).to have_content build.ref + expect(page).to have_content build.name expect(page).not_to have_link 'Cancel running' end end @@ -74,17 +81,17 @@ describe "Builds" do describe "POST /:project/builds/:id/cancel_all" do before do - @build.run! - visit namespace_project_builds_path(@project.namespace, @project) + build.run! + visit namespace_project_builds_path(project.namespace, project) click_link "Cancel running" end it 'shows all necessary content' do expect(page).to have_selector('.nav-links li.active', text: 'All') expect(page).to have_content 'canceled' - expect(page).to have_content @build.short_sha - expect(page).to have_content @build.ref - expect(page).to have_content @build.name + expect(page).to have_content build.short_sha + expect(page).to have_content build.ref + expect(page).to have_content build.name expect(page).not_to have_link 'Cancel running' end end @@ -92,20 +99,20 @@ describe "Builds" do describe "GET /:project/builds/:id" do context "Build from project" do before do - visit namespace_project_build_path(@project.namespace, @project, @build) + visit namespace_project_build_path(project.namespace, project, build) end it 'shows commit`s data' do expect(page.status_code).to eq(200) - expect(page).to have_content @commit.sha[0..7] - expect(page).to have_content @commit.git_commit_message - expect(page).to have_content @commit.git_author_name + expect(page).to have_content pipeline.sha[0..7] + expect(page).to have_content pipeline.git_commit_message + expect(page).to have_content pipeline.git_author_name end end context "Build from other project" do before do - visit namespace_project_build_path(@project.namespace, @project, @build2) + visit namespace_project_build_path(project.namespace, project, build2) end it { expect(page.status_code).to eq(404) } @@ -113,8 +120,8 @@ describe "Builds" do context "Download artifacts" do before do - @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@project.namespace, @project, @build) + build.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_build_path(project.namespace, project, build) end it 'has button to download artifacts' do @@ -124,8 +131,8 @@ describe "Builds" do context 'Artifacts expire date' do before do - @build.update_attributes(artifacts_file: artifacts_file, artifacts_expire_at: expire_at) - visit namespace_project_build_path(@project.namespace, @project, @build) + build.update_attributes(artifacts_file: artifacts_file, artifacts_expire_at: expire_at) + visit namespace_project_build_path(project.namespace, project, build) end context 'no expire date defined' do @@ -158,10 +165,10 @@ describe "Builds" do end end - context 'Build raw trace' do + feature 'Raw trace' do before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) + build.run! + visit namespace_project_build_path(project.namespace, project, build) end it do @@ -169,11 +176,43 @@ describe "Builds" do end end - describe 'Variables' do + feature 'HTML trace', :js do + before do + build.run! + + visit namespace_project_build_path(project.namespace, project, build) + end + + context 'when build has an initial trace' do + it 'loads build trace' do + expect(page).to have_content 'BUILD TRACE' + + build.append_trace(' and more trace', 11) + + expect(page).to have_content 'BUILD TRACE and more trace' + end + end + + context 'when build does not have an initial trace' do + let(:build) { create(:ci_build, pipeline: pipeline) } + + it 'loads new trace' do + build.append_trace('build trace', 0) + + expect(page).to have_content 'build trace' + end + end + end + + feature 'Variables' do + let(:trigger_request) { create(:ci_trigger_request_with_variables) } + + let(:build) do + create :ci_build, pipeline: pipeline, trigger_request: trigger_request + end + before do - @trigger_request = create :ci_trigger_request_with_variables - @build = create :ci_build, pipeline: @commit, trigger_request: @trigger_request - visit namespace_project_build_path(@project.namespace, @project, @build) + visit namespace_project_build_path(project.namespace, project, build) end it 'shows variable key and value after click', js: true do @@ -193,8 +232,8 @@ describe "Builds" do describe "POST /:project/builds/:id/cancel" do context "Build from project" do before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) + build.run! + visit namespace_project_build_path(project.namespace, project, build) click_link "Cancel" end @@ -207,9 +246,9 @@ describe "Builds" do context "Build from other project" do before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) - page.driver.post(cancel_namespace_project_build_path(@project.namespace, @project, @build2)) + build.run! + visit namespace_project_build_path(project.namespace, project, build) + page.driver.post(cancel_namespace_project_build_path(project.namespace, project, build2)) end it { expect(page.status_code).to eq(404) } @@ -219,8 +258,8 @@ describe "Builds" do describe "POST /:project/builds/:id/retry" do context "Build from project" do before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) + build.run! + visit namespace_project_build_path(project.namespace, project, build) click_link 'Cancel' page.within('.build-header') do click_link 'Retry build' @@ -238,10 +277,10 @@ describe "Builds" do context "Build from other project" do before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) + build.run! + visit namespace_project_build_path(project.namespace, project, build) click_link 'Cancel' - page.driver.post(retry_namespace_project_build_path(@project.namespace, @project, @build2)) + page.driver.post(retry_namespace_project_build_path(project.namespace, project, build2)) end it { expect(page).to have_http_status(404) } @@ -249,13 +288,13 @@ describe "Builds" do context "Build that current user is not allowed to retry" do before do - @build.run! - @build.cancel! - @project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + build.run! + build.cancel! + project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC) logout_direct login_with(create(:user)) - visit namespace_project_build_path(@project.namespace, @project, @build) + visit namespace_project_build_path(project.namespace, project, build) end it 'does not show the Retry button' do @@ -268,15 +307,15 @@ describe "Builds" do describe "GET /:project/builds/:id/download" do before do - @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@project.namespace, @project, @build) + build.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_build_path(project.namespace, project, build) click_link 'Download' end context "Build from other project" do before do - @build2.update_attributes(artifacts_file: artifacts_file) - visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build2) + build2.update_attributes(artifacts_file: artifacts_file) + visit download_namespace_project_build_artifacts_path(project.namespace, project, build2) end it { expect(page.status_code).to eq(404) } @@ -288,23 +327,23 @@ describe "Builds" do context 'build from project' do before do Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) + build.run! + visit namespace_project_build_path(project.namespace, project, build) page.within('.js-build-sidebar') { click_link 'Raw' } end it 'sends the right headers' do expect(page.status_code).to eq(200) expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') - expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace) + expect(page.response_headers['X-Sendfile']).to eq(build.path_to_trace) end end context 'build from other project' do before do Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build2.run! - visit raw_namespace_project_build_path(@project.namespace, @project, @build2) + build2.run! + visit raw_namespace_project_build_path(project.namespace, project, build2) end it 'sends the right headers' do @@ -325,8 +364,8 @@ describe "Builds" do context 'when build has trace in file' do before do Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) + build.run! + visit namespace_project_build_path(project.namespace, project, build) allow_any_instance_of(Project).to receive(:ci_id).and_return(nil) allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(existing_file) @@ -345,8 +384,8 @@ describe "Builds" do context 'when build has trace in old file' do before do Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) + build.run! + visit namespace_project_build_path(project.namespace, project, build) allow_any_instance_of(Project).to receive(:ci_id).and_return(999) allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(non_existing_file) @@ -365,8 +404,8 @@ describe "Builds" do context 'when build has trace in DB' do before do Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) + build.run! + visit namespace_project_build_path(project.namespace, project, build) allow_any_instance_of(Project).to receive(:ci_id).and_return(nil) allow_any_instance_of(Ci::Build).to receive(:path_to_trace).and_return(non_existing_file) @@ -385,7 +424,7 @@ describe "Builds" do describe "GET /:project/builds/:id/trace.json" do context "Build from project" do before do - visit trace_namespace_project_build_path(@project.namespace, @project, @build, format: :json) + visit trace_namespace_project_build_path(project.namespace, project, build, format: :json) end it { expect(page.status_code).to eq(200) } @@ -393,7 +432,7 @@ describe "Builds" do context "Build from other project" do before do - visit trace_namespace_project_build_path(@project.namespace, @project, @build2, format: :json) + visit trace_namespace_project_build_path(project.namespace, project, build2, format: :json) end it { expect(page.status_code).to eq(404) } @@ -403,7 +442,7 @@ describe "Builds" do describe "GET /:project/builds/:id/status" do context "Build from project" do before do - visit status_namespace_project_build_path(@project.namespace, @project, @build) + visit status_namespace_project_build_path(project.namespace, project, build) end it { expect(page.status_code).to eq(200) } @@ -411,7 +450,7 @@ describe "Builds" do context "Build from other project" do before do - visit status_namespace_project_build_path(@project.namespace, @project, @build2) + visit status_namespace_project_build_path(project.namespace, project, build2) end it { expect(page.status_code).to eq(404) } diff --git a/spec/features/projects/commits/cherry_pick_spec.rb b/spec/features/projects/commits/cherry_pick_spec.rb index e45e3a36d01..d46d9e9399e 100644 --- a/spec/features/projects/commits/cherry_pick_spec.rb +++ b/spec/features/projects/commits/cherry_pick_spec.rb @@ -64,7 +64,7 @@ describe 'Cherry-pick Commits' do context "I cherry-pick a commit from a different branch", js: true do it do - find('.commit-action-buttons a.dropdown-toggle').click + find('.header-action-buttons a.dropdown-toggle').click find(:css, "a[href='#modal-cherry-pick-commit']").click page.within('#modal-cherry-pick-commit') do diff --git a/spec/features/projects/files/browse_files_spec.rb b/spec/features/projects/files/browse_files_spec.rb new file mode 100644 index 00000000000..69295e450d0 --- /dev/null +++ b/spec/features/projects/files/browse_files_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +feature 'user checks git blame', feature: true do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + login_with(user) + visit namespace_project_tree_path(project.namespace, project, project.default_branch) + end + + scenario "can see blame of '.gitignore'" do + click_link ".gitignore" + click_link 'Blame' + + expect(page).to have_content "*.rb" + expect(page).to have_content "Dmitriy Zaporozhets" + expect(page).to have_content "Initial commit" + end +end diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb new file mode 100644 index 00000000000..3130d87fba5 --- /dev/null +++ b/spec/features/projects/labels/subscription_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +feature 'Labels subscription', feature: true do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:empty_project, :public, namespace: group) } + let!(:bug) { create(:label, project: project, title: 'bug') } + let!(:feature) { create(:group_label, group: group, title: 'feature') } + + context 'when signed in' do + before do + project.team << [user, :developer] + login_as user + end + + scenario 'users can subscribe/unsubscribe to labels', js: true do + visit namespace_project_labels_path(project.namespace, project) + + expect(page).to have_content('bug') + expect(page).to have_content('feature') + + within "#project_label_#{bug.id}" do + expect(page).not_to have_button 'Unsubscribe' + + click_button 'Subscribe' + + expect(page).not_to have_button 'Subscribe' + expect(page).to have_button 'Unsubscribe' + + click_button 'Unsubscribe' + + expect(page).to have_button 'Subscribe' + expect(page).not_to have_button 'Unsubscribe' + end + + within "#group_label_#{feature.id}" do + expect(page).not_to have_button 'Unsubscribe' + + click_link_on_dropdown('Group level') + + expect(page).not_to have_selector('.dropdown-group-label') + expect(page).to have_button 'Unsubscribe' + + click_button 'Unsubscribe' + + expect(page).to have_selector('.dropdown-group-label') + + click_link_on_dropdown('Project level') + + expect(page).not_to have_selector('.dropdown-group-label') + expect(page).to have_button 'Unsubscribe' + end + end + end + + context 'when not signed in' do + it 'users can not subscribe/unsubscribe to labels' do + visit namespace_project_labels_path(project.namespace, project) + + expect(page).to have_content 'bug' + expect(page).to have_content 'feature' + expect(page).not_to have_button('Subscribe') + expect(page).not_to have_selector('.dropdown-group-label') + end + end + + def click_link_on_dropdown(text) + find('.dropdown-group-label').click + + page.within('.dropdown-group-label') do + find('a.js-subscribe-button', text: text).click + end + end +end diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb index c4ed92d2780..4973e0aee85 100644 --- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb +++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' feature 'Projects > Members > Group requester cannot request access to project', feature: true do let(:user) { create(:user) } let(:owner) { create(:user) } - let(:group) { create(:group, :public) } - let(:project) { create(:project, :public, namespace: group) } + let(:group) { create(:group, :public, :access_requestable) } + let(:project) { create(:project, :public, :access_requestable, namespace: group) } background do group.add_owner(owner) diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb index d15376931c3..143390b71cd 100644 --- a/spec/features/projects/members/master_manages_access_requests_spec.rb +++ b/spec/features/projects/members/master_manages_access_requests_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Projects > Members > Master manages access requests', feature: true do let(:user) { create(:user) } let(:master) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public, :access_requestable) } background do project.request_access(user) diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb index 56ede8eb5be..97c42bd7f01 100644 --- a/spec/features/projects/members/user_requests_access_spec.rb +++ b/spec/features/projects/members/user_requests_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' feature 'Projects > Members > User requests access', feature: true do let(:user) { create(:user) } let(:master) { create(:user) } - let(:project) { create(:project, :public) } + let(:project) { create(:project, :public, :access_requestable) } background do project.team << [master, :master] diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb new file mode 100644 index 00000000000..abfc46601fb --- /dev/null +++ b/spec/features/projects/new_project_spec.rb @@ -0,0 +1,19 @@ +require "spec_helper" + +feature "New project", feature: true do + context "Visibility level selector" do + let(:user) { create(:admin) } + + before { login_as(user) } + + Gitlab::VisibilityLevel.options.each do |key, level| + it "sets selector to #{key}" do + stub_application_setting(default_project_visibility: level) + + visit new_project_path + + expect(find_field("project_visibility_level_#{level}")).to be_checked + end + end + end +end diff --git a/spec/features/projects/pipelines_spec.rb b/spec/features/projects/pipelines_spec.rb index db56a50e058..002c6f6b359 100644 --- a/spec/features/projects/pipelines_spec.rb +++ b/spec/features/projects/pipelines_spec.rb @@ -146,7 +146,8 @@ describe "Pipelines" do end describe 'GET /:project/pipelines/:id' do - let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') } + let(:project) { create(:project) } + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) } before do @success = create(:ci_build, :success, pipeline: pipeline, stage: 'build', name: 'build') diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb index 3de25d7af7d..bf60cca4ea4 100644 --- a/spec/features/projects/project_settings_spec.rb +++ b/spec/features/projects/project_settings_spec.rb @@ -18,7 +18,7 @@ describe 'Edit Project Settings', feature: true do click_button 'Save changes' expect(page).to have_field 'project_name_edit', with: 'foo&bar' - expect(page).to have_content "Name can contain only letters, digits, '_', '.', dash and space. It must start with letter, digit or '_'." + expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'." expect(page).to have_button 'Save changes' end end @@ -34,8 +34,21 @@ describe 'Edit Project Settings', feature: true do expect(page).to have_field 'Project name', with: 'foo&bar' expect(page).to have_field 'Path', with: 'foo&bar' - expect(page).to have_content "Name can contain only letters, digits, '_', '.', dash and space. It must start with letter, digit or '_'." + expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'." expect(page).to have_content "Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'" end end + + describe 'Rename repository name with emojis' do + it 'shows error for invalid project name' do + visit edit_namespace_project_path(project.namespace, project) + + fill_in 'Project name', with: '🚀 foo bar ☁️' + + click_button 'Rename project' + + expect(page).to have_field 'Project name', with: '🚀 foo bar ☁️' + expect(page).not_to have_content "Name can contain only letters, digits, emojis '_', '.', dash and space. It must start with letter, digit, emoji or '_'." + end + end end diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb index b3ba40b35af..472491188c9 100644 --- a/spec/features/projects/ref_switcher_spec.rb +++ b/spec/features/projects/ref_switcher_spec.rb @@ -22,8 +22,20 @@ feature 'Ref switcher', feature: true, js: true do input.native.send_keys :down input.native.send_keys :down input.native.send_keys :enter + end + + expect(page).to have_title 'expand-collapse-files' + end + + it "user selects ref with special characters" do + click_button 'master' + wait_for_ajax - expect(page).to have_content 'expand-collapse-files' + page.within '.project-refs-form' do + page.fill_in 'Search branches and tags', with: "'test'" + click_link "'test'" end + + expect(page).to have_title "'test'" end end diff --git a/spec/features/projects/services/mattermost_slash_command_spec.rb b/spec/features/projects/services/mattermost_slash_command_spec.rb new file mode 100644 index 00000000000..f474e7e891b --- /dev/null +++ b/spec/features/projects/services/mattermost_slash_command_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +feature 'Setup Mattermost slash commands', feature: true do + include WaitForAjax + + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:service) { project.create_mattermost_slash_commands_service } + + before do + project.team << [user, :master] + login_as(user) + end + + describe 'user visites the mattermost slash command config page', js: true do + it 'shows a help message' do + visit edit_namespace_project_service_path(project.namespace, project, service) + + wait_for_ajax + + expect(page).to have_content("This service allows GitLab users to perform common") + end + end + + describe 'saving a token' do + let(:token) { ('a'..'z').to_a.join } + + it 'shows the token after saving' do + visit edit_namespace_project_service_path(project.namespace, project, service) + + fill_in 'service_token', with: token + click_on 'Save' + + value = find_field('service_token').value + + expect(value).to eq(token) + end + end + + describe 'the trigger url' do + it 'shows the correct url' do + visit edit_namespace_project_service_path(project.namespace, project, service) + + value = find_field('request_url').value + expect(value).to match("api/v3/projects/#{project.id}/services/mattermost_slash_commands/trigger") + end + end +end diff --git a/spec/features/projects/slack_service/slack_service_spec.rb b/spec/features/projects/services/slack_service_spec.rb index 16541f51d98..16541f51d98 100644 --- a/spec/features/projects/slack_service/slack_service_spec.rb +++ b/spec/features/projects/services/slack_service_spec.rb diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb index 1806200c82c..caecd027aaa 100644 --- a/spec/features/search_spec.rb +++ b/spec/features/search_spec.rb @@ -100,6 +100,32 @@ describe "Search", feature: true do expect(page).to have_link(snippet.title) end + + it 'finds a commit' do + visit namespace_project_path(project.namespace, project) + + page.within '.search' do + fill_in 'search', with: 'add' + click_button 'Go' + end + + click_link "Commits" + + expect(page).to have_selector('.commit-row-description') + end + + it 'finds a code' do + visit namespace_project_path(project.namespace, project) + + page.within '.search' do + fill_in 'search', with: 'def' + click_button 'Go' + end + + click_link "Code" + + expect(page).to have_selector('.file-content .code') + end end describe 'Right header search field', feature: true do diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb index db53a9cec97..49deacc5c74 100644 --- a/spec/features/security/project/snippet/internal_access_spec.rb +++ b/spec/features/security/project/snippet/internal_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe "Internal Project Snippets Access", feature: true do include AccessMatchers - let(:project) { create(:project, :internal) } + let(:project) { create(:empty_project, :internal) } let(:owner) { project.owner } let(:master) { create(:user) } @@ -48,31 +48,63 @@ describe "Internal Project Snippets Access", feature: true do it { is_expected.to be_denied_for :visitor } end - describe "GET /:project_path/snippets/:id for an internal snippet" do - subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) } + describe "GET /:project_path/snippets/:id" do + context "for an internal snippet" do + subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for developer } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :external } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + context "for a private snippet" do + subject { namespace_project_snippet_path(project.namespace, project, private_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end - describe "GET /:project_path/snippets/:id for a private snippet" do - subject { namespace_project_snippet_path(project.namespace, project, private_snippet) } + describe "GET /:project_path/snippets/:id/raw" do + context "for an internal snippet" do + subject { raw_namespace_project_snippet_path(project.namespace, project, internal_snippet) } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for developer } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :external } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + context "for a private snippet" do + subject { raw_namespace_project_snippet_path(project.namespace, project, private_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end end diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb index d23d645c8e5..a1bfc076d99 100644 --- a/spec/features/security/project/snippet/private_access_spec.rb +++ b/spec/features/security/project/snippet/private_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe "Private Project Snippets Access", feature: true do include AccessMatchers - let(:project) { create(:project, :private) } + let(:project) { create(:empty_project, :private) } let(:owner) { project.owner } let(:master) { create(:user) } @@ -60,4 +60,18 @@ describe "Private Project Snippets Access", feature: true do it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :visitor } end + + describe "GET /:project_path/snippets/:id/raw for a private snippet" do + subject { raw_namespace_project_snippet_path(project.namespace, project, private_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb index e3665b6116a..30bcd87ef04 100644 --- a/spec/features/security/project/snippet/public_access_spec.rb +++ b/spec/features/security/project/snippet/public_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe "Public Project Snippets Access", feature: true do include AccessMatchers - let(:project) { create(:project, :public) } + let(:project) { create(:empty_project, :public) } let(:owner) { project.owner } let(:master) { create(:user) } @@ -49,45 +49,91 @@ describe "Public Project Snippets Access", feature: true do it { is_expected.to be_denied_for :visitor } end - describe "GET /:project_path/snippets/:id for a public snippet" do - subject { namespace_project_snippet_path(project.namespace, project, public_snippet) } + describe "GET /:project_path/snippets/:id" do + context "for a public snippet" do + subject { namespace_project_snippet_path(project.namespace, project, public_snippet) } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for developer } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :external } - it { is_expected.to be_allowed_for :visitor } - end + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end - describe "GET /:project_path/snippets/:id for an internal snippet" do - subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) } + context "for an internal snippet" do + subject { namespace_project_snippet_path(project.namespace, project, internal_snippet) } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for developer } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :external } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + context "for a private snippet" do + subject { namespace_project_snippet_path(project.namespace, project, private_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end - describe "GET /:project_path/snippets/:id for a private snippet" do - subject { namespace_project_snippet_path(project.namespace, project, private_snippet) } + describe "GET /:project_path/snippets/:id/raw" do + context "for a public snippet" do + subject { raw_namespace_project_snippet_path(project.namespace, project, public_snippet) } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for developer } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :external } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end + + context "for an internal snippet" do + subject { raw_namespace_project_snippet_path(project.namespace, project, internal_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + context "for a private snippet" do + subject { raw_namespace_project_snippet_path(project.namespace, project, private_snippet) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end end diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb new file mode 100644 index 00000000000..10a4597e467 --- /dev/null +++ b/spec/features/snippets/explore_spec.rb @@ -0,0 +1,16 @@ +require 'rails_helper' + +feature 'Explore Snippets', feature: true do + scenario 'User should see snippets that are not private' do + public_snippet = create(:personal_snippet, :public) + internal_snippet = create(:personal_snippet, :internal) + private_snippet = create(:personal_snippet, :private) + + login_as create(:user) + visit explore_snippets_path + + expect(page).to have_content(public_snippet.title) + expect(page).to have_content(internal_snippet.title) + expect(page).not_to have_content(private_snippet.title) + end +end diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb new file mode 100644 index 00000000000..34300ccb940 --- /dev/null +++ b/spec/features/snippets/public_snippets_spec.rb @@ -0,0 +1,19 @@ +require 'rails_helper' + +feature 'Public Snippets', feature: true do + scenario 'Unauthenticated user should see public snippets' do + public_snippet = create(:personal_snippet, :public) + + visit snippet_path(public_snippet) + + expect(page).to have_content(public_snippet.content) + end + + scenario 'Unauthenticated user should see raw public snippets' do + public_snippet = create(:personal_snippet, :public) + + visit raw_snippet_path(public_snippet) + + expect(page).to have_content(public_snippet.content) + end +end diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb new file mode 100644 index 00000000000..146cd3af848 --- /dev/null +++ b/spec/features/snippets/search_snippets_spec.rb @@ -0,0 +1,66 @@ +require 'rails_helper' + +feature 'Search Snippets', feature: true do + scenario 'User searches for snippets by title' do + public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle') + private_snippet = create(:personal_snippet, :private, title: 'Middle and End') + + login_as private_snippet.author + visit dashboard_snippets_path + + page.within '.search' do + fill_in 'search', with: 'Middle' + click_button 'Go' + end + + click_link 'Titles and Filenames' + + expect(page).to have_link(public_snippet.title) + expect(page).to have_link(private_snippet.title) + end + + scenario 'User searches for snippet contents' do + create(:personal_snippet, + :public, + title: 'Many lined snippet', + content: <<-CONTENT.strip_heredoc + |line one + |line two + |line three + |line four + |line five + |line six + |line seven + |line eight + |line nine + |line ten + |line eleven + |line twelve + |line thirteen + |line fourteen + CONTENT + ) + + login_as create(:user) + visit dashboard_snippets_path + + page.within '.search' do + fill_in 'search', with: 'line seven' + click_button 'Go' + end + + expect(page).to have_content('line seven') + + # 3 lines before the matched line should be visible + expect(page).to have_content('line six') + expect(page).to have_content('line five') + expect(page).to have_content('line four') + expect(page).not_to have_content('line three') + + # 3 lines after the matched line should be visible + expect(page).to have_content('line eight') + expect(page).to have_content('line nine') + expect(page).to have_content('line ten') + expect(page).not_to have_content('line eleven') + end +end diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb index 3ae83ac082d..88eabea7e3a 100644 --- a/spec/features/todos/todos_spec.rb +++ b/spec/features/todos/todos_spec.rb @@ -44,7 +44,7 @@ describe 'Dashboard Todos', feature: true do end it 'shows "All done" message' do - expect(page).to have_content("Good job! Looks like you don't have any todos left.") + expect(page).to have_selector('.todos-all-done', count: 1) end end @@ -64,7 +64,7 @@ describe 'Dashboard Todos', feature: true do end it 'shows "All done" message' do - expect(page).to have_content("Good job! Looks like you don't have any todos left.") + expect(page).to have_selector('.todos-all-done', count: 1) end end end @@ -152,7 +152,7 @@ describe 'Dashboard Todos', feature: true do within('.todos-pending-count') { expect(page).to have_content '0' } expect(page).to have_content 'To do 0' expect(page).to have_content 'Done 0' - expect(page).to have_content "Good job! Looks like you don't have any todos left." + expect(page).to have_selector('.todos-all-done', count: 1) end end end diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb index 33b52d1547e..e2d9cfdd0b0 100644 --- a/spec/features/unsubscribe_links_spec.rb +++ b/spec/features/unsubscribe_links_spec.rb @@ -26,11 +26,11 @@ describe 'Unsubscribe links', feature: true do expect(current_path).to eq unsubscribe_sent_notification_path(SentNotification.last) expect(page).to have_text(%(Unsubscribe from issue #{issue.title} (#{issue.to_reference}))) expect(page).to have_text(%(Are you sure you want to unsubscribe from issue #{issue.title} (#{issue.to_reference})?)) - expect(issue.subscribed?(recipient)).to be_truthy + expect(issue.subscribed?(recipient, project)).to be_truthy click_link 'Unsubscribe' - expect(issue.subscribed?(recipient)).to be_falsey + expect(issue.subscribed?(recipient, project)).to be_falsey expect(current_path).to eq new_user_session_path end @@ -38,11 +38,11 @@ describe 'Unsubscribe links', feature: true do visit body_link expect(current_path).to eq unsubscribe_sent_notification_path(SentNotification.last) - expect(issue.subscribed?(recipient)).to be_truthy + expect(issue.subscribed?(recipient, project)).to be_truthy click_link 'Cancel' - expect(issue.subscribed?(recipient)).to be_truthy + expect(issue.subscribed?(recipient, project)).to be_truthy expect(current_path).to eq new_user_session_path end end @@ -51,7 +51,7 @@ describe 'Unsubscribe links', feature: true do visit header_link expect(page).to have_text('unsubscribed') - expect(issue.subscribed?(recipient)).to be_falsey + expect(issue.subscribed?(recipient, project)).to be_falsey end end @@ -62,14 +62,14 @@ describe 'Unsubscribe links', feature: true do visit body_link expect(page).to have_text('unsubscribed') - expect(issue.subscribed?(recipient)).to be_falsey + expect(issue.subscribed?(recipient, project)).to be_falsey end it 'unsubscribes from the issue when visiting the link from the header' do visit header_link expect(page).to have_text('unsubscribed') - expect(issue.subscribed?(recipient)).to be_falsey + expect(issue.subscribed?(recipient, project)).to be_falsey end end end diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index 111ca7f7a70..afa98f3f715 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -74,16 +74,29 @@ feature 'Users', feature: true, js: true do visit new_user_session_path click_link 'Register' end + + scenario 'doesn\'t show an error border if the username is available' do + fill_in username_input, with: 'new-user' + wait_for_ajax + expect(find('.username')).not_to have_css '.gl-field-error-outline' + end + + scenario 'does not show an error border if the username contains dots (.)' do + fill_in username_input, with: 'new.user.username' + wait_for_ajax + expect(find('.username')).not_to have_css '.gl-field-error-outline' + end + scenario 'shows an error border if the username already exists' do fill_in username_input, with: user.username wait_for_ajax expect(find('.username')).to have_css '.gl-field-error-outline' end - scenario 'doesn\'t show an error border if the username is available' do - fill_in username_input, with: 'new-user' + scenario 'shows an error border if the username contains special characters' do + fill_in username_input, with: 'new$user!username' wait_for_ajax - expect(find('#new_user_username')).not_to have_css '.gl-field-error-outline' + expect(find('.username')).to have_css '.gl-field-error-outline' end end |