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: ...@@ -8,6 +8,7 @@ cache:
variables: variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1" MYSQL_ALLOW_EMPTY_PASSWORD: "1"
RAILS_ENV: "test" RAILS_ENV: "test"
NODE_ENV: "test"
SIMPLECOV: "true" SIMPLECOV: "true"
SETUP_DB: "true" SETUP_DB: "true"
USE_BUNDLE_INSTALL: "true" USE_BUNDLE_INSTALL: "true"
...@@ -129,9 +130,7 @@ setup-test-env: ...@@ -129,9 +130,7 @@ setup-test-env:
stage: prepare stage: prepare
script: script:
- node --version - node --version
- yarn --version
- yarn install --pure-lockfile - yarn install --pure-lockfile
- yarn check # ensure that yarn.lock matches package.json
- bundle exec rake gitlab:assets:compile - bundle exec rake gitlab:assets:compile
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts: artifacts:
......
...@@ -223,7 +223,7 @@ gem 'oj', '~> 2.17.4' ...@@ -223,7 +223,7 @@ gem 'oj', '~> 2.17.4'
gem 'chronic', '~> 0.10.2' gem 'chronic', '~> 0.10.2'
gem 'chronic_duration', '~> 0.10.6' 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 'rack-proxy', '~> 0.6.0'
gem 'sass-rails', '~> 5.0.6' gem 'sass-rails', '~> 5.0.6'
......
...@@ -823,8 +823,8 @@ GEM ...@@ -823,8 +823,8 @@ GEM
addressable (>= 2.3.6) addressable (>= 2.3.6)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff hashdiff
webpack-rails (0.9.9) webpack-rails (0.9.10)
rails (>= 3.2.0) railties (>= 3.2.0)
websocket-driver (0.6.3) websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2) websocket-extensions (0.1.2)
...@@ -1026,7 +1026,7 @@ DEPENDENCIES ...@@ -1026,7 +1026,7 @@ DEPENDENCIES
virtus (~> 1.0.1) virtus (~> 1.0.1)
vmstat (~> 2.3.0) vmstat (~> 2.3.0)
webmock (~> 1.24.0) webmock (~> 1.24.0)
webpack-rails (~> 0.9.9) webpack-rails (~> 0.9.10)
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
......
...@@ -187,6 +187,9 @@ import './visibility_select'; ...@@ -187,6 +187,9 @@ import './visibility_select';
import './wikis'; import './wikis';
import './zen_mode'; import './zen_mode';
// eslint-disable-next-line global-require
if (process.env.NODE_ENV !== 'production') require('./test_utils/');
document.addEventListener('beforeunload', function () { document.addEventListener('beforeunload', function () {
// Unbind scroll events // Unbind scroll events
$(document).off('scroll'); $(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 simulateEvent(el, type, options = {}) {
(function () { let event;
'use strict'; if (!el) return null;
function simulateEvent(el, type, options) { if (/^mouse/.test(type)) {
var event; event = el.ownerDocument.createEvent('MouseEvents');
if (!el) return; event.initMouseEvent(type, true, true, el.ownerDocument.defaultView,
var ownerDocument = el.ownerDocument; options.button, options.screenX, options.screenY, options.clientX, options.clientY,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
options = options || {}; } else {
event = el.ownerDocument.createEvent('CustomEvent');
if (/^mouse/.test(type)) {
event = ownerDocument.createEvent('MouseEvents'); event.initCustomEvent(type, true, true, el.ownerDocument.defaultView,
event.initMouseEvent(type, true, true, ownerDocument.defaultView, options.button, options.screenX, options.screenY, options.clientX, options.clientY,
options.button, options.screenX, options.screenY, options.clientX, options.clientY, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
} else { event.dataTransfer = {
event = ownerDocument.createEvent('CustomEvent'); data: {},
event.initCustomEvent(type, true, true, ownerDocument.defaultView, setData(key, val) {
options.button, options.screenX, options.screenY, options.clientX, options.clientY, this.data[key] = val;
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el); },
event.dataTransfer = { getData(key) {
data: {}, return this.data[key];
},
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 getTarget(target) { if (el.dispatchEvent) {
var el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el; el.dispatchEvent(event);
var children = el.children; } else if (el.fireEvent) {
el.fireEvent(`on${type}`, event);
return (
children[target.index] ||
children[target.index === 'first' ? 0 : -1] ||
children[target.index === 'last' ? children.length - 1 : -1] ||
el
);
} }
function getRect(el) { return event;
var rect = el.getBoundingClientRect(); }
var width = rect.right - rect.left;
var height = rect.bottom - rect.top + 10; function isLast(target) {
const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
return { const children = el.children;
x: rect.left,
y: rect.top, return children.length - 1 === target.index;
cx: rect.left + width / 2, }
cy: rect.top + height / 2,
w: width, function getTarget(target) {
h: height, const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
hw: width / 2, const children = el.children;
wh: height / 2
}; 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) { const dragInterval = setInterval(() => {
options.to.el = options.to.el || options.from.el; 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); simulateEvent(overEl, 'mousemove', {
var toEl = getTarget(options.to); clientX: x,
var firstEl = getTarget({ clientY: y,
el: options.to.el,
index: 'first'
}); });
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() { if (progress >= 1) {
var progress = (new Date().getTime() - startTime) / duration; if (options.ondragend) options.ondragend();
var x = (fromRect.cx + (toRect.cx - fromRect.cx) * progress) - scrollable.scrollLeft; simulateEvent(toEl, 'mouseup');
var y = (fromRect.cy + (toRect.cy - fromRect.cy) * progress) - scrollable.scrollTop; clearInterval(dragInterval);
var overEl = fromEl.ownerDocument.elementFromPoint(x, y); window.SIMULATE_DRAG_ACTIVE = 0;
}
simulateEvent(overEl, 'mousemove', { }, 100);
clientX: x,
clientY: y return {
}); target: fromEl,
fromList: fromEl.parentNode,
if (progress >= 1) { toList: toEl.parentNode,
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;
})();
= render "header_title" = 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/top', milestone: @milestone, group: @group
= render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true = render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true
= render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102 = render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
= page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('filtered_search') = page_specific_javascript_bundle_tag('filtered_search')
= page_specific_javascript_bundle_tag('boards') = 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-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" %script#js-board-list-template{ type: "text/x-template" }= render "projects/boards/components/board_list"
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
- hide_class = '' - hide_class = ''
= render "projects/issues/head" = 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? - if @labels.exists? || @prioritized_labels.exists?
%div{ class: container_class } %div{ class: container_class }
.top-area.adjust .top-area.adjust
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
- page_description @milestone.description - page_description @milestone.description
= render "projects/issues/head" = 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 } %div{ class: container_class }
.detail-page-header.milestone-page-header .detail-page-header.milestone-page-header
.status-box{ class: status_box_class(@milestone) } .status-box{ class: status_box_class(@milestone) }
......
...@@ -23,7 +23,6 @@ var config = { ...@@ -23,7 +23,6 @@ var config = {
main: './main.js', main: './main.js',
blob: './blob_edit/blob_bundle.js', blob: './blob_edit/blob_bundle.js',
boards: './boards/boards_bundle.js', boards: './boards/boards_bundle.js',
simulate_drag: './test_utils/simulate_drag.js',
cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js', cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js',
commit_pipelines: './commit/pipelines/pipelines_bundle.js', commit_pipelines: './commit/pipelines/pipelines_bundle.js',
diff_notes: './diff_notes/diff_notes_bundle.js', diff_notes: './diff_notes/diff_notes_bundle.js',
......
...@@ -76,6 +76,7 @@ describe 'Milestone draggable', feature: true, js: true do ...@@ -76,6 +76,7 @@ describe 'Milestone draggable', feature: true, js: true do
create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone)) create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone))
visit namespace_project_milestone_path(project.namespace, project, 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) drag_to(selector: '.issues-sortable-list', list_to_index: 1)
wait_for_ajax wait_for_ajax
...@@ -86,8 +87,13 @@ describe 'Milestone draggable', feature: true, js: true do ...@@ -86,8 +87,13 @@ describe 'Milestone draggable', feature: true, js: true do
visit namespace_project_milestone_path(project.namespace, project, milestone) visit namespace_project_milestone_path(project.namespace, project, milestone)
page.find("a[href='#tab-merge-requests']").click page.find("a[href='#tab-merge-requests']").click
scroll_into_view('.milestone-content')
drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1) drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1)
wait_for_ajax wait_for_ajax
end end
def scroll_into_view(selector)
page.evaluate_script("document.querySelector('#{selector}').scrollIntoView();")
end
end end
...@@ -3,11 +3,11 @@ module DragTo ...@@ -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}}});") 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 Timeout.timeout(Capybara.default_max_wait_time) do
loop until drag_active? loop while drag_active?
end end
end end
def drag_active? def drag_active?
page.evaluate_script('window.SIMULATE_DRAG_ACTIVE').zero? page.evaluate_script('window.SIMULATE_DRAG_ACTIVE').nonzero?
end end
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