Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sprint-3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"license": "CC-BY-SA-4.0",
"description": "",
"scripts": {
"test": "jest",
"test": "jest --config=../jest.config.js reading-list",
"format": "prettier --write ."
},
"workspaces": [
Expand All @@ -26,7 +26,7 @@
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/user-event": "^14.6.1",
"jest": "^30.0.4",
"jest": "^30.2.0",
"jest-environment-jsdom": "^30.0.4"
}
}
15 changes: 12 additions & 3 deletions Sprint-3/reading-list/index.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
<!DOCTYPE >
<!DOCTYPE html>
<html lang="en_US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>Title here</title>
<title>Reading list app</title>
</head>
<body>
<div id="content">
<ul id="reading-list"></ul>
<h1>My Reading List</h1>
<div class="legend">
<span class="legend-item">
<span class="legend-badge read-badge">✓</span> Already Read
</span>
<span class="legend-item">
<span class="legend-badge unread-badge">○</span> To Read
</span>
</div>
<ol id="reading-list"></ol>
</div>
<script src="script.js"></script>
</body>
Expand Down
7 changes: 5 additions & 2 deletions Sprint-3/reading-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"license": "CC-BY-SA-4.0",
"description": "You must update this package",
"scripts": {
"test": "jest --config=../jest.config.js reading-list"
"test": "jest"
},
"repository": {
"type": "git",
Expand All @@ -13,5 +13,8 @@
"bugs": {
"url": "https://github.com/CodeYourFuture/CYF-Coursework-Template/issues"
},
"homepage": "https://github.com/CodeYourFuture/CYF-Coursework-Template#readme"
"homepage": "https://github.com/CodeYourFuture/CYF-Coursework-Template#readme",
"devDependencies": {
"@testing-library/jest-dom": "^6.9.1"
}
}
43 changes: 43 additions & 0 deletions Sprint-3/reading-list/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,46 @@ const books = [
},
];

function readingList() {
const orderedList = document.querySelector("#reading-list");

// Base condition: check if books array is empty
if (!books || books.length === 0) {
const emptyMessage = document.createElement("li");
emptyMessage.className = "empty-message";
emptyMessage.textContent =
"No books in your reading list yet. Add some books to get started!";
orderedList.appendChild(emptyMessage);
return;
}

for (const book of books) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make a base condition, if book is empty, so your application does not crash.

Copy link
Author

@Baba05206 Baba05206 Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks @JaypeeLan. I have now updated the script to check if the books variable is null or undefined, or if the array is empty. Will return message telling the user their reading list is empty

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Baba05206 Please go through the conversations, I made a comment regarding the design.

const listItem = document.createElement("li");

const bookImage = document.createElement("img");
bookImage.src = book.bookCoverImage;
bookImage.alt = "Book cover for " + book.title;

const bookInfo = document.createElement("p");
bookInfo.textContent = book.title + " by " + book.author;

const statusBadge = document.createElement("span");
statusBadge.className = "status-badge";

if (book.alreadyRead) {
listItem.classList.add("read");
statusBadge.textContent = "✓";
} else {
listItem.classList.add("notRead");
statusBadge.textContent = "○";
}

listItem.appendChild(bookImage);
listItem.appendChild(bookInfo);
listItem.appendChild(statusBadge);

orderedList.appendChild(listItem);
}
}

readingList();
141 changes: 59 additions & 82 deletions Sprint-3/reading-list/script.test.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,59 @@
const path = require("path");
const { JSDOM } = require("jsdom");

let page = null;

beforeEach(async () => {
page = await JSDOM.fromFile(path.join(__dirname, "index.html"), {
resources: "usable",
runScripts: "dangerously",
});

// do this so students can use element.innerText which jsdom does not implement
Object.defineProperty(page.window.HTMLElement.prototype, "innerText", {
get() {
return this.textContent;
},
set(value) {
this.textContent = value;
},
});

return new Promise((res) => {
page.window.document.addEventListener("load", res);
});
});

afterEach(() => {
page = null;
});

describe("Reading list", () => {
test("renders a list of books with author and title", () => {
const readingList = page.window.document.querySelector("#reading-list");

expect(readingList).toHaveTextContent("The Design of Everyday Things");
expect(readingList).toHaveTextContent("Don Norman");

expect(readingList).toHaveTextContent("The Most Human Human");
expect(readingList).toHaveTextContent("Brian Christian");

expect(readingList).toHaveTextContent("The Pragmatic Programmer");
expect(readingList).toHaveTextContent("Andrew Hunt");
});
test("each book in the list has an image", () => {
const firstLi = page.window.document.querySelector(
"#reading-list > :first-child"
);
expect(firstLi).toContainHTML(
`<img src="https://blackwells.co.uk/jacket/l/9780465050659.jpg" />`
);

const secondLi = page.window.document.querySelector(
"#reading-list > :nth-child(2)"
);
expect(secondLi).toContainHTML(
`<img src="https://images-na.ssl-images-amazon.com/images/I/41m1rQjm5tL._SX322_BO1,204,203,200_.jpg" />`
);

const thirdLi = page.window.document.querySelector(
"#reading-list > :nth-child(3)"
);
expect(thirdLi).toContainHTML(
`<img src="https://blackwells.co.uk/jacket/l/9780135957059.jpg" />`
);
});
test("background color changes depending on whether book has been read", () => {
const firstLi = page.window.document.querySelector(
"#reading-list > :first-child"
);
expect(firstLi).toHaveStyle({ backgroundColor: "red" });

const secondLi = page.window.document.querySelector(
"#reading-list > :nth-child(2)"
);
expect(secondLi).toHaveStyle({ backgroundColor: "green" });

const thirdLi = page.window.document.querySelector(
"#reading-list > :nth-child(3)"
);
expect(thirdLi).toHaveStyle({ backgroundColor: "green" });
});
});
// for the tests, do not modify this array of books
const books = [
{
title: "The Design of Everyday Things",
author: "Don Norman",
alreadyRead: false,
bookCoverImage: "https://blackwells.co.uk/jacket/l/9780465050659.jpg",
},
{
title: "The Most Human Human",
author: "Brian Christian",
alreadyRead: true,
bookCoverImage:
"https://images-na.ssl-images-amazon.com/images/I/41m1rQjm5tL._SX322_BO1,204,203,200_.jpg",
},
{
title: "The Pragmatic Programmer",
author: "Andrew Hunt",
alreadyRead: true,
bookCoverImage: "https://blackwells.co.uk/jacket/l/9780135957059.jpg",
},
];

function readingList() {
const orderedList = document.querySelector("#reading-list");

// Base condition: check if books array is empty
if (!books || books.length === 0) {
const emptyMessage = document.createElement("li");
emptyMessage.className = "empty-message";
emptyMessage.textContent =
"No books in your reading list yet. Add some books to get started!";
orderedList.appendChild(emptyMessage);
return;
}

for (const book of books) {
const listItem = document.createElement("li");

const bookImage = document.createElement("img");
bookImage.src = book.bookCoverImage;
bookImage.alt = `Cover of ${book.title}`;

const bookInfo = document.createElement("p");
bookInfo.textContent = `${book.title} by ${book.author}`;

listItem.append(bookImage, bookInfo);

if (book.alreadyRead) {
listItem.classList.add("read");
} else {
listItem.classList.add("notRead");
}

orderedList.appendChild(listItem);
}
}

readingList();
Loading