From 426d6ebd45b45bceb64deb9354781c6dc826367a Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Sun, 29 Mar 2026 19:11:13 +0100 Subject: [PATCH 01/14] Committed index.html, script.mjs, todos.mjs and todos.test.mjs files --- Sprint-3/todo-list/index.html | 1 + Sprint-3/todo-list/script.mjs | 13 +++++++- Sprint-3/todo-list/todos.mjs | 16 ++++++++-- Sprint-3/todo-list/todos.test.mjs | 51 ++++++++++++++++++++++++++++--- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/Sprint-3/todo-list/index.html b/Sprint-3/todo-list/index.html index 4d12c4654..54c6ec4ae 100644 --- a/Sprint-3/todo-list/index.html +++ b/Sprint-3/todo-list/index.html @@ -20,6 +20,7 @@

My ToDo List

+ - - - + +
+

Your Tasks

+
+
+ + From b7e0ecfdd52964f868ea1c610ce5fd9911c1a68a Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Sun, 12 Apr 2026 19:26:03 +0100 Subject: [PATCH 03/14] Update todos.mjs based on voluntier feedback --- Sprint-3/todo-list/todos.mjs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/Sprint-3/todo-list/todos.mjs b/Sprint-3/todo-list/todos.mjs index 47f026033..ae99341d8 100644 --- a/Sprint-3/todo-list/todos.mjs +++ b/Sprint-3/todo-list/todos.mjs @@ -3,10 +3,9 @@ the following manner: [ - { task: "Description of task 1", completed: false}, - { task: "Description of task 2", completed: true} + { task: "Description of task 1", completed: false, deadline: null }, + { task: "Description of task 2", completed: true, deadline: "2026-12-31T23:59" } ] - */ // Append a new task to todos[] @@ -27,13 +26,3 @@ export function toggleCompletedOnTask(todos, taskIndex) { todos[taskIndex].completed = !todos[taskIndex].completed; } } - -// Remove all completed tasks from the todos array -export function deleteCompleted(todos) { - // Iterate backwards to safely remove items while mutating array - for (let i = todos.length - 1; i >= 0; i--) { - if (todos[i].completed) { - todos.splice(i, 1); - } - } -} From e529e168be99e5d7978e7ac761f526450b4cb680 Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Sun, 12 Apr 2026 19:27:06 +0100 Subject: [PATCH 04/14] Created webapp.js based on voluntier feedback --- Sprint-3/todo-list/webapp.js | 127 +++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 Sprint-3/todo-list/webapp.js diff --git a/Sprint-3/todo-list/webapp.js b/Sprint-3/todo-list/webapp.js new file mode 100644 index 000000000..ad682c7c0 --- /dev/null +++ b/Sprint-3/todo-list/webapp.js @@ -0,0 +1,127 @@ +// Import the todos module +import * as Todos from "./todos.mjs"; + +// Initialize empty todo list +let todos = []; + +// Wait for DOM to be fully loaded before accessing elements +document.addEventListener('DOMContentLoaded', () => { + // DOM elements + const taskInput = document.getElementById('taskInput'); + const deadlineInput = document.getElementById('deadlineInput'); + const addBtn = document.getElementById('addBtn'); + const todoListDiv = document.getElementById('todoList'); + + // Check if elements exist to prevent errors + if (!taskInput || !deadlineInput || !addBtn || !todoListDiv) { + console.error('Required DOM elements not found'); + return; + } + + // Function to format deadline for display + function formatDeadline(deadline) { + if (!deadline) return ''; + try { + const deadlineDate = new Date(deadline); + // Check if date is valid + if (isNaN(deadlineDate.getTime())) { + return ' (Invalid date)'; + } + return ` (Due: ${deadlineDate.toLocaleString()})`; + } catch (e) { + return ' (Invalid date)'; + } + } + + // Function to render the todo list to the page + function renderTodos() { + if (!todoListDiv) return; + + todoListDiv.innerHTML = ''; + + if (todos.length === 0) { + todoListDiv.innerHTML = '

