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/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/ex1-programmerFun/index.js b/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js index a99ca177b..13a422487 100644 --- a/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js +++ b/3-UsingAPIs/Week2/assignment/ex1-programmerFun/index.js @@ -16,29 +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) { - // TODO return a promise using `fetch()` -} +async function requestData(url) { + try { + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } +} +// Function to render image function renderImage(data) { - // TODO render the image to the DOM + const img = document.createElement('img'); + img.src = data.img; + document.body.appendChild(img); console.log(data); } - +// Function to render errors function renderError(error) { - // TODO render the error to the DOM + 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() { + try{ + const data = await requestData('https://xkcd.now.sh/?comic=latest'); + renderImage(data); + } catch (error) { + renderError(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); - }); -} -window.addEventListener('load', main); +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 262113997..471246502 100644 --- a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js +++ b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/index.js @@ -21,18 +21,76 @@ 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}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error('Fetch error:', error); + throw error; + } } -function fetchAndPopulatePokemons(/* TODO parameter(s) go here */) { - // TODO complete this function -} +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 = ''; -function fetchImage(/* TODO parameter(s) go here */) { - // TODO complete this function + 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); + } } +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); + } +} +// Main function to set up event listeners and initialize the app function main() { - // TODO complete this function + const button = document.createElement('button'); + button.id = 'get-button'; + button.textContent = 'get pokemon'; + document.body.appendChild(button); + + const select = document.createElement('select'); + select.id = 'pokemon-select'; + document.body.appendChild(select); + + const option = document.createElement('option'); + option.value = ''; + option.textContent = 'Select a Pokemon'; + select.appendChild(option); + + const img = document.createElement('img'); + img.id = 'pokemon-img'; + img.alt = `select a pokemon to see it image `; + document.body.appendChild(img); + + button.addEventListener('click', fetchAndPopulatePokemons); + + select.addEventListener('change', (event) => { + if (event.target.value) { + fetchImage(event.target.value); + } + }); } + +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..e79684620 100644 --- a/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css +++ b/3-UsingAPIs/Week2/assignment/ex2-pokemonApp/style.css @@ -1 +1,42 @@ /* add your styling here */ +/* Basic Reset and Setup */ +body { + font-family: Arial, sans-serif; + 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 */ +} + +/* 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: 16px; + cursor: pointer; +} + +#get-button { + background-color: #3498db; /* A nice blue color */ + color: white; + border-color: #2980b9; + transition: background-color 0.2s; +} + +#get-button:hover { + background-color: #2980b9; +} + +/* 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 861b31047..642a003db 100644 --- a/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js +++ b/3-UsingAPIs/Week2/assignment/ex3-rollAnAce.js @@ -17,24 +17,24 @@ 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); - } - return value; - }); +export async function rollDieUntil(desiredValue) { + let value; + while (value !== desiredValue) { + value = await rollDie(); + } + 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)); +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..ab91f49fc 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,13 +30,18 @@ function renderLaureates(laureates) { async function fetchAndRender() { try { - const laureates = getData( + 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(); +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 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..08e729c84 --- /dev/null +++ b/3-UsingAPIs/Week2/test-reports/ex1-programmerFun.report.txt @@ -0,0 +1,17 @@ +*** Unit Test Error Report *** + + PASS .dist/3-UsingAPIs/Week2/unit-tests/ex1-programmerFun.test.js (11.715 s) + api-wk2-ex1-programmerFun + ✅ HTML should be syntactically valid (149 ms) + ✅ should have all TODO comments removed + ✅ should use `fetch()` + ✅ should use async/wait + ✅ should use try/catch + +Test Suites: 1 passed, 1 total +Tests: 5 passed, 5 total +Snapshots: 0 total +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. +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 new file mode 100644 index 000000000..5343b1677 --- /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 (146 ms) + ✅ should have all TODO comments removed + ✅ should use `fetch()` + ✅ 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.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 new file mode 100644 index 000000000..2c9277ebb --- /dev/null +++ b/3-UsingAPIs/Week2/test-reports/ex3-rollAnAce.report.txt @@ -0,0 +1,19 @@ +*** Unit Test Error Report *** + + PASS .dist/3-UsingAPIs/Week2/unit-tests/ex3-rollAnAce.test.js + api-wk2-ex3-rollAnAce + ✅ 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 (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) + +Test Suites: 1 passed, 1 total +Tests: 7 passed, 7 total +Snapshots: 0 total +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. 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.