I'm using cypress runner to execute the spec file and return the result as like below.
Runner.js:
const cypress = require('cypress');
const param = require("./cypress/support/Param");
async function testRunner(fixture) {
return cypress.run({
config: {
},
env: {
testcaseID: `${fixture}`,
},
spec: './cypress/integration/' + `${param.getSpec()}` + ".spec.js",
});
}
Spec file:
let map = new Map();
describe("How to add map values in the cypress result.json",() =>{
const baseUrl = "https://www.google.com/";
const testData = Cypress.env('fixture')
beforeEach("",()=>{
cy.visit(baseUrl);
});
it("Test Case1: Search the keyword", function () {
cy.xpath("//input[#name='q']").type(testData.searchKeyword);
map.set("UserInput",testData.searchKeyword); //It's just sample
cy.xpath("//input[#value='Google Search']").click();
map.set("customMessage","test"); //It's just sample but actual usecase is different
cy.get("//ul/li[2]").should("be.visible");
});
});
using the below lines to get the result in the main runner:
result = await testRunner(dataSet[i]);
if (result.runs[0].stats.failures === 1) {
console.log(result);
}
The above code is working fine and I can able to get the result.json which contains whether the test case is pass/failed. But In addition I just want to add few more runtime values which I stored in the Map and wanna add those in the Cypress.run return result.
Can someone please suggest me the optimized way to handle this? How can I get some runtime values which is available inside the map and that will be returned along with cypress run result.
Thanks in advance.
Updated:
I just created a Map() variable and calling that variable by using the getter and setter but still no luck.
var EnumPojo = {
LISTENER: new Map(),
get listener()
{
return this.LISTENER;
},
set listener(value)
{
return this.listener =value;
};
}
Called the above map into script:
const runtimeValues = require("../../EnumPojo.js");
describe("How to add map values in the cypress result.json",() =>{
const baseUrl = "https://www.google.com/";
const testData = Cypress.env('fixture')
beforeEach("",()=>{
cy.visit(baseUrl);
});
it("Test Case1: Search the keyword", function () {
cy.xpath("//input[#name='q']").type(testData.searchKeyword);
runtimeValues.LISTENER.set("UserInput",testData.searchKeyword); //It's just sample
cy.xpath("//input[#value='Google Search']").click();
runtimeValues.LISTENER.set("customMessage","test"); //It's just sample but actual usecase is different
cy.get("//ul/li[2]").should("be.visible");
});
});
Runner:
const runtimeValues = require("../../EnumPojo.js");
result = await testRunner(dataSet[i]);
if (result.runs[0].stats.failures === 1) {
console.log(result);
console.log(runtimeValues.LISTENER);
}
Output:
Map{0}
In test, save the map under fixtures
it('my-test', function () {
...
cy.fixture('map.json').then(mapData => {
mapData['my-test'] = JSON.stringify(map) // use unique key for this test
cy.writeFile('./fixtures/map.json', JSON.stringify(mapData))
})
})
In runner,
const fs = require('fs')
cypress.run({...})
.then(result => {
const mapData = fs.readFileSync('./cypress/fixtures/map.json') // adjust path to where script is
/*
mapData is {
'my-test': { ...data here },
'other-test': { ...data here },
}
*/
})
BUT Javascript Map may not serialize correctly, best to convert to object before saving.
Related
Hi I have exported using data (hawkers collection) using getDocs() from Firebase.
After that I put each hawker data as an object in an array called allStall as shown in the screenshot of the console log below.
Question 1 - How do I access each individual object in my allStall array. I try to use .map() to access each of it, but i am getting nothing.
Do note that I already have data inside my allStall array, see screenshot above.
[Update] map doesn't work in code below because field is stallname not stallName. However, it needs to be async + await if using/call in/from other function.
Question 2 - Why is there [[Prototype]]: Array(0) in my allStall array
export /*Soln add async*/function getAllStall(){
var allStall = [];
try
{
/*Soln add await */getDocs(collection(db, "hawkers")).then((querySnapshot) =>
{
querySnapshot.forEach((doc) =>
{
var stall = doc.data();
var name = stall.stallname;
var category = stall.category;
var description = stall.description;
var stallData = {
stallName:name,
stallCategory:category,
stallDescription:description
};
allStall.push(stallData);
});});
console.log(allStall);
//Unable to access individual object in Array of objects
allStall.map(stall =>{console.log(stall.stallName);});}
catch (e) {console.error("Error get all document: ", e);}
return allStall;
}
In my main js file, i did the following:
useEffect(/*Soln add await*/() =>
{
getAllStall();
/*Soln:replace the statement above with the code below
const allStall = await getAllStall();
allStall.map((stall)=>console.log(stall.stallname));
*/
}
);
You are getting nothing because allStall is empty since you are not waiting for the promise to be fullfilled
try this
export const getAllStall = () => getDocs(collection(db, "hawkers"))
.then((querySnapshot) =>
querySnapshot.map((doc) =>
{
const {stallName, category, description} = doc.data();
return {
stallName:name,
stallCategory:category,
stallDescription:description
};
});
)
try to change use effect like this
useEffect(async () =>
{
const allStats = await getAllStall();
console.log(allStats)
allStats.forEach(console.log)
}
);
A very big thanks to R4ncid, you have been an inspiration!
And thank you all who commented below!
I managed to get it done with async and await. Latest update, I figure out what's wrong with my previous code too. I commented the solution in my question, which is adding the async to the function and await to getDocs.
Also map doesn't work in code above because field is stallname not stallName. However, it needs to be async + await if using in/calling from other function.
Helper function
export async function getAllStall(){
const querySnapshot = await getDocs(collection(db, "hawkers"));
var allStall = [];
querySnapshot.forEach(doc =>
{
var stall = doc.data();
var name = stall.stallname;
var category = stall.category;
var description = stall.description;
var stallData = {
stallName:name,
stallCategory:category,
stallDescription:description
};
allStall.push(stall);
}
);
return allStall;
}
Main JS file
useEffect(async () =>
{
const allStall = await getAllStall();
allStall.map((stall)=>console.log(stall.stallname));
}
);
Hurray
I'm practicing and was trying to write in a file, all names and links of 'cars' from amazon.
The following code is working but will only write one line in the txt file. How can I write the complete list? Maybe as an object?
Is there a better way to do this?
it.only("amazon cars", () => {
cy.get("input#twotabsearchtextbox").type("cars{enter}");
cy.get(".s-main-slot")
.find("div>h2>a>span")
.each((element) => {
const elname = element.text();
cy.wrap(element)
.parent()
.invoke("attr", "href")
.then((href) => {
cy.writeFile("element.txt", `${elname} and its link ${href}`);
});
});
});
You can use the append mode of cy.writefile().
cy.writeFile("element.txt", `${elname} and its link ${href}`, { flag: 'a+' });
Alternatively, ditch the .each() and use a mapping function instead. This way you only need to write once.
cy.get('.s-main-slot')
.find('div>h2>a>span')
.then($cars => {
const descriptions = [...$cars].map(car => { // car is raw element
const elname = car.innerText; // use DOM property innerText
const href = car.parentElement.href; // use DOM method parentElement
return `${elname} and its link ${href}`
})
cy.writeFile('element.txt', descriptions.join('\n'))
})
Or for a cleaner mapping function, take the parent of the span, the text will still be the same.
cy.get('.s-main-slot')
.find('div>h2>a')
.then($cars => {
const descriptions = [...$cars].map(car => {
return `${car.innerText} and its link ${car.href}`)
})
cy.writeFile('element.txt', descriptions.join('\n'))
})
Or as an object, use a reducer to map
cy.get('.s-main-slot')
.find('div>h2>a')
.then($cars => {
const asObject = [...$cars].reduce((obj, car) => {
obj[car.innerText] = car.href; // "Cars": "https://www.amazon.com...
return obj;
}, {})
cy.writeFile("element.json", asObject)
})
I have a command that overwrites pause to add the input from a dialog to the reports. I want to know if there is a way to know what test is calling the function so that I can customize the message on the inputs.
Cypress.Commands.overwrite('pause', (originalFn, element, options) => {
var tryThis = '';
if (//place calling the function == file1) {
tryThis = 'message1';
} else if (//place calling the function == file2) {
...
} else if (//place calling the function == file3) {
...
}
var datalog = window.prompt(tryThis, "Log your results");
cy.addContext("DATALOG:" + datalog);
return originalFn(element, options)
})
As well as access via the Mocha properties there is also
For the spec file Cypress.spec
Properties for my.spec.js
Cypress.spec.absolute: "C:/.../my.spec.js"
Cypress.spec.name: "my.spec.js"
Cypress.spec.relative: "cypress\integration\my.spec.js"
Cypress.spec.specFilter: "my"
Cypress.spec.specType: "integration"
For the test cy.state('runnable')
For
describe('my-context', () => {
it('my-test', () => {
Properties and methods,
const title = cy.state('runnable').title; // "my-test"
const fullTitle = cy.state('runnable').fullTitle(); // "my-context my-test"
const titlePath = cy.state('runnable').titlePath(); // ["my-context", "my-test"]
You can also add metadata to the test
describe('my-context', () => {
it('my-test', { message: "my-message" }, () => {
and grab it in the command overwrite
const message = cy.state('runnable').cfg.message; // "my-message"
I tried this and it worked for me (my version of cypress is 6.1.0):
cy.log(Cypress.mocha.getRunner().suite.ctx.test.title);
More info: https://github.com/cypress-io/cypress/issues/2972
// how I get the data
db.collection('Pins').get().then(snapshot => {
snapshot.forEach(pinInfo => {
pinsToMap(pinInfo)
});
});
// trying to set the data
function pinsToMap(pinInfo){
let pinName;
let pinCoOrdsLat;
let pinCoOrdsLong;
let pinToMapInfo;
pinName = doc.data().name
pinCoOrds = doc.data().coOrds
pinToMapInfo = doc.data().Info
Pins.child(Pins.coOrds).set({
coOrds: {
0:this = pinCoOrdsLat,
1:this = pinCoOrdsLong,
}
});
}
I am storing data in my database based off a map pin, I am now trying to use the stored data to create a pin on the map of the same place, how do I query out the coOrds in to pinCoOrdsLat / pinCoOrdsLong as this way doesn't seem to be working
If I correctly understand you question, the following should do the trick:
db.collection('Pins').get().then(snapshot => {
snapshot.forEach(pinInfo => {
pinsToMap(pinInfo)
});
});
// trying to set the data
function pinsToMap(pinInfo) { // IMPORTANT! => pinInfo is a DocumentSnapshot
const pinName = pinInfo.data().name
const pinCoOrds = pinInfo.data().coOrds
const pinToMapInfo = pinInfo.data().Info
//pinCoOrds is a JavaScript Array with two elements
const pinCoOrdsLat = pinCoOrds[0];
const pinCoOrdsLong = pinCoOrds[1];
//Use pinCoOrdsLat and pinCoOrdsLong the way you want, e.g. calling a leaflet method
}
You'll find here the doc for a DocumentSnapshot
I'm parsing a data file (which contains json data) line-by-line and creating objects. I then add these objects to an array which I have declared outside. But for some reason, my 'services' array becomes empty again outside the linereader.on function. I'm able to console.log(services) inside the linereader.on and see it printing data as expected. But I have no idea why it becomes empty again outside!
const getLineReader = function () {
return require('readline').createInterface({
input: require('fs').createReadStream('data.txt')
});
};
const getSystem = function () {
const lineReader = getLineReader();
const services = [];
lineReader.on('line', function (line) {
const serviceJSON = JSON.parse(line);
const tests = serviceJSON.tests.map(test => {
return new ServiceTest(
test.id,
test.name,
test.criticality);
});
const service = new NewService(new UniqueID(), serviceJSON.name, tests, new Timestamp());
services.push(service);
console.log(services); // prints Services { _services: [relevant data here] }
});
console.log(services); // prints Services { _services: [] }
You need to listen to the readline 'close' event and then print the array. close will be called once all lines have been read.
lineReader.on('close', function() {
console.log(services)
});
You'll then end up with something like:
const getSystem = function () {
const lineReader = getLineReader();
const services = [];
lineReader.on('line', function (line) {
const serviceJSON = JSON.parse(line);
const tests = serviceJSON.tests.map(test => {
return new ServiceTest(
test.id,
test.name,
test.criticality);
});
const service = new NewService(new UniqueID(), serviceJSON.name, tests, new Timestamp());
services.push(service);
console.log(services); // prints Services { _services: [relevant data here] }
});
lineReader.on('close', function() {
console.log(services)
});
}
In your current code, console.log(services) will fire before the line lineReader.on('line', ...) code.