diff --git a/src/components/ChatComponents/RentalMessage.vue b/src/components/ChatComponents/RentalMessage.vue index df2823fec366f458226cb8c7b5d0bfeb9e701260..c864d36bff56d845b8352e2664afcb4dd4f50ae6 100644 --- a/src/components/ChatComponents/RentalMessage.vue +++ b/src/components/ChatComponents/RentalMessage.vue @@ -100,12 +100,14 @@ export default { null, { headers: tokenHeader() } ); + this.$router.go(0); }, async reject() { await axios.delete( process.env.VUE_APP_BASEURL + `renting/${this.rent.rentId}/delete`, { headers: tokenHeader() } ); + this.$router.go(0); }, async getImage() { let images = await getItemPictures(this.rent.listingId); diff --git a/src/components/RentingComponents/ItemInfo.vue b/src/components/RentingComponents/ItemInfo.vue index 6df66e21241e7b6acf20b4461c491517701633eb..facb3af3ea55fabc7f849432a896efb82066dbaf 100644 --- a/src/components/RentingComponents/ItemInfo.vue +++ b/src/components/RentingComponents/ItemInfo.vue @@ -76,6 +76,7 @@ <p class="text-xl font-semibold text-gray-900"> Total pris: {{ totPrice }} kr </p> + <p v-if="error" style="color: red">Dato er påkrevd</p> <button class="px-4 py-2 font-medium tracking-wide text-white capitalize transition-colors duration-200 transform bg-gray-500 rounded-md focus:outline-none focus:ring focus:ring-opacity-80" v-bind:class="{ colorChange: allowForRent }" @@ -108,6 +109,7 @@ export default { data() { return { confirm: false, + error: false, item: { listingID: 0, title: "", @@ -155,6 +157,8 @@ export default { if (this.allowForRent) { this.confirm = true; this.createPushItem(); + } else { + this.error = true; } }, createPushItem() { @@ -214,11 +218,12 @@ export default { this.rentingEndDate = dateOfsomthing.endDate; this.calculateTotPrice(); this.allowForRent = true; + this.error = false; } }, calculateTotPrice() { let amountOfDays = this.rentingEndDate - this.rentingStartDate; - amountOfDays = amountOfDays / 86400000; + amountOfDays = Math.ceil(amountOfDays / 86400000); this.totPrice = this.item.pricePerDay * amountOfDays; }, async getUserRating() { diff --git a/src/components/UserProfileComponents/RatingComponents/Rating.vue b/src/components/UserProfileComponents/RatingComponents/Rating.vue index 88342d9131a8427ce73b28c196447c2598dc713a..dc8eee3eefd29f3f2014d8babf87ea3a08cd47d5 100644 --- a/src/components/UserProfileComponents/RatingComponents/Rating.vue +++ b/src/components/UserProfileComponents/RatingComponents/Rating.vue @@ -1,4 +1,5 @@ <template> + <!-- Shows rating, if no rating found, tells the user that no rating is registered --> <ul v-if="isNaN(rating)" class="flex justify-center"> <li> <p class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400"> @@ -29,12 +30,6 @@ ></path> </svg> </li> - <li> - <!-- Trenger vi å vise i tekst når vi har stjerner? --> - <!-- <p class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400"> - {{ rating }} out of 5 - </p> --> - </li> </ul> </template> @@ -46,6 +41,7 @@ export default { ratingType: String, }, methods: { + //Fills the rating stars getFill(i) { if (i <= Math.round(this.rating)) { return "w-5 h-5 text-warn-medium"; diff --git a/src/components/UserProfileComponents/RatingComponents/RatingModal.vue b/src/components/UserProfileComponents/RatingComponents/RatingModal.vue index 419d4f9e4d6ec8cdb2545efc701f4f602ae26bf3..1fa16c24680a9564da49013a5d9c42738a19c974 100644 --- a/src/components/UserProfileComponents/RatingComponents/RatingModal.vue +++ b/src/components/UserProfileComponents/RatingComponents/RatingModal.vue @@ -1,5 +1,5 @@ <template> - <!-- Main modal --> + <!-- Main modal for rating --> <div v-if="visible" class="fixed grid place-items-center bg-gray-600 bg-opacity-50 top-0 left-0 right-0 z-50 w-full overflow-x-hidden overflow-y-auto inset-0 h-full" @@ -58,6 +58,7 @@ /> </div> + <!-- 5 rating stars --> <div class="flex items-center justify-center mb-8"> <svg class="w-10 h-10 text-warn cursor-pointer" @@ -121,13 +122,13 @@ </svg> </div> + <!-- Button for submitting the rating --> <div class="flex justify-center mb-4"> <Button :text="'Send en vurdering'" @click="sendRating"></Button> </div> <!-- Modal footer --> <div class="rounded-b border-t border-gray-200 dark:border-gray-600"> - <!-- Slot: Add any html you want here --> <slot /> </div> </div> @@ -166,6 +167,7 @@ export default { Button, }, methods: { + //A method for setting the rating setRating(ratingNumber) { this.score = ratingNumber; for (let i = 0; i < 5; i++) { @@ -179,6 +181,10 @@ export default { close() { this.$emit("close"); }, + /** + * A method for sending the rating when button is clicked. + * It posts the rating to the db. + */ async sendRating() { const ratingInfo = { score: this.score, diff --git a/src/components/UserProfileComponents/RentHistoryComponents/RentHistoryItem.vue b/src/components/UserProfileComponents/RentHistoryComponents/RentHistoryItem.vue index c05823dcee3cd9a5bdcaf4661ec727d755346432..c3745f5821a630462d7f553456931eca1e3ba742 100644 --- a/src/components/UserProfileComponents/RentHistoryComponents/RentHistoryItem.vue +++ b/src/components/UserProfileComponents/RentHistoryComponents/RentHistoryItem.vue @@ -1,16 +1,24 @@ <template> + <!-- A card for a rent history containing the item's title, + name of the person who rented it, dates/renting period, and a button + to rate if not already rated it. --> <div class="bg-white shadow dark:bg-gray-800 select-none cursor-pointer hover:bg-gray-50" > + <!-- Title --> <p class="font-bold mx-4" id="title"> {{ historyItem.listing.title }} </p> + + <!-- Name of the user who rented --> <div class="flex flex-row items-center"> <div class="flex flex-col px-4 flex-1"> <router-link :to="{ path: '/profile/' + user.userId }"> Leid til: {{ user.firstName }} {{ user.lastName }} </router-link> </div> + + <!-- Period it was rented for --> <div class="flex flex-col flex-1"> <div> Fra: @@ -20,6 +28,8 @@ Til: {{ this.getDateString(historyItem.toTime, isMultipleDays) }} </div> </div> + + <!-- Button to rate --> <colored-button v-if="!isRated" :text="'Vurder'" @@ -74,12 +84,16 @@ export default { currentUser() { return parseCurrentUser(); }, + //Checks if the rent period was multiple days or not isMultipleDays() { if (this.historyItem.toTime - this.historyItem.fromTime < 86400000) { return false; } return true; }, + /** + * computed the total price based on how long the renting period was. + */ price() { if (this.isMultipleDays) { let numDays = Math.round( @@ -89,11 +103,17 @@ export default { } return this.historyItem.listing.pricePerDay; }, + /** + * Checks if the user rented its own item + */ currentUserIsRenter() { return this.isCurrentUser(this.historyItem.renterId); }, }, methods: { + /** + * returns a date as a string + */ getDateString(milliseconds) { let today = new Date(); let date = new Date(milliseconds); @@ -111,6 +131,9 @@ export default { return id == this.currentUser.accountId; }, }, + /** + * Gets the user and checks if a rating is saved, before mounting the page. + */ async beforeCreate() { if (this.currentUserIsRenter) { this.user = await userService.getUserFromId( diff --git a/src/components/UserProfileComponents/UserItems.vue b/src/components/UserProfileComponents/UserItems.vue index ac1736b5e713e14625729087f0493c4b859e55bf..9a988f48e44e66bfc65d3f786e5c97ffa56cc91c 100644 --- a/src/components/UserProfileComponents/UserItems.vue +++ b/src/components/UserProfileComponents/UserItems.vue @@ -1,4 +1,6 @@ <template> + <!-- Shows all the items a user has posted with search and pagination. + Includes a dropdown menu for editing or deleting an item. --> <div id="headline" class="text-xl md:text-2xl text-primary-light font-medium"> Mine gjenstander </div> @@ -46,6 +48,8 @@ :item="item" /> </div> + + <!-- Dropdown menu with options for editing an item and deleting an item --> <TripleDotButton class="DotButton" @click="openDotMenu(item)" /> <div @@ -78,6 +82,8 @@ </div> </div> + <!-- A waring asking the user if it is sure it wants to delete the item + with options to go ahead with the deleting or to cancel the delete. --> <CustomFooterModal @close="this.readyToDelete = false" :visible="readyToDelete" @@ -86,14 +92,14 @@ > <div class="flex justify-center p-2"> <ColoredButton - id="#cancelDeleteButton" + id="#cancelDeleteButton1" :text="'Avbryt'" @click="cancelDelete" class="bg-gray-500 m-2" ></ColoredButton> <ColoredButton - id="confirmDeleteButton" + id="confirmDeleteButton1" @click="deleteItem" :text="'Slett'" class="m-2 bg-error-medium" @@ -116,8 +122,9 @@ :item="item" /> </div> - <TripleDotButton class="DotButton" @click="openDotMenu(item)" /> + <!-- Dropdown menu with options for editing an item and deleting an item --> + <TripleDotButton class="DotButton" @click="openDotMenu(item)" /> <div v-show="item.toggle" class="options z-10 w-44 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700" @@ -147,6 +154,9 @@ </ul> </div> </div> + + <!-- A waring asking the user if it is sure it wants to delete the item + with options to go ahead with the deleting or to cancel the delete. --> <CustomFooterModal @close="this.readyToDelete = false" :visible="readyToDelete" @@ -172,6 +182,7 @@ </CustomFooterModal> </div> </div> + <!-- pagination --> <div class="flex justify-center" v-if="showItems"> <PaginationTemplate @@ -220,6 +231,7 @@ export default { search: "", readyToDelete: false, dropdown: false, + //Variables connected to pagination currentPage: 0, pageSize: 12, @@ -227,6 +239,9 @@ export default { }; }, computed: { + /** + * Searchs items based on their title, address and price per day. + */ searchedItems() { let filteredItems = []; @@ -264,7 +279,10 @@ export default { cancelDelete() { this.readyToDelete = false; }, - //Pagination + /** + * 2 methods related to pagination. Updates page and updates + * visible items on page. + */ updatePage(pageNumber) { this.currentPage = pageNumber; this.updateVisibleTodos(); @@ -280,6 +298,7 @@ export default { this.updatePage(this.currentPage - 1); } }, + goToDeleteItem(item) { this.chosenItem = item; this.readyToDelete = true; @@ -288,8 +307,11 @@ export default { await UserService.setListingToDeleted(this.chosenItem); this.$router.go(0); }, + + /** + * This method triggers when search input field is changed + */ searchWritten: function () { - //This method triggers when search input field is changed if (this.search.length > 0) { this.showItems = false; this.showSearchedItems = true; @@ -299,6 +321,11 @@ export default { } }, }, + + /** + * Gets userlistings and prepares the pagination by + * updating the items to be visible. + */ async beforeMount() { await this.getUserListingsFromAPI(); this.updateVisibleTodos(); diff --git a/src/components/UserProfileComponents/UserListItemCard.vue b/src/components/UserProfileComponents/UserListItemCard.vue index 012ba730b0e602dfa17624bbdbed0fbb10c8f0d0..0a6bd91d528dee33666d47cd8498829c6a3bb466 100644 --- a/src/components/UserProfileComponents/UserListItemCard.vue +++ b/src/components/UserProfileComponents/UserListItemCard.vue @@ -1,4 +1,7 @@ <template> + <!-- A card for displaying a user in a list. + Displays a user's profile picture, name, rating and some + buttons based on where the list is being shown. --> <div class="bg-white shadow dark:bg-gray-800 select-none cursor-pointer hover:bg-gray-50 flex items-center p-4" > @@ -95,6 +98,10 @@ export default { rating: Number, }, computed: { + /** + * returns the user's profile picture. If the user does not have any + * profile picture the default profile picture is returned. + */ getProfilePicture() { if (this.user.picture !== "" && this.user.picture != null) { return this.user.picture; @@ -103,18 +110,25 @@ export default { }, }, methods: { + /** + * If chat button clicked, the user's gets redirected to chat page. + */ openChatWithUser() { this.$router.push({ name: "messages", query: { userID: this.user.userId }, }); }, + + /** + * Admin related methods for kicking a user from a community, + * accepting and rejecting a user's join community request + */ kickUserFromCommunity() { CommunityAdminService.removeUserFromCommunity( this.communityID, this.user.userId ); - //Find a better way to do this window.location.reload(); }, acceptMemberRequest() { diff --git a/src/components/UserProfileComponents/UserProfile.vue b/src/components/UserProfileComponents/UserProfile.vue index 186f986c9513f9ed8d0cc44f36e23c4572ef4581..07b2a291268ba4656bda253878c8e80aec286374 100644 --- a/src/components/UserProfileComponents/UserProfile.vue +++ b/src/components/UserProfileComponents/UserProfile.vue @@ -1,12 +1,17 @@ <template> + <!-- User profile with the logged in user's info and a dropdown menu--> <div class="w-full max-w-xl m-auto md:ring-1 ring-gray-300 overflow-hidden rounded-xl p-4" > + <!-- A warning when deleting a user account to ask the user + if it is sure --> <DeleteUserModal :visible="show" @close="this.show = false" @deleteUser="deleteUser" /> + + <!-- dropdown icon button to toggle(open/close) the dropdown menu --> <div v-show="isCurrentUser" class="float-right px-4 pt-4"> <button id="dropdownDefault" @@ -27,6 +32,8 @@ </svg> </button> + <!-- Dropdown menu containing options for seeing user's items, user's communities, + renting history, logging out, changing password and deleting account --> <div id="dropdown" v-show="dropdown" @@ -84,6 +91,7 @@ </div> </div> + <!-- User info (name, rating and profile picture) --> <div class="flex flex-col items-center pb-10 mt-16 z-5"> <img class="mb-3 w-24 h-24 rounded-full shadow-lg" @@ -95,7 +103,10 @@ </h5> <div> <rating-component :rating="renterRating" :ratingType="'Leietaker'" /> - <RatingComponent :rating="ownerRating" :ratingType="'Utleier'"></RatingComponent> + <RatingComponent + :rating="ownerRating" + :ratingType="'Utleier'" + ></RatingComponent> </div> <div @@ -153,6 +164,9 @@ export default { }, }, methods: { + /** + * Gets the user and it's ratings + */ async getUser() { this.currentUser = await parseCurrentUser(); this.id = await this.$router.currentRoute.value.params.id; @@ -165,7 +179,9 @@ export default { this.user = await getUser(this.id); } let ratingAsOwner = await UserService.getUserRatingAverageOwner(this.id); - let ratingAsRenter = await UserService.getUserRatingAverageRenter(this.id); + let ratingAsRenter = await UserService.getUserRatingAverageRenter( + this.id + ); if (ratingAsOwner >= 0 && ratingAsOwner <= 5) { this.ownerRating = ratingAsOwner; @@ -174,6 +190,10 @@ export default { this.renterRating = ratingAsRenter; } }, + + /** + * Logs out the user and sets token in state to be null. + */ logout() { this.$store.commit("logout"); this.$router.push("/"); diff --git a/src/router/index.js b/src/router/index.js index 9f7228cc993ef163a3a40f9ea84fe3ce57f3bf2d..3ee7a1641ecf3fd9bdad529a64a7098a7b937601 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -3,22 +3,28 @@ import { createRouter, createWebHistory } from "vue-router"; import NotFound from "@/views/NotFound.vue"; /** - * Guards routes. If token is null, no user is logged in and only the - * login page and the register page will be accessible. + * Guards routes. + * If token is null, no user is logged in and only the + * login, register, reset password, help and listing of public groups and items pages will be accessible. + * Without a token the user gets redirected to the login page when trying to access guard routed pages. */ function guardRoute(to, from, next) { let isAuthenticated = store.state.user.token != null; if (isAuthenticated) { next(); // allow to enter route } else { - next("/login"); // go to '/login'; + next("/login"); // go to the login page } } +/** + * Guards routes for administartors. If token is null or + * the user is not the administrator of the group, the user gets sent to the home page. + */ function isAdmin(to, from, next) { if (store.state.user.adminList.includes(parseInt(from.params.communityID))) - next(); - else next("/"); + next(); // allow to enter route + else next("/"); // go to the home page } const routes = [ @@ -28,37 +34,43 @@ const routes = [ component: () => import("../views/CommunityViews/CommunityView.vue"), }, { - path: "/help", - name: "help", - component: () => import("../views/HelpView.vue"), + path: "/community/:communityID", + name: "communityHome", + component: () => import("../views/CommunityViews/CommunityHomeView.vue"), }, { - path: "/profile/:id", - name: "profile", - component: () => import("../views/UserProfileViews/ProfileView.vue"), - beforeEnter: guardRoute, + path: "/community/:communityID/admin", + name: "communityAdminPage", + component: () => import("@/views/CommunityViews/AdminView.vue"), + beforeEnter: [guardRoute, isAdmin], }, { - path: "/profile/history", - name: "history", - component: () => import("../views/UserProfileViews/RentHistoryView.vue"), + path: "/community/:communityID/memberlist", + name: "memberlist", + component: () => import("../views/CommunityViews/MemberListView.vue"), beforeEnter: guardRoute, }, { - path: "/profile/communities", - name: "myCommunities", - component: () => import("../views/UserProfileViews/MyCommunitiesView.vue"), + path: "/community/:communityID/private/join", + name: "communityRequest", + component: () => import("../views/CommunityViews/CommunityRequestView.vue"), beforeEnter: guardRoute, }, { - path: "/register", - name: "register", - component: () => import("../views/UserAuthViews/RegisterView.vue"), + path: "/help", + name: "help", + component: () => import("../views/HelpView.vue"), }, { - path: "/messages", - name: "messages", - component: () => import("../views/ChatViews/ChatView.vue"), + path: "/item/:id/edit", + name: "editItem", + component: () => import("../views/ItemViews/EditItemView.vue"), + beforeEnter: guardRoute, + }, + { + path: "/itempage/:id", + name: "itemInfo", + component: () => import("../views/RentingViews/ItemInfoPageView.vue"), beforeEnter: guardRoute, }, { @@ -67,33 +79,17 @@ const routes = [ component: () => import("../views/UserAuthViews/LoginView.vue"), }, { - path: "/newPassword", - name: "newPassword", - component: () => import("../views/UserAuthViews/NewPasswordView"), + path: "/messages", + name: "messages", + component: () => import("../views/ChatViews/ChatView.vue"), beforeEnter: guardRoute, }, - { - path: "/searchItemList", - name: "searchItemList", - component: () => import("../views/ItemViews/SearchItemListView.vue"), - }, - { - path: "/resetPassword", - name: "resetPassword", - component: () => import("../views/UserAuthViews/ResetPasswordView.vue"), - }, { path: "/newCommunity", name: "newCommunity", component: () => import("../views/CommunityViews/NewCommunityView.vue"), beforeEnter: guardRoute, }, - { - path: "/community/:communityID/memberlist", - name: "memberlist", - component: () => import("../views/CommunityViews/MemberListView.vue"), - beforeEnter: guardRoute, - }, { path: "/newItem", name: "newItem", @@ -101,45 +97,57 @@ const routes = [ beforeEnter: guardRoute, }, { - path: "/community/:communityID", - name: "communityHome", - component: () => import("../views/CommunityViews/CommunityHomeView.vue"), + path: "/newPassword", + name: "newPassword", + component: () => import("../views/UserAuthViews/NewPasswordView"), + beforeEnter: guardRoute, }, { - path: "/community/:communityID/private/join", - name: "communityRequest", - component: () => import("../views/CommunityViews/CommunityRequestView.vue"), + path: "/profile/:id", + name: "profile", + component: () => import("../views/UserProfileViews/ProfileView.vue"), beforeEnter: guardRoute, }, { - path: "/test", - name: "test", - component: () => import("../views/TestView.vue"), + path: "/profile/communities", + name: "myCommunities", + component: () => import("../views/UserProfileViews/MyCommunitiesView.vue"), + beforeEnter: guardRoute, }, { - path: "/community/:communityID/admin", - name: "CommunityAdminView", - component: () => import("@/views/CommunityViews/AdminView.vue"), - beforeEnter: isAdmin, + path: "/profile/history", + name: "history", + component: () => import("../views/UserProfileViews/RentHistoryView.vue"), + beforeEnter: guardRoute, }, { - path: "/itempage/:id", - name: "ItemInfo", - component: () => import("../views/RentingViews/ItemInfoPageView.vue"), - beforeEnter: guardRoute, + path: "/register", + name: "register", + component: () => import("../views/UserAuthViews/RegisterView.vue"), }, { - path: "/item/:id/edit", - name: "editItem", - component: () => import("../views/ItemViews/EditItemView.vue"), + path: "/resetPassword", + name: "resetPassword", + component: () => import("../views/UserAuthViews/ResetPasswordView.vue"), + }, + { + path: "/searchItemList", + name: "searchItemList", + component: () => import("../views/ItemViews/SearchItemListView.vue"), + }, + { + path: "/test", + name: "test", + component: () => import("../views/TestView.vue"), beforeEnter: guardRoute, }, { path: "/user/userItems", - name: "UserItems", + name: "userItems", component: () => import("../views/UserProfileViews/UserItemsView.vue"), beforeEnter: guardRoute, }, + // Make sure it's your last route definition { path: "/:pathMatch(.*)*", name: "not-found", component: NotFound }, ]; diff --git a/src/views/UserProfileViews/MyCommunitiesView.vue b/src/views/UserProfileViews/MyCommunitiesView.vue index e474528640a45dc3254256ee8ef9afef3871f7ea..bee55d4487fbdcfdcdec30435f0ba00f70ca02bb 100644 --- a/src/views/UserProfileViews/MyCommunitiesView.vue +++ b/src/views/UserProfileViews/MyCommunitiesView.vue @@ -1,4 +1,5 @@ <template> + <!-- A view for showing all the communities a user is part of --> <div> <div v-if="loading" class="flex place-content-center p-8"> <LoaderSpinner /> @@ -24,6 +25,7 @@ import CommunityList from "@/components/CommunityComponents/CommunityList.vue"; import CommunityService from "@/services/community.service"; import { UserAddIcon } from "@heroicons/vue/outline"; +import LoaderSpinner from "@/components/BaseComponents/LoaderSpinner"; export default { data() { @@ -35,6 +37,7 @@ export default { components: { CommunityList, UserAddIcon, + LoaderSpinner, }, async beforeCreate() { this.loading = true; diff --git a/src/views/UserProfileViews/ProfileView.vue b/src/views/UserProfileViews/ProfileView.vue index a490eb09dfd233803ffd624b428fa206ec767f63..cbd3a8294c0fb345ddebd3f24fbfc9c87c8fe77a 100644 --- a/src/views/UserProfileViews/ProfileView.vue +++ b/src/views/UserProfileViews/ProfileView.vue @@ -1,4 +1,5 @@ <template> + <!-- A view for the user profile card --> <div> <LargeProfileCard :isCurrentUser="true" class="md:mt-8" /> </div> diff --git a/src/views/UserProfileViews/RentHistoryView.vue b/src/views/UserProfileViews/RentHistoryView.vue index 45e7859306a8e3ede90ca30d039cb1f7840bb777..6046627d069e7410e2c23a64d5181a5bc74399b5 100644 --- a/src/views/UserProfileViews/RentHistoryView.vue +++ b/src/views/UserProfileViews/RentHistoryView.vue @@ -1,4 +1,5 @@ <template> + <!-- A view for showing rating and rent history --> <rating-modal :visible="modal.show" :name="modal.name" @@ -58,9 +59,6 @@ export default { fullHistory.sort(compareHistoryItems); return fullHistory; }, - hasNoHistory() { - return this.renterHistory.length == 0 && this.ownerHistory.length == 0; - }, }, methods: { openModal(modal) { diff --git a/src/views/UserProfileViews/UserItemsView.vue b/src/views/UserProfileViews/UserItemsView.vue index 8ac137d470d2dd6ed213d18d9c778c1da872996b..6cf4fc43868be784e6e3cccf9cb02b2059d793d4 100644 --- a/src/views/UserProfileViews/UserItemsView.vue +++ b/src/views/UserProfileViews/UserItemsView.vue @@ -1,4 +1,5 @@ <template> + <!-- A view for showing all the items the user has posted/added --> <user-items> </user-items> </template> @@ -11,5 +12,3 @@ export default { }, }; </script> - -<style></style> diff --git a/tests/unit/component-tests/ChatComponentsTest/RentalMessage.spec.js b/tests/unit/component-tests/ChatComponentsTest/RentalMessage.spec.js index c08130180924969b4b57b9c0a70eb2a50e3e35c2..9c520a8c89f7a1157b68f29f9c2ed77ba2fbf9a1 100644 --- a/tests/unit/component-tests/ChatComponentsTest/RentalMessage.spec.js +++ b/tests/unit/component-tests/ChatComponentsTest/RentalMessage.spec.js @@ -27,6 +27,9 @@ jest.mock("axios"); describe("RentalMessage.vue", () => { let wrapper; + const mockRouter = { + go: jest.fn(), + }; beforeEach(() => { wrapper = shallowMount(RentalMessage, { propsData: { @@ -47,6 +50,11 @@ describe("RentalMessage.vue", () => { deleted: false, }, }, + global: { + mocks: { + $router: mockRouter, + }, + }, }); }); diff --git a/tests/unit/component-tests/renting-compnents-tests/item-info.spec.js b/tests/unit/component-tests/renting-compnents-tests/item-info.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..33b2c610f69ba97491214d3fab01476b32a0e270 --- /dev/null +++ b/tests/unit/component-tests/renting-compnents-tests/item-info.spec.js @@ -0,0 +1,110 @@ +import { shallowMount } from "@vue/test-utils"; +import ItemInfo from "@/components/RentingComponents/ItemInfo.vue"; + +const mockMethod = jest.spyOn(ItemInfo.methods, "sendToConfirm"); +const mockCreatePush = jest.spyOn(ItemInfo.methods, "createPushItem"); + +jest.mock("@/utils/apiutil", () => { + return { + getItem: () => { + return new Promise((resolve) => { + resolve({ + listingID: 0, + title: "Title", + description: "Description", + pricePerDay: 100, + address: "ABC ROAD 3", + userID: 1, + categoryNames: [], + communityIDs: [], + }); + }); + }, + getItemPictures: () => { + return new Promise((resolve) => { + resolve([]); + }); + }, + }; +}); + +describe("ItemInfo component", () => { + let wrapper; + const mockRouter = { + push: jest.fn(), + currentRoute: { + value: { + params: { + id: "1", + }, + }, + }, + }; + const mockStore = { + commit: jest.fn(), + }; + + beforeEach(() => { + wrapper = shallowMount(ItemInfo, { + global: { + mocks: { + $router: mockRouter, + $store: mockStore, + }, + }, + }); + }); + + it("is instantiated", () => { + expect(wrapper.exists()).toBeTruthy(); + }); + + it("Check that title is displayed", () => { + expect(wrapper.findAll("h1")[0].text()).toBe("Title"); + }); + + it("Check that button cannot be clicked if date is not selected", async () => { + const jestCreatePushItemMock = jest.fn(); + wrapper.vm.createPushItem = jestCreatePushItemMock; + + // Click rent button + wrapper.find("button").trigger("click"); + + // wait a tick + await wrapper.vm.$nextTick(); + + // Check that jestMock was not clicked + expect(mockMethod).toHaveBeenCalledTimes(1); + expect(mockCreatePush).toHaveBeenCalledTimes(0); + // Check that the last p contains "Dato er påkrevd" + expect(wrapper.findAll("p")[wrapper.findAll("p").length - 1].text()).toBe( + "Dato er påkrevd" + ); + }); + + it("Check that send to confirm works", async () => { + // Set valid days + wrapper.vm.setDate({ + startDate: "2020-01-01", + endDate: "2020-02-01", + }); + wrapper.find("button").trigger("click"); + // wait a tick + await wrapper.vm.$nextTick(); + + // Check that method to change component was called + expect(mockCreatePush).toHaveBeenCalledTimes(1); + }); + + it("Test that price calculation works", async () => { + wrapper.vm.setDate({ + startDate: new Date("2022-01-01"), + endDate: new Date("2022-01-03"), + }); + // wait a tick + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + expect(wrapper.vm.totPrice).toBe(200); + }); +}); diff --git a/tests/unit/component-tests/user-component-tests/__snapshots__/rating.spec.js.snap b/tests/unit/component-tests/user-component-tests/__snapshots__/rating.spec.js.snap index f22076edecd3620957e0bbf9141873adfd3a6322..5d0cf24e39eb3292cb0d2eda697982c9c214fbda 100644 --- a/tests/unit/component-tests/user-component-tests/__snapshots__/rating.spec.js.snap +++ b/tests/unit/component-tests/user-component-tests/__snapshots__/rating.spec.js.snap @@ -1,22 +1,29 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Rating component renders correctly 1`] = ` -<ul - class="flex justify-center" +<div + data-v-app="" > - <li> - <p - class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400" - > - : - </p> - </li> - <li> - <p - class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400" - > - Ingen vurderinger - </p> - </li> -</ul> + + <!-- Shows rating, if no rating found, tells the user that no rating is registered --> + <ul + class="flex justify-center" + > + <li> + <p + class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400" + > + : + </p> + </li> + <li> + <p + class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400" + > + Ingen vurderinger + </p> + </li> + </ul> + +</div> `; diff --git a/tests/unit/component-tests/user-component-tests/__snapshots__/user-items.spec.js.snap b/tests/unit/component-tests/user-component-tests/__snapshots__/user-items.spec.js.snap index a5812dd15944a413f9281e682a1b364681eb63c9..6a73973168534f9f4153a75d0448d4e301d0ba97 100644 --- a/tests/unit/component-tests/user-component-tests/__snapshots__/user-items.spec.js.snap +++ b/tests/unit/component-tests/user-component-tests/__snapshots__/user-items.spec.js.snap @@ -5,6 +5,8 @@ exports[`UserItems component renders correctly 1`] = ` data-v-app="" > + <!-- Shows all the items a user has posted with search and pagination. + Includes a dropdown menu for editing or deleting an item. --> <div class="text-xl md:text-2xl text-primary-light font-medium" id="headline" @@ -53,6 +55,8 @@ exports[`UserItems component renders correctly 1`] = ` > + <!-- A waring asking the user if it is sure it wants to delete the item + with options to go ahead with the deleting or to cancel the delete. --> <!-- Main modal --> <!--v-if-->