Previously-defined variable not working in other parts of test in nightwatchjs - nightwatch.js

I'm using nightwatchjs and the ssh2 npm package to test that a file name is present in a location, and asserting that it's correct.
My code is as follows;
var Client = require('ssh2');
var conn = new Client();
var longFileNamePrdArray;
module.exports = {
before:
conn.on('ready', function(browser) {
console.log('Client :: ready');
conn.sftp(function(err, sftp) {
if (err) throw err;
sftp.readdir('parkersreviewcontent/', function (err, list) {
if (err) throw err;
list.map(a => a.longname);
longFileNamePrdArray = list[1].longname;
conn.end();
});
});
})
.connect({
host: '*hostname*',
port: 22,
user: '*user*',
password: '*password*',
}),
'Test Zen Reviews production file is listed': function (browser) {
console.log(longFileNameStgArray);
},
'Closing the browser': function (browser) {
browser.browserEnd();
},
};
However, this results in undefined being outputted for longFileNamePrdArray.
If I move the console.log command inside the before code block, then the filename is correctly displayed as follows;
-rwxr--r-- 1 - - 2492238 Feb 28 06:37 parkers-reviews-staging.xml
but when I move it outside the before block and into my test block, it fails with the undefined output.
I thought by defining longFileNamePrdArray and stating it at the beginning of the test script it work 'carry across' the longFileNamePrdArray value into the test block, but it's not.
Any help and assistance would be really appreciated. Thanks.

