clicks on blazor webpage not being registered after tab reload - javascript

I am just getting started with CEF4Delphi. I have a chromium window on a tab in a page control. There are javascript hooks attached to elements on the webpage which register clicks and fire off native delphi code. This is all working correctly on one webpage. I can switch tabs and keep clicking on the webpage and receiving the click events.
However, when loading a different webpage there is some strange behaviour. The first time the tab is loaded clicks will be registered just fine. The issue arises when switching tabs. The second time a tab is loaded there are no clicks being registered.
This second webpage is being created by blazor which is different to the first but I am not sure why that would be affecting it.
I have tested using Google.com and it works fine on there also.
Chromium1.LoadURL('localhost:6064');
is run when the tab is loaded.
class procedure TmyExtension.ButtonClick(const data: string);
var
msg: ICefProcessMessage;
begin
msg := TCefProcessMessageRef.New('ButtonClick');
TCefv8ContextRef.Current.Browser.MainFrame.SendProcessMessage(PID_BROWSER, msg);
end;
The delphi code being fired
Chromium1.browser.MainFrame.ExecuteJavaScript(
'document.getElementById("'+
'NewButton' +
'").addEventListener("click", function(evt){' +
'function getpath(n){var ret = n.id;' +
'if (n.parentNode){return "" + ret}' +
'else return ret};'
'myextension.ButtonClick(getpath(evt.target))' +
'})', Chromium1.browser.MainFrame.GetURL, 0);
The Javascript being executed
procedure TtabWebPage.Chromium1ProcessMessageReceived(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame;
sourceProcess: TCefProcessId; const message: ICefProcessMessage;
out Result: Boolean);
begin
if (message = nil) or (message.ArgumentList = nil) then exit;
// This function receives the messages with the JavaScript results
if (message.Name = 'ButtonClick') then begin
Inc(FCount);
Result := True;
end;
inherited;
end;
Receiving the chromium message and increasing a count.
There doesn't seem to be a way to debug whether the javascript is being run. The ExecuteJavaScript function is definitely being run every time the page loads. I am wondering if there is anything to do with the blazor aspect of the webpage that causes this, or if there is any way to debug the issue.

Related

What JavaScript code do I run when the user closes the tab?

Summarize the problem
I'm trying to create a macOS tabbed web browser using WKWebView and Swift. Everything works fine except the webpage doesn't know I closed the tab. I want to tell the webpage that I closed the tab
Describe what you've tried
I haven't tried anything because I don't know what to do
Show some code
I've tried doing this
webView.removeFromSuperView()
But the webpage didn't know that I closed the tab
I don't know much javascript so please include code instead of explaining
Javascript scripts use onbeforeunload and onunload events to tap into browser closing.
Since you know the browser is about to close, you can dispatch this event yourself. For example, with beforeunload event:
let script = """
const event = new Event("beforeunload", { cancellable: true });
const cancelled = !window.dispatch(event);
// return back whether the user cancelled
cancelled;
"""
When you need to close the browser, invoke the script:
let webView = WKWebView()
// ... other config
webView.evaluateJavaScript(script, completionHandler: { result, error in
guard let cancelled = result else { return }
if cancelled == 1 {
// ... do whatever
}
})

Test a self deleting/dynamic Webelement in Selenium

