Calling custom command in nightwatch.js - command

What I'd like to do is call a custom command within another custom command in nightwatch.js (with the aim of making the tests less brittle).
For example, in my first custom command (message1.js), I could have the following code;
exports.command = function(browser) {
this
browser
console.log('display this first')
return this;
and subsequently in my second custom command (message2.js), I want to call on the message1.js command first, then perform the rest of the code.
For example;
exports.command = function(browser) {
this
browser
//call the message1.js command
console.log('display the second message')
return this;
I've tried calling it with the method;
exports.command = function(browser) {
this
browser
.message1();
console.log('display the second message')
return this;
but this didn't work.
So my question(s) is;
Is it possible to call one custom command in another custom command
And if not, is there another way of doing this?

Yes, you can chain your custom commands together or with other nightwatch methods:
Custom method openUrl.js:
exports.command = function(url) {
const browser = this;
browser.url(url);
return this;
}
Custom method openStackOverflow.js, that calls openUrl.js:
exports.command = function(url) {
const browser = this;
browser.openUrl(url).waitForElementVisible('#submit-button');
return this;
}
Using them in the tests:
'Open Stack Overflow': browser => {
browser.openStackOverflow(`https://stackoverflow.com`);
},

Related

Protractor. How to restart browser between tests?

I wrote a pack of tests and separately each of them works correctly (for each test I created separate file - it important for my case) , but when I try to run them all together I have some problems:
At the first I tried to restart browser after each test with “restartBrowserBetweenTests: true” option but as a result I receive next error:
Failed: This driver instance does not have a valid session ID (did you
call WebDriver.quit()?) and may no longer be used.
I read some advice for this situation, but nothing helped me.
Basically, I can get without restarting browser but in this case I want to close all active tabs after each test and I also don’t know ho to do it. With the help of this function:
browser.getAllWindowHandles().then((handles) => {
browser.driver.switchTo().window(handles[1]);
browser.driver.close();
browser.driver.switchTo().window(handles[0]);
});
I can close active tab and return for the previous but the function works only if I place this code in the test spec, that's not comfortable for me (copy it for each test) and if I call it from special file with functions it works incorrectly due to the
“Failed: no such window: target window already closed
from unknown error: web view not found”
errors (a consequence of a lost context).
https://www.protractortest.org/#/api?view=ProtractorBrowser.prototype.restart
use instead:
browser.restart()
also try to use await
it('Test Puppeteer screenshot', async function() {
await browser.get('https://www.google.com')
let current = await browser.getWindowHandle()
await browser.executeScript("window.open('https://www.indiatoday.com')")
handles = await browser.getAllWindowHandles()
await browser.switchTo().window(handles[1]);
await browser.close();
await browser.switchTo().window(handles[0]);
});
Note if you are using page object the nyou have to reinitialize modules:
if you are using javascript object notation for page then
https://www.npmjs.com/package/decache
const decache = require('decache');
let stage1 = require('../pageobjects/stage1.js');
beforeEach(async function () {
decache('../pageobjects/stage1.js');
stage1 = require('../pageobjects/stage1.js');
elements = stage1.elements;
});
If you are using nodejs class:
let stage1 = require('../pageobjects/stage1.js');
beforeEach(async function () {
stage1 = new PageObjects.stage1();
});
This because the reference for elements have changed you have to reinitialize them

Loopback Operation hook receives options as empty

I'll try to explain this weird situation as simple as I can.
I've created an operation hook "before save" and make it in a mixin to add it to some models.
this mixin uses context.options to get current userId to do something.
this mixin is working perfectly if I call the operation directly (like POST /Accounts for example).
But if I call it inside a remote method, the context.options is empty, for example, if we have a method called POST /Accounts/Signup, and inside it, we call Account.create(...), the "before save" hook receives the options as empty object {}
A sandbox project has been hosted here
https://github.com/mustafamagdy/loopback-sandbox-issue
the mixin code snippet is as follows:
module.exports = function(Model, options) {
Model.observe("before save", async function(ctx) {
if (ctx.instance.id) return;
const userId = ctx.options && ctx.options.accessToken && ctx.options.accessToken.userId;
if (userId) {
//... do stuff
}
else
{
console.error("Failed to scope " + Model.name + " to user (null)");
}
});
};
After the investigation, I found this issue that talks about similar behaviour, however, the comments are very destractive. So I thoughlt to write the conclusion here for anyone who are facing the same issue.
Loopback require you to pass the options you declared from the remote method to the model method(s) if you want to receive it on operation hook, so I ended up doing so.
module.exports = function(Note) {
Note.makeNew = makeNew;
async function makeNew(options) {
await Note.create(obj, options);
}
};

How to fetch a Backbone Collection from sequelize-restful-extended in one call

I have a model called Instance which works fine.
define([], function(){
return Backbone.Model.extend({
urlRoot:'/api/Instances',
parse:function(content){
return content.data;
}
});
});
My REST at here
http://localhost:3000/api/Instances/1
returns this
{"status":"success","data":{"id":1,"name":"bangladesh","write":null,"read":null,"createdAt":"2015-09-01T23:03:16.000Z","updatedAt":"2015-09-01T23:03:16.000Z","UserId":1}}
hence the parse function in my model. All good so far.
If I just call
http://localhost:3000/api/Instances
Then I get a block of all my records,
{"status":"success","count":212,"data":[
{"id":1,"name":"bangladesh","write":null,"read":null,"createdAt":"2015-09-01T23:03:16.000Z","updatedAt":"2015-09-01T23:03:16.000Z","UserId":1},
{"id":2,"name":"abqride","write":null,"read":null,"createdAt":"2015-09-01T23:03:58.000Z","updatedAt":"2015-09-01T23:03:58.000Z","UserId":1},
....
And my collection code is just this
define(['models/instance.js'], function(Model){
return Backbone.Collection.extend({
url:'/api/Instances',
model:Model,
parse:function(content){
return content.data;
}
})
});
The above code for the Collection will create 212 models. I've checked that with an initialise function in the model just to see if it was being called OK with the right data, and it is.
But in my view code when I go
this.collection.each(function(model) {
console.log("model id="+model.get("id")+" count="+count++);
out+=model.get("id")+"="+model.get("name")+"<br>";
});
there's nothing in these models, but there are 212 of them, I just get "undefined=null" 212 times.
I can see three options, two of which involve customizing the use of Backbone: 1. Calling collection.sync manually and then executing custom code afterward, 2. Passing a custom option in the initial collection.fetch() and looking for it in the Model parse() method.
These two options don't solve the problem at the source, however, since the concern is at the initial response level. In order to apply a response-level filter to your data, try overriding Backbone.ajax() before starting your application:
Backbone.ajax = function () {
var settings = arguments[1] || arguments[0]; // jQuery.ajax(url[, settings])
var success = settings.success;
settings.success = function (data, status, xhr) {
if (success) { success(data.data, status, xhr); }
};
return Backbone.$.ajax.apply(Backbone.$, arguments);
};

Passing Chrome FileSystem API FileEntry to Web worker

I'm trying to use a WebWorker to parse certain files from a directory the user selected, like so:
function readEntries(reader, callback) {
reader.readEntries(function(results) {
results.forEach(function(entry) {
if (entry.isDirectory) { readEntries(entry.createReader(), callback); }
else { callback(entry); }
});
});
}
readEntries(initialDir.createReader(), function(entry) {
var nodeWorker = new Worker('worker.js');
nodeWorker.onmessage = function(e) { /* do stuff */ }
nodeWorker.postMessage(entry);
});
The initialDir is a DirectoryEntry from chrome.fileSystem.chooseEntry({'type': 'openDirectory'}, ....
The corresponding worker.js would use FileReaderSync to parse the passed file(s) and pass analysis results back to /* do stuff */.
If I execute this, I get the following DOMException:
Error: Failed to execute 'postMessage' on 'Worker': An object could not be cloned.
Since entry.toURL() returns an empty string, how am I supposed to pass local, unsandboxed files to the web worker? Is there any other way to achieve what I want?

events_create fails in facebook

strugglin to create event in javascript as
api.events_create(eventInfo,function(result,ex){
is failing and
catch(FacebookRestClientException){
gives
TypeError: api.events_create is not a function message=api.events_create is not a function
any clue
Some more context would help in debugging this.
You've created the api object, yes? (e.g., var api = FB.Facebook.apiClient;)
I'm having the same problem. If I look at the list of functions attached to FB.Facebook.apiClient using a DOM inspector, events_create() does not exist - even though other methods like events_get() and feed_publishUserAction() are there.
Facebook might have deliberately omitted it.
api.callMethod works - have put a sample call , hope it helps
var eventInfo = {
"name":this.name.value,
"category":"1",
"subcategory":"2",
"host":"My Host",
"location":"JP Nagar",
"city":"Bang",
"start_time":starttime,
"end_time":endtime};
function createEvent(eventinfo) {
try{
//check if user has extended permission to create otherwise prompt him for same
api.users_hasAppPermission('create_event',function(res,ex){
if (res == 0)
FB.Connect.showPermissionDialog("create_event",
function(res,ex){alert("Congratulations events");});
});
dict = {};
dict['event_info'] = eventinfo;
//provide a call back or a sequencer
var ret = api.callMethod(
'events.create',
dict,
function(eventid,ex){
console.log(data);
});
return ret;
}
catch(FacebookRestClientException){
console.log(FacebookRestClientException);
}
return;
}//createEvent routine