Electron webview events - events

I'm writing an app in electron and came across with some issues, appreciate if anyone can help.
I'm navigating a webview to src="about:blank"
and load it with
var urlOptions = {
extraHeaders: 'Authorization: Bearer ' + arg.token
};
webview.loadURL(arg.viewToLoad, urlOptions);
Then, when the dom-ready event is triggered, I'm calling
webview.addEventListener('did-finish-load', () => spiner.hide());
I'm trying to make a spinner stop after the webview is finished loading but the 'did-finish-load' event fires twice - this causes a blank page for couple of seconds.
Does anyone has a clue why?
I looked the documentation again but this is not explained right. Is there an event that's triggered once after all resources are done downloading and are ready to show?

The 'did-finish-load' event is triggered 2 times : when you load the about:blank, and when you use loadURL().
To convince you, try this:
main.js :
const { BrowserWindow} = require('electron')
let win = new BrowserWindow({width: 1450, height: 1200})
win.webContents.on('did-finish-load', () => {
console.log('finished to load ');
})
win.loadURL(`about:blank`)
Then you will notice that did-finish-load has been triggered!
That's why the event is triggered two times in your code.
Note that the important part here is the webContents EventEmitter, that is an attribute of both BrowserWindows and webviews. I used a BrowserWindow to keep the example minimal.

Related

Hide Testcafe Overlay

I'm trying to use testcafe to fill forms on a page.
When the form is filled, I'd like to be able to stop the test with the window still open so a human can review the form before clicking submit.
I can pause the test with t.debug() but this locks the page and shows the testcafe controls overlay at the bottom.
Is there a way I can remove this overlay and unlock the page?
I've tried using client functions to hide the element with javascript as follows:
test('test_1', async (t) => {
const hideOverlay = ClientFunction(function() {
const target = document.querySelector('#root-hammerhead-shadow-ui > div > div');
target.style.display = 'none';
return true;
})
await t.wait(5000);
setTimeout(async function() {
const res = await hideOverlay();
console.log('-------->', { res });
}, 6000);
await t.debug();
});
Since no code will be executed after debug is invoked, I thought I could use a settimeout to queue the call to the function that hides the overlay, so that it is queued and only executes after debug is called and the overlay is visible.
Didn't work though :( code didn't execute, got an unhandled promise rejection.
Could really use some help here, thanks :)
Yes, you can unlock the page by clicking the 'Unlock page' button in the footer as #VysakhMohan mentioned in the comment.
Please refer to the client-side debugging documentation for more details.

Nativescript Android - hide keyboard

In my Nativescript app, the application starts with the login page. On iOS everything looks good, but on android, the username field is focused and the keyboard is showing. Is there a way to prevent this from happening?
So far I have tried:
Getting a reference of another element (a label) and calling lbl.focus() in the page's onLoaded event
getting a reference of the username textfield and calling txt.dismissSoftInput() and txt.android.clearFocus()
None of this worked. Is there another way to hide the keyboard when the page is loaded?
Thank you
I guess the username field is either textview or textfield. If so, try this on loaded callback:
var myTextview = page.getViewById("myTextView");
myTextView.dismissSoftInput();
So I ended up implementing a different solution. This may not be the best approach, but it serves its purpose in my case and I wanted to share it for those of you that face a similar scenario.
in page's loaded event I included this code:
if (page.android) {
var un = page.getViewById('username');
var p = page.getViewById('password');
un.android.setFocusable(false);
p.android.setFocusable(false);
setTimeout(function () {
un.android.setFocusableInTouchMode(true);
p.android.setFocusableInTouchMode(true);
}, 300);
}
The key here is the setTimeout function (Thanks Emil Oberg for pointing me to the right direction). As far as I understand, here is what is happening:
The page loads and we call setFocusable(false) on the only 2 text fields to prevent Android from setting the focus on them
Then we wait 300ms to allow Android to do its initialization
When the timeout executes, call setFocusableInTouchMode(true) to allow the fields to gain focus.
At this point the page is loaded without any fields to be in focus and with the keyboard hidden. If the user taps any of the fields the keyboard will appear and they can proceed to log in as usual.
As I mentioned, this may not be the best, or correct, approach, but works for me. Hope this can save someone the time to research the issue.
You want to clear the focus of the field in the loaded callback:
var searchBar = page.getViewById('my-search-bar-id');
if (searchBar.android) {
searchBar.android.clearFocus();
}
What about combining both tips above?
onClear(args) {
const searchBar = <SearchBar>args.object;
if (isAndroid && searchBar.android != undefined){//avoid random unpleasant error
setTimeout(() => { // the key here was this timeout
searchBar.android.clearFocus();
}, 1)
}
}

