diff --git a/do.sh b/do.sh
index 757fd93..58ac7e0 100644
--- a/do.sh
+++ b/do.sh
@@ -11,7 +11,7 @@ build() {
run() {
echo "I am ${FUNCNAME[0]}ning"
- npx webpack serve
+ API_ROOT=http://localhost:3000/api npx webpack serve
}
fmt() {
diff --git a/package-lock.json b/package-lock.json
index 4fd64b1..ffc9054 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1105,6 +1105,16 @@
"regenerator-runtime": "^0.13.4"
}
},
+ "@babel/runtime-corejs3": {
+ "version": "7.12.5",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz",
+ "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==",
+ "dev": true,
+ "requires": {
+ "core-js-pure": "^3.0.0",
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
"@babel/template": {
"version": "7.12.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz",
@@ -1919,6 +1929,89 @@
"@sinonjs/commons": "^1.7.0"
}
},
+ "@testing-library/dom": {
+ "version": "7.29.4",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.29.4.tgz",
+ "integrity": "sha512-CtrJRiSYEfbtNGtEsd78mk1n1v2TUbeABlNIcOCJdDfkN5/JTOwQEbbQpoSRxGqzcWPgStMvJ4mNolSuBRv1NA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/runtime": "^7.12.5",
+ "@types/aria-query": "^4.2.0",
+ "aria-query": "^4.2.2",
+ "chalk": "^4.1.0",
+ "dom-accessibility-api": "^0.5.4",
+ "lz-string": "^1.4.4",
+ "pretty-format": "^26.6.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "@testing-library/react": {
+ "version": "11.2.3",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.3.tgz",
+ "integrity": "sha512-BirBUGPkTW28ULuCwIbYo0y2+0aavHczBT6N9r3LrsswEW3pg25l1wgoE7I8QBIy1upXWkwKpYdWY7NYYP0Bxw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.12.5",
+ "@testing-library/dom": "^7.28.1"
+ }
+ },
+ "@types/aria-query": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz",
+ "integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==",
+ "dev": true
+ },
"@types/babel__core": {
"version": "7.1.12",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.12.tgz",
@@ -2552,6 +2645,16 @@
"sprintf-js": "~1.0.2"
}
},
+ "aria-query": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
+ "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.10.2",
+ "@babel/runtime-corejs3": "^7.10.2"
+ }
+ },
"arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -3574,6 +3677,12 @@
}
}
},
+ "core-js-pure": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.3.tgz",
+ "integrity": "sha512-V5qQZVAr9K0xu7jXg1M7qTEwuxUgqr7dUOezGaNa7i+Xn9oXAU/d1fzqD9ObuwpVQOaorO5s70ckyi1woP9lVA==",
+ "dev": true
+ },
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -4001,6 +4110,12 @@
"esutils": "^2.0.2"
}
},
+ "dom-accessibility-api": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz",
+ "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==",
+ "dev": true
+ },
"dom-walk": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
@@ -8100,6 +8215,12 @@
"yallist": "^4.0.0"
}
},
+ "lz-string": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
+ "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=",
+ "dev": true
+ },
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
diff --git a/package.json b/package.json
index 3228bdf..76f3803 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,7 @@
"@babel/core": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
+ "@testing-library/react": "^11.2.3",
"babel-loader": "^8.2.2",
"css-loader": "^5.0.1",
"eslint": "^7.17.0",
@@ -59,5 +60,8 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-hot-loader": "^4.13.0"
- }
+ },
+ "browserslist": [
+ "since 2017-06"
+ ]
}
diff --git a/src/App.jsx b/src/App.jsx
index f08418c..7e0e167 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,19 +1,15 @@
import React from "react";
import { hot } from "react-hot-loader";
import "./App.css";
-import AllPlansComponent from "./components/AllPlansComponent.jsx";
+import UnauthenticatedApp from "./components/UnauthenticatedApp";
-class App extends React.Component {
- render() {
- return (
-
- );
- }
+function App() {
+ return (
+
+
Frontend
+
+
+ );
}
export default hot(module)(App);
diff --git a/src/components/Action.css b/src/components/Action.css
deleted file mode 100644
index c483af0..0000000
--- a/src/components/Action.css
+++ /dev/null
@@ -1,30 +0,0 @@
-.actionID,
-.actionDescription,
-.actionChunks {
- margin-left: 1rem;
- margin-right: 1rem;
-}
-
-.actionCompleted {
- flex: 1;
-}
-
-.actionChunks {
- flex: 4;
-}
-
-.actionID {
- flex: 4;
-}
-
-.actionDescription {
- flex: 8;
-}
-
-.actionWrapper {
- align-items: flex-end;
- display: flex;
- margin-top: 0.5rem;
- margin-bottom: 0.5rem;
- padding-top: 1rem;
-}
diff --git a/src/components/Action.jsx b/src/components/Action.jsx
deleted file mode 100644
index 65d34de..0000000
--- a/src/components/Action.jsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from "react";
-import { action } from "../types";
-import "./Action.css";
-
-class Action extends React.Component {
- render() {
- const action = this.props.action;
- const completed = "completed_on" in action;
- console.log([action.completed_on, completed]);
- return (
-
-
{completed ? "X" : " "}
-
- ID: {action.action_id} | {action.plan_id}
-
-
{action.action_description}
-
- {action.completed_chunks}/{action.estimated_chunks}
-
-
- );
- }
-}
-
-Action.propTypes = {
- action: action,
-};
-
-export default Action;
diff --git a/src/components/Action.test.jsx b/src/components/Action.test.jsx
deleted file mode 100644
index da0c2c1..0000000
--- a/src/components/Action.test.jsx
+++ /dev/null
@@ -1,3 +0,0 @@
-test("trying it out", () => {
- expect(true).toEqual(true);
-});
diff --git a/src/components/ActionsContainer.jsx b/src/components/ActionsContainer.jsx
deleted file mode 100644
index e116b1f..0000000
--- a/src/components/ActionsContainer.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from "react";
-import Action from "./Action.jsx";
-import PropTypes from "prop-types";
-import { action } from "../types";
-
-class ActionsContainer extends React.Component {
- render() {
- return (
-
-
- {this.props.actions.map((action) => {
- return (
- -
-
-
- );
- })}
-
-
- );
- }
-}
-
-ActionsContainer.propTypes = {
- actions: PropTypes.arrayOf(action),
-};
-
-export default ActionsContainer;
diff --git a/src/components/AllPlansComponent.jsx b/src/components/AllPlansComponent.jsx
deleted file mode 100644
index e2f6053..0000000
--- a/src/components/AllPlansComponent.jsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from "react";
-import PlanList from "./PlanList.jsx";
-
-class AllPlansComponent extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- plans: [],
- };
- }
-
- getPlans() {
- fetch("http://localhost:3000/api/plans", {
- headers: {
- Accept: "application/json",
- },
- })
- .then((response) => response.json())
- .then((data) => {
- this.setState({
- plans: data,
- });
- })
- .catch((error) => console.error(error));
- }
-
- componentDidMount() {
- this.getPlans();
- }
-
- render() {
- return (
-
- );
- }
-}
-
-export default AllPlansComponent;
diff --git a/src/components/Login-snapshot.test.jsx b/src/components/Login-snapshot.test.jsx
new file mode 100644
index 0000000..f5049f2
--- /dev/null
+++ b/src/components/Login-snapshot.test.jsx
@@ -0,0 +1,15 @@
+import React from "react";
+import renderer from "react-test-renderer";
+import Login from "./Login";
+
+test("Login Snapshot", () => {
+
+ const loginFunc = jest.fn();
+
+ const login = renderer.create();
+
+ const tree = login.toJSON();
+
+ expect(tree).toMatchSnapshot();
+
+});
diff --git a/src/components/Login.jsx b/src/components/Login.jsx
new file mode 100644
index 0000000..fdf8295
--- /dev/null
+++ b/src/components/Login.jsx
@@ -0,0 +1,44 @@
+import React, { useState } from "react";
+import PropTypes from "prop-types";
+
+export default function Login({ login }) {
+ const [username, setUserName] = useState();
+ const [password, setPassword] = useState();
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ login(username, password);
+ };
+
+ return (
+
+ );
+}
+
+Login.propTypes = {
+ login: PropTypes.func.isRequired,
+};
diff --git a/src/components/Login.test.jsx b/src/components/Login.test.jsx
new file mode 100644
index 0000000..6bb20c8
--- /dev/null
+++ b/src/components/Login.test.jsx
@@ -0,0 +1,17 @@
+import React from "react";
+import { render } from '@testing-library/react'
+import { logRoles } from "@testing-library/dom"
+// import '@testing-library/jest-dom/extend-expect'
+import Login from "./Login";
+
+test("Login inputs", () => {
+ const login = render()
+
+ const usernameInput = login.getByRole("textbox", {name: /Username/i})
+ expect(usernameInput).toBeTruthy();
+
+ const passwordInput = login.getByLabelText("Password")
+ expect(passwordInput).toBeTruthy();
+ logRoles(login.container)
+ expect(true).toBeTruthy();
+})
diff --git a/src/components/Plan.jsx b/src/components/Plan.jsx
deleted file mode 100644
index 038e736..0000000
--- a/src/components/Plan.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import React from "react";
-import ActionsContainer from "./ActionsContainer.jsx";
-import { plan } from "../types";
-
-class Plan extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- actions: [],
- };
- }
-
- getActions() {
- fetch(
- "http://localhost:3000/api/actions?" +
- new URLSearchParams({
- plan_id: this.props.plan.plan_id,
- }),
- {
- headers: {
- Accept: "application/json",
- },
- }
- )
- .then((response) => response.json())
- .then((data) => {
- this.setState({
- actions: data,
- });
- })
- .catch((error) => console.error(error));
- }
-
- componentDidMount() {
- this.getActions();
- }
-
- render() {
- return (
-
-
-
Plan for {this.props.plan.plan_date}
-
-
-
- );
- }
-}
-
-Plan.propTypes = {
- plan: plan,
-};
-
-export default Plan;
diff --git a/src/components/PlanList.jsx b/src/components/PlanList.jsx
deleted file mode 100644
index b011b4c..0000000
--- a/src/components/PlanList.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from "react";
-import Plan from "./Plan.jsx";
-import { plan } from "../types";
-import PropTypes from "prop-types";
-
-class PlanList extends React.Component {
- render() {
- return (
-
-
- {this.props.plans.map((plan) => {
- return (
- -
-
-
- );
- })}
-
-
- );
- }
-}
-
-PlanList.propTypes = {
- plans: PropTypes.arrayOf(plan),
-};
-
-export default PlanList;
diff --git a/src/components/Register.jsx b/src/components/Register.jsx
new file mode 100644
index 0000000..69f2150
--- /dev/null
+++ b/src/components/Register.jsx
@@ -0,0 +1,53 @@
+import React, { useState } from "react";
+import PropTypes from "prop-types";
+
+export default function Register({ register }) {
+ const [username, setUserName] = useState();
+ const [displayName, setDisplayName] = useState();
+ const [password, setPassword] = useState();
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ register(username, displayName, password);
+ };
+
+ return (
+
+
Enter your data to register.
+
+
+ );
+}
+
+Register.propTypes = {
+ register: PropTypes.func.isRequired,
+};
diff --git a/src/components/UnauthenticatedApp.jsx b/src/components/UnauthenticatedApp.jsx
new file mode 100644
index 0000000..cd1096a
--- /dev/null
+++ b/src/components/UnauthenticatedApp.jsx
@@ -0,0 +1,30 @@
+import React, { useState } from "react";
+import { useAuth } from "../context/AuthContext";
+import Login from "./Login";
+import Register from "./Register";
+
+function UnauthenticatedApp() {
+ const { login, register } = useAuth();
+
+ const [showRegister, setShowRegister] = useState(false);
+
+ function toggle() {
+ setShowRegister(!showRegister);
+ }
+
+ const form = showRegister ? (
+
+ ) : (
+
+ );
+ return (
+
+
+ {form}
+
+ );
+}
+
+export default UnauthenticatedApp;
diff --git a/src/components/__snapshots__/Login-snapshot.test.jsx.snap b/src/components/__snapshots__/Login-snapshot.test.jsx.snap
new file mode 100644
index 0000000..bb651de
--- /dev/null
+++ b/src/components/__snapshots__/Login-snapshot.test.jsx.snap
@@ -0,0 +1,42 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Login Snapshot 1`] = `
+
+`;
diff --git a/src/context/AuthContext.jsx b/src/context/AuthContext.jsx
index 6a7cf49..66c9fa9 100644
--- a/src/context/AuthContext.jsx
+++ b/src/context/AuthContext.jsx
@@ -1,55 +1,11 @@
import React from "react";
-import { useAsync } from "react-async";
-import { bootstrapAppData } from "../utils/bootstrap";
-import * as authClient from "../utils/auth-client";
-import { FullPageSpinner } from "../components/lib";
+import { register, login } from "../services/auth-service";
const AuthContext = React.createContext();
-function AuthProvider(props) {
- const [firstAttemptFinished, setFirstAttemptFinished] = React.useState(false);
- const {
- data = { user: null, listItems: [] },
- error,
- isRejected,
- isPending,
- isSettled,
- reload,
- } = useAsync({
- promiseFn: bootstrapAppData,
- });
-
- React.useLayoutEffect(() => {
- if (isSettled) {
- setFirstAttemptFinished(true);
- }
- }, [isSettled]);
-
- if (!firstAttemptFinished) {
- if (isPending) {
- return ;
- }
- if (isRejected) {
- return (
-
-
Uh oh... There's a problem. Try refreshing the app.
-
{error.message}
-
- );
- }
- }
-
- const login = (form) => authClient.login(form).then(reload);
- const register = (form) => authClient.register(form).then(reload);
- const logout = () => authClient.logout().then(reload);
-
- return (
-
- );
-}
+const AuthProvider = (props) => {
+ return ;
+};
function useAuth() {
const context = React.useContext(AuthContext);
diff --git a/src/context/index.jsx b/src/context/index.jsx
index 7c6dbb5..a0a8b5d 100644
--- a/src/context/index.jsx
+++ b/src/context/index.jsx
@@ -1,8 +1,13 @@
import React from "react";
import { AuthProvider } from "./AuthContext.jsx";
+import PropTypes from "prop-types";
function AppProviders({ children }) {
return {children};
}
export default AppProviders;
+
+AppProviders.propTypes = {
+ children: PropTypes.node,
+};
diff --git a/src/index.jsx b/src/index.jsx
index 4dba1e9..bd1cbf6 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -1,9 +1,8 @@
import React from "react";
import ReactDOM from "react-dom";
import App from "./App.jsx";
-ReactDOM.render(, document.getElementById("root"));
-
import AppProviders from "./context";
+
ReactDOM.render(
diff --git a/src/services/auth-service.js b/src/services/auth-service.js
index e69de29..c303186 100644
--- a/src/services/auth-service.js
+++ b/src/services/auth-service.js
@@ -0,0 +1,50 @@
+import { API_ROOT } from "./config";
+
+export const register = (username, displayName, password) => {
+ const url = API_ROOT + "auth/register";
+
+ const body = {
+ username: username,
+ display_name: displayName,
+ password: password,
+ };
+ return fetch(url, {
+ method: "POST",
+ headers: {
+ Accept: "application/json",
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ });
+};
+
+export const login = (username, password) => {
+ const url = API_ROOT + "auth/tokens";
+
+ const body = {
+ username: username,
+ password: password,
+ };
+ return fetch(url, {
+ method: "POST",
+ headers: {
+ Accept: "application/json",
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ })
+ .then((response) => {
+ if (!response.ok) {
+ throw Error(response.statusText);
+ }
+ return response.json();
+ })
+ .then((data) => {
+ console.log(data);
+ if (data.token) {
+ console.log(data.token);
+ return data.token;
+ }
+ throw Error("where's my token");
+ });
+};
diff --git a/src/services/config.test.jsx b/src/services/config.test.jsx
index ca52518..b339eed 100644
--- a/src/services/config.test.jsx
+++ b/src/services/config.test.jsx
@@ -1,4 +1,4 @@
-import { API_ROOT } from "./config"
+import { API_ROOT } from "./config";
test("testing config api root", () => {
expect(API_ROOT).toEqual("http://localhost:8080/");