cy.trigger() throws "cy.trigger() can only be called on a single element.in Cypress - drag-and-drop

For my application, I have to select multiple items and perform drag and drop operation, but I got the following error.
cy.trigger() can only be called on a single element. Your subject contained 2 elements.
cy.get('item1').click();
cy.get('item2')
.click({ctrlkey:true})
.trigger('dragstart',{dataTransfer}))
.trigger('drag',{});
cy.get('targetelement')
.trigger('dragover', { dataTransfer })
.trigger('drop', { dataTransfer })
.trigger('dragend', { dataTransfer });

Every drag and drop has some variation, so this might not work for you. See Issue: Move the cursor (Drag and Drop) #845
There isn't a generic answer for this - it depends on what your app is bound to, and what events and properties it's listening for.
If the dragstart event listener is attached to document you can use
cy.document()
.its('documentElement')
.trigger('dragstart', { dataTransfer: {} });
I used this CodePen Mouse Droppings as a basic multi-select drag-and-drop example app.
It has different events, which I've substituted below your events in the test,
dragover changed to dragenter
drop changed to dragleave
but you will use the events applicable to your app.
Hopefully the key principle cy.document().trigger('dragstart', { dataTransfer: {} }) also works with your app.
My test
it('drags and drops multiple items', () => {
cy.visit('http://127.0.0.1:5500/dist/index.html') // CodePen exported and run locally
cy.contains('Item 0').click(); // select 1st item
cy.contains('Item 2').click({ctrlKey:true}); // also select 3rd item
cy.document()
.its('documentElement')
.trigger('dragstart', { dataTransfer: {} })
cy.get('ol').eq(2) // get the target
// .trigger('dragover', { dataTransfer: {} })
.trigger('dragenter', { dataTransfer: {} })
// .trigger('drop', { dataTransfer: {} })
.trigger('dragleave', { dataTransfer: {} })
.trigger('dragend', { dataTransfer: {} });
cy.get('ol').eq(2).contains('Item 0'); // verify 1st item moved
cy.get('ol').eq(2).contains('Item 2'); // verify 3rd item moved
})
The result is items 0 & 2 from the first list end up on the third list.

Related

How to check if element exists & keep track of it's mutations once found using mutationObserver

First & foremost excuse my ignorance as im still trying to wrap my head aroud mutationObservers
I'm creating a chrome extension that detects certain words from an ordered list. when content script runs at first the ordered list element isn't rendered so I used a mutationObserver to capture it when it's created however as I scroll through the page the ordered list keeps increasing in size via API calls, how can I keep track of the new results rendered under the ordered list?
What i've done so far
const waitForElm = async (selector) => {
return new Promise((resolve) => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver((mutations) => {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
});
};
waitForElm("ol.artdeco-list").then((elm) => {
console.log("Element is ready");
// Tried to create a new mutationObserver here.
});
I've tried creating another mutationObserver once promise was resolved on the same element that was found "ol.artdeco-list", but that didn't seem to work...
Any advice??

How to know what suggestion item is selected in vscode

I complete a vscode extension by vscode.languages.registerCompletionItemProvider(selector, new FuncCompletionProvider(),'.')
I want to listen which suggestion is selected. In the image below,when I click the current item I want to get the CompletionItem Info.
I tried to use the resolveCompletionItem function, but before the suggestion is selected resolveCompletionItem was triggered.
I tried to use the resolveCompletionItem function, but before the suggestion is selected resolveCompletionItem was triggered.
It appears this is intentional. Per their docs:
Note that this function is called when completion items are already showing in the UI or when an item has been selected for insertion
'selected' meaning selected in the list, not committed
The recommended way to gain insight on when a CompletionItem is inserted is using the CompletionProvider#command property:
An optional command that is executed after inserting this completion. Note that additional modifications to the current document should be described with the additionalTextEdits-property.
Example usage:
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.languages.registerCompletionItemProvider('html', new MyCompletionProvider),
vscode.commands.registerCommand("doTheThing", () => {
console.log('did the thing!!');
});
);
}
class MyCompletionProvider implements vscode.CompletionItemProvider {
provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
const myHTMLCompletionItem: vscode.CompletionItem = new vscode.CompletionItem("myHTML");
myHTMLCompletionItem.command = {
title: '',
command: 'doTheThing'
};
return new vscode.CompletionList([myHTMLCompletionItem]);
}
}

Vue/Vuex/GSAP-Animation: Add DOM elements to store

