Simulating keyboard events with JavaScript - javascript

How to simulate typing of some text in a contenteditable element?
I need to simulate everything as a user will events will do for test purposes.
I can fire an event with:
node.dispatchEvent(new KeyboardEvent('keydown', {key: 'A'}))
node.dispatchEvent(new KeyboardEvent('keyup', {key: 'A'}))
But it will not print A. So I have to use API:
node.innerHTML += 'A'
But the cursor will be in a different place, so I need to create a range.
let r = document.createRange()
r.selectNodeContents(editor)
r.collapse(false)
let s = window.getSelection()!
s.removeAllRanges()
s.addRange(r)
What about simulating arrows? How to simulate arrow up? Maybe there is some library to do this?

Testing Library is a great option for simulating user interactions. It can be paired with a companion library, user-event, to simulate user input such as click, dblClick, arrow up/down, tab, and much more. Here's an example of simulating user input in a text field:
import { screen } from '#testing-library/dom';
import userEvent from '#testing-library/user-event';
test('type', () => {
document.body.innerHTML = `<textarea />`;
// type 'Hello' followed by the enter key, followed by 'World!' into the textarea
userEvent.type(screen.getByRole('textbox'), 'Hello,{enter}World!');
// We expect that textarea to now have the given value
expect(screen.getByRole('textbox')).toHaveValue('Hello,\nWorld!');
});
You can delay the input, or use the skipClick option to input before clicking the element (in the case where the element auto-focuses, for example).
There are tons of examples here: https://testing-library.com/docs/ecosystem-user-event/. They also have wrappers for various frameworks such as react, angular, and puppeteer. So depending on what framework you use (or just vanilla js), you can:
Create your page/component
Simulate user input events with the user-event library
Assert certain conditions that you expect to be true
I also really like that the library makes it easy to mock server requests so I can fully test a component without worrying about its internals and how it's structured.

Related

How to make page search on electron like in Chrome or VS Code or Firefox? [duplicate]

