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. diff --git a/README.md b/README.md index d457d2828..5e5773557 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ We highly recommend that you go through this README in detail before starting to > > This will ensure that the line endings for text files are compatible with those used on MacOS and Linux-based computers. -1. Fork the `HackYourAssignment/Assignments-CohortXX` repository to your own GitHub account. +1. Fork the `HackYourAssignment/JavaScript-CohortXX` repository to your own GitHub account. 2. Clone the fork to your local computer. 3. Open the root folder of the repository in VSCode. 4. When invited to do so, please install the recommended VSCode extensions. @@ -24,7 +24,7 @@ We highly recommend that you go through this README in detail before starting to 10. Fix any reported issues and rerun the test. Repeat until all issues are fixed. 11. When all assignments are done, commit all changed files. This includes the modified exercises, the generated test summary (`TEST_SUMMARY.md`) and test reports (`EXERCISE_NAME.report.txt`). 12. Push the changes to your fork. -13. Create a pull request against the `main` branch of the `HackYourAssignment/Assignments-CohortXX` repository. For the title of your pull request use the same format as the branch name, e.g.: `YOUR_NAME-w2-JavaScript`. +13. Create a pull request against the `main` branch of the `HackYourAssignment/JavaScript-CohortXX` repository. For the title of your pull request use the same format as the branch name, e.g.: `YOUR_NAME-w2-JavaScript`. Repeat steps 6-13 for each week. For subsequent weeks the mandated branch names are: @@ -39,7 +39,7 @@ For more information how to hand in your weekly assignments please refer to the Throughout your [HYF journey](https://github.com/HackYourFuture/curriculum) you will be asked to do certain exercises. This repository contains all of these exercises for the JavaScript modules (JavaScript, Browsers, UsingAPIs). The module repositories will tell you how to hand in the assignment, the curriculum will indicate what week you will need to do. -> Note that a fork of this repository will be created on the [HackYourAssignment](https://github.com/HackYourAssignment) GitHub account specifically for your cohort. The name of the repository will have the format `Assignments-cohortXX` where `XX` is your cohort number, +> Note that a fork of this repository will be created on the [HackYourAssignment](https://github.com/HackYourAssignment) GitHub account specifically for your cohort. The name of the repository will have the format `JavaScript-cohortXX` where `XX` is your cohort number, ## Installation @@ -64,9 +64,9 @@ From the command line, while inside the `Assignments-cohortXX` folder, you can u code . ``` -> When working on your assignments it is strongly recommended to always open the `Assignments-cohortXX` folder in VSCode rather than one of its sub-folders. This gives VSCode and all its extensions the full view on the repository for the best overall developer experience. +> When working on your assignments it is strongly recommended to always open the `JavaScript-cohortXX` folder in VSCode rather than one of its sub-folders. This gives VSCode and all its extensions the full view on the repository for the best overall developer experience. > -> Note that the name of the folder opened in VSCode can always be found in the `EXPLORER` panel ( `ASSIGNMENTS_COHORT49` in the picture below): +> Note that the name of the folder opened in VSCode can always be found in the `EXPLORER` panel ( `ASSIGNMENT_COHORT49` in the picture below): > > ![folder-name](./assets/folder-name.png)