Test DOM jasmine - javascript

I am new in Unit Test JS. I want create test in jasmine. I dynamically create element HTML in JS.
data.map((channel) => {
const { url, width, height } = channel.thumbnails.medium;
const { title, customUrl } = channel;
const { subscriberCount, videoCount, viewCount } = channel.statistics;
output += `
<li class="channel-wrraper">
<a href='${customUrl}' target="_blank">
<img src='${url}' alt="img-channel" height='${width}' width='${height}' class="channel-img">
</a>
<p class="channel-title">${title}</p>
<div class="channel-statistic">
<div class="statistic-wrraper">
<span class="statistic-name">subscribers:</span>
<span class="subscirber-count">${formatNumber(subscriberCount)}</span>
</div>
<div class="statistic-wrraper">
<span class="statistic-name">videos:</span>
<span class="video-count">${formatNumber(videoCount)}</span>
</div>
<div class="statistic-wrraper">
<span class="statistic-name">views:</span>
<span class="veiw-count">${formatNumber(viewCount)}</span>
</div>
</div>
</li>`
});
channelsList.innerHTML = output;
Then some element will be ordered. This is sort function:
const list = document.querySelector('.channels-list');
const sortNumber = (selector) => {
[...list.children]
.sort((a,b) => a.querySelector(selector).innerText.replace(/,/g, '') - b.querySelector(selector).innerText.replace(/,/g, ''))
.map(node => list.appendChild(node))
}
I read about JSDOM and I watched the tutorials in which they tested the DOM, however, these elements were in the html file...
I want test function sortNumber
But I don`t know how start this task..

You can try using jsdom-global, then you will have document.body setup for you:
require('jsdom-global')()
// you can now use the DOM
document.body.innerHTML = 'put your html here'
An alternative will be to use jest, which comes with JSDOM configured as default

Related

Multiple Rotating Title Script

I have a HTML/CSS/JS script that is allowing me to rotate text for a certain part of it. I'm just having the problem of making it work for multiple sections as the script targets a span[data-up] & span[data-show].
Any help appreciated, code shown below.
<section class="rotating-text-section">
<h2>
We educate by
<div class="wrapper">
<span data-up>teaching.</span>
<span data-show>showing.</span>
<span>doing.</span>
<span>repeating.</span>
</div>
</h2>
</section>
setInterval(() => {
const up = document.querySelector('span[data-up]');
const show = document.querySelector('span[data-show]');
const down = show.nextElementSibling || document.querySelector('span:first-child');
up.removeAttribute('data-up');
show.removeAttribute('data-show');
show.setAttribute('data-up', '');
down.setAttribute('data-show', '');
}, 2000);
I just changed the class names of each span element and targeted them by this in the JavaScript script.
My second snippet of HTML was:
<div class="wrapper-two">
<span class="second-span" data-up>technology.</span>
<span class="second-span" data-show>experience.</span>
<span class="second-span">listening.</span>
<span class="second-span">experimenting.</span>
</div>
My second JS script is as follows:
setInterval(() => {
const up = document.querySelector('.second-span[data-up]');
const show = document.querySelector('.second-span[data-show]');
const down = show.nextElementSibling ||
document.querySelector('.second-span:first-child');
up.removeAttribute('data-up');
show.removeAttribute('data-show');
show.setAttribute('data-up', '');
down.setAttribute('data-show', '');
}, 1000);

Is there a way to uniquely identity a dynamically added element?

I am trying to dynamically load a bunch of posts from a API and then implement a like button for each of them.
function load_allposts(){
fetch("/posts")
.then(response => response.json())
.then(posts => {
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=${element[0].author_id}> ${element[0].author_name} </button>
</div>
<div class="post-body">
${element[0].body}
</div>
<div class="p1">
<span class="like-status">${element[0].likes}</span> people like this
<button class="like-btn">${element[1]}</button>
</div>
<div class="post-time">
${element[0].timestamp}
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
});
}
I would to like to modify the <span class="like-status"> element when I click the <button class="like-btn">. The only way that I can think of to get a reference to <span class="like-status"> is by adding a ID to it by implementing some kind of counter, which I feel is more like a hack rather than real solution.
I tried googling but almost all solutions involved JQuery, which I am not familiar with. Any help would be appreciated.
You can use delegate event binding document.addEventListener('click', function(event) { to trigger click event for dynamically added button.
It will raise click on every element inside document you need to find if it is one which you expect with event.target.matches('button.like-btn').
Then you can find your span with getting parent and then finding span.like-status using querySelector.
Try it below. For demo modified load_allposts. You do not need to do any change in it.
load_allposts();
document.addEventListener('click', function(event) {
if (event.target.matches('button.like-btn')) {
let span = event.target.parentElement.querySelector('span.like-status');
span.innerText = 'Modified';
}
});
function load_allposts() {
let posts = [1]
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=element[0].author_id> element[0].author_name </button>
</div>
<div class="post-body">
element[0].body
</div>
<div class="p1">
<span class="like-status">element[0].likes</span> people like this
<button class="like-btn">element[1]</button>
</div>
<div class="post-time">
element[0].timestamp
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
}
<div id='all-posts'>
</div>
Note event delegation have extra overhead so alternatively you can use below code.
Here added two functions added as below and added one line bindClickEvent(enc); at end of load_allposts function.
likeClick - perform custom logic to update span.like-status
bindClickEvent - bind click event to all button.like-btn inside div
Call bindClickEvent(enc); at end of load_allposts function.
Try it below.
load_allposts();
// perform custom logic to update span.like-status
function likeClick(event) {
// querySelector will return first matching element
let span = event.target.parentElement.querySelector('span.like-status');
span.innerText = 'Modified';
}
// bind click event to all button.like-btn inside div
function bindClickEvent(enc) {
// querySelectorAll will return array of all matching elements
let buttons = enc.querySelectorAll('button.like-btn');
// loop over each button and assign click function
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = likeClick;
}
}
function load_allposts() {
let posts = [1]
var enc = document.createElement('div');
enc.className = "post-enc";
let s = ``;
posts.forEach(element => {
s += `<div class="p-container">
<div>
<button type="button" class="btn btn-link" class="profile-btn" data-id=element[0].author_id> element[0].author_name </button>
</div>
<div class="post-body">
element[0].body
</div>
<div class="p1">
<span class="like-status">element[0].likes</span> people like this
<button class="like-btn">element[1]</button>
</div>
<div class="post-time">
element[0].timestamp
</div>
</div>`;
});
enc.innerHTML = s;
document.querySelector('#all-posts').appendChild(enc);
// assign click event to buttons inside enc div.
bindClickEvent(enc);
}
<div id='all-posts'>
</div>

Json file struggling with the length

So, i got everything almost working as i want it, just a mistake that im struggling. Everytime i search for an item, when the result for that item shows the length is repeated.
When i search for ox there are 2 results and that is correct, but the length (2) shows in both of them, i only display one
[Code]
const resultHtml = (itemsMatch) => {
if (itemsMatch.length > 0) {
const html = itemsMatch
.map(
(item) => `
<span>${itemsMatch.length}</span>
<div class="card">
<div class="items-img">
</div>
<div class="items-info">
<h4>${item.title}</h4>
<small>${item.path}</small>
</div>
</div>
`
)
.join('');
//console.log(html);
itemList.innerHTML = html;
}
};
////
Question 2
I got one more question, i was trying to get the image from the Json and what i got was the path haha
why the apth and not the img
const resultHtml = (itemsMatch) => {
if (itemsMatch.length > 0) {
const html =
`<span class="items-results">${itemsMatch.length} Resultados</span>` +
itemsMatch
.map(
(item) => `
<div class="card">
<div class="items-img">
${item.image}
</div>
<div class="items-info">
<h4>${item.title}</h4>
<small>${item.path}</small>
</div>
</div>
`
)
.join('');
console.log(html);
itemList.innerHTML = html;
}
};
If you move <span>${itemsMatch.length}</span> out of your map callback, it will not repeat for each item. Read more about map() here.
Replace:
const html = itemsMatch
.map(
(item) => `
<span>${itemsMatch.length}</span>
... more HTML here
`
)
.join('');
With this:
const html = `<span>${itemsMatch.length}</span>` + (
itemsMatch
.map(
(item) => `
<div class="card">
<div class="items-img">
</div>
<div class="items-info">
<h4>${item.title}</h4>
<small>${item.path}</small>
</div>
</div>
`
)
.join('')
);
Regarding your image issue:
You are just outputting the path and that's why it's printing out just the path. If you are trying to display an image then put the path as source of <img> tag.
So, instead of just:
${item.image}
Use:
<img src="${item.image}">

How to get child of div in cheerio

I am working with cheerio and I am stuck at a point where I want to get the href value of children div of <div class="card">.
<div class="Card">
<div class="title">
<a target="_blank" href="test">
Php </a>
</div>
<div>some content</div>
<div>some content</div>
<div>some content</div>
</div>
I got first childern correctly but i want to get div class=title childern a href value. I am new to node and i already search for that but i didn't get an appropriate answer.
var jobs = $("div.jobsearch-SerpJobCard",html);
here is my script
const rp = require('request-promise');
const $ = require('cheerio');
const potusParse = require('./potusParser');
const url = "";
rp(url)
.then((html)=>{
const Urls = [];
var jobs = $("div.Card",html);
for (let i = 2; i < jobs.length; i++) {
Urls.push(
$("div.Card > div[class='title'] >a", html)[i].attribs.href
);
}
console.log(Urls);
})
.catch(err => console.log(err));
It looks something like this:
$('.Card').map((i, card) => {
return {
link: $(card).find('a').text(),
href: $(card).find('a').attr('href'),
}
}).get()
Edit: the nlp library is chrono-node and I also recommend timeago.js to go the opposite way

Merge HTML element attributes with node

I have this HTML string in node:
<a data-style="width:32px" id="heilo-wrld" style="height:64px">
Hello world
</a>
The code has data-style and style attributes I would like to merge in one style attribute like this:
<a id="heilo-wrld" style="width:32px; height:64px;">
Hello world
</a>
I could also have complex HTML blocks like this:
<div class="wrapper" data-style="background-color: red;">
<a data-style="width:32px" id="heilo-wrld" style="height:64px">
Hello world
</a>
</div>
To get this result:
<div class="wrapper" style="background-color: red;">
<a id="heilo-wrld" style="width:32px; height:64px;">
Hello world
</a>
</div>
I found some plug-in but it does not do this specific job:
sanitize-html
htmltidy
Does exists some smart way to do that?
Using jsdom, you could define a mergeStyles function like this:
const jsdom = require('jsdom');
function mergeStyles(html, callback) {
return jsdom.env(html, function(errs, window) {
const { document } = window;
Array.from(
document.querySelectorAll('[data-style]')
).forEach(function(el) {
const styles = [];
Array.from(el.attributes).forEach(function(attr) {
if (attr.name !== 'style' && attr.name !== 'data-style') {
return;
}
styles.push(attr.value);
el.removeAttributeNode(attr);
});
if (!styles.length) {
return;
}
el.setAttribute('style', styles.join(';'));
});
const result = document.body.innerHTML;
return callback(null, result);
});
}
Then call it like:
const input = `
<div class="wrapper" data-style="background-color: red;">
<a data-style="width:32px" id="heilo-wrld" style="height:64px">
Hello world
</a>
</div>
`;
mergeStyles(input, function(err, result) {
if (err) {
throw err;
}
// `result` should contain the HTML with the styles merged.
console.log(result);
});

Categories