How do I click and type on element in the shadow dom using intern - shadow-dom

I have a Google Polymer Project that I am trying to implement functional testing with Intern, which can be found here:
https://theintern.github.io/
The code I'm trying to test is in the Shadow DOM, as the page uses a variety of web components. The main goal of the test is to fill in a form and post the data. The structure of the code is as follows:
<app>
#shadow-root
<login-form>
#shadow-root
<card>
#shadow-root
<paper-input>
#shadow-root
<iron-input>
email
</iron-input>
</paper-input>
<paper-input>
#shadow-root
<iron-input>
password
</iron-input>
</paper-input>
<paper-button>
submit
</paper-button>
</card>
</login-form>
</app>
Where "#shadow-root" implies how the code is represented in a browser.
I have used
document.querySelector('app').document.querySelector('login-form')...etc
to access the specific elements, but cannot work out a way to click the elements, type in the fields, and click on the submit button upon completion. Is there any way to do this in Intern?

I think you should look at intern-examples to know how to write functional tests with Intern
For example, here is how you should write a test (open a url, find an element by id, click on an element, type something..)
define([
'intern!object',
'intern/chai!assert',
'require'
], function (registerSuite, assert, require) {
var url = '../../index.html';
registerSuite({
name: 'Todo (functional)',
'submit form': function () {
return this.remote
.get(require.toUrl(url))
.findById('new-todo')
.click()
.pressKeys('Task 1')
.pressKeys('\n')
.pressKeys('Task 2')
.pressKeys('\n')
.pressKeys('Task 3')
.getProperty('value')
.then(function (val) {
assert.ok(val.indexOf('Task 3') > -1, 'Task 3 should remain in the new todo');
});
}
});
});

Related

Playwright: Two elements with the same name, how to fill the one that is visible?

I have an Ionic React app that uses react-router 5 to display various pages.
The app defaults to a login form, but if users go to the support page, there is a contact form.
I'm trying to test the contact form in Playwright with code like this:
await page.fill('input[name=mail]', 'playwright#example.com');
However, I'm getting an error:
=========================== logs ===========================
waiting for selector "input[name=mail]"
selector resolved to 2 elements. Proceeding with the first one.
selector resolved to hidden <input name="mail" type="email" placeholder="" autocorr…/>
elementHandle.fill("playwright#example.com")
waiting for element to be visible, enabled and editable
element is not visible - waiting...
The problem is that Playwright is seeing the first email address input on the login form, which is not visible, and then trying to fill that in instead of the visible email address input on the contact page.
Obviously, I never want to fill in an element that isn't visible, so how can I force playwright to only try to fill in visible elements?
You can use page.locator to check for visibility first and then fill in the value.
await page.locator('input[name=mail] >> visible=true').fill('playwright#example.com');
To make tests easier to write, wrap this in a helper function:
async fillFormElement(
inputType: 'input' | 'textarea',
name: string,
value: string,
) {
await this.page
.locator(`${inputType}[name=${name}] >> visible=true`)
.fill(value);
}
It can be done in this way, if you are trying to check is element visible. Even you have two same elements with one selector one hidden one visible.
const element = await page.waitForSelector(selector, {state:visible});
await element.fill(yourString);

TableSorter : how to export results to csv?