No tasks yet. Add one above!

'; + return; + } + + todos.forEach((todo, index) => { + const todoDiv = document.createElement('div'); + todoDiv.className = 'todo-item'; + if (todo.completed) { + todoDiv.classList.add('completed'); + } + + // Task text with deadline info + const deadlineText = formatDeadline(todo.deadline); + const taskText = document.createElement('span'); + taskText.innerHTML = `${escapeHtml(todo.task)}${deadlineText}`; + + // Toggle completed button + const toggleBtn = document.createElement('button'); + toggleBtn.textContent = todo.completed ? '✓ Completed' : '○ Incomplete'; + toggleBtn.onclick = () => { + Todos.toggleCompletedOnTask(todos, index); + renderTodos(); // Re-render to show changes + }; + + // Delete button + const deleteBtn = document.createElement('button'); + deleteBtn.textContent = 'Delete'; + deleteBtn.onclick = () => { + Todos.deleteTask(todos, index); + renderTodos(); // Re-render to show changes + }; + + todoDiv.appendChild(taskText); + todoDiv.appendChild(toggleBtn); + todoDiv.appendChild(deleteBtn); + todoListDiv.appendChild(todoDiv); + }); + } + + // Helper function to escape HTML to prevent XSS + function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + + // Add task function (connects UI to the todos module) + function addTaskFromUI() { + if (!taskInput || !deadlineInput) return; + + const taskText = taskInput.value.trim(); + + if (!taskText) { + alert('Please enter a task description'); + return; + } + + // Get deadline from input + let deadline = null; + if (deadlineInput.value) { + deadline = deadlineInput.value; + } + + // Call the addTask function with deadline + Todos.addTask(todos, taskText, false, deadline); + + // Clear inputs + taskInput.value = ''; + deadlineInput.value = ''; + + // Re-render the updated list + renderTodos(); + } + + // Event listeners + addBtn.addEventListener('click', addTaskFromUI); + taskInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + addTaskFromUI(); + } + }); + + // Initial render + renderTodos(); +}); From 3ad8ce91bdd0caf3dd3f979bb375991c0d2462fe Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Sun, 12 Apr 2026 19:35:26 +0100 Subject: [PATCH 05/14] Little change to todos.mjs --- Sprint-3/todo-list/todos.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sprint-3/todo-list/todos.mjs b/Sprint-3/todo-list/todos.mjs index ae99341d8..b6043d1b4 100644 --- a/Sprint-3/todo-list/todos.mjs +++ b/Sprint-3/todo-list/todos.mjs @@ -13,14 +13,14 @@ export function addTask(todos, task, completed = false, deadline = null) { todos.push({ task, completed, deadline }); } -// Delete todos[taskIndex] if it exists +// Delete todos[taskIndex] export function deleteTask(todos, taskIndex) { if (todos[taskIndex]) { todos.splice(taskIndex, 1); } } -// Toggle the "completed" property of todos[taskIndex] if the task exists. +// Toggle the "completed" property of todos[taskIndex] export function toggleCompletedOnTask(todos, taskIndex) { if (todos[taskIndex]) { todos[taskIndex].completed = !todos[taskIndex].completed; From 3ca4d4b6f4bbc87ed263383880626bda64afcd5e Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Sun, 12 Apr 2026 19:38:22 +0100 Subject: [PATCH 06/14] Little change to webapp.js --- Sprint-3/todo-list/webapp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sprint-3/todo-list/webapp.js b/Sprint-3/todo-list/webapp.js index ad682c7c0..cf9cb9702 100644 --- a/Sprint-3/todo-list/webapp.js +++ b/Sprint-3/todo-list/webapp.js @@ -79,7 +79,7 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // Helper function to escape HTML to prevent XSS + // This added for security concern. Helper function to escape HTML to prevent XSS function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; From 00a584b1274f475023c78911099ce89a245d01cd Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Wed, 22 Apr 2026 00:03:29 +0100 Subject: [PATCH 07/14] Delete webapp.js file --- Sprint-3/todo-list/webapp.js | 127 ----------------------------------- 1 file changed, 127 deletions(-) delete mode 100644 Sprint-3/todo-list/webapp.js diff --git a/Sprint-3/todo-list/webapp.js b/Sprint-3/todo-list/webapp.js deleted file mode 100644 index cf9cb9702..000000000 --- a/Sprint-3/todo-list/webapp.js +++ /dev/null @@ -1,127 +0,0 @@ -// Import the todos module -import * as Todos from "./todos.mjs"; - -// Initialize empty todo list -let todos = []; - -// Wait for DOM to be fully loaded before accessing elements -document.addEventListener('DOMContentLoaded', () => { - // DOM elements - const taskInput = document.getElementById('taskInput'); - const deadlineInput = document.getElementById('deadlineInput'); - const addBtn = document.getElementById('addBtn'); - const todoListDiv = document.getElementById('todoList'); - - // Check if elements exist to prevent errors - if (!taskInput || !deadlineInput || !addBtn || !todoListDiv) { - console.error('Required DOM elements not found'); - return; - } - - // Function to format deadline for display - function formatDeadline(deadline) { - if (!deadline) return ''; - try { - const deadlineDate = new Date(deadline); - // Check if date is valid - if (isNaN(deadlineDate.getTime())) { - return ' (Invalid date)'; - } - return ` (Due: ${deadlineDate.toLocaleString()})`; - } catch (e) { - return ' (Invalid date)'; - } - } - - // Function to render the todo list to the page - function renderTodos() { - if (!todoListDiv) return; - - todoListDiv.innerHTML = ''; - - if (todos.length === 0) { - todoListDiv.innerHTML = '

