CucumberJS: Take screenshot after each step - cucumber

Has anybody figured out how to take screenshot for every step of a scenario in cucumberjs, and attach it to the json report?

Apparently this can be done via a custom reporter, using Cucumber event handlers.
browser.takeScreenshot().then((png: any) => {
var decodedImage = new Buffer(png, 'base64');
currentStep.embeddings.push({
data: decodedImage.toString('base64'),
mime_type: 'image/png'
});
});

Related

Getting a bad request 400 when trying to upload zipped wkt file to here-maps rest api

We have a problem with the fleet.ls.hereapi.com when uploading a new layer for geofencing.
const myId = 'MYLAYER'; // just a id to check
zip.file('data.wkt', `NAME\tWKT\n${myId}\t${wkt}`);
const content = await zip.generateAsync({ type: 'nodebuffer' });
const formData = new FormData();
formData.append('zipfile', content);
await axios.post(config.HERE_MAPS_UPLOAD_ENDPOINT, formData, {
headers: {
'content-type': 'multipart/form-data',
},
params: {
apiKey: config.HERE_MAPS_REST_API_KEY,
layer_id: myId,
},
});
We get a bad request without a message and do not know what the problem is. The same implementation works in the Frontend (with 'blob' as zip type). Is there a parameter to get a better error message from the api?
We got the instructions how to implement it from this tutorial: https://www.youtube.com/watch?v=Ayw9GcS1V-8 and as I mentioned it works fine in the frontend. Also it works if I write a file in node and upload it via curl. Thank you for any help in advance!
Edit: I'm getting the following issue from the API: 'Multipart should contain exactly one part but contains 0'
I fixed it!
The problem was that the api needed a filename for the form data. This filename can be provided as third parameter as described here.
So I basically changed formData.append('zipfile', content); to formData.append('zipfile', content, zipfile.zip); and it worked.
Hope this will help somebody in the future!

Send an image as the body of a request, image recived with a request from outside

