diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3756caaf..46c150e1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,8 +8,12 @@ For example, London Class 7 - Chris Owen - HTML/CSS - Week 1 +Please complete the details below this message + --> +**Volunteers: Are you marking this coursework?** _You can find a guide on how to mark this coursework in `HOW_TO_MARK.md` in the root of this repository_ + # Your Details - Your Name: @@ -20,3 +24,13 @@ London Class 7 - Chris Owen - HTML/CSS - Week 1 - Module: - Week: + +# Notes + +- What did you find easy? + +- What did you find hard? + +- What do you still not understand? + +- Any other notes? diff --git a/.github/workflows/close.yml b/.github/workflows/close.yml new file mode 100644 index 00000000..137dc57e --- /dev/null +++ b/.github/workflows/close.yml @@ -0,0 +1,17 @@ +name: "Close stale issues and PRs" +on: + workflow_dispatch: + schedule: + - cron: "30 1 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + stale-pr-message: "Your coursework submission has been closed because nobody has interacted with it in six weeks. You are welcome to re-open it to get more feedback." + days-before-stale: 42 + days-before-close: 0 + days-before-issue-stale: -1 + days-before-issue-close: -1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..49e0fc6b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules +/package-lock.json \ No newline at end of file diff --git a/.setup/jest.setup.js b/.setup/jest.setup.js new file mode 100644 index 00000000..e69de29b diff --git a/HOW_TO_MARK.md b/HOW_TO_MARK.md new file mode 100644 index 00000000..99bf0f0f --- /dev/null +++ b/HOW_TO_MARK.md @@ -0,0 +1,64 @@ + + +_This file is useful for Volunteers only_ + +## 1) Solutions + +### 1.1) Where to find solutions? + +You can find the solution to this coursework in a repository with the same name as this but with `-Solution` after the name. + +For example, for this repo: + +https://github.com/CodeYourFuture/JavaScript-Core-1-Coursework-Week1 + +The solutions would be found in: + +https://github.com/CodeYourFuture/JavaScript-Core-1-Coursework-Week1-Solution + +**If you do not have access to these repositories** then please contact your City Coordinator to get access to our Github Team. + +### 1.2) Using the Solutions Repo + +In these repositories you will find solutions to each weeks coursework. These solutions are example answers and will not be the exact solution that students give. You should use it to inform your feedback of the coursework. + +Additionally, you will find marking guides in these places + +- The `marking` folder - Used to store multiple guides on marking +- `marking.md` file - Used to store notes on common problems we're trying to address +- `solutions.md` - A file used by students for notes on best practice + +## 2) Before You Start + +### 2.1) Feedback Guide + +A guide for marking coursework can be found here. Please read it before you start. + +https://docs.codeyourfuture.io/teams/education/homework-feedback + +### 2.2) Marking Guide + +Here is a useful resources you can direct students to when you see them have common mistakes + +https://syllabus.codeyourfuture.io/guides/marking-guide + +This guide should be used when you see a student making a common mistake so instead of writing out a reply you can send them to the a good resource. + +For example, if the student is leaving in lots of comments out code you could write + +```txt +Great work so far! + +It's best if you remove code that you're not using, you can read more about this here +https://syllabus.codeyourfuture.io/guides/marking-guide#commented-out-code +``` + +### 3.3) Style Guide + +All code at CYF should follow this Style Guide + +https://syllabus.codeyourfuture.io/guides/code-style-guide/ diff --git a/README.md b/README.md index 38a45b15..7150b147 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,19 @@ The exercises are split into three folders: `exercises`, `mandatory` and `extra` The `extra` folder contains exercises that you can complete to challenge yourself, but are not required for the following lesson. +## Running the code/tests + +These files for the exercises are intended to be run as jest tests. + +- Once you have cloned the repository, run `npm install` once in the terminal to install jest (and any necessary dependencies). +- To run all exercises/tests in the mandatory folder, run `npm test` +- To run a single exercise/test (for example `mandatory/1-writer.js`), run `npm test -- --testPathPattern mandatory/1-writer.js` (Remember, you can use tab-completion to get files relative to the current directory, so m`Tab ↹`/1-`Tab ↹` will autocomplete get you the test file starting with 1-) +- Some of the exercises do not use jest. To run these individually, use node directly. `node mandatory/7-recipes.js`. These are: + - `7-recipes.js` + +For more information about tests, look here: + +https://syllabus.codeyourfuture.io/guides/intro-to-tests ## Solutions diff --git a/mandatory/1-writers.js b/mandatory/1-writers.js index d56af2fb..f815c156 100644 --- a/mandatory/1-writers.js +++ b/mandatory/1-writers.js @@ -32,7 +32,7 @@ let writers = [ firstName: "Zadie", lastName: "Smith", occupation: "writer", - age: 41, + age: 40, alive: true, }, { @@ -46,9 +46,16 @@ let writers = [ firstName: "Bell", lastName: "Hooks", occupation: "writer", - age: 64, + age: 63, alive: true, }, + { + firstName: "Yukiko", + lastName: "Motoya", + occupation: "writer", + age: 49, + alive: true, + } ]; /* @@ -59,6 +66,9 @@ Exercise 1: "Hi, my name is {firstName} {lastName}. I am {age} years old, and work as a {occupation}." */ +function logAllWriters() { + // write your code to log all writers here +}; /* Exercise 2: @@ -69,6 +79,10 @@ Exercise 2: "Writer {firstName} {lastName} died at {age} years old." */ +function logDeadWritersInTheirForties() { + // write your code here +} + /* Exercise 3: @@ -76,3 +90,40 @@ Exercise 3: "Hi, my name is {firstName} {lastName}. I am {age} years old." */ + +function logAliveWritersInTheirForties() { + // write your code here +} + +/* ======= TESTS - DO NOT MODIFY ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 1-writers.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) +*/ + +test("exercise 1", () => expectFunctionToLog(logAllWriters, [ + "Hi, my name is Virginia Woolf. I am 59 years old, and work as a writer.", + "Hi, my name is Zadie Smith. I am 40 years old, and work as a writer.", + "Hi, my name is Jane Austen. I am 41 years old, and work as a writer.", + "Hi, my name is Bell Hooks. I am 63 years old, and work as a writer.", + "Hi, my name is Yukiko Motoya. I am 49 years old, and work as a writer." +])); + +test("exercise 2", () => expectFunctionToLog(logDeadWritersInTheirForties, [ + "Writer Jane Austen died at 41 years old." +])); + +test("exercise 3", () => expectFunctionToLog(logAliveWritersInTheirForties, [ + "Hi, my name is Zadie Smith. I am 40 years old.", + "Hi, my name is Yukiko Motoya. I am 49 years old." +])); + +function expectFunctionToLog(f, values) { + const consoleLogSpy = jest.spyOn(console, 'log'); + f(); + expect(consoleLogSpy).toBeCalledTimes(values.length); + values.forEach((value, i) => { + expect(consoleLogSpy).nthCalledWith(i+1, value); + }); + consoleLogSpy.mockRestore(); +}; \ No newline at end of file diff --git a/mandatory/8-cheap-diner.js b/mandatory/10-cheap-diner.js similarity index 54% rename from mandatory/8-cheap-diner.js rename to mandatory/10-cheap-diner.js index a7032c5b..302dfbb9 100644 --- a/mandatory/8-cheap-diner.js +++ b/mandatory/10-cheap-diner.js @@ -20,7 +20,7 @@ chosenMeal(setOne) Should give the answer "Lobster" -If given an empty array, return null. +If given an empty array: let emptyArray = [] chosenMeal(emptyArray) @@ -30,66 +30,50 @@ Should give the answer "Nothing :(" **/ function chooseMeal(mealArray) { - // Write your code here } -/* -================================================== -====== TESTS - DO NOT MODIFY BELOW THIS LINE ===== -================================================== +/* ======= TESTS - DO MODIFY (!!!) ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 10-cheap-diner.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) */ -const util = require("util"); -function test(test_name, actual, expected) { - let status; - - if (actual === expected) { - status = `PASSED! You got the correct answer of ${util.inspect(expected)}`; - } else { - status = `FAILED: expected: ${util.inspect( - expected - )} but your function returned: ${util.inspect(actual)}`; - } - - console.log(`${test_name}: ${status}`); -} - -test( - "Test 1", - chooseMeal([ +test("Meal to select is last", () => { + expect(chooseMeal([ { name: "Dunkin' Donuts", price: 8.99 }, { name: "Captain D's", price: 13.99 }, { name: "Moe's Southwest Grill", price: 10.99 }, - ]), - "Moe's Southwest Grill" -); + ])).toEqual("Moe's Southwest Grill"); +}); + +test("Meal to select is first", () => { + expect(chooseMeal([ + { name: "Moe's Southwest Grill", price: 10.99 }, + { name: "Dunkin' Donuts", price: 8.99 }, + { name: "Captain D's", price: 13.99 }, + ])).toEqual("Moe's Southwest Grill"); +}); -test( - "Test 2", - chooseMeal([ +test("Meal to select is also most expensive", () => { + expect(chooseMeal([ { name: "Burger King", price: 8.99 }, { name: "Wingstop", price: 9.99 }, - ]), - "Wingstop" -); + ])).toEqual("Wingstop"); +}); -test("Test 3", chooseMeal([{ name: "Subway", price: 8.99 }]), "Subway"); +test("Only one meal to select", () => { + expect(chooseMeal([{ name: "Subway", price: 8.99 }])).toEqual("Subway"); +}); -test("Test 4", chooseMeal([]), "Nothing :("); +test("No meals to select", () => { + expect(chooseMeal([])).toEqual("Nothing :("); +}); -test( - "Test 5", - chooseMeal([ +test("Meal to select is second cheapest, not second most expensive", () => { + expect(chooseMeal([ { name: "Church's Chicken", price: 8.99 }, { name: "Smoothie King", price: 109.99 }, - { name: "Jamba Juice", price: 38.44 }, { name: "Jason's Deli", price: 22.77 }, - ]), - "Jason's Deli" -); - -test( - "Test 6", - chooseMeal([{ name: "Church's Chicken", price: 8.99 }]), - "Church's Chicken" -); + { name: "Jamba Juice", price: 38.44 }, + ])).toEqual("Jason's Deli"); +}); diff --git a/mandatory/11-choose-your-own-adventure.js b/mandatory/11-choose-your-own-adventure.js new file mode 100644 index 00000000..60e49d43 --- /dev/null +++ b/mandatory/11-choose-your-own-adventure.js @@ -0,0 +1,246 @@ +/* +Create a "Choose Your Own Adventure" game using an object. In these kind of +games, the player is in a room and can move to other rooms to the north, east, +south or west. + +To start the game, run this file with Node. Depending on your current directory, run one of: + + node 11-choose-your-own-adventure.js + node mandatory/11-choose-your-own-adventure.js + +To stop the game, press +Ctrl-C. + +To run the tests for the game, run this file with npm test + + npm test -- --testPathPattern 11-choose-your-own-adventure.js + +It has a currentRoom property to store which room the player is in. + +Give your object methods for: + +- Starting the game in the correct room when passed a room name parameter +- Moving the player to another room when passed a direction parameter + +The functionality for running a game has been provided for you. It first prompts +the player to enter the starting room. Then it will ask players to to type in a +direction (north/east/south/west) that they want to move in. + +An object containing one object per room has been provided for you. Take your +time to read it carefully. The rooms look something like this: ++-----------+-----------+ +| | | +| Hall | Classroom | +| | | ++-----------------------+ +| | +| Library | +| | ++-----------+ + +---------------------------------------------------------- + +Stretch goal: what happens if you try to move in a direction that the current +room doesn't allow? For example if you are in the Classroom and you try to move +east? If there is a bug in your code, try to fix it. + +To enable the tests for the stretch goals, remove the ".skip" on the appropriate tests below. +*/ + +let game = { + currentRoom: null, + + start: function (roomName) { + // This function is called with the name of the room that the player wants + // to start in. + // Finish the function so that the currentRoom property is set to the room + // object for the correct room. + // + // Hint: the only valid rooms are "hall", "classroom" and "library". + }, + + move: function (direction) { + // This function is called with the direction that the player wants to move. + // Finish the function so that the currentRoom property is updated with new + // room in the direction that the player wants to move in. + // + // Hint: the room objects have north/east/south/west methods which return + // a new room object that is in the relevant direction. + }, +}; + +/* +DO NOT EDIT BELOW THIS LINE +*/ + +let rooms = { + hall: { + name: "hall", + north: function () { + return null; + }, + east: function () { + return rooms.classroom; + }, + south: function () { + return rooms.library; + }, + west: function () { + return null; + }, + }, + classroom: { + name: "classroom", + north: function () { + return null; + }, + east: function () { + return null; + }, + south: function () { + return null; + }, + west: function () { + return rooms.hall; + }, + }, + library: { + name: "library", + north: function () { + return rooms.hall; + }, + east: function () { + return null; + }, + south: function () { + return null; + }, + west: function () { + return null; + }, + }, +}; + +/* +YOU ARE NOT EXPECTED TO UNDERSTAND THE CODE BELOW THIS +LINE! + +You only need to read it if you are interested in how it works. +*/ +const readline = require("readline"); +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +function start() { + rl.question( + "Which room would you like to start in? (hall/classroom/library) ", + function (room) { + game.start(room); + console.log("\n---------------------\n"); + play("start"); + } + ); +} + +function play(method) { + if (!game.currentRoom?.name) { + throw new Error( + `It looks like the game isn't quite right! Make sure your \`${method}\` method is correct` + ); + } + console.log(`You are in the ${game.currentRoom.name}.\n`); + rl.question( + "Which direction would you like to move? (north/east/south/west) ", + function (direction) { + game.move(direction); + console.log("\n---------------------\n"); + play("move"); + } + ); +} + +if (global["test"] == undefined) { + // running in node -> start game + start(); + test = () => {}; + beforeEach = () => {}; + test.skip = () => {}; +} else { + // running in jest + // don't start game, close the readline handle + rl.close(); +} + +// if we reach here, we are running in jest -> run tests + +/* ======= TESTS - ONLY MODIFY TO ENABLE TESTS FOR STRETCH GOALS ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 11-choose-your-own-adventure.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) +*/ + +beforeEach(() => { + // reset the game object + game.currentRoom = null; +}); + +test("start in hall", () => { + game.start("hall"); + expect(game.currentRoom.name).toEqual("hall"); +}); + +test("start in library", () => { + game.start("library"); + expect(game.currentRoom.name).toEqual("library"); +}); + +test("start in classroom", () => { + game.start("classroom"); + expect(game.currentRoom.name).toEqual("classroom"); +}); + +// remove ".skip" if your code correctly handles a non existent room (by setting currentRoom to null/doing nothing) +test.skip("start in non-existent place", () => { + game.start("does not exist"); + expect(game.currentRoom).toEqual(null); +}); + +test("start in hall and go south", () => { + game.currentRoom = rooms.hall; + game.move("south"); + expect(game.currentRoom.name).toEqual("library"); +}); + +test("start in library and go north", () => { + game.currentRoom = rooms.library; + game.move("north"); + expect(game.currentRoom.name).toEqual("hall"); +}); + +test("start in hall and go east", () => { + game.currentRoom = rooms.hall; + game.move("east"); + expect(game.currentRoom.name).toEqual("classroom"); +}); + +test("start in classroom and go west", () => { + game.currentRoom = rooms.classroom; + game.move("west"); + expect(game.currentRoom.name).toEqual("hall"); +}); + +// remove ".skip" if your code handles trying to go in a direction with no room (by staying in the same room) +test.skip("start in hall and go north (to non-existent room) -> stay in same room", () => { + game.currentRoom = rooms.hall; + game.move("north"); + expect(game.currentRoom.name).toEqual("hall"); +}); + +// remove ".skip" if your code handles trying to go in a direction that doesn't exist (by staying in the same room) +test.skip("start in hall and go backwards (non-existent direction) -> stay in same room", () => { + game.currentRoom = rooms.hall; + game.move("backwards"); + expect(game.currentRoom.name).toEqual("hall"); +}); diff --git a/mandatory/2-eligible-students.js b/mandatory/2-eligible-students.js new file mode 100644 index 00000000..cb472063 --- /dev/null +++ b/mandatory/2-eligible-students.js @@ -0,0 +1,44 @@ +/* + This exercise may look familiar! + In JS1 week 3 we had an exercise called eligible-students, + where we stored each student's name and score in an array. + + This is the same exercise again, but we've stored each student's + information in an object instead of an array. + + After you complete the exercise, compare your solution to your previous one. + Can you see how using objects leads to more clear code? + + ------------------------------------------------------------------------- + + Only students who have attended enough classes are eligible to sit an exam. + + Create a function which: + - Accepts an array which contains all the students' names and their attendance counts + (see tests to confirm how this data will be structured) + - Returns an array containing only the names of the who have attended AT LEAST 8 classes + */ + +function eligibleStudents(attendances) { + +} + +/* ======= TESTS - DO NOT MODIFY ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 2-eligible-students.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) +*/ + +const attendances = [ + {name: "Ahmed", attendance: 8}, + {name: "Clement", attendance: 10}, + {name: "Elamin", attendance: 6}, + {name: "Adam", attendance: 7}, + {name: "Tayoa", attendance: 11}, + {name: "Nina", attendance: 10}, +]; + +test("eligibleStudents function works", () => { + expect(eligibleStudents(attendances)).toEqual(["Ahmed", "Clement", "Tayoa", "Nina"]); +}); + \ No newline at end of file diff --git a/mandatory/2-water-bottle.js b/mandatory/2-water-bottle.js deleted file mode 100644 index d8207867..00000000 --- a/mandatory/2-water-bottle.js +++ /dev/null @@ -1,243 +0,0 @@ -/* -Create an object that acts a water bottle. - -It will need a volume property to store how full or empty the bottle is. - -Volume will be 100 when bottle is full and 0 when empty. - -Give your water bottle methods for - - filling it up - - pouring 10 units of water into it - Note: You cannot exceed the bottle capacity. - - drinking 10 units from it - Note: You cannot drink more than its actual contents. - - and telling if the bottle is full - - and telling if the bottle is empty - -We made a start on this here by giving you the skeleton of our object. - -You have to implement the missing features according to the specification. -*/ - -// Here is your starting point: -let bottle = { - volume: 0, - fillUp: function () { - // calling this function should completely fill your bottle (volume = 100); - }, - pour: function () { - // calling this function should increase your bottle volume by 10 units; - }, - drink: function () { - // calling this function should decrease your bottle volume by 10 units; - }, - isFull: function () { - // this function should return true if your bottle is full; - }, - isEmpty: function () { - // this function should return true if your bottle is empty; - }, -}; - -/* -TIP: - Remember that for changing properties on the current object inside one of its - methods you can refer to it by its variable name: `bottle` or by using the keyword `this`. -*/ - -/* -Extra question: - Why do you think it is preferred to use `this` inside the object rather than its variable name, in our case `bottle`? - Leave your answer below: -*/ - -// Write you answer to the question here - -/* -Once you have completed your object run the following -and see if your answer matches the expected result at the bottom :) -*/ - -// ONLY READ AND DO NOT MODIFY BELOW - -// ACTIONS -let failed = false; -bottle.fillUp(); - -// CHECKS -if (bottle.isFull()) { - console.log(`That's correct! Bottle is full.`); -} else { - failed = true; - console.warn(`Not quite right! Bottle should be full but it is not.`); -} - -if (!bottle.isEmpty()) { - console.log(`That's correct! Bottle isn't empty.`); -} else { - failed = true; - console.warn( - `Not quite right! Bottle should not be empty but it is already.` - ); -} - -// ACTIONS -bottle.pour(); - -// CHECKS -if (bottle.volume === 100) { - console.log( - `That's correct. Bottle is already full water volume cannot go beyond.` - ); -} else { - failed = true; - console.warn( - `Whoops!!! Looks like you've changed your bottle to a bigger one, it went beyond its maximum capacity up to ${bottle.volume} unit.` - ); -} - -if (bottle.isFull()) { - console.log(`That's correct! Bottle is still full.`); -} else { - failed = true; - console.warn(`Not quite right! Bottle should be still full but is not.`); -} - -// ACTIONS -bottle.drink(); -bottle.drink(); -bottle.drink(); - -// CHECKS -if (bottle.volume === 70) { - console.log(`That's correct! Water volume is ${bottle.volume}.`); -} else { - failed = true; - console.warn( - `Not quite right! Water volume should be 70 unit instead of ${bottle.volume}.` - ); -} - -// ACTIONS -bottle.drink(); -bottle.drink(); -bottle.drink(); - -// CHECKS -if (!bottle.isFull()) { - console.log(`That's correct! Bottle isn't full.`); -} else { - failed = true; - console.warn(`Not quite right! Bottle should not be full but it is.`); -} - -if (!bottle.isEmpty()) { - console.log(`That's correct! Bottle isn't empty yet.`); -} else { - failed = true; - - console.warn( - `Not quite right! Bottle should not be still empty but it is already.` - ); -} - -// ACTIONS -bottle.drink(); -bottle.drink(); -bottle.drink(); -bottle.drink(); - -// CHECKS -if (bottle.isEmpty()) { - console.log(`That's correct! Bottle is finally emptied.`); -} else { - failed = true; - - console.warn( - `Not quite right. Bottle should be already empty but it is not.` - ); -} - -if (bottle.volume === 0) { - console.log(`That's correct! Empty bottle volume is repesented as zero.`); -} else { - failed = true; - - console.warn( - `Not quite right. Volume should be zero instead of ${bottle.volume}.` - ); -} - -// ACTIONS -bottle.drink(); - -// CHECKS -if (bottle.volume === 0) { - console.log(`That's correct! Water volume cannot go below zero.`); -} else { - failed = true; - - console.warn( - `Whoops!!! Looks like your water volume went negative. Your water volume is ${bottle.volume} unit.` - ); -} - -if (bottle.isEmpty()) { - console.log(`That's correct! Bottle is still empty.`); -} else { - console.warn(`Not quite right. Bottle should be empty but it is not.`); -} - -// ACTIONS -bottle.pour(); - -// CHECKS -if (bottle.volume === 10) { - console.log(`That's correct! Water volume is ${bottle.volume}.`); -} else { - failed = true; - - console.warn( - `Not quite right! Water volume should be 10 unit instead of ${bottle.volume}.` - ); -} - -if (!bottle.isFull()) { - console.log(`That's correct! Bottle isn't yet full.`); -} else { - failed = true; - - console.warn(`Not quite right! Bottle should not be full but it is.`); -} - -if (!bottle.isEmpty()) { - console.log(`That's correct! Bottle isn't empty anymore.`); -} else { - failed = true; - - console.warn( - `Not quite right! Bottle should not be empty again but it is still.` - ); -} - -// ACTIONS -bottle.drink(); - -// CHECKS -if (bottle.isEmpty()) { - console.log(`That's correct! Bottle is emptied once more.`); -} else { - failed = true; - - console.warn(`Not quite right. Bottle should be empty again but it is not.`); -} - -console.log(""); - -if (failed) { - console.log( - "RESULT: Incorrect. Please read what went wrong above and try again" - ); -} else { - console.log("RESULT: Correct! Congratulations!"); -} diff --git a/mandatory/3-journey-planner.js b/mandatory/3-journey-planner.js new file mode 100644 index 00000000..2a96b8a2 --- /dev/null +++ b/mandatory/3-journey-planner.js @@ -0,0 +1,66 @@ +/* + This exercise was also in JS1 week 3, but you didn't know about objects yet. + It's more clear with objects than with arrays! + Feel free to look at your solution to that one to help you out - you already did this once! + + ----------------------------------------------------------------------- + + Write a function journeyPlanner that: + + - Accepts two paramters: + 1) An object where the keys are locations and the values are arrays of the transportation modes you can use to get there. + e.g. + { + Angel: ["tube", "bus"], + "London Bridge": ["tube", "river boat"], + } + + 2) A string containing a transport mode + e.g. "bus" + + - Returns an array of where I can go if I only want to use a specific mode of transport. + + NOTE: Only the location names should be returned, as strings. + + When you finish the exercise, think about how this solution is different to your last solution. + What's better about each approach? +*/ + +function journeyPlanner(locations, transportMode) { + +} + +/* ======= TESTS - DO NOT MODIFY ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 3-journey-planner.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) +*/ +const londonLocations = { + "Angel": ["tube", "bus"], + "London Bridge": ["tube", "river boat"], + "Tower Bridge": ["tube", "bus"], + "Greenwich": ["bus", "river boat"], +}; + +test("journeyPlanner function works - case 1", () => { + expect(journeyPlanner(londonLocations, "river boat")).toEqual([ + "London Bridge", + "Greenwich", + ]); +}); + +test("journeyPlanner function works - case 2", () => { + expect(journeyPlanner(londonLocations, "bus")).toEqual([ + "Angel", + "Tower Bridge", + "Greenwich", + ]); +}); + +test("journeyPlanner function works - case 3", () => { + expect(journeyPlanner(londonLocations, "tube")).toEqual([ + "Angel", + "London Bridge", + "Tower Bridge", + ]) +}); \ No newline at end of file diff --git a/mandatory/4-water-bottle.js b/mandatory/4-water-bottle.js new file mode 100644 index 00000000..4e914d11 --- /dev/null +++ b/mandatory/4-water-bottle.js @@ -0,0 +1,152 @@ +/* +Create an object that acts a water bottle. + +It will need a volume property to store how full or empty the bottle is. + +Volume will be 100 when bottle is full and 0 when empty. + +Give your water bottle methods for + - filling it up + - pouring 10 units of water into it + Note: You cannot exceed the bottle capacity. + - drinking 10 units from it + Note: You cannot drink more than its actual contents. + - and telling if the bottle is full + - and telling if the bottle is empty + +We made a start on this here by giving you the skeleton of our object. + +You have to implement the missing features according to the specification. +*/ + +// Here is your starting point: +let bottle = { + volume: 0, + fillUp: function () { + // calling this function should completely fill your bottle (volume = 100); + }, + pour: function () { + // calling this function should increase your bottle volume by 10 units; + }, + drink: function () { + // calling this function should decrease your bottle volume by 10 units; + }, + isFull: function () { + // this function should return true if your bottle is full; + }, + isEmpty: function () { + // this function should return true if your bottle is empty; + }, +}; + +/* +TIP: + Remember that for changing properties on the current object inside one of its + methods you can refer to it by its variable name: `bottle` or by using the keyword `this`. +*/ + +/* +Extra question: + Why do you think it is preferred to use `this` inside the object rather than its variable name, in our case `bottle`? + Leave your answer below: +*/ + +// Write you answer to the question here + +/* +Once you have completed your object run the following +and see if your answer matches the expected result at the bottom :) +*/ + +/* ======= TESTS - DO NOT MODIFY ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 4-water-bottle.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) +*/ + +test("When filled up, bottle is full", () => { + bottle.volume = 0; + bottle.fillUp(); + expect(bottle.isFull()).toEqual(true); +}); + +test("When filled up, bottle is not empty", () => { + bottle.volume = 0; + bottle.fillUp(); + expect(bottle.isEmpty()).toEqual(false); +}); + +test("When emptied, bottle is not full", () => { + bottle.volume = 0; + expect(bottle.isFull()).toEqual(false); +}); + +test("When emptied, bottle is empty", () => { + bottle.volume = 0; + expect(bottle.isEmpty()).toEqual(true); +}); + +test("When partially filled, bottle is not empty", () => { + bottle.volume = 40; // arbitrary amount + expect(bottle.isEmpty()).toEqual(false); +}); + +test("When partially filled, bottle is not full", () => { + bottle.volume = 40; // arbitrary amount + expect(bottle.isFull()).toEqual(false); +}); + +test("Given a full bottle, when pour is called, then the volume does not increase", () => { + bottle.volume = 100; + bottle.pour(); + expect(bottle.volume).toEqual(100); +}); + +test("Multiple calls to drink reduce the volume correctly", () => { + bottle.volume = 100; + // arbitrary number of calls to drink + bottle.drink(); + bottle.drink(); + bottle.drink(); + expect(bottle.volume).toEqual(70); +}); + +test("Given a full bottle, when drink has been called, then it is neither full nor empty", () => { + bottle.volume = 100; + // arbitrary number of calls to drink + bottle.drink(); + bottle.drink(); + bottle.drink(); + bottle.drink(); + bottle.drink(); + bottle.drink(); + expect(bottle.isEmpty()).toEqual(false); + expect(bottle.isFull()).toEqual(false); +}); + +test("Given a full bottle, when drink called 10 times, then bottle is empty", () => { + bottle.volume = 100; + for (var i = 0; i < 10; i++) { + bottle.drink(); + } + expect(bottle.isEmpty()).toEqual(true); +}); + +test("Given an empty bottle, when drink is called, then the volume does not decrease", () => { + bottle.volume = 0; + bottle.drink(); + expect(bottle.volume).toEqual(0); +}); + +test("Given an empty bottle, when pour is called, then the volume increases", () => { + bottle.volume = 0; + bottle.pour(); + expect(bottle.volume).toEqual(10); +}); + +test("Given an empty bottle, calling pour then drink, then the bottle is empty", () => { + bottle.volume = 0; + bottle.pour(); + bottle.drink(); + expect(bottle.volume).toEqual(0); +}); \ No newline at end of file diff --git a/mandatory/3-groceries.js b/mandatory/5-groceries.js similarity index 53% rename from mandatory/3-groceries.js rename to mandatory/5-groceries.js index 35bf3f65..2855fdcb 100644 --- a/mandatory/3-groceries.js +++ b/mandatory/5-groceries.js @@ -4,7 +4,7 @@ As you you can have an Array of Objects, you can also store Arrays in Objects. In this exercise, you'll practice: - How to loop through the properties (keys) of an Object and read its values. - How to access an Array stored inside an Object. - - How to access a specific property of an array and set it. + - How to access a specific property of an object and set it. You're going shopping, and you need a shopping list. You've already created your weekly meal plan that contains the missing ingredients for your menus. It is stored in the "weeklyMealPlan" object. @@ -26,7 +26,6 @@ let weeklyMealPlan = { Exercise 1: Loop through the weekly meal plan object to gather weekly ingredients into the weeklyGroceriesToBuy array. The weeklyGroceriesToBuy array shouldn't contain any repeating items. - Then use console.log() to print out the list. */ // Gather all week item names into this array let weeklyGroceriesToBuy = []; @@ -34,7 +33,6 @@ let weeklyGroceriesToBuy = []; /* Exercise 2: Loop through your list again, but now only collect the weekend items into the weekendGroceriesToBuy array. - Then use console.log() to print out the list. */ // Gather weekend item names into this array let weekendGroceriesToBuy = []; @@ -44,7 +42,6 @@ Exercise 3: Loop through your weekly meal plan: - count how many ingredients you should buy each day - and update the corresponding properties of numberOfItemsPerWeek object. - Finally use console.log() to print out the Object. */ // Gather daily item counts into this object let numberOfItemsPerWeek = { @@ -56,3 +53,46 @@ let numberOfItemsPerWeek = { saturday: 0, sunday: 0, }; + +/* ======= TESTS - DO NOT MODIFY ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 5-groceries.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) +*/ + +test("Exercise 1 - Weekly groceries to buy contains correct items", () => { + const expectedWeeklyGroceriesToBuy = [ + 'Cheese', 'Eggs', + 'Tomato', 'Paprika', + 'Leek', 'Wrap', + 'Tuna', 'Canned beans', + 'Carrot', 'Aubergine', + 'Orange Juice', 'Apple', + 'Ananas', 'Black tea', + 'Lamb', 'Salt', + 'Bulgur', 'Potato', + 'Rice milk', 'Blueberries', + 'Porridge', 'Banana', + 'Cinnamon', 'Olive oil', + 'Salmon', 'Asparagus' + ]; + expect(weeklyGroceriesToBuy).toIncludeSameMembers(expectedWeeklyGroceriesToBuy); +}); + +test("Exercise 2 - Weekend groceries to buy contains correct items", () => { + const expectedWeekendGroceriesToBuy = ["Olive oil", "Potato", "Salmon", "Asparagus"]; + expect(weekendGroceriesToBuy).toIncludeSameMembers(expectedWeekendGroceriesToBuy); +}); + +test("Exercise 3 - Numer of items per week contains the correct counts", () => { + const expectedNumberOfItemsPerWeek = { + monday: 5, + tuesday: 6, + wednesday: 4, + thursday: 4, + friday: 5, + saturday: 4, + sunday: 0, + }; + expect(numberOfItemsPerWeek).toEqual(expectedNumberOfItemsPerWeek); +}); \ No newline at end of file diff --git a/mandatory/4-people-I-know.js b/mandatory/6-people-I-know.js similarity index 82% rename from mandatory/4-people-I-know.js rename to mandatory/6-people-I-know.js index 2f223584..39cea6d0 100644 --- a/mandatory/4-people-I-know.js +++ b/mandatory/6-people-I-know.js @@ -1,20 +1,16 @@ /* - Below you will find a list of people that I know. - */ /* - 1) Reading Before you start, you should read through the object below so that you understand the structure of it. When you've finished. Continue to the exercises below. - */ -let people = [ +const friends = [ { age: 39, company: "PEARLESSA", @@ -23,7 +19,7 @@ let people = [ last: "Hardy", }, email: "vilma.hardy@pearlessa.info", - friends: [ + colleagues: [ { name: "Sally Nielsen", age: 37, @@ -59,7 +55,7 @@ let people = [ last: "Gentry", }, email: "aisha.gentry@plutorque.net", - friends: [ + colleagues: [ { name: "Latonya Hogan", age: 67, @@ -95,7 +91,7 @@ let people = [ last: "Whitfield", }, email: "mitchell.whitfield@lingoage.io", - friends: [ + colleagues: [ { name: "Head Fitzpatrick", age: 31, @@ -131,7 +127,7 @@ let people = [ last: "Kirk", }, email: "hooper.kirk@melbacor.me", - friends: [ + colleagues: [ { name: "Clarissa Kirby", age: 37, @@ -167,7 +163,7 @@ let people = [ last: "Quinn", }, email: "sutton.quinn@cipromox.ca", - friends: [ + colleagues: [ { name: "Melanie Patterson", age: 40, @@ -203,7 +199,7 @@ let people = [ last: "Knox", }, email: "haley.knox@envire.tv", - friends: [ + colleagues: [ { name: "Nannie Reyes", age: 47, @@ -239,7 +235,7 @@ let people = [ last: "Jacobson", }, email: "brittany.jacobson@prosely.name", - friends: [ + colleagues: [ { name: "Glass Weaver", age: 64, @@ -275,7 +271,7 @@ let people = [ last: "Harrison", }, email: "jana.harrison@capscreen.co.uk", - friends: [ + colleagues: [ { name: "Stacie Villarreal", age: 34, @@ -311,7 +307,7 @@ let people = [ last: "Hall", }, email: "gloria.hall@powernet.com", - friends: [ + colleagues: [ { name: "Lourdes Barr", age: 65, @@ -347,7 +343,7 @@ let people = [ last: "Livingston", }, email: "clay.livingston@powernet.com", - friends: [ + colleagues: [ { name: "Stacie Villarreal", age: 34, @@ -380,7 +376,7 @@ let people = [ /* 2) Aged 35 or Older -In the above object you can see my friends and the friends of my friends. +In the above object you can see my friends and the colleagues of my friends. First, I want you to find all of my friends who are 35 or older. @@ -391,7 +387,7 @@ let thirtyFiveOrOlder = []; /* 3) Find the email address -Next, I want you to find all of the people who work for "POWERNET" and then store their emails in the array below +Next, I want you to find all of my friends who work for "POWERNET" and then store their emails in the array below */ @@ -399,23 +395,22 @@ let powerNetEmails = []; /* -3) Friends with "Stacie Villarreal" +4) colleagues with "Stacie Villarreal" -Next, I want you to find all of my friends who are friends with Stacie Villarreal. +Next, I want you to find all of my friends who are colleagues of Stacie Villarreal. -You can see who people's friends are by seeing the "friends" array in each of my friends objects. +You can see who people's colleagues are by seeing the "colleagues" array in each of my friends objects. -This time, I only want the full names of the people are who friends with her. +This time, I only want the full names (" ") of my friends who are colleagues of hers. */ -let friendsWithStacie = []; - +let friendsWhoAreColleaguesOfStacie = []; /* -4) Find "Multi-tasking" friends +5) Find "Multi-tasking" colleagues -Next, I want you to find all of my friends of friends who are good at "Multi-tasking" +Next, I want you to find all of the colleagues of my friends who are good at "Multi-tasking" You can tell if they are good at "Multi-tasking" because they will have it listed in their skills @@ -423,46 +418,41 @@ This time, I only want the full names of the people who can multitask */ -let friendsWhoCanMultitask = []; +let colleaguesWhoCanMultitask = []; -/* -================================================== -====== TESTS - DO NOT MODIFY BELOW THIS LINE ===== -================================================== +/* ======= TESTS - DO NOT MODIFY ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 6-people-I-know.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) */ -const util = require("util"); - -function test(test_name, actual, expected) { - let status; - - if (actual.toString() === expected.toString()) { - status = "PASSED"; - } else { - status = `FAILED: expected: ${util.inspect( - expected - )} but your function returned: ${util.inspect(actual)}`; - } - - console.log(`${test_name}: ${status}`); -} - -test("Friends are over 35", thirtyFiveOrOlder.length, 5); - -test("Friends with Stacie Villarreal", friendsWithStacie, [ - "Clay Livingston", - "Jana Harrison", - "Haley Knox", -]); - -test("Powernet email addresses", powerNetEmails, [ - "clay.livingston@powernet.com", - "gloria.hall@powernet.com", -]); -test("Friends who can multitask", friendsWhoCanMultitask, [ +test("2 - friends that are over 35", () => { + expect(thirtyFiveOrOlder.map(({name}) => name.first)).toIncludeSameMembers([ + "Vilma", "Aisha", "Mitchell", "Sutton", "Jana" + ]); +}); + +test("3 - Powernet email addresses", () => { + expect(powerNetEmails).toIncludeSameMembers([ + "clay.livingston@powernet.com", + "gloria.hall@powernet.com", + ]); +}); + +test("4 - friends with Stacie Villarreal as a colleague", () => { + expect(friendsWhoAreColleaguesOfStacie).toIncludeSameMembers([ + "Clay Livingston", + "Jana Harrison", + "Haley Knox", + ]); +}); + +test("5 - colleagues who can multitask", () => { + expect(colleaguesWhoCanMultitask).toIncludeSameMembers([ "Rush May", "Gena Good", "Cunningham Shelton", "Castro Castaneda", "Luz Newton", -]); + ]); +}); diff --git a/mandatory/6-reading-list.js b/mandatory/6-reading-list.js deleted file mode 100644 index b0b3477e..00000000 --- a/mandatory/6-reading-list.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - -The Reading List -Keep track of which books you've read and which books you want to read! - -===== -Exercise 1 -===== - -Loop through the array of books. For each book, log the book title and book author like so: - -"The Hobbit by J.R.R. Tolkien" - -You should write and log at least 5 books - -===== -Exercise 2 -===== -Now use an if/else statement to change the output depending on whether you have read it yet or not. - -If you've read it, log a string like 'You've already read "The Hobbit" by J.R.R. Tolkien', -and if not, log a string like 'You still need to read "The Lord of the Rings" by J.R.R. Tolkien.' - -**/ - -let books = []; diff --git a/mandatory/5-recipes.js b/mandatory/7-recipes.js similarity index 81% rename from mandatory/5-recipes.js rename to mandatory/7-recipes.js index 4c07f439..da790642 100644 --- a/mandatory/5-recipes.js +++ b/mandatory/7-recipes.js @@ -7,9 +7,9 @@ Create an object to hold information on your favorite recipe. It should have properties for -- Title (a string), -- Servings (a number), and -- Ingredients (an array of strings) +- title (a string), +- servings (a number), and +- ingredients (an array of strings) On separate lines (one console.log statement for each), log the recipe information so it looks like: @@ -25,3 +25,4 @@ You should write and log at least 5 recipes **/ let recipes = {}; + diff --git a/mandatory/8-reading-list.js b/mandatory/8-reading-list.js new file mode 100644 index 00000000..6d22df00 --- /dev/null +++ b/mandatory/8-reading-list.js @@ -0,0 +1,97 @@ +/** + +The Reading List +Keep track of which books you've read and which books you want to read! + +===== +Exercise 1 +===== + +Create an array of objects, where each object describes a book and has properties for: +- The title (a string) +- Author (a string) +- and alreadyRead (a boolean indicating if you read it yet) + +Write a funciton that loops through the array of books. For each book, log the book title and book author like so: + +"The Hobbit by J.R.R. Tolkien" + +You should write and log at least 5 books. + +You should modify the tests so that they contain the values that correspond to your books. +In this style of testing it is typical to write out as strings exactly what you expect your output to be, +without using any variables or any logic like loops, template strings or if statements. + +*/ + +const books = []; + +// exercise 1 +function logBooks() { +} + + +/* + +===== +Exercise 2 +===== +Now modify the function, using an if/else statement to change the output depending on whether you have read it yet or not. + +If you've read it, log a string like 'You've already read "The Hobbit" by J.R.R. Tolkien', +and if not, log a string like 'You still need to read "The Lord of the Rings" by J.R.R. Tolkien.' + +You will need to modify the tests to check the correct output. If you have already learnt about red-green refactoring, +remember to practice: +- first change the test to the value that should be output, +- run the test to check that your test goes red +- now change your code to make the test pass + +As an example for this exercise, you might do the following steps +- Modify the tests so that they all say 'You've already read by ' +- Run the test (they will all fail) +- Modify your code so that it logs 'You've already read by ' +- Run the test (they will all pass again) +- Modify your code making all the books alreadyRead:false and adding the if/else so that it logs 'You still need to read by ' +- Run the test (they will all fail) +- Modify the tests to contain the correct list of statements (whether you've read the book or not) +- Run the test (the test will fail but there will be some successful results) +- Modify the books so that they have the correct alreadyRead value +- All tests should turn green!! + +**/ + +/* ======= TESTS - DO MODIFY (!!!) ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 8-reading-list.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) +*/ + +test("books are logged", function() { + expectLogBooksToLog([ + "The Hobbit by J.R.R. Tolkien", + "The Map of Salt and Stars by Jennifer Zeynab Joukhadar", + "Dietland by Sarai Walker", + "A Place for Us by Fatima Farheen Mirza", + "The House of Impossible Beauties by Joseph Cassara" + ]); +}); + +/* ======= TESTS - DO NOT MODIFY ===== */ +/* +* Assert that when the function logBooks is called, the values in the expectedValues array are logged in order via console.log. +* +* - If the number of calls to console.log does not match the number of elements in the array, the test will fail +* - If the calls to console.log do not contain the strings in the expectedValue array, the test will fail +* +* You do not need to understand how this function works to successfully complete the exercise. +*/ +function expectLogBooksToLog(expectedValues) { + const consoleLogSpy = jest.spyOn(console, 'log'); + logBooks(); + expect(consoleLogSpy).toBeCalledTimes(expectedValues.length); + expectedValues.forEach((value, i) => { + expect(consoleLogSpy).nthCalledWith(i+1, value); + }); + consoleLogSpy.mockRestore(); +}; \ No newline at end of file diff --git a/mandatory/7-budgets.js b/mandatory/9-budgets.js similarity index 84% rename from mandatory/7-budgets.js rename to mandatory/9-budgets.js index 787c8433..2c05a4b5 100644 --- a/mandatory/7-budgets.js +++ b/mandatory/9-budgets.js @@ -16,74 +16,53 @@ Should give return the answer of 62600. **/ -function getBudgets(peopleArray) {} +function getBudgets(peopleArray) { +} -/* -================================================== -====== TESTS - DO NOT MODIFY BELOW THIS LINE ===== -================================================== +/* ======= TESTS - DO MODIFY (!!!) ===== +- To run the tests for this exercise, run `npm test -- --testPathPattern 9-budgets.js` +- To run all exercises/tests in the mandatory folder, run `npm test` +- (Reminder: You must have run `npm install` one time before this will work!) */ -const util = require("util"); - -function test(test_name, actual, expected) { - let status; - - if (actual === expected) { - status = "PASSED"; - } else { - status = `FAILED: expected: ${util.inspect( - expected - )} but your function returned: ${util.inspect(actual)}`; - } - - console.log(`${test_name}: ${status}`); -} -test("No Budgets", getBudgets([]), 0); +test("No Budgets", () => { + expect(getBudgets([])).toEqual(0); +}); -test( - "Test 1", - getBudgets([ +test("Test 1", () => { + expect(getBudgets([ { name: "John", age: 21, budget: 23000 }, { name: "Steve", age: 32, budget: 40000 }, { name: "Martin", age: 16, budget: 2700 }, - ]), - 65700 -); + ])).toEqual(65700); +}); -test( - "Test 2", - getBudgets([ +test("Test 2", () => { + expect(getBudgets([ { name: "John", age: 21, budget: 29000 }, { name: "Steve", age: 32, budget: 32000 }, { name: "Martin", age: 16, budget: 1600 }, - ]), - 62600 -); + ])).toEqual(62600); +}); -test( - "Test 3", - getBudgets([ +test("Test 3", () => { + expect(getBudgets([ { name: "John", age: 21, budget: 19401 }, { name: "Steve", age: 32, budget: 12321 }, { name: "Martin", age: 16, budget: 1204 }, - ]), - 32926 -); + ])).toEqual(32926); +}); -test( - "Test 4", - getBudgets([ +test("Test 4", () => { + expect(getBudgets([ { name: "John", age: 21, budget: 10234 }, { name: "Steve", age: 32, budget: 21754 }, { name: "Martin", age: 16, budget: 4935 }, - ]), - 36923 -); + ])).toEqual(36923); +}); -test( - "Huge List", - getBudgets([ +test("Huge List", () => { + expect(getBudgets([ { name: "Reba", age: 73, @@ -334,6 +313,5 @@ test( age: 61, budget: 7849, }, - ]), - 289531 -); + ])).toEqual(289531); +}); diff --git a/package.json b/package.json new file mode 100644 index 00000000..f792ad1e --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "javascript-core-2-coursework-week1", + "version": "1.0.0", + "description": "Exercises for JS2 Week 1", + "scripts": { + "test": "jest --testRegex='mandatory[/\\\\].*\\.js$' --testPathIgnorePatterns='choose-your-own|recipes|water-bottle'" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/CodeYourFuture/JavaScript-Core-2-Coursework-Week1.git" + }, + "bugs": { + "url": "https://github.com/CodeYourFuture/JavaScript-Core-2-Coursework-Week1/issues" + }, + "jest": { + "setupFilesAfterEnv": [ + "jest-extended", + "./.setup/jest.setup.js" + ] + }, + "homepage": "https://github.com/CodeYourFuture/JavaScript-Core-2-Coursework-Week1#readme", + "devDependencies": { + "jest": "^26.6.3", + "jest-extended": "^0.11.5" + }, + "licence": "CC-BY-4.0" +}