Fakultas Ilmu Komputer UI

Commit eb0054fd authored by Lia Yuliana's avatar Lia Yuliana
Browse files

Merge branch 'PBI-2-registrasi' into 'staging'

Pbi 2 registrasi

See merge request !6
parents 6a671c97 cbbc61cd
Pipeline #72132 passed with stages
in 7 minutes and 12 seconds
......@@ -15288,6 +15288,11 @@
"workbox-webpack-plugin": "5.1.4"
}
},
"react-toast": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/react-toast/-/react-toast-1.0.1.tgz",
"integrity": "sha512-xqkO5ZJiJDOLxycZts7xi729blKw4frhg2I4bcIjT7mVlshxu0AsaHlKAdPhVeACAnn8nnamxg6zoYoC9BEjjg=="
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
......@@ -15917,6 +15922,45 @@
}
}
},
"router": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/router/-/router-1.3.5.tgz",
"integrity": "sha512-kozCJZUhuSJ5VcLhSb3F8fsmGXy+8HaDbKCAerR1G6tq3mnMZFMuSohbFvGv1c5oMFipijDjRZuuN/Sq5nMf3g==",
"requires": {
"array-flatten": "3.0.0",
"debug": "2.6.9",
"methods": "~1.1.2",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"setprototypeof": "1.2.0",
"utils-merge": "1.0.1"
},
"dependencies": {
"array-flatten": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz",
"integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
}
}
},
"rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
......
/* eslint-disable no-useless-escape */
import React, {useState, useRef, useEffect} from 'react'
import JustikaLogo from '../../assets/justika-logo.svg'
import IndonesianFlag from '../../assets/indonesian-flag.svg'
......@@ -14,7 +13,10 @@ import {isLoggedIn} from '../../store/auth/authSlice'
import {useSelector} from 'react-redux'
const Register = () => {
const {register, handleSubmit, watch, errors} = useForm()
const {register, handleSubmit, watch, errors} = useForm({
mode: 'onChange',
reValidateMode: 'onChange',
})
const [maskPassword, setMaskPassword] = useState(true)
const [maskConfirmPassword, setMaskConfirmPassword] = useState(true)
const password = useRef()
......@@ -28,6 +30,9 @@ const Register = () => {
}, [loggedIn])
const onSubmit = (data) => {
if(data["phone_number"].substring(0, 2) === "08") {
data["phone_number"] = data["phone_number"].substring(1)
}
axios
.post(`${BASE_URL.API_BASE_URL}/api/v1/auth/register/`, data)
.then(() => {
......@@ -81,7 +86,7 @@ const Register = () => {
</Link>
</p>
<div className="px-24 lg:px-96 xl:px-96 2xl:px-96 mx-8 lg:mx-20 xl:mx-20 2xl:mx-20 pt-8">
<div className="px-6 md:px-40 xl:px-96 sm:mx-20 pt-8">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col py-4">
<label className="text-charchoal heading-3" htmlFor="email">
......@@ -99,7 +104,7 @@ const Register = () => {
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message:
"Email perlu mengandung karakter '@' diikuti domain", //todo
"Email perlu mengandung karakter '@' diikuti domain",
},
})}
/>
......@@ -139,9 +144,9 @@ const Register = () => {
ref={register({
required: 'Wajib diisi',
pattern: {
value: /^8[0-9]*$/,
value: /^(?:08|8)[0-9]*$/,
message:
'Nomor Handphone seluruhnya harus angka dan diawali dengan angka 8',
'Nomor Handphone harus berupa angka',
},
})}
/>
......
......@@ -4,10 +4,11 @@ import {setupServer} from 'msw/node'
import {fireEvent, screen} from '@testing-library/react'
import Register from './Register'
import {testRender, makeTestStore} from '../../testUtils'
import BASE_URL from '../../api/config'
const fakeUserResponse = {token: 'fake_user_token'}
const server = setupServer(
rest.post('http://localhost:8000/api/v1/auth/register/', (req, res, ctx) => {
rest.post(`${BASE_URL.API_BASE_URL}/api/v1/auth/register/`, (req, res, ctx) => {
return res(ctx.json(fakeUserResponse))
}),
)
......@@ -42,10 +43,10 @@ test('register error if email format not valid', async () => {
const daftarButton = screen.getByTestId('daftar')
fireEvent.click(daftarButton)
const emailCharacter = await screen.findByText(
const emailCharacterError = await screen.findByText(
"Email perlu mengandung karakter '@' diikuti domain",
)
expect(emailCharacter).toBeVisible()
expect(emailCharacterError).toBeVisible()
})
test('register error if phone number not all the numbers', async () => {
......@@ -78,13 +79,13 @@ test('register error if phone number not all the numbers', async () => {
const daftarButton = screen.getByTestId('daftar')
fireEvent.click(daftarButton)
const phoneNumberStartsWith8 = await screen.findByText(
'Nomor Handphone seluruhnya harus angka dan diawali dengan angka 8',
const phoneNumberNotAllNumber = await screen.findByText(
'Nomor Handphone harus berupa angka',
)
expect(phoneNumberStartsWith8).toBeVisible()
expect(phoneNumberNotAllNumber).toBeVisible()
})
test('register error if phone number does not start with 8', async () => {
test('register error if phone number does not start with 08 or 8', async () => {
const store = makeTestStore()
testRender(<Register />, {store})
......@@ -114,10 +115,10 @@ test('register error if phone number does not start with 8', async () => {
const daftarButton = screen.getByTestId('daftar')
fireEvent.click(daftarButton)
const phoneNumberStartsWith8 = await screen.findByText(
'Nomor Handphone seluruhnya harus angka dan diawali dengan angka 8',
const phoneNumberNotStartWith08Or8 = await screen.findByText(
'Nomor Handphone harus berupa angka',
)
expect(phoneNumberStartsWith8).toBeVisible()
expect(phoneNumberNotStartWith08Or8).toBeVisible()
})
test('register error if password length less than eight character', async () => {
......@@ -186,10 +187,10 @@ test('register error if password not contain number', async () => {
const daftarButton = screen.getByTestId('daftar')
fireEvent.click(daftarButton)
const passwordOneNumber = await screen.findByText(
const passwordNotContainNumber = await screen.findByText(
'Password harus mengandung setidaknya 1 angka',
)
expect(passwordOneNumber).toBeVisible()
expect(passwordNotContainNumber).toBeVisible()
})
test('register error if confirm password does not match with password', async () => {
......@@ -248,11 +249,10 @@ test('can click toggle mask confirm password to show or hide password', async ()
expect(maskPassword.type).toBe('password')
})
test('data is sent to the server', async () => {
// mock the server error response for this test suite only.
test('data is successfully registered and phone number start with 08', async () => {
server.use(
rest.post(
'http://localhost:8000/api/v1/auth/register/',
`${BASE_URL.API_BASE_URL}/api/v1/auth/register/`,
(req, res, ctx) => {
return res(
ctx.status(200),
......@@ -260,12 +260,86 @@ test('data is sent to the server', async () => {
)
},
),
)
const store = makeTestStore()
testRender(<Register />, {store})
const emailElement = screen.getByLabelText(/Email/i)
fireEvent.change(emailElement, {
target: {value: 'sigendud@email.com'},
})
const noHandphoneElement = screen.getByLabelText(/No Handphone Aktif/i)
fireEvent.change(noHandphoneElement, {
target: {value: '08123456789'},
})
const passwordElement = screen.getByLabelText('Password*')
fireEvent.change(passwordElement, {
target: {value: 'sebuahPassYangValid22'},
})
const konfirmasiPasswordElement = screen.getByLabelText(
'Konfirmasi Password*',
)
fireEvent.change(konfirmasiPasswordElement, {
target: {value: 'sebuahPassYangValid22'},
})
const daftarButton = screen.getByTestId('daftar')
fireEvent.click(daftarButton)
})
test('data is successfully registered and phone number start with 8', async () => {
server.use(
rest.post(
'http://localhost:8000/api/v1/auth/register/',
`${BASE_URL.API_BASE_URL}/api/v1/auth/register/`,
(req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({message: 'Akun berhasil terdaftar'}),
)
},
),
)
const store = makeTestStore()
testRender(<Register />, {store})
const emailElement = screen.getByLabelText(/Email/i)
fireEvent.change(emailElement, {
target: {value: 'siunyil@email.com'},
})
const noHandphoneElement = screen.getByLabelText(/No Handphone Aktif/i)
fireEvent.change(noHandphoneElement, {
target: {value: '8123456789'},
})
const passwordElement = screen.getByLabelText('Password*')
fireEvent.change(passwordElement, {
target: {value: 'sebuahPassValid22'},
})
const konfirmasiPasswordElement = screen.getByLabelText(
'Konfirmasi Password*',
)
fireEvent.change(konfirmasiPasswordElement, {
target: {value: 'sebuahPassValid22'},
})
})
test('data is unsuccessfully registered because account already exists', async () => {
server.use(
rest.post(
`${BASE_URL.API_BASE_URL}/api/v1/auth/register/`,
(req, res, ctx) => {
return res(
ctx.status(400),
ctx.json({password: 'account with this email already exists.'}),
ctx.json({message: 'account with this email already exists.'}),
)
},
),
......@@ -277,7 +351,7 @@ test('data is sent to the server', async () => {
const emailElement = screen.getByLabelText(/Email/i)
fireEvent.change(emailElement, {
target: {value: 'sigendud@email.com'},
target: {value: 'siunyil@email.com'},
})
const noHandphoneElement = screen.getByLabelText(/No Handphone Aktif/i)
......@@ -287,16 +361,102 @@ test('data is sent to the server', async () => {
const passwordElement = screen.getByLabelText('Password*')
fireEvent.change(passwordElement, {
target: {value: 'sebuahPassYangValid22'},
target: {value: 'sebuahPassValid22'},
})
const konfirmasiPasswordElement = screen.getByLabelText(
'Konfirmasi Password*',
)
fireEvent.change(konfirmasiPasswordElement, {
target: {value: 'sebuahPassYangValid22'},
target: {value: 'sebuahPassValid22'},
})
const daftarButton = screen.getByTestId('daftar')
fireEvent.click(daftarButton)
})
test('data is unsuccessfully registered due to phone number count error', async () => {
server.use(
rest.post(
`${BASE_URL.API_BASE_URL}/api/v1/auth/register/`,
(req, res, ctx) => {
return res(
ctx.status(400),
ctx.json({message: 'Phone number must contain at least 9 digit number.'}),
)
},
),
)
const store = makeTestStore()
testRender(<Register />, {store})
const emailElement = screen.getByLabelText(/Email/i)
fireEvent.change(emailElement, {
target: {value: 'sibolang@email.com'},
})
const noHandphoneElement = screen.getByLabelText(/No Handphone Aktif/i)
fireEvent.change(noHandphoneElement, {
target: {value: '8123'},
})
const passwordElement = screen.getByLabelText('Password*')
fireEvent.change(passwordElement, {
target: {value: 'sebuahPass22'},
})
const konfirmasiPasswordElement = screen.getByLabelText(
'Konfirmasi Password*',
)
fireEvent.change(konfirmasiPasswordElement, {
target: {value: 'sebuahPass22'},
})
const daftarButton = screen.getByTestId('daftar')
fireEvent.click(daftarButton)
})
test('data is unsuccessfully registered because password is too common', async () => {
server.use(
rest.post(
`${BASE_URL.API_BASE_URL}/api/v1/auth/register/`,
(req, res, ctx) => {
return res(
ctx.status(400),
ctx.json({message: 'This password is too common.'}),
)
},
),
)
const store = makeTestStore()
testRender(<Register />, {store})
const emailElement = screen.getByLabelText(/Email/i)
fireEvent.change(emailElement, {
target: {value: 'sibolang@email.com'},
})
const noHandphoneElement = screen.getByLabelText(/No Handphone Aktif/i)
fireEvent.change(noHandphoneElement, {
target: {value: '8123000000'},
})
const passwordElement = screen.getByLabelText('Password*')
fireEvent.change(passwordElement, {
target: {value: 'abcde123'},
})
const konfirmasiPasswordElement = screen.getByLabelText(
'Konfirmasi Password*',
)
fireEvent.change(konfirmasiPasswordElement, {
target: {value: 'abcde123'},
})
const daftarButton = screen.getByTestId('daftar')
fireEvent.click(daftarButton)
})
\ No newline at end of file
import types from '../types'
export const startLogin = () => ({
type: types.AUTH.START,
})
export const errorLogin = (error) => ({
type: types.AUTH.ERROR,
error,
})
export const successLogin = (payload) => ({
type: types.AUTH.SUCCESS,
payload,
})
export const finishLogin = () => ({
type: types.AUTH.FINISH,
})
import types from '../types'
const initialState = {
token: undefined,
email: undefined,
isLoading: false,
errorMessage: undefined,
}
export default (state = initialState, action) => {
switch (action.type) {
case types.AUTH.START: {
return {
...state,
isLoading: true,
}
}
case types.AUTH.ERROR: {
return {
...state,
isLoading: false,
errorMessage: action.error,
}
}
case types.AUTH.SUCCESS: {
return {
...state,
token: action.payload.token,
user: action.payload.user,
isLoading: false,
errorMessage: undefined,
}
}
case types.AUTH.FINISH: {
return {
...state,
isLoading: false,
}
}
default: {
return state
}
}
}
export const isLoggedIn = (state) => {
if (state.auth.email) {
return true
}
return false
}
export const getStaffEmail = (state) => state.auth.email
export const getToken = (state) => {
if (state.auth.email) {
return state.auth.token
}
return undefined
}
import {startLogin, successLogin, finishLogin} from './authActions'
import setAuthToken from '../../api/http'
import apiService from '../../api/services'
export const login = (email, password) => (dispatch) => {
dispatch(startLogin())
apiService.account
.login({email, password})
.then((res) => {
setAuthToken(res.data.token)
dispatch(successLogin(res.data))
dispatch(finishLogin())
})
.catch((err) => {
const key = Object.keys(err.response.data)
key.map((k) => alert(err.response.data[k][0]))
dispatch(finishLogin())
})
}
import {combineReducers} from 'redux'
import auth from './auth/authReducer'
export default combineReducers({
auth,
})
const types = {
AUTH: {
START: 'START',
ERROR: 'ERROR',
SUCCESS: 'SUCCESS',
FINISH: 'FINISH',
},
}
export default types
{
"name": "app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@reach/router": "^1.3.4",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.1",
"autoprefixer": "^10.2.5",
"axios": "^0.21.1",
"postcss": "^8.2.8",
"react-scripts": "4.0.3",
"tailwindcss": "^2.0.3",
"web-vitals": "^1.1.0"
},
"scripts": {
"build:tailwind": "tailwindcss build src/tailwind.css -o src/tailwind.output.css",
"prestart": "npm run build:tailwind",
"prebuild": "npm run build:tailwind",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"test:coverage": "CI=true npm run test --env=jsdom -- --coverage",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"format": "prettier --write \"**/*.+(js|jsx|json|css)\""
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"coveragePathIgnorePatterns": [
"index.js"
]
},
"jest": {
"coveragePathIgnorePatterns": [
"index.js"
]
},
"devDependencies": {
"eslint": "^7.22.0",
"eslint-plugin-jest": "^24.3.2",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-testing-library": "^3.10.1",