What I'm doing
I've been experimenting with Selenium and making a simple program to make my Selenium testing life easier. Part of this is testing webelements and figuring out what methods (clicking submitting ect) make them reload the page, remain static, or become stale without reloading the page. In this question I'm particularly interested in the third case as the first two are already implemented by me.
The problem I'm having
The problem I have is finding a Webelement that goes stale and doesn't cause a page reload. I can't think of a good way to search for one, I don't have the HTML and javascript skills to make one (yet anyways) and I can't verify my code works unless I actually test it.
What I've done/tried
The first thing I thought to look for was a popup but those aren't actually part of the webpage and they're also quite unreliable. I want something thats going to behave consistently because otherwise the test won't work. I think dynamic Webelements, those that change their locators when acted upon will suit my needs but I have no good way of finding them. Any google results for "Self deleting webelement exmaple" or "Webelement goes stale doesn't cause page reload example" or similar, will only give me questions on stackoverflow like this one rather than what I want - concrete examples. The code I'm running simply waits for a staleReferenceException and for an onload event in javascript. If the staleReferenceException occurs but the onload event does not, then I know I've found a self-deleting / dynamic webelement (at least thats what I think is the proper way to detect this). Here is the code I'm running:
try {
//wait until the element goes stale
wait.until(ExpectedConditions.stalenessOf(webElement));
//init the async javascript callback script
String script = "var callback = arguments[arguments.length - 1];" +
"var classToCall = 'SeleniumTest.isPageReloaded';" +
"window.addEventListener('onload'," + "callback(classToCall));";
//execute the script and wait till it returns (unless timeout exceeded)
JavascriptExecutor js = (JavascriptExecutor) driver;
//execute the script and return the java classname to call
//if/when the callback function returns normally
String classToCall = (String) js.executeAsyncScript(script);
clazz = Class.forName(classToCall);
callbackMethod = clazz.getMethod("JavascriptWorking");
callbackMethod.invoke(null,null);
//page reloaded
threadcase = 1;
}
//waiting until webElement becomes stale exceeded timeoutSeconds
catch (TimeoutException e) {
//page was static
threadcase = 2;
}
//waiting until webElement Reloaded the page exceeded timeoutSeconds
catch (ScriptTimeoutException e) {
//the webElement became stale BUT didn't cause a page reload.
threadcase = 3;
As you can notice above there is an int variable named threadcase in this code. The three 'cases' starting from 1 (0 was the starting value which represented a program flow error) represent the three (non-error) possible results of this test:
the page reloads
the page remains static, webelement doesn't change
the page remains static, webelement changes
And I need a good example with which to test the third case.
Solutions I've considered
I've done some basic research into removing webelements in javascript but I A: don't even know if I can act on the page in Selenium like that and B: I'd rather get a test case that just uses the Webpage as is since introducing my edits makes the validity of my testcase reliant on more of my code (which is bad!). So what I need is a good way of finding a webelement that matches my criteria without having to scour the internet with the f12 window open hoping to find that one button that does what I need.
Edit 1
I just tried doing this test more manually, it was suggested in an answer that I manually delete a webelement at the right time and then test my program that way. What I tested was the Google homepage. I tried using the google apps button because when clicked it doesn't cause the whole page to reload. So my thinking was, I'll click it, halt program execution, manually delete it, run the rest of my code, and since no onload events will occur, my program will pass the test. To my suprise thats not what happened.
The exact code I ran is the below. I had my debug stop on the first line:
1 Method callbackMethod = null;
2 try {
3 //wait until the element goes stale
4 wait.until(ExpectedConditions.stalenessOf(webElement));
5 //init the async javascript callback script
6 String script = "var callback = arguments[arguments.length - 1];" +
7 "var classToCall = 'SeleniumTest.isPageReloaded';" +
8 "window.addEventListener('onload', callback(classToCall));";
9 //execute the script and wait till it returns (unless timeout
10 //exceeded)
11 JavascriptExecutor js = (JavascriptExecutor) driver;
12 //execute the script and return the java classname to call if/when
13 //the callback function returns normally
14 String classToCall = (String) js.executeAsyncScript(script);
15 clazz = Class.forName(classToCall);
16 callbackMethod = clazz.getMethod("JavascriptWorking");
17 callbackMethod.invoke(null,null);
18 //page reloaded
19 threadcase = 1;
20 }
21 //waiting until webElement becomes stale exceeded timeoutSeconds
22 catch (TimeoutException e) {
23 //page was static
24 threadcase = 2;
25 }
26 //waiting until webElement Reloaded the page exceeded
27 //timeoutSeconds
28 catch (ScriptTimeoutException e) {
29 //the webElement became stale BUT didn't cause a page reload.
30 threadcase = 3;
31 //trying to get the class from javascript callback failed.
32 }
whats supposed to happen is that a Stale webelement causes the program to stop waiting on line 4, the program progresses, initializes the Javascript callback in lines 6-11 and then on line 14 the call to executeAsyncScript is SUPPOSED to wait untill an 'onload' event which should only occur if the page reloads. Right now its not doing that or I'm blind. I must be confusing the program flow because I'm 99% certain that there are no page reloads happening when I manipulate the DOM to delete the webelement I'm clicking on.
This is the URL I'm trying:
https://www.google.com/webhp?gws_rd=ssl
Simple google homepage, the button I'm deleting is the google apps button (the black 9-grid in the top right)
some info on that element:
class="gb_8 gb_9c gb_R gb_g"
id="gbwa"
Its the general container element for the button itself and the dropdown it creates. I'm deleting this when my program hits the STOP on line 1. Then I go through my program in the debugger. Note (you may have to click inspect element on the button more than once to focus in on it). I'm going to try deleting lower level elements rather than the whole container and see if that changes anything but still this behavior baffles me. The goal here is to get the program flow to threadcase 3 because thats the one we are testing for. There should be no page reloads BUT the webelement should become stale after I manually delete it. I don't have any clue why the javascript callback is running when I can't see a page reload. Let me know if you need more info on what exactly I'm deleting on the google homepage and I'll try sending a picture (with optional freehand circles of course).
I would think that you could debug through a test, place a breakpoint at a suitable point, then use the browsers dev tools to manually update the HTML.
Obviously, if you want this to be a repeatable process it is not an option, but if you are just investigating, then a manual intervention could be suitable

Resuming from Javascript in WinDbg does still results in interactive shell

I'm attempting to use the new Javascript features in WinDbg in order to script collecting some data.
The basic setup of what I have is that I have one script, which generates a bunch of breakpoints, and a second script which is actually invoked by the breakpoints.
The objective of the script which is invoked by the breakpoints is to log some data, and then resume execution -- I never want to be dropped into an interactive prompt. Unfortunately that's not the case. Here's the script:
"use script";
function invokeScript() {
// If the process is not a content process (e.g. parent or GPU), exit out.
var cl = host.currentProcess.Environment.EnvironmentBlock.ProcessParameters.CommandLine.ToDisplayString();
if (cl.slice(-5, -1) !== " tab") {
host.diagnostics.debugLog("Exiting because not tab: " + cl.slice(-5, -1) + "\n");
} else {
host.diagnostics.debugLog("===WIN32K-START===\n");
for (var frame of host.currentThread.Stack.Frames) {
host.diagnostics.debugLog(frame.toString() + "\n");
}
host.diagnostics.debugLog("===WIN32K-END===\n");
}
host.namespace.Debugger.Utility.Control.ExecuteCommand("gc");
}
My expectation, based on Microsoft's examples, is that host.namespace.Debugger.Utility.Control.ExecuteCommand("gc") would resume execution, but it does not (apologies about the screenshot):
(the gcs there were all entered by hand).
Edit: Further testing shows that this occurs only in the WinDbg preview from the Microsoft Store, the existing WinDbg continues to work ok, so I'm inferring that this is a bug.

How do we correctly click on a link or button using Javascript?

I'm using the PHP + Ajax version of this template: http://192.241.236.31/themes/preview/smartadmin/1.5/ajaxversion/, Codeception WebDriver (Selenium) to perform my tests. Most of them are working fine, but I have some random, yes, random!, failing tests, so I always get back to them and try to harden, hoping it will not randomly fail sometime in the future. One of the failing reasons are usually due to wrong clicks on the interface. I tell Codeception to click the #element-id and when it fails, I see that it actually had a different page showing in the output png, but the link activated showing it tried to click the correct link. Sometimes I just have to load a different page before clicking that particular link and wait 10 seconds for it to render, and it works, silly huh? Yeah, but sometimes this doesn't work either.
This is how I used to test things:
$I->click('#element-id');
And I have to use the element id, because it's a multi-language app, all (mostly) strings come from a strings file and they might change at some point and break tests.
After too many random tests failing, I decided to make the frontend itself responsible for clicking things during tests, it's a long and silly circunventing shot, but it should be work, so I created a functionalHelper:
public function clickElement($element)
{
$I = $this->getDriver();
$I->executeJS("clickElement('{$element}');");
}
And two Javascript functions:
function clickElement(element)
{
element = jQuery(element);
if(typeof element !== undefined)
{
fakeClick(element[0]);
}
return true;
}
function fakeClick(object)
{
if (object.click)
{
object.click();
}
else if(document.createEvent)
{
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
var allowDefault = object.dispatchEvent(evt);
}
}
I'm using jQuery in the first one because it's already available in the template and it's easier to select non-id'ed things.
This is working fine, and you can test it yourself by
1) Opening the page:
http://192.241.236.31/themes/preview/smartadmin/1.5/ajaxversion/
2) Loading the script in your browser console by executing
loadScript('https://www.dropbox.com/s/kuh22fjjsa8zq0i/app.js?dl=1');
3) And clicking things:
Calendar
clickElement($x('//*[#id="left-panel"]/nav/ul/li[7]/a/span'));
Widgets
clickElement($x('//*[#id="left-panel"]/nav/ul/li[8]/a/span'));
Because the template is using the hash character to control the ajax part of the page and not reload everything, I had to (programatically) add the referer-url to all my href links, so I could, in my app, redirect back to the referer, as the hash part uf the accessed url is not sent to the server. This is a link example:
<a href="/users?referer-href-url=/dashboard" title="Users" id="users-navigation">
<span class="menu-item-parent">Users</span>
</a>
Wich basically means the user was looking at the dashboard when she clicked the users link. If something wrong happens, the app will redirect the user back to the dashboard, with an error message.
The problem is that, somehow, during tests, the current url, when tested using:
$I->seeCurrentUrlEquals('/#/clients?referer-href-url=/clients');
Becomes
/#/clients
Instead of
/#/clients?referer-href-url=/clients
This happens sometimes, because some other it also works. If I browse the site manually it works in 100% of the time and I always see the correct URL in the address bar. And if I manually execute clickElement() it also works fine. The problem only heppens my my suite is running.
Here's an example of it passing (green):
And the exact same test randomly failing (red):
This is the code related to the failing test:
$I->clickElement('#clients-navigation');
$I->waitForText('Jane Doe', 10);
$I->seeCurrentUrlEquals('/#/clients?referer-href-url=/clients');
I usually wait for something after a click, so the page can have time to render.
You can also see that there are a lot of "I click element", using those PHP and Javascript functions without a problem.
I'm using:
Ubuntu 14.04
PHP 5.5.9
Codeception 2.0.7
Selenium 2.44.0
Firefox 33.0
So, what could be the problem here?
Is this the correct way to click an element in Javascript?
In the process I also experience tests which does not fail when ran singly and fails when in batch, but this might be a different question, right?