I fixed this by using the done callback.
So my working code looks like this;
var Client = require('ssh2');
var conn = new Client();
var longFileNamePrdArray;
module.exports = {
before: function(browser, done) {
conn.on('ready', function(browser) {
console.log('Client :: ready');
conn.sftp(function(err, sftp) {
if (err) throw err;
sftp.readdir('parkersreviewcontent/', function (err, list) {
if (err) throw err;
list.map(a => a.longname);
longFileNamePrdArray = list[1].longname;
conn.end();
done();
});
});
})
.connect({
host: '*hostname*',
port: 22,
user: '*user*',
password: '*password*',
}),
'Test Zen Reviews production file is listed': function (browser) {
console.log(longFileNameStgArray);
},
'Closing the browser': function (browser) {
browser.browserEnd();
},
};

Related

Cypress not waiting for Before block to complete

I am trying to achieve the following functionality
Before Block : Call the Cy.visit("/login") and call a Function which will trigger a REST API and process the REST API response and set the local storage.
Only after the local storage is set click on "My Account" Link
Here is the source Code I am trying.
import * as subscriberHelpers from '../../../helpers/subscriberHelpers';
import * as localStorage from '../../../helpers/localStorage';
describe('testCode', () => {
before((done) => {
cy.visit('/login', {
timeout: 10000,
onLoad: () => {
localStorage.write("CD-Environment", Cypress.env('defaultEnvironment'));
localStorage.write("CD-Language", "en-US");
localStorage.write("CD-SystemId", "85788485-e411-48a9-b478-610c1285dc1a");
}
})
subscriberHelpers.createSubscriber().then(()=>{
done();
})
})
it('sClick on my account link', () => {
cy.get('.c-header-listItem > .c-link').contains("My Account").click();
})
})
Here is the code to createSubscriber function
export function createSubscriber() {
let URL = `SOME URL`;
let body = {
Some Body
}
return new Promise((resolve, reject) => {
request.subscriberServiceRequest(URL, body).then((response) => {
if (response.status === 200 && ("SessionId" in response.body)) {
localStorage.write("CD-SessionId", response.body.SessionId);
localStorage.write("CD-SubscriberId", response.body.Subscriber.Id);
resolve();
}
else if (response.status === 200 && ("Fault" in response.body)) {
reject(response.body.Fault.Message);
}
})
})
}
Here is the code to subscriber Service request function
export function subscriberServiceRequest(url, body, headers = null) {
let defaultHeaders = { "CD-SystemId": "85788485-e411-48a9-b478-610c1285dc1a" }
if (headers != null) {
defaultHeaders = addHeaders(defaultHeaders, headers);
}
return new Cypress.Promise((resolve, reject) => {
cy.request({
url: url,
method: 'POST',
body: body,
headers: defaultHeaders
}).then((response) => {
resolve(response);
});
})
}
When I try Executing the code I am getting following error in cypress
But the element existing in the UI
Questions:
Why I am getting the error
How to call more than one async functions
in before block
How to tell cypress to wait till the functions on
before block get processed meaning not only wait till receiving the
response but wait till the response got processed in the THEN block
To answer your first question:
Why I am getting the error
.contains() specifically searches for elements within but not including the tag it is called on. In other words, someElement.contains("My Account") will not match someElement.
What you should have instead is this:
cy.get('.c-header-listItem').contains("My Account").click();
Or simply:
cy.contains("My Account").click();

What is the correct field to pass --staging-location parameter for a Dataflow job in Node.js?

I wonder if I've hit a bug. A wrote a Node.js piece of code to trigger a "GCS Text to PubSub" Dataflow. The function is triggered upon file upload into a GCS bucket.
But it never executes successfully: "textPayload: "problem running dataflow template, error was: { Error: Invalid JSON payload received. Unknown name "staging_location": Cannot find field." It is an issue with the syntax of I specify the staging location for the job. I have tried "staginglocation", "stagingLocation", etc...none of them have worked.
Here's my code. Thanks for your help.
var {google} = require('googleapis');
exports.moveDataFromGCStoPubSub = (event, callback) => {
const file = event.data;
const context = event.context;
console.log(`Event ${context.eventId}`);
console.log(` Event Type: ${context.eventType}`);
console.log(` Bucket: ${file.bucket}`);
console.log(` File: ${file.name}`);
console.log(` Metageneration: ${file.metageneration}`);
console.log(` Created: ${file.timeCreated}`);
console.log(` Updated: ${file.updated}`);
google.auth.getApplicationDefault(function (err, authClient, projectId) {
if (err) {
throw err;
}
console.log(projectId);
const dataflow = google.dataflow({ version: 'v1b3', auth: authClient });
console.log(`gs://${file.bucket}/${file.name}`);
dataflow.projects.templates.create({
projectId: projectId,
resource: {
parameters: {
inputFile: `gs://${file.bucket}/${file.name}`,
outputTopic: `projects/iot-fitness-198120/topics/MemberFitnessData`,
},
jobName: 'CStoPubSub',
gcsPath: 'gs://dataflow-templates/latest/GCS_Text_to_Cloud_PubSub',
stagingLocation: 'gs://fitnessanalytics-tmp/tmp'
}
}, function(err, response) {
if (err) {
console.error("problem running dataflow template, error was: ", err);
}
console.log("Dataflow template response: ", response);
callback();
});
});
callback();
};
I don't think this is actually possible.
Looking at the documentation for the Dataflow API itself, there's nothing like a staging location in the parameter section, and the library you're using is basically a wrapper for this API.
I'm a bit surprised it changes the name of the parameter though.
So i finally got this to work. It was indeed a syntax issue in the parameters section. The code below works like a charm:
var {google} = require('googleapis');
exports.moveDataFromGCStoPubSub = (event, callback) => {
const file = event.data;
const context = event.context;
console.log(`Event ${context.eventId}`);
console.log(` Event Type: ${context.eventType}`);
console.log(` Bucket: ${file.bucket}`);
console.log(` File: ${file.name}`);
console.log(` Metageneration: ${file.metageneration}`);
console.log(` Created: ${file.timeCreated}`);
console.log(` Updated: ${file.updated}`);
google.auth.getApplicationDefault(function (err, authClient, projectId) {
if (err) {
throw err;
}
console.log(projectId);
const dataflow = google.dataflow({ version: 'v1b3', auth: authClient });
console.log(`gs://${file.bucket}/${file.name}`);
dataflow.projects.templates.create({
gcsPath: 'gs://dataflow-templates/latest/GCS_Text_to_Cloud_PubSub',
projectId: projectId,
resource: {
parameters: {
inputFilePattern: `gs://${file.bucket}/${file.name}`,
outputTopic: 'projects/iot-fitness-198120/topics/MemberFitnessData2'
},
environment: {
tempLocation: 'gs://fitnessanalytics-tmp/tmp'
},
jobName: 'CStoPubSub',
//gcsPath: 'gs://dataflow-templates/latest/GCS_Text_to_Cloud_PubSub',
}
}, function(err, response) {
if (err) {
console.error("problem running dataflow template, error was: ", err);
}
console.log("Dataflow template response: ", response);
callback();
});
});
callback();
};

uploading profile pic in hapijs 17.0

I am using hapijs version 17.0.1. I am trying to upload an image using ajax request on a hapijs route. Here is my AJAX code to upload profile pic:
var image_file_input = document.getElementById("user_profile_upload");
image_file_input.onchange = function () {
if(this.files != undefined)
{
if(this.files[0] != undefined)
{
var formData = tests.formdata ? new FormData() : null;
if (tests.formdata)
{
//alert(file)
formData.append('image_file', this.files[0]);
formData.append('userId', user_id);
formData.append('memberId', member_id);
}
$.ajax({
url: "/v1/User/uploadUserPic",
data: formData,
type: "POST",
dataType: "json",
contentType: false,
processData: false,
contentType: "multipart/form-data",
success: function(data){
console.log(data);
var errMsg = null;
var resData = null;
if(data.statusCode == 200)
{
resData = data.result;
}
else
{
alert(data.message)
}
},
error: function(error){
alert(error);
}
});
}
}
}
And here is my Hapijs route Code:
var uploadUserPic = {
method: 'POST',
path: '/v1/Module/uploadUserPic',
config: {
description: 'Update Image For User',
tags: ['api', 'User'],
auth: 'session',
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
userId : Joi.string().regex(/^[a-f\d]{24}$/i).required(),
memberId: Joi.string().required(),
image_file: Joi.object().required(),
},
failAction: FailCallBack
}
},
handler: function (request, reply) {
var resultData = null;
var error = null;
return new Promise(function (resolve) {
var multiparty = require('multiparty');
var fs = require('fs');
var form = new multiparty.Form();
form.parse(request.payload, function (err, fields, files) {
if(err)
{
error = err;
resolve();
}
else
{
var mkdirp = require('mkdirp');
var img_dir = "./files/users/";
mkdirp(img_dir, function (err) {
if (err)
{
error = err;
console.error(err);
resolve();
}
else
{
var oldpath = files.image_file.path;
var newpath = "./files/users/"+requestPayload.userId+".png";
fs.rename(oldpath, newpath, function (err) {
if(err)
{
error = err;
}
resolve();
});
}
});
}
});
}).then(function (err, result) {
if(err) return sendError(err);
if(error) return sendError(error)
return {
"statusCode": 200,
"success": true
};
});
}
}
The above code gives me following error cannot read property 'content-length' of undefined on line form.parse(request.payload, function (err, fields, files) {});
Please let me know If I am doing something wrong. If I replace the url in ajax request with anohter url that I have written in php then it works perfectly. which means that something is wrong with my hapijs/nodejs code.
There's a good post on how to handle file uploads in Hapi.js (written in version 16) https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js
Since you are using payload.parse = true, I am not seeing a particular reason why you have to use multiparty. I have the following working code that would save files (of any type) uploaded from client into uploads directory on the server (Please do not use directly on production as no sanitation is done)
{
path: '/upload',
method: 'POST',
config: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
files: Joi.array().single()
}
}
},
handler: function(request) {
const p = request.payload, files = p.files
if(files) {
console.log(`${files.length} files`)
files.forEach(async file => {
const filename= file.hapi.filename
console.log(`Saving ${filename} to ./uploads`)
const out = fs.createWriteStream(`./uploads/${filename}`)
await file.pipe(out)
})
}
return {result: 'ok'}
}
}
You can use the following curl command to test
curl http://localhost:8080/upload -F 'files=#/path/to/a/note.txt' -F 'files=#/path/to/test.png' -vvv
There are a few issues with your code. First in your $.ajax call, you have specified contentType twice, although it's not a syntax error but it's careless to code like that. Second the function's signature inside your .then() block is incorrect. You are mixing the idea of Promise and callback. I don't think the following line will be triggered
if(err) return sendError(err);
One last trivial thing, you said you are using Hapi 17 but based on the handler function's signature
handler: function (request, reply) {
...
Seems you are not totally onboard with Hapi17 as the new signature is
handler: function (request, h) {
And it's not just the rename of reply to h.

Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves

I have tried to insert the value in db in my mocha test i am getting this error i tried few of the following ways but nothing work out.
var assert=require('chai').assert;
const user=require('../model/user')
i tried both way
describe('insertDataLasone',()=>{
it('should save the value ',(done)=>{
var User = new user({fname:'test'});
User.save().then(done=>{
done()
}).catch(done=>done())
})
})
describe('User', function() {
describe('#save()', function() {
// this.timeout(5000)
it('should save without error', function(done) {
var User5 = new user({fname:'test'});
User5.save(function(done) {
if (err) done(err);
else setTimeout(done,3000);
});
});
});
});
This error occurs when done() is not called in a test. Make sure you are calling done().
var assert = require('chai').assert;
const User = require('../model/user');
describe('insertDataLasone', () => {
it('should save the value ', done => {
var user = new User({ fname: 'test' });
user.save().then(() => {
done();
})
.catch(done); // mocha done accepts Error instance
});
});
or
var assert = require('chai').assert;
const User = require('../model/user');
describe('User', function() {
describe('#save()', function() {
it('should save without error', function(done) {
var user5 = new User({ fname: 'test' });
user5.save(function(err) {
if (err) done(err);
else done();
});
});
});
});
Read https://mochajs.org/#asynchronous-code carefully

Unit-testing remote methods of a strongloop loopback.io model

I am trying to write unittests for a loopback model using jasmine. My model has the usual CRUD endpoints but I have defined a custom '/products/:id/upload' endpoint which expects a form with files.
My model looks like
'use strict';
var loopback = require('loopback');
var ProductSchema = {
location: {
type: String,
required: true
},
version: {
type: String,
required: true
},
id: { type: Number, id: 1, generated: true }
};
var opts = {
strict: true
};
var dataSource = loopback.createDataSource({
connector: loopback.Memory
});
var Product = dataSource.createModel('Product', ProductSchema, opts);
Product.beforeRemote('upload', function(ctx){
var uploader = function(req, res){
// parse a multipart form
res({
result:'success'
});
};
function createProduct(uploaderResult){
// create a product out of the uploaded file
ctx.res.send({
result: uploaderResult.result
});
}
uploader.upload(ctx.req, createProduct);
});
Product.upload = function () {
// empty function - all the logic takes place inside before remote
};
loopback.remoteMethod(
Product.upload,
{
accepts : [{arg: 'uploadedFiles', http: function(ctx){
return function() {
return { files : ctx.req.body.uploadedFiles, context : ctx };
};
}},
{arg: 'id', type: 'string'}],
returns : {arg: 'upload_result', type: String},
http: {path:'/:id/upload', verb: 'post'}
}
);
module.exports = Product;
My end goal is to test the logic of the "createProduct".
My test looks like
'use strict';
describe('Product Model', function(){
var app = require('../../app');
var loopback = require('loopback');
var ProductModel;
beforeEach(function(){
app = loopback();
app.boot(__dirname+'/../../'); // contains a 'models' folder
ProductModel = loopback.getModel('Product');
var dataSource = loopback.createDataSource({
connector: loopback.Memory
});
ProductModel.attachTo(dataSource);
});
it('should load file ', function(){
console.log(ProductModel.beforeRemote.toString());
console.log(ProductModel);
ProductModel.upload();
});
});
By calling ProductModel.upload(); I was hoping to trigger the before remote hook which would exercise the the createProduct. I could test "createProduct" in isolation but then I would omit the fact that createProduct ends up being called as a result of upload.
To be perfectly clear, the core question is:
How do I exercise remote method hooks inside unittests ?
It was suggested to use supertest as an http server. Below there is a code snippet illustrating how to do it in jasmine
describe('My product suite', function(){
var request = require('supertest');
var app;
beforeEach(function(){
app = loopback();
// don't forget to add REST to the app
app.use(app.rest());
});
it('should load file', function() {
request(app).post('/products/id-of-existing-product/upload')
.attach('file', 'path/to/local/file/to/upload.png')
.expect(200)
.end(function(err, res) {
if (err) return done(err);
// res is the HTTP response
// you can assert on res.body, etc.
});
});
});

Resources