In a Vue project, I am looking for a way to save DOM elements to the store. Those elements shall then be animated with GSAP.
Unfortunately, there is a bit of a problem with when the DOM is ready (so I can use document.querySelector) and when Vue's transition system is firing.
the store has about this structure:
const store = new Vuex.Store({
state: {
settings: {
ui: {
btns: {
cBtnNavMain: {
el: document.querySelector('.c-btn__nav--main') // does not work, because DOM is not yet ready
[...]
}
}
}
}
},
mutations: {
// Add DOM element later via a mutation?
addDomElementToStore: (state, obj) => {
console.log("MUTATION addDomElementToStore, obj =", obj) // shows `null`
obj.el = obj.domEl
}
}
}
Then, in App.vue (loaded by main.js) there is basically this script:
created() {
console.log("App.vue created")
}
mounted() {
console.log("App.vue created")
}
methods: {
beforeEnter(el) {
console.log("BeforeEnter called, obj")
// This could be the place to add DOM to the store, but how?
// I tried a mutations like this (see above in store.mutations):
this.$store.commit('addDomElementToSettings', {
el: this.$store.state.settings.ui.btns.cNavMainBtn.el,
domEl: document.querySelector('.c-btn__nav--main')
})
// ...but won't work though, the console shows, obj params are empty
}
[...]
}
The console result shows something like this:
App.vue created
BeforeEnter called // <-- beginEnter before mounted called!
MUTATION addDomElementToStore, obj = {el: null, domElem: null}
App.vue mounted
Since "beforeEnter" is called after created, but BEFORE mounted (which is, where the DOM would be easily accessible), the DOM is not really accessible yet, it seems. So I thought, I use "beforeEnter" to assign new DOM elements to store.settings using a mutation. But that doesn't work at all - and GSAP eventually has do DOM elements to animate on.
What do I have to do to get my DOM elements saves to the settings, so that I do not have to use document.querySelector all the time, when I want to address a DOM element with GSAP?
Why don't try you using the mounted() lifecycle method to fire the mutation that stores DOM element selections in vuex state? That way you know something exists to select.

SAPUI5 keydown in onInit not called

I have in my onInit in the controller following code:
$("input").on("keydown", (
function(evt) {
if (evt.keyCode == 17 && (evt.ctrlKey)) {
evt.preventDefault();
that.onMoveStorageBin();
}
So idea is, when I press F11 button, onMoveStorageBin function would be executed. But it does not go into the onInit when I press any button. Where is the correct place to put this handling of the keydown?
Thanks,
Tim
You should execute your code snippet on the onAfterRendering lifecycle hook. This is because in the onInit, the jQuery call will not be able to find any input (as the elements corresponding to your view were not yet added to the DOM).
Based on the description and code, I understand that you need to check if the F11 + CTRL was pressed (hence the check is evt.keyCode == 122 && evt.ctrlKey); if you want a sinple F11, the you only need to check evt.keyCode == 122.
You can find a working fiddle here: https://jsfiddle.net/8kcs8xch/4/
onAfterRendering: function() {
jQuery("input").on("keydown",
function(evt) {
if (evt.keyCode == 122 && evt.ctrlKey) {
evt.preventDefault();
sap.m.MessageToast.show('Alert');
}
});
}
This approach has the following limitations:
It uses the DOM directly and makes assumptions on how UI5 controls are rendered.
It applies the listener on ALL existing inputs. You can mediate this by using other selectors as well (e.g. CSS).
A considerably better solution would be to create a custom control which has this event. You can find an example here: https://jsfiddle.net/8kcs8xch/3/
Basically you need to listen to the keydown event by declaring a DOM handler in your new control. You can then define a custom UI5 event (I simply called it alert) based on this DOM event:
var CustomInput = sap.m.Input.extend("CustomInput", {
metadata: {
events: {alert: {}}
},
onkeydown: function(evt) {
if (evt.keyCode == 122 && evt.ctrlKey) {
// only prevent the default of the DOM event if the
// UI5 event's preventDefault is called as well.
if (!this.fireEvent("alert", {}, true)) {
evt.preventDefault();
}
}
},
renderer: {}
});

jstree select multiple nodes fire one event

I have a tree of files and am trying to create an editor for. When one node is selected a panel is loaded for that specific node. If multiple nodes are selected a more generic panel be loaded for all selected nodes.
The problem is, when selecting multiple nodes the "select_node.jstree" is fired for each node.
a snippet of related code...
$("#tree).on("select_node.jstree", function(event, node) {
var selected = node.instance.get_selected();
if(selected.length === 1) {
$("#editor").load(url);
} else if(selected.length > 1) {
$.post(url, {
data: selected
}, function(res) {
$("#editor").html(res);
});
}
});
So... with this if I select 5 items I am doing 1 GET and 4 POSTS.
What I am looking for is 1 GET (the first node selected) and 1 POST (the collection of selected nodes)...
Would it be just a timeout? I feel like I am missing something obvious. I am far from a good programmer, so any direction would be appreciated.
So, I was able to use doTimeout library to manage this. It works, not sure if it is optimal.
https://github.com/cowboy/jquery-dotimeout
$('#tree').on("select_node.jstree", function(event, node) {
$.doTimeout('select', 500, function () {
var selected = node.instance.get_selected();
if(selected.length === 1) {
$('#editor').load(url);
} else if(selected.length > 1) {
$.post(url, {
data: selected
}, function(res) {
$('#editor').html(res);
});
}
});
});