Playwright: how can i wait until the content of an iframe has been loaded - iframe

I the following example an iframe with the src myurl will be injected in the page.
I can find the iframe dom element but without the explicit wait the page.frame function returns null.
I guess this is because page.frame does not wait for the iframe to be loaded.
How can i wait until the content of an iframe has been loaded?
await expect(page.locator('iframe')).toHaveCount(1);
await page.waitForTimeout(2000); // explicit wait should be removed
const frame = page.frame({url: new RegExp('.*/myurl.*', 'i')});
expect(frame).not.toBeNull();

Related

Blazor's OnInitializedAsync event fires before DOM is finished

I'm currently working on a hosted/client Blazor WebAssembly app, which when initially run works fine.
In the 'OnInitializedAsync' method of the page being rendered is a javascript call to retrieve the width of an html element on the page. If I set a breakpoint in the javascript function, on initial app run the browser is displaying the rendered page and the element properties are correctly retrieved. However, when I refresh the browser page, when the breakpoint is hit the browser hasn't finished rendering the page and so the element's width is zero.
Can anyone tell me why this is and how I can fix the problem?
<div #ref="_carPanelWidth">
....
</div>
#code {
private ElementReference _carPanelWidth;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
var carPanelWidth = await JsRuntime.InvokeAsync<int>("utilityFunctions.getElementWidth", new object[] { _carPanelWidth });
StateHasChanged();
}
}
window.utilityFunctions = {
getElementWidth: function (element) {
return element.offsetWidth;
}
};
I deal with a similar situation when rendering grids. It causes a flashiness which looks unprofessional.
What I do is mark the html elements as hidden and later show them, when I know everything is loaded. This completely eliminates flash, if that is the problem you are having.
<div id="table_id_div" style="visibility:hidden">
I use interopt to apply the DataTable.com stuff to the table and when that is complete the javascript runs...
$("#" + tableName + "_div").attr('style', 'visibility : visible');
This seems to do the trick.

ReportViewerForMvc event on report load