No tasks yet. Add one above!

'; - return; - } - - todos.forEach((todo, index) => { - const todoDiv = document.createElement('div'); - todoDiv.className = 'todo-item'; - if (todo.completed) { - todoDiv.classList.add('completed'); - } - - // Task text with deadline info - const deadlineText = formatDeadline(todo.deadline); - const taskText = document.createElement('span'); - taskText.innerHTML = `${escapeHtml(todo.task)}${deadlineText}`; - - // Toggle completed button - const toggleBtn = document.createElement('button'); - toggleBtn.textContent = todo.completed ? '✓ Completed' : '○ Incomplete'; - toggleBtn.onclick = () => { - Todos.toggleCompletedOnTask(todos, index); - renderTodos(); // Re-render to show changes - }; - - // Delete button - const deleteBtn = document.createElement('button'); - deleteBtn.textContent = 'Delete'; - deleteBtn.onclick = () => { - Todos.deleteTask(todos, index); - renderTodos(); // Re-render to show changes - }; - - todoDiv.appendChild(taskText); - todoDiv.appendChild(toggleBtn); - todoDiv.appendChild(deleteBtn); - todoListDiv.appendChild(todoDiv); - }); - } - - // This added for security concern. Helper function to escape HTML to prevent XSS - function escapeHtml(text) { - const div = document.createElement('div'); - div.textContent = text; - return div.innerHTML; - } - - // Add task function (connects UI to the todos module) - function addTaskFromUI() { - if (!taskInput || !deadlineInput) return; - - const taskText = taskInput.value.trim(); - - if (!taskText) { - alert('Please enter a task description'); - return; - } - - // Get deadline from input - let deadline = null; - if (deadlineInput.value) { - deadline = deadlineInput.value; - } - - // Call the addTask function with deadline - Todos.addTask(todos, taskText, false, deadline); - - // Clear inputs - taskInput.value = ''; - deadlineInput.value = ''; - - // Re-render the updated list - renderTodos(); - } - - // Event listeners - addBtn.addEventListener('click', addTaskFromUI); - taskInput.addEventListener('keypress', (e) => { - if (e.key === 'Enter') { - addTaskFromUI(); - } - }); - - // Initial render - renderTodos(); -}); From e3b382f4c63f851197ad28847ec5b5b52cacbfc6 Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Wed, 22 Apr 2026 00:09:09 +0100 Subject: [PATCH 08/14] Update index.html --- Sprint-3/todo-list/index.html | 176 +++++++--------------------------- 1 file changed, 32 insertions(+), 144 deletions(-) diff --git a/Sprint-3/todo-list/index.html b/Sprint-3/todo-list/index.html index acb030f87..4d12c4654 100644 --- a/Sprint-3/todo-list/index.html +++ b/Sprint-3/todo-list/index.html @@ -1,152 +1,40 @@ - - - Todo List App - + + + ToDo List + + + + -