Protractor : How to wait for page complete after click a button?

In a test spec, I need to click a button on a web page, and wait for the new page completely loaded.
emailEl.sendKeys('jack');
passwordEl.sendKeys('123pwd');
btnLoginEl.click();
// ...Here need to wait for page complete... How?
ptor.waitForAngular();
expect(ptor.getCurrentUrl()).toEqual(url + 'abc#/efg');
Depending on what you want to do, you can try:
browser.waitForAngular();
or
btnLoginEl.click().then(function() {
// do some stuff
});
to solve the promise. It would be better if you can do that in the beforeEach.
NB: I noticed that the expect() waits for the promise inside (i.e. getCurrentUrl) to be solved before comparing.
I just had a look at the source - Protractor is waiting for Angular only in a few cases (like when element.all is invoked, or setting / getting location).
So Protractor won't wait for Angular to stabilise after every command.
Also, it looks like sometimes in my tests I had a race between Angular digest cycle and click event, so sometimes I have to do:
elm.click();
browser.driver.sleep(1000);
browser.waitForAngular();
using sleep to wait for execution to enter AngularJS context (triggered by click event).
You don't need to wait. Protractor automatically waits for angular to be ready and then it executes the next step in the control flow.
With Protractor, you can use the following approach
var EC = protractor.ExpectedConditions;
// Wait for new page url to contain newPageName
browser.wait(EC.urlContains('newPageName'), 10000);
So your code will look something like,
emailEl.sendKeys('jack');
passwordEl.sendKeys('123pwd');
btnLoginEl.click();
var EC = protractor.ExpectedConditions;
// Wait for new page url to contain efg
ptor.wait(EC.urlContains('efg'), 10000);
expect(ptor.getCurrentUrl()).toEqual(url + 'abc#/efg');
Note: This may not mean that new page has finished loading and DOM is ready. The subsequent 'expect()' statement will ensure Protractor waits for DOM to be available for test.
Reference: Protractor ExpectedConditions
In this case, you can used:
Page Object:
waitForURLContain(urlExpected: string, timeout: number) {
try {
const condition = browser.ExpectedConditions;
browser.wait(condition.urlContains(urlExpected), timeout);
} catch (e) {
console.error('URL not contain text.', e);
};
}
Page Test:
page.waitForURLContain('abc#/efg', 30000);
I typically just add something to the control flow, i.e.:
it('should navigate to the logfile page when attempting ' +
'to access the user login page, after logging in', function() {
userLoginPage.login(true);
userLoginPage.get();
logfilePage.expectLogfilePage();
});
logfilePage:
function login() {
element(by.buttonText('Login')).click();
// Adding this to the control flow will ensure the resulting page is loaded before moving on
browser.getLocationAbsUrl();
}
Use this I think it's better
*isAngularSite(false);*
browser.get(crmUrl);
login.username.sendKeys(username);
login.password.sendKeys(password);
login.submit.click();
*isAngularSite(true);*
For you to use this setting of isAngularSite should put this in your protractor.conf.js here:
global.isAngularSite = function(flag) {
browser.ignoreSynchronization = !flag;
};
to wait until the click itself is complete (ie to resolve the Promise), use await keyword
it('test case 1', async () => {
await login.submit.click();
})
This will stop the command queue until the click (sendKeys, sleep or any other command) is finished
If you're lucky and you're on angular page that is built well and doesn't have micro and macro tasks pending then Protractor should wait by itself until the page is ready. But sometimes you need to handle waiting yourself, for example when logging in through a page that is not Angular (read how to find out if page has pending tasks and how to work with non angular pages)
In the case you're handling the waiting manually, browser.wait is the way to go. Just pass a function to it that would have a condition which to wait for. For example wait until there is no loading animation on the page
let $animation = $$('.loading');
await browser.wait(
async () => (await animation.count()) === 0, // function; if returns true it stops waiting; can wait for anything in the world if you get creative with it
5000, // timeout
`message on timeout`
);
Make sure to use await
you can do something like this
emailEl.sendKeys('jack');
passwordEl.sendKeys('123pwd');
btnLoginEl.click().then(function(){
browser.wait(5000);
});
browser.waitForAngular();
btnLoginEl.click().then(function() { Do Something });
to solve the promise.