Yeah i kinda didn't know how to type the title well...
I've a node server which recives an image via post form. I then want to send this image to Microsoft vision and the same Google service in order to gether information from both, do some stuff, and return a result to the user that has accessed my server.
My problem is: how do i send the actual data?
This is the actual code that cares of that:
const microsofComputerVision = require("microsoft-computer-vision");
module.exports = function(req, res)
{
var file;
if(req.files)
{
file = req.files.file;
// Everything went fine
microsofComputerVision.analyzeImage(
{
"Ocp-Apim-Subscription-Key": vision_key,
"content-type": "multipart/form-data",
"body": file.data.toString(),
"visual-features":"Tags, Faces",
"request-origin":"westcentralus"
}).then((result) =>
{
console.log("A");
res.write(result);
res.end();
}).catch((err)=>
{
console.log(err);
res.writeHead(400, {'Content-Type': 'application/json'});
res.write(JSON.stringify({error: "The request must contain an image"}));
res.end();
});
}
else
{
res.writeHead(400, {'Content-Type': 'application/octet-stream'});
res.write(JSON.stringify({error: "The request must contain an image"}));
res.end();
}
}
If instead of calling "analyzeImage" i do the following
res.set('Content-Type', 'image/jpg')
res.send(file.data);
res.end();
The browser renders the image correctly, which made me think "file.data" contains the actual file (considered it's of type buffer).
But apparently Microsoft does not agree with that, because when i send the request to computer vision i get the following response:
"InvalidImageFormat"
The only examples i found are here, and the "data" that is used in that example comes from a file system read, not stright from a request. But saving the file to load it and then delete it to me looks like an horrible workaround, so i'd rather like to know in what form and how should i work on the "file" that i have to send it correctly for the APIs call.
Edit: if i use file.data (which i thought was the most correct since it would be sending the raw image as the body) i get an error which says that i must use a string or a buffer as content. So apparently that file.data is not a buffer in the way "body" requires O.o i'm not understanding honestly.
Solved, the error was quite stupid. In the "then" part, res.write(result) did not accept result as argument. This happened when i actually used the corret request (file.data which is a buffer). The other errors occurred everytime i tryed using toString() on file.data, in that case the request wasn't accepted.
Solved, the request asked for a buffer, and file.data is indeed a buffer. After chacking file.data type in any possible way i started looking for other problems. The error was much easier and, forgive my being stupid, too stupid to be evident. The result was a json, and res.write didn't accept a json as argument.
This is how I did it with Amazon Recognition Image Classifier, I know its not the same service your using - hoping this helps a little thou:
const imagePath = `./bat.jpg`;
const bitmap = fs.readFileSync(imagePath);
const params = {
Image: { Bytes: bitmap },
MaxLabels: 10,
MinConfidence: 50.0
};
route.post('/', upload.single('image'), (req, res) => {
let params = getImage();
rekognition.detectLabels(params, function(err, data) {
if (err) {
console.log('error');
}else {
console.log(data);
res.json(data);
}
});
});

cucumber attach screenshot to stepresult

I'm trying to generate report with cucumber-html-reporter. On it's GitHub page I saw a fancy bootstrap report, where a screenshot is attached to the failed step itself.
https://www.npmjs.com/package/cucumber-html-reporter
I'm working with cucumber-js 2.3.1 and cannot attach a screenshot to the StepResult.
I can attach a screenshot only in the After hook, where the World is available.
After(function (scenario) {
if (scenario.isFailed()) {
const world = this;
return browser.takeScreenshot().then(function (screenShot) {
world.attach(screenShot, 'image/png');
});
}
});
This is working fine, but unfortunately the screenshot is attached to the "After" step, not to the failed one.
I have tried this:
registerHandler('StepResult', function (StepResult) {
if (StepResult.isFailed()) {
return browser.takeScreenshot().then(function (screenShot) {
var decodedImage = new Buffer(screenShot, 'base64');
StepResult.attachments.push({
data: decodedImage.toString('base64'),
mimeType: 'image/png'
});
});
}
});
It works, the attachment is added, but not rendered into the report, since the cucumber json_formatter.handleStepResult is executed BEFORE the 'StepResult' hook is invoked.
Can someone show me a solution?
Thanks!
The reporter would show the Screenshot link in the failed STEP if you capture screenshot in the STEP itself. If you capture at the AFTER hook, it will show in the hook.
On the example shown in the Github page, it attaches the screenshot to the Step, and so it shows to the "then" step. Please take a look at here for more info.

Fabric.js loadFromJSON sometimes fails in Node.js if string contains images

I have a problem with PNG image ganeration at server side, using Fabric.js + Node.js. I am wondering that there is no one with similar probem found in forums. I am in total despair. It makes under risk of using Fabric.js in our project.
PNG image generation in Fabric.js Node.js service fails on a unregular basis. I can not determine why sometimes it gets generated and sometimes not.
I need to generate PNG at server side. I’ve developed a small Node.js webservice based on samples here and here.
I’ve developed also a custom Fabric.js image class “RemoteImage”, based on Kangax sample here.
To minimize JSON string size, I am storing a dataless JSON in my database and images are supposed to be loaded using provide link in “src” attribute of the Fabric.js Image element. As the result, I need to load following JSON into canvas that contains 3 images:
{"objects":[{"type":"remote-image","originX":"left","originY":"top","left":44,"top":29,"width":976,"height":544,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":0.5,"scaleY":0.5,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","localId":"222c0a8b-46ac-4c01-9c5c-79753937bc24","layerName":"productCanvas","itemName":"mainCanvas","src":"http://localhost:41075/en/RemoteStorage/GetRemoteItemImage/222c0a8b-46ac-4c01-9c5c-79753937bc24","filters":[],"crossOrigin":"use-credentials","alignX":"none","alignY":"none","meetOrSlice":"meet","remoteSrc":"http://localhost:41075/en/RemoteStorage/GetRemoteItemImage/222c0a8b-46ac-4c01-9c5c-79753937bc24","lockUniScaling":true},
{"type":"remote-image","originX":"left","originY":"top","left":382.5,"top":152.25,"width":292,"height":291,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":0.43,"scaleY":0.43,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","localId":"8d97050e-eae8-4e95-b50b-f934f0df2d4c","itemName":"BestDeal.png","src":"http://localhost:41075/en/RemoteStorage/GetRemoteItemImage/8d97050e-eae8-4e95-b50b-f934f0df2d4c","filters":[],"crossOrigin":"use-credentials","alignX":"none","alignY":"none","meetOrSlice":"meet","remoteSrc":"http://localhost:41075/en/RemoteStorage/GetRemoteItemImage/8d97050e-eae8-4e95-b50b-f934f0df2d4c","lockUniScaling":true},
{"type":"remote-image","originX":"left","originY":"top","left":38,"top":38.5,"width":678,"height":370,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":0.21,"scaleY":0.21,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","localId":"42dc0e49-e45f-4aa7-80cf-72d362deebb7","itemName":"simple_car.png","src":"http://localhost:41075/en/RemoteStorage/GetRemoteItemImage/42dc0e49-e45f-4aa7-80cf-72d362deebb7","filters":[],"crossOrigin":"use-credentials","alignX":"none","alignY":"none","meetOrSlice":"meet","remoteSrc":"http://localhost:41075/en/RemoteStorage/GetRemoteItemImage/42dc0e49-e45f-4aa7-80cf-72d362deebb7","lockUniScaling":true}],"background":""}
At Node.js server side I use the following code. I am transferring JSON string in base64 encoding to avoid some special-character problems:
var fabric = require('fabric').fabric;
function generatePNG(response, postData) {
var canvas = fabric.createCanvasForNode(1500, 800);
var decodedData = new Buffer(postData, 'base64').toString('utf8');
response.writeHead(200, "OK", { 'Content-Type': 'image/png' });
console.log("decodedData data: " + JSON.stringify(decodedData));
console.log("prepare to load");
canvas.loadFromJSON(decodedData, function () {
console.log("loaded");
canvas.renderAll();
console.log("rendered");
var stream = canvas.createPNGStream();
stream.on('data', function (chunk) {
response.write(chunk);
});
stream.on('end', function () {
response.end();
});
});
}
In a console I see that message “prepare to load” appears, but message “loaded” does not. I am not an expert in Node.js and this is the only way how I can determine that error happens during the loadFromJSON call. But I do not understand, where is the problem.
I am using fabric v.1.5.0 and node-canvas v.1.1.6 on server side.
Node.js + Fabric.js service is running on Windows 8 machine. And I am makeing a request from .NET MVC application, using POST request.
Remark: May be I needed to omit my comment about base64 encoding as it is confusing. I tried to run with normal json string and the same result.
If the images referenced in the JSON are on the NodeJS server, try changing the file path to the directory path on the server as opposed to a web URL.
I'm not sure I fully understand how you are using the base64 image, but there are some character corrections that are required for base64 images. I of course don't recall the specifics and don't have my code handy that I perform this in, but a Google search should set you in the right direction.
I hope those ideas help.
It turned out that problem was related to the way how fabric.util.loadImage method works. For external images loadImage mathod makes an http request assuming that no error can happen. Method used for requesting external images just simply logs an error and ends, instead of returning error through callback method back to loadImage method. At this moment image loading routine falls apart with erroneous state and without any feedback - it just terminates crashing whole Node.js.
It took 3 days for me to finally find out that actually it was my image supplying webservice who just responds with status code 500 making Node.js request to fail. Using my image supplying webservice through browser worked correctly and therefore at the first moment I did not considered that error is related particularly with request.
As the result I rewrote fromObject method of my custom Fabric.js object. Now it works in more safe fashion and in case of error I can get more feedback. Here is the implementation of my fromObject method. For http request I use module "request".
fabric.RemoteImage.fromObject = function (object, callback) {
var requestUrl = object.remoteSrc;
request({
url: object.remoteSrc,
encoding: null
},
function(error, response, body) {
if (error || response.statusCode !== 200) {
var errorMessage = "Error retrieving image " + requestUrl;
errorMessage += "\nResponse for a new image returned status code " + response.statusCode;
if (error) {
errorMessage += " " + error.name + " with message: \n" + error.message;
console.log(error.stack);
}
console.log(errorMessage);
callback && callback(null, new Error(errorMessage));
} else {
var img = new Image();
var buff = new Buffer(body, 'binary');
img.src = buff;
var fabrImg = new fabric.RemoteImage(img, object);
callback && callback(fabrImg);
}
});
};

Sending additional data with programatically created Dropzone using the sending event

I have the following (simplified for example) angular directive which creates a dropzone
directives.directive('dropzone', ['dropZoneFactory', function(dropZoneFactory){
'use strict';
return {
restrict: 'C',
link : function(scope, element, attrs){
new Dropzone('#'+attrs.id, {url: attrs.url});
var myDropZone = Dropzone.forElement('#'+attrs.id);
myDropZone.on('sending', function(file, xhr, formData){
//this gets triggered
console.log('sending');
formData.userName='bob';
});
}
}
}]);
As you can see the the sending event handler I'm trying to send the username ("bob") along with the uploaded file. However, I can't seem to retrieve it in my route middleware as req.params comes back as an empty array (I've also tried req.body).
My node route
{
path: '/uploads',
httpMethod: 'POST',
middleware: [express.bodyParser({ keepExtensions: true, uploadDir: 'uploads'}),function(request,response){
// comes back as []
console.log(request.params);
//this sees the files fine
console.log(request.files);
response.end("upload complete");
}]
}
Here is what the docs say on the sending event
Called just before each file is sent. Gets the xhr object and the formData objects as second and third parameters, so you can modify them (for example to add a CSRF token) or add additional data.
EDIT
I dropped the programmatic approach for now. I have two forms submitting to the same endpoint, a regular one with just post and a dropzone one. Both work, so I don't think it's an issue with the endpoint rather with how I handle the 'sending' event.
//Receives the POST var just fine
form(action="http://127.0.0.1:3000/uploads", method="post", id="mydz")
input(type="hidden", name="additionaldata", value="1")
input(type="submit")
//With this one I can get the POST var
form(action="http://127.0.0.1:3000/uploads", method="post", id="mydz2", class="dropzone")
input(type="hidden", name="additionaldata", value="1")
OK, I've actually figured it out, thanks to Using Dropzone.js to upload after new user creation, send headers
The sending event:
myDropZone.on('sending', function(file, xhr, formData){
formData.append('userName', 'bob');
});
As opposed to formData.userName = 'bob' which doesn't work for some reason.
I would like to add to NicolasMoise's answer.
As a beginner in webdev I got stuck on how to obtain an instance of Dropzone. I wanted to retrieve an instance of Dropzone that had been generated by the autodiscovery feature. But it turns out that the easiest way to do this is to manually add a Dropzone instance after first telling Dropzone not to auto-discover.
<input id="pathInput"/>
<div id="uploadForm" class="dropzone"/>
<script>
$(document).ready(function(){
Dropzone.autoDiscover = false;
var dZone = new Dropzone("div#uploadForm", {url: "/api/uploads"});
dZone.on("sending", function(file, xhr, data){
data.append("uploadFolder", $("#pathInput")[0].value);
});
});
</script>
Serverside the data will be in request.body.uploadFolder
Nicolas answer is one possible solution to the problem. It is especially useful if you need to alter the file object prior to sending.
An alternative is to use the params option:
var myDropzone = new Dropzone("div#myId",
{ url: "/file/post", params: { 'param_1': 1 }});
cf. the documention
For those that are using thatisuday/ng-dropzone the callback methods are done as such:
<ng-dropzone class="dropzone" options="dzOptions" callbacks="dzCallbacks" methods="dzMethods"></ng-dropzone>
In a controller:
$scope.dzCallbacks = {
sending: function(file, xhr, form) {
console.log('custom sending', arguments);
form.append('a', 'b');
}
};

Resources