summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/boards/issues_controller_spec.rb76
-rw-r--r--spec/controllers/projects/boards/lists_controller_spec.rb2
-rw-r--r--spec/controllers/projects_controller_spec.rb11
-rw-r--r--spec/factories/boards.rb1
-rw-r--r--spec/factories/events.rb12
-rw-r--r--spec/factories/lists.rb6
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb233
-rw-r--r--spec/features/boards/boards_spec.rb217
-rw-r--r--spec/features/boards/new_issue_spec.rb5
-rw-r--r--spec/features/boards/sidebar_spec.rb77
-rw-r--r--spec/features/merge_requests/toggler_behavior_spec.rb2
-rw-r--r--spec/features/merge_requests/user_uses_slash_commands_spec.rb76
-rw-r--r--spec/finders/contributed_projects_finder_spec.rb13
-rw-r--r--spec/fixtures/api/schemas/issue.json1
-rw-r--r--spec/fixtures/api/schemas/list.json2
-rw-r--r--spec/javascripts/abuse_reports_spec.js.es64
-rw-r--r--spec/javascripts/activities_spec.js.es67
-rw-r--r--spec/javascripts/awards_handler_spec.js6
-rw-r--r--spec/javascripts/behaviors/autosize_spec.js4
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js2
-rw-r--r--spec/javascripts/behaviors/requires_input_spec.js8
-rw-r--r--spec/javascripts/boards/boards_store_spec.js.es640
-rw-r--r--spec/javascripts/boards/issue_card_spec.js.es6191
-rw-r--r--spec/javascripts/boards/issue_spec.js.es623
-rw-r--r--spec/javascripts/boards/list_spec.js.es623
-rw-r--r--spec/javascripts/boards/modal_store_spec.js.es6132
-rw-r--r--spec/javascripts/bootstrap_linked_tabs_spec.js.es626
-rw-r--r--spec/javascripts/build_spec.js.es615
-rw-r--r--spec/javascripts/commits_spec.js.es620
-rw-r--r--spec/javascripts/dashboard_spec.js.es66
-rw-r--r--spec/javascripts/datetime_utility_spec.js.es62
-rw-r--r--spec/javascripts/diff_comments_store_spec.js.es67
-rw-r--r--spec/javascripts/environments/environment_actions_spec.js.es63
-rw-r--r--spec/javascripts/environments/environment_external_url_spec.js.es63
-rw-r--r--spec/javascripts/environments/environment_item_spec.js.es65
-rw-r--r--spec/javascripts/environments/environment_rollback_spec.js.es64
-rw-r--r--spec/javascripts/environments/environment_spec.js.es610
-rw-r--r--spec/javascripts/environments/environment_stop_spec.js.es64
-rw-r--r--spec/javascripts/environments/environments_store_spec.js.es65
-rw-r--r--spec/javascripts/extensions/array_spec.js.es62
-rw-r--r--spec/javascripts/extensions/element_spec.js.es62
-rw-r--r--spec/javascripts/extensions/jquery_spec.js2
-rw-r--r--spec/javascripts/extensions/object_spec.js.es62
-rw-r--r--spec/javascripts/filtered_search/dropdown_user_spec.js.es68
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js.es68
-rw-r--r--spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js.es66
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js.es620
-rw-r--r--spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es64
-rw-r--r--spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js.es66
-rw-r--r--spec/javascripts/gfm_auto_complete_spec.js.es66
-rw-r--r--spec/javascripts/gl_dropdown_spec.js.es614
-rw-r--r--spec/javascripts/gl_field_errors_spec.js.es63
-rw-r--r--spec/javascripts/gl_form_spec.js.es69
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_graph_spec.js2
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_util_spec.js2
-rw-r--r--spec/javascripts/graphs/stat_graph_spec.js2
-rw-r--r--spec/javascripts/header_spec.js6
-rw-r--r--spec/javascripts/helpers/class_spec_helper_spec.js.es63
-rw-r--r--spec/javascripts/issuable_spec.js.es619
-rw-r--r--spec/javascripts/issuable_time_tracker_spec.js.es67
-rw-r--r--spec/javascripts/issue_spec.js4
-rw-r--r--spec/javascripts/labels_issue_sidebar_spec.js.es620
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js.es62
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js.es616
-rw-r--r--spec/javascripts/line_highlighter_spec.js2
-rw-r--r--spec/javascripts/merge_request_spec.js2
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js34
-rw-r--r--spec/javascripts/merge_request_widget_spec.js6
-rw-r--r--spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es64
-rw-r--r--spec/javascripts/new_branch_spec.js4
-rw-r--r--spec/javascripts/notes_spec.js8
-rw-r--r--spec/javascripts/pipelines_spec.js.es67
-rw-r--r--spec/javascripts/pretty_time_spec.js.es62
-rw-r--r--spec/javascripts/project_title_spec.js14
-rw-r--r--spec/javascripts/right_sidebar_spec.js7
-rw-r--r--spec/javascripts/search_autocomplete_spec.js27
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js4
-rw-r--r--spec/javascripts/signin_tabs_memoizer_spec.js.es62
-rw-r--r--spec/javascripts/smart_interval_spec.js.es65
-rw-r--r--spec/javascripts/spec_helper.js48
-rw-r--r--spec/javascripts/subbable_resource_spec.js.es65
-rw-r--r--spec/javascripts/syntax_highlight_spec.js2
-rw-r--r--spec/javascripts/test_bundle.js40
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js10
-rw-r--r--spec/javascripts/u2f/register_spec.js10
-rw-r--r--spec/javascripts/visibility_select_spec.js.es62
-rw-r--r--spec/javascripts/vue_common_components/commit_spec.js.es62
-rw-r--r--spec/javascripts/vue_pagination/pagination_spec.js.es65
-rw-r--r--spec/javascripts/zen_mode_spec.js2
-rw-r--r--spec/lib/event_filter_spec.rb18
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb6
-rw-r--r--spec/models/event_spec.rb24
-rw-r--r--spec/models/list_spec.rb39
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb16
-rw-r--r--spec/models/user_spec.rb8
-rw-r--r--spec/requests/api/builds_spec.rb1
-rw-r--r--spec/requests/api/projects_spec.rb46
-rw-r--r--spec/requests/api/v3/projects_spec.rb1424
-rw-r--r--spec/services/boards/create_service_spec.rb7
-rw-r--r--spec/services/boards/issues/list_service_spec.rb5
-rw-r--r--spec/services/boards/issues/move_service_spec.rb49
-rw-r--r--spec/services/boards/lists/create_service_spec.rb4
-rw-r--r--spec/services/boards/lists/destroy_service_spec.rb9
-rw-r--r--spec/services/boards/lists/list_service_spec.rb2
-rw-r--r--spec/services/boards/lists/move_service_spec.rb9
-rw-r--r--spec/services/slash_commands/interpret_service_spec.rb32
-rw-r--r--spec/services/system_note_service_spec.rb10
-rw-r--r--spec/support/api_helpers.rb9
-rw-r--r--spec/support/import_export/export_file_helper.rb2
-rw-r--r--spec/support/kubernetes_helpers.rb3
-rw-r--r--spec/support/matchers/match_file.rb5
-rw-r--r--spec/teaspoon_env.rb178
113 files changed, 2688 insertions, 933 deletions
diff --git a/spec/controllers/projects/boards/issues_controller_spec.rb b/spec/controllers/projects/boards/issues_controller_spec.rb
index 299d2c981d3..ad15e3942a5 100644
--- a/spec/controllers/projects/boards/issues_controller_spec.rb
+++ b/spec/controllers/projects/boards/issues_controller_spec.rb
@@ -18,23 +18,7 @@ describe Projects::Boards::IssuesController do
end
describe 'GET index' do
- context 'with valid list id' do
- it 'returns issues that have the list label applied' do
- johndoe = create(:user, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png')))
- issue = create(:labeled_issue, project: project, labels: [planning])
- create(:labeled_issue, project: project, labels: [planning])
- create(:labeled_issue, project: project, labels: [development], due_date: Date.tomorrow)
- create(:labeled_issue, project: project, labels: [development], assignee: johndoe)
- issue.subscribe(johndoe, project)
-
- list_issues user: user, board: board, list: list2
-
- parsed_response = JSON.parse(response.body)
-
- expect(response).to match_response_schema('issues')
- expect(parsed_response.length).to eq 2
- end
- end
+ let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
context 'with invalid board id' do
it 'returns a not found 404 response' do
@@ -44,11 +28,47 @@ describe Projects::Boards::IssuesController do
end
end
- context 'with invalid list id' do
- it 'returns a not found 404 response' do
- list_issues user: user, board: board, list: 999
+ context 'when list id is present' do
+ context 'with valid list id' do
+ it 'returns issues that have the list label applied' do
+ issue = create(:labeled_issue, project: project, labels: [planning])
+ create(:labeled_issue, project: project, labels: [planning])
+ create(:labeled_issue, project: project, labels: [development], due_date: Date.tomorrow)
+ create(:labeled_issue, project: project, labels: [development], assignee: johndoe)
+ issue.subscribe(johndoe, project)
- expect(response).to have_http_status(404)
+ list_issues user: user, board: board, list: list2
+
+ parsed_response = JSON.parse(response.body)
+
+ expect(response).to match_response_schema('issues')
+ expect(parsed_response.length).to eq 2
+ end
+ end
+
+ context 'with invalid list id' do
+ it 'returns a not found 404 response' do
+ list_issues user: user, board: board, list: 999
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ context 'when list id is missing' do
+ it 'returns opened issues without board labels applied' do
+ bug = create(:label, project: project, name: 'Bug')
+ create(:issue, project: project)
+ create(:labeled_issue, project: project, labels: [planning])
+ create(:labeled_issue, project: project, labels: [development])
+ create(:labeled_issue, project: project, labels: [bug])
+
+ list_issues user: user, board: board
+
+ parsed_response = JSON.parse(response.body)
+
+ expect(response).to match_response_schema('issues')
+ expect(parsed_response.length).to eq 2
end
end
@@ -65,13 +85,17 @@ describe Projects::Boards::IssuesController do
end
end
- def list_issues(user:, board:, list:)
+ def list_issues(user:, board:, list: nil)
sign_in(user)
- get :index, namespace_id: project.namespace.to_param,
- project_id: project.to_param,
- board_id: board.to_param,
- list_id: list.to_param
+ params = {
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ board_id: board.to_param,
+ list_id: list.try(:to_param)
+ }
+
+ get :index, params.compact
end
end
diff --git a/spec/controllers/projects/boards/lists_controller_spec.rb b/spec/controllers/projects/boards/lists_controller_spec.rb
index 34d6119429d..b3f9f76a50c 100644
--- a/spec/controllers/projects/boards/lists_controller_spec.rb
+++ b/spec/controllers/projects/boards/lists_controller_spec.rb
@@ -27,7 +27,7 @@ describe Projects::Boards::ListsController do
parsed_response = JSON.parse(response.body)
expect(response).to match_response_schema('lists')
- expect(parsed_response.length).to eq 3
+ expect(parsed_response.length).to eq 2
end
context 'with unauthorized user' do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 9323f723bdb..e7aa8745b99 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -213,6 +213,17 @@ describe ProjectsController do
expect(response.status).to eq 404
end
end
+
+ context "redirection from http://someproject.git" do
+ it 'redirects to project page (format.html)' do
+ project = create(:project, :public)
+
+ get :show, namespace_id: project.namespace.path, id: project.path, format: :git
+
+ expect(response).to have_http_status(302)
+ expect(response).to redirect_to(namespace_project_path)
+ end
+ end
end
describe "#update" do
diff --git a/spec/factories/boards.rb b/spec/factories/boards.rb
index ec46146d9b5..a581725245a 100644
--- a/spec/factories/boards.rb
+++ b/spec/factories/boards.rb
@@ -3,7 +3,6 @@ FactoryGirl.define do
project factory: :empty_project
after(:create) do |board|
- board.lists.create(list_type: :backlog)
board.lists.create(list_type: :done)
end
end
diff --git a/spec/factories/events.rb b/spec/factories/events.rb
index bfe41f71b57..55727d6b62c 100644
--- a/spec/factories/events.rb
+++ b/spec/factories/events.rb
@@ -3,6 +3,18 @@ FactoryGirl.define do
project factory: :empty_project
author factory: :user
+ trait(:created) { action Event::CREATED }
+ trait(:updated) { action Event::UPDATED }
+ trait(:closed) { action Event::CLOSED }
+ trait(:reopened) { action Event::REOPENED }
+ trait(:pushed) { action Event::PUSHED }
+ trait(:commented) { action Event::COMMENTED }
+ trait(:merged) { action Event::MERGED }
+ trait(:joined) { action Event::JOINED }
+ trait(:left) { action Event::LEFT }
+ trait(:destroyed) { action Event::DESTROYED }
+ trait(:expired) { action Event::EXPIRED }
+
factory :closed_issue_event do
action { Event::CLOSED }
target factory: :closed_issue
diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb
index 9e3f06c682c..2a2f3cca91c 100644
--- a/spec/factories/lists.rb
+++ b/spec/factories/lists.rb
@@ -6,12 +6,6 @@ FactoryGirl.define do
sequence(:position)
end
- factory :backlog_list, parent: :list do
- list_type :backlog
- label nil
- position nil
- end
-
factory :done_list, parent: :list do
list_type :done
label nil
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
new file mode 100644
index 00000000000..2875fc1e533
--- /dev/null
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -0,0 +1,233 @@
+require 'rails_helper'
+
+describe 'Issue Boards add issue modal', :feature, :js do
+ include WaitForAjax
+ include WaitForVueResource
+
+ let(:project) { create(:empty_project, :public) }
+ let(:board) { create(:board, project: project) }
+ let(:user) { create(:user) }
+ let!(:planning) { create(:label, project: project, name: 'Planning') }
+ let!(:label) { create(:label, project: project) }
+ let!(:list1) { create(:list, board: board, label: planning, position: 0) }
+ let!(:list2) { create(:list, board: board, label: label, position: 1) }
+ let!(:issue) { create(:issue, project: project) }
+ let!(:issue2) { create(:issue, project: project) }
+
+ before do
+ project.team << [user, :master]
+
+ login_as(user)
+
+ visit namespace_project_board_path(project.namespace, project, board)
+ wait_for_vue_resource
+ end
+
+ context 'modal interaction' do
+ it 'opens modal' do
+ click_button('Add issues')
+
+ expect(page).to have_selector('.add-issues-modal')
+ end
+
+ it 'closes modal' do
+ click_button('Add issues')
+
+ page.within('.add-issues-modal') do
+ find('.close').click
+ end
+
+ expect(page).not_to have_selector('.add-issues-modal')
+ end
+
+ it 'closes modal if cancel button clicked' do
+ click_button('Add issues')
+
+ page.within('.add-issues-modal') do
+ click_button 'Cancel'
+ end
+
+ expect(page).not_to have_selector('.add-issues-modal')
+ end
+ end
+
+ context 'issues list' do
+ before do
+ click_button('Add issues')
+
+ wait_for_vue_resource
+ end
+
+ it 'loads issues' do
+ page.within('.add-issues-modal') do
+ page.within('.nav-links') do
+ expect(page).to have_content('2')
+ end
+
+ expect(page).to have_selector('.card', count: 2)
+ end
+ end
+
+ it 'shows selected issues' do
+ page.within('.add-issues-modal') do
+ click_link 'Selected issues'
+
+ expect(page).not_to have_selector('.card')
+ end
+ end
+
+ context 'list dropdown' do
+ it 'resets after deleting list' do
+ page.within('.add-issues-modal') do
+ expect(find('.add-issues-footer')).to have_button(planning.title)
+
+ click_button 'Cancel'
+ end
+
+ first('.board-delete').click
+
+ click_button('Add issues')
+
+ wait_for_vue_resource
+
+ page.within('.add-issues-modal') do
+ expect(find('.add-issues-footer')).not_to have_button(planning.title)
+ expect(find('.add-issues-footer')).to have_button(label.title)
+ end
+ end
+ end
+
+ context 'search' do
+ it 'returns issues' do
+ page.within('.add-issues-modal') do
+ find('.form-control').native.send_keys(issue.title)
+
+ expect(page).to have_selector('.card', count: 1)
+ end
+ end
+
+ it 'returns no issues' do
+ page.within('.add-issues-modal') do
+ find('.form-control').native.send_keys('testing search')
+
+ expect(page).not_to have_selector('.card')
+ expect(page).not_to have_content("You haven't added any issues to your project yet")
+ end
+ end
+ end
+
+ context 'selecing issues' do
+ it 'selects single issue' do
+ page.within('.add-issues-modal') do
+ first('.card').click
+
+ page.within('.nav-links') do
+ expect(page).to have_content('Selected issues 1')
+ end
+ end
+ end
+
+ it 'changes button text' do
+ page.within('.add-issues-modal') do
+ first('.card').click
+
+ expect(first('.add-issues-footer .btn')).to have_content('Add 1 issue')
+ end
+ end
+
+ it 'changes button text with plural' do
+ page.within('.add-issues-modal') do
+ all('.card').each do |el|
+ el.click
+ end
+
+ expect(first('.add-issues-footer .btn')).to have_content('Add 2 issues')
+ end
+ end
+
+ it 'shows only selected issues on selected tab' do
+ page.within('.add-issues-modal') do
+ first('.card').click
+
+ click_link 'Selected issues'
+
+ expect(page).to have_selector('.card', count: 1)
+ end
+ end
+
+ it 'selects all issues' do
+ page.within('.add-issues-modal') do
+ click_button 'Select all'
+
+ expect(page).to have_selector('.is-active', count: 2)
+ end
+ end
+
+ it 'deselects all issues' do
+ page.within('.add-issues-modal') do
+ click_button 'Select all'
+
+ expect(page).to have_selector('.is-active', count: 2)
+
+ click_button 'Deselect all'
+
+ expect(page).not_to have_selector('.is-active')
+ end
+ end
+
+ it 'selects all that arent already selected' do
+ page.within('.add-issues-modal') do
+ first('.card').click
+
+ expect(page).to have_selector('.is-active', count: 1)
+
+ click_button 'Select all'
+
+ expect(page).to have_selector('.is-active', count: 2)
+ end
+ end
+
+ it 'unselects from selected tab' do
+ page.within('.add-issues-modal') do
+ first('.card').click
+
+ click_link 'Selected issues'
+
+ first('.card').click
+
+ expect(page).not_to have_selector('.is-active')
+ end
+ end
+ end
+
+ context 'adding issues' do
+ it 'adds to board' do
+ page.within('.add-issues-modal') do
+ first('.card').click
+
+ click_button 'Add 1 issue'
+ end
+
+ page.within(first('.board')) do
+ expect(page).to have_selector('.card')
+ end
+ end
+
+ it 'adds to second list' do
+ page.within('.add-issues-modal') do
+ first('.card').click
+
+ click_button planning.title
+
+ click_link label.title
+
+ click_button 'Add 1 issue'
+ end
+
+ page.within(find('.board:nth-child(2)')) do
+ expect(page).to have_selector('.card')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index bfac5a1b8ab..34f47daf0e5 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -20,7 +20,7 @@ describe 'Issue Boards', feature: true, js: true do
before do
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 3)
+ expect(page).to have_selector('.board', count: 2)
end
it 'shows blank state' do
@@ -31,18 +31,18 @@ describe 'Issue Boards', feature: true, js: true do
page.within(find('.board-blank-state')) do
click_button("Nevermind, I'll use my own")
end
- expect(page).to have_selector('.board', count: 2)
+ expect(page).to have_selector('.board', count: 1)
end
it 'creates default lists' do
- lists = ['Backlog', 'To Do', 'Doing', 'Done']
+ lists = ['To Do', 'Doing', 'Done']
page.within(find('.board-blank-state')) do
click_button('Add default lists')
end
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 4)
+ expect(page).to have_selector('.board', count: 3)
page.all('.board').each_with_index do |list, i|
expect(list.find('.board-title')).to have_content(lists[i])
@@ -64,42 +64,41 @@ describe 'Issue Boards', feature: true, js: true do
let!(:list1) { create(:list, board: board, label: planning, position: 0) }
let!(:list2) { create(:list, board: board, label: development, position: 1) }
- let!(:confidential_issue) { create(:issue, :confidential, project: project, author: user) }
- let!(:issue1) { create(:issue, project: project, assignee: user) }
- let!(:issue2) { create(:issue, project: project, author: user2) }
- let!(:issue3) { create(:issue, project: project) }
- let!(:issue4) { create(:issue, project: project) }
+ let!(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning]) }
+ let!(:issue1) { create(:labeled_issue, project: project, assignee: user, labels: [planning]) }
+ let!(:issue2) { create(:labeled_issue, project: project, author: user2, labels: [planning]) }
+ let!(:issue3) { create(:labeled_issue, project: project, labels: [planning]) }
+ let!(:issue4) { create(:labeled_issue, project: project, labels: [planning]) }
let!(:issue5) { create(:labeled_issue, project: project, labels: [planning], milestone: milestone) }
let!(:issue6) { create(:labeled_issue, project: project, labels: [planning, development]) }
let!(:issue7) { create(:labeled_issue, project: project, labels: [development]) }
let!(:issue8) { create(:closed_issue, project: project) }
- let!(:issue9) { create(:labeled_issue, project: project, labels: [testing, bug, accepting]) }
+ let!(:issue9) { create(:labeled_issue, project: project, labels: [planning, testing, bug, accepting]) }
before do
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 4)
+ expect(page).to have_selector('.board', count: 3)
expect(find('.board:nth-child(1)')).to have_selector('.card')
expect(find('.board:nth-child(2)')).to have_selector('.card')
expect(find('.board:nth-child(3)')).to have_selector('.card')
- expect(find('.board:nth-child(4)')).to have_selector('.card')
end
it 'shows lists' do
- expect(page).to have_selector('.board', count: 4)
+ expect(page).to have_selector('.board', count: 3)
end
it 'shows description tooltip on list title' do
- page.within('.board:nth-child(2)') do
+ page.within('.board:nth-child(1)') do
expect(find('.board-title span.has-tooltip')[:title]).to eq('Test')
end
end
it 'shows issues in lists' do
+ wait_for_board_cards(1, 8)
wait_for_board_cards(2, 2)
- wait_for_board_cards(3, 2)
end
it 'shows confidential issues with icon' do
@@ -108,19 +107,6 @@ describe 'Issue Boards', feature: true, js: true do
end
end
- it 'search backlog list' do
- page.within('#js-boards-search') do
- find('.form-control').set(issue1.title)
- end
-
- wait_for_vue_resource
-
- expect(find('.board:nth-child(1)')).to have_selector('.card', count: 1)
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(4)')).to have_selector('.card', count: 0)
- end
-
it 'search done list' do
page.within('#js-boards-search') do
find('.form-control').set(issue8.title)
@@ -130,8 +116,7 @@ describe 'Issue Boards', feature: true, js: true do
expect(find('.board:nth-child(1)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(4)')).to have_selector('.card', count: 1)
+ expect(find('.board:nth-child(3)')).to have_selector('.card', count: 1)
end
it 'search list' do
@@ -141,157 +126,135 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
- expect(find('.board:nth-child(1)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 1)
+ expect(find('.board:nth-child(1)')).to have_selector('.card', count: 1)
+ expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(4)')).to have_selector('.card', count: 0)
end
it 'allows user to delete board' do
- page.within(find('.board:nth-child(2)')) do
+ page.within(find('.board:nth-child(1)')) do
find('.board-delete').click
end
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 3)
+ expect(page).to have_selector('.board', count: 2)
end
it 'removes checkmark in new list dropdown after deleting' do
click_button 'Add list'
wait_for_ajax
- page.within(find('.board:nth-child(2)')) do
+ page.within(find('.board:nth-child(1)')) do
find('.board-delete').click
end
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 3)
- expect(find(".js-board-list-#{planning.id}", visible: false)).not_to have_css('.is-active')
+ expect(page).to have_selector('.board', count: 2)
end
it 'infinite scrolls list' do
50.times do
- create(:issue, project: project)
+ create(:labeled_issue, project: project, labels: [planning])
end
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('56')
+ expect(page.find('.board-header')).to have_content('58')
expect(page).to have_selector('.card', count: 20)
- expect(page).to have_content('Showing 20 of 56 issues')
+ expect(page).to have_content('Showing 20 of 58 issues')
evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
wait_for_vue_resource
expect(page).to have_selector('.card', count: 40)
- expect(page).to have_content('Showing 40 of 56 issues')
+ expect(page).to have_content('Showing 40 of 58 issues')
evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
wait_for_vue_resource
- expect(page).to have_selector('.card', count: 56)
+ expect(page).to have_selector('.card', count: 58)
expect(page).to have_content('Showing all issues')
end
end
- context 'backlog' do
- it 'shows issues in backlog with no labels' do
- wait_for_board_cards(1, 6)
- end
-
- it 'moves issue from backlog into list' do
- drag_to(list_to_index: 1)
-
- wait_for_vue_resource
- wait_for_board_cards(1, 5)
- wait_for_board_cards(2, 3)
- end
- end
-
context 'done' do
it 'shows list of done issues' do
- wait_for_board_cards(4, 1)
+ wait_for_board_cards(3, 1)
wait_for_ajax
end
it 'moves issue to done' do
- drag_to(list_from_index: 0, list_to_index: 3)
+ drag_to(list_from_index: 0, list_to_index: 2)
- wait_for_board_cards(1, 5)
+ wait_for_board_cards(1, 7)
wait_for_board_cards(2, 2)
wait_for_board_cards(3, 2)
- wait_for_board_cards(4, 2)
expect(find('.board:nth-child(1)')).not_to have_content(issue9.title)
- expect(find('.board:nth-child(4)')).to have_selector('.card', count: 2)
- expect(find('.board:nth-child(4)')).to have_content(issue9.title)
- expect(find('.board:nth-child(4)')).not_to have_content(planning.title)
+ expect(find('.board:nth-child(3)')).to have_selector('.card', count: 2)
+ expect(find('.board:nth-child(3)')).to have_content(issue9.title)
+ expect(find('.board:nth-child(3)')).not_to have_content(planning.title)
end
it 'removes all of the same issue to done' do
- drag_to(list_from_index: 1, list_to_index: 3)
+ drag_to(list_from_index: 0, list_to_index: 2)
- wait_for_board_cards(1, 6)
- wait_for_board_cards(2, 1)
- wait_for_board_cards(3, 1)
- wait_for_board_cards(4, 2)
+ wait_for_board_cards(1, 7)
+ wait_for_board_cards(2, 2)
+ wait_for_board_cards(3, 2)
- expect(find('.board:nth-child(2)')).not_to have_content(issue6.title)
- expect(find('.board:nth-child(4)')).to have_content(issue6.title)
- expect(find('.board:nth-child(4)')).not_to have_content(planning.title)
+ expect(find('.board:nth-child(1)')).not_to have_content(issue9.title)
+ expect(find('.board:nth-child(3)')).to have_content(issue9.title)
+ expect(find('.board:nth-child(3)')).not_to have_content(planning.title)
end
end
context 'lists' do
it 'changes position of list' do
- drag_to(list_from_index: 1, list_to_index: 2, selector: '.board-header')
+ drag_to(list_from_index: 1, list_to_index: 0, selector: '.board-header')
- wait_for_board_cards(1, 6)
- wait_for_board_cards(2, 2)
- wait_for_board_cards(3, 2)
- wait_for_board_cards(4, 1)
+ wait_for_board_cards(1, 2)
+ wait_for_board_cards(2, 8)
+ wait_for_board_cards(3, 1)
- expect(find('.board:nth-child(2)')).to have_content(development.title)
- expect(find('.board:nth-child(2)')).to have_content(planning.title)
+ expect(find('.board:nth-child(1)')).to have_content(development.title)
+ expect(find('.board:nth-child(1)')).to have_content(planning.title)
end
it 'issue moves between lists' do
- drag_to(list_from_index: 1, card_index: 1, list_to_index: 2)
+ drag_to(list_from_index: 0, card_index: 1, list_to_index: 1)
- wait_for_board_cards(1, 6)
- wait_for_board_cards(2, 1)
- wait_for_board_cards(3, 3)
- wait_for_board_cards(4, 1)
+ wait_for_board_cards(1, 7)
+ wait_for_board_cards(2, 2)
+ wait_for_board_cards(3, 1)
- expect(find('.board:nth-child(3)')).to have_content(issue6.title)
- expect(find('.board:nth-child(3)').all('.card').last).not_to have_content(development.title)
+ expect(find('.board:nth-child(2)')).to have_content(issue6.title)
+ expect(find('.board:nth-child(2)').all('.card').last).not_to have_content(development.title)
end
it 'issue moves between lists' do
- drag_to(list_from_index: 2, list_to_index: 1)
+ drag_to(list_from_index: 1, list_to_index: 0)
- wait_for_board_cards(1, 6)
- wait_for_board_cards(2, 3)
+ wait_for_board_cards(1, 9)
+ wait_for_board_cards(2, 1)
wait_for_board_cards(3, 1)
- wait_for_board_cards(4, 1)
- expect(find('.board:nth-child(2)')).to have_content(issue7.title)
- expect(find('.board:nth-child(2)').all('.card').first).not_to have_content(planning.title)
+ expect(find('.board:nth-child(1)')).to have_content(issue7.title)
+ expect(find('.board:nth-child(1)').all('.card').first).not_to have_content(planning.title)
end
it 'issue moves from done' do
- drag_to(list_from_index: 3, list_to_index: 1)
+ drag_to(list_from_index: 2, list_to_index: 1)
expect(find('.board:nth-child(2)')).to have_content(issue8.title)
- wait_for_board_cards(1, 6)
+ wait_for_board_cards(1, 8)
wait_for_board_cards(2, 3)
- wait_for_board_cards(3, 2)
- wait_for_board_cards(4, 0)
+ wait_for_board_cards(3, 0)
end
context 'issue card' do
@@ -324,7 +287,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 5)
+ expect(page).to have_selector('.board', count: 4)
end
it 'creates new list for Backlog label' do
@@ -337,7 +300,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 5)
+ expect(page).to have_selector('.board', count: 4)
end
it 'creates new list for Done label' do
@@ -350,7 +313,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 5)
+ expect(page).to have_selector('.board', count: 4)
end
it 'keeps dropdown open after adding new list' do
@@ -366,21 +329,6 @@ describe 'Issue Boards', feature: true, js: true do
expect(find('.issue-boards-search')).to have_selector('.open')
end
- it 'moves issues from backlog into new list' do
- wait_for_board_cards(1, 6)
-
- click_button 'Add list'
- wait_for_ajax
-
- page.within('.dropdown-menu-issues-board-new') do
- click_link testing.title
- end
-
- wait_for_vue_resource
-
- wait_for_board_cards(1, 5)
- end
-
it 'creates new list from a new label' do
click_button 'Add list'
@@ -397,7 +345,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_ajax
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 5)
+ expect(page).to have_selector('.board', count: 4)
end
end
end
@@ -418,7 +366,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
wait_for_board_cards(1, 1)
- wait_for_empty_boards((2..4))
+ wait_for_empty_boards((2..3))
end
it 'filters by assignee' do
@@ -437,7 +385,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
wait_for_board_cards(1, 1)
- wait_for_empty_boards((2..4))
+ wait_for_empty_boards((2..3))
end
it 'filters by milestone' do
@@ -454,10 +402,9 @@ describe 'Issue Boards', feature: true, js: true do
end
wait_for_vue_resource
- wait_for_board_cards(1, 0)
- wait_for_board_cards(2, 1)
+ wait_for_board_cards(1, 1)
+ wait_for_board_cards(2, 0)
wait_for_board_cards(3, 0)
- wait_for_board_cards(4, 0)
end
it 'filters by label' do
@@ -474,7 +421,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
wait_for_board_cards(1, 1)
- wait_for_empty_boards((2..4))
+ wait_for_empty_boards((2..3))
end
it 'filters by label with space after reload' do
@@ -530,7 +477,7 @@ describe 'Issue Boards', feature: true, js: true do
it 'infinite scrolls list with label filter' do
50.times do
- create(:labeled_issue, project: project, labels: [testing])
+ create(:labeled_issue, project: project, labels: [planning, testing])
end
page.within '.issues-filters' do
@@ -580,32 +527,12 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
wait_for_board_cards(1, 1)
- wait_for_empty_boards((2..4))
- end
-
- it 'filters by no label' do
- page.within '.issues-filters' do
- click_button('Label')
- wait_for_ajax
-
- page.within '.dropdown-menu-labels' do
- click_link("No Label")
- wait_for_vue_resource
- find('.dropdown-menu-close').click
- end
- end
-
- wait_for_vue_resource
-
- wait_for_board_cards(1, 5)
- wait_for_board_cards(2, 0)
- wait_for_board_cards(3, 0)
- wait_for_board_cards(4, 1)
+ wait_for_empty_boards((2..3))
end
it 'filters by clicking label button on issue' do
page.within(find('.board', match: :first)) do
- expect(page).to have_selector('.card', count: 6)
+ expect(page).to have_selector('.card', count: 8)
expect(find('.card', match: :first)).to have_content(bug.title)
click_button(bug.title)
wait_for_vue_resource
@@ -614,7 +541,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
wait_for_board_cards(1, 1)
- wait_for_empty_boards((2..4))
+ wait_for_empty_boards((2..3))
page.within('.labels-filter') do
expect(find('.dropdown-toggle-text')).to have_content(bug.title)
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index a03cd6fbf2d..6d14a8cf483 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -6,6 +6,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
let(:project) { create(:empty_project, :public) }
let(:board) { create(:board, project: project) }
+ let!(:list) { create(:list, board: board, position: 0) }
let(:user) { create(:user) }
context 'authorized user' do
@@ -17,7 +18,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
visit namespace_project_board_path(project.namespace, project, board)
wait_for_vue_resource
- expect(page).to have_selector('.board', count: 3)
+ expect(page).to have_selector('.board', count: 2)
end
it 'displays new issue button' do
@@ -25,7 +26,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
end
it 'does not display new issue button in done list' do
- page.within('.board:nth-child(3)') do
+ page.within('.board:nth-child(2)') do
expect(page).not_to have_selector('.board-issue-count-holder .btn')
end
end
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index c28bb0dcdae..9cc50167395 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -4,14 +4,17 @@ describe 'Issue Boards', feature: true, js: true do
include WaitForAjax
include WaitForVueResource
- let(:project) { create(:empty_project, :public) }
- let(:board) { create(:board, project: project) }
- let(:user) { create(:user) }
- let!(:label) { create(:label, project: project) }
- let!(:label2) { create(:label, project: project) }
- let!(:milestone) { create(:milestone, project: project) }
- let!(:issue2) { create(:labeled_issue, project: project, assignee: user, milestone: milestone, labels: [label]) }
- let!(:issue) { create(:issue, project: project) }
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, :public) }
+ let!(:milestone) { create(:milestone, project: project) }
+ let!(:development) { create(:label, project: project, name: 'Development') }
+ let!(:bug) { create(:label, project: project, name: 'Bug') }
+ let!(:regression) { create(:label, project: project, name: 'Regression') }
+ let!(:stretch) { create(:label, project: project, name: 'Stretch') }
+ let!(:issue1) { create(:labeled_issue, project: project, assignee: user, milestone: milestone, labels: [development]) }
+ let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch]) }
+ let(:board) { create(:board, project: project) }
+ let!(:list) { create(:list, board: board, label: development, position: 0) }
before do
project.team << [user, :master]
@@ -62,8 +65,22 @@ describe 'Issue Boards', feature: true, js: true do
end
page.within('.issue-boards-sidebar') do
- expect(page).to have_content(issue.title)
- expect(page).to have_content(issue.to_reference)
+ expect(page).to have_content(issue2.title)
+ expect(page).to have_content(issue2.to_reference)
+ end
+ end
+
+ it 'removes card from board when clicking remove button' do
+ page.within(first('.board')) do
+ first('.card').click
+ end
+
+ page.within('.issue-boards-sidebar') do
+ click_button 'Remove from board'
+ end
+
+ page.within(first('.board')) do
+ expect(page).to have_selector('.card', count: 1)
end
end
@@ -244,22 +261,22 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_ajax
- click_link label.title
+ click_link bug.title
wait_for_vue_resource
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.label', count: 1)
- expect(page).to have_content(label.title)
+ expect(page).to have_selector('.label', count: 3)
+ expect(page).to have_content(bug.title)
end
end
page.within(first('.board')) do
page.within(first('.card')) do
- expect(page).to have_selector('.label', count: 1)
- expect(page).to have_content(label.title)
+ expect(page).to have_selector('.label', count: 2)
+ expect(page).to have_content(bug.title)
end
end
end
@@ -274,32 +291,32 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_ajax
- click_link label.title
- click_link label2.title
+ click_link bug.title
+ click_link regression.title
wait_for_vue_resource
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.label', count: 2)
- expect(page).to have_content(label.title)
- expect(page).to have_content(label2.title)
+ expect(page).to have_selector('.label', count: 4)
+ expect(page).to have_content(bug.title)
+ expect(page).to have_content(regression.title)
end
end
page.within(first('.board')) do
page.within(first('.card')) do
- expect(page).to have_selector('.label', count: 2)
- expect(page).to have_content(label.title)
- expect(page).to have_content(label2.title)
+ expect(page).to have_selector('.label', count: 3)
+ expect(page).to have_content(bug.title)
+ expect(page).to have_content(regression.title)
end
end
end
it 'removes a label' do
page.within(first('.board')) do
- find('.card:nth-child(2)').click
+ first('.card').click
end
page.within('.labels') do
@@ -307,22 +324,22 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_ajax
- click_link label.title
+ click_link stretch.title
wait_for_vue_resource
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.label', count: 0)
- expect(page).not_to have_content(label.title)
+ expect(page).to have_selector('.label', count: 1)
+ expect(page).not_to have_content(stretch.title)
end
end
page.within(first('.board')) do
- page.within(find('.card:nth-child(2)')) do
- expect(page).not_to have_selector('.label', count: 1)
- expect(page).not_to have_content(label.title)
+ page.within(first('.card')) do
+ expect(page).not_to have_selector('.label')
+ expect(page).not_to have_content(stretch.title)
end
end
end
diff --git a/spec/features/merge_requests/toggler_behavior_spec.rb b/spec/features/merge_requests/toggler_behavior_spec.rb
index 44a9b545ff8..a2cf9b18bf2 100644
--- a/spec/features/merge_requests/toggler_behavior_spec.rb
+++ b/spec/features/merge_requests/toggler_behavior_spec.rb
@@ -10,8 +10,8 @@ feature 'toggler_behavior', js: true, feature: true do
before do
login_as :admin
project = merge_request.source_project
- visit "#{namespace_project_merge_request_path(project.namespace, project, merge_request)}#{fragment_id}"
page.current_window.resize_to(1000, 300)
+ visit "#{namespace_project_merge_request_path(project.namespace, project, merge_request)}#{fragment_id}"
end
describe 'scroll position' do
diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
index 2582a540240..2f3c3e45ae6 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -120,5 +120,81 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
expect(page).not_to have_content '/due 2016-08-28'
end
end
+
+ describe '/target_branch command in merge request' do
+ let(:another_project) { create(:project, :public) }
+ let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } }
+
+ before do
+ logout
+ another_project.team << [user, :master]
+ login_with(user)
+ end
+
+ it 'changes target_branch in new merge_request' do
+ visit new_namespace_project_merge_request_path(another_project.namespace, another_project, new_url_opts)
+ click_button "Compare branches and continue"
+
+ fill_in "merge_request_title", with: 'My brand new feature'
+ fill_in "merge_request_description", with: "le feature \n/target_branch fix\nFeature description:"
+ click_button "Submit merge request"
+
+ merge_request = another_project.merge_requests.first
+ expect(merge_request.description).to eq "le feature \nFeature description:"
+ expect(merge_request.target_branch).to eq 'fix'
+ end
+
+ it 'does not change target branch when merge request is edited' do
+ new_merge_request = create(:merge_request, source_project: another_project)
+
+ visit edit_namespace_project_merge_request_path(another_project.namespace, another_project, new_merge_request)
+ fill_in "merge_request_description", with: "Want to update target branch\n/target_branch fix\n"
+ click_button "Save changes"
+
+ new_merge_request = another_project.merge_requests.first
+ expect(new_merge_request.description).to include('/target_branch')
+ expect(new_merge_request.target_branch).not_to eq('fix')
+ end
+ end
+
+ describe '/target_branch command from note' do
+ context 'when the current user can change target branch' do
+ it 'changes target branch from a note' do
+ write_note("message start \n/target_branch merge-test\n message end.")
+
+ expect(page).not_to have_content('/target_branch')
+ expect(page).to have_content('message start')
+ expect(page).to have_content('message end.')
+
+ expect(merge_request.reload.target_branch).to eq 'merge-test'
+ end
+
+ it 'does not fail when target branch does not exists' do
+ write_note('/target_branch totally_not_existing_branch')
+
+ expect(page).not_to have_content('/target_branch')
+
+ expect(merge_request.target_branch).to eq 'feature'
+ end
+ end
+
+ context 'when current user can not change target branch' do
+ let(:guest) { create(:user) }
+ before do
+ project.team << [guest, :guest]
+ logout
+ login_with(guest)
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it 'does not change target branch' do
+ write_note('/target_branch merge-test')
+
+ expect(page).not_to have_content '/target_branch merge-test'
+
+ expect(merge_request.target_branch).to eq 'feature'
+ end
+ end
+ end
end
end
diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb
index ad2d456529a..34f665826b6 100644
--- a/spec/finders/contributed_projects_finder_spec.rb
+++ b/spec/finders/contributed_projects_finder_spec.rb
@@ -10,15 +10,12 @@ describe ContributedProjectsFinder do
let!(:private_project) { create(:empty_project, :private) }
before do
- private_project.team << [source_user, Gitlab::Access::MASTER]
- private_project.team << [current_user, Gitlab::Access::DEVELOPER]
- public_project.team << [source_user, Gitlab::Access::MASTER]
+ private_project.add_master(source_user)
+ private_project.add_developer(current_user)
+ public_project.add_master(source_user)
- create(:event, action: Event::PUSHED, project: public_project,
- target: public_project, author: source_user)
-
- create(:event, action: Event::PUSHED, project: private_project,
- target: private_project, author: source_user)
+ create(:event, :pushed, project: public_project, target: public_project, author: source_user)
+ create(:event, :pushed, project: private_project, target: private_project, author: source_user)
end
describe 'without a current user' do
diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json
index 77f2bcee1f3..8e19cee5440 100644
--- a/spec/fixtures/api/schemas/issue.json
+++ b/spec/fixtures/api/schemas/issue.json
@@ -6,6 +6,7 @@
"confidential"
],
"properties" : {
+ "id": { "type": "integer" },
"iid": { "type": "integer" },
"title": { "type": "string" },
"confidential": { "type": "boolean" },
diff --git a/spec/fixtures/api/schemas/list.json b/spec/fixtures/api/schemas/list.json
index 8d94cf26ecb..819287bf919 100644
--- a/spec/fixtures/api/schemas/list.json
+++ b/spec/fixtures/api/schemas/list.json
@@ -10,7 +10,7 @@
"id": { "type": "integer" },
"list_type": {
"type": "string",
- "enum": ["backlog", "label", "done"]
+ "enum": ["label", "done"]
},
"label": {
"type": ["object", "null"],
diff --git a/spec/javascripts/abuse_reports_spec.js.es6 b/spec/javascripts/abuse_reports_spec.js.es6
index a2d57824585..76b370b345b 100644
--- a/spec/javascripts/abuse_reports_spec.js.es6
+++ b/spec/javascripts/abuse_reports_spec.js.es6
@@ -1,5 +1,5 @@
-/*= require lib/utils/text_utility */
-/*= require abuse_reports */
+require('~/lib/utils/text_utility');
+require('~/abuse_reports');
((global) => {
describe('Abuse Reports', () => {
diff --git a/spec/javascripts/activities_spec.js.es6 b/spec/javascripts/activities_spec.js.es6
index 7bc5b3268a0..e6a6fc36ca1 100644
--- a/spec/javascripts/activities_spec.js.es6
+++ b/spec/javascripts/activities_spec.js.es6
@@ -1,9 +1,8 @@
/* eslint-disable no-unused-expressions, no-prototype-builtins, no-new, no-shadow, max-len */
-/*= require js.cookie.js */
-/*= require jquery.endless-scroll.js */
-/*= require pager */
-/*= require activities */
+require('vendor/jquery.endless-scroll.js');
+require('~/pager');
+require('~/activities');
(() => {
window.gon || (window.gon = {});
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index f1bfd529983..001cd8d6325 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -1,10 +1,8 @@
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, max-len */
/* global AwardsHandler */
-/*= require awards_handler */
-/*= require jquery */
-/*= require js.cookie */
-/*= require ./fixtures/emoji_menu */
+require('~/awards_handler');
+require('./fixtures/emoji_menu');
(function() {
var awardsHandler, lazyAssert, urlRoot;
diff --git a/spec/javascripts/behaviors/autosize_spec.js b/spec/javascripts/behaviors/autosize_spec.js
index 51d911792ba..4a3da9e318b 100644
--- a/spec/javascripts/behaviors/autosize_spec.js
+++ b/spec/javascripts/behaviors/autosize_spec.js
@@ -1,6 +1,6 @@
/* eslint-disable space-before-function-paren, no-var, comma-dangle, no-return-assign, max-len */
-/*= require behaviors/autosize */
+require('~/behaviors/autosize');
(function() {
describe('Autosize behavior', function() {
@@ -15,7 +15,7 @@
});
});
return load = function() {
- return $(document).trigger('page:load');
+ return $(document).trigger('load');
};
});
}).call(this);
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js
index 0f046c2d83a..b84126c0e3d 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js
+++ b/spec/javascripts/behaviors/quick_submit_spec.js
@@ -1,6 +1,6 @@
/* eslint-disable space-before-function-paren, no-var, no-return-assign, comma-dangle, jasmine/no-spec-dupes, new-cap, max-len */
-/*= require behaviors/quick_submit */
+require('~/behaviors/quick_submit');
(function() {
describe('Quick Submit behavior', function() {
diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js
index 9467056f04c..a958ac76e66 100644
--- a/spec/javascripts/behaviors/requires_input_spec.js
+++ b/spec/javascripts/behaviors/requires_input_spec.js
@@ -1,6 +1,6 @@
/* eslint-disable space-before-function-paren, no-var */
-/*= require behaviors/requires_input */
+require('~/behaviors/requires_input');
(function() {
describe('requiresInput', function() {
@@ -34,11 +34,5 @@
$('#required5').val('1').change();
return expect($('.submit')).not.toBeDisabled();
});
- return it('is called on page:load event', function() {
- var spy;
- spy = spyOn($.fn, 'requiresInput');
- $(document).trigger('page:load');
- return expect(spy).toHaveBeenCalled();
- });
});
}).call(this);
diff --git a/spec/javascripts/boards/boards_store_spec.js.es6 b/spec/javascripts/boards/boards_store_spec.js.es6
index 7c5850111cb..9dd741a680b 100644
--- a/spec/javascripts/boards/boards_store_spec.js.es6
+++ b/spec/javascripts/boards/boards_store_spec.js.es6
@@ -6,24 +6,19 @@
/* global listObj */
/* global listObjDuplicate */
-//= require jquery
-//= require jquery_ujs
-//= require js.cookie
-//= require vue
-//= require vue-resource
-//= require lib/utils/url_utility
-//= require boards/models/issue
-//= require boards/models/label
-//= require boards/models/list
-//= require boards/models/user
-//= require boards/services/board_service
-//= require boards/stores/boards_store
-//= require ./mock_data
+require('~/lib/utils/url_utility');
+require('~/boards/models/issue');
+require('~/boards/models/label');
+require('~/boards/models/list');
+require('~/boards/models/user');
+require('~/boards/services/board_service');
+require('~/boards/stores/boards_store');
+require('./mock_data');
describe('Store', () => {
beforeEach(() => {
Vue.http.interceptors.push(boardsMockInterceptor);
- gl.boardService = new BoardService('/test/issue-boards/board', '1');
+ gl.boardService = new BoardService('/test/issue-boards/board', '', '1');
gl.issueBoards.BoardsStore.create();
Cookies.set('issue_board_welcome_hidden', 'false', {
@@ -61,18 +56,6 @@ describe('Store', () => {
expect(list).toBeDefined();
});
- it('finds list limited by type', () => {
- gl.issueBoards.BoardsStore.addList({
- id: 1,
- position: 0,
- title: 'Test',
- list_type: 'backlog'
- });
- const list = gl.issueBoards.BoardsStore.findList('id', 1, 'backlog');
-
- expect(list).toBeDefined();
- });
-
it('gets issue when new list added', (done) => {
gl.issueBoards.BoardsStore.addList(listObj);
const list = gl.issueBoards.BoardsStore.findList('id', 1);
@@ -117,10 +100,7 @@ describe('Store', () => {
expect(gl.issueBoards.BoardsStore.shouldAddBlankState()).toBe(false);
});
- it('check for blank state adding when backlog & done list exist', () => {
- gl.issueBoards.BoardsStore.addList({
- list_type: 'backlog'
- });
+ it('check for blank state adding when done list exist', () => {
gl.issueBoards.BoardsStore.addList({
list_type: 'done'
});
diff --git a/spec/javascripts/boards/issue_card_spec.js.es6 b/spec/javascripts/boards/issue_card_spec.js.es6
new file mode 100644
index 00000000000..4340a571017
--- /dev/null
+++ b/spec/javascripts/boards/issue_card_spec.js.es6
@@ -0,0 +1,191 @@
+/* global Vue */
+/* global ListUser */
+/* global ListLabel */
+/* global listObj */
+/* global ListIssue */
+
+require('~/boards/models/issue');
+require('~/boards/models/label');
+require('~/boards/models/list');
+require('~/boards/models/user');
+require('~/boards/stores/boards_store');
+require('~/boards/components/issue_card_inner');
+require('./mock_data');
+
+describe('Issue card component', () => {
+ const user = new ListUser({
+ id: 1,
+ name: 'testing 123',
+ username: 'test',
+ avatar: 'test_image',
+ });
+ const label1 = new ListLabel({
+ id: 3,
+ title: 'testing 123',
+ color: 'blue',
+ text_color: 'white',
+ description: 'test',
+ });
+ let component;
+ let issue;
+ let list;
+
+ beforeEach(() => {
+ setFixtures('<div class="test-container"></div>');
+
+ list = listObj;
+ issue = new ListIssue({
+ title: 'Testing',
+ iid: 1,
+ confidential: false,
+ labels: [list.label],
+ });
+
+ component = new Vue({
+ el: document.querySelector('.test-container'),
+ data() {
+ return {
+ list,
+ issue,
+ issueLinkBase: '/test',
+ rootPath: '/',
+ };
+ },
+ components: {
+ 'issue-card': gl.issueBoards.IssueCardInner,
+ },
+ template: `
+ <issue-card
+ :issue="issue"
+ :list="list"
+ :issue-link-base="issueLinkBase"
+ :root-path="rootPath"></issue-card>
+ `,
+ });
+ });
+
+ it('renders issue title', () => {
+ expect(
+ component.$el.querySelector('.card-title').textContent,
+ ).toContain(issue.title);
+ });
+
+ it('includes issue base in link', () => {
+ expect(
+ component.$el.querySelector('.card-title a').getAttribute('href'),
+ ).toContain('/test');
+ });
+
+ it('includes issue title on link', () => {
+ expect(
+ component.$el.querySelector('.card-title a').getAttribute('title'),
+ ).toBe(issue.title);
+ });
+
+ it('does not render confidential icon', () => {
+ expect(
+ component.$el.querySelector('.fa-eye-flash'),
+ ).toBeNull();
+ });
+
+ it('renders confidential icon', (done) => {
+ component.issue.confidential = true;
+
+ setTimeout(() => {
+ expect(
+ component.$el.querySelector('.confidential-icon'),
+ ).not.toBeNull();
+ done();
+ }, 0);
+ });
+
+ it('renders issue ID with #', () => {
+ expect(
+ component.$el.querySelector('.card-number').textContent,
+ ).toContain(`#${issue.id}`);
+ });
+
+ describe('assignee', () => {
+ it('does not render assignee', () => {
+ expect(
+ component.$el.querySelector('.card-assignee'),
+ ).toBeNull();
+ });
+
+ describe('exists', () => {
+ beforeEach((done) => {
+ component.issue.assignee = user;
+
+ setTimeout(() => {
+ done();
+ }, 0);
+ });
+
+ it('renders assignee', () => {
+ expect(
+ component.$el.querySelector('.card-assignee'),
+ ).not.toBeNull();
+ });
+
+ it('sets title', () => {
+ expect(
+ component.$el.querySelector('.card-assignee').getAttribute('title'),
+ ).toContain(`Assigned to ${user.name}`);
+ });
+
+ it('sets users path', () => {
+ expect(
+ component.$el.querySelector('.card-assignee').getAttribute('href'),
+ ).toBe('/test');
+ });
+
+ it('renders avatar', () => {
+ expect(
+ component.$el.querySelector('.card-assignee img'),
+ ).not.toBeNull();
+ });
+ });
+ });
+
+ describe('labels', () => {
+ it('does not render any', () => {
+ expect(
+ component.$el.querySelector('.label'),
+ ).toBeNull();
+ });
+
+ describe('exists', () => {
+ beforeEach((done) => {
+ component.issue.addLabel(label1);
+
+ setTimeout(() => {
+ done();
+ }, 0);
+ });
+
+ it('does not render list label', () => {
+ expect(
+ component.$el.querySelectorAll('.label').length,
+ ).toBe(1);
+ });
+
+ it('renders label', () => {
+ expect(
+ component.$el.querySelector('.label').textContent,
+ ).toContain(label1.title);
+ });
+
+ it('sets label description as title', () => {
+ expect(
+ component.$el.querySelector('.label').getAttribute('title'),
+ ).toContain(label1.description);
+ });
+
+ it('sets background color of button', () => {
+ expect(
+ component.$el.querySelector('.label').style.backgroundColor,
+ ).toContain(label1.color);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/boards/issue_spec.js.es6 b/spec/javascripts/boards/issue_spec.js.es6
index c8a61a0a9b5..aab4d9c501e 100644
--- a/spec/javascripts/boards/issue_spec.js.es6
+++ b/spec/javascripts/boards/issue_spec.js.es6
@@ -2,25 +2,20 @@
/* global BoardService */
/* global ListIssue */
-//= require jquery
-//= require jquery_ujs
-//= require js.cookie
-//= require vue
-//= require vue-resource
-//= require lib/utils/url_utility
-//= require boards/models/issue
-//= require boards/models/label
-//= require boards/models/list
-//= require boards/models/user
-//= require boards/services/board_service
-//= require boards/stores/boards_store
-//= require ./mock_data
+require('~/lib/utils/url_utility');
+require('~/boards/models/issue');
+require('~/boards/models/label');
+require('~/boards/models/list');
+require('~/boards/models/user');
+require('~/boards/services/board_service');
+require('~/boards/stores/boards_store');
+require('./mock_data');
describe('Issue model', () => {
let issue;
beforeEach(() => {
- gl.boardService = new BoardService('/test/issue-boards/board', '1');
+ gl.boardService = new BoardService('/test/issue-boards/board', '', '1');
gl.issueBoards.BoardsStore.create();
issue = new ListIssue({
diff --git a/spec/javascripts/boards/list_spec.js.es6 b/spec/javascripts/boards/list_spec.js.es6
index 7d942ec3d65..4397a32fedc 100644
--- a/spec/javascripts/boards/list_spec.js.es6
+++ b/spec/javascripts/boards/list_spec.js.es6
@@ -5,26 +5,21 @@
/* global List */
/* global listObj */
-//= require jquery
-//= require jquery_ujs
-//= require js.cookie
-//= require vue
-//= require vue-resource
-//= require lib/utils/url_utility
-//= require boards/models/issue
-//= require boards/models/label
-//= require boards/models/list
-//= require boards/models/user
-//= require boards/services/board_service
-//= require boards/stores/boards_store
-//= require ./mock_data
+require('~/lib/utils/url_utility');
+require('~/boards/models/issue');
+require('~/boards/models/label');
+require('~/boards/models/list');
+require('~/boards/models/user');
+require('~/boards/services/board_service');
+require('~/boards/stores/boards_store');
+require('./mock_data');
describe('List model', () => {
let list;
beforeEach(() => {
Vue.http.interceptors.push(boardsMockInterceptor);
- gl.boardService = new BoardService('/test/issue-boards/board', '1');
+ gl.boardService = new BoardService('/test/issue-boards/board', '', '1');
gl.issueBoards.BoardsStore.create();
list = new List(listObj);
diff --git a/spec/javascripts/boards/modal_store_spec.js.es6 b/spec/javascripts/boards/modal_store_spec.js.es6
new file mode 100644
index 00000000000..1815847f3fa
--- /dev/null
+++ b/spec/javascripts/boards/modal_store_spec.js.es6
@@ -0,0 +1,132 @@
+/* global Vue */
+/* global ListIssue */
+
+require('~/boards/models/issue');
+require('~/boards/models/label');
+require('~/boards/models/list');
+require('~/boards/models/user');
+require('~/boards/stores/modal_store');
+
+describe('Modal store', () => {
+ let issue;
+ let issue2;
+ const Store = gl.issueBoards.ModalStore;
+
+ beforeEach(() => {
+ // Setup default state
+ Store.store.issues = [];
+ Store.store.selectedIssues = [];
+
+ issue = new ListIssue({
+ title: 'Testing',
+ iid: 1,
+ confidential: false,
+ labels: [],
+ });
+ issue2 = new ListIssue({
+ title: 'Testing',
+ iid: 2,
+ confidential: false,
+ labels: [],
+ });
+ Store.store.issues.push(issue);
+ Store.store.issues.push(issue2);
+ });
+
+ it('returns selected count', () => {
+ expect(Store.selectedCount()).toBe(0);
+ });
+
+ it('toggles the issue as selected', () => {
+ Store.toggleIssue(issue);
+
+ expect(issue.selected).toBe(true);
+ expect(Store.selectedCount()).toBe(1);
+ });
+
+ it('toggles the issue as un-selected', () => {
+ Store.toggleIssue(issue);
+ Store.toggleIssue(issue);
+
+ expect(issue.selected).toBe(false);
+ expect(Store.selectedCount()).toBe(0);
+ });
+
+ it('toggles all issues as selected', () => {
+ Store.toggleAll();
+
+ expect(issue.selected).toBe(true);
+ expect(issue2.selected).toBe(true);
+ expect(Store.selectedCount()).toBe(2);
+ });
+
+ it('toggles all issues as un-selected', () => {
+ Store.toggleAll();
+ Store.toggleAll();
+
+ expect(issue.selected).toBe(false);
+ expect(issue2.selected).toBe(false);
+ expect(Store.selectedCount()).toBe(0);
+ });
+
+ it('toggles all if a single issue is selected', () => {
+ Store.toggleIssue(issue);
+ Store.toggleAll();
+
+ expect(issue.selected).toBe(true);
+ expect(issue2.selected).toBe(true);
+ expect(Store.selectedCount()).toBe(2);
+ });
+
+ it('adds issue to selected array', () => {
+ issue.selected = true;
+ Store.addSelectedIssue(issue);
+
+ expect(Store.selectedCount()).toBe(1);
+ });
+
+ it('removes issue from selected array', () => {
+ Store.addSelectedIssue(issue);
+ Store.removeSelectedIssue(issue);
+
+ expect(Store.selectedCount()).toBe(0);
+ });
+
+ it('returns selected issue index if present', () => {
+ Store.toggleIssue(issue);
+
+ expect(Store.selectedIssueIndex(issue)).toBe(0);
+ });
+
+ it('returns -1 if issue is not selected', () => {
+ expect(Store.selectedIssueIndex(issue)).toBe(-1);
+ });
+
+ it('finds the selected issue', () => {
+ Store.toggleIssue(issue);
+
+ expect(Store.findSelectedIssue(issue)).toBe(issue);
+ });
+
+ it('does not find a selected issue', () => {
+ expect(Store.findSelectedIssue(issue)).toBe(undefined);
+ });
+
+ it('does not remove from selected issue if tab is not all', () => {
+ Store.store.activeTab = 'selected';
+
+ Store.toggleIssue(issue);
+ Store.toggleIssue(issue);
+
+ expect(Store.store.selectedIssues.length).toBe(1);
+ expect(Store.selectedCount()).toBe(0);
+ });
+
+ it('gets selected issue array with only selected issues', () => {
+ Store.toggleIssue(issue);
+ Store.toggleIssue(issue2);
+ Store.toggleIssue(issue2);
+
+ expect(Store.getSelectedIssues().length).toBe(1);
+ });
+});
diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js.es6 b/spec/javascripts/bootstrap_linked_tabs_spec.js.es6
index ea953d0f5a5..fa9f95e16cd 100644
--- a/spec/javascripts/bootstrap_linked_tabs_spec.js.es6
+++ b/spec/javascripts/bootstrap_linked_tabs_spec.js.es6
@@ -1,6 +1,15 @@
-//= require lib/utils/bootstrap_linked_tabs
+require('~/lib/utils/bootstrap_linked_tabs');
(() => {
+ // TODO: remove this hack!
+ // PhantomJS causes spyOn to panic because replaceState isn't "writable"
+ let phantomjs;
+ try {
+ phantomjs = !Object.getOwnPropertyDescriptor(window.history, 'replaceState').writable;
+ } catch (err) {
+ phantomjs = false;
+ }
+
describe('Linked Tabs', () => {
preloadFixtures('static/linked_tabs.html.raw');
@@ -10,7 +19,9 @@
describe('when is initialized', () => {
beforeEach(() => {
- spyOn(window.history, 'replaceState').and.callFake(function () {});
+ if (!phantomjs) {
+ spyOn(window.history, 'replaceState').and.callFake(function () {});
+ }
});
it('should activate the tab correspondent to the given action', () => {
@@ -36,7 +47,7 @@
describe('on click', () => {
it('should change the url according to the clicked tab', () => {
- const historySpy = spyOn(history, 'replaceState').and.callFake(() => {});
+ const historySpy = !phantomjs && spyOn(history, 'replaceState').and.callFake(() => {});
const linkedTabs = new window.gl.LinkedTabs({ // eslint-disable-line
action: 'show',
@@ -49,10 +60,11 @@
secondTab.click();
- expect(historySpy).toHaveBeenCalledWith({
- turbolinks: true,
- url: newState,
- }, document.title, newState);
+ if (historySpy) {
+ expect(historySpy).toHaveBeenCalledWith({
+ url: newState,
+ }, document.title, newState);
+ }
});
});
});
diff --git a/spec/javascripts/build_spec.js.es6 b/spec/javascripts/build_spec.js.es6
index 0c556382980..0bd50588f5a 100644
--- a/spec/javascripts/build_spec.js.es6
+++ b/spec/javascripts/build_spec.js.es6
@@ -1,12 +1,11 @@
/* eslint-disable no-new */
/* global Build */
-/* global Turbolinks */
-//= require lib/utils/datetime_utility
-//= require build
-//= require breakpoints
-//= require jquery.nicescroll
-//= require turbolinks
+require('~/lib/utils/datetime_utility');
+require('~/lib/utils/url_utility');
+require('~/build');
+require('~/breakpoints');
+require('vendor/jquery.nicescroll');
describe('Build', () => {
const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/builds/1`;
@@ -167,7 +166,7 @@ describe('Build', () => {
});
it('reloads the page when the build is done', () => {
- spyOn(Turbolinks, 'visit');
+ spyOn(gl.utils, 'visitUrl');
jasmine.clock().tick(4001);
const [{ success, context }] = $.ajax.calls.argsFor(1);
@@ -177,7 +176,7 @@ describe('Build', () => {
append: true,
});
- expect(Turbolinks.visit).toHaveBeenCalledWith(BUILD_URL);
+ expect(gl.utils.visitUrl).toHaveBeenCalledWith(BUILD_URL);
});
});
});
diff --git a/spec/javascripts/commits_spec.js.es6 b/spec/javascripts/commits_spec.js.es6
index bb9a9072f3a..05260760c43 100644
--- a/spec/javascripts/commits_spec.js.es6
+++ b/spec/javascripts/commits_spec.js.es6
@@ -1,10 +1,19 @@
/* global CommitsList */
-//= require jquery.endless-scroll
-//= require pager
-//= require commits
+require('vendor/jquery.endless-scroll');
+require('~/pager');
+require('~/commits');
(() => {
+ // TODO: remove this hack!
+ // PhantomJS causes spyOn to panic because replaceState isn't "writable"
+ let phantomjs;
+ try {
+ phantomjs = !Object.getOwnPropertyDescriptor(window.history, 'replaceState').writable;
+ } catch (err) {
+ phantomjs = false;
+ }
+
describe('Commits List', () => {
beforeEach(() => {
setFixtures(`
@@ -25,7 +34,10 @@
beforeEach(() => {
CommitsList.init(25);
CommitsList.searchField.val('');
- spyOn(history, 'replaceState').and.stub();
+
+ if (!phantomjs) {
+ spyOn(history, 'replaceState').and.stub();
+ }
ajaxSpy = spyOn(jQuery, 'ajax').and.callFake((req) => {
req.success({
data: '<li>Result</li>',
diff --git a/spec/javascripts/dashboard_spec.js.es6 b/spec/javascripts/dashboard_spec.js.es6
index 4d851b2d320..c0bdb89ed63 100644
--- a/spec/javascripts/dashboard_spec.js.es6
+++ b/spec/javascripts/dashboard_spec.js.es6
@@ -1,9 +1,7 @@
/* eslint-disable no-new */
-/*= require sidebar */
-/*= require jquery */
-/*= require js.cookie */
-/*= require lib/utils/text_utility */
+require('~/sidebar');
+require('~/lib/utils/text_utility');
((global) => {
describe('Dashboard', () => {
diff --git a/spec/javascripts/datetime_utility_spec.js.es6 b/spec/javascripts/datetime_utility_spec.js.es6
index 8ece24555c5..d5eec10be42 100644
--- a/spec/javascripts/datetime_utility_spec.js.es6
+++ b/spec/javascripts/datetime_utility_spec.js.es6
@@ -1,4 +1,4 @@
-//= require lib/utils/datetime_utility
+require('~/lib/utils/datetime_utility');
(() => {
describe('Date time utils', () => {
diff --git a/spec/javascripts/diff_comments_store_spec.js.es6 b/spec/javascripts/diff_comments_store_spec.js.es6
index fbfa34a5da7..f956394ef53 100644
--- a/spec/javascripts/diff_comments_store_spec.js.es6
+++ b/spec/javascripts/diff_comments_store_spec.js.es6
@@ -1,10 +1,9 @@
/* eslint-disable jasmine/no-global-setup, dot-notation, jasmine/no-expect-in-setup-teardown, max-len */
/* global CommentsStore */
-//= require vue
-//= require diff_notes/models/discussion
-//= require diff_notes/models/note
-//= require diff_notes/stores/comments
+require('~/diff_notes/models/discussion');
+require('~/diff_notes/models/note');
+require('~/diff_notes/stores/comments');
(() => {
function createDiscussion(noteId = 1, resolved = true) {
diff --git a/spec/javascripts/environments/environment_actions_spec.js.es6 b/spec/javascripts/environments/environment_actions_spec.js.es6
index 056e4d41e93..b1838045a06 100644
--- a/spec/javascripts/environments/environment_actions_spec.js.es6
+++ b/spec/javascripts/environments/environment_actions_spec.js.es6
@@ -1,5 +1,4 @@
-//= require vue
-//= require environments/components/environment_actions
+require('~/environments/components/environment_actions');
describe('Actions Component', () => {
preloadFixtures('static/environments/element.html.raw');
diff --git a/spec/javascripts/environments/environment_external_url_spec.js.es6 b/spec/javascripts/environments/environment_external_url_spec.js.es6
index 950a5d53fad..a6a587e69f5 100644
--- a/spec/javascripts/environments/environment_external_url_spec.js.es6
+++ b/spec/javascripts/environments/environment_external_url_spec.js.es6
@@ -1,5 +1,4 @@
-//= require vue
-//= require environments/components/environment_external_url
+require('~/environments/components/environment_external_url');
describe('External URL Component', () => {
preloadFixtures('static/environments/element.html.raw');
diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6
index c178b9cc1ec..9858f346c83 100644
--- a/spec/javascripts/environments/environment_item_spec.js.es6
+++ b/spec/javascripts/environments/environment_item_spec.js.es6
@@ -1,6 +1,5 @@
-//= require vue
-//= require timeago
-//= require environments/components/environment_item
+window.timeago = require('vendor/timeago');
+require('~/environments/components/environment_item');
describe('Environment item', () => {
preloadFixtures('static/environments/table.html.raw');
diff --git a/spec/javascripts/environments/environment_rollback_spec.js.es6 b/spec/javascripts/environments/environment_rollback_spec.js.es6
index 95796f23894..043b8708a6e 100644
--- a/spec/javascripts/environments/environment_rollback_spec.js.es6
+++ b/spec/javascripts/environments/environment_rollback_spec.js.es6
@@ -1,5 +1,5 @@
-//= require vue
-//= require environments/components/environment_rollback
+require('~/environments/components/environment_rollback');
+
describe('Rollback Component', () => {
preloadFixtures('static/environments/element.html.raw');
diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6
index 239cd69dd3a..87eda136122 100644
--- a/spec/javascripts/environments/environment_spec.js.es6
+++ b/spec/javascripts/environments/environment_spec.js.es6
@@ -1,11 +1,9 @@
/* global Vue, environment */
-//= require vue
-//= require vue-resource
-//= require flash
-//= require environments/stores/environments_store
-//= require environments/components/environment
-//= require ./mock_data
+require('~/flash');
+require('~/environments/stores/environments_store');
+require('~/environments/components/environment');
+require('./mock_data');
describe('Environment', () => {
preloadFixtures('static/environments/environments.html.raw');
diff --git a/spec/javascripts/environments/environment_stop_spec.js.es6 b/spec/javascripts/environments/environment_stop_spec.js.es6
index bb998a32f32..2dfce5ba824 100644
--- a/spec/javascripts/environments/environment_stop_spec.js.es6
+++ b/spec/javascripts/environments/environment_stop_spec.js.es6
@@ -1,5 +1,5 @@
-//= require vue
-//= require environments/components/environment_stop
+require('~/environments/components/environment_stop');
+
describe('Stop Component', () => {
preloadFixtures('static/environments/element.html.raw');
diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6
index 17c00acf63e..9a8300d3832 100644
--- a/spec/javascripts/environments/environments_store_spec.js.es6
+++ b/spec/javascripts/environments/environments_store_spec.js.es6
@@ -1,8 +1,7 @@
/* global environmentsList */
-//= require vue
-//= require environments/stores/environments_store
-//= require ./mock_data
+require('~/environments/stores/environments_store');
+require('./mock_data');
(() => {
describe('Store', () => {
diff --git a/spec/javascripts/extensions/array_spec.js.es6 b/spec/javascripts/extensions/array_spec.js.es6
index 3949c5615d5..ba5eb81defc 100644
--- a/spec/javascripts/extensions/array_spec.js.es6
+++ b/spec/javascripts/extensions/array_spec.js.es6
@@ -1,6 +1,6 @@
/* eslint-disable space-before-function-paren, no-var */
-/*= require extensions/array */
+require('~/extensions/array');
(function() {
describe('Array extensions', function() {
diff --git a/spec/javascripts/extensions/element_spec.js.es6 b/spec/javascripts/extensions/element_spec.js.es6
index c5b86d35204..2d8a128ed33 100644
--- a/spec/javascripts/extensions/element_spec.js.es6
+++ b/spec/javascripts/extensions/element_spec.js.es6
@@ -1,4 +1,4 @@
-/*= require extensions/element */
+require('~/extensions/element');
(() => {
describe('Element extensions', function () {
diff --git a/spec/javascripts/extensions/jquery_spec.js b/spec/javascripts/extensions/jquery_spec.js
index 5cd0e5ab0f0..c0bb0419814 100644
--- a/spec/javascripts/extensions/jquery_spec.js
+++ b/spec/javascripts/extensions/jquery_spec.js
@@ -1,6 +1,6 @@
/* eslint-disable space-before-function-paren, no-var */
-/*= require extensions/jquery */
+require('~/extensions/jquery');
(function() {
describe('jQuery extensions', function() {
diff --git a/spec/javascripts/extensions/object_spec.js.es6 b/spec/javascripts/extensions/object_spec.js.es6
index 3b71c255b30..2467ed78459 100644
--- a/spec/javascripts/extensions/object_spec.js.es6
+++ b/spec/javascripts/extensions/object_spec.js.es6
@@ -1,4 +1,4 @@
-/*= require extensions/object */
+require('~/extensions/object');
describe('Object extensions', () => {
describe('assign', () => {
diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js.es6 b/spec/javascripts/filtered_search/dropdown_user_spec.js.es6
index 5eba4343a1d..10a316f31b4 100644
--- a/spec/javascripts/filtered_search/dropdown_user_spec.js.es6
+++ b/spec/javascripts/filtered_search/dropdown_user_spec.js.es6
@@ -1,7 +1,7 @@
-//= require filtered_search/dropdown_utils
-//= require filtered_search/filtered_search_tokenizer
-//= require filtered_search/filtered_search_dropdown
-//= require filtered_search/dropdown_user
+require('~/filtered_search/dropdown_utils');
+require('~/filtered_search/filtered_search_tokenizer');
+require('~/filtered_search/filtered_search_dropdown');
+require('~/filtered_search/dropdown_user');
(() => {
describe('Dropdown User', () => {
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js.es6 b/spec/javascripts/filtered_search/dropdown_utils_spec.js.es6
index 89e49b7c511..1e2d7582d5b 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js.es6
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js.es6
@@ -1,7 +1,7 @@
-//= require extensions/array
-//= require filtered_search/dropdown_utils
-//= require filtered_search/filtered_search_tokenizer
-//= require filtered_search/filtered_search_dropdown_manager
+require('~/extensions/array');
+require('~/filtered_search/dropdown_utils');
+require('~/filtered_search/filtered_search_tokenizer');
+require('~/filtered_search/filtered_search_dropdown_manager');
(() => {
describe('Dropdown Utils', () => {
diff --git a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js.es6 b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js.es6
index 4bd45eb457d..ed0b0196ec4 100644
--- a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js.es6
+++ b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js.es6
@@ -1,6 +1,6 @@
-//= require extensions/array
-//= require filtered_search/filtered_search_tokenizer
-//= require filtered_search/filtered_search_dropdown_manager
+require('~/extensions/array');
+require('~/filtered_search/filtered_search_tokenizer');
+require('~/filtered_search/filtered_search_dropdown_manager');
(() => {
describe('Filtered Search Dropdown Manager', () => {
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js.es6 b/spec/javascripts/filtered_search/filtered_search_manager_spec.js.es6
index a508dacf7f0..98959dda242 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js.es6
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js.es6
@@ -1,11 +1,9 @@
-/* global Turbolinks */
-
-//= require turbolinks
-//= require lib/utils/common_utils
-//= require filtered_search/filtered_search_token_keys
-//= require filtered_search/filtered_search_tokenizer
-//= require filtered_search/filtered_search_dropdown_manager
-//= require filtered_search/filtered_search_manager
+require('~/lib/utils/url_utility');
+require('~/lib/utils/common_utils');
+require('~/filtered_search/filtered_search_token_keys');
+require('~/filtered_search/filtered_search_tokenizer');
+require('~/filtered_search/filtered_search_dropdown_manager');
+require('~/filtered_search/filtered_search_manager');
(() => {
describe('Filtered Search Manager', () => {
@@ -38,7 +36,7 @@
it('should search with a single word', () => {
getInput().value = 'searchTerm';
- spyOn(Turbolinks, 'visit').and.callFake((url) => {
+ spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=searchTerm`);
});
@@ -48,7 +46,7 @@
it('should search with multiple words', () => {
getInput().value = 'awesome search terms';
- spyOn(Turbolinks, 'visit').and.callFake((url) => {
+ spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
});
@@ -58,7 +56,7 @@
it('should search with special characters', () => {
getInput().value = '~!@#$%^&*()_+{}:<>,.?/';
- spyOn(Turbolinks, 'visit').and.callFake((url) => {
+ spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
});
diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es6 b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es6
index 9d9097419ea..cf409a7e509 100644
--- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es6
+++ b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js.es6
@@ -1,5 +1,5 @@
-//= require extensions/array
-//= require filtered_search/filtered_search_token_keys
+require('~/extensions/array');
+require('~/filtered_search/filtered_search_token_keys');
(() => {
describe('Filtered Search Token Keys', () => {
diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js.es6 b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js.es6
index ac7f8e9cbcd..84c0e9cbfe2 100644
--- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js.es6
+++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js.es6
@@ -1,6 +1,6 @@
-//= require extensions/array
-//= require filtered_search/filtered_search_token_keys
-//= require filtered_search/filtered_search_tokenizer
+require('~/extensions/array');
+require('~/filtered_search/filtered_search_token_keys');
+require('~/filtered_search/filtered_search_tokenizer');
(() => {
describe('Filtered Search Tokenizer', () => {
diff --git a/spec/javascripts/gfm_auto_complete_spec.js.es6 b/spec/javascripts/gfm_auto_complete_spec.js.es6
index 99cebb32a8b..c61c32f8a13 100644
--- a/spec/javascripts/gfm_auto_complete_spec.js.es6
+++ b/spec/javascripts/gfm_auto_complete_spec.js.es6
@@ -1,6 +1,6 @@
-//= require gfm_auto_complete
-//= require jquery
-//= require jquery.atwho
+require('~/gfm_auto_complete');
+require('vendor/jquery.caret');
+require('vendor/jquery.atwho');
const global = window.gl || (window.gl = {});
const GfmAutoComplete = global.GfmAutoComplete;
diff --git a/spec/javascripts/gl_dropdown_spec.js.es6 b/spec/javascripts/gl_dropdown_spec.js.es6
index 4e7eed2767c..317f38c5888 100644
--- a/spec/javascripts/gl_dropdown_spec.js.es6
+++ b/spec/javascripts/gl_dropdown_spec.js.es6
@@ -1,11 +1,9 @@
/* eslint-disable comma-dangle, no-param-reassign, no-unused-expressions, max-len */
-/* global Turbolinks */
-/*= require jquery */
-/*= require gl_dropdown */
-/*= require turbolinks */
-/*= require lib/utils/common_utils */
-/*= require lib/utils/type_utility */
+require('~/gl_dropdown');
+require('~/lib/utils/common_utils');
+require('~/lib/utils/type_utility');
+require('~/lib/utils/url_utility');
(() => {
const NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-link';
@@ -113,13 +111,13 @@
expect(this.dropdownContainerElement).toHaveClass('open');
const randomIndex = Math.floor(Math.random() * (this.projectsData.length - 1)) + 0;
navigateWithKeys('down', randomIndex, () => {
- spyOn(Turbolinks, 'visit').and.stub();
+ spyOn(gl.utils, 'visitUrl').and.stub();
navigateWithKeys('enter', null, () => {
expect(this.dropdownContainerElement).not.toHaveClass('open');
const link = $(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement);
expect(link).toHaveClass('is-active');
const linkedLocation = link.attr('href');
- if (linkedLocation && linkedLocation !== '#') expect(Turbolinks.visit).toHaveBeenCalledWith(linkedLocation);
+ if (linkedLocation && linkedLocation !== '#') expect(gl.utils.visitUrl).toHaveBeenCalledWith(linkedLocation);
});
});
});
diff --git a/spec/javascripts/gl_field_errors_spec.js.es6 b/spec/javascripts/gl_field_errors_spec.js.es6
index f68fd9e00d7..733023481f5 100644
--- a/spec/javascripts/gl_field_errors_spec.js.es6
+++ b/spec/javascripts/gl_field_errors_spec.js.es6
@@ -1,7 +1,6 @@
/* eslint-disable space-before-function-paren, arrow-body-style */
-//= require jquery
-//= require gl_field_errors
+require('~/gl_field_errors');
((global) => {
preloadFixtures('static/gl_field_errors.html.raw');
diff --git a/spec/javascripts/gl_form_spec.js.es6 b/spec/javascripts/gl_form_spec.js.es6
index b5f99483bfb..71d6e2a7e22 100644
--- a/spec/javascripts/gl_form_spec.js.es6
+++ b/spec/javascripts/gl_form_spec.js.es6
@@ -1,8 +1,9 @@
/* global autosize */
-/*= require gl_form */
-/*= require autosize */
-/*= require lib/utils/text_utility */
-/*= require lib/utils/common_utils */
+
+window.autosize = require('vendor/autosize');
+require('~/gl_form');
+require('~/lib/utils/text_utility');
+require('~/lib/utils/common_utils');
describe('GLForm', () => {
const global = window.gl || (window.gl = {});
diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
index d76fcc5206a..a954bb60560 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
@@ -3,7 +3,7 @@
/* global ContributorsGraph */
/* global ContributorsMasterGraph */
-//= require graphs/stat_graph_contributors_graph
+require('~/graphs/stat_graph_contributors_graph');
describe("ContributorsGraph", function () {
describe("#set_x_domain", function () {
diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
index 63f28dfb8ad..b15764abe8c 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable quotes, no-var, camelcase, object-property-newline, comma-dangle, max-len, vars-on-top, quote-props */
/* global ContributorsStatGraphUtil */
-//= require graphs/stat_graph_contributors_util
+require('~/graphs/stat_graph_contributors_util');
describe("ContributorsStatGraphUtil", function () {
describe("#parse_log", function () {
diff --git a/spec/javascripts/graphs/stat_graph_spec.js b/spec/javascripts/graphs/stat_graph_spec.js
index 71b589e6b83..876c23361bc 100644
--- a/spec/javascripts/graphs/stat_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable quotes */
/* global StatGraph */
-//= require graphs/stat_graph
+require('~/graphs/stat_graph');
describe("StatGraph", function () {
describe("#get_log", function () {
diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js
index b846c5ab00b..cecebb0b038 100644
--- a/spec/javascripts/header_spec.js
+++ b/spec/javascripts/header_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable space-before-function-paren, no-var */
-/*= require header */
-/*= require lib/utils/text_utility */
-/*= require jquery */
+
+require('~/header');
+require('~/lib/utils/text_utility');
(function() {
describe('Header', function() {
diff --git a/spec/javascripts/helpers/class_spec_helper_spec.js.es6 b/spec/javascripts/helpers/class_spec_helper_spec.js.es6
index d1155f1bd1e..0a61e561640 100644
--- a/spec/javascripts/helpers/class_spec_helper_spec.js.es6
+++ b/spec/javascripts/helpers/class_spec_helper_spec.js.es6
@@ -1,5 +1,6 @@
/* global ClassSpecHelper */
-//= require ./class_spec_helper
+
+require('./class_spec_helper');
describe('ClassSpecHelper', () => {
describe('.itShouldBeAStaticMethod', function () {
diff --git a/spec/javascripts/issuable_spec.js.es6 b/spec/javascripts/issuable_spec.js.es6
index 917a6267b92..26d87cc5931 100644
--- a/spec/javascripts/issuable_spec.js.es6
+++ b/spec/javascripts/issuable_spec.js.es6
@@ -1,8 +1,7 @@
/* global Issuable */
-/* global Turbolinks */
-//= require issuable
-//= require turbolinks
+require('~/lib/utils/url_utility');
+require('~/issuable');
(() => {
const BASE_URL = '/user/project/issues?scope=all&state=closed';
@@ -42,39 +41,39 @@
});
it('should contain only the default parameters', () => {
- spyOn(Turbolinks, 'visit');
+ spyOn(gl.utils, 'visitUrl');
Issuable.filterResults($filtersForm);
- expect(Turbolinks.visit).toHaveBeenCalledWith(BASE_URL + DEFAULT_PARAMS);
+ expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + DEFAULT_PARAMS);
});
it('should filter for the phrase "broken"', () => {
- spyOn(Turbolinks, 'visit');
+ spyOn(gl.utils, 'visitUrl');
updateForm({ search: 'broken' }, $filtersForm);
Issuable.filterResults($filtersForm);
const params = `${DEFAULT_PARAMS}&search=broken`;
- expect(Turbolinks.visit).toHaveBeenCalledWith(BASE_URL + params);
+ expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
});
it('should keep query parameters after modifying filter', () => {
- spyOn(Turbolinks, 'visit');
+ spyOn(gl.utils, 'visitUrl');
// initial filter
updateForm({ milestone_title: 'v1.0' }, $filtersForm);
Issuable.filterResults($filtersForm);
let params = `${DEFAULT_PARAMS}&milestone_title=v1.0`;
- expect(Turbolinks.visit).toHaveBeenCalledWith(BASE_URL + params);
+ expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
// update filter
updateForm({ label_name: 'Frontend' }, $filtersForm);
Issuable.filterResults($filtersForm);
params = `${DEFAULT_PARAMS}&milestone_title=v1.0&label_name=Frontend`;
- expect(Turbolinks.visit).toHaveBeenCalledWith(BASE_URL + params);
+ expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
});
});
});
diff --git a/spec/javascripts/issuable_time_tracker_spec.js.es6 b/spec/javascripts/issuable_time_tracker_spec.js.es6
index c5671af235e..cb068a4f879 100644
--- a/spec/javascripts/issuable_time_tracker_spec.js.es6
+++ b/spec/javascripts/issuable_time_tracker_spec.js.es6
@@ -1,7 +1,8 @@
/* eslint-disable */
-//= require jquery
-//= require vue
-//= require issuable/time_tracking/components/time_tracker
+
+require('jquery');
+require('vue');
+require('~/issuable/time_tracking/components/time_tracker');
function initTimeTrackingComponent(opts) {
setFixtures(`
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index 673a4b3c07a..5b0b7aa7903 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -1,8 +1,8 @@
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */
/* global Issue */
-/*= require lib/utils/text_utility */
-/*= require issue */
+require('~/lib/utils/text_utility');
+require('~/issue');
(function() {
var INVALID_URL = 'http://goesnowhere.nothing/whereami';
diff --git a/spec/javascripts/labels_issue_sidebar_spec.js.es6 b/spec/javascripts/labels_issue_sidebar_spec.js.es6
index 0d19b4a25b9..37e038c16da 100644
--- a/spec/javascripts/labels_issue_sidebar_spec.js.es6
+++ b/spec/javascripts/labels_issue_sidebar_spec.js.es6
@@ -2,17 +2,15 @@
/* global IssuableContext */
/* global LabelsSelect */
-//= require lib/utils/type_utility
-//= require jquery
-//= require bootstrap
-//= require gl_dropdown
-//= require select2
-//= require jquery.nicescroll
-//= require api
-//= require create_label
-//= require issuable_context
-//= require users_select
-//= require labels_select
+require('~/lib/utils/type_utility');
+require('~/gl_dropdown');
+require('select2');
+require('vendor/jquery.nicescroll');
+require('~/api');
+require('~/create_label');
+require('~/issuable_context');
+require('~/users_select');
+require('~/labels_select');
(() => {
let saveLabelCount = 0;
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js.es6 b/spec/javascripts/lib/utils/common_utils_spec.js.es6
index 32c96e2a088..fbb06f3948b 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js.es6
+++ b/spec/javascripts/lib/utils/common_utils_spec.js.es6
@@ -1,4 +1,4 @@
-//= require lib/utils/common_utils
+require('~/lib/utils/common_utils');
(() => {
describe('common_utils', () => {
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js.es6 b/spec/javascripts/lib/utils/text_utility_spec.js.es6
index e97356b65d5..86ade66ec29 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js.es6
+++ b/spec/javascripts/lib/utils/text_utility_spec.js.es6
@@ -1,4 +1,4 @@
-//= require lib/utils/text_utility
+require('~/lib/utils/text_utility');
(() => {
describe('text_utility', () => {
@@ -21,5 +21,19 @@
expect(largeFont > regular).toBe(true);
});
});
+
+ describe('gl.text.pluralize', () => {
+ it('returns pluralized', () => {
+ expect(gl.text.pluralize('test', 2)).toBe('tests');
+ });
+
+ it('returns pluralized when count is 0', () => {
+ expect(gl.text.pluralize('test', 0)).toBe('tests');
+ });
+
+ it('does not return pluralized', () => {
+ expect(gl.text.pluralize('test', 1)).toBe('test');
+ });
+ });
});
})();
diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js
index 6605986c33a..8b196f7720f 100644
--- a/spec/javascripts/line_highlighter_spec.js
+++ b/spec/javascripts/line_highlighter_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable space-before-function-paren, no-var, no-param-reassign, quotes, prefer-template, no-else-return, new-cap, dot-notation, no-return-assign, comma-dangle, no-new, one-var, one-var-declaration-per-line, jasmine/no-spec-dupes, no-underscore-dangle, max-len */
/* global LineHighlighter */
-/*= require line_highlighter */
+require('~/line_highlighter');
(function() {
describe('LineHighlighter', function() {
diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js
index f644d39b1c7..25cfa9e9479 100644
--- a/spec/javascripts/merge_request_spec.js
+++ b/spec/javascripts/merge_request_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable space-before-function-paren, no-return-assign */
/* global MergeRequest */
-/*= require merge_request */
+require('~/merge_request');
(function() {
describe('MergeRequest', function() {
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 98201fb98ed..d20a59df041 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -1,11 +1,20 @@
/* eslint-disable no-var, comma-dangle, object-shorthand */
-/*= require merge_request_tabs */
-//= require breakpoints
-//= require lib/utils/common_utils
-//= require jquery.scrollTo
+require('~/merge_request_tabs');
+require('~/breakpoints');
+require('~/lib/utils/common_utils');
+require('vendor/jquery.scrollTo');
(function () {
+ // TODO: remove this hack!
+ // PhantomJS causes spyOn to panic because replaceState isn't "writable"
+ var phantomjs;
+ try {
+ phantomjs = !Object.getOwnPropertyDescriptor(window.history, 'replaceState').writable;
+ } catch (err) {
+ phantomjs = false;
+ }
+
describe('MergeRequestTabs', function () {
var stubLocation = {};
var setLocation = function (stubs) {
@@ -22,9 +31,11 @@
this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation });
setLocation();
- this.spies = {
- history: spyOn(window.history, 'replaceState').and.callFake(function () {})
- };
+ if (!phantomjs) {
+ this.spies = {
+ history: spyOn(window.history, 'replaceState').and.callFake(function () {})
+ };
+ }
});
describe('#activateTab', function () {
@@ -98,10 +109,11 @@
pathname: '/foo/bar/merge_requests/1'
});
newState = this.subject('commits');
- expect(this.spies.history).toHaveBeenCalledWith({
- turbolinks: true,
- url: newState
- }, document.title, newState);
+ if (!phantomjs) {
+ expect(this.spies.history).toHaveBeenCalledWith({
+ url: newState
+ }, document.title, newState);
+ }
});
it('treats "show" like "notes"', function () {
setLocation({
diff --git a/spec/javascripts/merge_request_widget_spec.js b/spec/javascripts/merge_request_widget_spec.js
index 6f1d6406897..8cefdd2409d 100644
--- a/spec/javascripts/merge_request_widget_spec.js
+++ b/spec/javascripts/merge_request_widget_spec.js
@@ -1,8 +1,8 @@
/* eslint-disable space-before-function-paren, quotes, comma-dangle, dot-notation, quote-props, no-var, max-len */
-/*= require merge_request_widget */
-/*= require smart_interval */
-/*= require lib/utils/datetime_utility */
+require('~/merge_request_widget');
+require('~/smart_interval');
+require('~/lib/utils/datetime_utility');
(function() {
describe('MergeRequestWidget', function() {
diff --git a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es6 b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es6
index a1c2fe3df37..a6994f6edf4 100644
--- a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es6
+++ b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js.es6
@@ -1,7 +1,7 @@
/* eslint-disable no-new */
-//= require flash
-//= require mini_pipeline_graph_dropdown
+require('~/flash');
+require('~/mini_pipeline_graph_dropdown');
(() => {
describe('Mini Pipeline Graph Dropdown', () => {
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index 8259d553f1b..9b657868523 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -1,8 +1,8 @@
/* eslint-disable space-before-function-paren, one-var, no-var, one-var-declaration-per-line, no-return-assign, quotes, max-len */
/* global NewBranchForm */
-/*= require jquery-ui/autocomplete */
-/*= require new_branch_form */
+require('jquery-ui/ui/autocomplete');
+require('~/new_branch_form');
(function() {
describe('Branch', function() {
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 015c35dfca7..af495787c54 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -1,10 +1,10 @@
/* eslint-disable space-before-function-paren, no-unused-expressions, no-var, object-shorthand, comma-dangle, max-len */
/* global Notes */
-/*= require notes */
-/*= require autosize */
-/*= require gl_form */
-/*= require lib/utils/text_utility */
+require('~/notes');
+require('vendor/autosize');
+require('~/gl_form');
+require('~/lib/utils/text_utility');
(function() {
window.gon || (window.gon = {});
diff --git a/spec/javascripts/pipelines_spec.js.es6 b/spec/javascripts/pipelines_spec.js.es6
index f0f9ad7430d..72770a702d3 100644
--- a/spec/javascripts/pipelines_spec.js.es6
+++ b/spec/javascripts/pipelines_spec.js.es6
@@ -1,4 +1,9 @@
-//= require pipelines
+require('~/pipelines');
+
+// Fix for phantomJS
+if (!Element.prototype.matches && Element.prototype.webkitMatchesSelector) {
+ Element.prototype.matches = Element.prototype.webkitMatchesSelector;
+}
(() => {
describe('Pipelines', () => {
diff --git a/spec/javascripts/pretty_time_spec.js.es6 b/spec/javascripts/pretty_time_spec.js.es6
index 7a04fba5f7f..a4662cfb557 100644
--- a/spec/javascripts/pretty_time_spec.js.es6
+++ b/spec/javascripts/pretty_time_spec.js.es6
@@ -1,4 +1,4 @@
-//= require lib/utils/pretty_time
+require('~/lib/utils/pretty_time');
(() => {
const prettyTime = gl.utils.prettyTime;
diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js
index e562385a6c6..e0b52f767e4 100644
--- a/spec/javascripts/project_title_spec.js
+++ b/spec/javascripts/project_title_spec.js
@@ -1,14 +1,12 @@
/* eslint-disable space-before-function-paren, no-unused-expressions, no-return-assign, no-param-reassign, no-var, new-cap, wrap-iife, no-unused-vars, quotes, jasmine/no-expect-in-setup-teardown, max-len */
-
/* global Project */
-/*= require bootstrap */
-/*= require select2 */
-/*= require lib/utils/type_utility */
-/*= require gl_dropdown */
-/*= require api */
-/*= require project_select */
-/*= require project */
+require('select2/select2.js');
+require('~/lib/utils/type_utility');
+require('~/gl_dropdown');
+require('~/api');
+require('~/project_select');
+require('~/project');
(function() {
window.gon || (window.gon = {});
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 3a01a534557..f7636865aa1 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -1,11 +1,8 @@
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, new-parens, no-return-assign, new-cap, vars-on-top, max-len */
/* global Sidebar */
-/*= require right_sidebar */
-/*= require jquery */
-/*= require js.cookie */
-
-/*= require extensions/jquery.js */
+require('~/right_sidebar');
+require('~/extensions/jquery.js');
(function() {
var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState;
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index 7ac9710654f..c79e30e9481 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -1,13 +1,10 @@
/* eslint-disable space-before-function-paren, max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, comma-dangle, object-shorthand, prefer-template, quotes, new-parens, vars-on-top, new-cap, max-len */
-/*= require gl_dropdown */
-/*= require search_autocomplete */
-/*= require jquery */
-/*= require lib/utils/common_utils */
-/*= require lib/utils/type_utility */
-/*= require fuzzaldrin-plus */
-/*= require turbolinks */
-/*= require jquery.turbolinks */
+require('~/gl_dropdown');
+require('~/search_autocomplete');
+require('~/lib/utils/common_utils');
+require('~/lib/utils/type_utility');
+require('vendor/fuzzaldrin-plus');
(function() {
var addBodyAttributes, assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget;
@@ -117,13 +114,15 @@
preloadFixtures('static/search_autocomplete.html.raw');
beforeEach(function() {
loadFixtures('static/search_autocomplete.html.raw');
- return widget = new gl.SearchAutocomplete;
+ widget = new gl.SearchAutocomplete;
+ // Prevent turbolinks from triggering within gl_dropdown
+ spyOn(window.gl.utils, 'visitUrl').and.returnValue(true);
});
it('should show Dashboard specific dropdown menu', function() {
var list;
addBodyAttributes();
mockDashboardOptions();
- widget.searchInput.focus();
+ widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, dashboardIssuesPath, dashboardMRsPath);
});
@@ -131,7 +130,7 @@
var list;
addBodyAttributes('group');
mockGroupOptions();
- widget.searchInput.focus();
+ widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, groupIssuesPath, groupMRsPath);
});
@@ -139,7 +138,7 @@
var list;
addBodyAttributes('project');
mockProjectOptions();
- widget.searchInput.focus();
+ widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
return assertLinks(list, projectIssuesPath, projectMRsPath);
});
@@ -148,7 +147,7 @@
addBodyAttributes('project');
mockProjectOptions();
widget.searchInput.val('help');
- widget.searchInput.focus();
+ widget.searchInput.triggerHandler('focus');
list = widget.wrap.find('.dropdown-menu').find('ul');
link = "a[href='" + projectIssuesPath + "/?assignee_id=" + userId + "']";
return expect(list.find(link).length).toBe(0);
@@ -159,7 +158,7 @@
addBodyAttributes();
mockDashboardOptions(true);
var submitSpy = spyOnEvent('form', 'submit');
- widget.searchInput.focus();
+ widget.searchInput.triggerHandler('focus');
widget.wrap.trigger($.Event('keydown', { which: DOWN }));
var enterKeyEvent = $.Event('keydown', { which: ENTER });
widget.searchInput.trigger(enterKeyEvent);
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index e0a5a7927bb..602ac01aec3 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -1,8 +1,8 @@
/* eslint-disable space-before-function-paren, no-return-assign, no-var, quotes */
/* global ShortcutsIssuable */
-/*= require copy_as_gfm */
-/*= require shortcuts_issuable */
+require('~/copy_as_gfm');
+require('~/shortcuts_issuable');
(function() {
describe('ShortcutsIssuable', function() {
diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js.es6 b/spec/javascripts/signin_tabs_memoizer_spec.js.es6
index c274b9c45f4..d83d9a57b42 100644
--- a/spec/javascripts/signin_tabs_memoizer_spec.js.es6
+++ b/spec/javascripts/signin_tabs_memoizer_spec.js.es6
@@ -1,4 +1,4 @@
-/*= require signin_tabs_memoizer */
+require('~/signin_tabs_memoizer');
((global) => {
describe('SigninTabsMemoizer', () => {
diff --git a/spec/javascripts/smart_interval_spec.js.es6 b/spec/javascripts/smart_interval_spec.js.es6
index 39d236986b9..4366ec2a5b8 100644
--- a/spec/javascripts/smart_interval_spec.js.es6
+++ b/spec/javascripts/smart_interval_spec.js.es6
@@ -1,5 +1,4 @@
-//= require jquery
-//= require smart_interval
+require('~/smart_interval');
(() => {
const DEFAULT_MAX_INTERVAL = 100;
@@ -164,7 +163,7 @@
const interval = this.smartInterval;
setTimeout(() => {
- $(document).trigger('page:before-unload');
+ $(document).triggerHandler('beforeunload');
expect(interval.state.intervalId).toBeUndefined();
expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval);
done();
diff --git a/spec/javascripts/spec_helper.js b/spec/javascripts/spec_helper.js
deleted file mode 100644
index f8e3aca29fa..00000000000
--- a/spec/javascripts/spec_helper.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/* eslint-disable space-before-function-paren */
-// PhantomJS (Teaspoons default driver) doesn't have support for
-// Function.prototype.bind, which has caused confusion. Use this polyfill to
-// avoid the confusion.
-/*= require support/bind-poly */
-
-// You can require your own javascript files here. By default this will include
-// everything in application, however you may get better load performance if you
-// require the specific files that are being used in the spec that tests them.
-/*= require jquery */
-/*= require jquery.turbolinks */
-/*= require bootstrap */
-/*= require underscore */
-
-// Teaspoon includes some support files, but you can use anything from your own
-// support path too.
-// require support/jasmine-jquery-1.7.0
-// require support/jasmine-jquery-2.0.0
-/*= require support/jasmine-jquery-2.1.0 */
-
-// require support/sinon
-// require support/your-support-file
-// Deferring execution
-// If you're using CommonJS, RequireJS or some other asynchronous library you can
-// defer execution. Call Teaspoon.execute() after everything has been loaded.
-// Simple example of a timeout:
-// Teaspoon.defer = true
-// setTimeout(Teaspoon.execute, 1000)
-// Matching files
-// By default Teaspoon will look for files that match
-// _spec.{js,js.es6}. Add a filename_spec.js file in your spec path
-// and it'll be included in the default suite automatically. If you want to
-// customize suites, check out the configuration in teaspoon_env.rb
-// Manifest
-// If you'd rather require your spec files manually (to control order for
-// instance) you can disable the suite matcher in the configuration and use this
-// file as a manifest.
-// For more information: http://github.com/modeset/teaspoon
-
-// set our fixtures path
-jasmine.getFixtures().fixturesPath = '/teaspoon/fixtures';
-jasmine.getJSONFixtures().fixturesPath = '/teaspoon/fixtures';
-
-// defined in ActionDispatch::TestRequest
-// see https://github.com/rails/rails/blob/v4.2.7.1/actionpack/lib/action_dispatch/testing/test_request.rb#L7
-window.gl = window.gl || {};
-window.gl.TEST_HOST = 'http://test.host';
-window.gon = window.gon || {};
diff --git a/spec/javascripts/subbable_resource_spec.js.es6 b/spec/javascripts/subbable_resource_spec.js.es6
index 99f45850ea3..454386697f5 100644
--- a/spec/javascripts/subbable_resource_spec.js.es6
+++ b/spec/javascripts/subbable_resource_spec.js.es6
@@ -1,9 +1,6 @@
/* eslint-disable max-len, arrow-parens, comma-dangle */
-//= vue
-//= vue-resource
-//= require jquery
-//= require subbable_resource
+require('~/subbable_resource');
/*
* Test that each rest verb calls the publish and subscribe function and passes the correct value back
diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js
index 436f7064a69..c0c3837d1f4 100644
--- a/spec/javascripts/syntax_highlight_spec.js
+++ b/spec/javascripts/syntax_highlight_spec.js
@@ -1,6 +1,6 @@
/* eslint-disable space-before-function-paren, no-var, no-return-assign, quotes */
-/*= require syntax_highlight */
+require('~/syntax_highlight');
(function() {
describe('Syntax Highlighter', function() {
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
new file mode 100644
index 00000000000..bf11ddbbea8
--- /dev/null
+++ b/spec/javascripts/test_bundle.js
@@ -0,0 +1,40 @@
+// enable test fixtures
+require('jasmine-jquery');
+
+jasmine.getFixtures().fixturesPath = 'base/spec/javascripts/fixtures';
+jasmine.getJSONFixtures().fixturesPath = 'base/spec/javascripts/fixtures';
+
+// include common libraries
+window.$ = window.jQuery = require('jquery');
+window._ = require('underscore');
+window.Cookies = require('vendor/js.cookie');
+window.Vue = require('vue');
+window.Vue.use(require('vue-resource'));
+require('jquery-ujs');
+require('bootstrap/js/affix');
+require('bootstrap/js/alert');
+require('bootstrap/js/button');
+require('bootstrap/js/collapse');
+require('bootstrap/js/dropdown');
+require('bootstrap/js/modal');
+require('bootstrap/js/scrollspy');
+require('bootstrap/js/tab');
+require('bootstrap/js/transition');
+require('bootstrap/js/tooltip');
+require('bootstrap/js/popover');
+
+// stub expected globals
+window.gl = window.gl || {};
+window.gl.TEST_HOST = 'http://test.host';
+window.gon = window.gon || {};
+
+// render all of our tests
+const testsContext = require.context('.', true, /_spec$/);
+testsContext.keys().forEach(function (path) {
+ try {
+ testsContext(path);
+ } catch (err) {
+ console.error('[ERROR] WITH SPEC FILE: ', path);
+ console.error(err);
+ }
+});
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index 0e2fb07ba7f..cba1af4daa4 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -2,11 +2,11 @@
/* global MockU2FDevice */
/* global U2FAuthenticate */
-/*= require u2f/authenticate */
-/*= require u2f/util */
-/*= require u2f/error */
-/*= require u2f */
-/*= require ./mock_u2f_device */
+require('~/u2f/authenticate');
+require('~/u2f/util');
+require('~/u2f/error');
+require('vendor/u2f');
+require('./mock_u2f_device');
(function() {
describe('U2FAuthenticate', function() {
diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js
index 0790553b67e..10578c2c4b5 100644
--- a/spec/javascripts/u2f/register_spec.js
+++ b/spec/javascripts/u2f/register_spec.js
@@ -2,11 +2,11 @@
/* global MockU2FDevice */
/* global U2FRegister */
-/*= require u2f/register */
-/*= require u2f/util */
-/*= require u2f/error */
-/*= require u2f */
-/*= require ./mock_u2f_device */
+require('~/u2f/register');
+require('~/u2f/util');
+require('~/u2f/error');
+require('vendor/u2f');
+require('./mock_u2f_device');
(function() {
describe('U2FRegister', function() {
diff --git a/spec/javascripts/visibility_select_spec.js.es6 b/spec/javascripts/visibility_select_spec.js.es6
index b21f6912e06..9727c03c91e 100644
--- a/spec/javascripts/visibility_select_spec.js.es6
+++ b/spec/javascripts/visibility_select_spec.js.es6
@@ -1,4 +1,4 @@
-/*= require visibility_select */
+require('~/visibility_select');
(() => {
const VisibilitySelect = gl.VisibilitySelect;
diff --git a/spec/javascripts/vue_common_components/commit_spec.js.es6 b/spec/javascripts/vue_common_components/commit_spec.js.es6
index d6c6f786fb1..bbd914de4ea 100644
--- a/spec/javascripts/vue_common_components/commit_spec.js.es6
+++ b/spec/javascripts/vue_common_components/commit_spec.js.es6
@@ -1,4 +1,4 @@
-//= require vue_common_component/commit
+require('~/vue_common_component/commit');
describe('Commit component', () => {
let props;
diff --git a/spec/javascripts/vue_pagination/pagination_spec.js.es6 b/spec/javascripts/vue_pagination/pagination_spec.js.es6
index efb11211ce2..8935c474ee5 100644
--- a/spec/javascripts/vue_pagination/pagination_spec.js.es6
+++ b/spec/javascripts/vue_pagination/pagination_spec.js.es6
@@ -1,6 +1,5 @@
-//= require vue
-//= require lib/utils/common_utils
-//= require vue_pagination/index
+require('~/lib/utils/common_utils');
+require('~/vue_pagination/index');
describe('Pagination component', () => {
let component;
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index be706ca304f..ce33a6814aa 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -3,7 +3,7 @@
/* global Mousetrap */
/* global ZenMode */
-/*= require zen_mode */
+require('~/zen_mode');
(function() {
var enterZen, escapeKeydown, exitZen;
diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb
index e3066311b7d..d70690f589d 100644
--- a/spec/lib/event_filter_spec.rb
+++ b/spec/lib/event_filter_spec.rb
@@ -5,15 +5,15 @@ describe EventFilter, lib: true do
let(:source_user) { create(:user) }
let!(:public_project) { create(:empty_project, :public) }
- let!(:push_event) { create(:event, action: Event::PUSHED, project: public_project, target: public_project, author: source_user) }
- let!(:merged_event) { create(:event, action: Event::MERGED, project: public_project, target: public_project, author: source_user) }
- let!(:created_event) { create(:event, action: Event::CREATED, project: public_project, target: public_project, author: source_user) }
- let!(:updated_event) { create(:event, action: Event::UPDATED, project: public_project, target: public_project, author: source_user) }
- let!(:closed_event) { create(:event, action: Event::CLOSED, project: public_project, target: public_project, author: source_user) }
- let!(:reopened_event) { create(:event, action: Event::REOPENED, project: public_project, target: public_project, author: source_user) }
- let!(:comments_event) { create(:event, action: Event::COMMENTED, project: public_project, target: public_project, author: source_user) }
- let!(:joined_event) { create(:event, action: Event::JOINED, project: public_project, target: public_project, author: source_user) }
- let!(:left_event) { create(:event, action: Event::LEFT, project: public_project, target: public_project, author: source_user) }
+ let!(:push_event) { create(:event, :pushed, project: public_project, target: public_project, author: source_user) }
+ let!(:merged_event) { create(:event, :merged, project: public_project, target: public_project, author: source_user) }
+ let!(:created_event) { create(:event, :created, project: public_project, target: public_project, author: source_user) }
+ let!(:updated_event) { create(:event, :updated, project: public_project, target: public_project, author: source_user) }
+ let!(:closed_event) { create(:event, :closed, project: public_project, target: public_project, author: source_user) }
+ let!(:reopened_event) { create(:event, :reopened, project: public_project, target: public_project, author: source_user) }
+ let!(:comments_event) { create(:event, :commented, project: public_project, target: public_project, author: source_user) }
+ let!(:joined_event) { create(:event, :joined, project: public_project, target: public_project, author: source_user) }
+ let!(:left_event) { create(:event, :left, project: public_project, target: public_project, author: source_user) }
it 'applies push filter' do
events = EventFilter.new(EventFilter.push).apply_filter(Event.all)
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index d480c3821ec..1d65b24c2c9 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -182,7 +182,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
project: project,
commit_id: ci_pipeline.sha)
- create(:event, target: milestone, project: project, action: Event::CREATED, author: user)
+ create(:event, :created, target: milestone, project: project, author: user)
create(:service, project: project, type: 'CustomIssueTrackerService', category: 'issue_tracker')
project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 7dd4d76d1a3..a32c6131030 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -42,7 +42,8 @@ describe Gitlab::Workhorse, lib: true do
out = {
subprotocols: ['foo'],
url: 'wss://example.com/terminal.ws',
- headers: { 'Authorization' => ['Token x'] }
+ headers: { 'Authorization' => ['Token x'] },
+ max_session_time: 600
}
out[:ca_pem] = ca_pem if ca_pem
out
@@ -53,7 +54,8 @@ describe Gitlab::Workhorse, lib: true do
'Terminal' => {
'Subprotocols' => ['foo'],
'Url' => 'wss://example.com/terminal.ws',
- 'Header' => { 'Authorization' => ['Token x'] }
+ 'Header' => { 'Authorization' => ['Token x'] },
+ 'MaxSessionTime' => 600
}
}
out['Terminal']['CAPem'] = ca_pem if ca_pem
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 349474bb656..8c90a538f57 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -19,7 +19,7 @@ describe Event, models: true do
let(:project) { create(:empty_project) }
it 'calls the reset_project_activity method' do
- expect_any_instance_of(Event).to receive(:reset_project_activity)
+ expect_any_instance_of(described_class).to receive(:reset_project_activity)
create_event(project, project.owner)
end
@@ -43,33 +43,33 @@ describe Event, models: true do
describe '#membership_changed?' do
context "created" do
- subject { build(:event, action: Event::CREATED).membership_changed? }
+ subject { build(:event, :created).membership_changed? }
it { is_expected.to be_falsey }
end
context "updated" do
- subject { build(:event, action: Event::UPDATED).membership_changed? }
+ subject { build(:event, :updated).membership_changed? }
it { is_expected.to be_falsey }
end
context "expired" do
- subject { build(:event, action: Event::EXPIRED).membership_changed? }
+ subject { build(:event, :expired).membership_changed? }
it { is_expected.to be_truthy }
end
context "left" do
- subject { build(:event, action: Event::LEFT).membership_changed? }
+ subject { build(:event, :left).membership_changed? }
it { is_expected.to be_truthy }
end
context "joined" do
- subject { build(:event, action: Event::JOINED).membership_changed? }
+ subject { build(:event, :joined).membership_changed? }
it { is_expected.to be_truthy }
end
end
describe '#note?' do
- subject { Event.new(project: target.project, target: target) }
+ subject { described_class.new(project: target.project, target: target) }
context 'issue note event' do
let(:target) { create(:note_on_issue) }
@@ -97,7 +97,7 @@ describe Event, models: true do
let(:note_on_commit) { create(:note_on_commit, project: project) }
let(:note_on_issue) { create(:note_on_issue, noteable: issue, project: project) }
let(:note_on_confidential_issue) { create(:note_on_issue, noteable: confidential_issue, project: project) }
- let(:event) { Event.new(project: project, target: target, author_id: author.id) }
+ let(:event) { described_class.new(project: project, target: target, author_id: author.id) }
before do
project.team << [member, :developer]
@@ -221,13 +221,13 @@ describe Event, models: true do
let!(:event2) { create(:closed_issue_event) }
describe 'without an explicit limit' do
- subject { Event.limit_recent }
+ subject { described_class.limit_recent }
it { is_expected.to eq([event2, event1]) }
end
describe 'with an explicit limit' do
- subject { Event.limit_recent(1) }
+ subject { described_class.limit_recent(1) }
it { is_expected.to eq([event2]) }
end
@@ -294,9 +294,9 @@ describe Event, models: true do
}
}
- Event.create({
+ described_class.create({
project: project,
- action: Event::PUSHED,
+ action: described_class::PUSHED,
data: data,
author_id: user.id
}.merge!(attrs))
diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb
index 9e1a52011c3..e6ca4853873 100644
--- a/spec/models/list_spec.rb
+++ b/spec/models/list_spec.rb
@@ -19,13 +19,6 @@ describe List do
expect(subject).to validate_uniqueness_of(:label_id).scoped_to(:board_id)
end
- context 'when list_type is set to backlog' do
- subject { described_class.new(list_type: :backlog) }
-
- it { is_expected.not_to validate_presence_of(:label) }
- it { is_expected.not_to validate_presence_of(:position) }
- end
-
context 'when list_type is set to done' do
subject { described_class.new(list_type: :done) }
@@ -41,12 +34,6 @@ describe List do
expect(subject.destroy).to be_truthy
end
- it 'can not be destroyed when list_type is set to backlog' do
- subject = create(:backlog_list)
-
- expect(subject.destroy).to be_falsey
- end
-
it 'can not be destroyed when when list_type is set to done' do
subject = create(:done_list)
@@ -55,19 +42,13 @@ describe List do
end
describe '#destroyable?' do
- it 'retruns true when list_type is set to label' do
+ it 'returns true when list_type is set to label' do
subject.list_type = :label
expect(subject).to be_destroyable
end
- it 'retruns false when list_type is set to backlog' do
- subject.list_type = :backlog
-
- expect(subject).not_to be_destroyable
- end
-
- it 'retruns false when list_type is set to done' do
+ it 'returns false when list_type is set to done' do
subject.list_type = :done
expect(subject).not_to be_destroyable
@@ -75,19 +56,13 @@ describe List do
end
describe '#movable?' do
- it 'retruns true when list_type is set to label' do
+ it 'returns true when list_type is set to label' do
subject.list_type = :label
expect(subject).to be_movable
end
- it 'retruns false when list_type is set to backlog' do
- subject.list_type = :backlog
-
- expect(subject).not_to be_movable
- end
-
- it 'retruns false when list_type is set to done' do
+ it 'returns false when list_type is set to done' do
subject.list_type = :done
expect(subject).not_to be_movable
@@ -102,12 +77,6 @@ describe List do
expect(subject.title).to eq 'Development'
end
- it 'returns Backlog when list_type is set to backlog' do
- subject.list_type = :backlog
-
- expect(subject.title).to eq 'Backlog'
- end
-
it 'returns Done when list_type is set to done' do
subject.list_type = :done
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index 4f3cd14e941..9052479d35e 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -181,11 +181,23 @@ describe KubernetesService, models: true, caching: true do
let(:pod) { kube_pod(app: environment.slug) }
let(:terminals) { kube_terminals(service, pod) }
- it 'returns terminals' do
- stub_reactive_cache(service, pods: [ pod, pod, kube_pod(app: "should-be-filtered-out") ])
+ before do
+ stub_reactive_cache(
+ service,
+ pods: [ pod, pod, kube_pod(app: "should-be-filtered-out") ]
+ )
+ end
+ it 'returns terminals' do
is_expected.to eq(terminals + terminals)
end
+
+ it 'uses max session time from settings' do
+ stub_application_setting(terminal_max_session_time: 600)
+
+ times = subject.map { |terminal| terminal[:max_session_time] }
+ expect(times).to eq [600, 600, 600, 600]
+ end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 6ca5ad747d1..6d58b1455c4 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1013,8 +1013,8 @@ describe User, models: true do
let!(:project2) { create(:empty_project, forked_from_project: project3) }
let!(:project3) { create(:empty_project) }
let!(:merge_request) { create(:merge_request, source_project: project2, target_project: project3, author: subject) }
- let!(:push_event) { create(:event, action: Event::PUSHED, project: project1, target: project1, author: subject) }
- let!(:merge_event) { create(:event, action: Event::CREATED, project: project3, target: merge_request, author: subject) }
+ let!(:push_event) { create(:event, :pushed, project: project1, target: project1, author: subject) }
+ let!(:merge_event) { create(:event, :created, project: project3, target: merge_request, author: subject) }
before do
project1.team << [subject, :master]
@@ -1058,7 +1058,7 @@ describe User, models: true do
let!(:push_data) do
Gitlab::DataBuilder::Push.build_sample(project2, subject)
end
- let!(:push_event) { create(:event, action: Event::PUSHED, project: project2, target: project1, author: subject, data: push_data) }
+ let!(:push_event) { create(:event, :pushed, project: project2, target: project1, author: subject, data: push_data) }
before do
project1.team << [subject, :master]
@@ -1086,7 +1086,7 @@ describe User, models: true do
expect(subject.recent_push(project2)).to eq(push_event)
push_data1 = Gitlab::DataBuilder::Push.build_sample(project1, subject)
- push_event1 = create(:event, action: Event::PUSHED, project: project1, target: project1, author: subject, data: push_data1)
+ push_event1 = create(:event, :pushed, project: project1, target: project1, author: subject, data: push_data1)
expect(subject.recent_push([project1, project2])).to eq(push_event1) # Newest
end
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
index f197fadebab..834c4e52693 100644
--- a/spec/requests/api/builds_spec.rb
+++ b/spec/requests/api/builds_spec.rb
@@ -188,6 +188,7 @@ describe API::Builds, api: true do
it 'returns specific job artifacts' do
expect(response).to have_http_status(200)
expect(response.headers).to include(download_headers)
+ expect(response.body).to match_file(build.artifacts_file.file.file)
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 753dde0ca3a..225e2e005df 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1085,52 +1085,6 @@ describe API::Projects, api: true do
end
end
- describe 'GET /projects/search/:query' do
- let!(:query) { 'query'}
- let!(:search) { create(:empty_project, name: query, creator_id: user.id, namespace: user.namespace) }
- let!(:pre) { create(:empty_project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
- let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
- let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
- let!(:unfound) { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
- let!(:internal) { create(:empty_project, :internal, name: "internal #{query}") }
- let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') }
- let!(:public) { create(:empty_project, :public, name: "public #{query}") }
- let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
- let!(:one_dot_two) { create(:empty_project, :public, name: "one.dot.two") }
-
- shared_examples_for 'project search response' do |args = {}|
- it 'returns project search responses' do
- get api("/projects/search/#{args[:query]}", current_user)
-
- expect(response).to have_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.size).to eq(args[:results])
- json_response.each { |project| expect(project['name']).to match(args[:match_regex] || /.*#{args[:query]}.*/) }
- end
- end
-
- context 'when unauthenticated' do
- it_behaves_like 'project search response', query: 'query', results: 1 do
- let(:current_user) { nil }
- end
- end
-
- context 'when authenticated' do
- it_behaves_like 'project search response', query: 'query', results: 6 do
- let(:current_user) { user }
- end
- it_behaves_like 'project search response', query: 'one.dot.two', results: 1 do
- let(:current_user) { user }
- end
- end
-
- context 'when authenticated as a different user' do
- it_behaves_like 'project search response', query: 'query', results: 2, match_regex: /(internal|public) query/ do
- let(:current_user) { user2 }
- end
- end
- end
-
describe 'PUT /projects/:id' do
before { project }
before { user }
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
new file mode 100644
index 00000000000..a495122bba7
--- /dev/null
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -0,0 +1,1424 @@
+require 'spec_helper'
+
+describe API::V3::Projects, api: true do
+ include ApiHelpers
+ include Gitlab::CurrentSettings
+
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:user3) { create(:user) }
+ let(:admin) { create(:admin) }
+ let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
+ let(:project2) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
+ let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
+ let(:project_member) { create(:project_member, :master, user: user, project: project) }
+ let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
+ let(:user4) { create(:user) }
+ let(:project3) do
+ create(:project,
+ :private,
+ :repository,
+ name: 'second_project',
+ path: 'second_project',
+ creator_id: user.id,
+ namespace: user.namespace,
+ merge_requests_enabled: false,
+ issues_enabled: false, wiki_enabled: false,
+ snippets_enabled: false)
+ end
+ let(:project_member3) do
+ create(:project_member,
+ user: user4,
+ project: project3,
+ access_level: ProjectMember::MASTER)
+ end
+ let(:project4) do
+ create(:empty_project,
+ name: 'third_project',
+ path: 'third_project',
+ creator_id: user4.id,
+ namespace: user4.namespace)
+ end
+
+ describe 'GET /projects' do
+ before { project }
+
+ context 'when unauthenticated' do
+ it 'returns authentication error' do
+ get v3_api('/projects')
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'when authenticated as regular user' do
+ it 'returns an array of projects' do
+ get v3_api('/projects', user)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['name']).to eq(project.name)
+ expect(json_response.first['owner']['username']).to eq(user.username)
+ end
+
+ it 'includes the project labels as the tag_list' do
+ get v3_api('/projects', user)
+ expect(response.status).to eq 200
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).to include('tag_list')
+ end
+
+ it 'includes open_issues_count' do
+ get v3_api('/projects', user)
+ expect(response.status).to eq 200
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).to include('open_issues_count')
+ end
+
+ it 'does not include open_issues_count' do
+ project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED)
+
+ get v3_api('/projects', user)
+ expect(response.status).to eq 200
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).not_to include('open_issues_count')
+ end
+
+ context 'GET /projects?simple=true' do
+ it 'returns a simplified version of all the projects' do
+ expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"]
+
+ get v3_api('/projects?simple=true', user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first.keys).to match_array expected_keys
+ end
+ end
+
+ context 'and using search' do
+ it 'returns searched project' do
+ get v3_api('/projects', user), { search: project.name }
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ end
+ end
+
+ context 'and using the visibility filter' do
+ it 'filters based on private visibility param' do
+ get v3_api('/projects', user), { visibility: 'private' }
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count)
+ end
+
+ it 'filters based on internal visibility param' do
+ get v3_api('/projects', user), { visibility: 'internal' }
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count)
+ end
+
+ it 'filters based on public visibility param' do
+ get v3_api('/projects', user), { visibility: 'public' }
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).count)
+ end
+ end
+
+ context 'and using sorting' do
+ before do
+ project2
+ project3
+ end
+
+ it 'returns the correct order when sorted by id' do
+ get v3_api('/projects', user), { order_by: 'id', sort: 'desc' }
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['id']).to eq(project3.id)
+ end
+ end
+ end
+ end
+
+ describe 'GET /projects/all' do
+ before { project }
+
+ context 'when unauthenticated' do
+ it 'returns authentication error' do
+ get v3_api('/projects/all')
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'when authenticated as regular user' do
+ it 'returns authentication error' do
+ get v3_api('/projects/all', user)
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'when authenticated as admin' do
+ it 'returns an array of all projects' do
+ get v3_api('/projects/all', admin)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+
+ expect(json_response).to satisfy do |response|
+ response.one? do |entry|
+ entry.has_key?('permissions') &&
+ entry['name'] == project.name &&
+ entry['owner']['username'] == user.username
+ end
+ end
+ end
+
+ it "does not include statistics by default" do
+ get v3_api('/projects/all', admin)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('statistics')
+ end
+
+ it "includes statistics if requested" do
+ get v3_api('/projects/all', admin), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).to include 'statistics'
+ end
+ end
+ end
+
+ describe 'GET /projects/owned' do
+ before do
+ project3
+ project4
+ end
+
+ context 'when unauthenticated' do
+ it 'returns authentication error' do
+ get v3_api('/projects/owned')
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'when authenticated as project owner' do
+ it 'returns an array of projects the user owns' do
+ get v3_api('/projects/owned', user4)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['name']).to eq(project4.name)
+ expect(json_response.first['owner']['username']).to eq(user4.username)
+ end
+
+ it "does not include statistics by default" do
+ get v3_api('/projects/owned', user4)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first).not_to include('statistics')
+ end
+
+ it "includes statistics if requested" do
+ attributes = {
+ commit_count: 23,
+ storage_size: 702,
+ repository_size: 123,
+ lfs_objects_size: 234,
+ build_artifacts_size: 345,
+ }
+
+ project4.statistics.update!(attributes)
+
+ get v3_api('/projects/owned', user4), statistics: true
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['statistics']).to eq attributes.stringify_keys
+ end
+ end
+ end
+
+ describe 'GET /projects/visible' do
+ shared_examples_for 'visible projects response' do
+ it 'returns the visible projects' do
+ get v3_api('/projects/visible', current_user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.map { |p| p['id'] }).to contain_exactly(*projects.map(&:id))
+ end
+ end
+
+ let!(:public_project) { create(:empty_project, :public) }
+ before do
+ project
+ project2
+ project3
+ project4
+ end
+
+ context 'when unauthenticated' do
+ it_behaves_like 'visible projects response' do
+ let(:current_user) { nil }
+ let(:projects) { [public_project] }
+ end
+ end
+
+ context 'when authenticated' do
+ it_behaves_like 'visible projects response' do
+ let(:current_user) { user }
+ let(:projects) { [public_project, project, project2, project3] }
+ end
+ end
+
+ context 'when authenticated as a different user' do
+ it_behaves_like 'visible projects response' do
+ let(:current_user) { user2 }
+ let(:projects) { [public_project] }
+ end
+ end
+ end
+
+ describe 'GET /projects/starred' do
+ let(:public_project) { create(:empty_project, :public) }
+
+ before do
+ project_member2
+ user3.update_attributes(starred_projects: [project, project2, project3, public_project])
+ end
+
+ it 'returns the starred projects viewable by the user' do
+ get v3_api('/projects/starred', user3)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id)
+ end
+ end
+
+ describe 'POST /projects' do
+ context 'maximum number of projects reached' do
+ it 'does not create new project and respond with 403' do
+ allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
+ expect { post v3_api('/projects', user2), name: 'foo' }.
+ to change {Project.count}.by(0)
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ it 'creates new project without path and return 201' do
+ expect { post v3_api('/projects', user), name: 'foo' }.
+ to change { Project.count }.by(1)
+ expect(response).to have_http_status(201)
+ end
+
+ it 'creates last project before reaching project limit' do
+ allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1)
+ post v3_api('/projects', user2), name: 'foo'
+ expect(response).to have_http_status(201)
+ end
+
+ it 'does not create new project without name and return 400' do
+ expect { post v3_api('/projects', user) }.not_to change { Project.count }
+ expect(response).to have_http_status(400)
+ end
+
+ it "assigns attributes to project" do
+ project = attributes_for(:project, {
+ path: 'camelCasePath',
+ description: FFaker::Lorem.sentence,
+ issues_enabled: false,
+ merge_requests_enabled: false,
+ wiki_enabled: false,
+ only_allow_merge_if_build_succeeds: false,
+ request_access_enabled: true,
+ only_allow_merge_if_all_discussions_are_resolved: false
+ })
+
+ post v3_api('/projects', user), project
+
+ project.each_pair do |k, v|
+ next if %i[has_external_issue_tracker issues_enabled merge_requests_enabled wiki_enabled].include?(k)
+ expect(json_response[k.to_s]).to eq(v)
+ end
+
+ # Check feature permissions attributes
+ project = Project.find_by_path(project[:path])
+ expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::DISABLED)
+ end
+
+ it 'sets a project as public' do
+ project = attributes_for(:project, :public)
+ post v3_api('/projects', user), project
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'sets a project as public using :public' do
+ project = attributes_for(:project, { public: true })
+ post v3_api('/projects', user), project
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'sets a project as internal' do
+ project = attributes_for(:project, :internal)
+ post v3_api('/projects', user), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ it 'sets a project as internal overriding :public' do
+ project = attributes_for(:project, :internal, { public: true })
+ post v3_api('/projects', user), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ it 'sets a project as private' do
+ project = attributes_for(:project, :private)
+ post v3_api('/projects', user), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'sets a project as private using :public' do
+ project = attributes_for(:project, { public: false })
+ post v3_api('/projects', user), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'sets a project as allowing merge even if build fails' do
+ project = attributes_for(:project, { only_allow_merge_if_build_succeeds: false })
+ post v3_api('/projects', user), project
+ expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
+ end
+
+ it 'sets a project as allowing merge only if build succeeds' do
+ project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
+ post v3_api('/projects', user), project
+ expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
+ end
+
+ it 'sets a project as allowing merge even if discussions are unresolved' do
+ project = attributes_for(:project, { only_allow_merge_if_all_discussions_are_resolved: false })
+
+ post v3_api('/projects', user), project
+
+ expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_falsey
+ end
+
+ it 'sets a project as allowing merge if only_allow_merge_if_all_discussions_are_resolved is nil' do
+ project = attributes_for(:project, only_allow_merge_if_all_discussions_are_resolved: nil)
+
+ post v3_api('/projects', user), project
+
+ expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_falsey
+ end
+
+ it 'sets a project as allowing merge only if all discussions are resolved' do
+ project = attributes_for(:project, { only_allow_merge_if_all_discussions_are_resolved: true })
+
+ post v3_api('/projects', user), project
+
+ expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_truthy
+ end
+
+ context 'when a visibility level is restricted' do
+ before do
+ @project = attributes_for(:project, { public: true })
+ stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
+ end
+
+ it 'does not allow a non-admin to use a restricted visibility level' do
+ post v3_api('/projects', user), @project
+
+ expect(response).to have_http_status(400)
+ expect(json_response['message']['visibility_level'].first).to(
+ match('restricted by your GitLab administrator')
+ )
+ end
+
+ it 'allows an admin to override restricted visibility settings' do
+ post v3_api('/projects', admin), @project
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to(
+ eq(Gitlab::VisibilityLevel::PUBLIC)
+ )
+ end
+ end
+ end
+
+ describe 'POST /projects/user/:id' do
+ before { project }
+ before { admin }
+
+ it 'should create new project without path and return 201' do
+ expect { post v3_api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1)
+ expect(response).to have_http_status(201)
+ end
+
+ it 'responds with 400 on failure and not project' do
+ expect { post v3_api("/projects/user/#{user.id}", admin) }.
+ not_to change { Project.count }
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq('name is missing')
+ end
+
+ it 'assigns attributes to project' do
+ project = attributes_for(:project, {
+ description: FFaker::Lorem.sentence,
+ issues_enabled: false,
+ merge_requests_enabled: false,
+ wiki_enabled: false,
+ request_access_enabled: true
+ })
+
+ post v3_api("/projects/user/#{user.id}", admin), project
+
+ expect(response).to have_http_status(201)
+ project.each_pair do |k, v|
+ next if %i[has_external_issue_tracker path].include?(k)
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'sets a project as public' do
+ project = attributes_for(:project, :public)
+ post v3_api("/projects/user/#{user.id}", admin), project
+
+ expect(response).to have_http_status(201)
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'sets a project as public using :public' do
+ project = attributes_for(:project, { public: true })
+ post v3_api("/projects/user/#{user.id}", admin), project
+
+ expect(response).to have_http_status(201)
+ expect(json_response['public']).to be_truthy
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'sets a project as internal' do
+ project = attributes_for(:project, :internal)
+ post v3_api("/projects/user/#{user.id}", admin), project
+
+ expect(response).to have_http_status(201)
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ it 'sets a project as internal overriding :public' do
+ project = attributes_for(:project, :internal, { public: true })
+ post v3_api("/projects/user/#{user.id}", admin), project
+ expect(response).to have_http_status(201)
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ it 'sets a project as private' do
+ project = attributes_for(:project, :private)
+ post v3_api("/projects/user/#{user.id}", admin), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'sets a project as private using :public' do
+ project = attributes_for(:project, { public: false })
+ post v3_api("/projects/user/#{user.id}", admin), project
+ expect(json_response['public']).to be_falsey
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'sets a project as allowing merge even if build fails' do
+ project = attributes_for(:project, { only_allow_merge_if_build_succeeds: false })
+ post v3_api("/projects/user/#{user.id}", admin), project
+ expect(json_response['only_allow_merge_if_build_succeeds']).to be_falsey
+ end
+
+ it 'sets a project as allowing merge only if build succeeds' do
+ project = attributes_for(:project, { only_allow_merge_if_build_succeeds: true })
+ post v3_api("/projects/user/#{user.id}", admin), project
+ expect(json_response['only_allow_merge_if_build_succeeds']).to be_truthy
+ end
+
+ it 'sets a project as allowing merge even if discussions are unresolved' do
+ project = attributes_for(:project, { only_allow_merge_if_all_discussions_are_resolved: false })
+
+ post v3_api("/projects/user/#{user.id}", admin), project
+
+ expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_falsey
+ end
+
+ it 'sets a project as allowing merge only if all discussions are resolved' do
+ project = attributes_for(:project, { only_allow_merge_if_all_discussions_are_resolved: true })
+
+ post v3_api("/projects/user/#{user.id}", admin), project
+
+ expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_truthy
+ end
+ end
+
+ describe "POST /projects/:id/uploads" do
+ before { project }
+
+ it "uploads the file and returns its info" do
+ post v3_api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
+
+ expect(response).to have_http_status(201)
+ expect(json_response['alt']).to eq("dk")
+ expect(json_response['url']).to start_with("/uploads/")
+ expect(json_response['url']).to end_with("/dk.png")
+ end
+ end
+
+ describe 'GET /projects/:id' do
+ context 'when unauthenticated' do
+ it 'returns the public projects' do
+ public_project = create(:empty_project, :public)
+
+ get v3_api("/projects/#{public_project.id}")
+
+ expect(response).to have_http_status(200)
+ expect(json_response['id']).to eq(public_project.id)
+ expect(json_response['description']).to eq(public_project.description)
+ expect(json_response.keys).not_to include('permissions')
+ end
+ end
+
+ context 'when authenticated' do
+ before do
+ project
+ project_member
+ end
+
+ it 'returns a project by id' do
+ group = create(:group)
+ link = create(:project_group_link, project: project, group: group)
+
+ get v3_api("/projects/#{project.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['id']).to eq(project.id)
+ expect(json_response['description']).to eq(project.description)
+ expect(json_response['default_branch']).to eq(project.default_branch)
+ expect(json_response['tag_list']).to be_an Array
+ expect(json_response['public']).to be_falsey
+ expect(json_response['archived']).to be_falsey
+ expect(json_response['visibility_level']).to be_present
+ expect(json_response['ssh_url_to_repo']).to be_present
+ expect(json_response['http_url_to_repo']).to be_present
+ expect(json_response['web_url']).to be_present
+ expect(json_response['owner']).to be_a Hash
+ expect(json_response['owner']).to be_a Hash
+ expect(json_response['name']).to eq(project.name)
+ expect(json_response['path']).to be_present
+ expect(json_response['issues_enabled']).to be_present
+ expect(json_response['merge_requests_enabled']).to be_present
+ expect(json_response['wiki_enabled']).to be_present
+ expect(json_response['builds_enabled']).to be_present
+ expect(json_response['snippets_enabled']).to be_present
+ expect(json_response['container_registry_enabled']).to be_present
+ expect(json_response['created_at']).to be_present
+ expect(json_response['last_activity_at']).to be_present
+ expect(json_response['shared_runners_enabled']).to be_present
+ expect(json_response['creator_id']).to be_present
+ expect(json_response['namespace']).to be_present
+ expect(json_response['avatar_url']).to be_nil
+ expect(json_response['star_count']).to be_present
+ expect(json_response['forks_count']).to be_present
+ expect(json_response['public_builds']).to be_present
+ expect(json_response['shared_with_groups']).to be_an Array
+ expect(json_response['shared_with_groups'].length).to eq(1)
+ expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
+ expect(json_response['shared_with_groups'][0]['group_name']).to eq(group.name)
+ expect(json_response['shared_with_groups'][0]['group_access_level']).to eq(link.group_access)
+ expect(json_response['only_allow_merge_if_build_succeeds']).to eq(project.only_allow_merge_if_build_succeeds)
+ expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to eq(project.only_allow_merge_if_all_discussions_are_resolved)
+ end
+
+ it 'returns a project by path name' do
+ get v3_api("/projects/#{project.id}", user)
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(project.name)
+ end
+
+ it 'returns a 404 error if not found' do
+ get v3_api('/projects/42', user)
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
+
+ it 'returns a 404 error if user is not a member' do
+ other_user = create(:user)
+ get v3_api("/projects/#{project.id}", other_user)
+ expect(response).to have_http_status(404)
+ end
+
+ it 'handles users with dots' do
+ dot_user = create(:user, username: 'dot.user')
+ project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace)
+
+ get v3_api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
+ expect(response).to have_http_status(200)
+ expect(json_response['name']).to eq(project.name)
+ end
+
+ it 'exposes namespace fields' do
+ get v3_api("/projects/#{project.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['namespace']).to eq({
+ 'id' => user.namespace.id,
+ 'name' => user.namespace.name,
+ 'path' => user.namespace.path,
+ 'kind' => user.namespace.kind,
+ })
+ end
+
+ describe 'permissions' do
+ context 'all projects' do
+ before { project.team << [user, :master] }
+
+ it 'contains permission information' do
+ get v3_api("/projects", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response.first['permissions']['project_access']['access_level']).
+ to eq(Gitlab::Access::MASTER)
+ expect(json_response.first['permissions']['group_access']).to be_nil
+ end
+ end
+
+ context 'personal project' do
+ it 'sets project access and returns 200' do
+ project.team << [user, :master]
+ get v3_api("/projects/#{project.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['permissions']['project_access']['access_level']).
+ to eq(Gitlab::Access::MASTER)
+ expect(json_response['permissions']['group_access']).to be_nil
+ end
+ end
+
+ context 'group project' do
+ let(:project2) { create(:empty_project, group: create(:group)) }
+
+ before { project2.group.add_owner(user) }
+
+ it 'sets the owner and return 200' do
+ get v3_api("/projects/#{project2.id}", user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['permissions']['project_access']).to be_nil
+ expect(json_response['permissions']['group_access']['access_level']).
+ to eq(Gitlab::Access::OWNER)
+ end
+ end
+ end
+ end
+ end
+
+ describe 'GET /projects/:id/events' do
+ shared_examples_for 'project events response' do
+ it 'returns the project events' do
+ member = create(:user)
+ create(:project_member, :developer, user: member, project: project)
+ note = create(:note_on_issue, note: 'What an awesome day!', project: project)
+ EventCreateService.new.leave_note(note, note.author)
+
+ get v3_api("/projects/#{project.id}/events", current_user)
+
+ expect(response).to have_http_status(200)
+
+ first_event = json_response.first
+
+ expect(first_event['action_name']).to eq('commented on')
+ expect(first_event['note']['body']).to eq('What an awesome day!')
+
+ last_event = json_response.last
+
+ expect(last_event['action_name']).to eq('joined')
+ expect(last_event['project_id'].to_i).to eq(project.id)
+ expect(last_event['author_username']).to eq(member.username)
+ expect(last_event['author']['name']).to eq(member.name)
+ end
+ end
+
+ context 'when unauthenticated' do
+ it_behaves_like 'project events response' do
+ let(:project) { create(:empty_project, :public) }
+ let(:current_user) { nil }
+ end
+ end
+
+ context 'when authenticated' do
+ context 'valid request' do
+ it_behaves_like 'project events response' do
+ let(:current_user) { user }
+ end
+ end
+
+ it 'returns a 404 error if not found' do
+ get v3_api('/projects/42/events', user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
+
+ it 'returns a 404 error if user is not a member' do
+ other_user = create(:user)
+
+ get v3_api("/projects/#{project.id}/events", other_user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe 'GET /projects/:id/users' do
+ shared_examples_for 'project users response' do
+ it 'returns the project users' do
+ member = create(:user)
+ create(:project_member, :developer, user: member, project: project)
+
+ get v3_api("/projects/#{project.id}/users", current_user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(1)
+
+ first_user = json_response.first
+
+ expect(first_user['username']).to eq(member.username)
+ expect(first_user['name']).to eq(member.name)
+ expect(first_user.keys).to contain_exactly(*%w[name username id state avatar_url web_url])
+ end
+ end
+
+ context 'when unauthenticated' do
+ it_behaves_like 'project users response' do
+ let(:project) { create(:empty_project, :public) }
+ let(:current_user) { nil }
+ end
+ end
+
+ context 'when authenticated' do
+ context 'valid request' do
+ it_behaves_like 'project users response' do
+ let(:current_user) { user }
+ end
+ end
+
+ it 'returns a 404 error if not found' do
+ get v3_api('/projects/42/users', user)
+
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
+
+ it 'returns a 404 error if user is not a member' do
+ other_user = create(:user)
+
+ get v3_api("/projects/#{project.id}/users", other_user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+
+ describe 'GET /projects/:id/snippets' do
+ before { snippet }
+
+ it 'returns an array of project snippets' do
+ get v3_api("/projects/#{project.id}/snippets", user)
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.first['title']).to eq(snippet.title)
+ end
+ end
+
+ describe 'GET /projects/:id/snippets/:snippet_id' do
+ it 'returns a project snippet' do
+ get v3_api("/projects/#{project.id}/snippets/#{snippet.id}", user)
+ expect(response).to have_http_status(200)
+ expect(json_response['title']).to eq(snippet.title)
+ end
+
+ it 'returns a 404 error if snippet id not found' do
+ get v3_api("/projects/#{project.id}/snippets/1234", user)
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ describe 'POST /projects/:id/snippets' do
+ it 'creates a new project snippet' do
+ post v3_api("/projects/#{project.id}/snippets", user),
+ title: 'v3_api test', file_name: 'sample.rb', code: 'test',
+ visibility_level: '0'
+ expect(response).to have_http_status(201)
+ expect(json_response['title']).to eq('v3_api test')
+ end
+
+ it 'returns a 400 error if invalid snippet is given' do
+ post v3_api("/projects/#{project.id}/snippets", user)
+ expect(status).to eq(400)
+ end
+ end
+
+ describe 'PUT /projects/:id/snippets/:snippet_id' do
+ it 'updates an existing project snippet' do
+ put v3_api("/projects/#{project.id}/snippets/#{snippet.id}", user),
+ code: 'updated code'
+ expect(response).to have_http_status(200)
+ expect(json_response['title']).to eq('example')
+ expect(snippet.reload.content).to eq('updated code')
+ end
+
+ it 'updates an existing project snippet with new title' do
+ put v3_api("/projects/#{project.id}/snippets/#{snippet.id}", user),
+ title: 'other v3_api test'
+ expect(response).to have_http_status(200)
+ expect(json_response['title']).to eq('other v3_api test')
+ end
+ end
+
+ describe 'DELETE /projects/:id/snippets/:snippet_id' do
+ before { snippet }
+
+ it 'deletes existing project snippet' do
+ expect do
+ delete v3_api("/projects/#{project.id}/snippets/#{snippet.id}", user)
+ end.to change { Snippet.count }.by(-1)
+ expect(response).to have_http_status(200)
+ end
+
+ it 'returns 404 when deleting unknown snippet id' do
+ delete v3_api("/projects/#{project.id}/snippets/1234", user)
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ describe 'GET /projects/:id/snippets/:snippet_id/raw' do
+ it 'gets a raw project snippet' do
+ get v3_api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user)
+ expect(response).to have_http_status(200)
+ end
+
+ it 'returns a 404 error if raw project snippet not found' do
+ get v3_api("/projects/#{project.id}/snippets/5555/raw", user)
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ describe :fork_admin do
+ let(:project_fork_target) { create(:empty_project) }
+ let(:project_fork_source) { create(:empty_project, :public) }
+
+ describe 'POST /projects/:id/fork/:forked_from_id' do
+ let(:new_project_fork_source) { create(:empty_project, :public) }
+
+ it "is not available for non admin users" do
+ post v3_api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
+ expect(response).to have_http_status(403)
+ end
+
+ it 'allows project to be forked from an existing project' do
+ expect(project_fork_target.forked?).not_to be_truthy
+ post v3_api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ expect(response).to have_http_status(201)
+ project_fork_target.reload
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target.forked_project_link).not_to be_nil
+ expect(project_fork_target.forked?).to be_truthy
+ end
+
+ it 'fails if forked_from project which does not exist' do
+ post v3_api("/projects/#{project_fork_target.id}/fork/9999", admin)
+ expect(response).to have_http_status(404)
+ end
+
+ it 'fails with 409 if already forked' do
+ post v3_api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ project_fork_target.reload
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ post v3_api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
+ expect(response).to have_http_status(409)
+ project_fork_target.reload
+ expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id)
+ expect(project_fork_target.forked?).to be_truthy
+ end
+ end
+
+ describe 'DELETE /projects/:id/fork' do
+ it "is not visible to users outside group" do
+ delete v3_api("/projects/#{project_fork_target.id}/fork", user)
+ expect(response).to have_http_status(404)
+ end
+
+ context 'when users belong to project group' do
+ let(:project_fork_target) { create(:empty_project, group: create(:group)) }
+
+ before do
+ project_fork_target.group.add_owner user
+ project_fork_target.group.add_developer user2
+ end
+
+ it 'is forbidden to non-owner users' do
+ delete v3_api("/projects/#{project_fork_target.id}/fork", user2)
+ expect(response).to have_http_status(403)
+ end
+
+ it 'makes forked project unforked' do
+ post v3_api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ project_fork_target.reload
+ expect(project_fork_target.forked_from_project).not_to be_nil
+ expect(project_fork_target.forked?).to be_truthy
+ delete v3_api("/projects/#{project_fork_target.id}/fork", admin)
+ expect(response).to have_http_status(200)
+ project_fork_target.reload
+ expect(project_fork_target.forked_from_project).to be_nil
+ expect(project_fork_target.forked?).not_to be_truthy
+ end
+
+ it 'is idempotent if not forked' do
+ expect(project_fork_target.forked_from_project).to be_nil
+ delete v3_api("/projects/#{project_fork_target.id}/fork", admin)
+ expect(response).to have_http_status(304)
+ expect(project_fork_target.reload.forked_from_project).to be_nil
+ end
+ end
+ end
+ end
+
+ describe "POST /projects/:id/share" do
+ let(:group) { create(:group) }
+
+ it "shares project with group" do
+ expires_at = 10.days.from_now.to_date
+
+ expect do
+ post v3_api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER, expires_at: expires_at
+ end.to change { ProjectGroupLink.count }.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['group_id']).to eq(group.id)
+ expect(json_response['group_access']).to eq(Gitlab::Access::DEVELOPER)
+ expect(json_response['expires_at']).to eq(expires_at.to_s)
+ end
+
+ it "returns a 400 error when group id is not given" do
+ post v3_api("/projects/#{project.id}/share", user), group_access: Gitlab::Access::DEVELOPER
+ expect(response).to have_http_status(400)
+ end
+
+ it "returns a 400 error when access level is not given" do
+ post v3_api("/projects/#{project.id}/share", user), group_id: group.id
+ expect(response).to have_http_status(400)
+ end
+
+ it "returns a 400 error when sharing is disabled" do
+ project.namespace.update(share_with_group_lock: true)
+ post v3_api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER
+ expect(response).to have_http_status(400)
+ end
+
+ it 'returns a 404 error when user cannot read group' do
+ private_group = create(:group, :private)
+
+ post v3_api("/projects/#{project.id}/share", user), group_id: private_group.id, group_access: Gitlab::Access::DEVELOPER
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'returns a 404 error when group does not exist' do
+ post v3_api("/projects/#{project.id}/share", user), group_id: 1234, group_access: Gitlab::Access::DEVELOPER
+
+ expect(response).to have_http_status(404)
+ end
+
+ it "returns a 400 error when wrong params passed" do
+ post v3_api("/projects/#{project.id}/share", user), group_id: group.id, group_access: 1234
+
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to eq 'group_access does not have a valid value'
+ end
+ end
+
+ describe 'DELETE /projects/:id/share/:group_id' do
+ it 'returns 204 when deleting a group share' do
+ group = create(:group, :public)
+ create(:project_group_link, group: group, project: project)
+
+ delete v3_api("/projects/#{project.id}/share/#{group.id}", user)
+
+ expect(response).to have_http_status(204)
+ expect(project.project_group_links).to be_empty
+ end
+
+ it 'returns a 400 when group id is not an integer' do
+ delete v3_api("/projects/#{project.id}/share/foo", user)
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'returns a 404 error when group link does not exist' do
+ delete v3_api("/projects/#{project.id}/share/1234", user)
+
+ expect(response).to have_http_status(404)
+ end
+
+ it 'returns a 404 error when project does not exist' do
+ delete v3_api("/projects/123/share/1234", user)
+
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ describe 'GET /projects/search/:query' do
+ let!(:query) { 'query'}
+ let!(:search) { create(:empty_project, name: query, creator_id: user.id, namespace: user.namespace) }
+ let!(:pre) { create(:empty_project, name: "pre_#{query}", creator_id: user.id, namespace: user.namespace) }
+ let!(:post) { create(:empty_project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) }
+ let!(:pre_post) { create(:empty_project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) }
+ let!(:unfound) { create(:empty_project, name: 'unfound', creator_id: user.id, namespace: user.namespace) }
+ let!(:internal) { create(:empty_project, :internal, name: "internal #{query}") }
+ let!(:unfound_internal) { create(:empty_project, :internal, name: 'unfound internal') }
+ let!(:public) { create(:empty_project, :public, name: "public #{query}") }
+ let!(:unfound_public) { create(:empty_project, :public, name: 'unfound public') }
+ let!(:one_dot_two) { create(:empty_project, :public, name: "one.dot.two") }
+
+ shared_examples_for 'project search response' do |args = {}|
+ it 'returns project search responses' do
+ get v3_api("/projects/search/#{args[:query]}", current_user)
+
+ expect(response).to have_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(args[:results])
+ json_response.each { |project| expect(project['name']).to match(args[:match_regex] || /.*#{args[:query]}.*/) }
+ end
+ end
+
+ context 'when unauthenticated' do
+ it_behaves_like 'project search response', query: 'query', results: 1 do
+ let(:current_user) { nil }
+ end
+ end
+
+ context 'when authenticated' do
+ it_behaves_like 'project search response', query: 'query', results: 6 do
+ let(:current_user) { user }
+ end
+ it_behaves_like 'project search response', query: 'one.dot.two', results: 1 do
+ let(:current_user) { user }
+ end
+ end
+
+ context 'when authenticated as a different user' do
+ it_behaves_like 'project search response', query: 'query', results: 2, match_regex: /(internal|public) query/ do
+ let(:current_user) { user2 }
+ end
+ end
+ end
+
+ describe 'PUT /projects/:id' do
+ before { project }
+ before { user }
+ before { user3 }
+ before { user4 }
+ before { project3 }
+ before { project4 }
+ before { project_member3 }
+ before { project_member2 }
+
+ context 'when unauthenticated' do
+ it 'returns authentication error' do
+ project_param = { name: 'bar' }
+ put v3_api("/projects/#{project.id}"), project_param
+ expect(response).to have_http_status(401)
+ end
+ end
+
+ context 'when authenticated as project owner' do
+ it 'updates name' do
+ project_param = { name: 'bar' }
+ put v3_api("/projects/#{project.id}", user), project_param
+ expect(response).to have_http_status(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'updates visibility_level' do
+ project_param = { visibility_level: 20 }
+ put v3_api("/projects/#{project3.id}", user), project_param
+ expect(response).to have_http_status(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'updates visibility_level from public to private' do
+ project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC })
+ project_param = { public: false }
+ put v3_api("/projects/#{project3.id}", user), project_param
+ expect(response).to have_http_status(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'does not update name to existing name' do
+ project_param = { name: project3.name }
+ put v3_api("/projects/#{project.id}", user), project_param
+ expect(response).to have_http_status(400)
+ expect(json_response['message']['name']).to eq(['has already been taken'])
+ end
+
+ it 'updates request_access_enabled' do
+ project_param = { request_access_enabled: false }
+
+ put v3_api("/projects/#{project.id}", user), project_param
+
+ expect(response).to have_http_status(200)
+ expect(json_response['request_access_enabled']).to eq(false)
+ end
+
+ it 'updates path & name to existing path & name in different namespace' do
+ project_param = { path: project4.path, name: project4.name }
+ put v3_api("/projects/#{project3.id}", user), project_param
+ expect(response).to have_http_status(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+ end
+
+ context 'when authenticated as project master' do
+ it 'updates path' do
+ project_param = { path: 'bar' }
+ put v3_api("/projects/#{project3.id}", user4), project_param
+ expect(response).to have_http_status(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'updates other attributes' do
+ project_param = { issues_enabled: true,
+ wiki_enabled: true,
+ snippets_enabled: true,
+ merge_requests_enabled: true,
+ description: 'new description' }
+
+ put v3_api("/projects/#{project3.id}", user4), project_param
+ expect(response).to have_http_status(200)
+ project_param.each_pair do |k, v|
+ expect(json_response[k.to_s]).to eq(v)
+ end
+ end
+
+ it 'does not update path to existing path' do
+ project_param = { path: project.path }
+ put v3_api("/projects/#{project3.id}", user4), project_param
+ expect(response).to have_http_status(400)
+ expect(json_response['message']['path']).to eq(['has already been taken'])
+ end
+
+ it 'does not update name' do
+ project_param = { name: 'bar' }
+ put v3_api("/projects/#{project3.id}", user4), project_param
+ expect(response).to have_http_status(403)
+ end
+
+ it 'does not update visibility_level' do
+ project_param = { visibility_level: 20 }
+ put v3_api("/projects/#{project3.id}", user4), project_param
+ expect(response).to have_http_status(403)
+ end
+ end
+
+ context 'when authenticated as project developer' do
+ it 'does not update other attributes' do
+ project_param = { path: 'bar',
+ issues_enabled: true,
+ wiki_enabled: true,
+ snippets_enabled: true,
+ merge_requests_enabled: true,
+ description: 'new description',
+ request_access_enabled: true }
+ put v3_api("/projects/#{project.id}", user3), project_param
+ expect(response).to have_http_status(403)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/archive' do
+ context 'on an unarchived project' do
+ it 'archives the project' do
+ post v3_api("/projects/#{project.id}/archive", user)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['archived']).to be_truthy
+ end
+ end
+
+ context 'on an archived project' do
+ before do
+ project.archive!
+ end
+
+ it 'remains archived' do
+ post v3_api("/projects/#{project.id}/archive", user)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['archived']).to be_truthy
+ end
+ end
+
+ context 'user without archiving rights to the project' do
+ before do
+ project.team << [user3, :developer]
+ end
+
+ it 'rejects the action' do
+ post v3_api("/projects/#{project.id}/archive", user3)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/unarchive' do
+ context 'on an unarchived project' do
+ it 'remains unarchived' do
+ post v3_api("/projects/#{project.id}/unarchive", user)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['archived']).to be_falsey
+ end
+ end
+
+ context 'on an archived project' do
+ before do
+ project.archive!
+ end
+
+ it 'unarchives the project' do
+ post v3_api("/projects/#{project.id}/unarchive", user)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['archived']).to be_falsey
+ end
+ end
+
+ context 'user without archiving rights to the project' do
+ before do
+ project.team << [user3, :developer]
+ end
+
+ it 'rejects the action' do
+ post v3_api("/projects/#{project.id}/unarchive", user3)
+
+ expect(response).to have_http_status(403)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/star' do
+ context 'on an unstarred project' do
+ it 'stars the project' do
+ expect { post v3_api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1)
+
+ expect(response).to have_http_status(201)
+ expect(json_response['star_count']).to eq(1)
+ end
+ end
+
+ context 'on a starred project' do
+ before do
+ user.toggle_star(project)
+ project.reload
+ end
+
+ it 'does not modify the star count' do
+ expect { post v3_api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
+
+ expect(response).to have_http_status(304)
+ end
+ end
+ end
+
+ describe 'DELETE /projects/:id/star' do
+ context 'on a starred project' do
+ before do
+ user.toggle_star(project)
+ project.reload
+ end
+
+ it 'unstars the project' do
+ expect { delete v3_api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(-1)
+
+ expect(response).to have_http_status(200)
+ expect(json_response['star_count']).to eq(0)
+ end
+ end
+
+ context 'on an unstarred project' do
+ it 'does not modify the star count' do
+ expect { delete v3_api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count }
+
+ expect(response).to have_http_status(304)
+ end
+ end
+ end
+
+ describe 'DELETE /projects/:id' do
+ context 'when authenticated as user' do
+ it 'removes project' do
+ delete v3_api("/projects/#{project.id}", user)
+ expect(response).to have_http_status(200)
+ end
+
+ it 'does not remove a project if not an owner' do
+ user3 = create(:user)
+ project.team << [user3, :developer]
+ delete v3_api("/projects/#{project.id}", user3)
+ expect(response).to have_http_status(403)
+ end
+
+ it 'does not remove a non existing project' do
+ delete v3_api('/projects/1328', user)
+ expect(response).to have_http_status(404)
+ end
+
+ it 'does not remove a project not attached to user' do
+ delete v3_api("/projects/#{project.id}", user2)
+ expect(response).to have_http_status(404)
+ end
+ end
+
+ context 'when authenticated as admin' do
+ it 'removes any existing project' do
+ delete v3_api("/projects/#{project.id}", admin)
+ expect(response).to have_http_status(200)
+ end
+
+ it 'does not remove a non existing project' do
+ delete v3_api('/projects/1328', admin)
+ expect(response).to have_http_status(404)
+ end
+ end
+ end
+end
diff --git a/spec/services/boards/create_service_spec.rb b/spec/services/boards/create_service_spec.rb
index fde807cc410..7b29b043296 100644
--- a/spec/services/boards/create_service_spec.rb
+++ b/spec/services/boards/create_service_spec.rb
@@ -11,12 +11,11 @@ describe Boards::CreateService, services: true do
expect { service.execute }.to change(Board, :count).by(1)
end
- it 'creates default lists' do
+ it 'creates the default lists' do
board = service.execute
- expect(board.lists.size).to eq 2
- expect(board.lists.first).to be_backlog
- expect(board.lists.last).to be_done
+ expect(board.lists.size).to eq 1
+ expect(board.lists.first).to be_done
end
end
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 7c206cf3ce7..305278843f5 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -13,7 +13,6 @@ describe Boards::Issues::ListService, services: true do
let(:p2) { create(:label, title: 'P2', project: project, priority: 2) }
let(:p3) { create(:label, title: 'P3', project: project, priority: 3) }
- let!(:backlog) { create(:backlog_list, board: board) }
let!(:list1) { create(:list, board: board, label: development, position: 0) }
let!(:list2) { create(:list, board: board, label: testing, position: 1) }
let!(:done) { create(:done_list, board: board) }
@@ -45,8 +44,8 @@ describe Boards::Issues::ListService, services: true do
end
context 'sets default order to priority' do
- it 'returns opened issues when listing issues from Backlog' do
- params = { board_id: board.id, id: backlog.id }
+ it 'returns opened issues when list id is missing' do
+ params = { board_id: board.id }
issues = described_class.new(project, user, params).execute
diff --git a/spec/services/boards/issues/move_service_spec.rb b/spec/services/boards/issues/move_service_spec.rb
index c43b2aec490..77f75167b3d 100644
--- a/spec/services/boards/issues/move_service_spec.rb
+++ b/spec/services/boards/issues/move_service_spec.rb
@@ -10,7 +10,6 @@ describe Boards::Issues::MoveService, services: true do
let(:development) { create(:label, project: project, name: 'Development') }
let(:testing) { create(:label, project: project, name: 'Testing') }
- let!(:backlog) { create(:backlog_list, board: board1) }
let!(:list1) { create(:list, board: board1, label: development, position: 0) }
let!(:list2) { create(:list, board: board1, label: testing, position: 1) }
let!(:done) { create(:done_list, board: board1) }
@@ -19,41 +18,6 @@ describe Boards::Issues::MoveService, services: true do
project.team << [user, :developer]
end
- context 'when moving from backlog' do
- it 'adds the label of the list it goes to' do
- issue = create(:labeled_issue, project: project, labels: [bug])
- params = { board_id: board1.id, from_list_id: backlog.id, to_list_id: list1.id }
-
- described_class.new(project, user, params).execute(issue)
-
- expect(issue.reload.labels).to contain_exactly(bug, development)
- end
- end
-
- context 'when moving to backlog' do
- it 'removes all list-labels' do
- issue = create(:labeled_issue, project: project, labels: [bug, development, testing])
- params = { board_id: board1.id, from_list_id: list1.id, to_list_id: backlog.id }
-
- described_class.new(project, user, params).execute(issue)
-
- expect(issue.reload.labels).to contain_exactly(bug)
- end
- end
-
- context 'when moving from backlog to done' do
- it 'closes the issue' do
- issue = create(:labeled_issue, project: project, labels: [bug])
- params = { board_id: board1.id, from_list_id: backlog.id, to_list_id: done.id }
-
- described_class.new(project, user, params).execute(issue)
- issue.reload
-
- expect(issue.labels).to contain_exactly(bug)
- expect(issue).to be_closed
- end
- end
-
context 'when moving an issue between lists' do
let(:issue) { create(:labeled_issue, project: project, labels: [bug, development]) }
let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list2.id } }
@@ -113,19 +77,6 @@ describe Boards::Issues::MoveService, services: true do
end
end
- context 'when moving from done to backlog' do
- it 'reopens the issue' do
- issue = create(:labeled_issue, :closed, project: project, labels: [bug])
- params = { board_id: board1.id, from_list_id: done.id, to_list_id: backlog.id }
-
- described_class.new(project, user, params).execute(issue)
- issue.reload
-
- expect(issue.labels).to contain_exactly(bug)
- expect(issue).to be_reopened
- end
- end
-
context 'when moving to same list' do
let(:issue) { create(:labeled_issue, project: project, labels: [bug, development]) }
let(:params) { { board_id: board1.id, from_list_id: list1.id, to_list_id: list1.id } }
diff --git a/spec/services/boards/lists/create_service_spec.rb b/spec/services/boards/lists/create_service_spec.rb
index a7e9efcf93f..ebac38e68f1 100644
--- a/spec/services/boards/lists/create_service_spec.rb
+++ b/spec/services/boards/lists/create_service_spec.rb
@@ -21,7 +21,7 @@ describe Boards::Lists::CreateService, services: true do
end
end
- context 'when board lists has backlog, and done lists' do
+ context 'when board lists has the done list' do
it 'creates a new list at beginning of the list' do
list = service.execute(board)
@@ -40,7 +40,7 @@ describe Boards::Lists::CreateService, services: true do
end
end
- context 'when board lists has backlog, label and done lists' do
+ context 'when board lists has label and done lists' do
it 'creates a new list at end of the label lists' do
list1 = create(:list, board: board, position: 0)
diff --git a/spec/services/boards/lists/destroy_service_spec.rb b/spec/services/boards/lists/destroy_service_spec.rb
index 628caf03476..a30860f828a 100644
--- a/spec/services/boards/lists/destroy_service_spec.rb
+++ b/spec/services/boards/lists/destroy_service_spec.rb
@@ -15,7 +15,6 @@ describe Boards::Lists::DestroyService, services: true do
end
it 'decrements position of higher lists' do
- backlog = board.backlog_list
development = create(:list, board: board, position: 0)
review = create(:list, board: board, position: 1)
staging = create(:list, board: board, position: 2)
@@ -23,20 +22,12 @@ describe Boards::Lists::DestroyService, services: true do
described_class.new(project, user).execute(development)
- expect(backlog.reload.position).to be_nil
expect(review.reload.position).to eq 0
expect(staging.reload.position).to eq 1
expect(done.reload.position).to be_nil
end
end
- it 'does not remove list from board when list type is backlog' do
- list = board.backlog_list
- service = described_class.new(project, user)
-
- expect { service.execute(list) }.not_to change(board.lists, :count)
- end
-
it 'does not remove list from board when list type is done' do
list = board.done_list
service = described_class.new(project, user)
diff --git a/spec/services/boards/lists/list_service_spec.rb b/spec/services/boards/lists/list_service_spec.rb
index 334cee3f06d..2dffc62b215 100644
--- a/spec/services/boards/lists/list_service_spec.rb
+++ b/spec/services/boards/lists/list_service_spec.rb
@@ -10,7 +10,7 @@ describe Boards::Lists::ListService, services: true do
service = described_class.new(project, double)
- expect(service.execute(board)).to eq [board.backlog_list, list, board.done_list]
+ expect(service.execute(board)).to eq [list, board.done_list]
end
end
end
diff --git a/spec/services/boards/lists/move_service_spec.rb b/spec/services/boards/lists/move_service_spec.rb
index 63fa0bb8c5f..3786dc82bf0 100644
--- a/spec/services/boards/lists/move_service_spec.rb
+++ b/spec/services/boards/lists/move_service_spec.rb
@@ -6,7 +6,6 @@ describe Boards::Lists::MoveService, services: true do
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
- let!(:backlog) { create(:backlog_list, board: board) }
let!(:planning) { create(:list, board: board, position: 0) }
let!(:development) { create(:list, board: board, position: 1) }
let!(:review) { create(:list, board: board, position: 2) }
@@ -87,14 +86,6 @@ describe Boards::Lists::MoveService, services: true do
end
end
- it 'keeps position of lists when list type is backlog' do
- service = described_class.new(project, user, position: 2)
-
- service.execute(backlog)
-
- expect(current_list_positions).to eq [0, 1, 2, 3]
- end
-
it 'keeps position of lists when list type is done' do
service = described_class.new(project, user, position: 2)
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/slash_commands/interpret_service_spec.rb
index 66fc8fc360b..0b0925983eb 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/slash_commands/interpret_service_spec.rb
@@ -653,5 +653,37 @@ describe SlashCommands::InterpretService, services: true do
let(:issuable) { issue }
end
end
+
+ context '/target_branch command' do
+ let(:non_empty_project) { create(:project) }
+ let(:another_merge_request) { create(:merge_request, author: developer, source_project: non_empty_project) }
+ let(:service) { described_class.new(non_empty_project, developer)}
+
+ it 'updates target_branch if /target_branch command is executed' do
+ _, updates = service.execute('/target_branch merge-test', merge_request)
+
+ expect(updates).to eq(target_branch: 'merge-test')
+ end
+
+ it 'handles blanks around param' do
+ _, updates = service.execute('/target_branch merge-test ', merge_request)
+
+ expect(updates).to eq(target_branch: 'merge-test')
+ end
+
+ context 'ignores command with no argument' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/target_branch' }
+ let(:issuable) { another_merge_request }
+ end
+ end
+
+ context 'ignores non-existing target branch' do
+ it_behaves_like 'empty command' do
+ let(:content) { '/target_branch totally_non_existing_branch' }
+ let(:issuable) { another_merge_request }
+ end
+ end
+ end
end
end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index bd7269045e1..7913a180f9b 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -752,13 +752,13 @@ describe SystemNoteService, services: true do
it 'sets the note text' do
noteable.update_attribute(:time_estimate, 277200)
- expect(subject.note).to eq "Changed time estimate of this issue to 1w 4d 5h"
+ expect(subject.note).to eq "changed time estimate to 1w 4d 5h"
end
end
context 'without a time estimate' do
it 'sets the note text' do
- expect(subject.note).to eq "Removed time estimate on this issue"
+ expect(subject.note).to eq "removed time estimate"
end
end
end
@@ -782,7 +782,7 @@ describe SystemNoteService, services: true do
it 'sets the note text' do
spend_time!(277200)
- expect(subject.note).to eq "Added 1w 4d 5h of time spent on this merge request"
+ expect(subject.note).to eq "added 1w 4d 5h of time spent"
end
end
@@ -790,7 +790,7 @@ describe SystemNoteService, services: true do
it 'sets the note text' do
spend_time!(-277200)
- expect(subject.note).to eq "Subtracted 1w 4d 5h of time spent on this merge request"
+ expect(subject.note).to eq "subtracted 1w 4d 5h of time spent"
end
end
@@ -798,7 +798,7 @@ describe SystemNoteService, services: true do
it 'sets the note text' do
spend_time!(:reset)
- expect(subject.note).to eq "Removed time spent on this merge request"
+ expect(subject.note).to eq "removed time spent"
end
end
diff --git a/spec/support/api_helpers.rb b/spec/support/api_helpers.rb
index 68b196d9033..ae6e708cf87 100644
--- a/spec/support/api_helpers.rb
+++ b/spec/support/api_helpers.rb
@@ -17,8 +17,8 @@ module ApiHelpers
# => "/api/v2/issues?foo=bar&private_token=..."
#
# Returns the relative path to the requested API resource
- def api(path, user = nil)
- "/api/#{API::API.version}#{path}" +
+ def api(path, user = nil, version: API::API.version)
+ "/api/#{version}#{path}" +
# Normalize query string
(path.index('?') ? '' : '?') +
@@ -31,6 +31,11 @@ module ApiHelpers
end
end
+ # Temporary helper method for simplifying V3 exclusive API specs
+ def v3_api(path, user = nil)
+ api(path, user, version: 'v3')
+ end
+
def ci_api(path, user = nil)
"/ci/api/v1/#{path}" +
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index 1b0a4583f5c..944ea30656f 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -35,7 +35,7 @@ module ExportFileHelper
project: project,
commit_id: ci_pipeline.sha)
- create(:event, target: milestone, project: project, action: Event::CREATED, author: user)
+ create(:event, :created, target: milestone, project: project, author: user)
create(:project_member, :master, user: user, project: project)
create(:ci_variable, project: project)
create(:ci_trigger, project: project)
diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/kubernetes_helpers.rb
index 6c4c246a68b..444612cf871 100644
--- a/spec/support/kubernetes_helpers.rb
+++ b/spec/support/kubernetes_helpers.rb
@@ -43,7 +43,8 @@ module KubernetesHelpers
url: container_exec_url(service.api_url, service.namespace, pod_name, container['name']),
subprotocols: ['channel.k8s.io'],
headers: { 'Authorization' => ["Bearer #{service.token}"] },
- created_at: DateTime.parse(pod['metadata']['creationTimestamp'])
+ created_at: DateTime.parse(pod['metadata']['creationTimestamp']),
+ max_session_time: 0
}
terminal[:ca_pem] = service.ca_pem if service.ca_pem.present?
terminal
diff --git a/spec/support/matchers/match_file.rb b/spec/support/matchers/match_file.rb
new file mode 100644
index 00000000000..d1888b3376a
--- /dev/null
+++ b/spec/support/matchers/match_file.rb
@@ -0,0 +1,5 @@
+RSpec::Matchers.define :match_file do |expected|
+ match do |actual|
+ expect(Digest::MD5.hexdigest(actual)).to eq(Digest::MD5.hexdigest(File.read(expected)))
+ end
+end
diff --git a/spec/teaspoon_env.rb b/spec/teaspoon_env.rb
deleted file mode 100644
index 643b161cdf4..00000000000
--- a/spec/teaspoon_env.rb
+++ /dev/null
@@ -1,178 +0,0 @@
-Teaspoon.configure do |config|
- # Determines where the Teaspoon routes will be mounted. Changing this to "/jasmine" would allow you to browse to
- # `http://localhost:3000/jasmine` to run your tests.
- config.mount_at = "/teaspoon"
-
- # Specifies the root where Teaspoon will look for files. If you're testing an engine using a dummy application it can
- # be useful to set this to your engines root (e.g. `Teaspoon::Engine.root`).
- # Note: Defaults to `Rails.root` if nil.
- config.root = nil
-
- # Paths that will be appended to the Rails assets paths
- # Note: Relative to `config.root`.
- config.asset_paths = ["spec/javascripts", "spec/javascripts/stylesheets"]
-
- # Fixtures are rendered through a controller, which allows using HAML, RABL/JBuilder, etc. Files in these paths will
- # be rendered as fixtures.
- config.fixture_paths = ["spec/javascripts/fixtures"]
-
- # SUITES
- #
- # You can modify the default suite configuration and create new suites here. Suites are isolated from one another.
- #
- # When defining a suite you can provide a name and a block. If the name is left blank, :default is assumed. You can
- # omit various directives and the ones defined in the default suite will be used.
- #
- # To run a specific suite
- # - in the browser: http://localhost/teaspoon/[suite_name]
- # - with the rake task: rake teaspoon suite=[suite_name]
- # - with the cli: teaspoon --suite=[suite_name]
- config.suite do |suite|
- # Specify the framework you would like to use. This allows you to select versions, and will do some basic setup for
- # you -- which you can override with the directives below. This should be specified first, as it can override other
- # directives.
- # Note: If no version is specified, the latest is assumed.
- #
- # Versions: 1.3.1, 2.0.3, 2.1.3, 2.2.0
- suite.use_framework :jasmine, "2.2.0"
-
- # Specify a file matcher as a regular expression and all matching files will be loaded when the suite is run. These
- # files need to be within an asset path. You can add asset paths using the `config.asset_paths`.
- suite.matcher = "{spec/javascripts,app/assets}/**/*_spec.{js,js.es6,es6}"
-
- # Load additional JS files, but requiring them in your spec helper is the preferred way to do this.
- # suite.javascripts = []
-
- # You can include your own stylesheets if you want to change how Teaspoon looks.
- # Note: Spec related CSS can and should be loaded using fixtures.
- # suite.stylesheets = ["teaspoon"]
-
- # This suites spec helper, which can require additional support files. This file is loaded before any of your test
- # files are loaded.
- suite.helper = "spec_helper"
-
- # Partial to be rendered in the head tag of the runner. You can use the provided ones or define your own by creating
- # a `_boot.html.erb` in your fixtures path, and adjust the config to `"/boot"` for instance.
- #
- # Available: boot, boot_require_js
- suite.boot_partial = "boot"
-
- # Partial to be rendered in the body tag of the runner. You can define your own to create a custom body structure.
- suite.body_partial = "body"
-
- # Hooks allow you to use `Teaspoon.hook("fixtures")` before, after, or during your spec run. This will make a
- # synchronous Ajax request to the server that will call all of the blocks you've defined for that hook name.
- # suite.hook :fixtures, &proc{}
-
- # Determine whether specs loaded into the test harness should be embedded as individual script tags or concatenated
- # into a single file. Similar to Rails' asset `debug: true` and `config.assets.debug = true` options. By default,
- # Teaspoon expands all assets to provide more valuable stack traces that reference individual source files.
- # suite.expand_assets = true
- end
-
- # Example suite. Since we're just filtering to files already within the root test/javascripts, these files will also
- # be run in the default suite -- but can be focused into a more specific suite.
- # config.suite :targeted do |suite|
- # suite.matcher = "spec/javascripts/targeted/*_spec.{js,js.coffee,coffee}"
- # end
-
- # CONSOLE RUNNER SPECIFIC
- #
- # These configuration directives are applicable only when running via the rake task or command line interface. These
- # directives can be overridden using the command line interface arguments or with ENV variables when using the rake
- # task.
- #
- # Command Line Interface:
- # teaspoon --driver=phantomjs --server-port=31337 --fail-fast=true --format=junit --suite=my_suite /spec/file_spec.js
- #
- # Rake:
- # teaspoon DRIVER=phantomjs SERVER_PORT=31337 FAIL_FAST=true FORMATTERS=junit suite=my_suite
-
- # Specify which headless driver to use. Supports PhantomJS and Selenium Webdriver.
- #
- # Available: :phantomjs, :selenium, :capybara_webkit
- # PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS
- # Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver
- # Capybara Webkit: https://github.com/modeset/teaspoon/wiki/Using-Capybara-Webkit
- # config.driver = :phantomjs
-
- # Specify additional options for the driver.
- #
- # PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS
- # Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver
- # Capybara Webkit: https://github.com/modeset/teaspoon/wiki/Using-Capybara-Webkit
- # config.driver_options = nil
-
- # Specify the timeout for the driver. Specs are expected to complete within this time frame or the run will be
- # considered a failure. This is to avoid issues that can arise where tests stall.
- # config.driver_timeout = 180
-
- # Specify a server to use with Rack (e.g. thin, mongrel). If nil is provided Rack::Server is used.
- # config.server = nil
-
- # Specify a port to run on a specific port, otherwise Teaspoon will use a random available port.
- # config.server_port = nil
-
- # Timeout for starting the server in seconds. If your server is slow to start you may have to bump this, or you may
- # want to lower this if you know it shouldn't take long to start.
- # config.server_timeout = 20
-
- # Force Teaspoon to fail immediately after a failing suite. Can be useful to make Teaspoon fail early if you have
- # several suites, but in environments like CI this may not be desirable.
- # config.fail_fast = true
-
- # Specify the formatters to use when outputting the results.
- # Note: Output files can be specified by using `"junit>/path/to/output.xml"`.
- #
- # Available: :dot, :clean, :documentation, :json, :junit, :pride, :rspec_html, :snowday, :swayze_or_oprah, :tap, :tap_y, :teamcity
- # config.formatters = [:dot]
-
- # Specify if you want color output from the formatters.
- # config.color = true
-
- # Teaspoon pipes all console[log/debug/error] to $stdout. This is useful to catch places where you've forgotten to
- # remove them, but in verbose applications this may not be desirable.
- # config.suppress_log = false
-
- # COVERAGE REPORTS / THRESHOLD ASSERTIONS
- #
- # Coverage reports requires Istanbul (https://github.com/gotwarlost/istanbul) to add instrumentation to your code and
- # display coverage statistics.
- #
- # Coverage configurations are similar to suites. You can define several, and use different ones under different
- # conditions.
- #
- # To run with a specific coverage configuration
- # - with the rake task: rake teaspoon USE_COVERAGE=[coverage_name]
- # - with the cli: teaspoon --coverage=[coverage_name]
-
- # Specify that you always want a coverage configuration to be used. Otherwise, specify that you want coverage
- # on the CLI.
- # Set this to "true" or the name of your coverage config.
- config.use_coverage = true
-
- # You can have multiple coverage configs by passing a name to config.coverage.
- # e.g. config.coverage :ci do |coverage|
- # The default coverage config name is :default.
- config.coverage do |coverage|
- # Which coverage reports Istanbul should generate. Correlates directly to what Istanbul supports.
- #
- # Available: text-summary, text, html, lcov, lcovonly, cobertura, teamcity
- coverage.reports = ["text-summary", "html"]
-
- # The path that the coverage should be written to - when there's an artifact to write to disk.
- # Note: Relative to `config.root`.
- coverage.output_path = "coverage-javascript"
-
- # Assets to be ignored when generating coverage reports. Accepts an array of filenames or regular expressions. The
- # default excludes assets from vendor, gems and support libraries.
- coverage.ignore = [%r{vendor/}, %r{spec/javascripts/(?!helpers/)}]
-
- # Various thresholds requirements can be defined, and those thresholds will be checked at the end of a run. If any
- # aren't met the run will fail with a message. Thresholds can be defined as a percentage (0-100), or nil.
- # coverage.statements = nil
- # coverage.functions = nil
- # coverage.branches = nil
- # coverage.lines = nil
- end
-end