Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-01-16 15:08:32 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-01-16 15:08:32 +0300
commitc158fa8d69c704663d289341a014c44c062cda88 (patch)
treed0cac82a9ac9e9ad28bb0030266eb8d5dc91fbbc /spec
parentb806264d29b8d52ccb78a41dcc3d67f2b040700c (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/groups/milestones_controller_spec.rb13
-rw-r--r--spec/features/issues/user_creates_issue_by_email_spec.rb46
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb126
-rw-r--r--spec/features/issues/user_edits_issue_spec.rb279
-rw-r--r--spec/features/issues/user_filters_issues_spec.rb39
-rw-r--r--spec/features/issues/user_resets_their_incoming_email_token_spec.rb32
-rw-r--r--spec/features/issues/user_sees_breadcrumb_links_spec.rb20
-rw-r--r--spec/features/issues/user_sees_empty_state_spec.rb51
-rw-r--r--spec/features/issues/user_sees_live_update_spec.rb52
-rw-r--r--spec/features/issues/user_sorts_issues_spec.rb187
-rw-r--r--spec/features/issues_spec.rb828
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb26
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb37
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb2
-rw-r--r--spec/features/projects/tree/create_file_spec.rb2
-rw-r--r--spec/frontend/diffs/components/compare_versions_spec.js3
-rw-r--r--spec/frontend/diffs/components/diff_stats_spec.js18
-rw-r--r--spec/frontend/ide/stores/actions/file_spec.js68
-rw-r--r--spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap4
-rw-r--r--spec/frontend/registry/settings/store/actions_spec.js14
-rw-r--r--spec/frontend/repository/components/last_commit_spec.js14
-rw-r--r--spec/frontend/vue_shared/components/changed_file_icon_spec.js6
-rw-r--r--spec/helpers/markup_helper_spec.rb15
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/form_spec.js19
-rw-r--r--spec/javascripts/ide/components/repo_tab_spec.js4
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js100
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb33
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb2
-rw-r--r--spec/models/commit_spec.rb6
-rw-r--r--spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb4
-rw-r--r--spec/services/spam/mark_as_spam_service_spec.rb52
31 files changed, 1150 insertions, 952 deletions
diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb
index 4f4f9e5143b..8fb9f0c516c 100644
--- a/spec/controllers/groups/milestones_controller_spec.rb
+++ b/spec/controllers/groups/milestones_controller_spec.rb
@@ -148,6 +148,19 @@ describe Groups::MilestonesController do
expect(response).to have_gitlab_http_status(200)
expect(response.content_type).to eq 'application/json'
end
+
+ context 'for a subgroup' do
+ let(:subgroup) { create(:group, parent: group) }
+
+ it 'includes ancestor group milestones' do
+ get :index, params: { group_id: subgroup.to_param }, format: :json
+
+ milestones = json_response
+
+ expect(milestones.count).to eq(1)
+ expect(milestones.first['title']).to eq('group milestone')
+ end
+ end
end
context 'external authorization' do
diff --git a/spec/features/issues/user_creates_issue_by_email_spec.rb b/spec/features/issues/user_creates_issue_by_email_spec.rb
new file mode 100644
index 00000000000..c73a65849cc
--- /dev/null
+++ b/spec/features/issues/user_creates_issue_by_email_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Issues > User creates issue by email' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :public) }
+
+ before do
+ sign_in(user)
+
+ project.add_developer(user)
+ end
+
+ 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 project_issues_path(project)
+ click_button('Email a new issue')
+ end
+
+ it 'click the button to show modal for the new email' do
+ page.within '#issuable-email-modal' do
+ email = project.new_issuable_address(user, 'issue')
+
+ expect(page).to have_selector("input[value='#{email}']")
+ end
+ end
+ end
+
+ context 'with existing issues' do
+ let!(:issue) { create(:issue, project: project, author: user) }
+
+ it_behaves_like 'show the email in the modal'
+ end
+
+ context 'without existing issues' do
+ it_behaves_like 'show the email in the modal'
+ end
+ end
+end
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index 39ce3415727..b0a2a734877 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -3,8 +3,32 @@
require "spec_helper"
describe "User creates issue" do
- let(:project) { create(:project_empty_repo, :public) }
- let(:user) { create(:user) }
+ include DropzoneHelper
+
+ let_it_be(:project) { create(:project_empty_repo, :public) }
+ let_it_be(:user) { create(:user) }
+
+ context "when unauthenticated" do
+ before do
+ sign_out(:user)
+ end
+
+ it "redirects to signin then back to new issue after signin" do
+ create(:issue, project: project)
+
+ visit project_issues_path(project)
+
+ page.within ".nav-controls" do
+ click_link "New issue"
+ end
+
+ expect(current_path).to eq new_user_session_path
+
+ gitlab_sign_in(create(:user))
+
+ expect(current_path).to eq new_project_issue_path(project)
+ end
+ end
context "when signed in as guest" do
before do
@@ -92,6 +116,104 @@ describe "User creates issue" do
.and have_content(label_titles.first)
end
end
+
+ context 'with due date', :js do
+ it 'saves with due date' do
+ date = Date.today.at_beginning_of_month
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+ find('#issuable-due-date').click
+
+ page.within '.pika-single' do
+ click_button date.day
+ end
+
+ expect(find('#issuable-due-date').value).to eq date.to_s
+
+ click_button 'Submit issue'
+
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
+ end
+ end
+
+ context 'dropzone upload file', :js do
+ before do
+ visit new_project_issue_path(project)
+ end
+
+ it 'uploads file when dragging into textarea' do
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+
+ expect(page.find_field("issue_description").value).to have_content 'banana_sample'
+ end
+
+ it "doesn't add double newline to end of a single attachment markdown" do
+ dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
+
+ expect(page.find_field("issue_description").value).not_to match /\n\n$/
+ end
+
+ it "cancels a file upload correctly" do
+ slow_requests do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
+
+ click_button 'Cancel'
+ end
+
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_button('Cancel')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
+ end
+
+ context 'form filled by URL parameters' do
+ let(:project) { create(:project, :public, :repository) }
+
+ before do
+ project.repository.create_file(
+ user,
+ '.gitlab/issue_templates/bug.md',
+ 'this is a test "bug" template',
+ message: 'added issue template',
+ branch_name: 'master')
+
+ visit new_project_issue_path(project, issuable_template: 'bug')
+ end
+
+ it 'fills in template' do
+ expect(find('.js-issuable-selector .dropdown-toggle-text')).to have_content('bug')
+ end
+ end
+
+ context 'suggestions', :js do
+ it 'displays list of related issues' do
+ issue = create(:issue, project: project)
+ create(:issue, project: project, title: 'test issue')
+
+ visit new_project_issue_path(project)
+
+ fill_in 'issue_title', with: issue.title
+
+ expect(page).to have_selector('.suggestion-item', count: 1)
+ end
+ end
+
+ it 'clears local storage after creating a new issue', :js do
+ 2.times do
+ visit new_project_issue_path(project)
+ wait_for_requests
+
+ expect(page).to have_field('Title', with: '')
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+
+ click_button 'Submit issue'
+ end
+ end
end
context "when signed in as user with special characters in their name" do
diff --git a/spec/features/issues/user_edits_issue_spec.rb b/spec/features/issues/user_edits_issue_spec.rb
index 0afc19d9519..ad984cf07e2 100644
--- a/spec/features/issues/user_edits_issue_spec.rb
+++ b/spec/features/issues/user_edits_issue_spec.rb
@@ -2,26 +2,283 @@
require "spec_helper"
-describe "User edits issue", :js do
- set(:project) { create(:project_empty_repo, :public) }
- set(:user) { create(:user) }
- set(:issue) { create(:issue, project: project, author: user) }
+describe "Issues > User edits issue", :js do
+ let_it_be(:project) { create(:project_empty_repo, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
+ let_it_be(:label) { create(:label, project: project) }
+ let_it_be(:milestone) { create(:milestone, project: project) }
before do
project.add_developer(user)
sign_in(user)
+ end
+
+ context "from edit page" do
+ before do
+ visit edit_project_issue_path(project, issue)
+ end
+
+ it "previews content" do
+ form = first(".gfm-form")
+
+ page.within(form) do
+ fill_in("Description", with: "Bug fixed :smile:")
+ click_button("Preview")
+ end
+
+ expect(form).to have_button("Write")
+ end
+
+ it 'allows user to select unassigned' do
+ visit edit_project_issue_path(project, issue)
+
+ expect(page).to have_content "Assignee #{user.name}"
+
+ first('.js-user-search').click
+ click_link 'Unassigned'
+
+ click_button 'Save changes'
+
+ page.within('.assignee') do
+ expect(page).to have_content 'None - assign yourself'
+ end
+ end
+
+ context 'with due date' do
+ before do
+ visit edit_project_issue_path(project, issue)
+ end
+
+ it 'saves with due date' do
+ date = Date.today.at_beginning_of_month.tomorrow
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+ find('#issuable-due-date').click
+
+ page.within '.pika-single' do
+ click_button date.day
+ end
+
+ expect(find('#issuable-due-date').value).to eq date.to_s
+
+ click_button 'Save changes'
- visit(edit_project_issue_path(project, issue))
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
+ end
+
+ it 'warns about version conflict' do
+ issue.update(title: "New title")
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+
+ click_button 'Save changes'
+
+ expect(page).to have_content 'Someone edited the issue the same time you did'
+ end
+ end
end
- it "previews content" do
- form = first(".gfm-form")
+ context "from issue#show" do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ describe 'update labels' do
+ it 'will not send ajax request when no data is changed' do
+ page.within '.labels' do
+ click_link 'Edit'
- page.within(form) do
- fill_in("Description", with: "Bug fixed :smile:")
- click_button("Preview")
+ find('.dropdown-menu-close', match: :first).click
+
+ expect(page).not_to have_selector('.block-loading')
+ end
+ end
end
- expect(form).to have_button("Write")
+ describe 'update assignee' do
+ context 'by authorized user' do
+ def close_dropdown_menu_if_visible
+ find('.dropdown-menu-toggle', visible: :all).tap do |toggle|
+ toggle.click if toggle.visible?
+ end
+ end
+
+ it 'allows user to select unassigned' do
+ visit project_issue_path(project, issue)
+
+ page.within('.assignee') do
+ expect(page).to have_content "#{user.name}"
+
+ click_link 'Edit'
+ click_link 'Unassigned'
+ first('.title').click
+ expect(page).to have_content 'None - assign yourself'
+ end
+ end
+
+ it 'allows user to select an assignee' do
+ issue2 = create(:issue, project: project, author: user)
+ visit project_issue_path(project, issue2)
+
+ page.within('.assignee') do
+ expect(page).to have_content "None"
+ end
+
+ page.within '.assignee' do
+ click_link 'Edit'
+ end
+
+ page.within '.dropdown-menu-user' do
+ click_link user.name
+ end
+
+ page.within('.assignee') do
+ expect(page).to have_content user.name
+ end
+ end
+
+ it 'allows user to unselect themselves' do
+ issue2 = create(:issue, project: project, author: user)
+
+ visit project_issue_path(project, issue2)
+
+ page.within '.assignee' do
+ click_link 'Edit'
+ click_link user.name
+
+ close_dropdown_menu_if_visible
+
+ page.within '.value .author' do
+ expect(page).to have_content user.name
+ end
+
+ click_link 'Edit'
+ click_link user.name
+
+ close_dropdown_menu_if_visible
+
+ page.within '.value .assign-yourself' do
+ expect(page).to have_content "None"
+ end
+ end
+ end
+ end
+
+ context 'by unauthorized user' do
+ let(:guest) { create(:user) }
+
+ before do
+ project.add_guest(guest)
+ end
+
+ it 'shows assignee text' do
+ sign_out(:user)
+ sign_in(guest)
+
+ visit project_issue_path(project, issue)
+ expect(page).to have_content issue.assignees.first.name
+ end
+ end
+ end
+
+ describe 'update milestone' do
+ context 'by authorized user' do
+ it 'allows user to select unassigned' do
+ visit project_issue_path(project, issue)
+
+ page.within('.milestone') do
+ expect(page).to have_content "None"
+ end
+
+ find('.block.milestone .edit-link').click
+ sleep 2 # wait for ajax stuff to complete
+ first('.dropdown-content li').click
+ sleep 2
+ page.within('.milestone') do
+ expect(page).to have_content 'None'
+ end
+ end
+
+ it 'allows user to de-select milestone' do
+ visit project_issue_path(project, issue)
+
+ page.within('.milestone') do
+ click_link 'Edit'
+ click_link milestone.title
+
+ page.within '.value' do
+ expect(page).to have_content milestone.title
+ end
+
+ click_link 'Edit'
+ click_link milestone.title
+
+ page.within '.value' do
+ expect(page).to have_content 'None'
+ end
+ end
+ end
+ end
+
+ context 'by unauthorized user' do
+ let(:guest) { create(:user) }
+
+ before do
+ project.add_guest(guest)
+ issue.milestone = milestone
+ issue.save
+ end
+
+ it 'shows milestone text' do
+ sign_out(:user)
+ sign_in(guest)
+
+ visit project_issue_path(project, issue)
+ expect(page).to have_content milestone.title
+ end
+ end
+ end
+
+ context 'update due date' do
+ it 'adds due date to issue' do
+ date = Date.today.at_beginning_of_month + 2.days
+
+ page.within '.due_date' do
+ click_link 'Edit'
+
+ page.within '.pika-single' do
+ click_button date.day
+ end
+
+ wait_for_requests
+
+ expect(find('.value').text).to have_content date.strftime('%b %-d, %Y')
+ end
+ end
+
+ it 'removes due date from issue' do
+ date = Date.today.at_beginning_of_month + 2.days
+
+ page.within '.due_date' do
+ click_link 'Edit'
+
+ page.within '.pika-single' do
+ click_button date.day
+ end
+
+ wait_for_requests
+
+ expect(page).to have_no_content 'None'
+
+ click_link 'remove due date'
+ expect(page).to have_content 'None'
+ end
+ end
+ end
end
end
diff --git a/spec/features/issues/user_filters_issues_spec.rb b/spec/features/issues/user_filters_issues_spec.rb
new file mode 100644
index 00000000000..714bc972025
--- /dev/null
+++ b/spec/features/issues/user_filters_issues_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'User filters issues' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project_empty_repo, :public) }
+
+ before do
+ %w[foobar barbaz].each do |title|
+ create(:issue,
+ author: user,
+ assignees: [user],
+ project: project,
+ title: title)
+ end
+
+ @issue = Issue.find_by(title: 'foobar')
+ @issue.milestone = create(:milestone, project: project)
+ @issue.assignees = []
+ @issue.save
+ end
+
+ let(:issue) { @issue }
+
+ it 'allows filtering by issues with no specified assignee' do
+ visit project_issues_path(project, assignee_id: IssuableFinder::FILTER_NONE)
+
+ expect(page).to have_content 'foobar'
+ expect(page).not_to have_content 'barbaz'
+ end
+
+ it 'allows filtering by a specified assignee' do
+ visit project_issues_path(project, assignee_id: user.id)
+
+ expect(page).not_to have_content 'foobar'
+ expect(page).to have_content 'barbaz'
+ end
+end
diff --git a/spec/features/issues/user_resets_their_incoming_email_token_spec.rb b/spec/features/issues/user_resets_their_incoming_email_token_spec.rb
new file mode 100644
index 00000000000..108b6f550db
--- /dev/null
+++ b/spec/features/issues/user_resets_their_incoming_email_token_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Issues > User resets their incoming email token' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :public, namespace: user.namespace) }
+ let_it_be(:issue) { create(:issue, project: project) }
+
+ before do
+ stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit namespace_project_issues_path(user.namespace, project)
+ end
+
+ it 'changes incoming email address token', :js do
+ find('.issuable-email-modal-btn').click
+ previous_token = find('input#issuable_email').value
+ find('.incoming-email-token-reset').click
+
+ wait_for_requests
+
+ expect(page).to have_no_field('issuable_email', with: previous_token)
+ new_token = project.new_issuable_address(user.reload, 'issue')
+ expect(page).to have_field(
+ 'issuable_email',
+ with: new_token
+ )
+ end
+end
diff --git a/spec/features/issues/user_sees_breadcrumb_links_spec.rb b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
index f31d730c337..8a120a0a0b2 100644
--- a/spec/features/issues/user_sees_breadcrumb_links_spec.rb
+++ b/spec/features/issues/user_sees_breadcrumb_links_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
describe 'New issue breadcrumb' do
- let(:project) { create(:project) }
+ let_it_be(:project, reload: true) { create(:project) }
let(:user) { project.creator }
before do
@@ -17,4 +17,22 @@ describe 'New issue breadcrumb' do
expect(find_link('New')[:href]).to end_with(new_project_issue_path(project))
end
end
+
+ it 'links to current issue in breadcrubs' do
+ issue = create(:issue, project: project)
+
+ visit project_issue_path(project, issue)
+
+ expect(find('.breadcrumbs-sub-title a')[:href]).to end_with(issue_path(issue))
+ end
+
+ it 'excludes award_emoji from comment count' do
+ issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar')
+ create(:award_emoji, awardable: issue)
+
+ visit project_issues_path(project, assignee_id: user.id)
+
+ expect(page).to have_content 'foobar'
+ expect(page.all('.no-comments').first.text).to eq "0"
+ end
end
diff --git a/spec/features/issues/user_sees_empty_state_spec.rb b/spec/features/issues/user_sees_empty_state_spec.rb
new file mode 100644
index 00000000000..114d119aca8
--- /dev/null
+++ b/spec/features/issues/user_sees_empty_state_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Issues > User sees empty state' do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:user) { project.creator }
+
+ shared_examples_for 'empty state with filters' do
+ it 'user sees empty state with filters' do
+ create(:issue, author: user, project: project)
+
+ visit project_issues_path(project, milestone_title: "1.0")
+
+ expect(page).to have_content('Sorry, your filter produced no results')
+ expect(page).to have_content('To widen your search, change or remove filters above')
+ end
+ end
+
+ describe 'while user is signed out' do
+ describe 'empty state' do
+ it 'user sees empty state' do
+ visit project_issues_path(project)
+
+ expect(page).to have_content('Register / Sign In')
+ expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project.')
+ expect(page).to have_content('You can register or sign in to create issues for this project.')
+ end
+
+ it_behaves_like 'empty state with filters'
+ end
+ end
+
+ describe 'while user is signed in' do
+ before do
+ sign_in(user)
+ end
+
+ describe 'empty state' do
+ it 'user sees empty state' do
+ visit project_issues_path(project)
+
+ expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project')
+ expect(page).to have_content('Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.')
+ expect(page).to have_content('New issue')
+ end
+
+ it_behaves_like 'empty state with filters'
+ end
+ end
+end
diff --git a/spec/features/issues/user_sees_live_update_spec.rb b/spec/features/issues/user_sees_live_update_spec.rb
new file mode 100644
index 00000000000..98c7d289fb0
--- /dev/null
+++ b/spec/features/issues/user_sees_live_update_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Issues > User sees live update', :js do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:user) { project.creator }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'title issue#show' do
+ it 'updates the title' do
+ issue = create(:issue, author: user, assignees: [user], project: project, title: 'new title')
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_text("new title")
+
+ issue.update(title: "updated title")
+
+ wait_for_requests
+ expect(page).to have_text("updated title")
+ end
+ end
+
+ describe 'confidential issue#show' do
+ it 'shows confidential sibebar information as confidential and can be turned off' do
+ issue = create(:issue, :confidential, project: project)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.issuable-note-warning')
+ expect(find('.issuable-sidebar-item.confidentiality')).to have_css('.is-active')
+ expect(find('.issuable-sidebar-item.confidentiality')).not_to have_css('.not-active')
+
+ find('.confidential-edit').click
+ expect(page).to have_css('.sidebar-item-warning-message')
+
+ within('.sidebar-item-warning-message') do
+ find('.btn-close').click
+ end
+
+ wait_for_requests
+
+ visit project_issue_path(project, issue)
+
+ expect(page).not_to have_css('.is-active')
+ end
+ end
+end
diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb
index 79938785633..66110f55435 100644
--- a/spec/features/issues/user_sorts_issues_spec.rb
+++ b/spec/features/issues/user_sorts_issues_spec.rb
@@ -3,12 +3,17 @@
require "spec_helper"
describe "User sorts issues" do
- set(:user) { create(:user) }
- set(:group) { create(:group) }
- set(:project) { create(:project_empty_repo, :public, group: group) }
- set(:issue1) { create(:issue, project: project) }
- set(:issue2) { create(:issue, project: project) }
- set(:issue3) { create(:issue, project: project) }
+ include SortingHelper
+ include IssueHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project_empty_repo, :public, group: group) }
+ let_it_be(:issue1, reload: true) { create(:issue, title: 'foo', created_at: Time.now, project: project) }
+ let_it_be(:issue2, reload: true) { create(:issue, title: 'bar', created_at: Time.now - 60, project: project) }
+ let_it_be(:issue3, reload: true) { create(:issue, title: 'baz', created_at: Time.now - 120, project: project) }
+ let_it_be(:newer_due_milestone) { create(:milestone, project: project, due_date: '2013-12-11') }
+ let_it_be(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') }
before do
create_list(:award_emoji, 2, :upvote, awardable: issue1)
@@ -62,4 +67,174 @@ describe "User sorts issues" do
end
end
end
+
+ it 'sorts by newest' do
+ visit project_issues_path(project, sort: sort_value_created_date)
+
+ expect(first_issue).to include('foo')
+ expect(last_issue).to include('baz')
+ end
+
+ it 'sorts by most recently updated' do
+ issue3.updated_at = Time.now + 100
+ issue3.save
+ visit project_issues_path(project, sort: sort_value_recently_updated)
+
+ expect(first_issue).to include('baz')
+ end
+
+ describe 'sorting by due date' do
+ before do
+ issue1.update(due_date: 1.day.from_now)
+ issue2.update(due_date: 6.days.from_now)
+ end
+
+ it 'sorts by due date' do
+ visit project_issues_path(project, sort: sort_value_due_date)
+
+ expect(first_issue).to include('foo')
+ end
+
+ it 'sorts by due date by excluding nil due dates' do
+ issue2.update(due_date: nil)
+
+ visit project_issues_path(project, sort: sort_value_due_date)
+
+ expect(first_issue).to include('foo')
+ end
+
+ context 'with a filter on labels' do
+ let(:label) { create(:label, project: project) }
+
+ before do
+ create(:label_link, label: label, target: issue1)
+ end
+
+ it 'sorts by least recently due date by excluding nil due dates' do
+ issue2.update(due_date: nil)
+
+ visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later)
+
+ expect(first_issue).to include('foo')
+ end
+ end
+ end
+
+ describe 'filtering by due date' do
+ before do
+ issue1.update(due_date: 1.day.from_now)
+ issue2.update(due_date: 6.days.from_now)
+ end
+
+ it 'filters by none' do
+ visit project_issues_path(project, due_date: Issue::NoDueDate.name)
+
+ page.within '.issues-holder' do
+ expect(page).not_to have_content('foo')
+ expect(page).not_to have_content('bar')
+ expect(page).to have_content('baz')
+ end
+ end
+
+ it 'filters by any' do
+ visit project_issues_path(project, due_date: Issue::AnyDueDate.name)
+
+ page.within '.issues-holder' do
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).to have_content('baz')
+ end
+ end
+
+ it 'filters by due this week' do
+ issue1.update(due_date: Date.today.beginning_of_week + 2.days)
+ issue2.update(due_date: Date.today.end_of_week)
+ issue3.update(due_date: Date.today - 8.days)
+
+ visit project_issues_path(project, due_date: Issue::DueThisWeek.name)
+
+ page.within '.issues-holder' do
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).not_to have_content('baz')
+ end
+ end
+
+ it 'filters by due this month' do
+ issue1.update(due_date: Date.today.beginning_of_month + 2.days)
+ issue2.update(due_date: Date.today.end_of_month)
+ issue3.update(due_date: Date.today - 50.days)
+
+ visit project_issues_path(project, due_date: Issue::DueThisMonth.name)
+
+ page.within '.issues-holder' do
+ expect(page).to have_content('foo')
+ expect(page).to have_content('bar')
+ expect(page).not_to have_content('baz')
+ end
+ end
+
+ it 'filters by overdue' do
+ issue1.update(due_date: Date.today + 2.days)
+ issue2.update(due_date: Date.today + 20.days)
+ issue3.update(due_date: Date.yesterday)
+
+ visit project_issues_path(project, due_date: Issue::Overdue.name)
+
+ page.within '.issues-holder' do
+ expect(page).not_to have_content('foo')
+ expect(page).not_to have_content('bar')
+ expect(page).to have_content('baz')
+ end
+ end
+
+ it 'filters by due next month and previous two weeks' do
+ issue1.update(due_date: Date.today - 4.weeks)
+ issue2.update(due_date: (Date.today + 2.months).beginning_of_month)
+ issue3.update(due_date: Date.yesterday)
+
+ visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name)
+
+ page.within '.issues-holder' do
+ expect(page).not_to have_content('foo')
+ expect(page).not_to have_content('bar')
+ expect(page).to have_content('baz')
+ end
+ end
+ end
+
+ describe 'sorting by milestone' do
+ before do
+ issue1.milestone = newer_due_milestone
+ issue1.save
+ issue2.milestone = later_due_milestone
+ issue2.save
+ end
+
+ it 'sorts by milestone' do
+ visit project_issues_path(project, sort: sort_value_milestone)
+
+ expect(first_issue).to include('foo')
+ expect(last_issue).to include('baz')
+ end
+ end
+
+ describe 'combine filter and sort' do
+ let(:user2) { create(:user) }
+
+ before do
+ issue1.assignees << user2
+ issue1.save
+ issue2.assignees << user2
+ issue2.save
+ end
+
+ it 'sorts with a filter applied' do
+ visit project_issues_path(project, sort: sort_value_created_date, assignee_id: user2.id)
+
+ expect(first_issue).to include('foo')
+ expect(last_issue).to include('bar')
+ expect(page).not_to have_content('baz')
+ end
+ end
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
deleted file mode 100644
index ef9daf70b0c..00000000000
--- a/spec/features/issues_spec.rb
+++ /dev/null
@@ -1,828 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe 'Issues' do
- include DropzoneHelper
- include IssueHelpers
- include SortingHelper
-
- let(:user) { create(:user) }
- let(:project) { create(:project, :public) }
-
- shared_examples_for 'empty state with filters' do
- it 'user sees empty state with filters' do
- create(:issue, author: user, project: project)
-
- visit project_issues_path(project, milestone_title: "1.0")
-
- expect(page).to have_content('Sorry, your filter produced no results')
- expect(page).to have_content('To widen your search, change or remove filters above')
- end
- end
-
- describe 'while user is signed out' do
- describe 'empty state' do
- it 'user sees empty state' do
- visit project_issues_path(project)
-
- expect(page).to have_content('Register / Sign In')
- expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project.')
- expect(page).to have_content('You can register or sign in to create issues for this project.')
- end
-
- it_behaves_like 'empty state with filters'
- end
- end
-
- describe 'while user is signed in' do
- before do
- sign_in(user)
- user2 = create(:user)
-
- project.add_developer(user)
- project.add_developer(user2)
- end
-
- describe 'empty state' do
- it 'user sees empty state' do
- visit project_issues_path(project)
-
- expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project')
- expect(page).to have_content('Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.')
- expect(page).to have_content('New issue')
- end
-
- it_behaves_like 'empty state with filters'
- end
-
- describe 'Edit issue' do
- let!(:issue) do
- create(:issue,
- author: user,
- assignees: [user],
- project: project)
- end
-
- before do
- visit edit_project_issue_path(project, issue)
- find('.js-zen-enter').click
- end
-
- it 'opens new issue popup' do
- expect(page).to have_content("Issue ##{issue.iid}")
- end
- end
-
- describe 'Editing issue assignee' do
- let!(:issue) do
- create(:issue,
- author: user,
- assignees: [user],
- project: project)
- end
-
- it 'allows user to select unassigned', :js do
- visit edit_project_issue_path(project, issue)
-
- expect(page).to have_content "Assignee #{user.name}"
-
- first('.js-user-search').click
- click_link 'Unassigned'
-
- click_button 'Save changes'
-
- page.within('.assignee') do
- expect(page).to have_content 'None - assign yourself'
- end
-
- expect(issue.reload.assignees).to be_empty
- end
- end
-
- describe 'due date', :js do
- context 'on new form' do
- before do
- visit new_project_issue_path(project)
- end
-
- it 'saves with due date' do
- date = Date.today.at_beginning_of_month
-
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
- find('#issuable-due-date').click
-
- page.within '.pika-single' do
- click_button date.day
- end
-
- expect(find('#issuable-due-date').value).to eq date.to_s
-
- click_button 'Submit issue'
-
- page.within '.issuable-sidebar' do
- expect(page).to have_content date.to_s(:medium)
- end
- end
- end
-
- context 'on edit form' do
- let(:issue) { create(:issue, author: user, project: project, due_date: Date.today.at_beginning_of_month.to_s) }
-
- before do
- visit edit_project_issue_path(project, issue)
- end
-
- it 'saves with due date' do
- date = Date.today.at_beginning_of_month
-
- expect(find('#issuable-due-date').value).to eq date.to_s
-
- date = date.tomorrow
-
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
- find('#issuable-due-date').click
-
- page.within '.pika-single' do
- click_button date.day
- end
-
- expect(find('#issuable-due-date').value).to eq date.to_s
-
- click_button 'Save changes'
-
- page.within '.issuable-sidebar' do
- expect(page).to have_content date.to_s(:medium)
- end
- end
-
- it 'warns about version conflict' do
- issue.update(title: "New title")
-
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
-
- click_button 'Save changes'
-
- expect(page).to have_content 'Someone edited the issue the same time you did'
- end
- end
- end
-
- describe 'Issue info' do
- it 'links to current issue in breadcrubs' do
- issue = create(:issue, project: project)
-
- visit project_issue_path(project, issue)
-
- expect(find('.breadcrumbs-sub-title a')[:href]).to end_with(issue_path(issue))
- end
-
- it 'excludes award_emoji from comment count' do
- issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar')
- create(:award_emoji, awardable: issue)
-
- visit project_issues_path(project, assignee_id: user.id)
-
- expect(page).to have_content 'foobar'
- expect(page.all('.no-comments').first.text).to eq "0"
- end
- end
-
- describe 'Filter issue' do
- before do
- %w(foobar barbaz gitlab).each do |title|
- create(:issue,
- author: user,
- assignees: [user],
- project: project,
- title: title)
- end
-
- @issue = Issue.find_by(title: 'foobar')
- @issue.milestone = create(:milestone, project: project)
- @issue.assignees = []
- @issue.save
- end
-
- let(:issue) { @issue }
-
- it 'allows filtering by issues with no specified assignee' do
- visit project_issues_path(project, assignee_id: IssuableFinder::FILTER_NONE)
-
- expect(page).to have_content 'foobar'
- expect(page).not_to have_content 'barbaz'
- expect(page).not_to have_content 'gitlab'
- end
-
- it 'allows filtering by a specified assignee' do
- visit project_issues_path(project, assignee_id: user.id)
-
- expect(page).not_to have_content 'foobar'
- expect(page).to have_content 'barbaz'
- expect(page).to have_content 'gitlab'
- end
- end
-
- describe 'filter issue' do
- titles = %w[foo bar baz]
- titles.each_with_index do |title, index|
- let!(title.to_sym) do
- create(:issue, title: title,
- project: project,
- created_at: Time.now - (index * 60))
- end
- end
- let(:newer_due_milestone) { create(:milestone, project: project, due_date: '2013-12-11') }
- let(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') }
-
- it 'sorts by newest' do
- visit project_issues_path(project, sort: sort_value_created_date)
-
- expect(first_issue).to include('foo')
- expect(last_issue).to include('baz')
- end
-
- it 'sorts by most recently updated' do
- baz.updated_at = Time.now + 100
- baz.save
- visit project_issues_path(project, sort: sort_value_recently_updated)
-
- expect(first_issue).to include('baz')
- end
-
- describe 'sorting by due date' do
- before do
- foo.update(due_date: 1.day.from_now)
- bar.update(due_date: 6.days.from_now)
- end
-
- it 'sorts by due date' do
- visit project_issues_path(project, sort: sort_value_due_date)
-
- expect(first_issue).to include('foo')
- end
-
- it 'sorts by due date by excluding nil due dates' do
- bar.update(due_date: nil)
-
- visit project_issues_path(project, sort: sort_value_due_date)
-
- expect(first_issue).to include('foo')
- end
-
- context 'with a filter on labels' do
- let(:label) { create(:label, project: project) }
-
- before do
- create(:label_link, label: label, target: foo)
- end
-
- it 'sorts by least recently due date by excluding nil due dates' do
- bar.update(due_date: nil)
-
- visit project_issues_path(project, label_names: [label.name], sort: sort_value_due_date_later)
-
- expect(first_issue).to include('foo')
- end
- end
- end
-
- describe 'filtering by due date' do
- before do
- foo.update(due_date: 1.day.from_now)
- bar.update(due_date: 6.days.from_now)
- end
-
- it 'filters by none' do
- visit project_issues_path(project, due_date: Issue::NoDueDate.name)
-
- page.within '.issues-holder' do
- expect(page).not_to have_content('foo')
- expect(page).not_to have_content('bar')
- expect(page).to have_content('baz')
- end
- end
-
- it 'filters by any' do
- visit project_issues_path(project, due_date: Issue::AnyDueDate.name)
-
- page.within '.issues-holder' do
- expect(page).to have_content('foo')
- expect(page).to have_content('bar')
- expect(page).to have_content('baz')
- end
- end
-
- it 'filters by due this week' do
- foo.update(due_date: Date.today.beginning_of_week + 2.days)
- bar.update(due_date: Date.today.end_of_week)
- baz.update(due_date: Date.today - 8.days)
-
- visit project_issues_path(project, due_date: Issue::DueThisWeek.name)
-
- page.within '.issues-holder' do
- expect(page).to have_content('foo')
- expect(page).to have_content('bar')
- expect(page).not_to have_content('baz')
- end
- end
-
- it 'filters by due this month' do
- foo.update(due_date: Date.today.beginning_of_month + 2.days)
- bar.update(due_date: Date.today.end_of_month)
- baz.update(due_date: Date.today - 50.days)
-
- visit project_issues_path(project, due_date: Issue::DueThisMonth.name)
-
- page.within '.issues-holder' do
- expect(page).to have_content('foo')
- expect(page).to have_content('bar')
- expect(page).not_to have_content('baz')
- end
- end
-
- it 'filters by overdue' do
- foo.update(due_date: Date.today + 2.days)
- bar.update(due_date: Date.today + 20.days)
- baz.update(due_date: Date.yesterday)
-
- visit project_issues_path(project, due_date: Issue::Overdue.name)
-
- page.within '.issues-holder' do
- expect(page).not_to have_content('foo')
- expect(page).not_to have_content('bar')
- expect(page).to have_content('baz')
- end
- end
-
- it 'filters by due next month and previous two weeks' do
- foo.update(due_date: Date.today - 4.weeks)
- bar.update(due_date: (Date.today + 2.months).beginning_of_month)
- baz.update(due_date: Date.yesterday)
-
- visit project_issues_path(project, due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name)
-
- page.within '.issues-holder' do
- expect(page).not_to have_content('foo')
- expect(page).not_to have_content('bar')
- expect(page).to have_content('baz')
- end
- end
- end
-
- describe 'sorting by milestone' do
- before do
- foo.milestone = newer_due_milestone
- foo.save
- bar.milestone = later_due_milestone
- bar.save
- end
-
- it 'sorts by milestone' do
- visit project_issues_path(project, sort: sort_value_milestone)
-
- expect(first_issue).to include('foo')
- expect(last_issue).to include('baz')
- end
- end
-
- describe 'combine filter and sort' do
- let(:user2) { create(:user) }
-
- before do
- foo.assignees << user2
- foo.save
- bar.assignees << user2
- bar.save
- end
-
- it 'sorts with a filter applied' do
- visit project_issues_path(project, sort: sort_value_created_date, assignee_id: user2.id)
-
- expect(first_issue).to include('foo')
- expect(last_issue).to include('bar')
- expect(page).not_to have_content('baz')
- end
- 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
- stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
- project1.add_maintainer(user)
- visit namespace_project_issues_path(user.namespace, project1)
- end
-
- it 'changes incoming email address token', :js do
- find('.issuable-email-modal-btn').click
- previous_token = find('input#issuable_email').value
- find('.incoming-email-token-reset').click
-
- wait_for_requests
-
- expect(page).to have_no_field('issuable_email', with: previous_token)
- new_token = project1.new_issuable_address(user.reload, 'issue')
- expect(page).to have_field(
- 'issuable_email',
- with: new_token
- )
- end
- end
-
- describe 'update labels from issue#show', :js do
- let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
- let!(:label) { create(:label, project: project) }
-
- before do
- visit project_issue_path(project, issue)
- end
-
- it 'will not send ajax request when no data is changed' do
- page.within '.labels' do
- click_link 'Edit'
-
- find('.dropdown-menu-close', match: :first).click
-
- expect(page).not_to have_selector('.block-loading')
- end
- end
- end
-
- describe 'update assignee from issue#show' do
- let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
-
- context 'by authorized user' do
- it 'allows user to select unassigned', :js do
- visit project_issue_path(project, issue)
-
- page.within('.assignee') do
- expect(page).to have_content "#{user.name}"
-
- click_link 'Edit'
- click_link 'Unassigned'
- first('.title').click
- expect(page).to have_content 'None'
- end
-
- wait_for_requests
-
- expect(issue.reload.assignees).to be_empty
- end
-
- it 'allows user to select an assignee', :js do
- issue2 = create(:issue, project: project, author: user)
- visit project_issue_path(project, issue2)
-
- page.within('.assignee') do
- expect(page).to have_content "None"
- end
-
- page.within '.assignee' do
- click_link 'Edit'
- end
-
- page.within '.dropdown-menu-user' do
- click_link user.name
- end
-
- page.within('.assignee') do
- expect(page).to have_content user.name
- end
- end
-
- it 'allows user to unselect themselves', :js do
- issue2 = create(:issue, project: project, author: user)
-
- visit project_issue_path(project, issue2)
-
- def close_dropdown_menu_if_visible
- find('.dropdown-menu-toggle', visible: :all).tap do |toggle|
- toggle.click if toggle.visible?
- end
- end
-
- page.within '.assignee' do
- click_link 'Edit'
- click_link user.name
-
- close_dropdown_menu_if_visible
-
- page.within '.value .author' do
- expect(page).to have_content user.name
- end
-
- click_link 'Edit'
- click_link user.name
-
- close_dropdown_menu_if_visible
-
- page.within '.value .assign-yourself' do
- expect(page).to have_content "None"
- end
- end
- end
- end
-
- context 'by unauthorized user' do
- let(:guest) { create(:user) }
-
- before do
- project.add_guest(guest)
- end
-
- it 'shows assignee text', :js do
- sign_out(:user)
- sign_in(guest)
-
- visit project_issue_path(project, issue)
- expect(page).to have_content issue.assignees.first.name
- end
- end
- end
-
- describe 'update milestone from issue#show' do
- let!(:issue) { create(:issue, project: project, author: user) }
- let!(:milestone) { create(:milestone, project: project) }
-
- context 'by authorized user' do
- it 'allows user to select unassigned', :js do
- visit project_issue_path(project, issue)
-
- page.within('.milestone') do
- expect(page).to have_content "None"
- end
-
- find('.block.milestone .edit-link').click
- sleep 2 # wait for ajax stuff to complete
- first('.dropdown-content li').click
- sleep 2
- page.within('.milestone') do
- expect(page).to have_content 'None'
- end
-
- expect(issue.reload.milestone).to be_nil
- end
-
- it 'allows user to de-select milestone', :js do
- visit project_issue_path(project, issue)
-
- page.within('.milestone') do
- click_link 'Edit'
- click_link milestone.title
-
- page.within '.value' do
- expect(page).to have_content milestone.title
- end
-
- click_link 'Edit'
- click_link milestone.title
-
- page.within '.value' do
- expect(page).to have_content 'None'
- end
- end
- end
- end
-
- context 'by unauthorized user' do
- let(:guest) { create(:user) }
-
- before do
- project.add_guest(guest)
- issue.milestone = milestone
- issue.save
- end
-
- it 'shows milestone text', :js do
- sign_out(:user)
- sign_in(guest)
-
- visit project_issue_path(project, issue)
- expect(page).to have_content milestone.title
- end
- end
- end
-
- describe 'new issue' do
- let!(:issue) { create(:issue, project: project) }
-
- context 'by unauthenticated user' do
- before do
- sign_out(:user)
- end
-
- it 'redirects to signin then back to new issue after signin' do
- visit project_issues_path(project)
-
- page.within '.nav-controls' do
- click_link 'New issue'
- end
-
- expect(current_path).to eq new_user_session_path
-
- gitlab_sign_in(create(:user))
-
- expect(current_path).to eq new_project_issue_path(project)
- end
- end
-
- it 'clears local storage after creating a new issue', :js do
- 2.times do
- visit new_project_issue_path(project)
- wait_for_requests
-
- expect(page).to have_field('Title', with: '')
-
- fill_in 'issue_title', with: 'bug 345'
- fill_in 'issue_description', with: 'bug description'
-
- click_button 'Submit issue'
- end
- end
-
- context 'dropzone upload file', :js do
- before do
- visit new_project_issue_path(project)
- end
-
- it 'uploads file when dragging into textarea' do
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
-
- expect(page.find_field("issue_description").value).to have_content 'banana_sample'
- end
-
- it "doesn't add double newline to end of a single attachment markdown" do
- dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
-
- expect(page.find_field("issue_description").value).not_to match /\n\n$/
- end
-
- it "cancels a file upload correctly" do
- slow_requests do
- dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
-
- click_button 'Cancel'
- end
-
- expect(page).to have_button('Attach a file')
- expect(page).not_to have_button('Cancel')
- expect(page).not_to have_selector('.uploading-progress-container', visible: true)
- end
- end
-
- context 'form filled by URL parameters' do
- let(:project) { create(:project, :public, :repository) }
-
- before do
- project.repository.create_file(
- user,
- '.gitlab/issue_templates/bug.md',
- 'this is a test "bug" template',
- message: 'added issue template',
- branch_name: 'master')
-
- visit new_project_issue_path(project, issuable_template: 'bug')
- end
-
- it 'fills in template' do
- expect(find('.js-issuable-selector .dropdown-toggle-text')).to have_content('bug')
- end
- end
-
- context 'suggestions', :js do
- it 'displays list of related issues' do
- create(:issue, project: project, title: 'test issue')
-
- visit new_project_issue_path(project)
-
- fill_in 'issue_title', with: issue.title
-
- expect(page).to have_selector('.suggestion-item', count: 1)
- end
- end
- end
-
- 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 project_issues_path(project)
- click_button('Email a new issue')
- end
-
- it 'click the button to show modal for the new email' do
- page.within '#issuable-email-modal' do
- email = project.new_issuable_address(user, 'issue')
-
- expect(page).to have_selector("input[value='#{email}']")
- end
- end
- end
-
- context 'with existing issues' do
- let!(:issue) { create(:issue, project: project, author: user) }
-
- it_behaves_like 'show the email in the modal'
- end
-
- context 'without existing issues' do
- it_behaves_like 'show the email in the modal'
- end
- end
-
- describe 'due date' do
- context 'update due on issue#show', :js do
- let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
-
- before do
- visit project_issue_path(project, issue)
- end
-
- it 'adds due date to issue' do
- date = Date.today.at_beginning_of_month + 2.days
-
- page.within '.due_date' do
- click_link 'Edit'
-
- page.within '.pika-single' do
- click_button date.day
- end
-
- wait_for_requests
-
- expect(find('.value').text).to have_content date.strftime('%b %-d, %Y')
- end
- end
-
- it 'removes due date from issue' do
- date = Date.today.at_beginning_of_month + 2.days
-
- page.within '.due_date' do
- click_link 'Edit'
-
- page.within '.pika-single' do
- click_button date.day
- end
-
- wait_for_requests
-
- expect(page).to have_no_content 'None'
-
- click_link 'remove due date'
- expect(page).to have_content 'None'
- end
- end
- end
- end
-
- describe 'title issue#show', :js do
- it 'updates the title', :js do
- issue = create(:issue, author: user, assignees: [user], project: project, title: 'new title')
-
- visit project_issue_path(project, issue)
-
- expect(page).to have_text("new title")
-
- issue.update(title: "updated title")
-
- wait_for_requests
- expect(page).to have_text("updated title")
- end
- end
-
- describe 'confidential issue#show', :js do
- it 'shows confidential sibebar information as confidential and can be turned off' do
- issue = create(:issue, :confidential, project: project)
-
- visit project_issue_path(project, issue)
-
- expect(page).to have_css('.issuable-note-warning')
- expect(find('.issuable-sidebar-item.confidentiality')).to have_css('.is-active')
- expect(find('.issuable-sidebar-item.confidentiality')).not_to have_css('.not-active')
-
- find('.confidential-edit').click
- expect(page).to have_css('.sidebar-item-warning-message')
-
- within('.sidebar-item-warning-message') do
- find('.btn-close').click
- end
-
- wait_for_requests
-
- visit project_issue_path(project, issue)
-
- expect(page).not_to have_css('.is-active')
- end
- end
- end
-end
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index cab86f3fd94..cd62bab412a 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -50,7 +50,7 @@ describe 'Merge request > User sees versions', :js do
expect(page).to have_content 'latest version'
end
- expect(page).to have_content '8 Files'
+ expect(page).to have_content '8 files'
end
it_behaves_like 'allows commenting',
@@ -84,7 +84,7 @@ describe 'Merge request > User sees versions', :js do
end
it 'shows comments that were last relevant at that version' do
- expect(page).to have_content '5 Files'
+ expect(page).to have_content '5 files'
position = Gitlab::Diff::Position.new(
old_path: ".gitmodules",
@@ -128,12 +128,10 @@ describe 'Merge request > User sees versions', :js do
diff_id: merge_request_diff3.id,
start_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
)
- expect(page).to have_content '4 Files'
+ expect(page).to have_content '4 files'
- additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-addition')
- .ancestor('.diff-stats-group').text
- deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-deletion')
- .ancestor('.diff-stats-group').text
+ additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-addition-line').text
+ deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-deletion-line').text
expect(additions_content).to eq '15'
expect(deletions_content).to eq '6'
@@ -156,12 +154,10 @@ describe 'Merge request > User sees versions', :js do
end
it 'show diff between new and old version' do
- additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-addition')
- .ancestor('.diff-stats-group').text
- deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-deletion')
- .ancestor('.diff-stats-group').text
+ additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-addition-line').text
+ deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-deletion-line').text
- expect(page).to have_content '4 Files'
+ expect(page).to have_content '4 files'
expect(additions_content).to eq '15'
expect(deletions_content).to eq '6'
end
@@ -171,7 +167,7 @@ describe 'Merge request > User sees versions', :js do
page.within '.mr-version-dropdown' do
expect(page).to have_content 'latest version'
end
- expect(page).to have_content '8 Files'
+ expect(page).to have_content '8 files'
end
it_behaves_like 'allows commenting',
@@ -197,7 +193,7 @@ describe 'Merge request > User sees versions', :js do
find('.btn-default').click
click_link 'version 1'
end
- expect(page).to have_content '0 Files'
+ expect(page).to have_content '0 files'
end
end
@@ -223,7 +219,7 @@ describe 'Merge request > User sees versions', :js do
expect(page).to have_content 'version 1'
end
- expect(page).to have_content '0 Files'
+ expect(page).to have_content '0 files'
end
end
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
new file mode 100644
index 00000000000..86da866a927
--- /dev/null
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Project > Settings > CI/CD > Container registry tag expiration policy', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+
+ context 'as owner' do
+ before do
+ sign_in(user)
+ visit project_settings_ci_cd_path(project)
+ end
+
+ it 'section is available' do
+ settings_block = find('#js-registry-policies')
+ expect(settings_block).to have_text 'Container Registry tag expiration policy'
+ end
+
+ it 'Save expiration policy submit the form', :js do
+ within '#js-registry-policies' do
+ within '.card-body' do
+ click_button(class: 'gl-toggle')
+ select('7 days until tags are automatically removed', from: 'expiration-policy-interval')
+ select('Every day', from: 'expiration-policy-schedule')
+ select('50 tags per image name', from: 'expiration-policy-latest')
+ fill_in('expiration-policy-name-matching', with: '*-production')
+ end
+ submit_button = find('.card-footer .btn.btn-success')
+ expect(submit_button).not_to be_disabled
+ submit_button.click
+ end
+ flash_text = find('.flash-text')
+ expect(flash_text).to have_content('Expiration policy successfully saved.')
+ end
+ end
+end
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 99285011405..7e0ee861b18 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -46,8 +46,6 @@ describe 'Multi-file editor new directory', :js do
find('.js-ide-commit-mode').click
- click_button 'Stage'
-
fill_in('commit-message', with: 'commit message ide')
find(:css, ".js-ide-commit-new-mr input").set(false)
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index 780575a5975..eba33168006 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -36,8 +36,6 @@ describe 'Multi-file editor new file', :js do
find('.js-ide-commit-mode').click
- click_button 'Stage'
-
fill_in('commit-message', with: 'commit message ide')
find(:css, ".js-ide-commit-new-mr input").set(false)
diff --git a/spec/frontend/diffs/components/compare_versions_spec.js b/spec/frontend/diffs/components/compare_versions_spec.js
index 7f47677f56c..7648c39976c 100644
--- a/spec/frontend/diffs/components/compare_versions_spec.js
+++ b/spec/frontend/diffs/components/compare_versions_spec.js
@@ -50,8 +50,7 @@ describe('CompareVersions', () => {
expect(treeListBtn.exists()).toBe(true);
expect(treeListBtn.attributes('title')).toBe('Hide file browser');
- expect(treeListBtn.findAll(Icon).length).not.toBe(0);
- expect(treeListBtn.find(Icon).props('name')).toBe('collapse-left');
+ expect(treeListBtn.find(Icon).props('name')).toBe('file-tree');
});
it('should render comparison dropdowns with correct values', () => {
diff --git a/spec/frontend/diffs/components/diff_stats_spec.js b/spec/frontend/diffs/components/diff_stats_spec.js
index 4482abf18c1..aa5c7f6278a 100644
--- a/spec/frontend/diffs/components/diff_stats_spec.js
+++ b/spec/frontend/diffs/components/diff_stats_spec.js
@@ -1,5 +1,4 @@
import { shallowMount } from '@vue/test-utils';
-import Icon from '~/vue_shared/components/icon.vue';
import DiffStats from '~/diffs/components/diff_stats.vue';
describe('diff_stats', () => {
@@ -24,18 +23,11 @@ describe('diff_stats', () => {
},
});
- const findIcon = name =>
- wrapper
- .findAll(Icon)
- .filter(c => c.attributes('name') === name)
- .at(0).element.parentNode;
+ const findFileLine = name => wrapper.find(name);
+ const additions = findFileLine('.js-file-addition-line');
+ const deletions = findFileLine('.js-file-deletion-line');
- const additions = findIcon('file-addition');
- const deletions = findIcon('file-deletion');
- const filesChanged = findIcon('doc-code');
-
- expect(additions.textContent).toContain('100');
- expect(deletions.textContent).toContain('200');
- expect(filesChanged.textContent).toContain('300');
+ expect(additions.text()).toBe('100');
+ expect(deletions.text()).toBe('200');
});
});
diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js
index e7b34aa3e7a..a8e48f0b85e 100644
--- a/spec/frontend/ide/stores/actions/file_spec.js
+++ b/spec/frontend/ide/stores/actions/file_spec.js
@@ -514,6 +514,8 @@ describe('IDE store file actions', () => {
describe('changeFileContent', () => {
let tmpFile;
+ const callAction = (content = 'content\n') =>
+ store.dispatch('changeFileContent', { path: tmpFile.path, content });
beforeEach(() => {
tmpFile = file('tmpFile');
@@ -523,11 +525,7 @@ describe('IDE store file actions', () => {
});
it('updates file content', done => {
- store
- .dispatch('changeFileContent', {
- path: tmpFile.path,
- content: 'content\n',
- })
+ callAction()
.then(() => {
expect(tmpFile.content).toBe('content\n');
@@ -537,11 +535,7 @@ describe('IDE store file actions', () => {
});
it('adds a newline to the end of the file if it doesnt already exist', done => {
- store
- .dispatch('changeFileContent', {
- path: tmpFile.path,
- content: 'content',
- })
+ callAction('content')
.then(() => {
expect(tmpFile.content).toBe('content\n');
@@ -551,11 +545,7 @@ describe('IDE store file actions', () => {
});
it('adds file into changedFiles array', done => {
- store
- .dispatch('changeFileContent', {
- path: tmpFile.path,
- content: 'content',
- })
+ callAction()
.then(() => {
expect(store.state.changedFiles.length).toBe(1);
@@ -564,7 +554,7 @@ describe('IDE store file actions', () => {
.catch(done.fail);
});
- it('adds file once into changedFiles array', done => {
+ it('adds file not more than once into changedFiles array', done => {
store
.dispatch('changeFileContent', {
path: tmpFile.path,
@@ -604,6 +594,52 @@ describe('IDE store file actions', () => {
.catch(done.fail);
});
+ describe('when `gon.feature.stageAllByDefault` is true', () => {
+ const originalGonFeatures = Object.assign({}, gon.features);
+
+ beforeAll(() => {
+ gon.features = { stageAllByDefault: true };
+ });
+
+ afterAll(() => {
+ gon.features = originalGonFeatures;
+ });
+
+ it('adds file into stagedFiles array', done => {
+ store
+ .dispatch('changeFileContent', {
+ path: tmpFile.path,
+ content: 'content',
+ })
+ .then(() => {
+ expect(store.state.stagedFiles.length).toBe(1);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('adds file not more than once into stagedFiles array', done => {
+ store
+ .dispatch('changeFileContent', {
+ path: tmpFile.path,
+ content: 'content',
+ })
+ .then(() =>
+ store.dispatch('changeFileContent', {
+ path: tmpFile.path,
+ content: 'content 123',
+ }),
+ )
+ .then(() => {
+ expect(store.state.stagedFiles.length).toBe(1);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
it('bursts unused seal', done => {
store
.dispatch('changeFileContent', {
diff --git a/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap b/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap
index bef4674bd8b..d26df308b97 100644
--- a/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap
+++ b/spec/frontend/registry/settings/components/__snapshots__/settings_form_spec.js.snap
@@ -106,7 +106,7 @@ exports[`Settings Form renders 1`] = `
<glformgroup-stub
id="expiration-policy-latest-group"
- label="Expiration latest:"
+ label="Number of tags to retain:"
label-align="right"
label-cols="3"
label-for="expiration-policy-latest"
@@ -136,7 +136,7 @@ exports[`Settings Form renders 1`] = `
<glformgroup-stub
id="expiration-policy-name-matching-group"
invalid-feedback="The value of this input should be less than 255 characters"
- label="Expire Docker tags with name matching:"
+ label="Expire Docker tags that match this regex:"
label-align="right"
label-cols="3"
label-for="expiration-policy-name-matching"
diff --git a/spec/frontend/registry/settings/store/actions_spec.js b/spec/frontend/registry/settings/store/actions_spec.js
index 71c815cd19c..80fb800ac3a 100644
--- a/spec/frontend/registry/settings/store/actions_spec.js
+++ b/spec/frontend/registry/settings/store/actions_spec.js
@@ -44,7 +44,9 @@ describe('Actions Registry Store', () => {
};
const payload = {
- tag_expiration_policies: 'foo',
+ data: {
+ container_expiration_policy: 'foo',
+ },
};
it('should fetch the data from the API', done => {
@@ -56,7 +58,7 @@ describe('Actions Registry Store', () => {
[],
[
{ type: 'toggleLoading' },
- { type: 'receiveSettingsSuccess', payload: payload.tag_expiration_policies },
+ { type: 'receiveSettingsSuccess', payload: payload.data.container_expiration_policy },
{ type: 'toggleLoading' },
],
done,
@@ -83,7 +85,9 @@ describe('Actions Registry Store', () => {
};
const payload = {
- tag_expiration_policies: 'foo',
+ data: {
+ tag_expiration_policies: 'foo',
+ },
};
it('should fetch the data from the API', done => {
@@ -95,11 +99,11 @@ describe('Actions Registry Store', () => {
[],
[
{ type: 'toggleLoading' },
- { type: 'receiveSettingsSuccess', payload: payload.tag_expiration_policies },
+ { type: 'receiveSettingsSuccess', payload: payload.data.container_expiration_policy },
{ type: 'toggleLoading' },
],
() => {
- expect(createFlash).toHaveBeenCalledWith(UPDATE_SETTINGS_SUCCESS_MESSAGE);
+ expect(createFlash).toHaveBeenCalledWith(UPDATE_SETTINGS_SUCCESS_MESSAGE, 'success');
done();
},
);
diff --git a/spec/frontend/repository/components/last_commit_spec.js b/spec/frontend/repository/components/last_commit_spec.js
index 30f701ed77a..d2576ec26b7 100644
--- a/spec/frontend/repository/components/last_commit_spec.js
+++ b/spec/frontend/repository/components/last_commit_spec.js
@@ -6,7 +6,7 @@ import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link
let vm;
function createCommitData(data = {}) {
- return {
+ const defaultData = {
sha: '123456789',
title: 'Commit title',
message: 'Commit message',
@@ -26,8 +26,8 @@ function createCommitData(data = {}) {
group: {},
},
},
- ...data,
};
+ return Object.assign(defaultData, data);
}
function factory(commit = createCommitData(), loading = false) {
@@ -46,6 +46,8 @@ function factory(commit = createCommitData(), loading = false) {
vm.vm.$apollo.queries.commit.loading = loading;
}
+const emptyMessageClass = 'font-italic';
+
describe('Repository last commit component', () => {
afterEach(() => {
vm.destroy();
@@ -135,4 +137,12 @@ describe('Repository last commit component', () => {
expect(vm.element).toMatchSnapshot();
});
});
+
+ it('sets correct CSS class if the commit message is empty', () => {
+ factory(createCommitData({ message: '' }));
+
+ return vm.vm.$nextTick().then(() => {
+ expect(vm.find('.item-title').classes()).toContain(emptyMessageClass);
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/components/changed_file_icon_spec.js b/spec/frontend/vue_shared/components/changed_file_icon_spec.js
index 9197cb8bc00..3a52941a06e 100644
--- a/spec/frontend/vue_shared/components/changed_file_icon_spec.js
+++ b/spec/frontend/vue_shared/components/changed_file_icon_spec.js
@@ -57,10 +57,10 @@ describe('Changed file icon', () => {
describe.each`
file | iconName | tooltipText | desc
- ${changedFile()} | ${'file-modified'} | ${'Unstaged modification'} | ${'with file changed'}
+ ${changedFile()} | ${'file-modified-solid'} | ${'Unstaged modification'} | ${'with file changed'}
${stagedFile()} | ${'file-modified-solid'} | ${'Staged modification'} | ${'with file staged'}
- ${changedAndStagedFile()} | ${'file-modified'} | ${'Unstaged and staged modification'} | ${'with file changed and staged'}
- ${newFile()} | ${'file-addition'} | ${'Unstaged addition'} | ${'with file new'}
+ ${changedAndStagedFile()} | ${'file-modified-solid'} | ${'Unstaged and staged modification'} | ${'with file changed and staged'}
+ ${newFile()} | ${'file-addition-solid'} | ${'Unstaged addition'} | ${'with file new'}
`('$desc', ({ file, iconName, tooltipText }) => {
beforeEach(() => {
factory({ file });
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index f415fb05b5b..a775c69335e 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -357,10 +357,10 @@ describe MarkupHelper do
describe '#markup_unsafe' do
subject { helper.markup_unsafe(file_name, text, context) }
+ let_it_be(:project_base) { create(:project, :repository) }
+ let_it_be(:context) { { project: project_base } }
let(:file_name) { 'foo.bar' }
let(:text) { 'Noël' }
- let(:project_base) { build(:project, :repository) }
- let(:context) { { project: project_base } }
context 'when text is missing' do
let(:text) { nil }
@@ -383,12 +383,21 @@ describe MarkupHelper do
context 'when renderer returns an error' do
before do
- allow(Banzai).to receive(:render).and_raise("An error")
+ allow(Banzai).to receive(:render).and_raise(StandardError, "An error")
end
it 'returns html (rendered by ActionView:TextHelper)' do
is_expected.to eq('<p>Noël</p>')
end
+
+ it 'logs the error' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(StandardError),
+ project_id: project.id, file_name: 'foo.md', context: context
+ )
+
+ subject
+ end
end
end
diff --git a/spec/javascripts/ide/components/commit_sidebar/form_spec.js b/spec/javascripts/ide/components/commit_sidebar/form_spec.js
index fdbabf84e25..2ee0b94582c 100644
--- a/spec/javascripts/ide/components/commit_sidebar/form_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/form_spec.js
@@ -33,6 +33,12 @@ describe('IDE commit form', () => {
});
describe('compact', () => {
+ beforeEach(done => {
+ vm.isCompact = true;
+
+ vm.$nextTick(done);
+ });
+
it('renders commit button in compact mode', () => {
expect(vm.$el.querySelector('.btn-primary')).not.toBeNull();
expect(vm.$el.querySelector('.btn-primary').textContent).toContain('Commit');
@@ -61,7 +67,7 @@ describe('IDE commit form', () => {
});
});
- it('toggles activity bar vie when clicking commit button', done => {
+ it('toggles activity bar view when clicking commit button', done => {
vm.$el.querySelector('.btn-primary').click();
vm.$nextTick(() => {
@@ -104,6 +110,17 @@ describe('IDE commit form', () => {
});
});
+ it('always opens itself in full view current activity view is not commit view when clicking commit button', done => {
+ vm.$el.querySelector('.btn-primary').click();
+
+ vm.$nextTick(() => {
+ expect(store.state.currentActivityView).toBe(activityBarViews.commit);
+ expect(vm.isCompact).toBe(false);
+
+ done();
+ });
+ });
+
describe('discard draft button', () => {
it('hidden when commitMessage is empty', () => {
expect(vm.$el.querySelector('.btn-default').textContent).toContain('Collapse');
diff --git a/spec/javascripts/ide/components/repo_tab_spec.js b/spec/javascripts/ide/components/repo_tab_spec.js
index 3b52f279bf2..7466ed5468b 100644
--- a/spec/javascripts/ide/components/repo_tab_spec.js
+++ b/spec/javascripts/ide/components/repo_tab_spec.js
@@ -93,13 +93,13 @@ describe('RepoTab', () => {
Vue.nextTick()
.then(() => {
- expect(vm.$el.querySelector('.file-modified')).toBeNull();
+ expect(vm.$el.querySelector('.file-modified-solid')).toBeNull();
vm.$el.dispatchEvent(new Event('mouseout'));
})
.then(Vue.nextTick)
.then(() => {
- expect(vm.$el.querySelector('.file-modified')).not.toBeNull();
+ expect(vm.$el.querySelector('.file-modified-solid')).not.toBeNull();
done();
})
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 9c24f20ca9c..d582462d542 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -225,6 +225,35 @@ describe('Multi-file store actions', () => {
.catch(done.fail);
});
+ describe('when `gon.feature.stageAllByDefault` is true', () => {
+ const originalGonFeatures = Object.assign({}, gon.features);
+
+ beforeAll(() => {
+ gon.features = { stageAllByDefault: true };
+ });
+
+ afterAll(() => {
+ gon.features = originalGonFeatures;
+ });
+
+ it('adds tmp file to staged files', done => {
+ const name = 'test';
+
+ store
+ .dispatch('createTempEntry', {
+ name,
+ branchId: 'mybranch',
+ type: 'blob',
+ })
+ .then(() => {
+ expect(store.state.stagedFiles).toEqual([jasmine.objectContaining({ name })]);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
it('adds tmp file to open files', done => {
const name = 'test';
@@ -255,41 +284,25 @@ describe('Multi-file store actions', () => {
type: 'blob',
})
.then(() => {
- const f = store.state.entries[name];
-
- expect(store.state.changedFiles.length).toBe(1);
- expect(store.state.changedFiles[0].name).toBe(f.name);
+ expect(store.state.changedFiles).toEqual([
+ jasmine.objectContaining({ name, tempFile: true }),
+ ]);
done();
})
.catch(done.fail);
});
- it('sets tmp file as active', done => {
- testAction(
- createTempEntry,
- {
- name: 'test',
- branchId: 'mybranch',
- type: 'blob',
- },
- store.state,
- [
- { type: types.CREATE_TMP_ENTRY, payload: jasmine.any(Object) },
- { type: types.TOGGLE_FILE_OPEN, payload: 'test' },
- { type: types.ADD_FILE_TO_CHANGED, payload: 'test' },
- ],
- jasmine.arrayContaining([
- {
- type: 'setFileActive',
- payload: 'test',
- },
- {
- type: 'triggerFilesChange',
- },
- ]),
- done,
+ it('sets tmp file as active', () => {
+ const dispatch = jasmine.createSpy();
+ const commit = jasmine.createSpy();
+
+ createTempEntry(
+ { state: store.state, getters: store.getters, dispatch, commit },
+ { name: 'test', branchId: 'mybranch', type: 'blob' },
);
+
+ expect(dispatch).toHaveBeenCalledWith('setFileActive', 'test');
});
it('creates flash message if file already exists', done => {
@@ -800,6 +813,33 @@ describe('Multi-file store actions', () => {
});
});
+ describe('when `gon.feature.stageAllByDefault` is true', () => {
+ const originalGonFeatures = Object.assign({}, gon.features);
+
+ beforeAll(() => {
+ gon.features = { stageAllByDefault: true };
+ });
+
+ afterAll(() => {
+ gon.features = originalGonFeatures;
+ });
+
+ it('by default renames an entry and stages it', () => {
+ const dispatch = jasmine.createSpy();
+ const commit = jasmine.createSpy();
+
+ renameEntry(
+ { dispatch, commit, state: store.state, getters: store.getters },
+ { path: 'orig', name: 'renamed' },
+ );
+
+ expect(commit.calls.allArgs()).toEqual([
+ [types.RENAME_ENTRY, { path: 'orig', name: 'renamed', parentPath: undefined }],
+ [types.STAGE_CHANGE, jasmine.objectContaining({ path: 'renamed' })],
+ ]);
+ });
+ });
+
it('by default renames an entry and adds to changed', done => {
testAction(
renameEntry,
@@ -819,12 +859,12 @@ describe('Multi-file store actions', () => {
payload: 'renamed',
},
],
- [{ type: 'burstUnusedSeal' }, { type: 'triggerFilesChange' }],
+ jasmine.any(Object),
done,
);
});
- it('if not changed, completely unstages entry if renamed to original', done => {
+ it('if not changed, completely unstages and discards entry if renamed to original', done => {
testAction(
renameEntry,
{ path: 'renamed', name: 'orig' },
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 9dea74f6345..058afddd73f 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -1735,6 +1735,39 @@ module Gitlab
it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:test1 dependencies the build2 should be part of needs') }
end
+
+ context 'needs with a Hash type and dependencies with a string type that are mismatching' do
+ let(:needs) do
+ [
+ "build1",
+ { job: "build2" }
+ ]
+ end
+ let(:dependencies) { %w(build3) }
+
+ it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:test1 dependencies the build3 should be part of needs') }
+ end
+
+ context 'needs with an array type and dependency with a string type' do
+ let(:needs) { %w(build1) }
+ let(:dependencies) { 'deploy' }
+
+ it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:test1 dependencies should be an array of strings') }
+ end
+
+ context 'needs with a string type and dependency with an array type' do
+ let(:needs) { 'build1' }
+ let(:dependencies) { %w(deploy) }
+
+ it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:test1:needs config can only be a hash or an array') }
+ end
+
+ context 'needs with a Hash type and dependency with a string type' do
+ let(:needs) { { job: 'build1' } }
+ let(:dependencies) { 'deploy' }
+
+ it { expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError, 'jobs:test1 dependencies should be an array of strings') }
+ end
end
context 'with when/rules conflict' do
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 7ec655eb113..c2fc228d34a 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -57,7 +57,7 @@ describe Gitlab::Git::Commit, :seed_helper do
it { expect(@commit.different_committer?).to be_truthy }
it { expect(@commit.parents).to eq(@gitlab_parents) }
it { expect(@commit.parent_id).to eq(@parents.first.oid) }
- it { expect(@commit.no_commit_message).to eq("--no commit message") }
+ it { expect(@commit.no_commit_message).to eq("No commit message") }
after do
# Erase the new commit so other tests get the original repo
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index a9d79454dd5..782d1ac4552 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -277,7 +277,7 @@ describe Commit do
describe '#title' do
it "returns no_commit_message when safe_message is blank" do
allow(commit).to receive(:safe_message).and_return('')
- expect(commit.title).to eq("--no commit message")
+ expect(commit.title).to eq("No commit message")
end
it 'truncates a message without a newline at natural break to 80 characters' do
@@ -308,7 +308,7 @@ eos
describe '#full_title' do
it "returns no_commit_message when safe_message is blank" do
allow(commit).to receive(:safe_message).and_return('')
- expect(commit.full_title).to eq("--no commit message")
+ expect(commit.full_title).to eq("No commit message")
end
it "returns entire message if there is no newline" do
@@ -330,7 +330,7 @@ eos
it 'returns no_commit_message when safe_message is blank' do
allow(commit).to receive(:safe_message).and_return(nil)
- expect(commit.description).to eq('--no commit message')
+ expect(commit.description).to eq('No commit message')
end
it 'returns description of commit message if title less than 100 characters' do
diff --git a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
index 0e8fe4987b9..f80a3401134 100644
--- a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
+++ b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb
@@ -52,8 +52,8 @@ describe 'Mark snippet as spam' do
end
it 'marks snippet as spam' do
- expect_next_instance_of(SpamService) do |instance|
- expect(instance).to receive(:mark_as_spam!)
+ expect_next_instance_of(Spam::MarkAsSpamService) do |instance|
+ expect(instance).to receive(:execute)
end
post_graphql_mutation(mutation, current_user: current_user)
diff --git a/spec/services/spam/mark_as_spam_service_spec.rb b/spec/services/spam/mark_as_spam_service_spec.rb
new file mode 100644
index 00000000000..cba9d6a39cb
--- /dev/null
+++ b/spec/services/spam/mark_as_spam_service_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Spam::MarkAsSpamService do
+ let(:user_agent_detail) { build(:user_agent_detail) }
+ let(:spammable) { build(:issue, user_agent_detail: user_agent_detail) }
+ let(:fake_akismet_service) { double(:akismet_service, submit_spam: true) }
+
+ subject { described_class.new(spammable: spammable) }
+
+ describe '#execute' do
+ before do
+ allow(subject).to receive(:akismet).and_return(fake_akismet_service)
+ end
+
+ context 'when the spammable object is not submittable' do
+ before do
+ allow(spammable).to receive(:submittable_as_spam?).and_return false
+ end
+
+ it 'does not submit as spam' do
+ expect(subject.execute).to be_falsey
+ end
+ end
+
+ context 'spam is submitted successfully' do
+ before do
+ allow(spammable).to receive(:submittable_as_spam?).and_return true
+ allow(fake_akismet_service).to receive(:submit_spam).and_return true
+ end
+
+ it 'submits as spam' do
+ expect(subject.execute).to be_truthy
+ end
+
+ it "updates the spammable object's user agent detail as being submitted as spam" do
+ expect(user_agent_detail).to receive(:update_attribute)
+
+ subject.execute
+ end
+
+ context 'when Akismet does not consider it spam' do
+ it 'does not update the spammable object as spam' do
+ allow(fake_akismet_service).to receive(:submit_spam).and_return false
+
+ expect(subject.execute).to be_falsey
+ end
+ end
+ end
+ end
+end