From 4851df2b262e31f7839f8c28e53b6da73d6e9aca Mon Sep 17 00:00:00 2001 From: Alexander Vasyliev Date: Sun, 29 Sep 2024 22:15:09 +0200 Subject: [PATCH 1/2] done: assignment week 2 --- .../hackyourtemperature/__tests__/app.test.js | 61 +++++++++++++++++++ .../hackyourtemperature/babel.config.cjs | 13 ++++ .../hackyourtemperature/jest.config.js | 8 +++ assignments/hackyourtemperature/package.json | 27 ++++++++ assignments/hackyourtemperature/server.js | 34 +++++++++++ .../hackyourtemperature/sources/keys.js | 1 + 6 files changed, 144 insertions(+) create mode 100644 assignments/hackyourtemperature/__tests__/app.test.js create mode 100644 assignments/hackyourtemperature/babel.config.cjs create mode 100644 assignments/hackyourtemperature/jest.config.js create mode 100644 assignments/hackyourtemperature/package.json create mode 100644 assignments/hackyourtemperature/server.js create mode 100644 assignments/hackyourtemperature/sources/keys.js diff --git a/assignments/hackyourtemperature/__tests__/app.test.js b/assignments/hackyourtemperature/__tests__/app.test.js new file mode 100644 index 000000000..1706bd198 --- /dev/null +++ b/assignments/hackyourtemperature/__tests__/app.test.js @@ -0,0 +1,61 @@ +import app from "../server.js"; +import supertest from "supertest"; +import { API_KEY } from "../sources/keys.js"; +import nock from "nock"; +const request = supertest(app); + +describe("POST /", (done) => { + afterEach(() => { + nock.cleanAll(); + }); + + it("main page works", () => { + request + .get("/") + .expect(200) + .then((res) => { + expect(res.text).toBe("hello from backend to frontend!"); + }); + }); + + it("user provide correct city name and has a response", async () => { + const mockCity = "Amsterdam"; + const mockTemp = 15; + + nock("https://api.openweathermap.org") + .get("/data/2.5/weather") + .query({ q: mockCity, APPID: API_KEY, units: "metric" }) + .reply(200, { name: "Amsterdam", main: { temp: 15 } }); + + const res = await request + .post("/weather") + .send({ cityName: "Amsterdam" }) + .expect(200); + + expect(res.body).toEqual({ + Amsterdam: 15, + }); + }); + + it("user does not provide city name", async () => { + await request + .post("/weather") + .send() + .set("Content-type", "application/json") + .set("Accept", "application/json") + .expect(400, { + weatherText: "City name is required!", + }); + }); + + it("user provides gibberish city name", async () => { + await request + .post("/weather") + .send({ cityName: "abcdefg" }) + .set("Content-type", "application/json") + .set("Accept", "application/json") + .expect(404, { + weatherText: "City is not found!", + }); + }); +}); diff --git a/assignments/hackyourtemperature/babel.config.cjs b/assignments/hackyourtemperature/babel.config.cjs new file mode 100644 index 000000000..fbb629af6 --- /dev/null +++ b/assignments/hackyourtemperature/babel.config.cjs @@ -0,0 +1,13 @@ +module.exports = { + presets: [ + [ + // This is a configuration, here we are telling babel what configuration to use + "@babel/preset-env", + { + targets: { + node: "current", + }, + }, + ], + ], +}; diff --git a/assignments/hackyourtemperature/jest.config.js b/assignments/hackyourtemperature/jest.config.js new file mode 100644 index 000000000..19ba9649e --- /dev/null +++ b/assignments/hackyourtemperature/jest.config.js @@ -0,0 +1,8 @@ +export default { + // Tells jest that any file that has 2 .'s in it and ends with either js or jsx should be run through the babel-jest transformer + transform: { + "^.+\\.jsx?$": "babel-jest", + }, + // By default our `node_modules` folder is ignored by jest, this tells jest to transform those as well + transformIgnorePatterns: [], +}; diff --git a/assignments/hackyourtemperature/package.json b/assignments/hackyourtemperature/package.json new file mode 100644 index 000000000..9506531f5 --- /dev/null +++ b/assignments/hackyourtemperature/package.json @@ -0,0 +1,27 @@ +{ + "name": "node", + "version": "1.0.0", + "description": "> If you are following the HackYourFuture curriculum we recommend you to start with module 1: [HTML/CSS/GIT](https://github.com/HackYourFuture/HTML-CSS). To get a complete overview of the HackYourFuture curriculum first, click [here](https://github.com/HackYourFuture/curriculum).", + "main": "index.js", + "type": "module", + "scripts": { + "test": "jest", + "start": "nodemon server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.21.0", + "express-handlebars": "^8.0.1", + "nock": "^13.5.5", + "node-fetch": "^3.3.2", + "nodemon": "^3.1.7" + }, + "devDependencies": { + "@babel/preset-env": "^7.25.4", + "babel-jest": "^29.7.0", + "jest": "^29.7.0", + "supertest": "^7.0.0" + } +} diff --git a/assignments/hackyourtemperature/server.js b/assignments/hackyourtemperature/server.js new file mode 100644 index 000000000..cc7636191 --- /dev/null +++ b/assignments/hackyourtemperature/server.js @@ -0,0 +1,34 @@ +import express from "express"; +import fetch from "node-fetch"; +import { API_KEY } from "./sources/keys.js"; + +const app = express(); + +app.use(express.json()); + +app.get("/", (req, res) => { + res.send("hello from backend to frontend!"); +}); + +app.post("/weather", async (req, res) => { + if (!req.body.cityName) { + return res.status(400).send({ weatherText: "City name is required!" }); + } + + const cityName = req.body.cityName; + const url = `https://api.openweathermap.org/data/2.5/weather?q=${cityName}&APPID=${API_KEY}&units=metric`; + + const response = await fetch(url); + + if (!response.ok) { + return res.status(404).send({ weatherText: "City is not found!" }); + } + + const data = await response.json(); + + res.status(200).send({ [data.name]: data.main.temp }); +}); + +app.listen(3000); + +export default app; diff --git a/assignments/hackyourtemperature/sources/keys.js b/assignments/hackyourtemperature/sources/keys.js new file mode 100644 index 000000000..577968fff --- /dev/null +++ b/assignments/hackyourtemperature/sources/keys.js @@ -0,0 +1 @@ +export const API_KEY = "8330edd4dfc04fbec36a7e51070c9c62"; From 3c77943ff3c041a20125f4d419f2c8842aded9f5 Mon Sep 17 00:00:00 2001 From: Alexander Vasyliev Date: Wed, 9 Oct 2024 15:28:16 +0200 Subject: [PATCH 2/2] add app.js, change server.js and fix tests --- .gitignore | 4 +-- .../hackyourtemperature/__tests__/app.test.js | 17 +++++---- assignments/hackyourtemperature/app.js | 33 +++++++++++++++++ assignments/hackyourtemperature/server.js | 35 +++---------------- .../hackyourtemperature/sources/keys.js | 1 - 5 files changed, 50 insertions(+), 40 deletions(-) create mode 100644 assignments/hackyourtemperature/app.js delete mode 100644 assignments/hackyourtemperature/sources/keys.js diff --git a/.gitignore b/.gitignore index b6b402ed9..242626159 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,5 @@ npm-debug.log package-lock.json yarn-error.log *.bkp - -week3/prep-exercise/server-demo/ +/assignments/hackyourtemperature/sources/keys.js +week3/prep-exercise/server-demo/ \ No newline at end of file diff --git a/assignments/hackyourtemperature/__tests__/app.test.js b/assignments/hackyourtemperature/__tests__/app.test.js index 1706bd198..ab67e2a80 100644 --- a/assignments/hackyourtemperature/__tests__/app.test.js +++ b/assignments/hackyourtemperature/__tests__/app.test.js @@ -1,4 +1,4 @@ -import app from "../server.js"; +import app from "../app.js"; import supertest from "supertest"; import { API_KEY } from "../sources/keys.js"; import nock from "nock"; @@ -9,8 +9,8 @@ describe("POST /", (done) => { nock.cleanAll(); }); - it("main page works", () => { - request + it("App returns 200 status on the main page.", async () => { + await request .get("/") .expect(200) .then((res) => { @@ -18,7 +18,7 @@ describe("POST /", (done) => { }); }); - it("user provide correct city name and has a response", async () => { + it("App returns 200 status when the city name is correct.", async () => { const mockCity = "Amsterdam"; const mockTemp = 15; @@ -37,7 +37,7 @@ describe("POST /", (done) => { }); }); - it("user does not provide city name", async () => { + it("App returns 400 status when the city name is missing.", async () => { await request .post("/weather") .send() @@ -48,7 +48,12 @@ describe("POST /", (done) => { }); }); - it("user provides gibberish city name", async () => { + it("App returns 404 status when the city name is gibberish.", async () => { + nock("https://api.openweathermap.org") + .get("/data/2.5/weather") + .query({ q: "abcdefg", APPID: API_KEY, units: "metric" }) + .reply(404, { message: "city not found" }); + await request .post("/weather") .send({ cityName: "abcdefg" }) diff --git a/assignments/hackyourtemperature/app.js b/assignments/hackyourtemperature/app.js new file mode 100644 index 000000000..9bb585726 --- /dev/null +++ b/assignments/hackyourtemperature/app.js @@ -0,0 +1,33 @@ +import express from "express"; +import fetch from "node-fetch"; +import { API_KEY } from "./sources/keys.js"; + +const app = express(); + +app.use(express.json()); +const baseUrl = "https://api.openweathermap.org/data/2.5/weather"; + +app.get("/", (req, res) => { + res.send("hello from backend to frontend!"); +}); + +app.post("/weather", async (req, res) => { + if (!req.body.cityName) { + return res.status(400).send({ weatherText: "City name is required!" }); + } + + const cityName = req.body.cityName; + const url = `${baseUrl}?q=${cityName}&APPID=${API_KEY}&units=metric`; + + const response = await fetch(url); + + if (!response.ok) { + return res.status(404).send({ weatherText: "City is not found!" }); + } + + const data = await response.json(); + + res.status(200).send({ [data.name]: data.main.temp }); +}); + +export default app; diff --git a/assignments/hackyourtemperature/server.js b/assignments/hackyourtemperature/server.js index cc7636191..14bc3d9bd 100644 --- a/assignments/hackyourtemperature/server.js +++ b/assignments/hackyourtemperature/server.js @@ -1,34 +1,7 @@ -import express from "express"; -import fetch from "node-fetch"; -import { API_KEY } from "./sources/keys.js"; +import app from "./app.js"; -const app = express(); +const PORT = 3004; -app.use(express.json()); - -app.get("/", (req, res) => { - res.send("hello from backend to frontend!"); -}); - -app.post("/weather", async (req, res) => { - if (!req.body.cityName) { - return res.status(400).send({ weatherText: "City name is required!" }); - } - - const cityName = req.body.cityName; - const url = `https://api.openweathermap.org/data/2.5/weather?q=${cityName}&APPID=${API_KEY}&units=metric`; - - const response = await fetch(url); - - if (!response.ok) { - return res.status(404).send({ weatherText: "City is not found!" }); - } - - const data = await response.json(); - - res.status(200).send({ [data.name]: data.main.temp }); +app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); }); - -app.listen(3000); - -export default app; diff --git a/assignments/hackyourtemperature/sources/keys.js b/assignments/hackyourtemperature/sources/keys.js deleted file mode 100644 index 577968fff..000000000 --- a/assignments/hackyourtemperature/sources/keys.js +++ /dev/null @@ -1 +0,0 @@ -export const API_KEY = "8330edd4dfc04fbec36a7e51070c9c62";