From 59e579a3359026e7f4e356d41445462369c3b328 Mon Sep 17 00:00:00 2001 From: Matt Crowder Date: Fri, 15 Jun 2018 13:04:41 -0400 Subject: [PATCH 1/8] adding some server stuff --- package.json | 4 +++- src/client/actions/sagas/ping-server.js | 3 ++- src/server/index.js | 13 ++++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e2867e6..d78b9ad 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "jest": "npm run pre-jest && jest --coverage --silent && npm run pre-jest", "bundlesize": "bundlesize", "coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls", - "prepublishOnly": "npm run build" + "prepublishOnly": "npm run build", + "server-watch": "NODE_ENV=development && babel-watch src/server/index.js" }, "bundlesize": [ { @@ -54,6 +55,7 @@ "jest": { "collectCoverageFrom": [ "src/client/**/*.{js*}", + "src/server/**/*.{js*}", "index.js", "!src/client/browser-history.js", "!src/client/app.js", diff --git a/src/client/actions/sagas/ping-server.js b/src/client/actions/sagas/ping-server.js index 3409f98..270a83d 100644 --- a/src/client/actions/sagas/ping-server.js +++ b/src/client/actions/sagas/ping-server.js @@ -2,9 +2,10 @@ import { call, put } from "redux-saga/effects"; import { setPing } from "../index"; +const url = process.env.NODE_ENV === "production" ? "" : "http://localhost:3000"; export const apiCall = async () => { // this is up to you whether or not you want to implement this server... - const res = await fetch("http://localhost:3000/ping", { method: "GET" }); + const res = await fetch(`${url}/ping`, { method: "GET" }); return res.text(); }; diff --git a/src/server/index.js b/src/server/index.js index af971c6..e0da30d 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -3,7 +3,15 @@ import bodyParser from "body-parser"; import path from "path"; const app = express(); - +if (process.env.NODE_ENV === "development") { + // eslint-disable-next-line no-console + console.log("CORs enabled"); + app.use((req, res, next) => { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + next(); + }); +} app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.get("*.js", (req, res, next) => { @@ -12,6 +20,9 @@ app.get("*.js", (req, res, next) => { res.set("Content-Encoding", "gzip"); next(); }); +app.get("/ping", (req, res) => { + res.send(`The server says hello`); +}); app.use(express.static(path.resolve(__dirname, "../..", "build"))); // eslint-disable-next-line no-console app.listen(3000, () => console.log(`server started on port 3000`)); \ No newline at end of file From 5caca57e2bd2a646c8dd88a2c841300d017fb328 Mon Sep 17 00:00:00 2001 From: Matt Crowder Date: Fri, 15 Jun 2018 13:05:49 -0400 Subject: [PATCH 2/8] todo --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 9aa3f24..7dfc4cb 100644 --- a/index.js +++ b/index.js @@ -88,7 +88,7 @@ const executeBashCommand = (command, loadingText) => { const createFolder = folder => { return executeFunction(callback => fs.mkdir(folder, callback), `Creating ${folder}`); }; - +//TODO don't forget to include the correct dependencies const cli = () => { return new Promise((outerResolve, outerReject) => { From e1479d96971c4890d341557018109c1c2cce7e46 Mon Sep 17 00:00:00 2001 From: Matt Crowder Date: Fri, 15 Jun 2018 13:05:53 -0400 Subject: [PATCH 3/8] todo --- build-index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/build-index.js b/build-index.js index d2376ac..58a5318 100755 --- a/build-index.js +++ b/build-index.js @@ -77,6 +77,7 @@ var createFolder = function createFolder(folder) { return fs.mkdir(folder, callback); }, "Creating " + folder); }; +//TODO don't forget to include the correct dependencies var cli = function cli() { return new Promise(function (outerResolve, outerReject) { From e771f9df67ea4313d3c4ba2853ec5cb5b4a141c6 Mon Sep 17 00:00:00 2001 From: Matt Crowder Date: Fri, 15 Jun 2018 17:21:59 -0400 Subject: [PATCH 4/8] server stuff with fetch-wrapper --- .vscode/launch.json | 23 +++++++++ build-index.js | 12 ++--- index-tests/force.spec.js | 2 +- index-tests/forcef.spec.js | 2 +- index.js | 18 +++++-- package-lock.json | 8 ++- package.json | 7 +-- src/server/index.js | 4 +- src/shared/constants.js | 5 ++ src/shared/fetch-wrapper.js | 57 +++++++++++++++++++++ test/server/index.spec.js | 4 ++ test/shared/fetch-wrapper.spec.js | 82 +++++++++++++++++++++++++++++++ 12 files changed, 206 insertions(+), 18 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 src/shared/constants.js create mode 100644 src/shared/fetch-wrapper.js create mode 100644 test/server/index.spec.js create mode 100644 test/shared/fetch-wrapper.spec.js diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5ef4131 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest All", + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "args": ["--runInBand"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + }, + { + "type": "node", + "request": "launch", + "name": "Jest Current File", + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "args": ["${relativeFile}"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + } + ] +} \ No newline at end of file diff --git a/build-index.js b/build-index.js index 58a5318..b8fa29d 100755 --- a/build-index.js +++ b/build-index.js @@ -34,8 +34,8 @@ var fs = require("fs-extra"); var packageJson = require("./package.json"); var deps = { - "dependencies": ["babel-runtime", "babel-polyfill", "html-webpack-plugin", "prop-types", "react", "react-dom", "react-redux", "react-router", "react-router-dom", "redux", "redux-saga", "webpack", "node-sass", "history"], - "devDependencies": ["babel-core", "babel-eslint", "babel-jest", "babel-loader", "babel-plugin-transform-async-to-generator", "babel-plugin-transform-class-properties", "babel-plugin-transform-es2015-modules-umd", "babel-plugin-transform-object-rest-spread", "babel-plugin-transform-runtime", "babel-preset-env", "babel-preset-react", "bundlesize", "compression-webpack-plugin", "css-loader", "enzyme", "enzyme-adapter-react-16", "eslint-config-mcrowder65", "jest", "fetch-mock", "style-loader", "postcss-loader", "postcss-flexbugs-fixes", "sass-loader", "react-hot-loader", "webpack-dev-server", "identity-obj-proxy", "webpack-bundle-analyzer"] + "dependencies": ["babel-runtime", "babel-polyfill", "html-webpack-plugin", "prop-types", "express", "react", "react-dom", "react-redux", "react-router", "react-router-dom", "redux", "redux-saga", "webpack", "node-sass", "history", "isomorphic-fetch"], + "devDependencies": ["babel-core", "babel-eslint", "babel-jest", "babel-loader", "babel-plugin-transform-async-to-generator", "babel-plugin-transform-class-properties", "babel-plugin-transform-es2015-modules-umd", "babel-plugin-transform-object-rest-spread", "babel-plugin-transform-runtime", "babel-preset-env", "babel-preset-react", "bundlesize", "compression-webpack-plugin", "css-loader", "enzyme", "enzyme-adapter-react-16", "eslint-config-mcrowder65", "jest", "fetch-mock", "style-loader", "shortid", "postcss-loader", "postcss-flexbugs-fixes", "sass-loader", "react-hot-loader", "webpack-dev-server", "identity-obj-proxy", "webpack-bundle-analyzer"] }; var executeFunction = function executeFunction(func, loadingText) { var spinner = void 0; @@ -77,7 +77,6 @@ var createFolder = function createFolder(folder) { return fs.mkdir(folder, callback); }, "Creating " + folder); }; -//TODO don't forget to include the correct dependencies var cli = function cli() { return new Promise(function (outerResolve, outerReject) { @@ -125,7 +124,8 @@ var cli = function cli() { linter: "./node_modules/.bin/eslint src --ext .js,.jsx && ./node_modules/.bin/eslint test --ext .js,.jsx", webpack: "export NODE_ENV=production && ./node_modules/.bin/webpack -p --progress", bundlesize: "bundlesize", - "analyze-bundle": "export ANALYZE_BUNDLE=true && npm run webpack" + "analyze-bundle": "export ANALYZE_BUNDLE=true && npm run webpack", + "server-watch": "NODE_ENV=development && babel-watch src/server/index.js" }), jest: (0, _extends4.default)({}, pkgJson.jest, { setupTestFrameworkScriptFile: "/test/client/config.js", @@ -133,7 +133,7 @@ var cli = function cli() { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/file-mock.js", "\\.(css|scss|less)$": "identity-obj-proxy" }, - collectCoverageFrom: ["src/client/**/*.{js*}", "!src/client/browser-history.js", "!src/client/app.js", "!src/client/router.js", "!src/client/actions/sagas/config.js", "!src/client/actions/sagas/index.js"], + collectCoverageFrom: ["src/**/*.{js*}", "!src/client/browser-history.js", "!src/client/app.js", "!src/client/router.js", "!src/client/actions/sagas/config.js", "!src/client/actions/sagas/index.js"], modulePaths: ["src/client/"], coverageReporters: ["html"] }) @@ -228,7 +228,7 @@ var cli = function cli() { while (1) { switch (_context3.prev = _context3.next) { case 0: - files = ["webpack.config.js", ".babelrc", "src/client/actions/sagas/config.js", "src/client/actions/sagas/index.js", "src/client/actions/sagas/ping-server.js", "src/client/actions/sagas/types.js", "src/client/actions/index.js", "src/client/actions/types.js", "src/client/components/home.js", "src/client/reducers/index.js", "src/client/reducers/initial-state.js", "src/client/styles/base.scss", "src/client/app.js", "src/client/browser-history.js", "src/client/index.html", "src/client/router.js", "test/client/__mocks__/file-mock.js", "test/client/actions/sagas/ping-server.spec.js", "test/client/actions/index.spec.js", "test/client/config.js", "test/client/reducers/index.spec.js"]; + files = ["webpack.config.js", ".babelrc", "src/client/actions/sagas/config.js", "src/client/actions/sagas/index.js", "src/client/actions/sagas/ping-server.js", "src/client/actions/sagas/types.js", "src/client/actions/index.js", "src/client/actions/types.js", "src/client/components/home.js", "src/client/reducers/index.js", "src/client/reducers/initial-state.js", "src/client/styles/base.scss", "src/client/app.js", "src/client/browser-history.js", "src/client/index.html", "src/client/router.js", "src/server/index.js", "src/shared/constants.js", "src/shared/fetch-wrapper.js", "test/client/__mocks__/file-mock.js", "test/client/actions/sagas/ping-server.spec.js", "test/client/actions/index.spec.js", "test/client/config.js", "test/client/reducers/index.spec.js", "test/server/index.spec.js", "test/shared/fetch-wrapper.spec.js"]; _iteratorNormalCompletion = true; _didIteratorError = false; _iteratorError = undefined; diff --git a/index-tests/force.spec.js b/index-tests/force.spec.js index aa8889c..1b623ed 100644 --- a/index-tests/force.spec.js +++ b/index-tests/force.spec.js @@ -3,7 +3,7 @@ import { executeBashFunction, cli } from "./utils.js"; test("When force exists, it should reject", async () => { try { const folder = "force"; - await executeBashFunction(`mkdir ${folder}`); + await executeBashFunction(`rm -rf ${folder} && mkdir ${folder}`); process.argv.push(folder); await cli(); } catch (e) { diff --git a/index-tests/forcef.spec.js b/index-tests/forcef.spec.js index 58ceab2..aac48ba 100644 --- a/index-tests/forcef.spec.js +++ b/index-tests/forcef.spec.js @@ -2,7 +2,7 @@ import { executeBashFunction, cli, doesFileExist } from "./utils.js"; test("When forcef exists and you pass -f, it should delete it and work!", async () => { const folder = "forcef"; - await executeBashFunction(`mkdir ${folder}`); + await executeBashFunction(`rm -rf ${folder} && mkdir ${folder}`); process.argv.push(folder); process.argv.push("-f"); process.argv.push("-s"); diff --git a/index.js b/index.js index 7dfc4cb..1b175d6 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ const deps = { "babel-polyfill", "html-webpack-plugin", "prop-types", + "express", "react", "react-dom", "react-redux", @@ -21,7 +22,8 @@ const deps = { "redux-saga", "webpack", "node-sass", - "history" + "history", + "isomorphic-fetch" ], "devDependencies": [ "babel-core", @@ -44,6 +46,7 @@ const deps = { "jest", "fetch-mock", "style-loader", + "shortid", "postcss-loader", "postcss-flexbugs-fixes", "sass-loader", @@ -88,7 +91,6 @@ const executeBashCommand = (command, loadingText) => { const createFolder = folder => { return executeFunction(callback => fs.mkdir(folder, callback), `Creating ${folder}`); }; -//TODO don't forget to include the correct dependencies const cli = () => { return new Promise((outerResolve, outerReject) => { @@ -160,7 +162,8 @@ const cli = () => { linter: "./node_modules/.bin/eslint src --ext .js,.jsx && ./node_modules/.bin/eslint test --ext .js,.jsx", webpack: "export NODE_ENV=production && ./node_modules/.bin/webpack -p --progress", bundlesize: "bundlesize", - "analyze-bundle": "export ANALYZE_BUNDLE=true && npm run webpack" + "analyze-bundle": "export ANALYZE_BUNDLE=true && npm run webpack", + "server-watch": "NODE_ENV=development && babel-watch src/server/index.js" }, jest: { ...pkgJson.jest, @@ -170,7 +173,7 @@ const cli = () => { "\\.(css|scss|less)$": "identity-obj-proxy" }, collectCoverageFrom: [ - "src/client/**/*.{js*}", + "src/**/*.{js*}", "!src/client/browser-history.js", "!src/client/app.js", "!src/client/router.js", @@ -236,11 +239,16 @@ npm-debug.log`; "src/client/browser-history.js", "src/client/index.html", "src/client/router.js", + "src/server/index.js", + "src/shared/constants.js", + "src/shared/fetch-wrapper.js", "test/client/__mocks__/file-mock.js", "test/client/actions/sagas/ping-server.spec.js", "test/client/actions/index.spec.js", "test/client/config.js", - "test/client/reducers/index.spec.js" + "test/client/reducers/index.spec.js", + "test/server/index.spec.js", + "test/shared/fetch-wrapper.spec.js" ]; for (const f of files) { try { diff --git a/package-lock.json b/package-lock.json index 54b31d8..201e4e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "create-react-matt", - "version": "0.0.87", + "version": "0.0.88", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -11458,6 +11458,12 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, + "shortid": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.8.tgz", + "integrity": "sha1-AzsRfWoul1gE9vCWnb59PQs1UTE=", + "dev": true + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", diff --git a/package.json b/package.json index d78b9ad..aaa55f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-react-matt", - "version": "0.0.87", + "version": "0.0.88", "description": "React, Redux, Webpack, Babel, Jest, and code coverage all provided for you", "main": "main-index.js", "repository": { @@ -54,8 +54,7 @@ ], "jest": { "collectCoverageFrom": [ - "src/client/**/*.{js*}", - "src/server/**/*.{js*}", + "src/**/*.{js*}", "index.js", "!src/client/browser-history.js", "!src/client/app.js", @@ -123,6 +122,7 @@ "history": "4.7.2", "html-webpack-plugin": "3.2.0", "identity-obj-proxy": "3.0.0", + "isomorphic-fetch": "2.2.1", "jest": "23.1.0", "node-sass": "4.9.0", "path": "0.12.7", @@ -139,6 +139,7 @@ "redux": "4.0.0", "redux-saga": "0.16.0", "sass-loader": "7.0.3", + "shortid": "^2.2.8", "style-loader": "0.21.0", "webpack": "3.12.0", "webpack-bundle-analyzer": "2.13.1", diff --git a/src/server/index.js b/src/server/index.js index e0da30d..583a31b 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -25,4 +25,6 @@ app.get("/ping", (req, res) => { }); app.use(express.static(path.resolve(__dirname, "../..", "build"))); // eslint-disable-next-line no-console -app.listen(3000, () => console.log(`server started on port 3000`)); \ No newline at end of file +app.listen(3000, () => console.log(`server started on port 3000`)); + +export default app; \ No newline at end of file diff --git a/src/shared/constants.js b/src/shared/constants.js new file mode 100644 index 0000000..6735e16 --- /dev/null +++ b/src/shared/constants.js @@ -0,0 +1,5 @@ +export const HTTP_RESPONSE_TYPES = { + JSON: "application/json", + HTML: "text/html", + PLAIN: "text/plain" +}; \ No newline at end of file diff --git a/src/shared/fetch-wrapper.js b/src/shared/fetch-wrapper.js new file mode 100644 index 0000000..ca55318 --- /dev/null +++ b/src/shared/fetch-wrapper.js @@ -0,0 +1,57 @@ +import "isomorphic-fetch"; +import { HTTP_RESPONSE_TYPES } from "./constants"; + +const resolveResponse = (response, responseType) => { + if (responseType.indexOf(HTTP_RESPONSE_TYPES.JSON) !== -1) { + return response.json(); + } else if (responseType.indexOf(HTTP_RESPONSE_TYPES.HTML) !== -1) { + return response.text(); + } else if (responseType.indexOf(HTTP_RESPONSE_TYPES.PLAIN) !== -1) { + return response.text(); + } else { + throw Error("Response type not supported yet!"); + } +}; + +const initialHeaders = { "Content-Type": HTTP_RESPONSE_TYPES.JSON, Accept: HTTP_RESPONSE_TYPES.JSON }; + +const fetchWrapper = async ({ + url, + method, + body, + headers, +}) => { + // do this here to get Content-Type to not be overridden if you didn't want to. + const actualHeaders = { ...initialHeaders, ...headers }; + const response = await fetch(url, { + method, + headers: actualHeaders, + body + }); + const responseType = response.headers.get("content-type"); + if (!responseType) { + throw Error("Response type was not defined"); + } + const resolvedResponse = await resolveResponse(response, responseType); + if (!response.ok) { + throw Error(JSON.stringify(resolvedResponse)); + } + return resolvedResponse; +}; + + +export const fetchGet = ({ url, headers, credentials }) => { + return fetchWrapper({ url, method: "GET", headers, credentials }); +}; + +export const fetchPost = ({ url, body, headers, credentials }) => { + return fetchWrapper({ url, method: "POST", body, headers, credentials }); +}; + +export const fetchPut = ({ url, body, headers, credentials }) => { + return fetchWrapper({ url, method: "PUT", body, headers, credentials }); +}; + +export const fetchDelete = ({ url, body, headers, credentials }) => { + return fetchWrapper({ url, method: "DELETE", body, headers, credentials }); +}; diff --git a/test/server/index.spec.js b/test/server/index.spec.js new file mode 100644 index 0000000..bf390cf --- /dev/null +++ b/test/server/index.spec.js @@ -0,0 +1,4 @@ +test.skip("that it turns on and off", () => { + // const server = require("../../server/index"); + +}); \ No newline at end of file diff --git a/test/shared/fetch-wrapper.spec.js b/test/shared/fetch-wrapper.spec.js new file mode 100644 index 0000000..5412178 --- /dev/null +++ b/test/shared/fetch-wrapper.spec.js @@ -0,0 +1,82 @@ +import fetchMock from "fetch-mock"; +import shortId from "shortid"; + +import { + fetchDelete, + fetchGet, + fetchPost, + fetchPut +} from "../../src/shared/fetch-wrapper"; +import { HTTP_RESPONSE_TYPES } from "../../src/shared/constants"; + +const restore = () => fetchMock.restore(); +describe("edge cases", () => { + afterEach(restore); + + test("When not giving back a response type, it should throw!", async () => { + expect.assertions(1); + try { + const url = shortId.generate(); + fetchMock.put(url, { body: {}, headers: { "content-type": undefined }, sendAsJson: false }); + await fetchPut({ url }); + } catch (e) { + expect(e.message).toEqual("Response type was not defined"); + } + }); + test(`When giving a random content-type, (unique id), + it should throw since it's not handled`, async () => { + expect.assertions(1); + try { + const url = shortId.generate(); + fetchMock.get(url, { body: {}, headers: { "content-type": shortId.generate() } }); + await fetchGet({ url }); + } catch (e) { + expect(e.message).toEqual("Response type not supported yet!"); + } + }); + test(`When giving back a 500 response code, it should throw`, async () => { + expect.assertions(1); + try { + const url = shortId.generate(); + fetchMock.post(url, { body: {}, status: 500 }); + await fetchPost({ url }); + } catch (e) { + expect(e).toBeTruthy(); + } + }); +}); +describe("All the different response types", () => { + afterEach(restore); + test(`content-type: ${HTTP_RESPONSE_TYPES.JSON} should resolve to a json`, async () => { + const body = { hello: "I am a string in a json" }; + const url = shortId.generate(); + fetchMock.post(url, { + body, + headers: { "content-type": HTTP_RESPONSE_TYPES.JSON } + }); + const result = await fetchPost({ url }); + expect(result).toEqual(body); + }); + test(`content-type ${HTTP_RESPONSE_TYPES.HTML} should resolve to a string`, async () => { + const body = "I am a string "; + const url = shortId.generate(); + fetchMock.put(url, { + body, + headers: { "content-type": HTTP_RESPONSE_TYPES.HTML } + }); + const result = await fetchPut({ url }); + expect(result).toEqual(body); + }); + test(`content-type ${HTTP_RESPONSE_TYPES.PLAIN} should resolve to a string`, async () => { + const body = "I am a string "; + const url = shortId.generate(); + fetchMock.delete(url, { + body, + headers: { "content-type": HTTP_RESPONSE_TYPES.PLAIN } + }); + const result = await fetchDelete({ url }); + expect(result).toEqual(body); + }); + +}); + From 404bc5bc624ff705235d42c13263e0bf50255222 Mon Sep 17 00:00:00 2001 From: Matt Crowder Date: Fri, 15 Jun 2018 17:35:35 -0400 Subject: [PATCH 5/8] got some tests --- src/client/actions/sagas/ping-server.js | 8 ++++---- src/shared/fetch-wrapper.js | 17 +++++++++-------- test/client/actions/sagas/ping-server.spec.js | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/client/actions/sagas/ping-server.js b/src/client/actions/sagas/ping-server.js index 270a83d..59b53f5 100644 --- a/src/client/actions/sagas/ping-server.js +++ b/src/client/actions/sagas/ping-server.js @@ -1,15 +1,15 @@ import { call, put } from "redux-saga/effects"; import { setPing } from "../index"; +import { fetchGet } from "../../../shared/fetch-wrapper"; +import { HTTP_RESPONSE_TYPES } from "../../../shared/constants"; const url = process.env.NODE_ENV === "production" ? "" : "http://localhost:3000"; -export const apiCall = async () => { +export const apiCall = () => { // this is up to you whether or not you want to implement this server... - const res = await fetch(`${url}/ping`, { method: "GET" }); - return res.text(); + return fetchGet({ url: `${url}/ping`, headers: { "Content-Type": HTTP_RESPONSE_TYPES.PLAIN, Accept: HTTP_RESPONSE_TYPES.PLAIN } }); }; -// eslint-disable-next-line export function* pingServer() { try { const resp = yield call(apiCall); diff --git a/src/shared/fetch-wrapper.js b/src/shared/fetch-wrapper.js index ca55318..24b2c1f 100644 --- a/src/shared/fetch-wrapper.js +++ b/src/shared/fetch-wrapper.js @@ -23,6 +23,7 @@ const fetchWrapper = async ({ }) => { // do this here to get Content-Type to not be overridden if you didn't want to. const actualHeaders = { ...initialHeaders, ...headers }; + const response = await fetch(url, { method, headers: actualHeaders, @@ -40,18 +41,18 @@ const fetchWrapper = async ({ }; -export const fetchGet = ({ url, headers, credentials }) => { - return fetchWrapper({ url, method: "GET", headers, credentials }); +export const fetchGet = ({ url, headers }) => { + return fetchWrapper({ url, method: "GET", headers }); }; -export const fetchPost = ({ url, body, headers, credentials }) => { - return fetchWrapper({ url, method: "POST", body, headers, credentials }); +export const fetchPost = ({ url, body, headers }) => { + return fetchWrapper({ url, method: "POST", body, headers }); }; -export const fetchPut = ({ url, body, headers, credentials }) => { - return fetchWrapper({ url, method: "PUT", body, headers, credentials }); +export const fetchPut = ({ url, body, headers }) => { + return fetchWrapper({ url, method: "PUT", body, headers }); }; -export const fetchDelete = ({ url, body, headers, credentials }) => { - return fetchWrapper({ url, method: "DELETE", body, headers, credentials }); +export const fetchDelete = ({ url, body, headers }) => { + return fetchWrapper({ url, method: "DELETE", body, headers }); }; diff --git a/test/client/actions/sagas/ping-server.spec.js b/test/client/actions/sagas/ping-server.spec.js index 20e753d..df1bda9 100644 --- a/test/client/actions/sagas/ping-server.spec.js +++ b/test/client/actions/sagas/ping-server.spec.js @@ -24,7 +24,7 @@ describe("pingServer", () => { describe("apiCall", () => { test("api call", async () => { const response = "hello"; - fetchMock.mock("http://localhost:3000/ping", response); + fetchMock.get("http://localhost:3000/ping", { body: response, sendAsJson: false, headers: { "Content-Type": "text/html" } }); const result = await apiCall(); expect(result).toEqual(response); From 83ba30c5efd348b9f68a1f7e2bfa0e5ff42346aa Mon Sep 17 00:00:00 2001 From: Matt Crowder Date: Fri, 15 Jun 2018 20:05:53 -0400 Subject: [PATCH 6/8] got some things --- index.js | 2 +- package.json | 2 +- src/client/actions/sagas/config.js | 3 ++- src/client/router.js | 1 + src/server/index.js | 5 ++--- test/client/actions/sagas/ping-server.spec.js | 15 +++++++++++++-- test/client/components/home.spec.js | 10 +++++----- test/client/reducers/index.spec.js | 6 +++--- test/server/index.spec.js | 8 ++++++-- webpack.config.js | 2 +- 10 files changed, 35 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index 1b175d6..53c5766 100644 --- a/index.js +++ b/index.js @@ -180,7 +180,7 @@ const cli = () => { "!src/client/actions/sagas/config.js", "!src/client/actions/sagas/index.js" ], - modulePaths: ["src/client/"], + modulePaths: ["src/"], coverageReporters: ["html"] } diff --git a/package.json b/package.json index aaa55f0..7a5bef7 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "lcov" ], "modulePaths": [ - "src/client/" + "src/" ], "globals": { "localStorage": {} diff --git a/src/client/actions/sagas/config.js b/src/client/actions/sagas/config.js index b52d783..61c5ed0 100644 --- a/src/client/actions/sagas/config.js +++ b/src/client/actions/sagas/config.js @@ -1,5 +1,6 @@ +import { pingServer } from "client/actions/sagas/ping-server"; import * as sagaTypes from "./types"; -import { pingServer } from "./ping-server"; + // The second argument of your value object can be method, // where you can override takeEvery and do takeLatest instead const sagaConfig = { diff --git a/src/client/router.js b/src/client/router.js index 56d7ec3..b78f27c 100644 --- a/src/client/router.js +++ b/src/client/router.js @@ -9,6 +9,7 @@ const Router = () => (
+
diff --git a/src/server/index.js b/src/server/index.js index 583a31b..e7d185f 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -25,6 +25,5 @@ app.get("/ping", (req, res) => { }); app.use(express.static(path.resolve(__dirname, "../..", "build"))); // eslint-disable-next-line no-console -app.listen(3000, () => console.log(`server started on port 3000`)); - -export default app; \ No newline at end of file +const server = app.listen(3000, () => console.log(`server started on port 3000`)); +export default server; \ No newline at end of file diff --git a/test/client/actions/sagas/ping-server.spec.js b/test/client/actions/sagas/ping-server.spec.js index df1bda9..3bab24a 100644 --- a/test/client/actions/sagas/ping-server.spec.js +++ b/test/client/actions/sagas/ping-server.spec.js @@ -1,8 +1,8 @@ import { call, put } from "redux-saga/effects"; import fetchMock from "fetch-mock"; -import { setPing, } from "actions/index"; -import { apiCall, pingServer } from "actions/sagas/ping-server"; +import { setPing, } from "client/actions/index"; +import { apiCall, pingServer } from "client/actions/sagas/ping-server"; describe("pingServer", () => { @@ -22,6 +22,17 @@ describe("pingServer", () => { }); }); describe("apiCall", () => { + test.skip("api call with server", async () => { + let server; + try { + const response = "The server says hello"; + server = require("server/index"); + const result = await apiCall(); + expect(result).toEqual(response); + } finally { + server.default.close(); + } + }); test("api call", async () => { const response = "hello"; fetchMock.get("http://localhost:3000/ping", { body: response, sendAsJson: false, headers: { "Content-Type": "text/html" } }); diff --git a/test/client/components/home.spec.js b/test/client/components/home.spec.js index e606720..3d14ff6 100644 --- a/test/client/components/home.spec.js +++ b/test/client/components/home.spec.js @@ -5,10 +5,10 @@ import { createStore, applyMiddleware, compose } from "redux"; import { Provider } from "react-redux"; import fetchMock from "fetch-mock"; -import initialState from "reducers/initial-state"; -import rootReducer from "reducers/index"; -import Home from "components/home"; -import sagaActions from "actions/sagas"; +import initialState from "client/reducers/initial-state"; +import rootReducer from "client/reducers/index"; +import Home from "client/components/home"; +import sagaActions from "client/actions/sagas"; const generateEvent = value => ({ target: { value } }); describe("test/client/components/home.spec.js", () => { @@ -43,7 +43,7 @@ describe("full suite around", () => { home.find("#input").props().onChange(generateEvent("hello")); expect(store.getState().input).toEqual("hello"); const response = "hello"; - fetchMock.mock("http://localhost:3000/ping", response); + fetchMock.get("http://localhost:3000/ping", { body: response, sendAsJson: false, headers: { "Content-Type": "text/html" } }); home.find("#ping-server").props().onClick(); await delay(1000); home.update(); diff --git a/test/client/reducers/index.spec.js b/test/client/reducers/index.spec.js index 119a7b6..a542865 100644 --- a/test/client/reducers/index.spec.js +++ b/test/client/reducers/index.spec.js @@ -1,6 +1,6 @@ -import rootReducer from "reducers/index"; -import initialState from "reducers/initial-state"; -import { setInput, setPing } from "actions"; +import rootReducer from "client/reducers/index"; +import initialState from "client/reducers/initial-state"; +import { setInput, setPing } from "client/actions"; describe("input", () => { test("When not passing in a state, it should default to initialState.input", () => { diff --git a/test/server/index.spec.js b/test/server/index.spec.js index bf390cf..0c9ce81 100644 --- a/test/server/index.spec.js +++ b/test/server/index.spec.js @@ -1,4 +1,8 @@ -test.skip("that it turns on and off", () => { - // const server = require("../../server/index"); +import { fetchGet } from "shared/fetch-wrapper"; +test("responds?", async () => { + const server = require("server/index"); + const result = await fetchGet({ url: "http://localhost:3000/ping" }); + expect(result).toEqual("The server says hello"); + server.default.close(); }); \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index b81e913..12f72df 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,7 +5,7 @@ const CompressionPlugin = require("compression-webpack-plugin"); const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; const isProd = process.env.NODE_ENV === "production"; -const sourcePath = path.join(__dirname, "./src/client"); +const sourcePath = path.join(__dirname, "src"); const webpackConfig = { cache: !isProd, devtool: isProd ? "" : "eval-cheap-module-source-map", From 824ba1687e1665b5d1d86cfb8bb6bf388ea4aeae Mon Sep 17 00:00:00 2001 From: Matt Crowder Date: Fri, 15 Jun 2018 20:06:01 -0400 Subject: [PATCH 7/8] forgot build-index --- build-index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-index.js b/build-index.js index b8fa29d..7c08b5b 100755 --- a/build-index.js +++ b/build-index.js @@ -134,7 +134,7 @@ var cli = function cli() { "\\.(css|scss|less)$": "identity-obj-proxy" }, collectCoverageFrom: ["src/**/*.{js*}", "!src/client/browser-history.js", "!src/client/app.js", "!src/client/router.js", "!src/client/actions/sagas/config.js", "!src/client/actions/sagas/index.js"], - modulePaths: ["src/client/"], + modulePaths: ["src/"], coverageReporters: ["html"] }) From 730159d35fad885ae22fcbca9435affa0497eeaf Mon Sep 17 00:00:00 2001 From: Matt Crowder Date: Tue, 26 Jun 2018 21:22:08 -0400 Subject: [PATCH 8/8] got the server routing!! --- src/server/index.js | 15 ++++++++++++--- test/server/index.spec.js | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/server/index.js b/src/server/index.js index e7d185f..d3340f7 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -8,7 +8,10 @@ if (process.env.NODE_ENV === "development") { console.log("CORs enabled"); app.use((req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); - res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + res.header( + "Access-Control-Allow-Headers", + "Origin, X-Requested-With, Content-Type, Accept" + ); next(); }); } @@ -24,6 +27,12 @@ app.get("/ping", (req, res) => { res.send(`The server says hello`); }); app.use(express.static(path.resolve(__dirname, "../..", "build"))); +app.get("*", (req, res) => { + res.sendFile(path.join(__dirname, "../../build", "index.html")); +}); + // eslint-disable-next-line no-console -const server = app.listen(3000, () => console.log(`server started on port 3000`)); -export default server; \ No newline at end of file +const server = app.listen(3000, () => + console.log(`server started on port 3000`) +); +export default server; diff --git a/test/server/index.spec.js b/test/server/index.spec.js index 0c9ce81..233379d 100644 --- a/test/server/index.spec.js +++ b/test/server/index.spec.js @@ -1,8 +1,8 @@ import { fetchGet } from "shared/fetch-wrapper"; -test("responds?", async () => { +test("hitting /ping should return 'The server says hello'", async () => { const server = require("server/index"); const result = await fetchGet({ url: "http://localhost:3000/ping" }); expect(result).toEqual("The server says hello"); server.default.close(); -}); \ No newline at end of file +});