I'm finding it incredibly slow to fix issues that are specific to the iOS portion of my app. I'd like the know the recommended way to debug Worklight apps when the browser debugger isn't available.
In particular, I'm working on issues with WL.JSONStore which only works on iOS and Android. I can't use the browser debugger to see what's going on. When I do WL.Logger.debug() statements, nothing is showing up in the Xcode console, and the iPad simulator console (Cordova) only displays a few lines. There have also been periods this week that no output is printed anywhere.
I have downloaded and installed Weinre too, but none of the print statements appear to show up in its console and in general I just don't see information about the areas I need.
Thanks in advance for your suggestions.
General Worklight 5.0.6 Debugging
Look at the training module titled Debugging your applications. (Direct PDF link)
Debug Tips for JSONStore on Worklight 5.0.6
Try console.log('message') or WL.Logger.debug('message') inside jsonstore.js and your code ([app-name].js, etc.). The output should show up in Xcode's console and Android's LogCat.
Reset the Simulator or Emulator and/or call WL.JSONStore.destroy().
Make sure you're running on a supported environment:
Android >=2.2 ARM/x86 Emulator or Devices
iOS >=5.0 Simulator or Device
Try turning encryption off (ie. do not pass a password to WL.JSONStore.init or WL.JSONStore.initCollection).
Look at the SQLite Database file generated by JSONStore. This only works if encryption is off.
Android:
$ adb shell
$ cd /data/data/com.[app-name]/databases/wljsonstore
$ sqlite3 jsonstore.sqlite
iOS
$ cd ~/Library/Application Support/iPhone Simulator/6.1/Applications/[id]/Documents/wljsonstore
$ sqlite3 jsonstore.sqlite
Try looking at the searchFields with .schema and selecting data with SELECT * FROM [collection-name];. To exit sqlite3 type .exit. Take a look at this StackOverflow question for an example.
(Android Only) Enable verbose JSONStore.
adb shell setprop log.tag.jsonstore-core VERBOSE
adb shell getprop log.tag.jsonstore-core
(iOS >=6.0 and Safari >=6.0 Only) Try to use the JavaScript debugger. Set break points inside jsonstore.js. Helpful lines:
Bridge to Native code:
cdv.exec(options.onSuccess, options.onFailure, pluginName, nativeFunction, args);
Success Callbacks returning from Native code:
deferred.resolve(data, more);
Failure Callbacks returning from Native code:
deferred.reject(new ErrorObject(errorObject));
Write Proper Tests (Unit, Functional, Integration -- get test coverage). Here's a template that uses QUnit and Sinon.js to create a Sandbox environment where you can test how JSONStore handles different types of data/calls:
<!DOCTYPE HTML>
<html>
<head>
<title>JSONStore Test App</title>
<link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.11.0.css">
<script src="http://code.jquery.com/qunit/qunit-1.11.0.js"></script>
<script src="http://sinonjs.org/releases/sinon-1.6.0.js"></script>
<script>
//QUnit configuration flags, no need to change it.
QUnit.config.requireExpects = true;
</script>
</head>
<body id="content" style="display: none;">
<!-- Test results will be appended to the div below, no need to make changes here. -->
<div id="qunit"></div>
<script>
//Start Worklight
WL.Client.init({connectOnStartup : false});
//Hook into the deviceready event
document.addEventListener("deviceready", onDeviceReady, false);
//onDeviceReady will be called when JSONStore/Cordova is ready
function onDeviceReady () {
//Auto executing function that holds the test
(function (jQuery) { //The variable jQuery is usable inside.
//Mock WL.Client.invokeProcedure using a Stub.
//This is only useful if you need to link a Worklight Adapter
//to a JSONStore collection to reproduce your issue or bug.
//API Doc: http://sinonjs.org/docs/#stubs
var fakeAdapter = sinon.stub(WL.Client, "invokeProcedure", function (invocationData, options) {
//DO NOT Create a real adapter, just mock the reponse here if it's relevant to the bug.
var ADAPTER_RESPONSE = {invocationResult: {fakeKey: [{fn: 'carlos'}, {fn: 'mike'}]}};
options.onSuccess(ADAPTER_RESPONSE);
});
//[**Explain your test here**]
var EXPECTED_ASSERTIONS = 2; //every assertion is a deepEqual below.
asyncTest('[**Meaningful title here**]', EXPECTED_ASSERTIONS, function () {
//Destroy first to make sure we don't depend on state
WL.JSONStore.destroy()
.then(function () {
//[**Start writting your test here**]
//The test below is an example, it does the following:
// - Initializes a collection linked to a fake adapter (see stub above).
// - Checks if initialization worked by checking the collection name.
// - Loads data from the fake adapter (see stub above).
// - Checks if load worked by checking the number of documents loaded.
var collections = {
col1 : {
searchFields : {fn: 'string'},
adapter : {name: 'fakeAdapter',
load: {
procedure: 'fakeProcedure',
params: [],
key: 'fakeKey'
}
}
}
};
return WL.JSONStore.init(collections);
})
.then(function (response) {
//Prep for your assertion
var ACTUAL_VALUE = response.col1.name;
var EXPECTED_VALUE = 'col1';
var COMMENT = 'Checking for the right collection name';
//Do your assertion using deepEqual
//API Doc: http://api.qunitjs.com/deepEqual/
deepEqual(ACTUAL_VALUE, EXPECTED_VALUE, COMMENT);
return WL.JSONStore.get('col1').load();
})
.then(function (response) {
//Prep for your assertion
var ACTUAL_VALUE = response; //load returns number of documents loaded
var EXPECTED_VALUE = 2; //two documents are returned by the fake adapter (stub)
var COMMENT = 'Checking if load worked';
//Do the assertion using deepEqual
deepEqual(ACTUAL_VALUE, EXPECTED_VALUE, COMMENT);
start();//call start() after you finish your test succesfully
})
.fail(function (error) {
deepEqual(false, true, 'Failure callback should not be called' + error.toString());
start();//call start() after you finish your test with a failure
});
});
}(WLJQ)); //end auto executing function that holds the test
} //end wlCommonInit
</script>
</body>
</html>
Expected output of the code above:
Side-note: Here's a general article about the PhoneGap/Cordova workflow for a specific developer. There's a part of debugging, just browser-based though. Some of it applies to IBM Worklight development too.
cnandreu provides great tips here. Still, visibility is pretty poor and these approaches didn't really solve my problem. I would like to also suggest what I've found to be most useful in my project (aside from WL.Logger.debug() everywhere):
JSConsole has been indispensable (http://jsconsole.com/). In reality, I don't actually use it that much like it's intended. However, I've found that it's startup warning message does something with WL.Logger.debug() (and console.log()) that enables the statements to actually print to the console so I can see what I'm doing.
In iOS 6 Safari on the Mac lets you inspect the DOM of an attached device. It's moderately useful, especially for hybrid UI issues that only are misbehaving when running natively on iOS. I don't find it super helpful otherwise. See more at https://developer.apple.com/library/safari/#documentation/AppleApplications/Reference/SafariWebContent/DebuggingSafarioniPhoneContent/DebuggingSafarioniPhoneContent.html
The single most useful technique I've been using has been to write status messages to the UI. Yes, it's an ugly prehistoric way to do things, but everything else - including 80s error print statements to the console - have failed miserably. Here's what I do (using Dojo & JavaScript):
var v = dom.byId('audio_status');
if (v) { v.innerHTML += "recording file ["+filename+"]"; }
Where audio_status is the ID of a DIV that displays the debug content.
This stuff is ugly, but at least we can see something.
Related
I have set up my jasmine framework using the steps mentioned here, but when I try to use jasmine keywords like browser.open to open a URL in the Browser I get an error browser not defined. When I use require to get another page, it gives Reference error: module not found.
Also, with my jasmine package, I did not get the specRunner.html.
I have tried installing protractor also and different approaches, but it's not working.
I need to set up jasmine framework for UI automation, can anyone help me with the exact set up and issues that I am facing right now?
The jasmine library is not related to browser automation. It is a library about testing. It does not define the browser object. I'm not sure which library you expect to require. Perhaps you want to use Selenium. In particular, you will want to look at the WebDriver API.
It allows you to do things like this:
var driver = new webdriver.Builder().build();
driver.get('http://www.google.com');
var element = driver.findElement(webdriver.By.name('q'));
element.sendKeys('Cheese!');
element.submit();
driver.getTitle().then(function(title) {
console.log('Page title is: ' + title);
});
driver.wait(function() {
return driver.getTitle().then(function(title) {
return title.toLowerCase().lastIndexOf('cheese!', 0) === 0;
});
}, 3000);
driver.getTitle().then(function(title) {
console.log('Page title is: ' + title);
});
driver.quit();
I have written multiple spec files for unit testing various modules on the webpage. If i run them individually, one at a time they work fine. But when i try to run all the files in a sequence, only the first file in the spec folder works while all other tests fail. Any help would be appreciated.
Every spec file loads a static page using requirejs and renders them on the page. Once the page is rendered i check whether the title, text etc is proper or not. The spec files looks like this.
AboutSpec.js-->
require(["views/About", "nls/messages"], function (About, messages) {
beforeEach(function(){
var temp = new About();
temp.render();
});
describe("Test for About Page", function () {
it("Check For About Title", function () {
var aboutTitleText = $('.eight.columns h2').text();
expect(aboutTitleText).toEqual(messages["about_title"]);
});
});
});
FooterSpec.js-->
require(["views/Footer", "nls/messages"], function (Footer, messages) {
beforeEach(function(){
var temp = new Footer();
temp.render();
});
describe("Test for Footer Page", function () {
it("Check For Footer Content", function () {
var footerText = $('.five.columns h2').text();
expect(footerText).toEqual(messages["footer_content"]);
});
});
});
jstestDriver.conf-->
load:
- jasmine/lib/jasmine-1.3.1/jasmine.js
- jasmine/lib/adapter/JasmineAdapter.js
- js-src/javaScript/require.js
- js-src/javaScript/default.js
test:
- js-test/AboutSpec.js
- js-test/FooterSpec.js
When i run this setup, the About page does not render. Only the Footer page renders due to which all the test cases of about page fails.
We're facing the exact same problem, and I've spent--how many hours, now? Oh yeah, too many!-- trying to solve this problem.
Today I discovered Karma, a JsTD replacement from Google which runs every test in a fresh iframe. It also integrates with Jenkins. I'm in the process of installing it now and will report back on how it went.
My query is that I have a link that redirects to another page. In webkit browsers, how to force the sitecatalyst server call (script execution) to finish before the redirect happens?
I am using sitecatalyst for a portal. I have
configured the custom link call to include the doneAction parameter for
successful call completion on webkit browsers (As mentioned in Adobe guide).
The custom link code for onClick event of button is as below:
<script language="javascript" >
function search(keyword)
{
var s=s_gi('testing');
s.linkTrackVars="prop11,events";
s.linkTrackEvents="event6";
s.prop11=keyword;
s.events="event6";
s.tl(this,'o','Search',navigate());
window.location=searchresults.html;
}
</script>
<script language="javascript" >
function navigate()
{
return false;
/*To induce a delay to ensure that image request is sent to Adobe before the
user leaves the page.(As given in Adobe guide - code release H.25))
Code version H.25 (released July 2012) includes an overloaded
track link method ( s.tl ) that forces WebKit
browsers to wait for the track link call to complete.)*/
}
</script>
However, even after this, I am getting error in custom link tracking. The redirect happens before the call can complete.
Please help on the same. Thanks in Advance.
Regards,
Harshil
Okay so firstly there are a number of issues with how you implemented it. Here is an example of how it should look like:
search
<script type='text/javascript'>
function search(keyword) {
var s=s_gi('testing');
s.linkTrackVars="prop11,events";
s.linkTrackEvents="event6";
s.prop11=keyword;
s.events="event6";
s.tl(this,'o','Search',null,navigate);
return false;
}
function navigate(){
window.location="searchresults.html";
}
</script>
Some points
You didn't actually post the link or whatever you are using that calls the search function so I have a link shown as an example.
You passed the navigate function as the 4th argument when it should be the 5th (with a null or blank string as a placeholder for the 4th)
It should be navigate not navigate(). The way you did it, you are making a call to the function and passing the result of the function as an argument. s.tl requires the actual function or reference to a function, and it will make the call to the function. In fairness, the Adobe document is typoed: it shows the example wrapped in quotes which doesn't work.
The redirect should be placed in navigate, not in search.
Replace the link href with javascript function
function trackLink(e) {
nextUrl = e.href;
e.href = "javascript:sendData('" + nextUrl + "')";
}
function sendData(url) {
s.tl(this, "o", "Link Name", null, function() {
window.location.href = url;
});
}
or try out the following
function sendData(obj) {
s.tl(obj, "o", "Link Name", null, "navigate");
return false;
}
Link
Link tracking is a dinosaur form of tracking as the numbers are barely accurate these days if you are not putting analytics before user experience. What I don't understand is that why don't you measure this on the next page instead of the link, unless you have no control over the next step?
As for your question: Previous examples on how to prevent link following before event is executed are quite solid, but remember if you have other JS code binded, be sure not to break it. As for the syntax, you can pass all variables to s.tl function as an object without setting linkTrackVars and linkTrackEvents for the s-object, which might have negative effects on events if case you use the code on dynamic pages.
E.g.
...
var data = {
linkTrackVars="prop11,events",
linkTrackEvents="event6",
prop11=keyword,
events="event6"
};
s.tl(this, "o", "Search", data, "navigate");
...
Note: You can't actually use props and events together in standard reporting. As per the code you pasted in comments to Crayon I can see that you are using eVars, so I assume that the example was not that accurate.
I am developing a mobile web app using Kendo UI Mobile. Whenever we make any AJAX calls, or our DataSources make them we call app.startLoading() to show the loading icon to the user. This works very well.
However, depending on the context in which the call is made we would like to change the text that is displayed along with the loading icon. I know you can define this when I create the kendo.mobile.Application instance. How can I change it afterwards?
The documentation does not suggest a way to do this, and a browse of the source code did not help me either. Is this really not possible?
EDIT: This is using Kendo UI Mobile v.2012.3.1114
I usually make a "utility" function to do this:
var _kendoApp = new kendo.mobile.Application(document.body, {});
var showLoading = function (message) {
_kendoApp.loading = "<h1>" + (message ? message : "Loading...") + "</h1>";
_kendoApp.showLoading();
};
I am also setting a default message of "Loading..." if one isn't passed in.
Edit:
I could have sworn that worked for me in a past app I did, but judging by thr source, I think you are right, my answer above shouldn't work. My best suggestion is to add a class to the message element so you can target it, and use jQuery to change the text.
var _kendoApp;
var showLoading = function (message) {
$(".loading-message").text(message ? message : "Loading...");
_kendoApp.showLoading();
};
_kendoApp = new kendo.mobile.Application(document.body, {
loading: '<h1 class="loading-message">Loading...</h1>'
});
I have protocol (like http) with scheme managed with 3rd party App registered in Mac OS X.
I.e, x-someapp://someaction or something like that.
How can I open this URL with Google Chrome?
By default, Chrome starts searching in Google engine instead launching App and passing URL handling to it...
Safari launches some registered App. And it is right thing.
Firefox and Opera asks what to do... and I can launch App also.
But Chrome... Doesn't ask.
I even tried to write some HTML page with JavaScript inside to send XHttpRequest:
function _httpExecuteCallback()
{
if (httpRequestCallbackFunction != null) {
if (httpRequest.readyState == 4) {
if (httpRequest.status == 200) {
httpRequestCallbackFunction();
httpRequestCallbackFunction = null;
}
}
}
}
function _httpGet(url, callbackFunction)
{
httpRequest = false;
httpRequestCallbackFunction = callbackFunction;
httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = _httpExecuteCallback;
httpRequest.open('GET', url, true);
httpRequest.send(null);
}
_httpGet('x-someapp://test',function(){})
No results also...
The current accepted solution has a problem with Chrome for SSL https. Watching the console log, Chrome blocks the request because it thinks the custom url protocol is not secure:
[blocked] The page at reports blah blah ran insecure content from customproto//blah blah
Here is a solution (this took me a few days to research):
<input type='button' value='Test Custom Url' onclick='exec()'>
<script>
function submitRequest(buttonId) {
var d = (window.parent)?window.parent.document:window.document
if (d.getElementById(buttonId) == null || d.getElementById(buttonId) == undefined) return;
if (d.getElementById(buttonId).dispatchEvent) {
var e = d.createEvent("MouseEvents");
e.initEvent("click", true, true);
d.getElementById(buttonId).dispatchEvent(e);
}
else {
d.getElementById(buttonId).click();
}
}
function exec(){
var d = (window.parent)?window.parent.document:window.document
var f = d.getElementById('customUrlLink')
if (f ) {f.parentNode.removeChild(f);}
var a = d.createElement('a');
a.href = 'mycustomproto://arg1';
a.innerHTML = "Link"
a.setAttribute('id', 'customUrlLink');
a.setAttribute("style", "display:none; ");
d.body.appendChild(a);
submitRequest("customUrlLink");
}
</script>
This code will not work for IE. I've found using this technique IE limits the argument of the custom protocol to less than 1000 where as using the iFrame technique IE will allow 2083 chars.
The only way to overcome the url limit in javascript is chuck the data and call multiple times. If anyone wants to take a stab at that, please let me know how it goes. I would like to use it.
To handle long urls in the executing app, pass a token into the app and have it go get the data from a url GET.
So for right now I am using one function for Chrome/FF and another function for IE.
These links helped me develop this solution:
https://superuser.com/questions/655405/custom-protocol-handler-not-working-in-chrome-on-ssl-page
Simulating a click in jQuery/JavaScript on a link
(wish I had known this a few days ago....hope this helps someone)
==================================================
Update: (8hr later)
==================================================
Jake posted a great solution for chrome: https://superuser.com/questions/655405/custom-protocol-handler-not-working-in-chrome-on-ssl-page
This works in chrome only:
window.location.assign("customprotocol://");
It will fail in an iframe so this is working:
var w = (window.parent)?window.parent:window
w.location.assign(service + '://' + data)
==================================================
Update: (weeks later)
==================================================
All of the examples of opening the custom protocol, including my own, have a "://" in the url. And this is what is causing the SSL warnings.
Turns out the solution is to change "://" to ":"
so do this:
src="x-myproto:query" .....
and the SSL warnings will go away.
==================================================
Follow: (after months of production use)
==================================================
This has been working well for chorme. Detect the browser and if chrome do this:
var w = (window.parent)?window.parent:window
w.location.assign('myproto://xyzabcdefetc')
For IE and other browsers I do something slightly different.
Note that browsers do impose a limit on how much data you can put in custom url protocol. As long as your string is under 800 chars this seems to be the magic number for which works in all browsers.
It looks like it's Google's locationbar parsing which is getting in the way.
The browser, however, does seem to handle custom URL schemes properly. Try this in your locationbar:
javascript:document.location = 'myscheme://whatever'
Any link on your page that uses the custom scheme should also do the right thing.
I found the solution that works with Chrome.
I use the IFRAME-way.
Example (with JQuery):
$("body").append('<span id="__protoProxy"></span>');
function queryWord(aWord)
{
var protoProxy = document.getElementById('__protoProxy');
if (protoProxy)
{
var word = aWord.replace('"','\"');
protoProxy.innerHTML = '<div style="display:none;"><iframe src="x-myproto://query?' + word + '"></iframe></div>';
}
}
queryWord('hello');
Here's a solution that also includes a redirect to the App Store / Play Store if the user doesn't have the app. It uses a setTimeout for this. It also makes use of an iframe to support more browsers. So this works on Chrome, and any other mobile browser. We use this as my company, Branch. Just modify the two links below to correspond to your URI and App Store link.
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
window.onload = function() {
// Deep link to your app goes here
document.getElementById("l").src = "my_app://somepath";
setTimeout(function() {
// Link to the App Store should go here -- only fires if deep link fails
window.location = "https://itunes.apple.com/us/app/myapp/id123456789?ls=1&mt=8";
}, 500);
};
</script>
<iframe id="l" width="1" height="1" style="visibility:hidden"></iframe>
</body>
</html>
Again, this should work on any browser, thanks to the iframe.
If Chrome does not recognize the URL scheme, it defaults to a search.
This is what I see in Safari:
alt text http://img62.imageshack.us/img62/6792/clipboard02oh.jpg
and in Firefox:
alt text http://img138.imageshack.us/img138/9986/clipboard04xk.jpg
I believe the reason why Chrome defaults to search is that there are special google searches that use the colon.
E.g:
define: dictionary
filetype:pdf google chromium
This is one of the annoyances I have with Firefox, I have to jump to the "search box" rather than the address bar to execute these types of searches. Since Chrome does not have a separate search box like Firefox, IE and Safari have, this functionality is required.
Ajax requests won't get you around this.
Some weeks later ....
Looks like window.location.replace('myscheme://whatever') has full cross-browser support , works with chrome,firefox,safari,edge,opera see https://developer.mozilla.org/en-US/docs/Web/API/Location/replace