Drag and Drop with Protractor in AngularJS - angularjs

I appreciate there's quite a bit of stuff already been said about automating drag and drop as part of E2E testing. However after many, many hours of fiddling around, I cannot get any of the methods described to work...that is using Functions, coordinates etc etc. Oddly enough, console.log maintains the tests have passed, but the screenshots clearly show nothing has happened.
Screenshots shows a portion of the application
The user selects a paper and drags onto the image. As the drag 'starts' the grey overlay on the image clears and the paper is rendered on the room.
The code snippet shows one of the more simple ideas I've tried and I would be very pleased to receive any help going!
const JS_HTML5_DND = 'function e(e,t,n,i){var r=a.createEvent("DragEvent");r.initMouseEvent(t,!0,!0,o,0,0,0,c,g,!1,!1,!1,!1,0,null),Object.defineProperty(r,"dataTransfer",{get:function(){return d}}),e.dispatchEvent(r),o.setTimeout(i,n)}var t=arguments[0],n=arguments[1],i=arguments[2]||0,r=arguments[3]||0;if(!t.draggable)throw new Error("Source element is not draggable.");var a=t.ownerDocument,o=a.defaultView,l=t.getBoundingClientRect(),u=n?n.getBoundingClientRect():l,c=l.left+(l.width>>1),g=l.top+(l.height>>1),s=u.left+(u.width>>1)+i,f=u.top+(u.height>>1)+r,d=Object.create(Object.prototype,{_items:{value:{}},effectAllowed:{value:"all",writable:!0},dropEffect:{value:"move",writable:!0},files:{get:function(){return this._items.Files}},types:{get:function(){return Object.keys(this._items)}},setData:{value:function(e,t){this._items[e]=t}},getData:{value:function(e){return this._items[e]}},clearData:{value:function(e){delete this._items[e]}},setDragImage:{value:function(e){}}});if(n=a.elementFromPoint(s,f),!n)throw new Error("The target element is not interactable and need to be scrolled into the view.");u=n.getBoundingClientRect(),e(t,"dragstart",101,function(){var i=n.getBoundingClientRect();c=i.left+s-u.left,g=i.top+f-u.top,e(n,"dragenter",1,function(){e(n,"dragover",101,function(){n=a.elementFromPoint(c,g),e(n,"drop",1,function(){e(t,"dragend",1,callback)})})})})';
describe('Drag and Drop Test', function() {
it('should drag', function () {
var e1 = element(by.xpath('html/body/webapp-app/div/div/webapp-johnlewis-visualiser/div/div[2]/div/digitalbridge-shortlist/div/div/ul/li[1]/a/img'));
var e2 = element(by.css('.db-project-designer'));
element(by.xpath('html/body/webapp-app/div/div/webapp-johnlewis-visualiser/div/div[2]/div/digitalbridge-shortlist/div/div/ul/li[1]/a/img')).click();
//element(by.xpath('html/body/webapp-app/div/div/webapp-johnlewis-visualiser/div/div[1]/div/div/digitalbridge-project/div/digitalbridge-project-designer/canvas')).click();
browser.driver.actions().dragAndDrop(e1.getWebElement(),e2.getWebElement()).perform();
browser.sleep(2000);
});
});
The constant is showing an error 'const' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz). (W104) - I do have ES6 installed in Node_Modules.
I inserted the click line to see if pre-selecting the item made any difference...it didn't!
Thank you
David

Try this library https://github.com/SunGard-Labs/sg-protractor-tools
The library also includes functions that simplify common tasks like
Scrolling to an element
Drag and drop
Waiting for DOM elements to become visible or hidden