Does the Electron application framework have built-in text search?
The quick-start application doesn't provide any apparent search functionality (e.g. using Ctrl-F or from the menu options). I would have expected this to be a BrowserWindow option (or an option of its WebContents), but I don't see anything helpful in the docs.
I know this is an old thread, but might still be relevant for people out there.
Had the same problem, and first fixed by using electron-in-page-search, but this component doesn't work properly with Electron 2 or greater.
Then finally found electron-find resolved my problem. Using with Electron 4.
You just add the component to your project:
npm install electron-find --save
Add a global shortcut in your Electron main process to send an event to the renderer in a ctrl+f:
globalShortcut.register('CommandOrControl+F', () => {
window.webContents.send('on-find');
});
And then you can add this to your page (the renderer process)
const remote = require('electron').remote;
const FindInPage = require('electron-find').FindInPage;
let findInPage = new FindInPage(remote.getCurrentWebContents());
ipcRenderer.on('on-find', (e, args) => {
findInPage.openFindWindow()
})
Hope that helps.
Try webContents.findInPage just added in the latest version.
There is an issue with the solution Robson Hermes offered. globalShortcut is, by definition, global, so the shortcut will be detected even when the app is not focused. This will result in the Ctrl+F shortcut being "stolen" from everywhere else.
I have found no ideal solution (see this issue on the electron repository), but a hacky one can be achieved by doing what Robson said and adding
win.on('focus', () => {
globalShortcut.register('CommandOrControl+F', () => windows.main.send('on-find'))
})
win.on('blur', () => {
globalShortcut.unregister('CommandOrControl+F')
}
Note that as seen here, this is not ideal and can lead to several issues:
Other applications can get a lock on the shortcut when you lose focus, i.e. the shortcut will magically stop working when you switch back to the app later.
Some apps can appear on screen without taking focus (spotlight I believe has this behavior) and during the app's appearance the shortcuts will still be captured by your application.
There's also gonna be those weird one in a thousand situations where somehow you switch focus and the shortcut is not removed.
Instead of using global shortcuts , use Accelerators ( normal Keyboard shortcut )
{
label : 'help',
click : function(){.
electron.shell.openExternal('http://....').
},
accelerator : 'CmdOrCtrl+ Shift + H'
}
The above shown is just an example of How to use accelerator

Search a Sencha TreePicker

I am using TreePicker from ExtJS 6.0.2. I would like to know how can I perform a search or query on the Picker data similar to this example - Fiddle. It has this property which is an built-in feature from combo-box:
queryMode: 'local'
I have made the TextField editable and I want to know if there is any inbuilt way to perform a search or do I have to write code to do it manually. For the manual way I tried to capture the change event of the TextField by writing the code for it in the config property of the TreePicker but failed to get the event to be fired. What am I missing here, please guide.
it seems this component has a very simple implementation and doesn't support any search functionality.
You could start to implement your need studying the Ext.form.field.ComboBox source code in order to "copy" only the behaviours you want.
For example you will see there's a picker's method to override to handle the change event. A very very simple "search on change" extension can be added with the following override:
...
onFieldMutation: function(e) {
var me = this,
store = me.getStore(),
rawValue = me.inputEl.dom.value;
store.filter('text', rawValue);
},
...
Fiddle

Using nightwatchjs to test a page with a CodeMirror component in it

I am attempting to edit a query on a page in a web application that uses a CodeMirror component. I am using nightwatchjs with selenium in chrome for the test. I can't set the underlying textarea element as it is not visible. Nightwatchjs setValue method doesn't work as the query is in an editable div.
Example Attempt:
module.exports = {
'Testing save code change' : function (browser) {
browser
.url("http://codemirror.net/index.html")
.waitForElementVisible("#demo", 3000)
.waitForElementVisible('.CodeMirror-code div:nth-child(3) .cm-string', 3000)
.setValue('.CodeMirror-code div:nth-child(3) .cm-string', 'New String');
}
};
Can anyone suggest an approach that will work?
You could click the CodeMirror element and then use keys() to do the typing.
browser.click('.CodeMirror textarea').keys('New String');
This will prepend the value to whatever is already inside the text area, so you'd have to do a bit more wrangling to delete whatever's there if you need to.
Alternatively, you could use nightwatch.js's execute() function to call CodeMirror's setValue() directly, relying on the fact that you can get the CodeMirror instance directly from the DOM element.
browser.execute(function(text) {
document.getElementsByClassName('CodeMirror')[0].CodeMirror.setValue(text);
}, ["you could also pass a variable in here"]);
Unlike the first option, it won't trigger all the same things as typing directly would (e.g. hints/autocompletions) but it'll correctly set an exact value.

Testing tab navigation order

In one of our tests, we need to make sure that the tab keyboard navigation inside a form is performed in the correct order.
Question: What is the conventional way to check the tab navigation order with protractor?
Currently we are solving it by repeating the following step for as many input fields existing in a form (code below):
check the ID of the currently focused element (using getId())
send TAB key to the currently focused element
Here is the example spec:
it("should navigate with tab correctly", function () {
var regCodePage = new RegCodePage();
browser.wait(protractor.ExpectedConditions.visibilityOf(regCodePage.title), 10000);
// registration code field has focus by default
expect(regCodePage.registrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Remember Registration Code
regCodePage.registrationCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.rememberRegistrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Request Code
regCodePage.rememberRegistrationCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.requestCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved to Cancel
regCodePage.requestCode.sendKeys(protractor.Key.TAB);
expect(regCodePage.cancelButton.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
// focus moved back to the input
regCodePage.cancelButton.sendKeys(protractor.Key.TAB);
expect(regCodePage.registrationCode.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
});
where regCodePage is a Page Object:
var RegCodePage = function () {
this.title = element(by.css("div.modal-header b.login-modal-title"));
this.registrationCode = element(by.id("regCode"));
this.rememberRegistrationCode = element(by.id("rememberRegCode"));
this.requestCode = element(by.id("forgotCode"));
this.errorMessage = element(by.css("div.auth-reg-code-block div#message"));
this.sendRegCode = element(by.id("sendRegCode"));
this.cancelButton = element(by.id("cancelButton"));
this.closeButton = element(by.css("div.modal-header button.close"));
};
module.exports = RegCodePage;
It is working, but it is not really explicit and readable which makes it difficult to maintain. Also, another "smell" in the current approach is a code duplication.
If the current approach is how you would also do it, I would appreciate any insights about making it reusable.
I think the PageObject should define a tab order list, since that is really a direct property of the page, and should be expressible as simple data. An array of items seems like a sufficient representation, so something like:
this.tabOrder = [ this.registrationCode, this.rememberRegistrationCode, this.requestCode, this.cancelButton ];
Then you need a bit of generic code that can check a tab order.
function testTabOrder(tabOrder) {
// Assumes TAB order hasn't been messed with and page is on default element
tabOrder.forEach(function(el) {
expect(el.getId()).toEqual(browser.driver.switchTo().activeElement().getId());
el.sendKeys(protractor.Key.TAB);
});
}
Then your test would be something like:
it('has correct tab order', function() {
var regCodePage = new RegCodePage(); // this should probably be in the beforeEach
testTabOrder(regCodePage.tabOrder);
});
Of course, this assumes each element has a "getId()" method that works. (That seems like a reasonable assumption to me, but some environments may not support it.)
I think this keeps the tab-order nicely isolated on the PageObject (so its easy to keep in sync with the page content and doesn't get lost in the code that verifies the order). The testing code seem "optimistic" (I suspect the real world will introduce enough problems that you will end up expanding this code a bit).
I haven't tried any of this yet, so feel free to downvote if this doesn't work. :)
Also, I believe the forEach loop will work as-is, but I wouldn't be surprised if it needs some more explicit promise handling to make the dependencies explicit.

Event handling in Dojo

Taking Jeff Atwood's advice, I decided to use a JavaScript library for the very basic to-do list application I'm writing. I picked the Dojo toolkit, version 1.1.1. At first, all was fine: the drag-and-drop code I wrote worked first time, you can drag tasks on-screen to change their order of precedence, and each drag-and-drop operation calls an event handler that sends an AJAX call to the server to let it know that order has been changed.
Then I went to add in the email tracking functionality. Standard stuff: new incoming emails have a unique ID number attached to their subject line, all subsequent emails about that problem can be tracked by simply leaving that ID number in the subject when you reply. So, we have a list of open tasks, each with their own ID number, and each of those tasks has a time-ordered list of associated emails. I wanted the text of those emails to be available to the user as they were looking at their list of tasks, so I made each task box a Dijit "Tree" control - top level contains the task description, branches contain email dates, and a single "leaf" off of each of those branches contains the email text.
First problem: I wanted the tree view to be fully-collapsed by default. After searching Google quite extensively, I found a number of solutions, all of which seemed to be valid for previous versions of Dojo but not the one I was using. I eventually figured out that the best solution would seem to be to have a event handler called when the Tree control had loaded that simply collapsed each branch/leaf. Unfortunately, even though the Tree control had been instantiated and its "startup" event handler called, the branches and leaves still hadn't loaded (the data was still being loaded via an AJAX call). So, I modified the system so that all email text and Tree structure is added server-side. This means the whole fully-populated Tree control is available when its startup event handler is called.
So, the startup event handler fully collapses the tree. Next, I couldn't find a "proper" way to have nice formatted text for the email leaves. I can put the email text in the leaf just fine, but any HTML gets escaped out and shows up in the web page. Cue more rummaging around Dojo's documentation (tends to be out of date, with code and examples for pre-1.0 versions) and Google. I eventually came up with the solution of getting JavaScript to go and read the SPAN element that's inside each leaf node and un-escape the escaped HTML code in it's innerHTML. I figured I'd put code to do this in with the fully-collapse-the-tree code, in the Tree control's startup event handler.
However... it turns out that the SPAN element isn't actually created until the user clicks on the expando (the little "+" symbol in a tree view you click to expand a node). Okay, fair enough - I'll add the re-formatting code to the onExpand() event handler, or whatever it's called. Which doesn't seem to exist. I've searched to documentation, I've searched Google... I'm quite possibly mis-understanding Dojo's "publish/subscribe" event handling system, but I think that mainly because there doesn't seem to be any comprehensive documentation for it anywhere (like, where do I find out what events I can subscribe to?).
So, in the end, the best solution I can come up with is to add an onClick event handler (not a "Dojo" event, but a plain JavaScript event that Dojo knows nothing about) to the expando node of each Tree branch that re-formats the HTML inside the SPAN element of each leaf. Except... when that is called, the SPAN element still doesn't exist (sometimes - other times it's been cached, just to further confuse you). Therefore, I have the event handler set up a timer that periodically calls a function that checks to see if the relevant SPAN element has turned up yet before then re-formatting it.
// An event handler called whenever a "email title" tree node is expanded.
function formatTreeNode(nodeID) {
if (dijit.byId(nodeID).getChildren().length != 0) {
clearInterval(nodeUpdateIntervalID);
messageBody = dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML
if (messageBody.indexOf("<b>Message text:</b>") == -1) {
messageBody = messageBody.replace(/>/g, ">");
messageBody = messageBody.replace(/</g, "<");
messageBody = messageBody.replace(/&/g, "&");
dijit.byId(nodeID).getChildren()[0].labelNode.innerHTML = "<b>Message text:</b><div style=\"font-family:courier\">"+messageBody+"</div>";
}
}
}
// An event handler called when a tree node has been set up - we changed the default fully-expanded to fully-collapsed.
function setupTree(theTree) {
dijit.byId("tree-"+theTree).rootNode.collapse();
messageNode = dijit.byId("tree-"+theTree).rootNode.getChildren();
for (pl = 0; pl < messageNode.length; pl++) {
messageNode[pl].collapse();
messageNode[pl].expandoNode.onclick = eval("nodeUpdateIntervalID = setInterval(\"formatTreeNode('"+messageNode[pl].id+"')\",200); formatTreeNode('"+messageNode[pl].id+"');");
}
}
The above has the feel of a truly horrible hack, and I feel sure I must have taken a wrong turn somewhere early on in my thought process. Can someone please tell me:
The correct way to go about putting nicely-formatted text inside a Dojo/Dijit Tree control.
The correct way to handle Dojo events, like where I can figure out what events are available for me to subscribe to.
A better JavaScript library to use (can I do what I want to with JQuery and avoid the all-around-the-houses approach seen above?).
PS: If you're naming a software project, give thought to its name's uniqueness in Google - I'm sure searching for "Dojo" documentation in Google would be easier without all the martial arts results getting in the way.
PPS: Firefox spellchecker knows how to spell "Atwood", correcting me when I put two 'T's instead of one. Is Jeff just that famous now?
I assume that you followed the dijit.Tree and dojo.data in Dojo 1.1 tutorial which directed you to pass the data to the tree control using a data store. That had me banging my head of a brick wall for a while.
Its not really a great approach and the alternative is not really well documented. You need to create a use model instead. I have included an example below of a tree model that I created for displaying the structure of an LDAP directory.
You will find the default implementation of the model in your dojo distribution at ./dijit/_tree/model.js. The comments should help you understand the functions supported by the model.
The IDirectoryService class the code below are stubs for server-side Java POJOs generated by Direct Web Remoting (DWR). I highly recommend DWR if you going to be doing a lot of client-server interaction.
dojo.declare("LDAPDirectoryTreeModel", [ dijit.tree.model ], {
getRoot : function(onItem) {
IDirectoryService.getRoots( function(roots) {
onItem(roots[0])
});
},
mayHaveChildren : function(item) {
return true;
},
getChildren : function(parentItem, onComplete) {
IDirectoryService.getChildrenImpl(parentItem, onComplete);
},
getIdentity : function(item) {
return item.dn;
},
getLabel : function(item) {
return item.rdn;
}
});
And here is an extract from the my JSP page where I created the model and used it to populate the tree control.
<div
dojoType="LDAPDirectoryTreeModel"
jsid="treeModel"
id="treeModel">
</div>
<div
jsid="tree"
id="tree"
dojoType="dijit.Tree" model="treeModel"
labelAttr="name"
label="${directory.host}:${directory.port}">
</div>

Categories

Resources