diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..89d533e50ec3edaf1b3916b80bebdb35ddf9eae8 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,45 @@ +# This file is a template, and might need editing before it works on your project. +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml + +# This is a sample GitLab CI/CD configuration file that should run without any modifications. +# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts, +# it uses echo commands to simulate the pipeline execution. +# +# A pipeline is composed of independent jobs that run scripts, grouped into stages. +# Stages run in sequential order, but jobs within stages run in parallel. +# +# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages + +stages: # List of stages for jobs, and their order of execution + - build + - test + - deploy + +build-job: # This job runs in the build stage, which runs first. + stage: build + script: + - echo "Compiling the code..." + - echo "Compile complete." + +unit-test-job: # This job runs in the test stage. + stage: test # It only starts when the job in the build stage completes successfully. + script: + - echo "Running unit tests... This will take about 60 seconds." + - sleep 60 + - echo "Code coverage is 90%" + +lint-test-job: # This job also runs in the test stage. + stage: test # It can run at the same time as unit-test-job (in parallel). + script: + - echo "Linting code... This will take about 10 seconds." + - sleep 10 + - echo "No lint issues found." + +deploy-job: # This job runs in the deploy stage. + stage: deploy # It only runs when *both* jobs in the test stage complete successfully. + script: + - echo "Deploying application..." + - echo "Application successfully deployed." diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue index 44d12c8accba3203d5ace551cf67685c856f6cf0..4f8b255939253b9470ee2170e24759f931c0d5fa 100644 --- a/src/components/LoginForm.vue +++ b/src/components/LoginForm.vue @@ -1,201 +1,162 @@ -<template> - <div class="loginForm"> - <v-img :src="require('../assets/logo3.svg')" class="image" contain /> - <form @submit.prevent="onSubmit"> - <div class="inputFields"> - <div :class="{ error: v$.user.email.$errors.length }"> - <br /><label class="label" id="emailLabelId">E-post </label><br /> - <input - class="loginInputs" - type="email" - v-model="v$.user.email.$model" - /> - - <!-- error message --> - <div - class="input-errors" - v-for="(error, index) of v$.user.email.$errors" - :key="index" - > - <div class="error-msg" v-show="showError" id="emailErrorId"> - {{ error.$message }} - </div> - </div> - </div> - - <!-- password --> - <div :class="{ error: v$.user.password.$errors.length }"> - <br /><label class="label" id="passwordLabelId">Passord </label><br /> - <input - class="loginInputs" - type="password" - v-model="v$.user.password.$model" - /> - - <!-- error message --> - <div - class="input-errors" - v-for="(error, index) of v$.user.password.$errors" - :key="index" - > - <div class="error-msg" v-show="showError" id="passwordErrorId"> - {{ error.$message }} - </div> - </div> - - <!-- Link to forgot password page will be added here --> - <br /><a href="url" id="forgottenPasswordLink">Glemt passord</a> - </div> - </div> - - <div class="buttonLink"> - <!-- Submit Button --> - <div class="buttons-w"> - <br /><br /><button v-on:click="loginClicked" class="loginButton"> - Logg inn - </button> - - <!-- Link to register new user page will be added here --> - <br /><a id="newUserLink" href="url">Ny bruker</a> - - <p id="messageUser">{{ message }}</p> - </div> - </div> - </form> - </div> -</template> - -<script> -import useVuelidate from "@vuelidate/core"; -import { required, email, minLength, helpers } from "@vuelidate/validators"; -import { doLogin } from "@/utils/apiutil"; -import { mapState } from "vuex"; - -export default { - name: "LoginForm.vue", - - setup() { - return { v$: useVuelidate() }; - }, - - validations() { - return { - user: { - email: { - required, - email: helpers.withMessage(`E-posten er ugyldig`, email), - }, - password: { - required, - min: helpers.withMessage( - ({ $params }) => `Passordet må inneholde minst ${$params.min} tegn`, - minLength(8) - ), - }, - }, - }; - }, - - computed: mapState({ - token: (state) => state.user.token, - }), - - data() { - return { - message: "", - user: { - email: "", - password: "", - }, - showError: false, - }; - }, - - methods: { - async loginClicked() { - this.showError = true; - const loginRequest = { - email: this.user.email, - password: this.user.password, - }; - const loginResponse = await doLogin(loginRequest); - - if (loginResponse === "Failed login") { - this.message = "kunne ikke logge inn"; - this.$store.commit('logout'); - return; - } - - this.$store.commit("saveToken", loginResponse); - console.log(loginResponse); - }, - }, -}; -</script> - -<style scoped> -.loginForm { - background-color: white; - border-radius: 10px; - margin: auto; - width: 80%; - margin-top: 20%; - justify-content: center; - padding: 10px; - font-size: 18px; -} -.label { - float: left; - margin-left: 5%; -} -.loginInputs { - background-color: #c4c4c4; - border-radius: 5px; - width: 90%; - height: 40px; - padding: 5px; -} -.loginButton { - width: 55%; - height: 50px; - background-color: #1071b8; - color: white; - border-radius: 10px; - justify-content: center; - text-align: center; - margin: auto; - font-size: 25px; - margin-bottom: 20px; -} -.loginButton:disabled { - opacity: 50%; - cursor: not-allowed; -} -.buttonLink { - margin: auto; - text-align: center; - margin-bottom: 40px; -} -.image { - width: 45%; - margin: auto; - margin-top: 20px; -} -#forgottenPasswordLink { - float: right; - margin: 10px 5% 0 0; -} - -#newUserLink { - text-decoration: none; - margin-bottom: 40px; -} -.inputFields { - margin: auto; - text-align: center; -} - -.input-errors { - color: red; -} -</style> +<template> + <div> + <v-col + align="center" + justify="space-around" + > + <v-img + max-width="45%" + :src="require('../assets/logo3.svg')" + align="center" + ></v-img> + </v-col> + + <v-form + ref="form" + v-model="valid" + lazy-validation + > + <v-col> + <v-text-field + v-model="user.email" + :rules="emailRules" + label="E-mail" + required + ></v-text-field> + </v-col> + + + <v-col + align="right" + > + <v-text-field + v-model="user.password" + :append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'" + :rules="[rules.required, rules.min]" + :type="showPassword ? 'text' : 'password'" + name="input-10-1" + label="Password" + counter + @click:append="showPassword = !showPassword" + ></v-text-field> + + <div class="text-decoration-underline mt-n4 mr-10"> + Glemt passord + </div> + </v-col> + + + + <v-col + align="center" + justify="space-around" + > + <v-btn + :disabled="!valid" + color="success" + class="mb-4 mt-4" + width="50%" + height="40px" + @click="loginClicked" + > + Logg inn + </v-btn> + + <div> + <a + href="/" + class="text-decoration-none" + >Ny bruker</a> + </div> + </v-col> + + + + </v-form> + </div> + +</template> + +<script> +import useVuelidate from "@vuelidate/core"; +import { required, email, minLength, helpers } from "@vuelidate/validators"; +import { doLogin } from "@/utils/apiutil"; +import { mapState } from "vuex"; + +export default { + name: "LoginForm.vue", + + setup() { + return { v$: useVuelidate() }; + }, + + validations() { + return { + user: { + email: { + required, + email: helpers.withMessage(`E-posten er ugyldig`, email), + }, + password: { + required, + min: helpers.withMessage( + ({ $params }) => `Passordet må inneholde minst ${$params.min} tegn`, + minLength(8) + ), + }, + }, + }; + }, + + computed: mapState({ + token: (state) => state.user.token, + }), + + data() { + return { + message: "", + user: { + email: "", + password: "", + }, + + showPassword: false, + valid : true, + emailRules: [ + v => !!v || 'E-mail is required', + v => /.+@.+\..+/.test(v) || 'E-mail must be valid', + ], + rules: { + required: value => !!value || 'Required.', + min: v => v.length >= 8 || 'Min 8 characters', + }, + }; + }, + + methods: { + async loginClicked() { + console.log(this.user.email + " " + this.user.password); + this.showError = true; + const loginRequest = { + email: this.user.email, + password: this.user.password, + }; + const loginResponse = await doLogin(loginRequest); + + if (loginResponse === "Failed login") { + this.message = "kunne ikke logge inn"; + this.$store.commit('logout'); + return; + } + + this.$store.commit("saveToken", loginResponse); + console.log(loginResponse); + }, + + + validate () { + this.$refs.form.validate() + }, + }, +}; +</script> diff --git a/src/router/index.js b/src/router/index.js index 1de09f0e8fb51580cce0f36e6de4b9fe5edcdb8e..3e9afc866ddd3b838a5720151bca3d7cd89f819a 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,5 +1,7 @@ import { createRouter, createWebHistory } from "vue-router"; import HomeView from "../views/HomeView.vue"; +import LoginView from "../views/LoginView.vue"; + const routes = [ { @@ -17,7 +19,6 @@ const routes = [ import(/* webpackChunkName: "about" */ "../views/AboutView.vue"), }, { -<<<<<<< HEAD path: "/register", name: "register", // route level code-splitting @@ -25,12 +26,13 @@ const routes = [ // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "register" */ "../views/RegisterView.vue"), -======= - path: "/", - name: "loginView", - component: () => import("../views/LoginView.vue"), ->>>>>>> main }, + { + path: "/", + name: "login", + component: LoginView, + } + ]; const router = createRouter({ diff --git a/tests/unit/LoginFormComponentTest.spec.js b/tests/unit/LoginFormComponentTest.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..1f892f5e720420f3aae91f6f3716955cd3b7daf2 --- /dev/null +++ b/tests/unit/LoginFormComponentTest.spec.js @@ -0,0 +1,16 @@ +import { shallowMount } from "@vue/test-utils"; +import LoginForm from "@/components/LoginForm"; + +describe("Tests labels in LoginForm component", () => { + it("checks the E-post label", () => { + const wrapper = shallowMount(LoginForm); + + expect(wrapper.find('#emailLabelId').text()).toMatch("E-post"); + }); + + it("checks the password label", () => { + const wrapper = shallowMount(LoginForm); + + expect(wrapper.find('#passwordLabelId').text()).toMatch("Passord"); + }); +});