SoundCloud: how to stop playing when hash change

I'm using ajax page switching method with SoundCloud script.
How to stop playing music when the user leaves the page with SoundCloud container?
I've found these stokes:
// stop all players, might be useful, before replacing the player dynamically
$.scPlayer.stopAll = function() {
$('.sc-player.playing a.sc-pause').click();
};
Currently I call initialization $(".sc-player").scPlayer(); inside .load function, but where do I need the put the code which will stop playing audio before any page switching?
you can add event listener window.onhashchange = stopAll;
and definition of stopAll() will be
stopAll = function() {
$('.sc-player.playing a.sc-pause').click();
};

Website loses performance after some jQuery $.ajax calls

I admit I'm quite noob with full ajax websites, and so I'm surely making some mistakes.
The problem is this:
in http://lamovida.arabianessence.com
every page is loaded with an $.ajax call using this function
function getAjaxPage() {
$('a.ajaxc').click(function() {
$("li.page_block").find(".wrapper").fadeOut(400).remove();
hideSplash();
var $thishref = $(this).attr('href'),
$thisurl = $thishref.replace("#!/",""),
$urlArr = $thisurl.split('-'),
$urlOk = $urlArr[0],
$dataOk = $urlArr[1];
$.ajax({
url : $urlOk + ".php",
data : 'id='+$dataOk,
success : function (data,stato) {
$("#content").css({opacity:1}).fadeIn(400);
$("li.page_block").html(data);
$("li.page_block").css('visibility', 'visible');
$("li.page_block").find(".wrapper").css({opacity:0}).show().animate({opacity:1},1000);
var $whgt = $(".wrapper").height(),
$ctop = ( ( $(window).height() - $whgt ) /2 )-40;
$("#content").stop().animate({height: $whgt+40, top: $ctop},1000);
$("li.page_block").css('padding-top',20);
$('.scrollable').jScrollPane();
$('.slider>ul>li').jScrollPane();
getAjaxPage();
},
error : function (richiesta,stato,errori) {
alert(errori);
}
});
});
}
Every time this function is called the content gets loader slower, and after about 20 clicks things get real bad, and the loading time grows and grows.
I tried to analyze the situation using the Google Chrome's Timeline, and I saw that after each click the browser uses more memory. If I comment the getAjaxPage(); row in the "success" section the situation starts to get better, but of course I lose all the internal navigation.
What could I do to avoid this problem?
Many thanks to all!
Every call to $('a.ajaxc').click() is adding new event handler thus every click causes more requests to be made. After the first click, every click will cause two requests. Another click? Another three requests. Etc.
Put the handler outside the function and you will have only one AJAX call per click:
$(document).ready(function() {
$('a.ajaxc').click(getAjaxPage);
});
I also don't see the reason behind calling getAjaxPage again from within the callback, so remove it as well to avoid infinite loop of requests.

Resources