I need to test a data table, which on every change has a loading indicator.
Sometimes this indicator is very fast.
Is there any best practice for selenium in order to follow these very fast UI changes? (Such as loading indicator).
I use the following c# to query the dom for id's and classes with known loading values and wait for them to not be present. Using driver.FindElement(By*) attempts to declare the element is present at a given identifier rather than searching for it, which will cause errors and isn't the behavior we're looking for.
public IWebDriver WaitForPage()
{// Query document for loading elements until none found
var wait = new WebDriverWait(driver,MyDefaultTimeout)
.Until(
e => ((IJavaScriptExecutor)e)
.ExecuteScript(#"
var MyDefaultTimeout = 500;
var loaded = false;
do {
var elementClasses = document.getElementsByClassName('//*[contains(#class=\'is-loading\') or contains(#class=\'fa-gear\') or contains(#class=\'loading\') and not(#class=\'displayed\')]');
var elementIds = document.getElementById('//*[contains(#id=\'hover-gear\') or contains(#id=\loading-gear\')]');
if((!elementClasses === null) || (!elementIds === null)){
setTimeout(function() { loaded = false }, MyDefaultTimeout);
}
else{
if(!document.readyState === 'complete'){
setTimeout(function() { loaded = false }, MyDefaultTimeout);
}
else{
loaded = true;
return document.readyState;
}
}
}
while(loaded === false);", null))
.Equals("complete");
return driver;
}
Related
Hi i am trying to add two new fields to the Barcode mobile view. I went through the default js code of odoo, but didn't find a way to add my custome fields to it.
Here is the default code
stock_barcode/static/src/js/client_action/lines_widget.js
init: function (parent, page, pageIndex, nbPages) {
this._super.apply(this, arguments);
this.page = page; #don't know where this argument page coming from in argument list.
this.pageIndex = pageIndex;
this.nbPages = nbPages;
this.mode = parent.mode;
this.groups = parent.groups;
this.model = parent.actionParams.model;
this.show_entire_packs = parent.show_entire_packs;
this.displayControlButtons = this.nbPages > 0 && parent._isControlButtonsEnabled();
this.displayOptionalButtons = parent._isOptionalButtonsEnabled();
this.isPickingRelated = parent._isPickingRelated();
this.isImmediatePicking = parent.isImmediatePicking ? true : false;
this.sourceLocations = parent.sourceLocations;
this.destinationLocations = parent.destinationLocations;
// detect if touchscreen (more complicated than one would expect due to browser differences...)
this.istouchSupported = 'ontouchend' in document ||
'ontouchstart' in document ||
'ontouchstart' in window ||
navigator.maxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0;
},
In _renderLines function,
_renderLines: function () {
//Skipped some codes here
// Render and append the lines, if any.
var $body = this.$el.filter('.o_barcode_lines');
console.log('this model',this.model);
if (this.page.lines.length) {
var $lines = $(QWeb.render('stock_barcode_lines_template', {
lines: this.getProductLines(this.page.lines),
packageLines: this.getPackageLines(this.page.lines),
model: this.model,
groups: this.groups,
isPickingRelated: this.isPickingRelated,
istouchSupported: this.istouchSupported,
}));
$body.prepend($lines);
for (const line of $lines) {
if (line.dataset) {
this._updateIncrementButtons($(line));
}
}
$lines.on('click', '.o_edit', this._onClickEditLine.bind(this));
$lines.on('click', '.o_package_content', this._onClickTruckLine.bind(this));
}
In the above code, you can see this.page.lines field, i need to add my custom two more fields.
Actually it's dead-end for me.
Any solution?
I am new to protractor and want to create logs for my test cases. I used if and else to write logs. I wanted to know if there is any better way of writing logs for protractor test cases?
My Code:
var colors = require('colors/safe');
var mapFeedBackpage=require('./mapFeedBack-page.js')
describe("Map feedback Automation",function()
{
var mapFeedBack= new mapFeedBackpage();
it("Check if the Url works ",function() //spec1
{
console.log("Checking the url :"+browser.params.url+'\n')
browser.get(browser.params.url);
browser.getCurrentUrl().then(function(value){
if(/report/.test(value) === false) {
fail("Result: URL doesnt works-FAIL \n");
}
else
{
console.log(colors.green("PASS :")+browser.params.url+ "is reachable \n");
}
});
});
it("test browser should reach report road option",function() //spec2s
{
console.log("Checking if road report option is available \n");
mapFeedBack.REPORT_ROAD.click();
expect(browser.getCurrentUrl()).toContain("report_road");
browser.getCurrentUrl().then(function(value){
if(/report_road/.test(value) === false) {
fail("Result: URL doesnt works-FAIL");
}
else
{
console.log(colors.green("PASS")+" Road report option is available");
}
});
});
Yes, you can use https://www.npmjs.com/package/log4js which is basically log4j module for nodejs apps. Since protractor is nodejs program it would certainly support this. It's very easy to implement this-
var log4js = require('log4js');
var logger = log4js.getLogger();
logger.debug("Some debug messages");
or you could write a custom logger:
var logger = exports;
logger.debugLevel = 'warn';
logger.log = function(level, message) {
var levels = ['error', 'warn', 'info'];
if (levels.indexOf(level) >= levels.indexOf(logger.debugLevel) ) {
if (typeof message !== 'string') {
message = JSON.stringify(message);
};
console.log(level+': '+message);
}
}
and then use this in your scripts as :
var logger = require('./logger');
logger.debugLevel = 'warn';
logger.log('info', 'Everything started properly.');
logger.log('warn', 'Running out of memory...');
logger.log('error', { error: 'flagrant'});
I'm using System.Windows.Forms.WebBrowser in console application to render a web page then invoke scripts on it. To render the web page, I use Application.DoEvents to load and execute scripts.
However sometime the app hangs on calling Application.DoEvents. I found no reason. I don't think I'm creating deadlock on the event loop.
Many people on the web says that one should not use Application.DoEvents at all because it creates more problems than it solves. So I'm thinking there must be an alternative to it. But I've searched a lot and find no alternative to Application.DoEvents.
Does anyone know one?
Any information is appreciated. Thank you in advance!
Thanks to Noseratio's help, I finished drafting my code but still there's freezing issue.
Take following code as example(code is too long so I pasted it on pastebin): http://pastebin.com/DkDcrirU
When you run this code, occasionally there will be at least one window fail to close. And if you attach to the frozen process, you will find the code stuck at following line(indicated by ">>>"):
public static bool NavigateLoadAndRender(WebBrowserContext browserContext, string url, TimeSpan loadTimeout, TimeSpan renderTime, out string errMsg)
{
ForceInitActiveXInstance(browserContext);
object axi = null;
Func<Uri> getBrowserUri = null;
Action<Uri> navigateBrowser = null;
Func<IHTMLDocument2> getBrowserDoc = null;
switch (browserContext.WebBrowserType)
{
case WebBrowserTypeEnum.Forms:
{
var browser = browserContext.GetWebBrowserAsFormsType();
getBrowserUri = () => browser.Url;
navigateBrowser = u =>
{
var finished = false;
browserContext.SyncContext.Post(state =>
{
browser.Navigate(u);
finished = true;
}, null);
while (!finished) Thread.Sleep(DefaultConfig_SyncContextPostCheckInterval);
};
getBrowserDoc = () =>
{
IHTMLDocument2 doc = null;
bool finished = false;
browserContext.SyncContext.Post(state =>
{
doc = (IHTMLDocument2)browser.Document.DomDocument;
finished = true;
}, null);
>>> while (!finished) Thread.Sleep(DefaultConfig_SyncContextPostCheckInterval);
return doc;
};
axi = GetActiveXInstance(browserContext);
}
break;
case WebBrowserTypeEnum.Wpf:
{
var browser = browserContext.GetWebBrowserAsWpfType();
axi = GetActiveXInstance(browser);
getBrowserUri = () => browser.Source;
navigateBrowser = u =>
{
var finished = false;
browserContext.SyncContext.Post(state =>
{
browser.Navigate(u);
finished = true;
}, null);
while (!finished) Thread.Sleep(DefaultConfig_SyncContextPostCheckInterval);
};
getBrowserDoc = () =>
{
IHTMLDocument2 doc = null;
bool finished = false;
browserContext.SyncContext.Post(state =>
{
doc = (IHTMLDocument2)browser.Document;
finished = true;
}, null);
while (!finished) Thread.Sleep(DefaultConfig_SyncContextPostCheckInterval);
return doc;
};
axi = GetActiveXInstance(browserContext);
}
break;
default: throw new ArgumentException("unknown browser type", browserContext.WebBrowserType.ToString());
}
var success = NavigateLoadAndRender(
axi, url,
getBrowserUri,
navigateBrowser,
//() => DoEvents(browser),
getBrowserDoc,
loadTimeout, renderTime, out errMsg);
return success;
}
Anyone knows what's happening?
Not sure why facebook refered me here but anyhow, let me ask the question. I have a group on facebook with over 4000 members. I want to delete old members that are not active on the group anymore. Is there a way to select multiple users for deletion?
How to get a list of ID's of your facebook group to avoid removal of active users, it's used to reduce as well a group from 10.000 to 5000 members as well as removal of not active members or old members "You will risk removing some few viewers of the group" "remember to open all comments while you browse down the page":
You will need to have Notepad++ for this process:
After you save the HTML. Remove all information before of document:
"div id=contentArea" to
"div id=bottomContent"
to avoid using messenger ID's,
somehow script will run problems if you have ID's by blocked users.
As well as a different example of how to parse as well as text and code out of HTML. And a range of numbers if they are with 2 digits up to 30.
You can try this to purge the list of member_id= and with them along with numbers from 2 to up to 30 digits long. Making sure only numbers and whole "member_id=12456" or "member_id=12" is written to file. Later you can replace out the member_id= with blanking it out. Then copy the whole list to a duplicate scanner or remove duplicates. And have all unique ID's. And then use it in the Java code below.
"This is used to purge all Facebook user ID's by a group out of a single HTML file after you saved it scrolling down the group"
Find: (member_id=\d{2,30})|.
Replace: $1
You should use the "Regular Expression" and ". matches newline" on above code.
Second use the Extended Mode on this mode:
Find: member_id=
Replace: \n
That will make new lines and with an easy way to remove all Fx0 in all lines to manually remove all the extra characters that come in buggy Notepad++
Then you can easily as well then remove all duplicates. Connect all lines into one single space between. The option was to use this tool which aligns the whole text with one space between each ID: https://www.tracemyip.org/tools/remove-duplicate-words-in-text/
As well then again "use Normal option in Notepad++":
Find: "ONE SPACE"
Replace ','
Remember to add ' to beginning and end
Then you can copy the whole line into your java edit and then remove all members who are not active. If you though use a whole scrolled down HTML of a page. ['21','234','124234'] <-- remember right characters from beginning. Extra secure would be to add your ID's to the beginning.
You put your code into this line:
var excludedFbIds = ['1234','11223344']; // make sure each id is a string!
The facebook group removal java code is on the user that as well posted to this solution.
var deleteAllGroupMembers = (function () {
var deleteAllGroupMembers = {};
// the facebook ids of the users that will not be removed.
// IMPORTANT: bobby.leopold.5,LukeBryannNuttTx!
var excludedFbIds = ['1234','11223344']; // make sure each id is a string!
var usersToDeleteQueue = [];
var scriptEnabled = false;
var processing = false;
deleteAllGroupMembers.start = function() {
scriptEnabled = true;
deleteAll();
};
deleteAllGroupMembers.stop = function() {
scriptEnabled = false;
};
function deleteAll() {
if (scriptEnabled) {
queueMembersToDelete();
processQueue();
}
}
function queueMembersToDelete() {
var adminActions = document.getElementsByClassName('adminActions');
console.log(excludedFbIds);
for(var i=0; i<adminActions.length; i++) {
var gearWheelIconDiv = adminActions[i];
var hyperlinksInAdminDialog = gearWheelIconDiv.getElementsByTagName('a');
var fbMemberId = gearWheelIconDiv.parentNode.parentNode.id.replace('member_','');
var fbMemberName = getTextFromElement(gearWheelIconDiv.parentNode.parentNode.getElementsByClassName('fcb')[0]);
if (excludedFbIds.indexOf(fbMemberId) != -1) {
console.log("SKIPPING "+fbMemberName+' ('+fbMemberId+')');
continue;
} else {
usersToDeleteQueue.push({'memberId': fbMemberId, 'gearWheelIconDiv': gearWheelIconDiv});
}
}
}
function processQueue() {
if (!scriptEnabled) {
return;
}
if (usersToDeleteQueue.length > 0) {
removeNext();
setTimeout(function(){
processQueue();
},1000);
} else {
getMore();
}
}
function removeNext() {
if (!scriptEnabled) {
return;
}
if (usersToDeleteQueue.length > 0) {
var nextElement = usersToDeleteQueue.pop();
removeMember(nextElement.memberId, nextElement.gearWheelIconDiv);
}
}
function removeMember(memberId, gearWheelIconDiv) {
if (processing) {
return;
}
var gearWheelHref = gearWheelIconDiv.getElementsByTagName('a')[0];
gearWheelHref.click();
processing = true;
setTimeout(function(){
var popupRef = gearWheelHref.id;
var popupDiv = getElementByAttribute('data-ownerid',popupRef);
var popupLinks = popupDiv.getElementsByTagName('a');
for(var j=0; j<popupLinks.length; j++) {
if (popupLinks[j].getAttribute('href').indexOf('remove.php') !== -1) {
// this is the remove link
popupLinks[j].click();
setTimeout(function(){
var confirmButton = document.getElementsByClassName('layerConfirm uiOverlayButton selected')[0];
var errorDialog = getElementByAttribute('data-reactid','.4.0');
if (confirmButton != null) {
if (canClick(confirmButton)) {
confirmButton.click();
} else {
console.log('This should not happen memberid: '+memberId);
5/0;
console.log(gearWheelIconDiv);
}
}
if (errorDialog != null) {
console.log("Error while removing member "+memberId);
errorDialog.getElementsByClassName('selected layerCancel autofocus')[0].click();
}
processing = false;
},700);
continue;
}
}
},500);
}
function canClick(el) {
return (typeof el != 'undefined') && (typeof el.click != 'undefined');
}
function getMore() {
processing = true;
more = document.getElementsByClassName("pam uiBoxLightblue uiMorePagerPrimary");
if (typeof more != 'undefined' && canClick(more[0])) {
more[0].click();
setTimeout(function(){
deleteAll();
processing = false;
}, 2000);
} else {
deleteAllGroupMembers.stop();
}
}
function getTextFromElement(element) {
var text = element.textContent;
return text;
}
function getElementByAttribute(attr, value, root) {
root = root || document.body;
if(root.hasAttribute(attr) && root.getAttribute(attr) == value) {
return root;
}
var children = root.children,
element;
for(var i = children.length; i--; ) {
element = getElementByAttribute(attr, value, children[i]);
if(element) {
return element;
}
}
return null;
}
return deleteAllGroupMembers;
})();
deleteAllGroupMembers.start();
// stop the script by entering this in the console: deleteAllGroupMembers.stop();
Use this in Chrome or Firefox Javascript control panel.
I am trying to add views to my scroll view when it reaches 40% scroll. This is the way I am doing it :
scrollView.add(//add first 10 initial containerView's);
var triggerScroll = true;
var scrollPercentage = 0;
scrollView.addEventListener('scroll', function(e) {
var devHeight = Ti.Platform.displayCaps.platformHeight;
var currPos = scrollView.contentOffset.y;
if(currPos > devHeight){
currPos = currPos - devHeight;
}
scrollPercentage = (currPos)/devHeight * 100;
if(scrollPercentage > 40 && triggerScroll){
triggerScroll = false;
var containerView = myapp.createMyView();
scrollView.add(containerView);
}
//reset scroll to true after the offset reaches end of the screen, so that the
//'scroll' event listener only gets called ONCE every time it crosses 40%
if(scrollPercentage > 101){
triggerScroll = true;
}
});
But its just not working. I am trying to support infinite scroll in my vertical scroll view. Any idea whats going wrong ?
I use the module below when working with infinite scrolling. It use a TableView, but I would think you can apply it to a ScrollView as well. You need to pass in a function that will be called when the TableView register that more content should be loaded.
When you have finished loading you must call the loadingDone-function in order to enable the TableView to initiate another loading sequence.
The value m_bNoDataFound ensure that the loading sequence is not initiated, when there is no more data to fill into the list.
You can alter the offsets (currently 0.75 for Android and 0.90 for iOS) if want the loading sequence to be initiated sooner or later during scroll.
function TableView( onLoad ) {
var isAndroid = Ti.Platform.osname === 'android' ? true : false;
var m_bNoDataFound = false;
var m_nLastDistance = 0;
var m_bPulling = false;
var m_bLoading = false;
var table = Ti.UI.createTableView( {
height : Ti.UI.FILL
} );
table.addEventListener( 'scroll', function( evt ) {
//Scroll to load more data
if( !m_bNoDataFound ) {
if( isAndroid ) {
if( !m_bLoading && ( evt.firstVisibleItem + evt.visibleItemCount ) >= ( evt.totalItemCount * 0.75 ) ) {
onLoad( true );
m_bLoading = true;
}
}
else {
var nTotal = evt.contentOffset.y + evt.size.height;
var nEnd = evt.contentSize.height;
var nDistance = nEnd - nTotal;
if( nDistance < m_nLastDistance ) {
var nNearEnd = nEnd * 0.90;
if( !m_bLoading && ( nTotal >= nNearEnd ) ) {
onLoad( true );
m_bLoading = true;
}
}
m_nLastDistance = nDistance;
}
}
} );
function m_fLoadingDone( a_bNoDataFound ) {
m_bNoDataFound = a_bNoDataFound;
if( m_bLoading )
setTimeout( function( ) {
m_bLoading = false;
}, 250 );
}
return {
table : table,
loadingDone : m_fLoadingDone
};
};
module.exports = TableView;
When integrating an infinite scroll within a scrollview, there are some important things you have to consider:
1. scroll event is triggered a lot: try to throttle your scroll event callback using underscoreJS.
Throttle creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with. See the underscorejs documentation for more.
2. Default and system units on Android vs iOS: The size of a view on Android uses a different display unit than coordinates of a view. This mismatch in units will cause incorrect calculation of your trigger for the infinite scroll. To solve this, you have to get and set the default unit yourself. The solution can be found in this widget (see getDefaultUnit()): https://github.com/FokkeZB/nl.fokkezb.color/blob/master/controllers/widget.js
3. The ti-infini-scroll can help you with this: this library creates a wrapper around the default Titanium ScrollView. This wrapper contains the calculation of the end of scroll (your trigger for updating/getting new data). When using this library, don't forget to implement bullet number 2.
https://github.com/goodybag/ti-infini-scroll