I am loading a report in an iframe using ReportViewerForMvc. Currently, I have a spinner so that the user will know the report is loading. However, the spinner stops spinning when the iframe is placed on the page...not when the content of the report is finished rendering. I have found people using isLoading with $find but I am pretty sure that is just for asp and I need my to be in .Net
What is the simplest way to have spinner continue to spin until the report is loaded in the iframe?
Currently, I have a shared view for all reports that I am hoping to add some javascript to:
#using ReportViewerForMvc;
<div id="reportViewer">#Html.ReportViewer(Model.ReportViewer as Microsoft.Reporting.WebForms.ReportViewer)</div>
iframe onload does not work to stop spinner here.You would need cookies and client side script to accomplish that.
The server code will set the value in the cookie .Once the report is rendered the value will be read on the client side(cshtml) and spinner can be stopped.
Read this article.Here you can replace the blocker with the spinner.
http://gruffcode.com/2010/10/28/detecting-the-file-download-dialog-in-the-browser/
//This should be called on the event when you are loading the report
//In your case you will route the url to controller or invoke the link
//for the report
$(document).ready(function () {
$('#create_pdf_form').submit(function () {
blockUIForDownload();
});
});
//This is where you will place the spinner
function blockUIForDownload() {
var token = new Date().getTime();
//use the current timestamp as the token value
$('#download_token_value_id').val(token);
$.blockUI();
fileDownloadCheckTimer = window.setInterval(function () {
var cookieValue = $.cookie('fileDownloadToken');
if (cookieValue == token)
finishDownload();
}, 1000);
}
//This will read the token generated from the server side controller or
//aspx.cs or ashx handler
function finishDownload() {
window.clearInterval(fileDownloadCheckTimer);
// $.removeCookie('fileDownloadToken'); //clears this cookie value
//$.cookie('fileDownloadToken', null);
//$.removeCookie("fileDownloadToken");
setCookie("fileDownloadToken", '2')
$.unblockUI();
}
//On the server side set the token , it could be controller or ashx handler
var response = HttpContext.Current.Response;
response.Clear();
response.AppendCookie(new HttpCookie("fileDownloadToken",
downloadTokenValue); //downloadTokenValue will have been provided in the
form submit via the hidden input field
response.Flush();
//Lastly don't forget to add these source js files.
<script src="~/Scripts/jquery-1.5.1.js"></script>
<script src="~/Scripts/jquery.blockUI.js"></script>
<script src="~/Scripts/jquery.cookie.js"></script>

Reloading a parents iframe from a child

I have two iframes on my page, frameOne and frameTwo.
I'm trying to refresh/reload frameOne from a script running in frameTwo.
function reload(){
srcLink = parent.document.getElementById('frameOne').src;
parent.document.getElementById('frameOne').src=srcLink;
}
This works but it refreshes to the initial value of the iframe.
IE:
If the parent page loaded with that iframe's src set to 'google.com' but then changed to (due to a user action) 'yahoo.com' my code will refresh that page to 'google.com', the link it was set to when the page was loaded. I would like to take into account the change of link and refresh that one.
Try this from frames[1] (the 2nd iframe page):
function changeFrameSrc(becauseURL, changeURL){
var bc = new RegExp(becauseURL, 'i');
if(bc.test(location)){
parent.frames[0].src = changeURL;
}
}

What happens when removing an object used by a ngRepeat in AngularJS?

EDIT: jsFiddle example here.
I have a ngRepeat that spawns directives containing iframes.
div(ng-repeat='element in elements')
ng-switch(on="element.type")
a-directive(ng-switch-when="something")
another-directive(ng-switch-when="somethingElse")
Now, inside the directive I am loading content into the iframe after some event, by doing:
$iframe[0].contentWindow.d_contents = "html"
$iframe[0].src = 'javascript:window["d_contents"]'
Everything works nicely.
When I remove one of these elements from the model (in the controller) with something like:
elements.remove(object) //using sugarjs, that's not the issue, same behaviour with splice
the UI gets updated accordingly, i.e. the element disappear.
The problem
This works as expected:
elements.push(ele1)
elements.push(ele2)
.. init iframes inside ele1 and ele2 with content ..
elements.remove(ele2)
Result: ele2 disappears from UI, ele1 still there with iframe loaded
This does not:
elements.push(ele1)
elements.push(ele2)
.. init iframes inside ele1 and ele2 with content ..
elements.remove(ele1)
Result: ele1 disappears from UI, ele2 still there with iframe, but iframe content is back to empty, and iframe.load() gets fired.
What is happening here? Why is my iframe getting reseted?
You need to add the loading logic inside the load() to reload the content when the DOM changes.
var linkfn = function (scope, element, attrs) {
init(function () {
var $iframe = element.find("iframe")
var refresh = function () {
var doc = $iframe[0].contentWindow.document;
doc.open();
doc.write(scope.ele.source);
doc.close();
}
$iframe.load(function () {
console.log(scope.ele.id + ": loaded ");
//refreshing
refresh();
});
//initial loading
refresh()
})
}
You need to use doc.write() for this scenario or you will get recursive loop error.
DEMO

Passing arguments from one asp.net page to another using jQuery

I need to pass 4 arguments (3 strings and one comma separated list) from an ASP.NET page to another ASP.NET page using jQuery. The destination page ought to be launched as a separate window, which works fine with the following jQuery snippet:
$('#sourcePageBtn').click(function(){
window.open("destinationPage.aspx");
return false;
});
How can I pass the arguments to the destination page? I am trying to avoid the query string to pass the arguments because:
I don't want to show the url arguments (which can also be very long) in the destination window.
There are some special characters like ',/,\, & etc. in the string arguments.
Please suggest.
Edit:
I'm trying to access the arguments in the script section of the aspx file i.e.
<script language="C#" runat="server">
protected void Page_Load ( object src, EventArgs e)
{
//Creating dynamic asp controls here
}
</script>
My specific need for the arguments in the Page_Load of the script section stems from the fact that I am creating a few dynamic Chart controls in the Page_Load which depend on these arguments.
cheers
Initial Thoughts (before solution created)
Use POST for large data instead of GET. With POST no querystring will be used for data and therefore URL length restriction isn't a concern. (The max URL length differs between browsers so you're right to stay away from it when large data is moving).
Special URL characters can be encoded to be passed in the query string so that shouldn't be an issue.
Alternatively you might store the data on the server side from the first page, and have the second page pick it up from the server side. But this is overkill. And it makes you do unneeded server programming.
Passing state via HTTP calls is standard practice. You shouldn't try to circumvent it. Work with it. All the facilities are built in for you. Now it's just up to jQuery to provide us some help...
Note: Be careful using jQuery for main app features in case JavaScript is disabled in the browser. In most cases your web application should be usable at a basic level even when JavaScript is disabled. After that's working, layer on JavaScript/jQuery to make the experience even better, even awesome.
Edit: Solution (with ASP.NET processing)
Key resources for solution implementation are:
How use POST from jQuery - initiates the request, passes arguments, gets response
jQuery context argument - this is how the popup window DOM is accessed/affected from the main window
How it works: From a main page, a POST occurs and results are displayed in a popup window. It happens in this order:
The main script opens a popup window (if it doesn't already exist)
main script waits for popup window to fully initialize
main script POSTs (using AJAX) arguments to another page (sends a request)
main script receives response and displays it in the popup window.
Effectively we have posted data to a popup window and passed arguments to the processing.
Three pages follow and they constitute the complete solution. I had all 3 sitting on my desktop and it works in Google Chrome stable version 3.0.195.38. Other browsers untested. You'll also need jquery-1.3.2.js sitting in the same folder.
main_page.html
This is the expansion of the logic you provided. Sample uses a link instead of a form button, but it has the same id=sourcePageBtn.
This sample passes two key/value pairs when the POST occurs (just for example). You will pass key/value pairs of your choice in this place.
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
</head>
<body>
<a id="sourcePageBtn" href="javascript:void(0);">click to launch popup window</a>
<script>
$(function() {
$('#sourcePageBtn').click( function() {
// Open popup window if not already open, and store its handle into jQuery data.
($(window).data('popup') && !$(window).data('popup').closed)
|| $(window).data('popup', window.open('popup.html','MyPopupWin'));
// Reference the popup window handle.
var wndPop = $(window).data('popup');
// Waits until popup is loaded and ready, then starts using it
(waitAndPost = function() {
// If popup not loaded, Wait for 200 more milliseconds before retrying
if (!wndPop || !wndPop['ready'])
setTimeout(waitAndPost, 200);
else {
// Logic to post (pass args) and display result in popup window...
// POST args name=John, time=2pm to the process.aspx page...
$.post('process.aspx', { name: "John", time: "2pm" }, function(data) {
// and display the response in the popup window <P> element.
$('p',wndPop.document).html(data);
});
}
})(); //First call to the waitAndPost() function.
});
});
</script>
</body>
</html>
popup.html
This is the popup window that is targeted from the main page. You'll see a reference to popup.html in the jQuery script back in the main page.
There's a "trick" here to set window['ready'] = true when the popup window DOM is finally loaded. The main script keeps checking and waiting until this popup is ready.
<html>
<head>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
</head>
<body>
<!-- The example P element to display HTTP response inside -->
<p>page is loaded</p>
</body>
<script>
$(function() {
window['ready'] = true;
});
</script>
</html>
process.aspx.cs (C# - ASP.NET process.aspx page)
The dynamic server page the arguments are POSTed to by the main page script.
The AJAX arguments arrive in the Page.Request collection.
The output is delivered back as plain text for this example, but you can customize the response for your apps requirements.
public partial class process : System.Web.UI.Page {
protected void Page_Load(object sender, EventArgs e) {
// Access "name" argument.
string strName = Request["name"] ?? "(no name)";
// Access "time" argument.
string strTime = Request["time"] ?? "(no time)";
Response.ContentType = "text/plain";
Response.Write(string.Format("{0} arrives at {1}", strName, strTime));
}
protected override void Render(System.Web.UI.HtmlTextWriter writer) {
// Just to suppress Page from outputting extraneous HTML tags.
//base.Render(writer); //don't run.
}
}
Results of this are displayed into the popup window by the original/main page.
So the contents of the popup window are overwritten with "[name] arrives at [time]"
Main References: HTTP Made Really Easy, jQuery Ajax members and examples.
If you keep a reference to the new window when you open it, ie var destWin = window.open(...) then you can access the variables and methods on the destWin window object. Alternatively you can "reach back" from the destination window with window.opener.

Resources