Skip to content
Snippets Groups Projects
Commit eb682645 authored by Gilgard's avatar Gilgard
Browse files

Merge branch 'main' into user-profile-view

parents aa63fe56 eda2530f
No related branches found
No related tags found
1 merge request!13User profile view
Pipeline #175902 passed
.DS_Store
node_modules
/dist
coverage
# local env files
......
......@@ -41,6 +41,7 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.0.3",
"jest": "^27.0.5",
"jest-serializer-vue": "^2.0.2",
"postcss": "^8.4.12",
"prettier": "^2.4.1",
"tailwindcss": "^3.0.24"
<template>
<v-form ref="form" v-model="valid" lazy-validation>
<v-text-field
v-model="email"
:rules="emailRules"
label="E-mail"
required
></v-text-field>
<v-text-field
v-model="password"
:counter="32"
:rules="passwordRules"
label="Passord"
:append-icon="passwordHidden ? 'mdi-eye' : 'mdi-eye-off'"
:type="passwordHidden ? 'text' : 'password'"
@click:append="passwordHidden = !passwordHidden"
required
></v-text-field>
<v-container class="grey lighten-5">
<v-row>
<v-text-field
class="pr-2"
v-model="firstName"
:counter="32"
:rules="firstNameRules"
label="Fornavn"
required
></v-text-field>
<v-text-field
class="pl-2"
v-model="lastName"
:counter="32"
:rules="lastNameRules"
label="Etternavn"
required
></v-text-field>
</v-row>
</v-container>
<v-text-field
v-model="address"
:counter="32"
:rules="addressRules"
label="Addresse"
required
></v-text-field>
<!-- <v-text-field
v-model="confirmPassword"
:rules="confirmPasswordRules"
label="Bekreft passord"
:append-icon="confirmPasswordHidden ? 'mdi-eye' : 'mdi-eye-off'"
:type="confirmPasswordHidden ? 'text' : 'password'"
@click:append="confirmPasswordHidden = !confirmPasswordHidden"
required
></v-text-field> -->
<!-- <v-select
v-model="select"
:items="items"
:rules="[(v) => !!v || 'Item is required']"
label="Item"
required
></v-select> -->
<!-- <v-checkbox
v-model="checkbox"
:rules="[(v) => !!v || 'You must agree to continue!']"
label="Do you agree?"
required
></v-checkbox> -->
<v-btn :disabled="!valid" color="success" class="mr-4" @click="submit()"
>Registrer</v-btn
>
<v-btn color="error" class="mr-4" @click="reset()">Tøm felter</v-btn>
</v-form>
<section
class="max-w-4xl p-6 mx-auto bg-white rounded-md shadow-md dark:bg-gray-800"
>
<h2 class="text-lg font-semibold text-gray-700 capitalize dark:text-white">
Opprett ny bruker
</h2>
<form @submit.prevent>
<div class="grid grid-cols-1 gap-6 mt-4 sm:grid-cols-2">
<div>
<label class="text-gray-700 dark:text-gray-200" for="email"
>E-mail</label
>
<input
v-model="email"
id="email"
type="email"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="password"
>Passord</label
>
<input
v-model="password"
id="password"
type="password"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="confirmPassword"
>Bekreft Passord</label
>
<input
v-model="confirmPassword"
id="confirmPassword"
type="password"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="firstName"
>Fornavn</label
>
<input
data-test="firstNameTest"
v-model="firstName"
id="firstName"
type="text"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="lastName"
>Etternavn</label
>
<input
v-model="lastName"
id="lastName"
type="text"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
<div>
<label class="text-gray-700 dark:text-gray-200" for="address"
>Addresse</label
>
<input
v-model="address"
id="address"
type="text"
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
/>
</div>
</div>
<div class="flex justify-end mt-6">
<button
class="px-6 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded-md hover:bg-gray-600 focus:outline-none focus:bg-gray-600"
@click="submit()"
type="submit"
:disabled="loading"
>
<div v-if="loading">
<div v-if="loading" class="lds-ring">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<div v-else>Lagre</div>
</button>
</div>
</form>
</section>
<ul data-test="errorMessageList">
<li v-if="errorMessage" data-test="customErrorMsg">{{ errorMessage }}</li>
<li v-for="error of v$.$errors" :key="error.$uid">
<!-- {{ error.$validator }} -->
Field
{{ error.$property }}
has error:
{{ error.$message }}
</li>
</ul>
</template>
<script>
import axios from "axios";
import useVuelidate from "@vuelidate/core";
import { doLogin, registerUser } from "@/utils/apiutil";
import { required, email, minLength, sameAs } from "@vuelidate/validators";
export default {
data: () => ({
passwordHidden: false,
// confirmPasswordHidden: false,
valid: true,
firstName: "",
firstNameRules: [
(v) => !!v || "Fornavn er påkrevd",
(v) => (v && v.length <= 32) || "Fornavn må være mindre enn 32 bokstaver",
],
lastName: "",
lastNameRules: [
(v) => !!v || "Etternavn er påkrevd",
(v) =>
(v && v.length <= 32) || "Etternavn må være mindre enn 32 bokstaver",
],
address: "",
addressRules: [
(v) => !!v || "Addresse er påkrevd",
(v) =>
(v && v.length <= 32) || "Addresse må være mindre enn 32 bokstaver",
],
password: "",
passwordRules: [
(v) => !!v || "Passord er påkrevd",
(v) => (v && v.length <= 32) || "Passord må være mindre enn 32 tegn",
(v) => (v && v.length >= 8) || "Passord må være større enn 8 tegn",
],
// confirmPassword: "",
// confirmPasswordRules: [
// (v) => !!v || "Passord er påkrevd",
// (v) => (v && v.length <= 32) || "Passord må være mindre enn 32 bokstaver",
// // (v) => v === this.password || "Passordene må være like",
// ],
email: "",
emailRules: [
(v) => !!v || "E-mail is required",
(v) => /.+@.+\..+/.test(v) || "E-mail must be valid",
],
// select: null,
// items: ["Item 1", "Item 2", "Item 3", "Item 4"],
// checkbox: false,
}),
// const isEmailTaken = (value) =>
// fetch(`/api/unique/${value}`).then((r) => r.json()); // check the email in the server
export default {
setup: () => ({ v$: useVuelidate() }),
data() {
return {
errorMessage: "",
loading: false,
email: "",
password: "",
confirmPassword: "",
firstName: "",
lastName: "",
address: "",
};
},
validations() {
return {
email: {
required,
email,
// isUnique: helpers.withAsync(isEmailTaken),
},
password: {
required,
minLength: minLength(8),
},
confirmPassword: { sameAs: sameAs(this.password) },
firstName: { required },
lastName: { required },
address: { required },
};
},
methods: {
submit() {
console.log("Attempting to register user");
this.valid = this.$refs.form.validate();
if (!this.valid) return;
this.valid = false;
console.log("User is validated");
axios
.post("http://localhost:3000/api/register", {
email: this.email,
firstName: this.firstName,
lastname: this.lastName,
password: this.password,
address: this.address,
})
.then(console.log("Sent"))
.catch((e) => console.log(e));
async submit() {
//Display loading symbol
this.loading = true;
//Validate form
const result = await this.v$.$validate();
if (!result) {
this.loading = false;
return;
}
//Send a request to create a user and save success as a bool
const userCreated = await this.sendRegisterRequest();
//If a user is created succsessfully, try to login
//If we get this far, we will be pushed anyway so there is no point updating "loading"
if (!userCreated) {
this.errorMessage = "Could not create user.";
return;
}
const loginRequest = {
email: this.email,
password: this.password,
};
const loginResponse = await doLogin(loginRequest);
if (loginResponse === "Failed login") {
this.errorMessage = "Failed to log in with new user";
this.$store.commit("logout");
this.$router.push("/login");
return;
}
this.$store.commit("saveToken", loginResponse);
this.$router.push("/");
},
reset() {
this.$refs.form.reset();
this.$refs.form.resetValidation();
this.valid = true;
async sendRegisterRequest() {
const registerInfo = {
email: this.email,
firstName: this.firstName,
lastname: this.lastName,
password: this.password,
address: this.address,
};
const response = await registerUser(registerInfo);
if (response.status === 200) return true;
return false;
},
},
};
</script>
<style scoped>
/* https://loading.io/css/ */
.lds-ring {
display: inline-block;
position: relative;
width: 20px;
height: 20px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 16px;
height: 16px;
margin: 2px;
border: 2px solid #fff;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #fff transparent transparent transparent;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
......@@ -102,30 +102,28 @@ export default {
name: "LargeProfileCard",
data() {
return {
user: this.getUser(),
renterRating: 3, //getRenterRating(this.userID),
ownerRating: 5, //getOwnerRating(this.userID),
user: {},
currentUser: {},
id: -1,
isCurrentUser: false,
renterRating: 0, //getRenterRating(this.userID),
ownerRating: 0, //getOwnerRating(this.userID),
dropdown: false,
};
},
computed: {
isCurrentUser() {
if (this.user.id == parseUserFromToken().id) return true;
return false;
},
},
components: {
RatingComponent,
},
methods: {
getUser() {
let id = router.currentRoute.value.params.id;
let currentUser = parseUserFromToken();
console.log(currentUser);
if ((id = currentUser.account_id)) return currentUser;
let user = getUser(id);
console.log(user);
return user;
this.currentUser = parseUserFromToken();
this.id = router.currentRoute.value.params.id;
if (this.id == this.currentUser.account_id) {
this.isCurrentUser = true;
this.user = this.currentUser;
return;
}
this.user = getUser(this.id);
},
getProfilePicture() {
/* if (this.user.picture == "") {
......@@ -134,5 +132,8 @@ export default {
return "../assets/defaultUserProfileImage.jpg";
},
},
beforeMount() {
this.getUser();
},
};
</script>
......@@ -9,7 +9,7 @@
</div>
<div class="flex-1 pl-1">
<div class="font-medium dark:text-white">
{{ user.firstName }} {{ user.lastName }}
{{ user.first_name }} {{ user.last_name }}
</div>
</div>
<div class="flex flex-row justify-center">
......
......@@ -21,6 +21,7 @@ export function getUser(userid) {
headers: tokenHeader(),
})
.then((response) => {
console.log(response.data);
return response.data;
})
.catch((error) => {
......
<template>
<register-form-component id="form" class="pa-8" />
<div class="h-screen bg-gray-200 content-center grid place-items-center">
<RegisterFormComponent />
</div>
</template>
<script>
......@@ -11,10 +13,3 @@ export default {
},
};
</script>
<style scoped>
#form {
max-width: 600px;
margin: auto;
}
</style>
module.exports = {
darkMode: "class",
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
theme: {
extend: {},
......
import { mount } from "@vue/test-utils";
import RegisterFormComponent from "@/components/RegisterFormComponent";
describe("RegisterFormComponent", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(RegisterFormComponent);
});
it("renders correctly", () => {
expect(wrapper.element).toMatchSnapshot();
});
it("is instantiated", () => {
expect(wrapper.exists()).toBeTruthy();
});
it("renders error message to user", async () => {
await wrapper.setData({ errorMessage: "test message" });
expect(wrapper.find('li[data-test="customErrorMsg"]').text()).toBe(
"test message"
);
});
it("renders the h2 text correctly", () => {
expect(wrapper.find("h2").text()).toBe("Opprett ny bruker");
});
it("has a button", () => {
expect(wrapper.exists("button")).toBe(true);
});
it("updates data when field is updated", async () => {
await wrapper.find('input[data-test="firstNameTest"]').setValue("Gunnar");
expect(wrapper.vm.firstName).toBe("Gunnar");
});
it("displays 5 error messages when submit is clicked with no data", async () => {
await wrapper.find("button").trigger("click");
expect(wrapper.findAll("li").length).toBe(5);
});
/* it("button click with correct sum", () => {
wrapper.setData({ guess: "15" });
const button = wrapper.find("button");
button.trigger("click");
expect(wrapper.vm.message).toBe("SUCCESS!");
}); */
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`RegisterFormComponent renders correctly 1`] = `
<div
data-v-app=""
>
<section
class="max-w-4xl p-6 mx-auto bg-white rounded-md shadow-md dark:bg-gray-800"
>
<h2
class="text-lg font-semibold text-gray-700 capitalize dark:text-white"
>
Opprett ny bruker
</h2>
<form>
<div
class="grid grid-cols-1 gap-6 mt-4 sm:grid-cols-2"
>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="email"
>
E-mail
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="email"
type="email"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="password"
>
Passord
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="password"
type="password"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="confirmPassword"
>
Bekreft Passord
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="confirmPassword"
type="password"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="firstName"
>
Fornavn
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
data-test="firstNameTest"
id="firstName"
type="text"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="lastName"
>
Etternavn
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="lastName"
type="text"
/>
</div>
<div>
<label
class="text-gray-700 dark:text-gray-200"
for="address"
>
Addresse
</label>
<input
class="block w-full px-4 py-2 mt-2 text-gray-700 bg-white border border-gray-200 rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-blue-400 focus:ring-blue-300 focus:ring-opacity-40 dark:focus:border-blue-300 focus:outline-none focus:ring"
id="address"
type="text"
/>
</div>
</div>
<div
class="flex justify-end mt-6"
>
<button
class="px-6 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded-md hover:bg-gray-600 focus:outline-none focus:bg-gray-600"
type="submit"
>
<div>
Lagre
</div>
</button>
</div>
</form>
</section>
<ul
data-test="errorMessageList"
>
<!--v-if-->
</ul>
</div>
`;
import { getUser } from "@/utils/apiutil";
import axios from "axios";
jest.mock("axios");
describe("testing mocking of apiutil.js", () => {
it("check that existing user returns correctly", async () => {
const expectedResponse = { response: "" };
axios.get.mockImplementation(() =>
Promise.resolve({ data: expectedResponse })
);
const userResponse = await getUser(1);
expect(userResponse).not.toEqual(expectedResponse);
});
it("check that non-existing user returns 404", async () => {
const expectedResponse = { response: "User not found in DB" };
axios.get.mockImplementation(() =>
Promise.resolve({ data: expectedResponse })
);
const userResponse = await getUser(100000);
expect(userResponse).toEqual(expectedResponse);
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment