519ffa1ebf
* master: (297 commits) Fix deletion of container registry or images returning an error The fog-aliyun gem had a bug in v0.1.0 for file storage creation/update. This merge requests update the gem to v0.2.0 which contains the fix: Decrease ABC threshold to 54.28 Update VERSION to 10.2.0-pre Update CHANGELOG.md for 10.1.0 Document `CI_SHARED_ENVIRONMENT` and `CI_DISPOSABLE_ENVIRONMENT` Fix the external URLs generated for online view of HTML artifacts Use title as placeholder instead of issue title for reusability Fix failure in current_settings_spec.rb Clarify the difference between project_update and project_rename URI decode Page-Title header to preserve UTF-8 characters Update Gitaly version to v0.49.0 Decrease Perceived Complexity threshold to 14 Resolve "Remove help text regarding group issues on group issues page (and group merge requests page)" Force non diff resolved discussion to display when collapse toggled Added submodule support in multi-file editor add note about after_script being run separately Check for element before evaluate_script Merge branch 'master-i18n' into 'master' Update Prometheus gem to fix problems with other files overwriting current file ...
606 lines
19 KiB
Ruby
606 lines
19 KiB
Ruby
require 'rails_helper'
|
|
|
|
describe 'Issue Boards', :js do
|
|
include CookieHelper
|
|
include DragTo
|
|
|
|
let(:group) { create(:group, :nested) }
|
|
let(:project) { create(:project, :public, namespace: group) }
|
|
let(:board) { create(:board, project: project) }
|
|
let(:user) { create(:user) }
|
|
let!(:user2) { create(:user) }
|
|
|
|
before do
|
|
project.team << [user, :master]
|
|
project.team << [user2, :master]
|
|
|
|
set_cookie('sidebar_collapsed', 'true')
|
|
|
|
sign_in(user)
|
|
end
|
|
|
|
context 'no lists' do
|
|
before do
|
|
visit project_board_path(project, board)
|
|
wait_for_requests
|
|
expect(page).to have_selector('.board', count: 3)
|
|
end
|
|
|
|
it 'shows blank state' do
|
|
expect(page).to have_content('Welcome to your Issue Board!')
|
|
end
|
|
|
|
it 'shows tooltip on add issues button' do
|
|
button = page.find('.filter-dropdown-container button', text: 'Add issues')
|
|
|
|
expect(button[:"data-original-title"]).to eq("Please add a list to your board first")
|
|
end
|
|
|
|
it 'hides the blank state when clicking nevermind button' 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)
|
|
end
|
|
|
|
it 'creates default lists' do
|
|
lists = ['Backlog', 'To Do', 'Doing', 'Closed']
|
|
|
|
page.within(find('.board-blank-state')) do
|
|
click_button('Add default lists')
|
|
end
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.board', count: 4)
|
|
|
|
page.all('.board').each_with_index do |list, i|
|
|
expect(list.find('.board-title')).to have_content(lists[i])
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with lists' do
|
|
let(:milestone) { create(:milestone, project: project) }
|
|
|
|
let(:planning) { create(:label, project: project, name: 'Planning', description: 'Test') }
|
|
let(:development) { create(:label, project: project, name: 'Development') }
|
|
let(:testing) { create(:label, project: project, name: 'Testing') }
|
|
let(:bug) { create(:label, project: project, name: 'Bug') }
|
|
let!(:backlog) { create(:label, project: project, name: 'Backlog') }
|
|
let!(:closed) { create(:label, project: project, name: 'Closed') }
|
|
let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
|
|
|
|
let!(:list1) { create(:list, board: board, label: planning, position: 0) }
|
|
let!(:list2) { create(:list, board: board, label: development, position: 1) }
|
|
|
|
let!(:confidential_issue) { create(:labeled_issue, :confidential, project: project, author: user, labels: [planning], relative_position: 9) }
|
|
let!(:issue1) { create(:labeled_issue, project: project, title: 'aaa', description: '111', assignees: [user], labels: [planning], relative_position: 8) }
|
|
let!(:issue2) { create(:labeled_issue, project: project, title: 'bbb', description: '222', author: user2, labels: [planning], relative_position: 7) }
|
|
let!(:issue3) { create(:labeled_issue, project: project, title: 'ccc', description: '333', labels: [planning], relative_position: 6) }
|
|
let!(:issue4) { create(:labeled_issue, project: project, title: 'ddd', description: '444', labels: [planning], relative_position: 5) }
|
|
let!(:issue5) { create(:labeled_issue, project: project, title: 'eee', description: '555', labels: [planning], milestone: milestone, relative_position: 4) }
|
|
let!(:issue6) { create(:labeled_issue, project: project, title: 'fff', description: '666', labels: [planning, development], relative_position: 3) }
|
|
let!(:issue7) { create(:labeled_issue, project: project, title: 'ggg', description: '777', labels: [development], relative_position: 2) }
|
|
let!(:issue8) { create(:closed_issue, project: project, title: 'hhh', description: '888') }
|
|
let!(:issue9) { create(:labeled_issue, project: project, title: 'iii', description: '999', labels: [planning, testing, bug, accepting], relative_position: 1) }
|
|
|
|
before do
|
|
visit project_board_path(project, board)
|
|
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.board', count: 4)
|
|
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 description tooltip on list title' do
|
|
page.within('.board:nth-child(2)') do
|
|
expect(find('.board-title span.has-tooltip')[:title]).to eq('Test')
|
|
end
|
|
end
|
|
|
|
it 'shows issues in lists' do
|
|
wait_for_board_cards(2, 8)
|
|
wait_for_board_cards(3, 2)
|
|
end
|
|
|
|
it 'shows confidential issues with icon' do
|
|
page.within(find('.board:nth-child(2)')) do
|
|
expect(page).to have_selector('.confidential-icon', count: 1)
|
|
end
|
|
end
|
|
|
|
it 'search closed list' do
|
|
find('.filtered-search').set(issue8.title)
|
|
find('.filtered-search').native.send_keys(:enter)
|
|
|
|
wait_for_requests
|
|
|
|
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)
|
|
end
|
|
|
|
it 'search list' do
|
|
find('.filtered-search').set(issue5.title)
|
|
find('.filtered-search').native.send_keys(:enter)
|
|
|
|
wait_for_requests
|
|
|
|
expect(find('.board:nth-child(2)')).to have_selector('.card', count: 1)
|
|
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
|
|
accept_confirm { find('.board-delete').click }
|
|
end
|
|
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.board', count: 3)
|
|
end
|
|
|
|
it 'removes checkmark in new list dropdown after deleting' do
|
|
click_button 'Add list'
|
|
wait_for_requests
|
|
|
|
find('.dropdown-menu-close').click
|
|
|
|
page.within(find('.board:nth-child(2)')) do
|
|
accept_confirm { find('.board-delete').click }
|
|
end
|
|
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.board', count: 3)
|
|
end
|
|
|
|
it 'infinite scrolls list' do
|
|
50.times do
|
|
create(:labeled_issue, project: project, labels: [planning])
|
|
end
|
|
|
|
visit project_board_path(project, board)
|
|
wait_for_requests
|
|
|
|
page.within(find('.board:nth-child(2)')) do
|
|
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 58 issues')
|
|
|
|
find('.board .board-list')
|
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.card', count: 40)
|
|
expect(page).to have_content('Showing 40 of 58 issues')
|
|
|
|
find('.board .board-list')
|
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.card', count: 58)
|
|
expect(page).to have_content('Showing all issues')
|
|
end
|
|
end
|
|
|
|
context 'closed' do
|
|
it 'shows list of closed issues' do
|
|
wait_for_board_cards(4, 1)
|
|
wait_for_requests
|
|
end
|
|
|
|
it 'moves issue to closed' do
|
|
drag(list_from_index: 1, list_to_index: 3)
|
|
|
|
wait_for_board_cards(2, 7)
|
|
wait_for_board_cards(3, 2)
|
|
wait_for_board_cards(4, 2)
|
|
|
|
expect(find('.board:nth-child(2)')).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)
|
|
end
|
|
|
|
it 'removes all of the same issue to closed' do
|
|
drag(list_from_index: 1, list_to_index: 3)
|
|
|
|
wait_for_board_cards(2, 7)
|
|
wait_for_board_cards(3, 2)
|
|
wait_for_board_cards(4, 2)
|
|
|
|
expect(find('.board:nth-child(2)')).not_to have_content(issue9.title)
|
|
expect(find('.board:nth-child(4)')).to have_content(issue9.title)
|
|
expect(find('.board:nth-child(4)')).not_to have_content(planning.title)
|
|
end
|
|
end
|
|
|
|
context 'lists' do
|
|
it 'changes position of list' do
|
|
drag(list_from_index: 2, list_to_index: 1, selector: '.board-header')
|
|
|
|
wait_for_board_cards(2, 2)
|
|
wait_for_board_cards(3, 8)
|
|
wait_for_board_cards(4, 1)
|
|
|
|
expect(find('.board:nth-child(2)')).to have_content(development.title)
|
|
expect(find('.board:nth-child(2)')).to have_content(planning.title)
|
|
end
|
|
|
|
it 'issue moves between lists' do
|
|
drag(list_from_index: 1, from_index: 1, list_to_index: 2)
|
|
|
|
wait_for_board_cards(2, 7)
|
|
wait_for_board_cards(3, 2)
|
|
wait_for_board_cards(4, 1)
|
|
|
|
expect(find('.board:nth-child(3)')).to have_content(issue6.title)
|
|
expect(find('.board:nth-child(3)').all('.card').last).to have_content(development.title)
|
|
end
|
|
|
|
it 'issue moves between lists' do
|
|
drag(list_from_index: 2, list_to_index: 1)
|
|
|
|
wait_for_board_cards(2, 9)
|
|
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).to have_content(planning.title)
|
|
end
|
|
|
|
it 'issue moves from closed' do
|
|
drag(list_from_index: 2, list_to_index: 3)
|
|
|
|
wait_for_board_cards(2, 8)
|
|
wait_for_board_cards(3, 1)
|
|
wait_for_board_cards(4, 2)
|
|
|
|
expect(find('.board:nth-child(4)')).to have_content(issue8.title)
|
|
end
|
|
|
|
context 'issue card' do
|
|
it 'shows assignee' do
|
|
page.within(find('.board:nth-child(2)')) do
|
|
expect(page).to have_selector('.avatar', count: 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'new list' do
|
|
it 'shows all labels in new list dropdown' do
|
|
click_button 'Add list'
|
|
wait_for_requests
|
|
|
|
page.within('.dropdown-menu-issues-board-new') do
|
|
expect(page).to have_content(planning.title)
|
|
expect(page).to have_content(development.title)
|
|
expect(page).to have_content(testing.title)
|
|
end
|
|
end
|
|
|
|
it 'creates new list for label' do
|
|
click_button 'Add list'
|
|
wait_for_requests
|
|
|
|
page.within('.dropdown-menu-issues-board-new') do
|
|
click_link testing.title
|
|
end
|
|
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.board', count: 5)
|
|
end
|
|
|
|
it 'creates new list for Backlog label' do
|
|
click_button 'Add list'
|
|
wait_for_requests
|
|
|
|
page.within('.dropdown-menu-issues-board-new') do
|
|
click_link backlog.title
|
|
end
|
|
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.board', count: 5)
|
|
end
|
|
|
|
it 'creates new list for Closed label' do
|
|
click_button 'Add list'
|
|
wait_for_requests
|
|
|
|
page.within('.dropdown-menu-issues-board-new') do
|
|
click_link closed.title
|
|
end
|
|
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.board', count: 5)
|
|
end
|
|
|
|
it 'keeps dropdown open after adding new list' do
|
|
click_button 'Add list'
|
|
wait_for_requests
|
|
|
|
page.within('.dropdown-menu-issues-board-new') do
|
|
click_link closed.title
|
|
end
|
|
|
|
wait_for_requests
|
|
|
|
expect(page).to have_css('#js-add-list.open')
|
|
end
|
|
|
|
it 'creates new list from a new label' do
|
|
click_button 'Add list'
|
|
|
|
wait_for_requests
|
|
|
|
click_link 'Create new label'
|
|
|
|
fill_in('new_label_name', with: 'Testing New Label')
|
|
|
|
first('.suggest-colors a').click
|
|
|
|
click_button 'Create'
|
|
|
|
wait_for_requests
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.board', count: 5)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'filtering' do
|
|
it 'filters by author' do
|
|
set_filter("author", user2.username)
|
|
click_filter_link(user2.username)
|
|
submit_filter
|
|
|
|
wait_for_requests
|
|
wait_for_board_cards(2, 1)
|
|
wait_for_empty_boards((3..4))
|
|
end
|
|
|
|
it 'filters by assignee' do
|
|
set_filter("assignee", user.username)
|
|
click_filter_link(user.username)
|
|
submit_filter
|
|
|
|
wait_for_requests
|
|
|
|
wait_for_board_cards(2, 1)
|
|
wait_for_empty_boards((3..4))
|
|
end
|
|
|
|
it 'filters by milestone' do
|
|
set_filter("milestone", "\"#{milestone.title}")
|
|
click_filter_link(milestone.title)
|
|
submit_filter
|
|
|
|
wait_for_requests
|
|
wait_for_board_cards(2, 1)
|
|
wait_for_board_cards(3, 0)
|
|
wait_for_board_cards(4, 0)
|
|
end
|
|
|
|
it 'filters by label' do
|
|
set_filter("label", testing.title)
|
|
click_filter_link(testing.title)
|
|
submit_filter
|
|
|
|
wait_for_requests
|
|
wait_for_board_cards(2, 1)
|
|
wait_for_empty_boards((3..4))
|
|
end
|
|
|
|
it 'filters by label with space after reload' do
|
|
set_filter("label", "\"#{accepting.title}")
|
|
click_filter_link(accepting.title)
|
|
submit_filter
|
|
|
|
# Test after reload
|
|
page.evaluate_script 'window.location.reload()'
|
|
wait_for_board_cards(2, 1)
|
|
wait_for_empty_boards((3..4))
|
|
|
|
wait_for_requests
|
|
|
|
page.within(find('.board:nth-child(2)')) do
|
|
expect(page.find('.board-header')).to have_content('1')
|
|
expect(page).to have_selector('.card', count: 1)
|
|
end
|
|
|
|
page.within(find('.board:nth-child(3)')) do
|
|
expect(page.find('.board-header')).to have_content('0')
|
|
expect(page).to have_selector('.card', count: 0)
|
|
end
|
|
end
|
|
|
|
it 'removes filtered labels' do
|
|
set_filter("label", testing.title)
|
|
click_filter_link(testing.title)
|
|
submit_filter
|
|
|
|
wait_for_board_cards(2, 1)
|
|
|
|
find('.clear-search').click
|
|
submit_filter
|
|
|
|
wait_for_board_cards(2, 8)
|
|
end
|
|
|
|
it 'infinite scrolls list with label filter' do
|
|
50.times do
|
|
create(:labeled_issue, project: project, labels: [planning, testing])
|
|
end
|
|
|
|
set_filter("label", testing.title)
|
|
click_filter_link(testing.title)
|
|
submit_filter
|
|
|
|
wait_for_requests
|
|
|
|
page.within(find('.board:nth-child(2)')) do
|
|
expect(page.find('.board-header')).to have_content('51')
|
|
expect(page).to have_selector('.card', count: 20)
|
|
expect(page).to have_content('Showing 20 of 51 issues')
|
|
|
|
find('.board .board-list')
|
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
|
|
|
expect(page).to have_selector('.card', count: 40)
|
|
expect(page).to have_content('Showing 40 of 51 issues')
|
|
|
|
find('.board .board-list')
|
|
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
|
|
|
|
expect(page).to have_selector('.card', count: 51)
|
|
expect(page).to have_content('Showing all issues')
|
|
end
|
|
end
|
|
|
|
it 'filters by multiple labels' do
|
|
set_filter("label", testing.title)
|
|
click_filter_link(testing.title)
|
|
|
|
set_filter("label", bug.title)
|
|
click_filter_link(bug.title)
|
|
|
|
submit_filter
|
|
|
|
wait_for_requests
|
|
|
|
wait_for_board_cards(2, 1)
|
|
wait_for_empty_boards((3..4))
|
|
end
|
|
|
|
it 'filters by clicking label button on issue' do
|
|
page.within(find('.board:nth-child(2)')) do
|
|
expect(page).to have_selector('.card', count: 8)
|
|
expect(find('.card', match: :first)).to have_content(bug.title)
|
|
click_button(bug.title)
|
|
wait_for_requests
|
|
end
|
|
|
|
page.within('.tokens-container') do
|
|
expect(page).to have_content(bug.title)
|
|
end
|
|
|
|
wait_for_requests
|
|
|
|
wait_for_board_cards(2, 1)
|
|
wait_for_empty_boards((3..4))
|
|
end
|
|
|
|
it 'removes label filter by clicking label button on issue' do
|
|
page.within(find('.board:nth-child(2)')) do
|
|
page.within(find('.card', match: :first)) do
|
|
click_button(bug.title)
|
|
end
|
|
|
|
wait_for_requests
|
|
|
|
expect(page).to have_selector('.card', count: 1)
|
|
end
|
|
|
|
wait_for_requests
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'keyboard shortcuts' do
|
|
before do
|
|
visit project_board_path(project, board)
|
|
wait_for_requests
|
|
end
|
|
|
|
it 'allows user to use keyboard shortcuts' do
|
|
find('body').native.send_keys('i')
|
|
expect(page).to have_content('New Issue')
|
|
end
|
|
end
|
|
|
|
context 'signed out user' do
|
|
before do
|
|
sign_out(:user)
|
|
visit project_board_path(project, board)
|
|
wait_for_requests
|
|
end
|
|
|
|
it 'displays lists' do
|
|
expect(page).to have_selector('.board')
|
|
end
|
|
|
|
it 'does not show create new list' do
|
|
expect(page).not_to have_selector('.js-new-board-list')
|
|
end
|
|
|
|
it 'does not allow dragging' do
|
|
expect(page).not_to have_selector('.user-can-drag')
|
|
end
|
|
end
|
|
|
|
context 'as guest user' do
|
|
let(:user_guest) { create(:user) }
|
|
|
|
before do
|
|
project.team << [user_guest, :guest]
|
|
sign_out(:user)
|
|
sign_in(user_guest)
|
|
visit project_board_path(project, board)
|
|
wait_for_requests
|
|
end
|
|
|
|
it 'does not show create new list' do
|
|
expect(page).not_to have_selector('.js-new-board-list')
|
|
end
|
|
end
|
|
|
|
def drag(selector: '.board-list', list_from_index: 0, from_index: 0, to_index: 0, list_to_index: 0)
|
|
# ensure there is enough horizontal space for four boards
|
|
page.current_window.resize_to(2000, 800)
|
|
|
|
drag_to(selector: selector,
|
|
scrollable: '#board-app',
|
|
list_from_index: list_from_index,
|
|
from_index: from_index,
|
|
to_index: to_index,
|
|
list_to_index: list_to_index)
|
|
end
|
|
|
|
def wait_for_board_cards(board_number, expected_cards)
|
|
page.within(find(".board:nth-child(#{board_number})")) do
|
|
expect(page.find('.board-header')).to have_content(expected_cards.to_s)
|
|
expect(page).to have_selector('.card', count: expected_cards)
|
|
end
|
|
end
|
|
|
|
def wait_for_empty_boards(board_numbers)
|
|
board_numbers.each do |board|
|
|
wait_for_board_cards(board, 0)
|
|
end
|
|
end
|
|
|
|
def set_filter(type, text)
|
|
find('.filtered-search').native.send_keys("#{type}:#{text}")
|
|
end
|
|
|
|
def submit_filter
|
|
find('.filtered-search').native.send_keys(:enter)
|
|
end
|
|
|
|
def click_filter_link(link_text)
|
|
page.within('.filtered-search-box') do
|
|
expect(page).to have_button(link_text)
|
|
|
|
click_button(link_text)
|
|
end
|
|
end
|
|
end
|