Commit 0a4b853f authored by Clement Ho's avatar Clement Ho

Merge branch 'refactor_simulate_drag' into 'master'

Refactor test_utils bundle

See merge request !10437
parents d8cbe979 4461e708
......@@ -8,6 +8,7 @@ cache:
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
RAILS_ENV: "test"
NODE_ENV: "test"
SIMPLECOV: "true"
SETUP_DB: "true"
USE_BUNDLE_INSTALL: "true"
......@@ -129,9 +130,7 @@ setup-test-env:
stage: prepare
script:
- node --version
- yarn --version
- yarn install --pure-lockfile
- yarn check # ensure that yarn.lock matches package.json
- bundle exec rake gitlab:assets:compile
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts:
......
......@@ -223,7 +223,7 @@ gem 'oj', '~> 2.17.4'
gem 'chronic', '~> 0.10.2'
gem 'chronic_duration', '~> 0.10.6'
gem 'webpack-rails', '~> 0.9.9'
gem 'webpack-rails', '~> 0.9.10'
gem 'rack-proxy', '~> 0.6.0'
gem 'sass-rails', '~> 5.0.6'
......
......@@ -823,8 +823,8 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
webpack-rails (0.9.9)
rails (>= 3.2.0)
webpack-rails (0.9.10)
railties (>= 3.2.0)
websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
......@@ -1026,7 +1026,7 @@ DEPENDENCIES
virtus (~> 1.0.1)
vmstat (~> 2.3.0)
webmock (~> 1.24.0)
webpack-rails (~> 0.9.9)
webpack-rails (~> 0.9.10)
wikicloth (= 0.8.1)
BUNDLED WITH
......
......@@ -187,6 +187,9 @@ import './visibility_select';
import './wikis';
import './zen_mode';
// eslint-disable-next-line global-require
if (process.env.NODE_ENV !== 'production') require('./test_utils/');
document.addEventListener('beforeunload', function () {
// Unbind scroll events
$(document).off('scroll');
......
import simulateDrag from './simulate_drag';
// Export to global space for rspec to use
window.simulateDrag = simulateDrag;
/* eslint-disable wrap-iife, func-names, strict, no-var, vars-on-top, no-param-reassign, object-shorthand, no-shadow, comma-dangle, prefer-template, consistent-return, no-mixed-operators, no-unused-vars, no-unused-expressions, prefer-arrow-callback, max-len */
(function () {
'use strict';
function simulateEvent(el, type, options) {
var event;
if (!el) return;
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 isLast(target) {
var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
var children = el.children;
return children.length - 1 === target.index;
function simulateEvent(el, type, options = {}) {
let event;
if (!el) return null;
if (/^mouse/.test(type)) {
event = el.ownerDocument.createEvent('MouseEvents');
event.initMouseEvent(type, true, true, el.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 = el.ownerDocument.createEvent('CustomEvent');
event.initCustomEvent(type, true, true, el.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(key, val) {
this.data[key] = val;
},
getData(key) {
return this.data[key];
},
};
}
function getTarget(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] ||
el
);
if (el.dispatchEvent) {
el.dispatchEvent(event);
} else if (el.fireEvent) {
el.fireEvent(`on${type}`, event);
}
function getRect(el) {
var rect = el.getBoundingClientRect();
var width = rect.right - rect.left;
var height = rect.bottom - rect.top + 10;
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
};
return event;
}
function isLast(target) {
const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
const children = el.children;
return children.length - 1 === target.index;
}
function getTarget(target) {
const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
const children = el.children;
return (
children[target.index] ||
children[target.index === 'first' ? 0 : -1] ||
children[target.index === 'last' ? children.length - 1 : -1] ||
el
);
}
function getRect(el) {
const rect = el.getBoundingClientRect();
const width = rect.right - rect.left;
const height = (rect.bottom - rect.top) + 10;
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,
};
}
export default function simulateDrag(options) {
const { to, from } = options;
to.el = to.el || from.el;
const fromEl = getTarget(from);
const toEl = getTarget(to);
const firstEl = getTarget({
el: to.el,
index: 'first',
});
const lastEl = getTarget({
el: options.to.el,
index: 'last',
});
const fromRect = getRect(fromEl);
const toRect = getRect(toEl);
const firstRect = getRect(firstEl);
const lastRect = getRect(lastEl);
const startTime = new Date().getTime();
const duration = options.duration || 1000;
simulateEvent(fromEl, 'mousedown', {
button: 0,
clientX: fromRect.cx,
clientY: fromRect.cy,
});
if (options.ontap) options.ontap();
window.SIMULATE_DRAG_ACTIVE = 1;
if (options.to.index === 0) {
toRect.cy = firstRect.y;
} else if (isLast(options.to)) {
toRect.cy = lastRect.y + lastRect.h + 50;
}
function simulateDrag(options, callback) {
options.to.el = options.to.el || options.from.el;
const dragInterval = setInterval(() => {
const progress = (new Date().getTime() - startTime) / duration;
const x = (fromRect.cx + ((toRect.cx - fromRect.cx) * progress));
const y = (fromRect.cy + ((toRect.cy - fromRect.cy) * progress));
const overEl = fromEl.ownerDocument.elementFromPoint(x, y);
var fromEl = getTarget(options.from);
var toEl = getTarget(options.to);
var firstEl = getTarget({
el: options.to.el,
index: 'first'
simulateEvent(overEl, 'mousemove', {
clientX: x,
clientY: y,
});
var lastEl = getTarget({
el: options.to.el,
index: 'last'
});
var scrollable = options.scrollable;
var fromRect = getRect(fromEl);
var toRect = getRect(toEl);
var firstRect = getRect(firstEl);
var lastRect = getRect(lastEl);
var startTime = new Date().getTime();
var duration = options.duration || 1000;
simulateEvent(fromEl, 'mousedown', { button: 0 });
options.ontap && options.ontap();
window.SIMULATE_DRAG_ACTIVE = 1;
if (options.to.index === 0) {
toRect.cy = firstRect.y;
} else if (isLast(options.to)) {
toRect.cy = lastRect.y + lastRect.h + 50;
}
var dragInterval = setInterval(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) - scrollable.scrollTop;
var overEl = fromEl.ownerDocument.elementFromPoint(x, y);
simulateEvent(overEl, 'mousemove', {
clientX: x,
clientY: y
});
if (progress >= 1) {
options.ondragend && options.ondragend();
simulateEvent(toEl, 'mouseup');
clearInterval(dragInterval);
window.SIMULATE_DRAG_ACTIVE = 0;
}
}, 100);
return {
target: fromEl,
fromList: fromEl.parentNode,
toList: toEl.parentNode
};
}
// Export
window.simulateEvent = simulateEvent;
window.simulateDrag = simulateDrag;
})();
if (progress >= 1) {
if (options.ondragend) options.ondragend();
simulateEvent(toEl, 'mouseup');
clearInterval(dragInterval);
window.SIMULATE_DRAG_ACTIVE = 0;
}
}, 100);
return {
target: fromEl,
fromList: fromEl.parentNode,
toList: toEl.parentNode,
};
}
= render "header_title"
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test?
= render 'shared/milestones/top', milestone: @milestone, group: @group
= render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true
= render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102
......@@ -6,7 +6,6 @@
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('filtered_search')
= page_specific_javascript_bundle_tag('boards')
= page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test?
%script#js-board-template{ type: "text/x-template" }= render "projects/boards/components/board"
%script#js-board-list-template{ type: "text/x-template" }= render "projects/boards/components/board_list"
......
......@@ -3,9 +3,6 @@
- hide_class = ''
= render "projects/issues/head"
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test?
- if @labels.exists? || @prioritized_labels.exists?
%div{ class: container_class }
.top-area.adjust
......
......@@ -3,9 +3,6 @@
- page_description @milestone.description
= render "projects/issues/head"
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('simulate_drag') if Rails.env.test?
%div{ class: container_class }
.detail-page-header.milestone-page-header
.status-box{ class: status_box_class(@milestone) }
......
......@@ -23,7 +23,6 @@ var config = {
main: './main.js',
blob: './blob_edit/blob_bundle.js',
boards: './boards/boards_bundle.js',
simulate_drag: './test_utils/simulate_drag.js',
cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js',
commit_pipelines: './commit/pipelines/pipelines_bundle.js',
diff_notes: './diff_notes/diff_notes_bundle.js',
......
......@@ -76,6 +76,7 @@ describe 'Milestone draggable', feature: true, js: true do
create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone))
visit namespace_project_milestone_path(project.namespace, project, milestone)
scroll_into_view('.milestone-content')
drag_to(selector: '.issues-sortable-list', list_to_index: 1)
wait_for_ajax
......@@ -86,8 +87,13 @@ describe 'Milestone draggable', feature: true, js: true do
visit namespace_project_milestone_path(project.namespace, project, milestone)
page.find("a[href='#tab-merge-requests']").click
scroll_into_view('.milestone-content')
drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1)
wait_for_ajax
end
def scroll_into_view(selector)
page.evaluate_script("document.querySelector('#{selector}').scrollIntoView();")
end
end
......@@ -3,11 +3,11 @@ module DragTo
evaluate_script("simulateDrag({scrollable: $('#{scrollable}').get(0), from: {el: $('#{selector}').eq(#{list_from_index}).get(0), index: #{from_index}}, to: {el: $('#{selector}').eq(#{list_to_index}).get(0), index: #{to_index}}});")
Timeout.timeout(Capybara.default_max_wait_time) do
loop until drag_active?
loop while drag_active?
end
end
def drag_active?
page.evaluate_script('window.SIMULATE_DRAG_ACTIVE').zero?
page.evaluate_script('window.SIMULATE_DRAG_ACTIVE').nonzero?
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment