iOS UI Automation wait until web view is ready and rendered - javascript

I am creating an iOS UI Automation javascript using Instruments to automate taking a screenshot for my iOS app. The tool I am using to automate taking a screenshot is Snapshot.
I am using a webview for part of my app and I want to take a screenshot of fully rendered webview before proceeding to the rest of the script.
So currently my script looks like:
var target = UIATarget.localTarget();
target.frontMostApp().mainWindow().scrollViews()[0].webViews()[0].links()[0].tap();
// take screen shot here
target.frontMostApp().navigationBar().leftButton().tap();
But when it takes the screen shot, the webview was not fully rendered so it's taking an empty screen and go back to the main screen.
Is there a way to wait until the webview is fully loaded, then take a screen shot and continue rest of the script?

The Illuminator framework (full disclosure, I wrote it) provides a function called waitForChildExistence() in its Extensions.js that allows you to continuously evaluate a reference to an element until it actually appears.
You would do something like this to wait up to 10 seconds for the webview to load:
var webView = target.frontMostApp().mainWindow().scrollViews()[0].webViews()[0];
webView.tap();
webView.waitForChildExistence(10, true, "a specific link in the web view", function(wb) {
// the argument wb will contain the reference to our var webView
return wb.links()["the link you are waiting for"];
});
// take screen shot here
target.frontMostApp().navigationBar().leftButton().tap();

You can use computed style, put some inert style property (I used clip), and you can check with intervals of 1 second (if you use css files better create a class with it and use the class on element).
The function below i user to get the style computed.
function getStyle (el, prop) {
try {
if (getComputedStyle !== undefined) {
return getComputedStyle(el, null).getPropertyValue(prop);
}
else {
return el.currentStyle[prop];
}
} catch (e) {
return el.currentStyle[prop];
}
}
The timer you can try this
Be aware of that computed style may vary by browsers.
Hope it helps...

Related

Open a custom URL using window.open() in Firefox

In Firefox 88 it seems that opening a custom deep-linking URL with window.open(customURL, '_parent') reloads the current tab? Is there any solution to this problem? Should I use an <iframe> instead of window.open()? The behavior is different in Chrome however.
Required behaviour: window.open() opens the deeplinking app while the web app (written in Angular) continues to work in the same frame.
Recommend an Iframe
Some browsers are particularly weary of window.open as its abused quite heavily and thus blocked. However, an iframe is pretty normal and if using an app scheme and supported, has the side-effect of opening up the app.
Here's an approach you could try.
function checkIsAppSupported(appSchemeUrl, msThreshold) {
return new Promise((resolve) => {
let docBlurOccurred = false
const iframe = document.createElement('iframe', {
src: appSchemeUrl,
style: 'display:none'
})
document.appendChild(iframe)
function onDocBlur() {
docBlurOccurred = true
}
document.addEventListener('blur', onDocBlur)
// or try the newer
// document.addEventListener('visibilitychange', onDocBlur)
setTimeout(() => {
// cleanup
iframe.remove()
document.removeEventListener('blur', onDocBlur)
resolve(docBlurOccurred)
}, msThreshold); // checks if document focus changed within ms
});
}
async function tryAppSupported() {
const isAppSupported = await checkIsAppSupported('my-app://', 500);
if (isAppSupported) {
// do something
}
}
tryAppSupported()
Meaning if the main window becomes out of focus near the time of iframe loading, you could reasonably assume the actual app scheme was loaded and moved focus off the current window.
There are some npms that do this type of thing for example
https://www.npmjs.com/package/open-native-app and has great support for caring about what type of OS checks this behavior.
Happy coding.

How to launch uwp webview app in full screen mode by default

