Skip to content
Snippets Groups Projects
Commit 9e1cb93f authored by VIktorGrev's avatar VIktorGrev
Browse files

feat: Adding upload img and update password

parent 8e452fba
No related branches found
No related tags found
1 merge request!52feat: Adding upload img and update password
Pipeline #280759 failed
...@@ -3,6 +3,6 @@ import { defineConfig } from 'cypress' ...@@ -3,6 +3,6 @@ import { defineConfig } from 'cypress'
export default defineConfig({ export default defineConfig({
e2e: { e2e: {
specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}', specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}',
baseUrl: 'http://localhost:4173' baseUrl: 'http://localhost:5173'
} }
}) })
describe('Login Form Tests', () => {
beforeEach(() => {
cy.visit('/login');
});
it('validates user input and displays error messages', () => {
cy.get('#emailInput input').type('test');
cy.get('#passwordInput input').type('pass');
cy.get('form').submit();
cy.get('#invalid').should('contain', 'Invalid email');
});
it('successfully logs in and redirects to the home page', () => {
cy.visit('/');
cy.url().should('include', '/login');
});
it('successfully logs in and redirects to the home page', () => {
cy.get('#emailInput input').type('user@example.com');
cy.get('#passwordInput input').type('John1');
cy.get('form').submit();
cy.wait(1000);
cy.url().should('include', '/');
});
it('shows an error message on login failure', () => {
cy.get('#emailInput input').type('wrong@example.com');
cy.get('#passwordInput input').type('wrongPass1');
cy.get('#confirmButton').click();
cy.wait(1000);
cy.get('[data-cy="error"]').should('contain', 'User not found');
});
});
describe('Login Form Tests', () => {
beforeEach(() => {
cy.visit('/sign-up');
});
it('validates user input and displays error messages', () => {
cy.get('#emailInput input').type('test');
cy.get('#passwordInput input').type('pass');
cy.get('form').submit();
cy.get('#invalid').should('contain', 'Invalid email');
});
it('successfully logs in and redirects to the home page', () => {
cy.visit('/');
cy.url().should('include', '/login');
});
it('successfully logs in and redirects to the home page', () => {
cy.get('#emailInput input').type('user@example.com');
cy.get('#passwordInput input').type('John1');
cy.get('form').submit();
cy.wait(1000);
cy.url().should('include', '/');
});
it('shows an error message on login failure', () => {
// Update the intercept to simulate a login failure
cy.intercept('POST', '/api/login', {
statusCode: 401,
body: { message: 'Invalid credentials' }
}).as('apiLoginFail');
cy.get('#emailInput input').type('wrong@example.com');
cy.get('#passwordInput input').type('wrongPass1');
cy.get('#confirmButton').click();
cy.wait(1000);
cy.get('[data-cy="error"]').should('contain', 'User not found');
});
});
...@@ -15,6 +15,5 @@ import { RouterView } from 'vue-router' ...@@ -15,6 +15,5 @@ import { RouterView } from 'vue-router'
main { main {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-weight: 600; font-weight: 600;
} }
</style> </style>
\ No newline at end of file
...@@ -105,7 +105,7 @@ const handleSubmit = async () => { ...@@ -105,7 +105,7 @@ const handleSubmit = async () => {
/> />
<p>Forgotten password? <RouterLink to="/forgotten-password">Reset password</RouterLink></p> <p>Forgotten password? <RouterLink to="/forgotten-password">Reset password</RouterLink></p>
<p class="text-danger">{{ errorMsg }}</p> <p class="text-danger" data-cy="error">{{ errorMsg }}</p>
<button1 id="confirmButton" type="submit" @click="handleSubmit" button-text="Login"></button1> <button1 id="confirmButton" type="submit" @click="handleSubmit" button-text="Login"></button1>
<SignUpLink/> <SignUpLink/>
</form> </form>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import {useRoute, useRouter} from "vue-router"; import {useRoute, useRouter} from "vue-router";
import {onMounted, ref} from "vue"; import {onMounted, ref} from "vue";
import {UserService} from "@/api"; import {UserService, type ProfileDTO} from "@/api";
let numberOfHistory = 6; let numberOfHistory = 6;
...@@ -12,6 +12,10 @@ let username = ref() ...@@ -12,6 +12,10 @@ let username = ref()
let friend = ref(false) let friend = ref(false)
let profile: ProfileDTO;;
const imageUrl = ref(`../src/assets/userprofile.png`);
let id = ref() let id = ref()
...@@ -33,7 +37,12 @@ onMounted(async () => { ...@@ -33,7 +37,12 @@ onMounted(async () => {
let response = await UserService.getProfile({ let response = await UserService.getProfile({
userId: id.value.id userId: id.value.id
}) })
username.value = response.firstName profile = response;
console.log(profile)
username.value = profile.firstName
if (profile.profileImage){
imageUrl.value = `http://localhost:8080/api/images/${profile.profileImage}`
}
console.log(username) console.log(username)
} catch (error) { } catch (error) {
console.error("Something went wrong getting the profile: ", error) console.error("Something went wrong getting the profile: ", error)
...@@ -69,7 +78,7 @@ function toUpdateUserSettings(){ ...@@ -69,7 +78,7 @@ function toUpdateUserSettings(){
<div class="card"> <div class="card">
<div class="rounded-top text-white d-flex flex-row bg-primary" style="height:200px;"> <div class="rounded-top text-white d-flex flex-row bg-primary" style="height:200px;">
<div class="ms-4 mt-5 d-flex flex-column" style="width: 150px;"> <div class="ms-4 mt-5 d-flex flex-column" style="width: 150px;">
<img src="https://bootdey.com/img/Content/avatar/avatar3.png" alt="Generic placeholder image" <img :src="imageUrl" alt="Generic placeholder image"
class="img-fluid img-thumbnail mt-4 mb-2" style="width: 150px; z-index: 1"> class="img-fluid img-thumbnail mt-4 mb-2" style="width: 150px; z-index: 1">
<button v-if="!friend" type="button" data-mdb-button-init data-mdb-ripple-init class="btn btn-outline-primary" <button v-if="!friend" type="button" data-mdb-button-init data-mdb-ripple-init class="btn btn-outline-primary"
data-mdb-ripple-color="dark" style="z-index: 1;" @click="addFriend"> data-mdb-ripple-color="dark" style="z-index: 1;" @click="addFriend">
......
...@@ -9,6 +9,9 @@ let cardTitles = ["Spain tour", "Food waste", "Coffee", "Concert", "New book", " ...@@ -9,6 +9,9 @@ let cardTitles = ["Spain tour", "Food waste", "Coffee", "Concert", "New book", "
let firstname = ref(""); let firstname = ref("");
let lastname = ref(""); let lastname = ref("");
let imageID = ref(12)
const imageUrl = ref(`http://localhost:8080/api/images/${imageID.value}`);
const router = useRouter(); const router = useRouter();
const userStore = useUserInfoStore(); const userStore = useUserInfoStore();
...@@ -33,7 +36,7 @@ const toUpdateUserSettings = () => { ...@@ -33,7 +36,7 @@ const toUpdateUserSettings = () => {
<div class="card"> <div class="card">
<div class="rounded-top text-white d-flex flex-row bg-primary" style="height:200px;"> <div class="rounded-top text-white d-flex flex-row bg-primary" style="height:200px;">
<div class="ms-4 mt-5 d-flex flex-column" style="width: 150px;"> <div class="ms-4 mt-5 d-flex flex-column" style="width: 150px;">
<img src="https://bootdey.com/img/Content/avatar/avatar3.png" alt="Generic placeholder image" <img :src="imageUrl" alt="Generic placeholder image"
class="img-fluid img-thumbnail mt-4 mb-2" style="width: 150px; z-index: 1"> class="img-fluid img-thumbnail mt-4 mb-2" style="width: 150px; z-index: 1">
<button type="button" data-mdb-button-init data-mdb-ripple-init class="btn btn-outline-primary" <button type="button" data-mdb-button-init data-mdb-ripple-init class="btn btn-outline-primary"
data-mdb-ripple-color="dark" style="z-index: 1;" id="toUpdate" @click="toUpdateUserSettings"> data-mdb-ripple-color="dark" style="z-index: 1;" id="toUpdate" @click="toUpdateUserSettings">
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import BaseInput from '@/components/InputFields/BaseInput.vue'; import BaseInput from '@/components/InputFields/BaseInput.vue';
import { useUserInfoStore } from "@/stores/UserStore"; import { useUserInfoStore } from "@/stores/UserStore";
import { UserService } from '@/api'; import { UserService, ImageService } from '@/api';
import type { UserUpdateDTO } from '@/api'; import type { UserUpdateDTO } from '@/api';
const firstNameRef = ref() const firstNameRef = ref()
...@@ -12,6 +12,9 @@ const passwordRef = ref('') ...@@ -12,6 +12,9 @@ const passwordRef = ref('')
const formRef = ref() const formRef = ref()
let samePasswords = ref(true) let samePasswords = ref(true)
const iconSrc = ref('https://bootdey.com/img/Content/avatar/avatar7.png');
const fileInputRef = ref();
const handleFirstNameInputEvent = (newValue: any) => { const handleFirstNameInputEvent = (newValue: any) => {
firstNameRef.value = newValue firstNameRef.value = newValue
} }
...@@ -21,6 +24,31 @@ const handleSurnameInputEvent = (newValue: any) => { ...@@ -21,6 +24,31 @@ const handleSurnameInputEvent = (newValue: any) => {
surnameRef.value = newValue surnameRef.value = newValue
} }
const triggerFileUpload = () => {
fileInputRef.value.click();
};
const handleFileChange = (event: any) => {
const file = event.target.files[0];
if (file) {
uploadImage(file);
}
};
const uploadImage = async (file: any) => {
const formData = { file: new Blob([file])}
try {
const response = await ImageService.uploadImage({formData});
console.log('Image uploaded:', response);
iconSrc.value = "http://localhost:8080/api/images/" + response;
} catch (error) {
console.error('Failed to upload image:', error);
}
};
async function setupForm() { async function setupForm() {
try { try {
let response = await UserService.getUser(); let response = await UserService.getUser();
...@@ -66,11 +94,10 @@ onMounted(() => { ...@@ -66,11 +94,10 @@ onMounted(() => {
<hr> <hr>
<form @submit.prevent="handleSubmit" novalidate> <form @submit.prevent="handleSubmit" novalidate>
<div class="user-avatar"> <div class="user-avatar">
<img id="icon" src="https://bootdey.com/img/Content/avatar/avatar7.png" alt="Maxwell Admin"> <input type="file" ref="fileInputRef" @change="handleFileChange" accept=".jpg, .jpeg, .png" style="display: none;" />
</div> <img :src="iconSrc" alt="User Avatar">
<div class="btn">
<div class="mt-2"> <div class="mt-2">
<span class="btn btn-primary"><img src="@/assets/icons/download.svg"></span> <button type="button" class="btn btn-primary" @click="triggerFileUpload"><img src="@/assets/icons/download.svg"> Upload Image</button>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
......
...@@ -2,22 +2,76 @@ ...@@ -2,22 +2,76 @@
<div class="tab-pane active" id="security"> <div class="tab-pane active" id="security">
<h6>SECURITY SETTINGS</h6> <h6>SECURITY SETTINGS</h6>
<hr> <hr>
<form> <form @submit.prevent="handleSubmit" novalidate>
<div class="form-group"> <div class="form-group">
<label class="d-block">Change Password</label> <label class="d-block">Change Password</label>
<input type="text" class="form-control" placeholder="Enter your old password"> <BaseInput :model-value="oldPasswordRef" @input-change-event="handleOldPasswordInputEvent"
<input type="text" class="form-control mt-3" placeholder="New password"> id="passwordInput-change" input-id="password-old" type="password"
<input type="text" class="form-control mt-3 mb-2" placeholder="Confirm new password"> pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,16}" label="Old Password" placeholder="Enter password"
invalid-message="Password must be between 4 and 16 characters and contain one capital letter, small letter and a number" />
<BaseInput :model-value="newPasswordRef" @input-change-event="handleNewPasswordInputEvent"
id="passwordInput-change" input-id="password-new" type="password"
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,16}" label="New Password" placeholder="Enter password"
invalid-message="Password must be between 4 and 16 characters and contain one capital letter, small letter and a number" />
<BaseInput :model-value="confirmPasswordRef" @input-change-event="handleConfirmPasswordInputEvent"
id="passwordInput-change" input-id="password-confirm" type="password"
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,16}" label="Confirm New Password" placeholder="Enter password"
invalid-message="Password must be between 4 and 16 characters and contain one capital letter, small letter and a number" />
</div> </div>
<button type="button" class="btn btn-primary">Update Password</button> <button type="submit" class="btn btn-primary">Update Password</button>
<button type="reset" class="btn btn-light">Reset Changes</button> <button type="reset" class="btn btn-light">Reset Changes</button>
</form> </form>
<hr> <hr>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
</script> import { ref } from 'vue'
import BaseInput from '@/components/InputFields/BaseInput.vue'
import { type PasswordUpdateDTO, UserService } from '@/api'
const oldPasswordRef = ref('');
const newPasswordRef = ref('');
const confirmPasswordRef = ref('');
const handleOldPasswordInputEvent = (newValue: any) => {
oldPasswordRef.value = newValue
}
const handleNewPasswordInputEvent = (newValue: any) => {
newPasswordRef.value = newValue
}
const handleConfirmPasswordInputEvent = (newValue: any) => {
confirmPasswordRef.value = newValue
}
<style scoped> const handleSubmit = async () => {
if (newPasswordRef.value !== confirmPasswordRef.value) {
console.error('Passwords do not match')
return
}
const updateUserPayload: PasswordUpdateDTO = {
oldPassword: oldPasswordRef.value,
newPassword: newPasswordRef.value,
};
try {
const response = UserService.updatePassword({ requestBody: updateUserPayload })
console.log(response)
} catch (err) {
console.error(err)
}
}
</script>
</style> <style scoped></style>
\ No newline at end of file \ No newline at end of file
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