diff --git a/.gitignore b/.gitignore index e43b0f9..b77f0c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ .DS_Store + +node_modules + +/coverage.data +/coverage/ \ No newline at end of file diff --git a/.harness/reacthdh.yaml b/.harness/reacthdh.yaml new file mode 100644 index 0000000..c3771e0 --- /dev/null +++ b/.harness/reacthdh.yaml @@ -0,0 +1,63 @@ +pipeline: + name: react-hdh + identifier: reacthdh + projectIdentifier: DhrubaCI + orgIdentifier: default + tags: {} + variables: + - name: DOCKERHUB_USERNAME + type: String + description: Your Docker Hub username + value: <+input> + stages: + - stage: + name: Build + identifier: Build + type: CI + spec: + cloneCodebase: true + execution: + steps: + - step: + type: Run + name: install node modules + identifier: install_node_modules + spec: + shell: Sh + command: npm install + - step: + type: Run + name: build app + identifier: build_app + spec: + shell: Sh + command: npm run build + - step: + type: Run + name: run tests + identifier: run_tests + spec: + shell: Sh + command: "# npm run test" + - step: + type: BuildAndPushDockerRegistry + name: Build and Push an image to Docker Registry + identifier: BuildandPushanimagetoDockerRegistry + spec: + connectorRef: harnesscommunitydocker + repo: <+pipeline.variables.DOCKERHUB_USERNAME>/test-react + tags: + - <+pipeline.sequenceId> + platform: + os: Linux + arch: Amd64 + runtime: + type: Cloud + spec: {} + properties: + ci: + codebase: + connectorRef: dhrubaaccountconnector + repoName: react-pipeline-sample + build: <+input> + allowStageExecutions: true diff --git a/.harness/samplereactapp.yaml b/.harness/samplereactapp.yaml index 55bf547..b9b2c32 100644 --- a/.harness/samplereactapp.yaml +++ b/.harness/samplereactapp.yaml @@ -20,6 +20,13 @@ pipeline: spec: shell: Sh command: npm install + - step: + type: Run + name: build app + identifier: run_tests + spec: + shell: Sh + command: npm run build - step: type: BuildAndPushDockerRegistry name: build and push to docker @@ -42,4 +49,3 @@ pipeline: connectorRef: <+input> repoName: sample-react-app-ci build: <+input> - diff --git a/.harness/testreact.yaml b/.harness/testreact.yaml new file mode 100644 index 0000000..b807913 --- /dev/null +++ b/.harness/testreact.yaml @@ -0,0 +1,90 @@ +pipeline: + name: test-react + identifier: testreact + projectIdentifier: DhrubaCI + orgIdentifier: default + tags: {} + variables: + - name: DOCKERHUB_USERNAME + type: String + description: Your Docker Hub username + value: <+input> + stages: + - stage: + name: Build + identifier: Build + type: CI + spec: + cloneCodebase: true + execution: + steps: + - step: + type: Run + name: build app + identifier: build_app + spec: + shell: Sh + command: npm run build + - step: + type: Run + name: run tests + identifier: run_tests + spec: + shell: Sh + command: npm run test + - step: + type: BuildAndPushDockerRegistry + name: Build and Push an image to Docker Registry + identifier: BuildandPushanimagetoDockerRegistry + spec: + connectorRef: DhrubajyotiDocker + repo: <+pipeline.variables.DOCKERHUB_USERNAME>/test-react + tags: + - <+pipeline.sequenceId> + platform: + os: Linux + arch: Amd64 + runtime: + type: Cloud + spec: {} + - stage: + name: Run Connectivity Test + identifier: Run_Connectivity_Test + description: "" + type: CI + spec: + cloneCodebase: false + platform: + os: Linux + arch: Amd64 + runtime: + type: Cloud + spec: {} + execution: + steps: + - step: + type: Background + name: Run Java HTTP Server + identifier: Run_Java_HTTP_Server + spec: + connectorRef: DhrubajyotiDocker + image: <+pipeline.variables.DOCKERHUB_USERNAME>/test-react:<+pipeline.sequenceId> + shell: Sh + portBindings: + "8888": "8888" + - step: + type: Run + name: Test Connection to Java HTTP Server + identifier: Test_Connection_to_Java_HTTP_Server + spec: + shell: Sh + command: |- + until curl --max-time 1 http://localhost:8888; do + sleep 2; + done + properties: + ci: + codebase: + connectorRef: dhrubaaccountconnector + repoName: react-pipeline-sample + build: <+input> diff --git a/.harness/testreact2.yaml b/.harness/testreact2.yaml new file mode 100644 index 0000000..69c5e5b --- /dev/null +++ b/.harness/testreact2.yaml @@ -0,0 +1,90 @@ +pipeline: + name: test-react-2 + identifier: testreact2 + projectIdentifier: DhrubaCI + orgIdentifier: default + tags: {} + variables: + - name: DOCKERHUB_USERNAME + type: String + description: Your Docker Hub username + value: <+input> + stages: + - stage: + name: Build + identifier: Build + type: CI + spec: + cloneCodebase: true + execution: + steps: + - step: + type: Run + name: build app + identifier: build_app + spec: + shell: Sh + command: npm run build + - step: + type: Run + name: run tests + identifier: run_tests + spec: + shell: Sh + command: npm run test + - step: + type: BuildAndPushDockerRegistry + name: Build and Push an image to Docker Registry + identifier: BuildandPushanimagetoDockerRegistry + spec: + connectorRef: DhrubajyotiDocker + repo: <+pipeline.variables.DOCKERHUB_USERNAME>/test-react + tags: + - <+pipeline.sequenceId> + platform: + os: Linux + arch: Amd64 + runtime: + type: Cloud + spec: {} + - stage: + name: Run Connectivity Test + identifier: Run_Connectivity_Test + description: "" + type: CI + spec: + cloneCodebase: false + platform: + os: Linux + arch: Amd64 + runtime: + type: Cloud + spec: {} + execution: + steps: + - step: + type: Background + name: Test Server + identifier: Test_Server + spec: + connectorRef: DhrubajyotiDocker + image: <+pipeline.variables.DOCKERHUB_USERNAME>/test-react:<+pipeline.sequenceId> + shell: Sh + portBindings: + "8888": "8888" + - step: + type: Run + name: Test Connection to Java HTTP Server + identifier: Test_Connection_to_Java_HTTP_Server + spec: + shell: Sh + command: |- + until curl --max-time 1 http://localhost:8888; do + sleep 2; + done + properties: + ci: + codebase: + connectorRef: dhrubaaccountconnector + repoName: react-pipeline-sample + build: <+input> diff --git a/.harness/testreact3.yaml b/.harness/testreact3.yaml new file mode 100644 index 0000000..69c5e5b --- /dev/null +++ b/.harness/testreact3.yaml @@ -0,0 +1,90 @@ +pipeline: + name: test-react-2 + identifier: testreact2 + projectIdentifier: DhrubaCI + orgIdentifier: default + tags: {} + variables: + - name: DOCKERHUB_USERNAME + type: String + description: Your Docker Hub username + value: <+input> + stages: + - stage: + name: Build + identifier: Build + type: CI + spec: + cloneCodebase: true + execution: + steps: + - step: + type: Run + name: build app + identifier: build_app + spec: + shell: Sh + command: npm run build + - step: + type: Run + name: run tests + identifier: run_tests + spec: + shell: Sh + command: npm run test + - step: + type: BuildAndPushDockerRegistry + name: Build and Push an image to Docker Registry + identifier: BuildandPushanimagetoDockerRegistry + spec: + connectorRef: DhrubajyotiDocker + repo: <+pipeline.variables.DOCKERHUB_USERNAME>/test-react + tags: + - <+pipeline.sequenceId> + platform: + os: Linux + arch: Amd64 + runtime: + type: Cloud + spec: {} + - stage: + name: Run Connectivity Test + identifier: Run_Connectivity_Test + description: "" + type: CI + spec: + cloneCodebase: false + platform: + os: Linux + arch: Amd64 + runtime: + type: Cloud + spec: {} + execution: + steps: + - step: + type: Background + name: Test Server + identifier: Test_Server + spec: + connectorRef: DhrubajyotiDocker + image: <+pipeline.variables.DOCKERHUB_USERNAME>/test-react:<+pipeline.sequenceId> + shell: Sh + portBindings: + "8888": "8888" + - step: + type: Run + name: Test Connection to Java HTTP Server + identifier: Test_Connection_to_Java_HTTP_Server + spec: + shell: Sh + command: |- + until curl --max-time 1 http://localhost:8888; do + sleep 2; + done + properties: + ci: + codebase: + connectorRef: dhrubaaccountconnector + repoName: react-pipeline-sample + build: <+input> diff --git a/Dockerfile b/Dockerfile index cdfb36b..864e183 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,9 @@ COPY package.json ./ RUN npm install COPY . . RUN npm run build +EXPOSE 8080 FROM nginx:1.19 COPY ./nginx/nginx.conf /etc/nginx/nginx.conf COPY --from=build /react-app/build /usr/share/nginx/html +EXPOSE 8080 + diff --git a/package-lock.json b/package-lock.json index 2c09763..49a8347 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "uuid": "^9.0.0", "web-vitals": "^2.1.4" } }, @@ -14981,6 +14982,14 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -15985,9 +15994,9 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", "bin": { "uuid": "dist/bin/uuid" } @@ -27514,6 +27523,13 @@ "faye-websocket": "^0.11.3", "uuid": "^8.3.2", "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } } }, "source-list-map": { @@ -28261,9 +28277,9 @@ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, "v8-to-istanbul": { "version": "8.1.1", diff --git a/package.json b/package.json index 57fe9f7..638d190 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", + "uuid": "^9.0.0", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/src/App.css b/src/App.css index 74b5e05..00d9bec 100644 --- a/src/App.css +++ b/src/App.css @@ -2,37 +2,13 @@ text-align: center; } -.App-logo { - height: 40vmin; - pointer-events: none; +table { + margin: 4rem auto; } -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } +table, +th, +td { + border: 1px solid black; } -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.js b/src/App.js index 3784575..e3838a0 100644 --- a/src/App.js +++ b/src/App.js @@ -1,23 +1,17 @@ -import logo from './logo.svg'; -import './App.css'; +import "./App.css"; +import { useReducer } from "react"; +import tableReducer, { initialState } from "./reducer/table.reducer"; +import Header from "./components/Header"; +import Form from "./components/Form"; +import Table from "./components/Table"; function App() { + const [state, dispatch] = useReducer(tableReducer, initialState); return (
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
+
+
+ ); } diff --git a/src/App.test.js b/src/App.test.js index 1f03afe..1923bf3 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -3,6 +3,6 @@ import App from './App'; test('renders learn react link', () => { render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); + const titleElement = screen.getByText(/React Sample Application/i); + expect(titleElement).toBeInTheDocument(); }); diff --git a/src/components/Form.js b/src/components/Form.js new file mode 100644 index 0000000..1d4a872 --- /dev/null +++ b/src/components/Form.js @@ -0,0 +1,44 @@ +import { useState } from "react"; +import { v4 as uuidv4 } from "uuid"; + +const Form = ({dispatch}) => { + const initialObj = { + id: uuidv4(), + name: "", + role: "", + } + const [contributorObj, setContributorObj] = useState(initialObj); + return( +
+

Form

+ + setContributorObj({ ...contributorObj, name: e.target.value }) + } + /> + + setContributorObj({ ...contributorObj, role: e.target.value }) + } + /> + +
+ ) +} + + +export default Form \ No newline at end of file diff --git a/src/components/Header.js b/src/components/Header.js new file mode 100644 index 0000000..20ec67a --- /dev/null +++ b/src/components/Header.js @@ -0,0 +1,6 @@ +const Header = () => { + return
+

React Sample Application

+
; +} +export default Header \ No newline at end of file diff --git a/src/components/Table.js b/src/components/Table.js new file mode 100644 index 0000000..63c3505 --- /dev/null +++ b/src/components/Table.js @@ -0,0 +1,36 @@ +const Table = ({ state, dispatch }) => { + return ( +
+ + + + + + + + {state.members?.map((contributor) => { + return ( + + + + + + ); + })} + +
NameRole
{contributor.name}{contributor.role} + +
+ ); +}; + +export default Table; diff --git a/src/index.css b/src/index.css index ec2585e..8b13789 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/src/reducer/table.reducer.js b/src/reducer/table.reducer.js new file mode 100644 index 0000000..2080d4e --- /dev/null +++ b/src/reducer/table.reducer.js @@ -0,0 +1,39 @@ +export const initialState = { + members: [{ id: 1234, name: "Jessica Adams", role: "Community Engineer" }], + totalMembers: 1, +}; + +const tableReducer = (state = initialState, action) => { + switch (action.type) { + case "ADD_MEMBER": + console.log("result", { + ...state, + members: [...state.members, action.payload.member], + totalMembers: state.totalMembers + 1, + }); + return { + ...state, + members: [...state.members, action.payload.member], + totalMembers: state.totalMembers + 1, + }; + + case "REMOVE_MEMBER": + const memberToBeRemoved = state.members.find( + (member) => member.id === action.payload.member.id + ); + + const members = state.members.filter( + (member) => member.id !== action.payload.member.id + ); + return { + ...state, + members, + totalMembers: state.totalMembers - 1, + }; + + default: + break; + } +}; + +export default tableReducer; diff --git a/src/reducer/table.reducer.test.js b/src/reducer/table.reducer.test.js new file mode 100644 index 0000000..886d69e --- /dev/null +++ b/src/reducer/table.reducer.test.js @@ -0,0 +1,74 @@ +import tableReducer, { initialState } from "./table.reducer"; + +describe("testing reducer", () => { + test("ADD_MEMBER", () => { + const initialState = { + members: [ + { id: 1234, name: "Jessica Adams", role: "Community Engineer" }, + ], + totalMembers: 1, + }; + + const action = { + type: "ADD_MEMBER", + payload: { + member: { + id: 1235, + name: "Ompragash Vishwanathan", + role: "Senior Software Engineer", + }, + }, + }; + + const expectedState = { + members: [ + { id: 1234, name: "Jessica Adams", role: "Community Engineer" },{ + id: 1235, + name: "Ompragash Vishwanathan", + role: "Senior Software Engineer", + } + ], + totalMembers: 2, + }; + + const state = tableReducer(initialState, action) + expect(state).toEqual(expectedState) + }); + + + test("REMOVE_MEMBER", () => { + const initialState = { + members: [ + { id: 1234, name: "Jessica Adams", role: "Community Engineer" }, + { id: 1235, name: "Ompragash Vishwanathan", role: "Senior Software Engineer" }, + { id: 1236, name: "Pravin Mali", role: "Community Engineer Manager" }, + { id: 1237, name: "Alex Garg", role: "Community Engineer" }, + ], + totalMembers: 4, + }; + + const action = { + type: "REMOVE_MEMBER", + payload: { + member: { + id: 1237, + name: "Alex Garg", + role: "Community Engineer", + }, + }, + }; + + const expectedState = { + members: [ + { id: 1234, name: "Jessica Adams", role: "Community Engineer" }, + { id: 1235, name: "Ompragash Vishwanathan", role: "Senior Software Engineer" }, + { id: 1236, name: "Pravin Mali", role: "Community Engineer Manager" } + ], + totalMembers: 3, + }; + + const state = tableReducer(initialState, action) + expect(state).toEqual(expectedState) + + }) +});