We have created a full fledged UWP webview app (WinJs) with help of Visual Studio 2017. Recently going through microsoft documentation and stackoverflow threads, we found out that the uwp app can be launched full screen with title bar removed.
Following code needed to inserted into App.Xaml.Cs file
ApplicationView view = ApplicationView.GetForCurrentView();
view.TryEnterFullScreenMode();
But the problem here is that, we are unable to locate this file to insert this. May be its because of WinJS template i have chosen, i don't know.
Other notables files include main.js | packageapp.manifest file. I do not know whether this code could be integrated with either of this file.
Edit:
With roy's help, the main js file is modified according to the sample given in windows universal sample github, but still full screen does not seem to open up.
The main.js file code is as given below
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
var isFirstActivation = true;
var ViewManagement = Windows.UI.ViewManagement;
var ApplicationViewWindowingMode = ViewManagement.ApplicationViewWindowingMode;
var ApplicationView = ViewManagement.ApplicationView;
function onLaunchInFullScreenModeChanged() {
ApplicationView.preferredLaunchWindowingMode = launchInFullScreenMode.checked ? ApplicationViewWindowingMode.fullScreen : ApplicationViewWindowingMode.auto;
}
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.voiceCommand) {
// TODO: Handle relevant ActivationKinds. For example, if your app can be started by voice commands,
// this is a good place to decide whether to populate an input field or choose a different initial view.
}
else if (args.detail.kind === activation.ActivationKind.launch) {
launchInFullScreenMode.addEventListener("click", onLaunchInFullScreenModeChanged);
launchInFullScreenMode.checked = ApplicationView.preferredLaunchWindowingMode == ApplicationViewWindowingMode.fullScreen;
// A Launch activation happens when the user launches your app via the tile
// or invokes a toast notification by clicking or tapping on the body.
if (args.detail.arguments) {
// TODO: If the app supports toasts, use this value from the toast payload to determine where in the app
// to take the user in response to them invoking a toast notification.
}
else if (args.detail.previousExecutionState === activation.ApplicationExecutionState.terminated) {
// TODO: This application had been suspended and was then terminated to reclaim memory.
// To create a smooth user experience, restore application state here so that it looks like the app never stopped running.
// Note: You may want to record the time when the app was last suspended and only restore state if they've returned after a short period.
}
}
if (!args.detail.prelaunchActivated) {
// TODO: If prelaunchActivated were true, it would mean the app was prelaunched in the background as an optimization.
// In that case it would be suspended shortly thereafter.
// Any long-running operations (like expensive network or disk I/O) or changes to user state which occur at launch
// should be done here (to avoid doing them in the prelaunch case).
// Alternatively, this work can be done in a resume or visibilitychanged handler.
}
if (isFirstActivation) {
// TODO: The app was activated and had not been running. Do general startup initialization here.
document.addEventListener("visibilitychange", onVisibilityChanged);
args.setPromise(WinJS.UI.processAll();
launchInFullScreenMode.addEventListener("click", onLaunchInFullScreenModeChanged);
launchInFullScreenMode.checked = ApplicationView.preferredLaunchWindowingMode == ApplicationViewWindowingMode.fullScreen;
}
isFirstActivation = false;
};
function onVisibilityChanged(args) {
if (!document.hidden) {
// TODO: The app just became visible. This may be a good time to refresh the view.
}
}
app.oncheckpoint = function (args) {
// TODO: This application is about to be suspended. Save any state that needs to persist across suspensions here.
// You might use the WinJS.Application.sessionState object, which is automatically saved and restored across suspension.
// If you need to complete an asynchronous operation before your application is suspended, call args.setPromise().
};
app.start();
})();
Any suggestions on how to solve this problem.
You could put the code in a JS file that will be referenced by the HTML that you are showing.
There used to be a JS UWP FullScreenMode Sample here: FullScreenMode JS. Although it is archived, you could still take a look at scenario2-launch.js and scenario1-basic.js. It shows how to these APIs are used in JavaScript.

Qt function runJavaScript() does not execute JavaScript code

I am trying to implement the displaying of a web page in Qt. I chose to use the Qt WebEngine to achieve my task. Here's what I did :
Wrote a sample web page consisting of a empty form.
Wrote a JS file with just an API to create a radio button inside the form.
In my code, it looks like this :
View = new QWebEngineView(this);
// read the js file using qfile
file.open("path to jsFile");
myJsApi = file.Readall();
View->page()->runjavascript (myjsapi);
View->page()->runjavascript ("createRadioButton(\"button1\");");
I find that the runJavaScript() function has no effect on the web page. I can see the web page in the output window, but the radio button I expected is not present. What am I doing wrong?
I think you will have to connect the signal loadFinished(bool) of your page() to a slot, then execute runJavaScript() in this slot.
void yourClass::mainFunction()
{
View = new QWebEngineView(this);
connect( View->page(), SIGNAL(loadFinished(bool)), this, SLOT(slotForRunJS(bool)));
}
void yourClass::slotForRunJS(bool ok)
{
// read the js file using qfile
file.open("path to jsFile");
myJsApi = file.Readall();
View->page()->runJavaScript(myjsapi);
View->page()->runJavaScript("createRadioButton(\"button1\");");
}
I had this problem, runJavascript didn't have any effect. I had to put some html content into the view (with page().setHtml("") before running it.
Check the application output, it might contain JavaScript errors. Even if your JS code is valid, you might encounter the situation where the script is run before DOMContentLoaded event, that is document.readyState == 'loading'. Therefore, the DOM might not be available yet, as well as variables or functions provided by other scripts. If you depend on them for your code to run, when you detect this readyState, either wait for the event or try calling the function later, after a timeout. The second approach with timeout might be needed if you need to get the result of the code execution, as this can be done only synchronously.

Delay while loading locally stored file on webView