Chrome Bug - onDOMContentLoaded firing before any DOM has loaded

I have set up an event listener in my background.js file:
chrome.webNavigation.onDOMContentLoaded.addListener(function (info) {
// Checks for the URL bar navigation and not any child frames
if (info.frameId === 0){
var url = info.url;
console.log('visit: ' + url);
}
});
I would expect the a log entry showing the url of the current page when the dom has loaded. The problem arises when typing in a search in the omnibox. Say I visit www.twitter.com often enough to have it cached when I enter a search that starts with "t" in the omnibox.
If I enter the search term "testing123" in the omnibox, I'll see a log entry saying "visit: http://www.twitter.com/". The same thing will happen if I enter the search term "callbacks": I'll see a log entry saying "visit: http://www.cnn.com" These get logged even before I hit enter on the search. I'm assuming this is because Chrome is doing some sort of pre-fetching on commonly visited URLs, but I still think this is unexpected behavior.
Can someone confirm? Thanks.
Edit:
I tried some other webNavigation methods but I'm still seeing some inconsistencies:
onComitted - Fired almost all the time.
onHistoryStateUpdated - Fired some of the time
onTabReplaced - Was not firing as no pre-rendering was done.
Edit 2:
The following code helped me sort out events that were being fired on inactive tabs. (See comments 5/6 for more explanation.)
chrome.webNavigation.onCommitted.addListener(
function (info) {
// Checks for the URL bar navigation and not any child frames
if (info.frameId === 0){
chrome.tabs.query({active: true, currentWindow: true}, function (tab) {
// Ensures that the listener is only attached to the active tab
if (tab[0].id === info.tabId){
// Everything's good! Do your work here
}
});
}
}
);

Categories

Resources