From 745ca234face74579d5b5f02be1a5cdb6096511c Mon Sep 17 00:00:00 2001 From: Majd Hamde Date: Thu, 16 Oct 2025 10:44:26 +0200 Subject: [PATCH 1/5] Week 2 Using APIs exercises --- .../assignment/ex1-programmerFun/index.js | 66 ++++++++++++---- .../Week2/assignment/ex2-pokemonApp/index.js | 75 +++++++++++++++++-- .../Week2/assignment/ex2-pokemonApp/style.css | 48 ++++++++++++ 3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js | 37 +++++---- 3-UsingAPIs/Week2/assignment/ex4-diceRace.js | 27 +++++-- 3-UsingAPIs/Week2/assignment/ex5-vscDebug.js | 7 +- .../assignment/ex6-browserDebug/index.js | 57 ++++++++++++-- 7 files changed, 265 insertions(+), 52 deletions(-) diff --git a/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js b/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js index a99ca177b..caef9a9bd 100644 --- a/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js +++ b/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js @@ -17,28 +17,68 @@ Full description at: https://github.com/HackYourFuture/Assignments/blob/main/3-U should result in a network (DNS) error. ------------------------------------------------------------------------------*/ function requestData(url) { - // TODO return a promise using `fetch()` + try { + const response = await fetch(url); + + // Handle HTTP errors + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + // Parse the response as JSON + const data = await response.json(); + return data; + } catch (error) { + // Network or parsing error + throw new Error(`Failed to fetch data: ${error.message}`); + } } function renderImage(data) { - // TODO render the image to the DOM + // Create an image element + const img = document.createElement('img'); + img.src = data.img; // The xkcd API returns `img` as the image URL + img.alt = data.alt; // Alt text is included in the API + img.style.maxWidth = '90%'; + img.style.display = 'block'; + img.style.margin = '40px auto'; + img.style.borderRadius = '12px'; + img.style.boxShadow = '0 4px 10px rgba(0,0,0,0.2)'; + + // Add title + const title = document.createElement('h2'); + title.textContent = data.title; + title.style.textAlign = 'center'; + title.style.fontFamily = 'sans-serif'; + + // Append both to the document body + document.body.appendChild(title); + document.body.appendChild(img); console.log(data); } function renderError(error) { - // TODO render the error to the DOM + // Create an error message element + const errorMessage = document.createElement('h1'); + errorMessage.textContent = `⚠️ ${error.message}`; + errorMessage.style.color = 'red'; + errorMessage.style.textAlign = 'center'; + errorMessage.style.marginTop = '50px'; + errorMessage.style.fontFamily = 'sans-serif'; + + document.body.appendChild(errorMessage); console.log(error); } -// TODO refactor with async/await and try/catch -function main() { - requestData('https://xkcd.now.sh/?comic=latest') - .then((data) => { - renderImage(data); - }) - .catch((error) => { - renderError(error); - }); +async function main() { + const url = 'https://xkcd.now.sh/?comic=latest'; + + try { + const data = await requestData(url); + renderImage(data); + } catch (error) { + renderError(error); + } } -window.addEventListener('load', main); +window.addEventListener('load', main); \ No newline at end of file diff --git a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js index 262113997..562dd93d7 100644 --- a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js +++ b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js @@ -21,18 +21,77 @@ Use async/await and try/catch to handle promises. Try and avoid using global variables. As much as possible, try and use function parameters and return values to pass data back and forth. ------------------------------------------------------------------------------*/ -function fetchData(/* TODO parameter(s) go here */) { - // TODO complete this function +async function fetchData(url) { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return await response.json(); + } catch (error) { + console.error('Fetch error:', error); + } } -function fetchAndPopulatePokemons(/* TODO parameter(s) go here */) { - // TODO complete this function +async function fetchAndPopulatePokemons(selectEl) { + const data = await fetchData('https://pokeapi.co/api/v2/pokemon?limit=150'); + if (!data) return; + + data.results.forEach((pokemon) => { + const option = document.createElement('option'); + option.value = pokemon.url; // Store the API URL for each Pokémon + option.textContent = pokemon.name; + selectEl.appendChild(option); + }); } -function fetchImage(/* TODO parameter(s) go here */) { - // TODO complete this function +async function fetchImage(pokemonUrl, imgEl) { + if (!pokemonUrl) { + imgEl.hidden = true; + return; + } + + const data = await fetchData(pokemonUrl); + if (data && data.sprites && data.sprites.front_default) { + imgEl.src = data.sprites.front_default; + imgEl.alt = data.name; + imgEl.hidden = false; + } else { + imgEl.hidden = true; + } } -function main() { - // TODO complete this function +async function main() { + // Create elements dynamically + const container = document.createElement('div'); + container.classList.add('container'); + + const title = document.createElement('h1'); + title.textContent = 'Pokémon Browser'; + + const select = document.createElement('select'); + const defaultOption = document.createElement('option'); + defaultOption.textContent = 'Select a Pokémon'; + defaultOption.value = ''; + select.appendChild(defaultOption); + + const img = document.createElement('img'); + img.id = 'pokemon-image'; + img.hidden = true; + + // Add elements to the DOM + container.append(title, select, img); + document.body.appendChild(container); + + // Populate dropdown + await fetchAndPopulatePokemons(select); + + // Add event listener + select.addEventListener('change', async (event) => { + const pokemonUrl = event.target.value; + await fetchImage(pokemonUrl, img); + }); } + +// Run when page is loaded +window.addEventListener('load', main); \ No newline at end of file diff --git a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css index 44cb05eeb..d8c55e11b 100644 --- a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css +++ b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css @@ -1 +1,49 @@ /* add your styling here */ +body { + font-family: Arial, sans-serif; + background: #f2f6fc; + color: #333; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; +} + +.container { + text-align: center; + background: white; + padding: 2rem 3rem; + border-radius: 10px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); +} + +h1 { + color: #ef5350; + margin-bottom: 1rem; +} + +select { + padding: 0.5rem 1rem; + border-radius: 8px; + border: 1px solid #ccc; + font-size: 1rem; + background: #fafafa; + cursor: pointer; +} + +select:hover { + border-color: #ef5350; +} + +img { + display: block; + width: 150px; + height: 150px; + margin: 1.5rem auto 0; + image-rendering: pixelated; + transition: transform 0.3s ease; +} + +img:hover { + transform: scale(1.1); +} diff --git a/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js b/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js index 861b31047..cc9194231 100644 --- a/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js +++ b/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js @@ -17,24 +17,35 @@ import { rollDie } from '../../helpers/pokerDiceRoller.js'; * @param {DieFace} desiredValue * @returns {Promise} */ -export function rollDieUntil(desiredValue) { - // TODO rewrite this function using async/await - return rollDie().then((value) => { - if (value !== desiredValue) { - return rollDieUntil(desiredValue); +export async function rollDieUntil(desiredValue) { + // Rewritten with async/await and a while loop (no recursion) + let value; + + while (true) { + try { + value = await rollDie(); // wait for the result + console.log(`Rolled: ${value}`); + if (value === desiredValue) { + return value; // stop when ACE is rolled + } + } catch (err) { + // If the die rolls off the table (rejection), rethrow the error + throw err; } - return value; - }); + } } -// TODO refactor this function to use try/catch -function main() { - rollDieUntil('ACE') - .then((results) => console.log('Resolved!', results)) - .catch((error) => console.log('Rejected!', error.message)); +// Refactored main() to async/await and try/catch +async function main() { + try { + const result = await rollDieUntil('ACE'); + console.log('Resolved!', result); + } catch (error) { + console.log('Rejected!', error.message); + } } // ! Do not change or remove the code below if (process.env.NODE_ENV !== 'test') { main(); -} +} \ No newline at end of file diff --git a/3-UsingAPIs/Week2/assignment/ex4-diceRace.js b/3-UsingAPIs/Week2/assignment/ex4-diceRace.js index ddff3242c..4c6640dc0 100644 --- a/3-UsingAPIs/Week2/assignment/ex4-diceRace.js +++ b/3-UsingAPIs/Week2/assignment/ex4-diceRace.js @@ -15,15 +15,20 @@ import { rollDie } from '../../helpers/pokerDiceRoller.js'; export function rollDice() { const dice = [1, 2, 3, 4, 5]; - // TODO complete this function; use Promise.race() and rollDie() - rollDie(1); // TODO placeholder: modify as appropriate + // Each die rolls independently (returns a Promise) + const dicePromises = dice.map(() => rollDie()); + // Return a promise that resolves when the first die finishes rolling + return Promise.race(dicePromises); } -// Refactor this function to use async/await and try/catch -function main() { - rollDice() - .then((results) => console.log('Resolved!', results)) - .catch((error) => console.log('Rejected!', error.message)); +// Refactored using async/await and try/catch +async function main() { + try { + const winner = await rollDice(); + console.log('Resolved!', winner); + } catch (error) { + console.log('Rejected!', error.message); + } } // ! Do not change or remove the code below @@ -31,4 +36,10 @@ if (process.env.NODE_ENV !== 'test') { main(); } -// TODO Replace this comment by your explanation that was asked for in the assignment description. +/*------------------------------------------------------------------------------ +Explanation: +Some dice continue rolling after Promise.race() resolves because +Promise.race() only resolves/rejects based on the *first* promise that settles. +The other promises keep running in the background until they complete, +but their results are ignored. +*/ \ No newline at end of file diff --git a/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js b/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js index a65448e57..ed4fe9da5 100644 --- a/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js +++ b/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js @@ -20,8 +20,9 @@ function renderLaureates(laureates) { async function fetchAndRender() { try { - const laureates = getData( - 'http://api.nobelprize.org/2.0/laureates?birthCountry=Netherlands&format=json&csvLang=en' + // Await the fetchData promise to get actual data + const laureates = await getData( + 'https://api.nobelprize.org/2.0/laureates?birthCountry=Netherlands&format=json&csvLang=en' ); renderLaureates(laureates); } catch (err) { @@ -29,4 +30,4 @@ async function fetchAndRender() { } } -fetchAndRender(); +fetchAndRender(); \ No newline at end of file diff --git a/3-UsingAPIs/Week2/assignment/ex6-browserDebug/index.js b/3-UsingAPIs/Week2/assignment/ex6-browserDebug/index.js index 91e0402be..bbc573051 100644 --- a/3-UsingAPIs/Week2/assignment/ex6-browserDebug/index.js +++ b/3-UsingAPIs/Week2/assignment/ex6-browserDebug/index.js @@ -2,9 +2,22 @@ Full description at:https://github.com/HackYourFuture/Assignments/blob/main/3-UsingAPIs/Week2/README.md#exercise-6-using-the-browser-debugger */ +/* +Full description at: +https://github.com/HackYourFuture/Assignments/blob/main/3-UsingAPIs/Week2/README.md#exercise-6-using-the-browser-debugger + +This exercise focuses on learning to debug web-based JavaScript using the browser debugger. +Try placing breakpoints in getData(), renderLaureate(), and fetchAndRender() to inspect +data flow, function calls, and variable values. +*/ + async function getData(url) { const response = await fetch(url); - return response.json(); + if (!response.ok) { + throw new Error(`HTTP error: ${response.status}`); + } + const data = await response.json(); + return data; } function createAndAppend(name, parent, options = {}) { @@ -29,9 +42,39 @@ function addTableRow(table, label, value) { function renderLaureate(ul, { knownName, birth, death }) { const li = createAndAppend('li', ul); const table = createAndAppend('table', li); - addTableRow(table, 'Name', knownName.en); - addTableRow(table, 'Birth', `${birth.date}, ${birth.place.locationString}`); - addTableRow(table, 'Death', `${death.date}, ${death.place.locationString}`); + + // Name + addTableRow(table, 'Name', knownName?.en ?? 'Unknown'); + + // Birth + const birthPlace = birth?.place + ? [ + birth.place.city?.en, + birth.place.country?.en, + ] + .filter(Boolean) + .join(', ') + : ''; + + const birthInfo = birth + ? `${birth.date ?? 'Unknown'}${birthPlace ? ', ' + birthPlace : ''}` + : 'Unknown'; + addTableRow(table, 'Birth', birthInfo); + + // Death + const deathPlace = death?.place + ? [ + death.place.city?.en, + death.place.country?.en, + ] + .filter(Boolean) + .join(', ') + : ''; + + const deathInfo = death + ? `${death.date ?? 'N/A'}${deathPlace ? ', ' + deathPlace : ''}` + : 'Still alive'; + addTableRow(table, 'Death', deathInfo); } function renderLaureates(laureates) { @@ -41,13 +84,13 @@ function renderLaureates(laureates) { async function fetchAndRender() { try { - const laureates = getData( + const data = await getData( 'https://api.nobelprize.org/2.0/laureates?birthCountry=Netherlands&format=json&csvLang=en' ); - renderLaureates(laureates); + renderLaureates(data.laureates); } catch (err) { console.error(`Something went wrong: ${err.message}`); } } -window.addEventListener('load', fetchAndRender); +window.addEventListener('load', fetchAndRender); \ No newline at end of file From ea7f481dff655b7246a34afa3d514cf1c39eee1e Mon Sep 17 00:00:00 2001 From: Majd Hamde Date: Thu, 16 Oct 2025 11:06:26 +0200 Subject: [PATCH 2/5] Week2 Ex1: Programmer Fun - fixed async/await with fetch and try/catch --- .test-summary/TEST_SUMMARY.md | 14 +++++++ .../test-reports/ex1-programmerFun.report.txt | 40 +++++++++++++++++++ .../test-reports/ex2-pokemonApp.report.txt | 17 ++++++++ .../test-reports/ex3-rollAnAce.report.txt | 27 +++++++++++++ .../test-reports/ex4-diceRace.report.txt | 19 +++++++++ .../test-reports/ex5-vscDebug.report.txt | 3 ++ .../test-reports/ex6-browserDebug.report.txt | 3 ++ 7 files changed, 123 insertions(+) create mode 100644 .test-summary/TEST_SUMMARY.md create mode 100644 3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt create mode 100644 3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.report.txt create mode 100644 3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt create mode 100644 3-UsingAPIs/Week2/test-reports/ex4-diceRace.report.txt create mode 100644 3-UsingAPIs/Week2/test-reports/ex5-vscDebug.report.txt create mode 100644 3-UsingAPIs/Week2/test-reports/ex6-browserDebug.report.txt diff --git a/.test-summary/TEST_SUMMARY.md b/.test-summary/TEST_SUMMARY.md new file mode 100644 index 000000000..60857fe8e --- /dev/null +++ b/.test-summary/TEST_SUMMARY.md @@ -0,0 +1,14 @@ +## Test Summary + +**Mentors**: For more information on how to review homework assignments, please refer to the [Review Guide](https://github.com/HackYourFuture/mentors/blob/main/assignment-support/review-guide.md). + +### 3-UsingAPIs - Week2 + +| Exercise | Passed | Failed | ESLint | +|-------------------|--------|--------|--------| +| ex1-programmerFun | 2 | 3 | ✓ | +| ex2-pokemonApp | 5 | - | ✓ | +| ex3-rollAnAce | 6 | 1 | ✓ | +| ex4-diceRace | 7 | - | ✓ | +| ex5-vscDebug | - | - | ✓ | +| ex6-browserDebug | - | - | ✓ | diff --git a/3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt b/3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt new file mode 100644 index 000000000..d4e13fb38 --- /dev/null +++ b/3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt @@ -0,0 +1,40 @@ +*** Unit Test Error Report *** + +Command failed: npx jest /Users/majdjadalhaq/Desktop/Assignments-Cohort54/.dist/3-UsingAPIs/Week2/unit-tests/ex1-programmerFun.test.js --colors --noStackTrace --json + FAIL .dist/3-UsingAPIs/Week2/unit-tests/ex1-programmerFun.test.js + api-wk2-ex1-programmerFun + ✅ HTML should be syntactically valid (145 ms) + ✅ should have all TODO comments removed + ❌ should use `fetch()` (1 ms) + ❌ should use async/wait + ❌ should use try/catch + + ● api-wk2-ex1-programmerFun › should use `fetch()` + + expect(received).toBeDefined() + + Received: undefined + + ● api-wk2-ex1-programmerFun › should use async/wait + + expect(received).toBeDefined() + + Received: undefined + + ● api-wk2-ex1-programmerFun › should use try/catch + + expect(received).toBeDefined() + + Received: undefined + +Test Suites: 1 failed, 1 total +Tests: 3 failed, 2 passed, 5 total +Snapshots: 0 total +Time: 2.984 s, estimated 4 s +Ran all test suites matching /\/Users\/majdjadalhaq\/Desktop\/Assignments-Cohort54\/.dist\/3-UsingAPIs\/Week2\/unit-tests\/ex1-programmerFun.test.js/i. +No linting errors detected. + + +*** Spell Checker Report *** + +3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js:40:30 - Unknown word (xkcd) diff --git a/3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.report.txt b/3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.report.txt new file mode 100644 index 000000000..29087d70f --- /dev/null +++ b/3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.report.txt @@ -0,0 +1,17 @@ +*** Unit Test Error Report *** + + PASS .dist/3-UsingAPIs/Week2/unit-tests/ex2-pokemonApp.test.js + api-wk2-ex2-pokemonApp + ✅ HTML should be syntactically valid (149 ms) + ✅ should have all TODO comments removed (1 ms) + ✅ should use `fetch()` + ✅ should use `await fetch()` (1 ms) + ✅ should use try/catch + +Test Suites: 1 passed, 1 total +Tests: 5 passed, 5 total +Snapshots: 0 total +Time: 2.696 s, estimated 4 s +Ran all test suites matching /\/Users\/majdjadalhaq\/Desktop\/Assignments-Cohort54\/.dist\/3-UsingAPIs\/Week2\/unit-tests\/ex2-pokemonApp.test.js/i. +No linting errors detected. +No spelling errors detected. diff --git a/3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt b/3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt new file mode 100644 index 000000000..a1fb09a00 --- /dev/null +++ b/3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt @@ -0,0 +1,27 @@ +*** Unit Test Error Report *** + +Command failed: npx jest /Users/majdjadalhaq/Desktop/Assignments-Cohort54/.dist/3-UsingAPIs/Week2/unit-tests/ex3-rollAnAce.test.js --colors --noStackTrace --json + FAIL .dist/3-UsingAPIs/Week2/unit-tests/ex3-rollAnAce.test.js + api-wk2-ex3-rollAnAce + ✅ should have all TODO comments removed (1 ms) + ❌ `rollDieUntil` should not contain unneeded console.log calls (2 ms) + ✅ should not include a recursive call (1 ms) + ✅ should use async/wait + ✅ should use try/catch + ✅ should resolve as soon as a die settles on an ACE (22 ms) + ✅ should reject with an Error when a die rolls off the table (11 ms) + + ● api-wk2-ex3-rollAnAce › `rollDieUntil` should not contain unneeded console.log calls + + expect(received).toBe(expected) // Object.is equality + + Expected: false + Received: true + +Test Suites: 1 failed, 1 total +Tests: 1 failed, 6 passed, 7 total +Snapshots: 0 total +Time: 0.467 s, estimated 1 s +Ran all test suites matching /\/Users\/majdjadalhaq\/Desktop\/Assignments-Cohort54\/.dist\/3-UsingAPIs\/Week2\/unit-tests\/ex3-rollAnAce.test.js/i. +No linting errors detected. +No spelling errors detected. diff --git a/3-UsingAPIs/Week2/test-reports/ex4-diceRace.report.txt b/3-UsingAPIs/Week2/test-reports/ex4-diceRace.report.txt new file mode 100644 index 000000000..9381c1c4a --- /dev/null +++ b/3-UsingAPIs/Week2/test-reports/ex4-diceRace.report.txt @@ -0,0 +1,19 @@ +*** Unit Test Error Report *** + + PASS .dist/3-UsingAPIs/Week2/unit-tests/ex4-diceRace.test.js + api-wk2-ex4-diceRace + ✅ should exist and be executable (2 ms) + ✅ should have all TODO comments removed + ✅ `rollDice` should not contain unneeded console.log calls (1 ms) + ✅ should use `dice.map()` + ✅ should use `Promise.race()` (1 ms) + ✅ should resolve as soon as a die settles successfully (10 ms) + ✅ should reject with an Error as soon as a die rolls off the table (31 ms) + +Test Suites: 1 passed, 1 total +Tests: 7 passed, 7 total +Snapshots: 0 total +Time: 0.603 s +Ran all test suites matching /\/Users\/majdjadalhaq\/Desktop\/Assignments-Cohort54\/.dist\/3-UsingAPIs\/Week2\/unit-tests\/ex4-diceRace.test.js/i. +No linting errors detected. +No spelling errors detected. diff --git a/3-UsingAPIs/Week2/test-reports/ex5-vscDebug.report.txt b/3-UsingAPIs/Week2/test-reports/ex5-vscDebug.report.txt new file mode 100644 index 000000000..d985f405c --- /dev/null +++ b/3-UsingAPIs/Week2/test-reports/ex5-vscDebug.report.txt @@ -0,0 +1,3 @@ +A unit test file was not provided for this exercise. +No linting errors detected. +No spelling errors detected. diff --git a/3-UsingAPIs/Week2/test-reports/ex6-browserDebug.report.txt b/3-UsingAPIs/Week2/test-reports/ex6-browserDebug.report.txt new file mode 100644 index 000000000..d985f405c --- /dev/null +++ b/3-UsingAPIs/Week2/test-reports/ex6-browserDebug.report.txt @@ -0,0 +1,3 @@ +A unit test file was not provided for this exercise. +No linting errors detected. +No spelling errors detected. From 8211434a6c3daf700608800c3cdbdee47c1347ac Mon Sep 17 00:00:00 2001 From: Majd Hamde Date: Mon, 27 Oct 2025 17:34:40 +0100 Subject: [PATCH 3/5] fixed --- .../assignment/ex1-programmerFun/index.js | 54 +++-------- .../Week2/assignment/ex2-pokemonApp/index.js | 97 +++++++++---------- .../Week2/assignment/ex2-pokemonApp/style.css | 65 ++++++------- 3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js | 17 +--- .../test-reports/ex1-programmerFun.report.txt | 41 ++------ .../test-reports/ex2-pokemonApp.report.txt | 10 +- .../test-reports/ex3-rollAnAce.report.txt | 26 ++--- 7 files changed, 117 insertions(+), 193 deletions(-) diff --git a/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js b/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js index caef9a9bd..13a422487 100644 --- a/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js +++ b/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js @@ -16,69 +16,43 @@ Full description at: https://github.com/HackYourFuture/Assignments/blob/main/3-U url with `.shx`. There is no server at the modified url, therefore this should result in a network (DNS) error. ------------------------------------------------------------------------------*/ -function requestData(url) { +async function requestData(url) { try { const response = await fetch(url); - // Handle HTTP errors if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } - // Parse the response as JSON const data = await response.json(); return data; } catch (error) { - // Network or parsing error - throw new Error(`Failed to fetch data: ${error.message}`); + throw error; } } - +// Function to render image function renderImage(data) { - // Create an image element const img = document.createElement('img'); - img.src = data.img; // The xkcd API returns `img` as the image URL - img.alt = data.alt; // Alt text is included in the API - img.style.maxWidth = '90%'; - img.style.display = 'block'; - img.style.margin = '40px auto'; - img.style.borderRadius = '12px'; - img.style.boxShadow = '0 4px 10px rgba(0,0,0,0.2)'; - - // Add title - const title = document.createElement('h2'); - title.textContent = data.title; - title.style.textAlign = 'center'; - title.style.fontFamily = 'sans-serif'; - - // Append both to the document body - document.body.appendChild(title); + img.src = data.img; document.body.appendChild(img); console.log(data); } - +// Function to render errors function renderError(error) { - // Create an error message element - const errorMessage = document.createElement('h1'); - errorMessage.textContent = `⚠️ ${error.message}`; - errorMessage.style.color = 'red'; - errorMessage.style.textAlign = 'center'; - errorMessage.style.marginTop = '50px'; - errorMessage.style.fontFamily = 'sans-serif'; - - document.body.appendChild(errorMessage); + const h1 = document.createElement('h1'); + h1.textContent = error.message || error; + document.body.appendChild(h1); console.log(error); } - +// Refactored main function to use async/await async function main() { - const url = 'https://xkcd.now.sh/?comic=latest'; - - try { - const data = await requestData(url); + try{ + const data = await requestData('https://xkcd.now.sh/?comic=latest'); renderImage(data); } catch (error) { renderError(error); } -} + } + -window.addEventListener('load', main); \ No newline at end of file +window.addEventListener('load', main); window.addEventListener('load', main); diff --git a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js index 562dd93d7..471246502 100644 --- a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js +++ b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js @@ -27,71 +27,70 @@ async function fetchData(url) { if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } - return await response.json(); + const data = await response.json(); + return data; } catch (error) { console.error('Fetch error:', error); + throw error; } } -async function fetchAndPopulatePokemons(selectEl) { - const data = await fetchData('https://pokeapi.co/api/v2/pokemon?limit=150'); - if (!data) return; - - data.results.forEach((pokemon) => { - const option = document.createElement('option'); - option.value = pokemon.url; // Store the API URL for each Pokémon - option.textContent = pokemon.name; - selectEl.appendChild(option); - }); -} - -async function fetchImage(pokemonUrl, imgEl) { - if (!pokemonUrl) { - imgEl.hidden = true; - return; +async function fetchAndPopulatePokemons() { + const url = 'https://pokeapi.co/api/v2/pokemon?limit=150'; + try { + const data = await fetchData(url); + const select = document.querySelector('select'); + select.innerHTML = ''; + + data.results.forEach((pokemon) => { + const option = document.createElement('option'); + option.value = pokemon.url; + option.textContent = pokemon.name; + select.appendChild(option); + }); + } catch (error) { + console.error('Error fetch pokemon list:', error); } +} - const data = await fetchData(pokemonUrl); - if (data && data.sprites && data.sprites.front_default) { - imgEl.src = data.sprites.front_default; - imgEl.alt = data.name; - imgEl.hidden = false; - } else { - imgEl.hidden = true; +async function fetchImage(pokemonUrl) { + try { + const data = await fetchData(pokemonUrl); + const img = document.querySelector('img'); + img.src = data.sprites.front_default; + img.alt = `${data.name} Pokemon sprite`; + } catch (error) { + console.error('Error fetch image:', error); } } - -async function main() { - // Create elements dynamically - const container = document.createElement('div'); - container.classList.add('container'); - - const title = document.createElement('h1'); - title.textContent = 'Pokémon Browser'; +// Main function to set up event listeners and initialize the app +function main() { + const button = document.createElement('button'); + button.id = 'get-button'; + button.textContent = 'get pokemon'; + document.body.appendChild(button); const select = document.createElement('select'); - const defaultOption = document.createElement('option'); - defaultOption.textContent = 'Select a Pokémon'; - defaultOption.value = ''; - select.appendChild(defaultOption); + select.id = 'pokemon-select'; + document.body.appendChild(select); - const img = document.createElement('img'); - img.id = 'pokemon-image'; - img.hidden = true; + const option = document.createElement('option'); + option.value = ''; + option.textContent = 'Select a Pokemon'; + select.appendChild(option); - // Add elements to the DOM - container.append(title, select, img); - document.body.appendChild(container); + const img = document.createElement('img'); + img.id = 'pokemon-img'; + img.alt = `select a pokemon to see it image `; + document.body.appendChild(img); - // Populate dropdown - await fetchAndPopulatePokemons(select); + button.addEventListener('click', fetchAndPopulatePokemons); - // Add event listener - select.addEventListener('change', async (event) => { - const pokemonUrl = event.target.value; - await fetchImage(pokemonUrl, img); + select.addEventListener('change', (event) => { + if (event.target.value) { + fetchImage(event.target.value); + } }); } -// Run when page is loaded window.addEventListener('load', main); \ No newline at end of file diff --git a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css index d8c55e11b..e79684620 100644 --- a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css +++ b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css @@ -1,49 +1,42 @@ /* add your styling here */ +/* Basic Reset and Setup */ body { font-family: Arial, sans-serif; - background: #f2f6fc; - color: #333; - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; + display: flex; /* Arrange items */ + flex-direction: column; /* Stack them vertically */ + align-items: center; /* Center them horizontally */ + margin-top: 50px; /* Add some space from the top */ + background-color: #f4f4f4; /* Light background color */ } -.container { - text-align: center; - background: white; - padding: 2rem 3rem; - border-radius: 10px; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); -} - -h1 { - color: #ef5350; - margin-bottom: 1rem; -} - -select { - padding: 0.5rem 1rem; - border-radius: 8px; +/* Style for the Button and Select */ +#get-button, #pokemon-select { + padding: 10px 15px; + margin-bottom: 20px; + border-radius: 5px; border: 1px solid #ccc; - font-size: 1rem; - background: #fafafa; + font-size: 16px; cursor: pointer; } -select:hover { - border-color: #ef5350; +#get-button { + background-color: #3498db; /* A nice blue color */ + color: white; + border-color: #2980b9; + transition: background-color 0.2s; } -img { - display: block; - width: 150px; - height: 150px; - margin: 1.5rem auto 0; - image-rendering: pixelated; - transition: transform 0.3s ease; +#get-button:hover { + background-color: #2980b9; } -img:hover { - transform: scale(1.1); -} +/* Style for the Image */ +#pokemon-img { + max-width: 200px; /* Keep the image at a reasonable size */ + height: auto; + padding: 15px; + border: 1px solid #ddd; + border-radius: 8px; + background-color: white; /* White background for the sprite */ + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); /* Subtle shadow */ +} \ No newline at end of file diff --git a/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js b/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js index cc9194231..642a003db 100644 --- a/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js +++ b/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js @@ -18,24 +18,13 @@ import { rollDie } from '../../helpers/pokerDiceRoller.js'; * @returns {Promise} */ export async function rollDieUntil(desiredValue) { - // Rewritten with async/await and a while loop (no recursion) let value; - - while (true) { - try { - value = await rollDie(); // wait for the result - console.log(`Rolled: ${value}`); - if (value === desiredValue) { - return value; // stop when ACE is rolled - } - } catch (err) { - // If the die rolls off the table (rejection), rethrow the error - throw err; - } + while (value !== desiredValue) { + value = await rollDie(); } + return value; } -// Refactored main() to async/await and try/catch async function main() { try { const result = await rollDieUntil('ACE'); diff --git a/3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt b/3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt index d4e13fb38..08e729c84 100644 --- a/3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt +++ b/3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt @@ -1,40 +1,17 @@ *** Unit Test Error Report *** -Command failed: npx jest /Users/majdjadalhaq/Desktop/Assignments-Cohort54/.dist/3-UsingAPIs/Week2/unit-tests/ex1-programmerFun.test.js --colors --noStackTrace --json - FAIL .dist/3-UsingAPIs/Week2/unit-tests/ex1-programmerFun.test.js + PASS .dist/3-UsingAPIs/Week2/unit-tests/ex1-programmerFun.test.js (11.715 s) api-wk2-ex1-programmerFun - ✅ HTML should be syntactically valid (145 ms) + ✅ HTML should be syntactically valid (149 ms) ✅ should have all TODO comments removed - ❌ should use `fetch()` (1 ms) - ❌ should use async/wait - ❌ should use try/catch + ✅ should use `fetch()` + ✅ should use async/wait + ✅ should use try/catch - ● api-wk2-ex1-programmerFun › should use `fetch()` - - expect(received).toBeDefined() - - Received: undefined - - ● api-wk2-ex1-programmerFun › should use async/wait - - expect(received).toBeDefined() - - Received: undefined - - ● api-wk2-ex1-programmerFun › should use try/catch - - expect(received).toBeDefined() - - Received: undefined - -Test Suites: 1 failed, 1 total -Tests: 3 failed, 2 passed, 5 total +Test Suites: 1 passed, 1 total +Tests: 5 passed, 5 total Snapshots: 0 total -Time: 2.984 s, estimated 4 s +Time: 12.311 s Ran all test suites matching /\/Users\/majdjadalhaq\/Desktop\/Assignments-Cohort54\/.dist\/3-UsingAPIs\/Week2\/unit-tests\/ex1-programmerFun.test.js/i. No linting errors detected. - - -*** Spell Checker Report *** - -3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js:40:30 - Unknown word (xkcd) +No spelling errors detected. diff --git a/3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.report.txt b/3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.report.txt index 29087d70f..5343b1677 100644 --- a/3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.report.txt +++ b/3-UsingAPIs/Week2/test-reports/ex2-pokemonApp.report.txt @@ -2,16 +2,16 @@ PASS .dist/3-UsingAPIs/Week2/unit-tests/ex2-pokemonApp.test.js api-wk2-ex2-pokemonApp - ✅ HTML should be syntactically valid (149 ms) - ✅ should have all TODO comments removed (1 ms) + ✅ HTML should be syntactically valid (146 ms) + ✅ should have all TODO comments removed ✅ should use `fetch()` - ✅ should use `await fetch()` (1 ms) - ✅ should use try/catch + ✅ should use `await fetch()` + ✅ should use try/catch (1 ms) Test Suites: 1 passed, 1 total Tests: 5 passed, 5 total Snapshots: 0 total -Time: 2.696 s, estimated 4 s +Time: 2.729 s Ran all test suites matching /\/Users\/majdjadalhaq\/Desktop\/Assignments-Cohort54\/.dist\/3-UsingAPIs\/Week2\/unit-tests\/ex2-pokemonApp.test.js/i. No linting errors detected. No spelling errors detected. diff --git a/3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt b/3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt index a1fb09a00..2c9277ebb 100644 --- a/3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt +++ b/3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt @@ -1,27 +1,19 @@ *** Unit Test Error Report *** -Command failed: npx jest /Users/majdjadalhaq/Desktop/Assignments-Cohort54/.dist/3-UsingAPIs/Week2/unit-tests/ex3-rollAnAce.test.js --colors --noStackTrace --json - FAIL .dist/3-UsingAPIs/Week2/unit-tests/ex3-rollAnAce.test.js + PASS .dist/3-UsingAPIs/Week2/unit-tests/ex3-rollAnAce.test.js api-wk2-ex3-rollAnAce - ✅ should have all TODO comments removed (1 ms) - ❌ `rollDieUntil` should not contain unneeded console.log calls (2 ms) + ✅ should have all TODO comments removed (2 ms) + ✅ `rollDieUntil` should not contain unneeded console.log calls ✅ should not include a recursive call (1 ms) ✅ should use async/wait - ✅ should use try/catch - ✅ should resolve as soon as a die settles on an ACE (22 ms) - ✅ should reject with an Error when a die rolls off the table (11 ms) + ✅ should use try/catch (1 ms) + ✅ should resolve as soon as a die settles on an ACE (20 ms) + ✅ should reject with an Error when a die rolls off the table (10 ms) - ● api-wk2-ex3-rollAnAce › `rollDieUntil` should not contain unneeded console.log calls - - expect(received).toBe(expected) // Object.is equality - - Expected: false - Received: true - -Test Suites: 1 failed, 1 total -Tests: 1 failed, 6 passed, 7 total +Test Suites: 1 passed, 1 total +Tests: 7 passed, 7 total Snapshots: 0 total -Time: 0.467 s, estimated 1 s +Time: 0.441 s, estimated 1 s Ran all test suites matching /\/Users\/majdjadalhaq\/Desktop\/Assignments-Cohort54\/.dist\/3-UsingAPIs\/Week2\/unit-tests\/ex3-rollAnAce.test.js/i. No linting errors detected. No spelling errors detected. From bd24b7205187cdf0dfaa9d337c71aa52eac61138 Mon Sep 17 00:00:00 2001 From: majdjadalhaq Date: Thu, 30 Oct 2025 20:53:14 +0100 Subject: [PATCH 4/5] finished ex5 --- 3-UsingAPIs/Week2/assignment/ex5-vscDebug.js | 28 +++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js b/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js index ed4fe9da5..b9ef0e295 100644 --- a/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js +++ b/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js @@ -10,8 +10,18 @@ async function getData(url) { function renderLaureate({ knownName, birth, death }) { console.log(`\nName: ${knownName.en}`); - console.log(`Birth: ${birth.date}, ${birth.place.locationString}`); - console.log(`Death: ${death.date}, ${death.place.locationString}`); + + if (birth) { + console.log(`Birth: ${birth?.date || 'Unknown'}, ${birth?.place?.locationString || 'Unknown'}`); + } else { + console.log(`Birth: Unknown`); + } + + if (death) { + console.log(`Death: ${death?.date || 'Unknown'}, ${death?.place?.locationString || 'Unknown'}`); + } else { + console.log(`Death: still alive`); + } } function renderLaureates(laureates) { @@ -20,14 +30,18 @@ function renderLaureates(laureates) { async function fetchAndRender() { try { - // Await the fetchData promise to get actual data - const laureates = await getData( - 'https://api.nobelprize.org/2.0/laureates?birthCountry=Netherlands&format=json&csvLang=en' + const data = await getData( + 'http://api.nobelprize.org/2.0/laureates?birthCountry=Netherlands&format=json&csvLang=en' ); - renderLaureates(laureates); + + if (Array.isArray(data.laureates)) { + renderLaureates(data.laureates); + } else { + console.error('No laureates found.'); + } } catch (err) { console.error(`Something went wrong: ${err.message}`); } } -fetchAndRender(); \ No newline at end of file +fetchAndRender(); From d65a3a6ea98fb7a951a3c965fa59b7529099855b Mon Sep 17 00:00:00 2001 From: Majd Hamde Date: Thu, 30 Oct 2025 21:04:30 +0100 Subject: [PATCH 5/5] finised ex5 --- 3-UsingAPIs/Week2/assignment/.vscode/settings.json | 3 +++ 3-UsingAPIs/Week2/assignment/ex5-vscDebug.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 3-UsingAPIs/Week2/assignment/.vscode/settings.json diff --git a/3-UsingAPIs/Week2/assignment/.vscode/settings.json b/3-UsingAPIs/Week2/assignment/.vscode/settings.json new file mode 100644 index 000000000..6f3a2913e --- /dev/null +++ b/3-UsingAPIs/Week2/assignment/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js b/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js index b9ef0e295..ab91f49fc 100644 --- a/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js +++ b/3-UsingAPIs/Week2/assignment/ex5-vscDebug.js @@ -44,4 +44,4 @@ async function fetchAndRender() { } } -fetchAndRender(); +fetchAndRender(); \ No newline at end of file