📝 Todo List

- -
- - - +
+

My ToDo List

+ +
+ +
- -
-

Your Tasks

-
-
- - + +
    +
+ + + + +
From 0c067124d4ffb6ea0298b027c9fcd0adcc976df8 Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Wed, 22 Apr 2026 00:10:02 +0100 Subject: [PATCH 09/14] Update script.mjs --- Sprint-3/todo-list/script.mjs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Sprint-3/todo-list/script.mjs b/Sprint-3/todo-list/script.mjs index 34402dea4..e73ca973e 100644 --- a/Sprint-3/todo-list/script.mjs +++ b/Sprint-3/todo-list/script.mjs @@ -8,16 +8,10 @@ const todos = []; window.addEventListener("load", () => { document.getElementById("add-task-btn").addEventListener("click", addNewTodo); -document - .getElementById("delete-completed-btn") - .addEventListener("click", deleteCompletedTasks); - // Populate sample data Todos.addTask(todos, "Wash the dishes", false); Todos.addTask(todos, "Do the shopping", true); - Todos.addTask(todos, "Finish assignment", false, "2026-04-01"); - render(); }); @@ -80,8 +74,3 @@ function createListItem(todo, index) { return li; } - -function deleteCompletedTasks() { - Todos.deleteCompleted(todos); - render(); -} From 1669629c4aa78ad7bccaccec4694c0c9c0324818 Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Wed, 22 Apr 2026 16:28:03 +0100 Subject: [PATCH 10/14] Change deadline argument to true --- Sprint-3/todo-list/script.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sprint-3/todo-list/script.mjs b/Sprint-3/todo-list/script.mjs index e73ca973e..b3d424a82 100644 --- a/Sprint-3/todo-list/script.mjs +++ b/Sprint-3/todo-list/script.mjs @@ -10,7 +10,7 @@ window.addEventListener("load", () => { // Populate sample data Todos.addTask(todos, "Wash the dishes", false); - Todos.addTask(todos, "Do the shopping", true); + Todos.addTask(todos, "Do the shopping", false); // if 2nd argument is true the msg 'Do the shopping' is crossed out render(); }); From 958fcfe1c9371f191f3dbe5d45a5a295fe58e3e3 Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Wed, 22 Apr 2026 16:46:25 +0100 Subject: [PATCH 11/14] Add a new class inside div --- Sprint-3/todo-list/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/Sprint-3/todo-list/index.html b/Sprint-3/todo-list/index.html index 4d12c4654..5e1785496 100644 --- a/Sprint-3/todo-list/index.html +++ b/Sprint-3/todo-list/index.html @@ -15,6 +15,7 @@

My ToDo List

+
From 196ca030df09d25ae3366deec988264774de34b7 Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Wed, 22 Apr 2026 16:47:18 +0100 Subject: [PATCH 12/14] Add new argument to ajust for change in index.html --- Sprint-3/todo-list/script.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sprint-3/todo-list/script.mjs b/Sprint-3/todo-list/script.mjs index b3d424a82..5e0f23519 100644 --- a/Sprint-3/todo-list/script.mjs +++ b/Sprint-3/todo-list/script.mjs @@ -21,8 +21,11 @@ window.addEventListener("load", () => { function addNewTodo() { const taskInput = document.getElementById("new-task-input"); const task = taskInput.value.trim(); + const deadlineInput = document.getElementById("new-task-deadline"); + const deadline = deadlineInput.value || null; + if (task) { - Todos.addTask(todos, task, false); + Todos.addTask(todos, task, false, deadline); render(); } From bf2eb25db1d56ce3e7521671d7cb3a6e47b0915c Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Wed, 22 Apr 2026 17:03:10 +0100 Subject: [PATCH 13/14] Enlarged the container and ensured the Add button fits completely inside the same rounded container --- Sprint-3/todo-list/style.css | 191 +++++++++++++++++++++++++++-------- 1 file changed, 150 insertions(+), 41 deletions(-) diff --git a/Sprint-3/todo-list/style.css b/Sprint-3/todo-list/style.css index 535e91227..2afd54b07 100644 --- a/Sprint-3/todo-list/style.css +++ b/Sprint-3/todo-list/style.css @@ -2,106 +2,215 @@ box-sizing: border-box; margin: 0; padding: 0; - font-family: Arial, sans-serif; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { - background-color: #f4f4f4; - padding: 40px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + padding: 40px 20px; + display: flex; + align-items: center; + justify-content: center; } .todo-container { - max-width: 500px; + max-width: 650px; + width: 100%; margin: 0 auto; - background: white; - border-radius: 10px; - padding: 20px; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + background: rgba(255, 255, 255, 0.98); + border-radius: 32px; + padding: 36px 32px 44px; + box-shadow: 0 25px 45px -12px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.1) inset; + transition: transform 0.2s ease; +} + +.todo-container:hover { + transform: scale(1.01); } h1 { text-align: center; - margin-bottom: 20px; + font-size: 2.2rem; + font-weight: 700; + background: linear-gradient(120deg, #2d3748, #4a5568); + background-clip: text; + -webkit-background-clip: text; + color: transparent; + margin-bottom: 32px; + letter-spacing: -0.5px; } +/* Input area – both field and button fully inside the rounded container */ .todo-input { display: flex; - gap: 10px; - margin-bottom: 20px; + gap: 14px; + margin-bottom: 36px; + background: #f8fafc; + /* Extra right padding to give more space after the Add button */ + padding: 10px 18px 10px 10px; + border-radius: 80px; + box-shadow: inset 0 1px 3px rgba(0,0,0,0.02), 0 2px 6px rgba(0,0,0,0.05); + min-height: 70px; + align-items: center; } .todo-input input { flex: 1; - padding: 10px; - font-size: 16px; - border-radius: 6px; - border: 1px solid #ccc; + padding: 16px 22px; + font-size: 1rem; + border: none; + background: white; + border-radius: 60px; + outline: none; + transition: all 0.2s; + box-shadow: 0 1px 3px rgba(0,0,0,0.05); + font-weight: 500; + color: #1a202c; +} + +.todo-input input:focus { + box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.2); + background: #ffffff; } .todo-input button { - padding: 10px 20px; - font-size: 16px; - background-color: #4CAF50; + padding: 14px 32px; + font-size: 1rem; + font-weight: 600; + background: linear-gradient(95deg, #4c6ef5, #3b4fcf); color: white; border: none; - border-radius: 6px; + border-radius: 60px; cursor: pointer; + transition: all 0.2s ease; + box-shadow: 0 8px 18px rgba(60, 80, 200, 0.3); + letter-spacing: 0.5px; + white-space: nowrap; + display: inline-flex; + align-items: center; + gap: 8px; + /* No margin-right needed; the parent's padding-right provides the space */ } .todo-input button:hover { - background-color: #45a049; + background: linear-gradient(95deg, #5f7cf5, #4a62e0); + transform: translateY(-2px); + box-shadow: 0 14px 24px -8px rgba(60, 80, 200, 0.5); +} + +.todo-input button:active { + transform: translateY(1px); + box-shadow: 0 6px 12px rgba(60, 80, 200, 0.25); } +/* Todo list styling */ .todo-list { list-style-type: none; padding-left: 0; + margin-top: 10px; + display: flex; + flex-direction: column; + gap: 12px; } .todo-item { display: flex; align-items: center; justify-content: space-between; - padding: 12px 10px; - margin-bottom: 10px; - border: 1px solid #ddd; - border-radius: 6px; - background-color: #fff; + padding: 14px 20px; + background: #ffffff; + border-radius: 80px; + border: 1px solid #edf2f7; + transition: all 0.2s; + box-shadow: 0 1px 3px rgba(0,0,0,0.03); +} + +.todo-item:hover { + background: #fafcff; + border-color: #cbd5e1; + box-shadow: 0 6px 14px -8px rgba(0,0,0,0.12); } .description { flex: 1; - margin-right: 10px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + margin-right: 16px; + font-size: 1rem; + font-weight: 500; + color: #2d3e50; + word-break: break-word; + white-space: normal; + line-height: 1.4; +} + +/* Completed tasks */ +.todo-item.completed .description { + text-decoration: line-through; + color: #94a3b8; } +/* Action buttons */ .actions { display: flex; - gap: 10px; + gap: 12px; } .actions button { background: none; border: none; cursor: pointer; - font-size: 18px; - display: flex; + font-size: 1.2rem; + width: 40px; + height: 40px; + border-radius: 40px; + display: inline-flex; align-items: center; justify-content: center; - width: 32px; - height: 32px; + transition: all 0.2s; + background: #f1f5f9; + color: #475569; } -.complete-btn i { - color: green; +.actions button:hover { + transform: scale(1.05); } -.delete-btn i { - color: red; +.complete-btn:hover { + background: #e0f2fe; + color: #0f6b3a; } -.todo-item.completed .description { - text-decoration: line-through; - color: gray; +.delete-btn:hover { + background: #fee2e2; + color: #c2410c; +} + +/* Responsive */ +@media (max-width: 550px) { + .todo-container { + padding: 24px 18px 32px; + } + .todo-input { + flex-wrap: wrap; + background: transparent; + padding: 0; + border-radius: 0; + gap: 16px; + min-height: auto; + } + .todo-input input, + .todo-input button { + width: 100%; + border-radius: 60px; + } + .todo-item { + flex-wrap: wrap; + gap: 12px; + border-radius: 32px; + padding: 16px; + } + .actions { + width: 100%; + justify-content: flex-end; + } } From fbca2dffc54ee7d15203f89627999e270fce5a3f Mon Sep 17 00:00:00 2001 From: Carlos Abreu Date: Thu, 23 Apr 2026 17:47:23 +0100 Subject: [PATCH 14/14] The deleteCompleted function has been added to todos.mjs to match the test expectations --- Sprint-3/todo-list/todos.mjs | 9 +++ Sprint-3/todo-list/todos.test.mjs | 115 +++++++++++++----------------- 2 files changed, 57 insertions(+), 67 deletions(-) diff --git a/Sprint-3/todo-list/todos.mjs b/Sprint-3/todo-list/todos.mjs index b6043d1b4..c5602621c 100644 --- a/Sprint-3/todo-list/todos.mjs +++ b/Sprint-3/todo-list/todos.mjs @@ -26,3 +26,12 @@ export function toggleCompletedOnTask(todos, taskIndex) { todos[taskIndex].completed = !todos[taskIndex].completed; } } + +// Delete all completed tasks +export function deleteCompleted(todos) { + for (let i = todos.length - 1; i >= 0; i--) { + if (todos[i].completed) { + todos.splice(i, 1); + } + } +} diff --git a/Sprint-3/todo-list/todos.test.mjs b/Sprint-3/todo-list/todos.test.mjs index eecb693ff..a58896827 100644 --- a/Sprint-3/todo-list/todos.test.mjs +++ b/Sprint-3/todo-list/todos.test.mjs @@ -1,11 +1,9 @@ -// The tests is prepared to demonstrate we can test the functions -// in a module independently. - // Command to execute this script: -// npm test todos.test.mjs +// node --test todos.test.mjs -// Import all the exported members through an object import * as Todos from "./todos.mjs"; +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; // Return a mock ToDo List data with exactly 4 elements. function createMockTodos() { @@ -21,153 +19,136 @@ function createMockTodos() { const theTask = { task: "The Task", completed: false, deadline: null }; describe("addTask()", () => { - test("Add a task to an empty ToDo list", () => { + it("Add a task to an empty ToDo list", () => { let todos = []; Todos.addTask(todos, theTask.task, theTask.completed); - expect(todos).toHaveLength(1); - expect(todos[0]).toEqual(theTask); + assert.strictEqual(todos.length, 1); + assert.deepEqual(todos[0], theTask); }); - test("Should append a new task to the end of a ToDo list", () => { - + it("Should append a new task to the end of a ToDo list", () => { const todos = createMockTodos(); const lengthBeforeAddition = todos.length; Todos.addTask(todos, theTask.task, theTask.completed); - // todos should now have one more task - expect(todos).toHaveLength(lengthBeforeAddition + 1); - - // New task should be appended to the todos - expect(todos[todos.length - 1]).toEqual(theTask); + assert.strictEqual(todos.length, lengthBeforeAddition + 1); + assert.deepEqual(todos[todos.length - 1], theTask); }); }); describe("deleteTask()", () => { - - test("Delete the first task", () => { + it("Delete the first task", () => { const todos = createMockTodos(); const todosBeforeDeletion = createMockTodos(); const lengthBeforeDeletion = todos.length; Todos.deleteTask(todos, 0); - expect(todos).toHaveLength(lengthBeforeDeletion - 1); - - expect(todos[0]).toEqual(todosBeforeDeletion[1]); - expect(todos[1]).toEqual(todosBeforeDeletion[2]); - expect(todos[2]).toEqual(todosBeforeDeletion[3]); + assert.strictEqual(todos.length, lengthBeforeDeletion - 1); + assert.deepEqual(todos[0], todosBeforeDeletion[1]); + assert.deepEqual(todos[1], todosBeforeDeletion[2]); + assert.deepEqual(todos[2], todosBeforeDeletion[3]); }); - test("Delete the second task (a middle task)", () => { + it("Delete the second task (a middle task)", () => { const todos = createMockTodos(); const todosBeforeDeletion = createMockTodos(); const lengthBeforeDeletion = todos.length; Todos.deleteTask(todos, 1); - expect(todos).toHaveLength(lengthBeforeDeletion - 1); - - expect(todos[0]).toEqual(todosBeforeDeletion[0]); - expect(todos[1]).toEqual(todosBeforeDeletion[2]); - expect(todos[2]).toEqual(todosBeforeDeletion[3]); + assert.strictEqual(todos.length, lengthBeforeDeletion - 1); + assert.deepEqual(todos[0], todosBeforeDeletion[0]); + assert.deepEqual(todos[1], todosBeforeDeletion[2]); + assert.deepEqual(todos[2], todosBeforeDeletion[3]); }); - test("Delete the last task", () => { + it("Delete the last task", () => { const todos = createMockTodos(); const todosBeforeDeletion = createMockTodos(); const lengthBeforeDeletion = todos.length; Todos.deleteTask(todos, todos.length - 1); - expect(todos).toHaveLength(lengthBeforeDeletion - 1); - - expect(todos[0]).toEqual(todosBeforeDeletion[0]); - expect(todos[1]).toEqual(todosBeforeDeletion[1]); - expect(todos[2]).toEqual(todosBeforeDeletion[2]); + assert.strictEqual(todos.length, lengthBeforeDeletion - 1); + assert.deepEqual(todos[0], todosBeforeDeletion[0]); + assert.deepEqual(todos[1], todosBeforeDeletion[1]); + assert.deepEqual(todos[2], todosBeforeDeletion[2]); }); - test("Delete a non-existing task", () => { + it("Delete a non-existing task", () => { const todos = createMockTodos(); const todosBeforeDeletion = createMockTodos(); Todos.deleteTask(todos, 10); - expect(todos).toEqual(todosBeforeDeletion); + assert.deepEqual(todos, todosBeforeDeletion); Todos.deleteTask(todos, -1); - expect(todos).toEqual(todosBeforeDeletion); + assert.deepEqual(todos, todosBeforeDeletion); }); }); describe("toggleCompletedOnTask()", () => { - - test("Expect the 'completed' property to toggle on an existing task", () => { + it("Expect the 'completed' property to toggle on an existing task", () => { const todos = createMockTodos(); const taskIndex = 1; const completedStateBeforeToggle = todos[taskIndex].completed; Todos.toggleCompletedOnTask(todos, taskIndex); - expect(todos[taskIndex].completed).toEqual(!completedStateBeforeToggle); + assert.strictEqual(todos[taskIndex].completed, !completedStateBeforeToggle); // Toggle again Todos.toggleCompletedOnTask(todos, taskIndex); - expect(todos[taskIndex].completed).toEqual(completedStateBeforeToggle); + assert.strictEqual(todos[taskIndex].completed, completedStateBeforeToggle); }); - test("Expect toggling on a task does not affect other tasks", () => { + it("Expect toggling on a task does not affect other tasks", () => { const todos = createMockTodos(); const todosBeforeToggle = createMockTodos(); Todos.toggleCompletedOnTask(todos, 1); - - expect(todos[0]).toEqual(todosBeforeToggle[0]); - expect(todos[2]).toEqual(todosBeforeToggle[2]); - expect(todos[3]).toEqual(todosBeforeToggle[3]); - }); + assert.deepEqual(todos[0], todosBeforeToggle[0]); + assert.deepEqual(todos[2], todosBeforeToggle[2]); + assert.deepEqual(todos[3], todosBeforeToggle[3]); + }); - test("Expect no change when toggling on a non-existing task", () => { + it("Expect no change when toggling on a non-existing task", () => { const todos = createMockTodos(); const todosBeforeToggle = createMockTodos(); Todos.toggleCompletedOnTask(todos, 10); - expect(todos).toEqual(todosBeforeToggle); + assert.deepEqual(todos, todosBeforeToggle); Todos.toggleCompletedOnTask(todos, -1); - expect(todos).toEqual(todosBeforeToggle); + assert.deepEqual(todos, todosBeforeToggle); }); }); describe("deleteCompleted()", () => { - - test("Remove all completed tasks", () => { + it("Remove all completed tasks", () => { const todos = createMockTodos(); Todos.deleteCompleted(todos); - // Only incomplete tasks should remain - expect(todos).toHaveLength(2); - expect(todos[0].completed).toBe(false); - expect(todos[1].completed).toBe(false); - - expect(todos[0].task).toBe("Task 2 description"); - expect(todos[1].task).toBe("Task 4 description"); + assert.strictEqual(todos.length, 2); + assert.strictEqual(todos[0].completed, false); + assert.strictEqual(todos[1].completed, false); + assert.strictEqual(todos[0].task, "Task 2 description"); + assert.strictEqual(todos[1].task, "Task 4 description"); }); - test("No change if no tasks are completed", () => { + it("No change if no tasks are completed", () => { const todos = [ { task: "Task A", completed: false }, { task: "Task B", completed: false } ]; const before = JSON.parse(JSON.stringify(todos)); - Todos.deleteCompleted(todos); - - expect(todos).toEqual(before); + assert.deepEqual(todos, before); }); - test("All tasks removed if all are completed", () => { + it("All tasks removed if all are completed", () => { const todos = [ { task: "Task A", completed: true }, { task: "Task B", completed: true } ]; Todos.deleteCompleted(todos); - - expect(todos).toHaveLength(0); + assert.strictEqual(todos.length, 0); }); - });