Skip to content
Snippets Groups Projects
Commit 21490b5f authored by Ingrid Martinsheimen Egge's avatar Ingrid Martinsheimen Egge :cow2:
Browse files

merge with main

parents 541fceb8 39847f2e
No related branches found
No related tags found
1 merge request!21Merge profilinnstillinger into main
Showing
with 2622 additions and 189 deletions
describe('Correct navigation links', () => {
<<<<<<< HEAD
it("is sent to error page if page does not exist", () => {
cy.visit('/qwerty')
cy.contains('#msg', '404')
......@@ -22,4 +23,10 @@ describe('Navbar on all pages', () => {
cy.get('nav').should('exist')
})
/*TODO: other pages*/
=======
/*TODO*/
})
describe('Navbar on all pages', () => {
/*TODO*/
>>>>>>> main
})
\ No newline at end of file
describe('Cannot register with wrong fields', () => {
it('passes', () => {
cy.visit('http://localhost:4173/registerAccount')
cy.get('#register-button').trigger('click')
cy.get('#error-message').contains("Vennligst fyll inn alle feltene")
cy.get('#email-input').type('en ugyldig epost')
cy.get('#register-button').trigger('click')
cy.get('#error-message').contains("Vennligst fyll inn alle feltene")
cy.get('#password-input').type('hei')
cy.get('#register-button').trigger('click')
cy.get('#error-message').contains("Vennligst fyll inn alle feltene")
})
})
\ No newline at end of file
This diff is collapsed.
......@@ -11,19 +11,23 @@
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' :4173 'cypress run --e2e'"
},
"dependencies": {
"@iconify/vue": "^4.1.1",
"@iconify/iconify": "^3.1.0",
"jwt-decode": "^3.1.2",
"pinia": "^2.0.28",
"pinia-plugin-persistedstate": "^3.1.0",
"sass": "^1.62.0",
"pinia-plugin-persistedstate": "^3.1.0",
"vue": "^3.2.45",
"vue-router": "^4.1.6"
},
"devDependencies": {
"@iconify/vue": "^4.1.1",
"@pinia/testing": "^0.0.16",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/test-utils": "^2.2.6",
"cypress": "^12.0.2",
"jsdom": "^20.0.3",
"node-sass": "^8.0.0",
"sass-loader": "^13.2.2",
"start-server-and-test": "^1.15.2",
"vite": "^4.0.0",
"vitest": "^0.25.6"
......
<script setup>
import { RouterLink, RouterView } from 'vue-router'
import Navbar from "@/components/Navbar.vue";
</script>
<template>
......
......@@ -29,7 +29,6 @@ a,
#app {
display: grid;
/*grid-template-columns: 1fr 1fr;*/
padding: 0 2rem;
}
}
......@@ -25,8 +25,13 @@
</RouterLink>
</li>
<li>
<<<<<<< HEAD
<RouterLink :to="'/profileSettings'" :aria-label="'link to settings page'">
<Icon id="settingsIcon" icon="mdi:cog" :color="iconColor" :style="{ fontSize: iconSize }"/>
=======
<RouterLink :to="'/'" :aria-label="'link to settings page'">
<Icon icon="mdi:cog" :color="iconColor" :style="{ fontSize: iconSize }"/>
>>>>>>> main
</RouterLink>
</li>
</ul>
......@@ -48,30 +53,34 @@ export default {
return `32px`;
},
logoSize() {
return '62px';
return '52px';
}
}
}
</script>
<style scoped lang="scss">
#logoContainer {
background-color: white;
border-radius: 50%;
display: flex;
justify-content: center;
align-items:center;
}
#logoContainer img{
width:70%;
}
nav {
z-index: 999;
position: fixed;
top: auto;
bottom: 0;
width: 100%;
background-color: base.$green;
margin:0;
padding:0;
z-index: 999;
padding:5px;
}
ul {
......@@ -84,8 +93,6 @@ ul {
margin-left: 1em;
}
@media only screen and (min-width: base.$desktop-min){
nav {
top:0;
......
......@@ -9,8 +9,8 @@ describe('Navbar', () => {
expect(wrapper.findAll('RouterLink').length).toBe(5)
})
it('contains 5 icons', () => {
it('contains 4 icons', () => {
const wrapper = mount(Navbar)
const icons = wrapper.findAllComponents(Icon)
expect(icons).toHaveLength(5)})
expect(icons).toHaveLength(4)})
})
......@@ -4,6 +4,7 @@ import MissingPage from "@/views/MissingPage.vue";
import HomeView from '../views/HomeView.vue'
import LoginView from '../views/LoginView.vue'
import SelectProfileView from '../views/SelectProfileView.vue'
import RegisterAccountView from '../views/RegisterAccountView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
......@@ -23,6 +24,12 @@ const router = createRouter({
name: "selectProfile",
component: SelectProfileView
},
{
path: '/registerAccount',
name: 'registerAccount',
component: RegisterAccountView
},
{
path: '/profileSettings',
name: 'profileSettings',
......
......@@ -3,8 +3,9 @@ export const useAuthStore = defineStore("auth", {
state: () => {
return {
token: "",
user: {},
account: {},
profile: {},
profiles: []
};
},
persist: {
......@@ -19,8 +20,8 @@ export const useAuthStore = defineStore("auth", {
setToken(token) {
this.token = token;
},
setUser(user) {
this.user = user;
setAccount(account) {
this.account = account;
},
logout() {
this.$reset();
......@@ -28,6 +29,9 @@ export const useAuthStore = defineStore("auth", {
setProfile(profile) {
this.profile = profile;
},
setProfiles(profiles) {
this.profiles = profiles;
},
updateProfile(name, image, isRestricted){
this.profile.name = name;
this.profile.profileImageUrl = image;
......
/*APP COLORS*/
$green: #00663C;
$light-green: hsla(160, 100%, 37%, 1);
$white:#FFFFFF;
$grey:#D9D9D9;
$red:#EE6D6D;
......
......@@ -17,6 +17,7 @@ export const API = {
login: async (request) => {
const authStore = useAuthStore();
let token;
authStore.logout(); //in case someone for some reason is logged in
return axios.post(
`${import.meta.env.VITE_BACKEND_URL}/login`,
......@@ -25,11 +26,14 @@ export const API = {
.then(async (response) => {
token = response.data;
const id = (jwt_decode(token)).id;
return API.getAccount(id, token)
.then((user) => {
authStore.setUser(user);
authStore.setAccount(user);
authStore.setToken(token);
API.getProfiles()
.then(response => {authStore.setProfiles(response)})
.catch(() => {throw new Error()})
return;
})
.catch(() => {
......@@ -69,7 +73,7 @@ export const API = {
.then((response) => {
authStore.setProfile(response.data)
router.push("/")
})
.catch(() => {
throw new Error("Profile not found or not accessible")
......@@ -78,7 +82,7 @@ export const API = {
},
// Sends the user into the "register profile" view
// Sends the user into the "register profile" view
addProfile: async () => {
console.log("todo");
},
......@@ -90,20 +94,30 @@ export const API = {
throw new Error();
}
return axios.get(import.meta.env.VITE_BACKEND_URL + '/profile', {
headers: { Authorization: "Bearer " + authStore.token },
},
)
.then(response => {
console.log(response.data)
return response.data
}).catch(() => {
throw new Error();
});
},
// Registers a new account and logs into it
addAccount: async (request) => {
const authStore = useAuthStore();
axios.post(import.meta.env.VITE_BACKEND_URL + '/account', request)
.then(() => {
API.login({email: request.email, password: request.password})
.catch(err => {console.log(err)})
})
.catch(() => {throw new Error()})
}
},
/**
* Deletes account from the
* @param id
......@@ -182,4 +196,4 @@ export const API = {
}
}
\ No newline at end of file
<script setup>
<script>
import { useAuthStore } from "@/stores/authStore.js";
import { mapState } from 'pinia'
export default {
data() {
return {
}
},
computed: {
...mapState(useAuthStore, ['profile'])
}
}
</script>
<template>
<main>
<p>HALLO</p>
<div class="content">
<div class="welcome">
<img id="logo" src="../components/icons/logo.png" alt="Logo">
<h1>Velkommen, {{ this.profile.name }}!</h1>
</div>
<div class="gamification">
</div>
<div class="tips">
<img id="tips-img" src="../components/icons/logo.png" alt="Logo">
<p id="tips-text">Her kommer tips du kanskje kan ha nytte av, trykk på meg for å gå til neste tips!</p>
</div>
</div>
</main>
</template>
<style scoped>
.container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
min-width: 300px;
margin-top: 40px;
}
.welcome {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 20px;
}
#logo {
width: 75px;
height: 75px;
}
#tips-img {
width: 40px;
height: 40px;
margin: auto 0;
}
.tips {
display:flex;
padding: 10px;
min-height: 75px;
max-height: 250px;
background-color: rgb(232, 232, 232);
margin-left: 10px;
margin-right: 10px;
}
#tips-text {
overflow: hidden;
padding-left: 10px;
}
@media (min-width: 1024px) {
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
}
}
</style>
......@@ -31,7 +31,7 @@
<div class="login-container">
<img id="logo" src="../components/icons/logo.png" alt="Logo">
<h1>{{ welcomemsg }}</h1>
<form @submit.prevent="login">
<form @submit.prevent>
<div class="field-container">
<label for="email">E-post</label>
<input id="email-input" name="email" type="text" v-model="email" />
......@@ -46,12 +46,12 @@
<button @click="login" id="login-button">Logg inn</button>
</form>
<p><RouterLink to="/newuser">Ny bruker</RouterLink> - <a href="#">Glemt passord?</a></p>
<p><RouterLink to="/registerAccount">Ny bruker</RouterLink> - <a href="#">Glemt passord?</a></p>
</div>
</main>
</template>
<style scoped>
<style lang="scss" scoped>
.login-container {
display: flex;
......@@ -87,7 +87,7 @@ label {
}
#login-button {
background-color: #00663C;
background-color: base.$green;
color: #FFFFFF;
border-radius: 5px;
border-style: none;
......@@ -98,6 +98,11 @@ label {
margin: 20px;
}
#login-button:hover {
background-color: base.$light-green;
}
#logo {
width: 100px;
height: 100px;
......
<template>
<div class="content">
<img id="logo" src="../components/icons/logo.png" alt="Logo">
<h1>Registrer konto</h1>
<form @submit.prevent>
<div class="field-container">
<label for="name">Navn</label>
<input id="input" name="name" type="text" v-model="name" />
</div>
<div class="field-container">
<label for="email">E-post</label>
<input id="email-input" name="email" type="email" v-model="email" />
</div>
<div class="field-container">
<label for="password">Passord</label>
<input id="password-input" name="password" type="password" v-model="password" />
</div>
<p id="error-message">{{ errormsg }}</p>
<button @click="register" id="register-button">Registrer</button>
</form>
</div>
</template>
<script>
import { API } from '@/util/API.js';
import router from '@/router/index.js';
export default {
data() {
return {
email: "",
name: "",
password: "",
errormsg: ""
}
},
methods: {
register() {
if (this.name === "" || this.password === "") {
this.errormsg = "Vennligst fyll inn alle feltene"
}
else {
API.addAccount({firstname: this.name, email: this.email, password: this.password}).then(() => {
router.push("/selectProfile")
})
.catch(() => {
throw new Error()
});
}
}
}
}
</script>
<style scoped>
.content {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
min-width: 300px;
margin-top: 40px;
}
form {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.field-container {
padding: 10px;
display: flex;
flex-direction: column;
}
input {
height: 40px;
font-size: 16px;
padding-left: 10px;
}
label {
font-size: 18px;
}
#register-button {
background-color: #00663C;
color: #FFFFFF;
border-radius: 5px;
border-style: none;
width: 150px;
height: 40px;
font-size: 18px;
font-weight: bold;
margin: 20px;
}
#register-button:hover {
background-color: #04be80;
}
#logo {
width: 100px;
height: 100px;
}
@media (min-width: 1024px) {
.login-container {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
}
}
</style>
\ No newline at end of file
<script>
import { API } from '@/util/API.js';
import { API } from '@/util/API.js';
import { useAuthStore } from "@/stores/authStore.js";
import { mapState } from 'pinia'
export default {
data() {
return {
profiles: []
}
},
computed: {
...mapState(useAuthStore, ['profiles'])
},
methods: {
// Sends the user into the home page logged in as the profile they clicked on
selectProfile(id) {
......@@ -21,7 +28,6 @@ import { API } from '@/util/API.js';
// Receives all profiles from this user
async getProfiles() {
await API.getProfiles()
.then(response => {this.profiles = response})
.catch(() => new Error());
}
},
......@@ -55,7 +61,7 @@ import { API } from '@/util/API.js';
</template>
<style scoped>
<style scoped lang="scss">
.container {
display: flex;
......@@ -87,7 +93,7 @@ import { API } from '@/util/API.js';
}
.icon:hover {
background-color: #d5d5d5;
background-color: base.$grey;
border-radius: 10%;
}
......@@ -95,6 +101,7 @@ import { API } from '@/util/API.js';
height: 130px;
width: 130px;
border-radius: 50%;
padding: 10px;
}
......
import { describe, it, expect, vi } from 'vitest'
import { createTestingPinia } from '@pinia/testing'
import { mount } from '@vue/test-utils'
import HomeView from '../HomeView.vue'
describe('Home', () => {
it('renders properly', () => {
const wrapper = mount(HomeView, {
global: {
plugins: [createTestingPinia({
createSpy: vi.fn,
})],
},
})
expect(wrapper.text()).toContain('Velkommen,')
})
it('tips renders properly', () => {
const wrapper = mount(HomeView, {
global: {
plugins: [createTestingPinia({
createSpy: vi.fn,
})],
},
})
expect(wrapper.find('#tips-text').text()).not.toBe("");
})
})
\ No newline at end of file
......@@ -7,10 +7,11 @@ describe('Login', () => {
it('renders properly', () => {
const wrapper = mount(LoginView)
expect(wrapper.text()).toContain('E-post')
expect(wrapper.text()).toContain('Passord')
})
it('login button exists', () => {
const wrapper = mount(LoginView)
wrapper.find('#login-button').exists()
expect(wrapper.find('#login-button').exists()).toBe(true);
})
})
\ No newline at end of file
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import RegisterAccountView from '../RegisterAccountView.vue'
describe('Login', () => {
it('renders properly', () => {
const wrapper = mount(RegisterAccountView)
expect(wrapper.text()).toContain('E-post')
expect(wrapper.text()).toContain('Navn')
expect(wrapper.text()).toContain('Passord')
})
it('register button exists', () => {
const wrapper = mount(RegisterAccountView)
expect(wrapper.find('#register-button').exists()).toBe(true)
})
})
\ No newline at end of file
import { describe, it, expect } from 'vitest'
import { describe, it, expect, vi } from 'vitest'
import { createTestingPinia } from '@pinia/testing'
import { mount } from '@vue/test-utils'
import SelectProfileView from '../SelectProfileView.vue'
describe('Select profile', () => {
it('renders properly', () => {
const wrapper = mount(SelectProfileView)
const wrapper = mount(SelectProfileView, {
global: {
plugins: [createTestingPinia({
createSpy: vi.fn,
})],
},
})
expect(wrapper.text()).toContain('Hvem bruker appen?')
expect(wrapper.text()).toContain('+')
})
it('loads with one profile', () => {
const wrapper = mount(SelectProfileView, {
global: {
plugins: [createTestingPinia({
createSpy: vi.fn,
})],
},
data() {
return {
profiles: [{
......
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