module.exports = function simulateDragAndDrop(sourceNode, destinationNode) {
const EVENT_TYPES = {
DRAG_END: 'dragend',
DRAG_START: 'dragstart',
DROP: 'drop'
};
function createCustomEvent(type) {
const event = new CustomEvent('CustomEvent');
event.initCustomEvent(type, true, true, null);
event.dataTransfer = {
data: {
},
setData: function(type, val) {
this.data[type] = val;
},
getData: function(type) {
return this.data[type];
}
};
return event;
}
function dispatchEvent(node, type, event) {
if (node.dispatchEvent) {
return node.dispatchEvent(event);
}
if (node.fireEvent) {
return node.fireEvent('on' + type, event);
}
}
const event = createCustomEvent(EVENT_TYPES.DRAG_START);
dispatchEvent(sourceNode, EVENT_TYPES.DRAG_START, event);
const dropEvent = createCustomEvent(EVENT_TYPES.DROP);
dropEvent.dataTransfer = event.dataTransfer;
dispatchEvent(destinationNode, EVENT_TYPES.DROP, dropEvent);
const dragEndEvent = createCustomEvent(EVENT_TYPES.DRAG_END);
dragEndEvent.dataTransfer = event.dataTransfer;
dispatchEvent(sourceNode, EVENT_TYPES.DRAG_END, dragEndEvent);
}
You can call it from you code like this
browser.executeScript(dragAndDrop, element, targetArea);

Related

Integrate highcharts-custom-events with React

I've installed a highcharts-custom-events package to handle custom events(dblclick).
like the code below
var Highcharts = require('highcharts'),
HighchartsCustomEvents = require('highcharts-custom-events')(Highcharts);
But after adding this code, even the existing click is also not working.
Please help me to implement custom events to react.
Here is an example with implemented custom events in Highcharts with using react wrapper.
import CustomEvents from "highcharts-custom-events";
CustomEvents(Highcharts);
//require('highcharts-custom-events')(Highcharts);
Both above ways work - import and require.
Demo: https://codesandbox.io/s/highcharts-react-demo-1rtxl
If this wouldn't help - could you reproduce your case in the online editor which I could work on?
I was having the same problem using this lib, it was breaking the standard single click, I believe this is a duplicate post from this one.
On that post there is a function implementation of double click, that solution also breakes the single click, the thing is that you can add the single click as a condition inside double click function:
Fisrt define the settings:
var doubleClicker = {
clickedOnce : false,
timer : null,
timeBetweenClicks : 400
};
Then define a 'double click reset' function in case the double click is not fast enough and a double click callback:
// call to reset double click timer
var resetDoubleClick = function() {
clearTimeout(doubleClicker.timer);
doubleClicker.timer = null;
doubleClicker.clickedOnce = false;
};
// the actual callback for a double-click event
var ondbclick = function(e, point) {
if (point && point.x) {
// Do something with point data
}
};
Highcharts settings example
series: [{
point: {
events: {
click: function(e) {
if (doubleClicker.clickedOnce === true && doubleClicker.timer) {
resetDoubleClick();
ondbclick(e, this);
} else {
doubleClicker.clickedOnce = true;
doubleClicker.timer = setTimeout(function(){
resetDoubleClick();
}, doubleClicker.timeBetweenClicks);
}
}
}
}
}]

Quill Editor, The 'on' method works only once

I have the following, script for the QuillEditor(I have multiple editors):
var editors = {};
function editors() {
var toolbarOptions = [
[{'list': 'ordered'}, {'list': 'bullet'}],
];
data.forEach(function (el) {
editors[el] = new Quill(el['editor'], {modules: {toolbar: toolbarOptions}, theme: 'snow', scrollingContainer:el['quill'] });
editors[el].on('text-change', copyText(el['text'], editors[el]));
});
}
}
function copyText(text, editor) {
text.innerHTML = editor.root.innerHTML;
console.log(text.innerHTML)
}
To use it in backend, I'm copying the text from the editor to a textarea copyText(el['text'].
I need to always work, but it is coping text/html, only once when the function is executed. I'm expecting editors[el].on('text-change', to work like an event listener.
The scrollingContainer, doesn't a scroll. I check that the target exist, is the parent of the editor.
I am not sure if this part of the error but you have an extra } after after the editors function.
The main problem here is that instead of setting the event listener you are running the event listener which is why it is running only once.
So change the event-listener line to:
editors[el].on('text-change', function () {
copyText(el['text'], editors[el]);
});
I don't generally like creating functions in other functions and especially inside loops so I would recommend creating a function factory function that will create a new function for each element.
function createListener(text, editor) {
return function(){
copyText(text, editor);
};
}
And call it like this:
editors[el].on('text-change', createListener(el['text'], editors[el]));

