I am trying to execute javascript Appium test using WD. My code should look something like this:
"use strict";
var wd = require("wd");
var chai = require("chai");
var chaiAsPromised = require("chai-as-promised");
var jQuery = require("jQuery");
chai.use(chaiAsPromised);
chai.should();
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
var desired = {
"appium-version": "1.0",
platformName: "iOS",
platformVersion: "7.1",
deviceName: "iPhone Retina (3.5-inch)",
app: "/Users/{myuser}/Library/Developer/Xcode/DerivedData/{MyApp}/Build/Products/Debug-iphonesimulator/MyApp.app",
};
var browser = wd.promiseChainRemote("0.0.0.0", 4723);
browser.init(desired).then(function() {
var getIframe = function(){
//var r = jQuery('#myIframe');
return {'response':"1"};//only for testing that this method works...
};
return browser
.elementByName("Show Webview").click()
.sleep(10000)
.contexts().then(function (contexts) { // get list of available views. Returns array: ["NATIVE_APP","WEBVIEW_1"]
return browser.context(contexts[1]); // choose the webview context
})
.execute(getIframe).then(function(res){
console.log(res);
})
.sleep(10000)
.fin(function() {
//return browser.quit();
});
}, function(e){console.log(e);}).done();
I am running this code using node mytest.js.
The problem is that I cannot execute js code. In this case I am getting the following error:
Error: [execute()] Not JSON response
What am I doing wrong?
Comments:
What I finally am trying to do here is access and manipulate an iFrame in my code (in the same domain), using iFrame.contentDocument
My insperation for using 'execute like this is from this post
tnx,
Yaniv
UPDATE:
I have managed to execute javascript using "safeExecute" method, instead of "execute".
My issue now is that I do not have access to "window" object, so I cannot run "jQuery" or "window.document.getElementById"...
Related
I'm launching native apps with the help of WebdriverIO and mocha, but unable to communicate with the device, but able to launch the application but not interact with the element.
android_app_test.js
const webdriverio = require('webdriverio');
const androidOptions = require('../../../helpers/caps').androidOptions;
const assert = require('chai').assert;
androidOptions.capabilities.appPackage = "com.google.android.calculator"
androidOptions.capabilities.appActivity = "com.android.calculator2.Calculator"
describe('Create Chrome web session', function () {
let client;
before(async function () {
client = await webdriverio.remote(androidOptions)
});
after(async function () {
await client.deleteSession();
});
it('should create and destroy Android browser session', async function () {
const elem = await $('#digit_2')
elem.waitForDisplayed(3000);
await client.touchClick('digit_2');
});
});
config.js
var Mocha = require('mocha'), fs = require('fs');
var mocha = new Mocha({
reporter: 'mochawesome-screenshots',
reporterOptions: {
reportDir: 'customReportDir',
reportName: 'customReportName',
reportTitle: 'customReportTitle',
reportPageTitle: 'customReportPageTitle',
takePassedScreenshot: true,
clearOldScreenshots: true,
shortScrFileNames: true,
jsonReport: false,
multiReport: false
},
timeout: 600000,
})
var file = ['./test/basic/app/']; //location of the test js
for (var i = 0; i < file.length; i++) {
fs.readdirSync(file[i]).forEach(function (filename) {
mocha.addFile(file[i] + filename);
});
}
mocha.run(function (failures) {
process.on('exit', function () {
process.exit(failures);
});
});
package.json
"scripts": {
"test": "mocha config.js"
},
Not sure about that, i think something was wrong in my configuration or else
The $ global is added through the WebdriverIO test runner. Since you're using wdio through standalone mode, you don't get access to those globals. Try this instead:
const elem = await client.$('#digit_2')
Make sure you're using the newest version of Webdriver.io. Webdriver.io v5 is the latest version that also implements the $('selector') shortcut.
If you're using Webdriver.io v4 - you may still need to use browser.element('selector') to find your elements.
It appears from the tags in your question, and the code you posted you maybe on version 4.
$ is usually used as a shorthand to run JQuery functions (such as your $('#digit_2'), in the "android_app_test.js" file).
From the WebdriverIO's doc:
The $ command is a short way to call the findElement command in order to fetch a single element on the page. It returns an object that with an extended prototype to call action commands without passing in a selector. However if you still pass in a selector it will look for that element first and call the action on that element.
To fix this you have to install JQuery with this commands:
In a terminal run:
npm install --save jquery
npm install --save-dev #types/jquery
then import it at the top of your "android_app_test.js" file like this
import * as $ from "jquery";
I am trying to write a JXA script which extends the bounds the current window of the current application vertically so it reaches from the top to the bottom of the screen. If I run the following script in Automator as a "Run JavaScript" quick action, it works:
var app = Application.currentApplication();
var window = app.windows[0];
var orig_bounds = window.properties().bounds;
var vertical_res =
Application("Finder").desktop.window.properties().bounds.height;
window.bounds = {
"x": orig_bounds.x,
"y": 0,
"width": orig_bounds.width,
"height": vertical_res
};
I want this script to be bound to a hotkey. When I bind it in System Preferences -> Keyboard -> Shortcuts -> Services -> General and try to activate it while some app is active (say, iTerm 2), it doesn't work, and I get the error:
The action “Run JavaScript” encountered an error: “Error on line 4: TypeError: undefined is not an object (evaluating 'window.properties')”
Note that if I modify the script to always operate on a specific app (var app = Application("Google Chrome");) and run it in Automator, it works.
You need to get the application currently in use (the front most application), as the current application is the one running the Javascript code. This is why the code works when it's run in Automator and when a certain application is hard-coded.
To get the application in use you can use the two lines below:
var frontAppName = Application("System Events").processes.whose({frontmost: {'=': true }})[0].name();
var frontApp = Application(frontAppName);
I can't be certain about the error but I understand that it is generally considered good practice to include the Standard Definitions, and I've included it in the revised code below which doesn't cause this error when using a hot key combination.
function run(input, parameters) {
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var frontAppName = Application("System Events").processes.whose({frontmost: {'=': true }})[0].name();
var frontApp = Application(frontAppName);
var window = frontApp.windows[0];
var orig_bounds = window.properties().bounds;
var vertical_res = Application("Finder").desktop.window.properties().bounds.height;
var orig_x = orig_bounds.x;
var orig_width = orig_bounds.width;
frontApp.windows[0].bounds = {
x: orig_x,
y: 0,
width: orig_width,
height: vertical_res
};
}
In my JS test, I need to check if the console.info is called. That's why I want to mock console. However, it seems that the console variable cannot be assigned with a different object. Did I make any mistake?
Here is the code I used:
var oldConsole = console;
var infoContent;
console = {
info: function(content) {
infoContent = content;
}
};
game.process('a command');
infoContent.should.equal('a command is processed');
console = oldConsole;
You can use rewire to replace the whole of console to silence it, or to inject a mock. I use deride but sinon would also work.
var rewire = require('rewire');
var deride = require('deride');
var Game = rewire('../lib/game');
describe('game testing', function() {
var stubConsole, game;
beforeEach(function() {
stubConsole = deride.stub(['info']);
stubConsole.setup.info.toReturn();
Game.__set__({
console: stubConsole
});
game = new Game();
});
it('logs info messages', function() {
game.process('a command');
stubConsole.expect.info.called.withArgs(['a command is processed']);
});
});
I find the solution. I can change the method info of console.
console.info = function(content) {
infoContent = content;
};
The question is now why console object itself cannot be reassigned?
you can use sinon npm to count the call to a function :
it("calls the original function only once", function () {
var callback = sinon.spy();
var proxy = once(callback);
proxy();
proxy();
assert(callback.calledOnce);
// ...or:
// assert.equals(callback.callCount, 1);
});
You can find the docs here : sinonjs.org
I thought I had the same problem and my solution was using this std-mocks module:
https://github.com/neoziro/std-mocks
This has the advantage of not taking over the global "console" but allows you to see what gets logged to the stdout / stderr. This solves the problem in a different way than the question was explicitly looking for; however I believe it is a good answer for the problem the question implies and may be useful for others.
const stdMocks = require('std-mocks');
stdMocks.use(); console.log('test'); stdMocks.restore();
// => undefined [nothing gets output, stdout intercepted]
const logged = stdMocks.flush();
console.log(logged)
// => { stdout: [ 'test\n' ], stderr: [] }
I have a method in a products.js file like so:
var handler = function(errors, window) {...}
and would like to execute it within a jsdom env callback:
jsdom.env({
html : "http://dev.mysite.com:3000/products.html",
scripts : [ "http://code.jquery.com/jquery.js", "page-scrapers/products.js" ],
done : function(errors, window) {
handler(errors, window)
}
});
When executed, it tells me 'handler is not defined'. Am I getting close?
Context of the problem is to scrape data from an existing web site. We want to associate a javascript scraper for each page, and access the scraped data via URLs served up via a node.js server.
As suggested by Juan, the key is using node.js modules. The bulk of the hander method is exported from product.js:
exports.handler = function(errors, window, resp) {...
and then imported in the node.js-based server instance:
//note: subdir paths must start with './' :
var products = require('./page-scrapers/products.js');
This creates a reference to the method by name 'products.handler', which can then be called in the request handler:
var router = new director.http.Router({
'/floop' : {
get : funkyFunc
}
})
var funkyFunc = function() {
var resp = this.res
jsdom.env({
html : "http://dev.mySite.com:3000/products.html",
scripts : [ "http://code.jquery.com/jquery.js"],
done : function(errors, window) {products.handler(errors, window, resp)}
});
}
And that works.
If you want a variable to be accessible to another file, you have to export it. http://nodejs.org/api/modules.html
//products.js
exports.handler = function(window, error) {...}
// another.file.js
var products = require('products.js');
jsdom.env({
html : "http://dev.mysite.com:3000/products.html",
scripts : [ "http://code.jquery.com/jquery.js", "page-scrapers/products.js" ],
// This can be simplified as follows
done : products.handler
});
This sounds like a bad idea though, why would a handler be made into a global? I think you should restructure your code
Is it possible to user jasmine-node with the jQueryify feature of jsdom? What I'm trying to do is use NodeJS to test some JavaScript that depends on the presence of the DOM.
Here is a reduced case of what I tried. When I run the script, jasmine-node recognizes the spec, but doesn't run the expect():
var fs = require('fs'),
jsdom = require('jsdom'),
window = jsdom.createWindow(),
jasmine = require('jasmine-node')
describe("jQueryify", function() {
it('should run the test!', function () {
jsdom.jQueryify(window, function (window, jquery) {
expect(1).toEqual(1)
})
})
})
Alternately, is there a different/better way to test stuff in NodeJS that assumes a browser-like environment?
OK, based on one this answer to a semi-related question, I was able to get the expect() to execute. I guess the answer to my question is not to use the built-in jQueryify function, but to pull in jQuery 'manually'.
var fs = require('fs'),
window = require('jsdom').jsdom('<html><head></head><body></body></html>').createWindow(),
script = window.document.createElement('script'),
jasmine = require('jasmine-node')
describe("jQueryify", function() {
it('should run the test!', function () {
script.src = './path/to/jquery.js'
script.onload = function () {
expect(typeof window.$).toEqual('function')
asyncSpecDone()
}
window.document.head.appendChild(script)
asyncSpecWait()
})
})