I need to test code responsible for searching and sorting.
Should I use Jest or Testing Library? How tests should look like?
I can’t find any good tutorials on the Internet.
Thank you for any advice,
Kuba
// SEARCHING
const searchBar = document.forms.search.querySelector("input");
const ul = document.querySelector(".channels-list");
const li = ul.getElementsByTagName("li");
let term = "";
searchBar.addEventListener("keyup", e => {
term = e.target.value.toLowerCase();
for (let i = 0; i < li.length; i++) {
const h2 = li[i].getElementsByTagName("h2")[0].textContent;
if (h2.toLowerCase().indexOf(term) > -1) {
li[i].style.display = "flex";
} else {
li[i].style.display = "none";
}
}
});
Testing frameworks you can checkout for testing js code on browsers are Karma.js and Selenium Web Driver. However both require some knowledge of Node.js.
Split the sorting out, don’t tie it to the DOM, ie you have a function that takes your data, some input and produces your sorted data
Use Jest to unit test that (if you have data a and you sort, you should get data b)
Plug that into the DOM (press button, produce the sorted output in the DOM)
Now if you want to test the whole system works in the browser, use something like Cypress to test that it works in the browser, for end-to-end tests (Selenium is another option, but Cypress is easier).
Or reverse the above and go Cypress first; there are different valid ways to approach this
Edit: The reason you would do it this way is that it’s likely going to make the code much more robust and testable. You have some data, and you need to sort it. Rendering it visually in the browser is an implementation detail, it’s a separate thing. Unit tests are easy to write and very fast. E2E tests are difficult to write, brittle and slow. Unit tests check whether individual parts of your code work. E2E tests don’t, they test whether the whole system behaves as expected, so they won’t tell you whether there’s something wrong with your sorting logic, for example.
At this point it would be fine to just do E2E tests with (eg) Cypress, but the issue is that it’s fairly likely you want your search to be improved. So a standard approach is to inject the dependencies needed to run the search and to render the results into the function:
function searchByTerm (searchTerm, terms, renderFunc) {
const matches = terms.filter(term => term === searchTerm);
renderFunc(matches);
return matches;
}
describe("search", () => {
const terms = ["foo", "bar", "baz"];
// Unit tests do not care if anything is rendered:
const fakeRender = () => void 0;
it("returns an array of matching terms", () => {
expect(searchByTerm("foo", terms, fakeRender)).toEqual(["foo"]);
});
it("returns an empty list when nothing found", () => {
expect(searchByTerm("xxx", terms, fakeRender)).toEqual([]);
});
// A test to check that the render function fires each time
// More tests
});
// In real code
function render(matches) {
document
.querySelectorAll(theThingsYouAreSearching)
.forEach(element => {
// get the title or whatever here
if (matches.includes(title) {
// Show element
} else {
// Hide element
}
});
}
// Get your list of terms from the document
document
.querySelector(searchTrigger)
.addEventListener("change", (e) => searchByTerm(e.target.value.toLowerCase(), yourListOfTerms, render));
What you may want to do with search that is made difficult to test by coupling the DOM and the business logic is to return search results for partial matches (which is how most useful search works). Eg, you check the first letter and filter all results with that first letter, then first two and so on. Or you check for close matches (see tools like Levenshtein distance, a simple JS implementation here)