Mock document.activeElement in Jasmine test

I have the following function:
function focusIsNotInInput() {
// If the element currently in focus is of a certain type, then the key handler shouldn't run
var currentlyInFocus = $window.document.activeElement;
var blacklist = ['INPUT', 'TEXTAREA', 'BUTTON', 'SELECT', 'IFRAME', 'MD-OPTION'];
return !blacklist.some(function (nodeName) {
return nodeName === currentlyInFocus.nodeName;
});
}
And I need to mock that the element currently in focus is of one of the specified types, but can't get it to work.
I've tried injecting window, like this:
beforeEach(function() {
var $windowMock;
inject(function(_$window_) {
$windowMock = _$window_;
$windowMock.document.activeElement.nodeName = 'INPUT';
});
});
But when the code above runs, the active element is always still body. It's getting overwritten. I have also tried creating an element and setting focus on it:
var elementInFocus = $('<input>');
this.elem.append(elementInFocus);
elementInFocus.triggerHandler('focus');
elementInFocus.focus();
But it's the same, body is always in focus, what ever I do.
I had some trouble with this too, a possible solution (worked for me) is to add a spyOn(element, 'focus') -- here's a reference: How do I check if my element has been focussed in a unit test
My successful solution:
const htmlItem = fixture.nativeElement;
const searchBar = htmlItem.querySelector('.search-box');
let focusSpy = spyOn(searchBar, 'focus');
searchBar.focus();
expect(focusSpy).toHaveBeenCalled();

Capturing click events on clusters with markercluster and angular-leaflet-directive

I'm playing with the angular-leaflet-directive, and getting the marker names from a mouse click is straight forward. I just listen for the leafletDirectiveMarker.click event and then access args.markerName.
angular-leaflet-directive also works with markercluster, so I can cluster markers that have the same coordinates or ones that are close by. However, I would like to do the following, but it is not clear from the documentation on how to do it:
Make user double-click on cluster to zoom in. Currently doing a single click on a cluster will zoom in on the markers. see example.
How to listen for click event on cluster and get all marker names in the cluster.
The documentation for clustermarker has a cluster event:
markers.on('clusterclick', function (a) {
console.log('cluster ' + a.layer.getAllChildMarkers().length);
});
But I'm not sure what event I should be listening to using angular-leaflet-directive.
As far as your first question goes, you'll have to hook the doubleclick and pass it the fire('click') command after overriding the usual click event. Probably more trouble than its really worth, especially on mobile - and not something I can easily solve.
Regarding your second question, I have just solved it.
$scope.openMarker is a reference to an ng-click event in my jade template that is attached to an ng-repeat which pulls images and their id's from the database.
$scope.openMarker = function(id) {
var _this = [];
_this.id = id;
leafletData.getMarkers()
.then(function(markers) {
$scope.london = {
lat: $scope.markers[_this.id].lat,
lng: $scope.markers[_this.id].lng,
zoom: 19
};
var _markers = [];
_markers.currentMarker = markers[_this.id];
_markers.currentParent = _markers.currentMarker.__parent._group;
_markers.visibleParent = _markers.currentParent.getVisibleParent(markers[id]);
_markers.markers = markers;
return _markers;
}).then(function(_markers){
if (_markers.visibleParent !== null) {
_markers.visibleParent.fire('clusterclick');
} else {
_markers.currentMarker.fire('click');
}
return _markers;
}).then(function(_markers){
_markers.currentParent.zoomToShowLayer(_markers.markers[ _this.id ], function() {
$scope.hamburg = {
lat: $scope.markers[_this.id].lat,
lng: $scope.markers[_this.id].lng,
zoom: 19
};
if (_markers.currentMarker !== null) {
_markers.currentMarker.fire('click');
} else {
_markers.visibleParent.fire('clusterclick');
_markers.currentMarker.fire('click');
}
});
});
};
You can read more about how I came to this solution here at github.
Much like many people, I too had a long search with no results. While experimenting with another method, I came across this:
$timeout(function(){
leafletData.getLayers().then(function(layers) {
$scope.markerClusterGrp = layers.overlays.locations;
var clusters = $scope.markerClusterGrp.getLayers();
$scope.markerClusterGrp.on('clustermouseover', function (a) {
var clusterObjects = a.layer.getAllChildMarkers();
console.log(clusterObjects);
});
$scope.markerClusterGrp.on('clusterclick', function (a) {
var clusterObjects = a.layer.getAllChildMarkers();
console.log(clusterObjects);
});
});
},1000);
It works the same, the difference is that it requires a timeout in order to wait for the layer to render with all markers (my understanding, correct me if wrong :-) ).
I hope this helps anyone searching for an angular solution. Just remember to include $timeout in your controller dependencies.