TableSorter is a great jquery script to sort html tables with many options.
But I don't know how to add a simple 'export to csv' button (or link) to get a file containing the records of my table (with no special formatting).
I know the Output Plugin but it seems far too complex to me.
Thanks by advance for your help !
Ted
It's actually not complicated, it only looks intimidating because of all the options. The output widget can output csv, tsv, any other separated (space, semi-colon, etc) values, javascript array or JSON.
If you are just using basic functionality, the default settings will:
Output csv to a popup window
Only include the last header row
Only include filtered rows (so all rows if the filter widget isn't even being used)
Will only output the table cell text (ignores HTML)
All you would need is this code (demo):
HTML
<button class="download">Get CSV</button>
<table class="tablesorter">
....
</table>
Script
$(function () {
var $table = $('table');
$('.download').click(function(){
$table.trigger('outputTable');
});
$table.tablesorter({
theme: 'blue',
widgets: ['zebra', 'output']
});
});
I created another demo, showing all options, with a set of radio buttons which allow the user to choose between sending the output to a popup window, or downloading the file.
HTML
<label><input data-delivery="p" name="delivery" type="radio" checked /> popup</label>
<label><input data-delivery="d" name="delivery" type="radio" /> download</label>
<button class="download">Get CSV</button>
Script
var $table = $('table');
$('.download').click(function(){
// get delivery type
var delivery = $('input[name=delivery]:checked').attr('data-delivery');
$table.data('tablesorter').widgetOptions.output_delivery = delivery;
$table.trigger('outputTable');
});
So, you can make it as simple or complex as you want (see the actual output widget demo which allows the user to set almost all the options).

JQM 1.4 - What is the preffered way to use pagecontainer event on a specific page/selector?

jQuery Mobile v1.4, we are february 2014.
So, I've read here (gihub) that we are supposed to make an if statement on catched pagecontainer events to assume if the page loaded is the one intended.
Here is a little scenario trying to understand the intended behavior of the new pageContainer widget and it's intended use.
Simple as that, a login page, pre-fetch a welcome page programatically, then switch to welcome page on succesful login. Welcome page have some JS script to animate the page, must be launched only when page is visible. How do we do that ?
Here are the results I got while investigating the pagecontainer events through the console. My goal here is to find a way to detect that my welcome (any page in fact) page is active/visible.
I used this format as query for the sake of understanding:
$( 'body' ).on( 'pagecontainerbeforeload', function( event, ui ) {
console.log("beforeload");
console.log(event);
console.log(ui);
});
So fresh start, when I load a page & JQM for the first time (ie. /Users/login)
Events usable :
pagecontainercreate => empty ui
PCbeforetransition => ui.toPage usable
PCshow => only get a ui.prevPage (which is empty in that case)
PCtransition => ui.toPage usable
Now, these are always launched even if you disabled the transition effects ( See api )
Ok, then I want to programatically load a page (pre-fetch it), I do : (I want /Users/welcome)
$("body").pagecontainer("load", "/Users/welcome");
I get these event launched (the page is not yet visible):
PCbeforeload => I get a url which I could use to identify the page..
PCload => pretty much the same data as PCbeforeload
All right, now I go change my page : (to /Users/welcome)
$("body").pagecontainer("change", "/Users/welcome");
I get these events triggered:
PChide => ui.nextPage is the same as a ui.toPage...
PCbeforetransition => ui.toPage usable
PCshow => only gives ui.prevPage
PCtransition => ui.toPage present as expected
Fine, now I'm pretty sure the only pagecontainer event I want to use to be sure that this page is loaded is pagecontainertransition. Here is what I implemented on every page that needs to launch JS :
Set id of the page container (PHP)
<div data-role="page" data-theme='a' id="<?php echo $this->id_url()?>">
...at the end of the page (JS)
$( 'body' ).on( 'pagecontainertransition', function( event, ui ) {
if(ui.toPage[0] == $('#'+id_url )[0] ) {
functionToLaunchWhenPageShowUp();
}
} );
Now, as you can see, I'm referring to ui.toPage 1st child [0] to compare it to $('.selector') 1st child [0] . Is that the right way to do that ? I mean, the intended way by the new API. Thanks to share your knowledge ;)
I managed to do something that works, is relatively simple, and as close I could to the DRY principle (don't repeat yourself).
In the order they are "loaded" :
JS script in < head > of document
<script type="text/javascript" src="/js/jquery-2.1.0.min.js"></script>
<script type="text/javascript">
(function(){
//Global settings for JQM 1.4.0
$( document ).on( "mobileinit", function() {
//apply overrides here
$.mobile.defaultPageTransition = 'flip';
});
// The event listener in question !
$( document ).ready(function () { //..only launched once the body exist
// The event listener in question :
$( 'body' ).on( 'pagecontainertransition', function( event, ui ) {
//gets the id you programatically give to your page
id_url = $( "body" ).pagecontainer( "getActivePage" )[0].id;
//compare the actual event data (ui.toPage)
if( ui.toPage[0] == $('#'+id_url )[0] ) {
// failsafe
if ( typeof( window[ id_url ].initPage ) === "function" ) {
// call the dynamically created init function
window[ id_url ].initPage();
}
}
} );
});
document.loadPage = function( url ){
$( "body" ).pagecontainer( "load", url , options);
};
document.changePage = function( url ){
$( "body" ).pagecontainer( "change", url , options);
};
})();
</script>
<script type="text/javascript" src="/js/jquery.mobile-1.4.0.min.js"></script>
Start of every returned page
<div data-role="page" data-theme='a' id="<?php echo $this->id_url()?>">
<div data-role="content">
<script type="text/javascript">
// we create dynamically a function named after the id you gave to that page
// this will bind it to window
this[ '<?php echo $this->id_url() ?>' ].initPage = function(){
// put anything you want here, I prefer to use a call
// to another JS function elsewhere in the code. This way I don't touch the
// settings anymore
NameOfThisPage.launchEverythingThatNeedsTo();
};
</script>
...
Here's the description for this code. First, I get one place for all those global query, JQM already forced me to put stuff in-between jquery.js & jquery.mobile.js, so let's use that.
Along with the global JQM settings, I use only one event listener bind to the body/document/(whatever it'll be in the future). It's only launched once the body exist.
Now, here's where the fun begins. I programatically give an id to every pages the server returns (namely the script route with _ instead of / ). You then create a function named after that id, it's attached to the window, (I suppose you could put it elsewhere..).
Then back to the event listener, on transition, you get that id you've set through pagecontainer( "getActivePage" ) method, use that id to grab the page jQuery style, then compare it with the data returned by the event listener.
If success, use that id to launch the init function you've putted in your page. There's a failsafe in case you don't put an init script in page.
Bonus here, are those document.loadPage / changePage . I've putted them there in case the methos to change page changes. One place to modify, and it'll apply to the entire app. That's DRY ^^
All in all, if you have comment on a way to improve this method, please share. There's a big lack of example for v1.4 methods (along with a bit of confusion with v1.3 examples). I've tried to share my discoveries the best I could (ps. I need those rep points :P )

Is there any way to control the layout of the close button on a jQuery Mobile custom select menu?

I have a custom select menu (multiple) defined as follows:
<select name="DanceStyles" id="DanceStyles" multiple="multiple" data-native-menu="false">
Everything works fine except that I want to move the header's button icon over to the right AND display the Close text. (I have found some mobile users have a problem either realising what the X icon is for or they have trouble clicking it, so I want it on the right with the word 'Close' making too big to miss.) There don't seem to be any options for doing that on the select since its options apply to the select bar itself.
I have tried intercepting the create event and in there, finding the button anchor and adding a create handler for that, doing something like this (I have tried several variations, as you can see by the commenting out):
$('#search').live('pagecreate', function (event) {
$("#DanceStyles").selectmenu({
create: function (event, ui) {
$('ul#DanceStyles-menu').prev().find('a.ui-btn').button({
create: function (event, ui) {
var $btn = $(this);
$btn.attr('class', $btn.attr('class').replace('ui-btn-left', 'ui-btn-right'));
$btn.attr('class', $btn.attr('class').replace('ui-btn-icon-notext', 'ui-btn-icon-left'));
// $(this).button({ iconpos: 'right' });
// $btn.attr('class', $btn.attr('class').replace('ui-btn-icon-notext', 'ui-btn-icon-left'));
// // $btn.attr('data-iconpos', 'left');
$(this).button('refresh');
}
});
}
});
});
So I have tried resetting the button options and calling refresh (didn't work), and changing the CSS. Neither worked and I got weird formatting issues with the close icon having a line break.
Anyone know the right way to do this?
I got this to work cleanly after looking at the source code for the selectmenu plugin. It is not in fact using a button; the anchor tag is the source for the buttonMarkup plugin, which has already been created (natch) before the Create event fires.
This means that the markup has already been created. My first attempt (see my question) where I try to mangle the existing markup is too messy. It is cleaner and more reliable to remove the buttonMarkup and recreate it with my desired options. Note that the '#search' selector is the id of the JQ page-div, and '#DanceStyles' is the id of my native select element. I could see the latter being used for the id of the menu, which is why I select it first and navigate back up and down to the anchor; I couldn't see any other reliable way to get to the anchor.
$('#search').live('pagecreate', function (event) {
$("#DanceStyles").selectmenu({
create: function (event, ui) {
$('ul#DanceStyles-menu').prev().find('a.ui-btn')
.empty()
.text('Done')
.attr('class', 'ui-btn-right')
.attr("data-" + $.mobile.ns + "iconpos", '')
.attr("data-" + $.mobile.ns + "icon", '')
.attr("title", 'Done')
.buttonMarkup({ iconpos: 'left', icon: 'arrow-l' });
}
});
});
The buttonMarkup plugin uses the A element's text and class values when creating itself but the other data- attributes result from the previous buttonMarkup and have to be removed, as does the inner html that the buttonMarkup creates (child span, etc). The title attribute was not recreated, for some reason, so I set it myself.
PS If anyone knows of a better way to achieve this (buttonMarkup('remove')? for example), please let us know.
the way i achieved it was changing a bit of the jquery mobile code so that the close button always came to the right, without an icon and with the text, "Close"
not the best way i agree. but works..
I got a similar case, and I did some dirty hack about this :P
$("#DanceStyles-button").click(function() {
setTimeout(function(){
$("#DanceStyles-dialog a[role=button]").removeClass("ui-icon-delete").addClass("ui-icon-check");
$("#DanceStyles-dialog .ui-title").html("<span style='float:left;margin-left:25px' id='done'>Done</span>Dance Styles");
$("#DanceStyles-dialog .ui-title #done").click(function() {
$("#DanceStyles").selectmenu("close")
});
},1);
} );

Trying to understand - HTML replacements and scripts having access to replaced elements on the page

I have a page with a ticket list. In it, there is a <td> that is either a grab or release link. That link inside the '' is wrapped in a '' for an ajax html replacement. Like:
<td>
<div id="ticket_grab_release_<%= ticket.id %>">
*---- either a grab or release link here ----*
<div>
</td>
So, the user clicks on 'grab': They are assigned the ticket, the worklist is updated with their info on the page via HTML replacements, and the grab link is replaced with a 'release' link.
Next to this is a 'complete' link. When the user clicks on that, a small complete form opens in a jQuery UI-Dialog window. I ran into a problem though because along with the grab/replace link changing I also had to toggle this 'complete' link with a grey non-link 'complete' or an actual 'complete' link (if ticket released - disable complete link or visa versa).
The problem is that if this 'complete' link was greyed out and I replaced that with a 'complete' link, the UI Dialog window would not open. Like (no idea what I'm saying) the link wasn't in the DOM.
I got frustrated for a bit and then tried wrapping the script in a <div> and doing an html page replacement on the whole script. I HTML replaced the greyed out 'complete' with a 'complete' link and then HTML replaced the script right after. Interestingly that worked, but I'm really curious as to why it worked. When you ajax insert a script through an HTML replacement, does that inserted script have access to the modified DOM where the original script only has access to the what was the original DOM from the page load?
<div id="html_replace_sript">
<script type="text/javascript">
$('.complete_ticket_link' ).click(function(){
var url = $(this).attr("href");
$("#form_load").load(url,
function() {
$(this).dialog({
modal:true,
draggable: true,
resizable: false,
width:'auto',
height:'auto',
title: ' Complete Ticket ',
position: [125, 50]
});
});
return false;
});
</script>
</div>
Thanks - much apprecaited!
Check out live()'s much less recource-demanding counterpart: delegate()
Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements.
That means that instead of having to look through the entire window for an element, it starts at the specified root, significantly reducing overhead. This is the best solution for your issue.
The answer is YES.
But if you want to bind events to elements that match the selector now and in the future, you can use .live()
So you'd do:
$('.complete_ticket_link' ).live('click' function(){
...
});
Using this, your code can be on your main page and it will work.
Hope this helps. Cheers

Resources