diff --git a/app/assets/javascripts/boards/test_utils/simulate_drag.js b/app/assets/javascripts/boards/test_utils/simulate_drag.js new file mode 100755 index 00000000000..92bc8ea73a8 --- /dev/null +++ b/app/assets/javascripts/boards/test_utils/simulate_drag.js @@ -0,0 +1,121 @@ +(function () { + 'use strict'; + + function simulateEvent(el, type, options) { + var event; + var ownerDocument = el.ownerDocument; + + options = options || {}; + + if (/^mouse/.test(type)) { + event = ownerDocument.createEvent('MouseEvents'); + event.initMouseEvent(type, true, true, ownerDocument.defaultView, + options.button, options.screenX, options.screenY, options.clientX, options.clientY, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el); + } else { + event = ownerDocument.createEvent('CustomEvent'); + + event.initCustomEvent(type, true, true, ownerDocument.defaultView, + options.button, options.screenX, options.screenY, options.clientX, options.clientY, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el); + + event.dataTransfer = { + data: {}, + + setData: function (type, val) { + this.data[type] = val; + }, + + getData: function (type) { + return this.data[type]; + } + }; + } + + if (el.dispatchEvent) { + el.dispatchEvent(event); + } else if (el.fireEvent) { + el.fireEvent('on' + type, event); + } + + return event; + } + + function getTraget(target) { + var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el; + var children = el.children; + + return ( + children[target.index] || + children[target.index === 'first' ? 0 : -1] || + children[target.index === 'last' ? children.length - 1 : -1] + ); + } + + function getRect(el) { + var rect = el.getBoundingClientRect(); + var width = rect.right - rect.left; + var height = rect.bottom - rect.top; + + return { + x: rect.left, + y: rect.top, + cx: rect.left + width / 2, + cy: rect.top + height / 2, + w: width, + h: height, + hw: width / 2, + wh: height / 2 + }; + } + + function simulateDrag(options, callback) { + options.to.el = options.to.el || options.from.el; + + var fromEl = getTraget(options.from); + var toEl = getTraget(options.to); + var scrollable = options.scrollable; + + var fromRect = getRect(fromEl); + var toRect = getRect(toEl); + + var startTime = new Date().getTime(); + var duration = options.duration || 1000; + simulateEvent(fromEl, 'mousedown', {button: 0}); + options.ontap && options.ontap(); + + requestAnimationFrame(function () { + options.ondragstart && options.ondragstart(); + }); + + requestAnimationFrame(function loop() { + var progress = (new Date().getTime() - startTime) / duration; + var x = (fromRect.cx + (toRect.cx - fromRect.cx) * progress) - scrollable.scrollLeft; + var y = fromRect.cy + (toRect.cy - fromRect.cy) * progress; + var overEl = fromEl.ownerDocument.elementFromPoint(x, y); + + simulateEvent(overEl, 'mousemove', { + clientX: x, + clientY: y + }); + + if (progress < 1) { + requestAnimationFrame(loop); + } else { + options.ondragend && options.ondragend(); + simulateEvent(toEl, 'mouseup'); + } + }); + + return { + target: fromEl, + fromList: fromEl.parentNode, + toList: toEl.parentNode + }; + } + + + // Export + window.simulateEvent = simulateEvent; + window.simulateDrag = simulateDrag; +})(); diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 491cb4e37de..f0aecd10248 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -23,18 +23,33 @@ } .issue-boards-page { + .content-wrapper { + display: -webkit-flex; + display: flex; + } + + .sub-nav, + .issues-filters { + -webkit-flex: none; + flex: none; + } + .page-with-sidebar { + display: -webkit-flex; display: flex; min-height: 100vh; max-height: 100vh; } .issue-boards-content { + display: -webkit-flex; display: flex; - height: 100%; + width: 100%; .content { + display: -webkit-flex; display: flex; + -webkit-flex-direction: column; flex-direction: column; width: 100%; } @@ -42,8 +57,10 @@ } .boards-list { + display: -webkit-flex; display: flex; - height: 100%; + -webkit-flex: 1; + flex: 1; min-height: 455px; padding-top: 25px; padding-right: ($gl-padding / 2); @@ -52,17 +69,19 @@ } .board { + display: -webkit-flex; + display: flex; min-width: 400px; max-width: 400px; - height: 100%; padding-right: ($gl-padding / 2); padding-left: ($gl-padding / 2); } .board-inner { + display: -webkit-flex; display: flex; + -webkit-flex-direction: column; flex-direction: column; - height: 100%; width: 100%; font-size: $issue-boards-font-size; background: $background-color; @@ -162,6 +181,7 @@ } .board-list { + -webkit-flex: 1; flex: 1; margin: 0; padding: 5px; diff --git a/app/views/projects/boards/index.html.haml b/app/views/projects/boards/index.html.haml index 515638b5fd4..620cdb12a35 100644 --- a/app/views/projects/boards/index.html.haml +++ b/app/views/projects/boards/index.html.haml @@ -4,6 +4,7 @@ - content_for :page_specific_javascripts do = page_specific_javascript_tag('boards/boards_bundle.js') + = page_specific_javascript_tag('boards/test_utils/simulate_drag.js') if Rails.env.test? = render "projects/issues/head" diff --git a/config/application.rb b/config/application.rb index cd2d51d5908..0c136623477 100644 --- a/config/application.rb +++ b/config/application.rb @@ -86,6 +86,7 @@ module Gitlab config.assets.precompile << "network/network_bundle.js" config.assets.precompile << "profile/profile_bundle.js" config.assets.precompile << "boards/boards_bundle.js" + config.assets.precompile << "boards/test_utils/simulate_drag.js" config.assets.precompile << "lib/utils/*.js" config.assets.precompile << "lib/*.js" config.assets.precompile << "u2f.js" diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb new file mode 100644 index 00000000000..6c514598653 --- /dev/null +++ b/spec/features/boards/boards_spec.rb @@ -0,0 +1,71 @@ +require 'rails_helper' + +describe 'Issue Boards', feature: true, js: true do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + login_as(user) + + visit namespace_project_boards_path(project.namespace, project) + end + + it 'shows default lists' do + lists = all('.board') + + page.within lists.first do + expect(page).to have_content 'Backlog' + end + + page.within lists.last do + expect(page).to have_content 'Done' + end + end + + it 'removes blank state list' do + click_button 'Nevermind, i\'ll use my own' + + expect(page).to have_selector('.board', count: 2) + end + + it 'can drag card to new list' do + sleep 0.5 + lists = all('.board') + drag_to(list_from_index: 0, list_to_index: 1) + + page.within lists[1].find('.board-list') do + expect(page).to have_content('Test') + expect(page).to have_selector('.card', count: 2) + + page.within first('.card .card-footer') do + expect(page).to have_content 'Frontend' + end + end + end + + it 'removes all labels from card' do + sleep 0.5 + lists = all('.board') + drag_to(list_from_index: 1, list_to_index: 3) + + page.within lists[3].find('.board-list') do + expect(page).to have_content('Frontend bug') + expect(page).to have_selector('.card', count: 2) + + page.within first('.card .card-footer') do + expect(page).not_to have_content 'Frontend' + end + end + + page.within lists[1].find('.board-list') do + expect(page).not_to have_content('Frontend bug') + expect(page).not_to have_selector('.card') + end + end + + def drag_to(list_from_index: 0, card_index: 0, to_index: 0, list_to_index: 0) + evaluate_script("simulateDrag({scrollable: document.getElementById('board-app'), from: {el: $('.board-list').eq(#{list_from_index}).get(0), index: #{card_index}}, to: {el: $('.board-list').eq(#{list_to_index}).get(0), index: #{to_index}}});") + sleep 1 + end +end