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)
})
Related
I am trying to use axios to retrieve data from a url and then append the data to html elements I created using javascript.
In a nutshell for each programming language in my url I would like to have a card showing the headline and author name of each article.
This is my HTML
<body>
<div class="parentDiv">
</div>
</body>
</html>
and my JS
const CardsTest = (one) => {
// class headline
const divHead = Object.assign(document.createElement('div'), {className: 'one', textContent: one.headline});
// class author
const divAut = Object.assign(document.createElement('div'), {className: 'writer'});
const spanCont = Object.assign(document.createElement('span'), {className: 'name', textContent: one.authorName});
divAut.appendChild(SpanCont);
divHead.appendChild(divAut);
return divHead;
}
const cardAppender = (div) => {
const divOne = document.querySelector(div);
axios.get('http://localhost:5000/api/articles')
.then((resp) => {
Object.keys(resp.data).forEach (
function(obj) {
const topicsData = CardsTest(obj.articles);
divOne.appendChild(obj.articles)
}
)
})
}
cardAppender('parentDiv')
I know that my function CardsTest creates all the components and my cardsappender can, at the very least print out the JSON from the target URL. I know if I run the function with axios and console log obj.articles I get an object promise containing the articles in the URL.
To summarize; I expect cardAppender to take a url, and take a callback function (Cards Test) appending the writer and headline to the elements in Cards Test and then append that to my html parentDiv. However this is not happening
UPDATE
Tried changing my cardAppender function by creating an array of programming languages (the keys in my JSON) and then appending headline and authorname for each article to my Cards Test function, but this function is still not creating the components in Cards Test:
const cardsAppender = (div) => {
const newArr = ["javascript","bootstrap","technology","jquery","node"]
const divOne = document.querySelector('.parentDiv');
axios.get('http://localhost:5000/api/articles')
.then((resp) => {
newArr.forEach((item) => {
const cardsHolds = CardsTest(resp.data.articles[item])
divOne.appendChild(cardsHolds)
})
})
}
cardsAppender('.parentDiv')
You are using Object.keys to iterate, so you need to use that key as a property index. Or use Object.values. It's not really clear what shape your data is though, so this might need to be tweaked.
Object.keys(resp.data).forEach (
function(obj) {
const topicsData = CardsTest(res.data[obj].articles);
divOne.appendChild(res.data[obj].articles)
}
Object.values(resp.data).forEach (
function(obj) {
const topicsData = CardsTest(obj.articles);
divOne.appendChild(obj.articles)
}
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.
So, I am trying to grab the data from the GET fetch that I used.
const BASE_URL = "http://localhost:3000/quotes"
document.addEventListener('DOMContentLoaded', function() {
randomButton();
createButton();
loadQuotes();
});
const loadQuotes = function() {
fetch(BASE_URL)
.then(res => res.json())
.then(results => {
results.data.forEach(json => json.attributes.sentence)
});
}
function addQuote(quote) {
let ul = document.querySelector('#quote-display');
let li = document.createElement('li');
li.innerText = quote;
ul.appendChild(li);
}
When I wrap a console.log around json.attributes.sentence or use the addQuote(json.attributes.sentence) function like so. I definitely get the out put in the console.log/in the ul/li tags. I also ran a typeof on the json.attributes.sentence and it shows it's outputting strings.
However, I would like to add that data to an array to access later from other methods.
I tried creating a global variable that was an empty array and push the json data attribute strings to the array but didn't work. Ideas?
Thanks in advance.
btw the JSON looks like this
{
data: [
{
id: "1",
type: "quote",
attributes: {
sentence: "Here's looking at you, kid."
}
}
]
}
I'm a beginner writing e2e Javascript tests using Protractor. I have been trying to identify an array of elements that have a text property associated with them. I'm able to target them using
groupedNodeNumbers : ElementArrayFinder;
this.groupedNodeNumbers = this.element.all(by.css(".side-label-background + .side-label"));
This is supposed to give me an array of elements on which I can call getText() to extract the value associated with each of them.
validateCountGraphNodes = async() => {
let count = 0;
console.log('Inside')
this.groupedNodeNumbers.each((groupedNodeElement) => {
groupedNodeElement.getText().then((num) => {
console.log('Inside loop')
count += parseInt(num, 10);
console.log(count);
});
});
}`
I am able to log ('Inside') but not ('Inside Loop') and hence my function fails to retrieve the text associated with each element.
Could someone please point where I'm going wrong here?
Since getText() applied returns string instead of an array of strings (existing issue), you can try the following:
const cssSelector = ".side-label-background + .side-label";
$$(cssSelector).getText().then(textArrNotArr => {
for(let i = 0; i< textArrNotArr.lenght; i++) {
console.log('arr[i] = ', textArrNotArr[i]);
}
});
or
$$(cssSelector).count().then(elFinderArray => {
elFinderArray.forEach(elFinder => {
elFinder.getText().then((txt, index) => {
console.log(index);
});
});
});
note: $$('cssSelector') can be used instead of element.all(by.css('cssSelector'))
I have a firebase cloud function that creates a transaction that creates a document and I want in the same transaction to get the id of the document and update another document with a reference to that one.
return db.runTransaction(t => {
return t.get(userDocRef)
.then(userDoc => {
var userDocData = userDoc.data();
let ref = db.collection('colect').doc();
return t.set(ref, {'userinfo' : userDocData.name});
}
.then(resp => {
// here `$resp` is reference to transaction and not to the resulted document
// here i want a reference to the new document or the id
// of the document or a way of knowing which document was inserted
}
})
The following should do the trick:
const userDocRef = .....;
let colectDocRef;
return db.runTransaction(t => {
return t
.get(userDocRef)
.then(userDoc => {
const userDocData = userDoc.data();
colectDocRef = db.collection('colect').doc();
return t.set(colectDocRef, { userinfo: userDocData.name });
})
.then(t => {
//I don't know what you exactly want to do with the reference of the new doc, i.e. colectDocRef
//So I just write a new doc in a collection to show that it works
//Just change accordingly to your requirements
const tempoRef = db.collection('colect1').doc();
return t.set(tempoRef, { colectRef: colectDocRef });
})
.then(t => {
//This "fake" update is mandatory unless you'll get the following error: "Every document read in a transaction must also be written."
return t.update(userDocRef, {});
});
});