I am loading locally stored html(files with javascript) file on my webView. These files are loaded in my ViewController.swift. However, there is a delay of ~2-3 seconds when the app is launched for the first time. I have searched over the web regarding an approach to fix this issue, with the approaches I observed(First loading of the webView delays the loading of resources), I have decided to use NSNotificationCenter in my ViewController and call them from 'AppDelegate' so that the webView is loaded first time.
Upon implementation of these changes in my code, there is delay in the LaunchScreen of the application, which is as expected as I am loading a file here, however, the loading time of ViewController is still the same.
I am calling the Notifications
AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
NSNotificationCenter.defaultCenter().postNotificationName("loadDefaultWebViewID",object:nil)
return true
}
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "loadDefaultWebView:", name: "loadDefaultWebViewID", object: nil)
if let htmlURL = NSBundle.mainBundle().URLForResource("index", withExtension: "html") {
let requestObject = NSURLRequest(URL: htmlURL)
webViewHTML5Player.loadRequest(requestObject)
}
I wish to reduce the delay that on the ViewController.If this is not the correct way, Is there any other better optimized approached to achieve this task ?
Edit 1
I have tried using WKWebView(with preferences.javaScriptEnabled = true) and UIWebView for this purpose, loading time is still the same.
This is a common problem occuring in UIWebView. My point is to make the web view initially hidden (showing some sort of "Loading..." indicator instead), then make it un-hidden in the webViewDidFinishLoad: method. and also you can refer this link UIWebView lower loading time with a more efficient way and Preloading webView doesn't work - trying to reduce loading time

Saving animated SVG from a webpage

There's an Internet Site with a Chinese dictionary that uses animations to show you the proper way to write Chinese characters. I'm not entirely sure how the animations work, but they appear to SVG-based. You can find an example of the animation here:
http://ce.linedict.com/dict.html#/cnen/entry/e1d0a1716f89470d88087dce285914a1
Be sure to click "Strokes" to view the animation.
So here"s the challenge I'm facing: I was wondering if there is any way to download the animations so that they can be viewed offline. The reason I want to do this is to add them to Anki, a flashcard app that I use on my iPad when I'm on the go (i.e. offline).
I tried examining the element in Firefox and saving the inner HTML of the element as a separate file, but that results in an static page. Doing the same in Safari is impossible as the inner HTML keeps changing as the animation is rendered, which makes me think that the inner HTML is being rendered on the fly by a separate (?) Java / (?) Jquery script nested somewhere on the site. Unfortunately, my knowledge of Java is rudimentary at best, and although I've started a tutorial online to get myself acquainted with the code, I think it'd be ages before I can figure out what's going on here. That's why I'm looking for help from you guys who have more experience with webpages than I do.
So is there any way to save the animation for offline use, either as a code or a svg / animated gif file? Anki, the flashcard app I'm using, uses HTML to render its content in a similar manner to web browsers (and as far as I know there are ways to integrate JavaScript).
I guess that I could use screen capturing programs to record the animation and save it as a .gif file, but given the fact that there are about 3500-5000 Chinese characters that a learner must master to be fluent in Chinese, that would take a hell of a time, so I'm looking for something that can be Scriptable (I have some working understanding of Apple Scripting, and could take it from myself there once I understand how to the animation works).
I'd appreciate any help or suggestion that could bring me any further.
---Edit on 2015-05-23:
Like I mentioned in a reply to Robert below, I've had a look at the DOM structure of a different entry, this time for a single character rather than a multi-character word. When the stroke order animation button on the page is activated, the following code can be found in the DOM structure:
<input type="hidden" id="hidden_strokeFileName" value="1200爸.swf">
as you can see there seems to be a reference to a file name with a flash-based animation. If the animation is indeed contained in an .swf file, it should be possible to download it for offline use, right? However, I'm baffled as to how the browser stitches up the URL under which the animation can be found so that I could download it. Can anyone help?
Here's the link to the page: http://ce.linedict.com/dict.html#/cnen/entry/cca145dd67574395a5a28af08a3afb30
I created a simple tool (script snippet, 16 lines of code): https://gist.github.com/VityaSchel/bc2a83e330661c2bae580ebb4aab05cf
It records svg changes using MutationObserver interface. I used it to record animations of "d" argument but you can modify this script to record all changes in svg structure and attributes.
One major issue with this tool is that it records unevenly, so some parts may be slightly slower than others.
let recordedChanges = []
let startRecordingSVG = () => { recordedChanges = [] }
let stopRecordingSVG = () => { console.log(JSON.stringify(recordedChanges)) }
let observer = new MutationObserver(mutationRecords => {
for(const record of mutationRecords) {
if(record.attributeName === 'd') {
recordedChanges.push(record.target.getAttribute('d'))
}
}
})
observer.observe($0, {
subtree: true,
attributes: true
})
/* USAGE */
// 1. Select target svg (or better it's parent, because svg may be overwritten) in Elements panel
// 2. Paste this into Console
// 3. Paste startRecordingSVG() into console
// 4. Do something so animation plays
// 5. Paste stopRecordingSVG() into console
// 6. An array with all animation keyframes will be printed in JSON format, use it to transform to lottie animation or CSS keyframes
In addition to that I created a function to convert this array to CSS keyframes:
function arrayFramesToCSSKeyframes(array) {
let frames = '', i = 1
for(let framePath of array) {
const percentage = i/array.length*100
const frameCode = []
frames += ` ${Math.round(percentage)}% {\n`
frames += ` d: path("${framePath}");\n`
frames += ` }\n\n`
i++
}
return `#keyframes frames {\n${frames}\n}`
}

Categories

Resources