How to use commands on sections in Nightwatch.js? - javascript

So basically I've started working with PageObjects and I'm having some issues.
I want to execute a command directly on some section. For example - I want to .waitForElementVisible directly on section (not on elements). Is it even possible? I've tried a lot of combinations, for example:
browser.page.topMenu().section.loginBox.section.unauthenticated.waitForElementVisible('#loginTooltip', 10000)
So it looks like this: topMenu() is my pageObject file, then there is loginBox section containing -> unauthenticated section containing -> loginTooltip section. I want to .waitForElementVisible on the last one section. How to do this? I know I can combine my sections without limitations, but how to work on them later on?
[would say this question is a bonus because it's not related with question in title]
I'm having problems when making an assertions on section in section. How to do this? I've tried a lot of ways, one of them is below:
browser.page.topMenu().expect.section('#loginBox').to.be.visible - this works - because it's only one section
browser.page.topMenu().expect.section('#loginBox').section('#unauthenticated').to.be.visible - doesn't work - I want to check if section unauthenticated which is inside loginBox section is visible. How to do this?
Thanks in advance for all answers, I've tried to figure this out myself without any success.

OK first let's break it down so it will be more readable:
var topMenu = browser.page.topMenu(); // Declare the page object.
var loginBox = topMenu.section.loginBox; // Declare the first section.
var unauthenticated = loginBox.section.unauthenticated // Declare the second section.
and so on...
Once you want to perform a command you can do:
loginBox.waitForElementVisible('#unauthenticated');
Note that the section selector should be declared:
sections: {
unauthenticated: {
selector: '.unauthenticated_title',
elements: {
sectionElements: '.selector_selector'
}
},
anotherSection: {
...
}
}
The second question is similar.
loginBox.expect.element('# unauthenticated').to.be.visible;

Related

Disabling Items in APEX Interactive Grid - Action Dropdown Menu

The idea here is to remove/disable certain items from the Actions Dropdown Menu from an Interactive Grid.
I've managed to disable the "Aggregate" option so far with the following code:
`function(config) {
config.initActions = function( actions ) {
actions.hide("show-aggregate-dialog");
}
return config;
}`
Now I'm trying to do the same with some other options, such as Refresh (shown as Aktualisieren), but the following line, which was added to the previous code, does nothing:
actions.hide("show-filter-dialog");
I've tried a couple things to attempt and remove the rest, such as the the none !important css function, without results:
#ig_id button[data-action="show-filter-dialog"] {
display: none !important;
}
I've attempted the Remove action as well, with no success:
actions.remove("show-filter-dialog");
Also with the use of the "Index menu removal" function, I managed to remove the whole Daten option, though I'd prefer only disabling certain items from it, not the whole thing:
var $ = apex.jQuery;
var toolbarData = $.apex.interactiveGrid.copyDefaultToolbar();
config.toolbarData = toolbarData;
toolbarData[3].controls[0].menu.items[3]['hide'] = true;
return config;
Is there something wrong with the methods I'm using? Are they capable of changing these items? Or I can only change these with plugins?
Also, I sometimes feel confused on where I should be putting some of these codes. Should javascript functions be put only in the Attributes section of the Interactive Grid or in the When Page Loaded section?
So, after a bit of experimenting and messing around with the code, I managed to solve everything here. I'm posting the solution in case someone else might want to use it as well. There might be better, easier and likely more unified ways of hiding it, rather than using so many different functions, but I suppose I have to learn more about Javascript first. This is the code and the properties that worked for me:
function(config) {
config.initActions = function( actions ) {
actions.hide("show-aggregate-dialog"); // hides Aggregate
actions.hide("refresh"); // Hides Refresh inside "Data"
actions.hide("chart-view"); // Hides Chart. Thanks to Alli Pierre Yotti in Apex Forums
actions.remove("show-columns-dialog"); // Hides Columns
actions.remove("show-filter-dialog"); // Hides Filter
actions.remove("show-help-dialog"); // Hides Help
}
var $ = apex.jQuery;
var toolbarData = $.apex.interactiveGrid.copyDefaultToolbar();
config.toolbarData = toolbarData;
toolbarData[3].controls[0].menu.items[4]['hide'] = true; // Hides Format
toolbarData[3].controls[0].menu.items[8]['hide'] = true; // Hides Report
config.features.flashback = false; // Hides Flashback
return config;
}

how to handle javascript that only runs on certain pages when bundled together