Drupal.attachBehaviours with jQuery infinitescroll and jQuery masonry

I am a little desperate here. I have been reading everything I was able to find on Drupal.behaviours but obviously its still not enough. I try running a masonry grid with the infinitescroll plugin to attach the new images to the masonry. This works fine so far. The next thing I wanted to implement to my website is a hover effect (which shows information on the images) and later fancybox to show the images in a huger size.
(function ($) {
Drupal.behaviors.views_fluidgrid = {
attach: function (context) {
$('.views-fluidgrid-wrapper:not(.views-fluidgrid-processed)', context).addClass('views-fluidgrid-processed').each(function () {
// hide items while loading
var $this = $(this).css({opacity: 0}),
id = $(this).attr('id'),
settings = Drupal.settings.viewsFluidGrid[id];
$this.imagesLoaded(function() {
// show items after .imagesLoaded()
$this.animate({opacity: 1});
$this.masonry({
//the masonry settings
});
});
//implement the function of jquery.infinitescroll.min.js
$this.infinitescroll({
//the infinitescroll settings
},
//show new items and attach behaviours in callback
function(newElems) {
var newItems = $(newElems).css({opacity: 0});
$(newItems).imagesLoaded(function() {
$(newItems).animate({opacity: 1});
$this.masonry('appended', newItems);
Drupal.attachBehaviours(newItems);
});
});
});
}
};
})(jQuery);
Now I read that I need to Reattach the Drupal.behaviours if I want the hover event to also take place on the newly added content.
(function ($) {
Drupal.behaviors.imgOverlay = {
attach: function (context) {
var timeout;
$('.img_gallery').hover(function() {
$this = $(this);
timeout = setTimeout(change_opacity, 500);
}, reset_opacity);
function change_opacity() {
//set opacity to show the desired elements
}
function reset_opacity() {
clearTimeout(timeout);
//reset opacity to 0 on desired elements
}
}
};
})(jQuery)
Where do I now write the Drupal.attachBehaviours() to make it work actually? Or is there some other error I just dont see atm? I hope I wrote the question so that its understandable and maybe it also helps somebody else, since I experienced that there is no real "official" running Version of this combination in drupal 7.
Ok, the solution is actually pretty simple. When writing it correctly than it also runs. its of course not Drupal.attachBehaviours() but Drupal.attachBehaviors() . So this combination now works and I am finally relieved :).

Resources