When working on small client sites, I often end up working with a main.js file that includes a bunch of jQuery plugins and small toggle functionality. Some of these code snippets are only relevant on certain pages, but ends up bundled together in one main.min.js file.
My question is, how do people write the individual code snippets in order to only execute that code when the correct page is being rendered?
Here's an example: Let's say I have a page with a search input field. This input is hooked up with jQuery autocomplete in order to show search suggestions as the user types. the code in main.js could look something like this:
var data = [
{
value: 'some value',
data: 'some data'
},
{...}
]
$('#autocomplete').autocomplete({
lookup: data,
lookupLimit: 10,
minChars: 3,
});
This code is only useful on the template that has that input field, but as main.js contains a bunch of other smaller bits like this that are useful globally and on other pages, the whole file is loaded on every pageview. What strategy should I use to only execute that piece of code when the page needs it?
I though of a few ways my self:
Check if the DOM-element (in this case #autocomplete) exists.
Check if the URL is == '/page-with-autocomplete'.
Use a class on , and check for that class i n order to run the script.
Other ideas? Any standard way to do this sort of thing? Anything considered a "best practice"?
Stick your JS in an if block and check for the unique DOM element on the page you want the script to run.
Although you can't just do:
if ( $('#my-el') ) {}
You have to check if the element has a length, like:
if ( $('#my-el').length ) {}

How to deal with DOM elements?

I am learning about writing custom JavaScript for my Odoo 10 addons.
I've written the following piece of code:
odoo.define('ioio.io', function(require) {
'use strict'
const e = $('div.o_sub_menu_footer')
console.log('--testing--'.repeat(7))
console.log(e)
// the "Powered by Odoo" down the secondary menu
e.remove()
})
The code is well loaded and I can see my testing string in the console.
However when this code is being loaded before the target div, so e empty/not yet filled and thus its content is not removed.
Doing it manually from the console works.
My question is what is the right way to do that? And how to know exactly when the code gets executed?
You can
put your html code before the script tag in your file
use jQuery $(document).ready(...);
Place your script at the bottom of the <body> tag to make sure the DOM renders before trying to manipulate it.
This is an Odoo specific question, so you should use the Odoo standard way, which is via its base JS class. That class contains a ready() method which does exactly what you need.
In your case, to use that function, you need to require the class first. Then you can use ready().
Updating your code, it should look like this:
odoo.define('ioio.io', function(require) {
'use strict'
// require base class
var base = require('web_editor.base');
//use its ready method
base.ready().done(function () {
// put all the code you want to get loaded
// once the DOM is loaded within this block
const e = $('div.o_sub_menu_footer')
console.log('--testing--'.repeat(7))
console.log(e)
// the "Powered by Odoo" down the secondary menu
e.remove()
});
})
While your accepted answer leads to the same outcome, you might want to update it to this one since this is the Odoo way. It's generally advised to work within the Odoo framework as much as possible and customise only if really needed. (Though it can be tough to learn what features Odoo already provides because of its poor documentation.)

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.

how to apply DRY principle to javascript and query

I've got about 30 web pages, all of them HTML forms. Each page has two or more different form elements - select, input text, checkboxes, text areas - along with various ui elements, popups, form validation etc. I'm trying to refactor the jquery used in the pages to use the DRY principle but am not sure how to do it. Here area few examples of some of the jquery used:
Example Code Block A:
$(".show-tool", _container).mouseover(function() {
$(this).nextAll(":hidden").css('display','block');
});
Example Code Block B:
$(".optional").blur(function(){
if ($(this).val() == '')
{
$(this).addClass('optional');
$(this).val('(Optional)');
}
});
Example Code Block C:
$('.howtoremain').click(function() {
$('.hiddendiv').slideToggle("10000");
if($(this).hasClass('howtoremain')) {
$(this).removeClass('howtoremain').addClass('howtoremain2');
}
else {
$(this).removeClass('howtoremain2').addClass('howtoremain');
}
});
All of these are contained in the document.ready. The actual code list above isn't that relevant. I'm trying to have each HTML page only include the jquery code that is relevant. For example page 1 might use code block A and B. Page 2 might use A,B,C,D,E, and F. Page 3 might use code block C and G. Rather than have one giant document.ready with every code block (which will probably cause bugs at some point anyway if one code block needs to be slightly different than another for the same form element), how do you code this? Have one javascript file per code block also seems lousy, as it would cause multiple hits to the server per page. I think I am trying to get at one big javascript file, but only initialize in the document.ready those functions that are relevant to each page.
In my projects, I have gone over to using multiple JS-Files per module and concatenating them into a closure within a build process. This is similar to what jQuery does in its build process (cf. intro.js, outro.js)
This way, I can use granular, DRY modules in smaller files, then concatenate them. A typical single module file might look like this for your Example B:
( function($) {
var subjects = $('.optional');
if ( subjects.length === 0 ) {
// this is a knockout criteria for this module, thus exit this enclosed function
return;
}
subjects.blur(function(){
if ($(this).val() == '')
{
$(this).addClass('optional');
$(this).val('(Optional)');
}
});
// now use whatever you need to initialise.
})($);
As you can see, I use the outer function not only to keep my scope clean, but much more important, to be able to cancel the module's initialisation as soon as I realise, it is not needed on the current page / event / ... - Of course, you may find several more efficient ways of determining whether or not each module should initialise itself.
On some projects, I have a build script to concatenate these modules within another closure which might look like this:
( function( window ) {
var $ = window.jQuery; //call me paranoid, but I like my vars clean
$(document).ready( function() {
// stuff the modules here, one after another, in any sensible order.
});
}(window);
In other projects, I am able to move the $(document).ready()-Bit into the modules where document.ready is needed, and listening to other Initialisation-Events within others, which feels a bit cleaner for me.
But anyway - having
a build process rather than a lot of single requests
several small, "one-thing-only"-Files to go into the build
a self-enforced "top-level lambda function" due to the intro/outro-Concatenation-Style I adapted from jQuery itself
has significantly improved my DRY-ness and, even, my JS code style.

Categories

Resources