UNCLASSIFIED

Commit 02f0931c authored by Michael Winberry's avatar Michael Winberry
Browse files

Merge branch 'master' into Valkyrie

parents 72508796 233c47f1
......@@ -34,22 +34,22 @@ appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
......
This diff is collapsed.
......@@ -17,51 +17,51 @@
"prettify:pretty-quick"
],
"dependencies": {
"apexcharts": "^3.26.0",
"apexcharts": "^3.28.1",
"axios": "^0.21.1",
"core-js": "^3.6.4",
"core-js": "^3.16.3",
"downloadjs": "^1.4.7",
"lodash": "4.17.21",
"moment": "^2.29.1",
"vue": "^2.6.11",
"vue-apexcharts": "^1.6.0",
"vue": "^2.6.14",
"vue-apexcharts": "^1.6.2",
"vue-body-class": "^3.0.2",
"vue-router": "^3.1.6",
"vuetify": "^2.4.2",
"vue-router": "^3.5.2",
"vuetify": "^2.5.8",
"vuex": "^3.5.1"
},
"devDependencies": {
"@babel/plugin-transform-strict-mode": "^7.12.13",
"@babel/plugin-transform-strict-mode": "^7.14.5",
"@mdi/font": "^5.9.55",
"@vue/cli-plugin-babel": "^4.5.11",
"@vue/cli-plugin-babel": "^4.5.13",
"@vue/cli-plugin-e2e-cypress": "^4.5.13",
"@vue/cli-plugin-eslint": "^4.5.11",
"@vue/cli-plugin-router": "^4.5.11",
"@vue/cli-plugin-unit-jest": "^4.5.11",
"@vue/cli-service": "^4.5.11",
"@vue/cli-plugin-eslint": "^4.5.13",
"@vue/cli-plugin-router": "^4.5.13",
"@vue/cli-plugin-unit-jest": "^4.5.13",
"@vue/cli-service": "^4.5.13",
"@vue/eslint-config-prettier": "^6.0.0",
"@vue/test-utils": "^1.1.3",
"@vue/test-utils": "^1.2.2",
"babel-eslint": "^10.1.0",
"cypress": "^7.3.0",
"cypress": "^7.7.0",
"eslint": "^6.8.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-prettier": "^3.4.1",
"eslint-plugin-vue": "^6.2.2",
"flush-promises": "^1.0.2",
"husky": "^4.3.8",
"pre-commit": "^1.2.2",
"prettier": "^2.2.1",
"pretty-quick": "^3.1.0",
"sass": "^1.32.8",
"prettier": "^2.3.2",
"pretty-quick": "^3.1.1",
"sass": "~1.38.1",
"sass-loader": "^8.0.2",
"style-resources-loader": "^1.3.2",
"stylelint": "^13.11.0",
"stylelint-webpack-plugin": "^2.1.1",
"vue-cli-plugin-style-resources-loader": "~0.1.4",
"vue-cli-plugin-vuetify": "^2.0.9",
"stylelint": "^13.13.1",
"stylelint-webpack-plugin": "^2.2.2",
"vue-cli-plugin-style-resources-loader": "^0.1.5",
"vue-cli-plugin-vuetify": "^2.4.2",
"vue-loader-v16": "^16.0.0-beta.5.4",
"vue-svg-loader": "^0.16.0",
"vue-template-compiler": "^2.6.12",
"vue-template-compiler": "^2.6.14",
"vuetify-loader": "^1.7.2"
},
"eslintConfig": {
......
......@@ -59,9 +59,9 @@ export default {
await this.addStudent(courseId, { userId: student.id });
}
},
async addStudentsToCourses(courses, students) {
async addStudentsToCourses(courses, students, pmId) {
for (const { id: courseId } of courses) {
await this.addStudents(courseId, students);
await this.addPendingStudents(courseId, students, pmId);
}
},
async exportCourses() {
......@@ -89,4 +89,21 @@ export default {
);
}
},
async getCoursePendingRegistrationsById(courseId) {
return HTTP.get(`/courses/${courseId}/pending-registrations`);
},
async putCoursePendingRegistrationsById(courseId, params) {
return HTTP.put(
`/courses/${courseId}/pending-registrations/acceptance`,
params
);
},
async addPendingStudent(courseId, student) {
return HTTP.post(`/courses/${courseId}/pending-registrations`, student);
},
async addPendingStudents(courseId, students, pmId) {
for (const student of students) {
await this.addPendingStudent(courseId, { userId: student.id, pmId: pmId });
}
},
};
......@@ -4,6 +4,12 @@ export default {
async getUser() {
return HTTP.get("/whoami");
},
async getNotifications() {
return HTTP.get("/users/notifications");
},
async deleteNotifications(notificationId) {
return HTTP.delete(`/users/notification/${notificationId}`);
},
async search(params) {
return HTTP.get("/users", { params });
},
......
<svg width="24" height="21" viewBox="0 0 24 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 20.7273H24L12 0L0 20.7273ZM13.0909 17.4545H10.9091V15.2727H13.0909V17.4545ZM13.0909 13.0909H10.9091V8.72727H13.0909V13.0909Z" fill="#BD0707"/>
</svg>
<template>
<v-dialog v-model="denyDialog" width="700">
<template v-slot:activator="{ on, attrs }">
<v-btn color="secondary" width="112px" v-bind="attrs" v-on="on">
DENY
</v-btn>
</template>
<v-card class="py-12">
<v-img
contain
src="@/assets/images/logos/Logo-Platform-One-HEAD.png"
class="logo mx-auto"
height="100px"
width="100px"
/>
<v-card-title class="text-h4 mb-8 justify-center font-weight-bold">
NOTIFY THE PM ON WHY THEIR<br />
REQUEST WAS DENIED
</v-card-title>
<v-select
v-model="select"
:items="items"
item-text="title"
item-value="key"
label="Templated Responses"
return-object
single-line
light
outlined
hide-details
class="select mx-auto"
@change="updateTextArea()"
></v-select>
<v-textarea
ref="inputRef"
clearable
v-model="selectedMessage"
class="text-area mx-auto mt-10"
placeholder="Edit your response in here or choose from the templated responses provided in the dropdown..."
outlined
auto-grow
>
</v-textarea>
<v-card-actions class="justify-center">
<v-btn color="secondary" @click="denyDialog = false"> CANCEL </v-btn>
<v-btn
:disabled="!selectedMessage"
color="primary"
v-on:click="denyPendingRegistration(), (denyDialog = false)"
>
SEND NOTIFICATION
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import TrainingService from "@/api/services/training";
import { SET_ERROR_MESSAGE, SET_ERROR_DIALOG } from "@/store/mutation-types";
export default {
props: ["courseId", "id", "pm", "mainMessage"],
data() {
return {
selectedMessage: "",
messageSubText: "",
select: "",
items: [
{
key: 0,
title: "Payment has not been verified",
mainText: "",
subText:
"According to our records, your payment for another seat has not been validated. Please contact the Customer Success team.",
},
{
key: 1,
title: "Name has not been verified",
mainText: "",
subText:
"According to our records, the team member you have requested does not match our submission list. Please contact the Customer Success Team.",
},
{
key: 2,
title: "Course capacity is at maximum",
mainText: "",
subText:
"We are unable to add another seat to the course as we have reached maximum capacity. Please email the course instructor for further advice.",
},
],
denyDialog: false,
};
},
methods: {
updateTextArea() {
this.selectedMessage = this.select.subText;
this.subText = this.select.subText;
},
async denyPendingRegistration() {
try {
const response =
await TrainingService.putCoursePendingRegistrationsById(
this.$props.courseId,
{
userId: this.$props.id,
pmId: this.$props.pm,
mainText: this.$props.mainMessage,
subText: this.subText,
add: false,
}
);
this.total = response.meta?.total || 0;
} catch (error) {
const errorMessage = error;
this.$store.commit(SET_ERROR_MESSAGE, errorMessage);
this.$store.commit(SET_ERROR_DIALOG, true);
} finally {
this.$emit("change", true);
}
},
},
};
</script>
<style lang="scss" scoped>
.select {
background-color: white;
width: 60%;
height: 56px;
font-size: 14px;
}
.text-area {
width: 80%;
font-size: 14px;
}
</style>
......@@ -114,7 +114,8 @@ export default {
try {
await CourseService.addStudentsToCourses(
[this.course],
this.selectedMembers
this.selectedMembers,
this.$store.state.user.user.id
);
this.$emit("update-students");
} catch (error) {
......
<template>
<v-container class="py-0">
<v-list color="tertiary" max-height="300px" class="v-list pa-0">
<v-list-item v-if="this.listItems.count === 0">
<v-list-item-content class="pa-0">
<v-list-item-title class="text-wrap text-left"
>No notifications to display.</v-list-item-title
>
</v-list-item-content>
</v-list-item>
<v-list-item v-else v-for="(item, i) in listItems.rows" :key="i">
<v-col class="text-left pa-0">
<v-list-item-icon>
<NotificationLogo />
</v-list-item-icon>
<v-list-item-content class="pa-0">
<v-list-item-title
class="text-wrap text-left"
v-text="item.mainText"
></v-list-item-title>
<v-list-item-subtitle
v-text="item.subText"
class="text-wrap mt-3"
></v-list-item-subtitle>
<v-col class="v-list-item-col text-right">
<v-btn
color="primary"
outlined
type="button"
@click="clearNotification(item.id)"
>Clear Notification</v-btn
>
</v-col>
</v-list-item-content>
</v-col>
</v-list-item>
</v-list>
</v-container>
</template>
<script>
import UserService from "@/api/services/user";
import NotificationLogo from "@/assets/images/logos/Vector.svg";
import { SET_ERROR_MESSAGE, SET_ERROR_DIALOG } from "@/store/mutation-types";
export default {
components: {
NotificationLogo,
},
props: ["courseId", "id", "pm"],
data() {
return {
listItems: [],
createDialog: false,
editDialog: false,
sentDialog: false,
editNotif: false,
};
},
async mounted() {
this.fetchData();
},
methods: {
async fetchData() {
this.loading = true;
try {
this.listItems = await UserService.getNotifications();
} catch (error) {
const errorMessage = error;
this.$store.commit(SET_ERROR_MESSAGE, errorMessage);
this.$store.commit(SET_ERROR_DIALOG, true);
}
this.loading = false;
this.fetchingData = false;
},
async clearNotification(id) {
try {
await UserService.deleteNotifications(id);
} catch (error) {
const errorMessage = error;
this.$store.commit(SET_ERROR_MESSAGE, errorMessage);
this.$store.commit(SET_ERROR_DIALOG, true);
} finally {
this.fetchData();
}
},
},
};
</script>
<style lang="scss" scoped>
.v-list {
overflow-y: auto;
}
.v-list-item-col {
width: 90%;
}
</style>
......@@ -122,7 +122,6 @@
}}
</span>
</v-tooltip>
<v-tooltip top>
<template v-slot:activator="{ on, attrs }">
<v-btn
......
<template>
<v-container>
<v-row>
<v-col>
<h2 class="text-left px-0 my-3">Pending Requests</h2>
</v-col>
</v-row>
<v-data-table
id="pending-table"
v-if="headers"
:headers="header"
:items="listItems"
:search="search"
:hide-default-header="onMobile"
mobile-breakpoint="800"
:options.sync="options"
:footer-props="footerProps"
:server-items-length="total"
:hide-default-footer="listItems.length === 0"
:loading="loading"
>
<template v-slot:[`item.user.name`]="{ item }" class="px-0 my-0">
<div class="my-0 py-0">
<p class="py-0 mb-0">
{{ item.user.name }}
</p>
</div>
</template>
<template v-slot:[`item.user.role`]="{ item }" class="px-0 my-0">
<div class="my-0 py-0">
<p class="py-0 my-0">
{{ item.user.role }}
</p>
</div>
</template>
<template v-slot:[`item.user.email`]="{ item }" class="py-0 my-0">
<div class="my-0 py-0">
<p class="py-0 my-0">
{{ item.user.email }}
</p>
</div>
</template>
<template v-slot:[`item.pm.name`]="{ item }" class="px-0 my-0">
<div class="my-0 py-0">
<p class="py-0 mb-0">
{{ item.pm.name }}
</p>
</div>
</template>
<template v-slot:[`item.approval`]="{ item }" class="py-0">
<div class="d-flex align-center justify-center">
<DenialNotification
v-bind:courseId="currentCourseId"
v-bind:id="item.user.id"
v-bind:pm="item.pm.id"
v-bind:mainMessage="mainMessage"
class="mx-2"
@change="fetchReload"
/>
<v-btn
color="primary"
class="mx-2 black--text align-center justify-center"
max-width="50"
v-on:click="acceptPendingRegistration(item.user.id, item.pm.id)"
>
Approve
</v-btn>
</div>
</template>
</v-data-table>
</v-container>
</template>
<script>
import TrainingService from "@/api/services/training";
import DenialNotification from "@/components/DenialNotification";
import { debounceTimeout } from "@/config/config";
import { SET_ERROR_MESSAGE, SET_ERROR_DIALOG } from "@/store/mutation-types";
import {
DEFAULT_FOOTER_PROPS,
DEFAULT_PAGINATION_PARAMS,
DEFAULT_TABLE_OPTIONS,
} from "@/config/table-constants";
export default {
name: "PendingRequests",
components: { DenialNotification },
props: {
trainingCourse: {
type: Object,
required: true,
},
reload: {
type: Number,
},
},
data() {
return {
search: "",
loading: false,
total: 0,
footerProps: DEFAULT_FOOTER_PROPS,
params: {
...DEFAULT_PAGINATION_PARAMS,
},
options: DEFAULT_TABLE_OPTIONS,
state: {
isAddingStudent: false,
isAddingDuplicate: false,
isAddingBusy: false,
},
listItems: [],
mobileBreakpoint: 800,
headers: [
{ text: "Name", value: "user.name", width: "175px", sortable: false },
{ text: "Role", value: "user.role", width: "175px", sortable: false },
{
text: "Email",
value: "user.email",
width: "175px",
sortable: false,
},
{
text: "Team PM",
value: "pm.name",
width: "175px",
sortable: false,
},
{
text: "Approval",
value: "approval",
width: "250px",
sortable: false,
},
],
mobileHeader: [
{
text: "Name",
value: "user.name",
width: "100%",
align: "center",
sortable: false,
},
{
text: "Email",
value: "user.email",
width: "100%",
align: "center",
sortable: false,
},
{
text: "Approval",
value: "approval",
width: "!00%",
sortable: false,
},
],
};
},
async mounted() {
await this.fetchData();
},
methods: {
fetchDebounced() {
// cancel pending call
clearTimeout(this._timerId);
this.fetchingData = true;
// delay new call
this._timerId = setTimeout(() => {
this.fetchData();
}, debounceTimeout);
},
async fetchData() {
this.loading = true;
try {
const response =
await TrainingService.getCoursePendingRegistrationsById(
this.currentCourseId
);
this.listItems = response.registrations;
this.total = response.meta?.total || 0;
} catch (error) {
const errorMessage = error;
console.error("ERROR");
this.$store.commit(SET_ERROR_MESSAGE, errorMessage);
this.$store.commit(SET_ERROR_DIALOG, true);
}
this.loading = false;
this.fetchingData = false;
},
async acceptPendingRegistration(userId, pmId) {
try {
await TrainingService.putCoursePendingRegistrationsById(
this.currentCourseId,
{
userId: userId,
pmId: pmId,
mainText:
"Your request to add a team member into " +
this.$props.trainingCourse.name +
" - " +
this.$props.trainingCourse.startDate +
"-" +
this.$props.trainingCourse.endDate +
" has been approved",
subText: "",
add: true,
}
);
} catch (error) {
const errorMessage = error;
this.$store.commit(SET_ERROR_MESSAGE, errorMessage);
} finally {
this.$emit("change", true);
this.fetchDebounced();
}
},
fetchReload(value) {
if (value) {
this.fetchDebounced();
}
},
},
computed: {
onMobile() {
return this.$vuetify.breakpoint.width < this.mobileBreakpoint;
},
header() {
return this.onMobile ? this.mobileHeader : this.headers;
},
currentCourseId() {
return this.trainingCourse.id;
},
mainMessage() {
return (
"Your request to add a team member into " +
this.$props.trainingCourse.name +
" - " +
this.$props.trainingCourse.startDate +
"-" +
this.$props.trainingCourse.endDate +
" has been denied due to the following reasons:"
);
},
},
};
</script>
<style lang="scss" scoped>
.search {
width: 250px;
}
#pending-table {
::v-deep .v-data-table__wrapper {
table > tbody > tr > td:nth-child(5) > div > button.v-btn {
min-width: 112px;
}
.v-data-table__mobile-table-row {
> .v-data-table__mobile-row {
margin: 0px 0px -10px 0px;
padding: 0px 0px 0px 0px;
&:nth-child(n) {
width: 100%;
}
&:nth-child(2) {
color: #bdc931;
.v-data-table__mobile-row__cell {
margin: -30px 0px 0px 0px;
}
}
.v-data-table__mobile-row__cell {
width: 100% !important;
align-self: center;
text-align: center;
justify-content: center;
}
&:nth-child(3) {
white-space: nowrap;
div > button.v-btn {
min-width: 112px;
}
.v-data-table__mobile-row__cell {
padding-top: 6px;
padding-bottom: 32px;
}
}
}
}
}
}
</style>
......@@ -20,6 +20,7 @@
color="primary"
@click="state.isAddingStudent = true"
class="mx-auto ml-4 black--text"
v-if="!onMobile"
>
Add Student
</v-btn>
......@@ -315,6 +316,7 @@
background-color="white"
height="8px"
:value="courseProgress"
v-if="loading"
></v-progress-linear>
</div>
</template>
......@@ -370,7 +372,12 @@
Edit Course
</v-btn>
</div>
<PendingRequests
ref="pendingRequests"
class="px-0"
v-if="trainingCourse.id"
:trainingCourse="trainingCourse"
/>
<TrainingAttendance
ref="trainingAttendance"
class="px-0"
......@@ -389,6 +396,7 @@ import TrainingService from "@/api/services/training";
import inputRules from "@/utils/inputRules";
import NotFoundComponent from "@/components/NotFoundComponent";
import TrainingAttendance from "@/components/Training/TrainingAttendance";
import PendingRequests from "@/components/Training/PendingRequests";
import { SET_ERROR_MESSAGE, SET_ERROR_DIALOG } from "@/store/mutation-types";
import UserSelect from "@/components/UserSelect";
import { DEFAULT_PAGINATION_PARAMS } from "@/config/table-constants";
......@@ -399,6 +407,7 @@ export default {
AddStudentsToCourseDialog,
TrainingAttendance,
UserSelect,
PendingRequests,
NotFoundComponent,
},
data: () => ({
......@@ -583,7 +592,7 @@ export default {
// delay new call
this.fetchData();
this.$refs.trainingAttendance.fetchDebounced();
this.$refs.addStudentsToCourseDialog.fetchDebounced();
this.$refs.trainingAttendance.fetchDebounced();
},
async updateCourse() {
this.loading = true;
......
......@@ -17,6 +17,10 @@
<WelcomeSummary slot="content" />
</Section>
<Section class="mt-6" headerTitle="Notifications">
<Notification slot="content" />
</Section>
<Section
class="mt-6"
sync
......@@ -113,6 +117,7 @@ import UserBanner from "@/components/UserBanner";
import Section from "@/components/Section";
import AdminCourses from "@/components/AdminCourses";
import HelpDeskSummary from "@/components/HelpDeskSummary";
import Notification from "@/components/Notification";
import {
SET_PAST_METRICS_STATE,
SET_PRESENT_METRICS_STATE,
......@@ -132,6 +137,7 @@ export default {
Section,
AdminCourses,
HelpDeskSummary,
Notification,
WelcomeSummary,
SeatMetrics,
},
......
import { shallowMount } from "@vue/test-utils";
import TrainingService from "@/api/services/training";
import DenialNotification from "@/components/DenialNotification";
import { SET_ERROR_MESSAGE, SET_ERROR_DIALOG } from "@/store/mutation-types";
describe("DenialNotification", () => {
const mockError = "mock error";
it("should send back a notification that denies the user", async () => {
const wrapper = shallowMount(DenialNotification, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
select: { subText: "mockOne" },
selectedMessage: "",
mainText: "",
subText: "",
};
},
});
wrapper.vm.updateTextArea();
expect(wrapper.vm.selectedMessage).toEqual("mockOne");
expect(wrapper.vm.subText).toEqual("mockOne");
});
it("should send a notification of denial to pm", async () => {
TrainingService.putCoursePendingRegistrationsById = jest
.fn()
.mockReturnValue({ status: "success" });
const wrapper = shallowMount(DenialNotification, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
});
await wrapper.vm.denyPendingRegistration();
expect(TrainingService.putCoursePendingRegistrationsById).toBeCalledTimes(
1
);
});
it("should set error message if unable to send denial notification", async () => {
TrainingService.putCoursePendingRegistrationsById = jest
.fn()
.mockRejectedValue(mockError);
const wrapper = shallowMount(DenialNotification, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
});
await wrapper.vm.denyPendingRegistration();
expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(
SET_ERROR_MESSAGE,
"mock error"
);
expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(
SET_ERROR_DIALOG,
true
);
});
});
......@@ -112,6 +112,15 @@ describe("SelectAddStudentsToCourseDialog", () => {
});
it("addSelectedUsersToSelectedCourses should call addStudentsToCourses", async () => {
const wrapper = shallowMount(SelectAddStudentsToCourseDialog, {
mocks: {
$store: {
state: {
error: {},
user: { user: { id: 1 } },
},
commit: jest.fn(),
},
},
propsData: {
value: [],
selectedMembers: [],
......@@ -139,6 +148,10 @@ describe("SelectAddStudentsToCourseDialog", () => {
},
mocks: {
$store: {
state: {
error: {},
user: { user: { id: 1 } },
},
commit: jest.fn(),
},
},
......@@ -168,6 +181,10 @@ describe("SelectAddStudentsToCourseDialog", () => {
},
mocks: {
$store: {
state: {
error: {},
user: { user: { id: 1 } },
},
commit: jest.fn(),
},
},
......
import { shallowMount } from "@vue/test-utils";
import UserService from "@/api/services/user";
import Notification from "@/components/Notification";
import { SET_ERROR_MESSAGE, SET_ERROR_DIALOG } from "@/store/mutation-types";
describe("Notification", () => {
const mockError = "mock error";
it("should grab all notifications sent to current user", async () => {
const wrapper = shallowMount(Notification, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
listItems: [],
createDialog: false,
editDialog: false,
sentDialog: false,
editNotif: false,
};
},
});
wrapper.vm.fetchData();
expect(wrapper.vm.listItems).toEqual([]);
});
it("should set error message if unable to receive notifications", async () => {
UserService.getNotifications = jest.fn().mockRejectedValue(mockError);
const wrapper = shallowMount(Notification, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
});
await wrapper.vm.fetchData();
expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(
SET_ERROR_MESSAGE,
"mock error"
);
expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(
SET_ERROR_DIALOG,
true
);
});
it("should delete the desired notification from the user", async () => {
UserService.deleteNotifications = jest
.fn()
.mockResolvedValue({ result: "true" });
const wrapper = shallowMount(Notification, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
listItems: [],
};
},
});
await wrapper.vm.clearNotification();
expect(UserService.deleteNotifications).toHaveBeenCalledTimes(1);
});
it("should set error message if unable to delete notification", async () => {
UserService.deleteNotifications = jest.fn().mockRejectedValue(mockError);
const wrapper = shallowMount(Notification, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
});
await wrapper.vm.clearNotification();
expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(
SET_ERROR_MESSAGE,
"mock error"
);
expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(
SET_ERROR_DIALOG,
true
);
});
});
import Vuex from "vuex";
import Vuetify from "vuetify";
import { shallowMount, createLocalVue } from "@vue/test-utils";
import PendingRequests from "@/components/Training/PendingRequests";
import TrainingService from "@/api/services/training";
import { SET_ERROR_MESSAGE } from "@/store/mutation-types";
import flushPromises from "flush-promises";
const vuetify = new Vuetify();
const localVue = createLocalVue();
localVue.use(Vuex);
describe("PendingRequests", () => {
const propsData = {
trainingCourse: {
id: 1,
startDate: "2021-01-01",
endDate: "2021-01-03",
name: "Test",
},
};
afterEach(() => {
jest.resetAllMocks();
});
it("should load component", async () => {
TrainingService.getCoursePendingRegistrationsById = jest
.fn()
.mockResolvedValue({
registrations: [{ user: [{ name: "user4" }] }, {}],
});
// render the component
const wrapper = shallowMount(PendingRequests, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
loading: true,
initialLoad: true,
listItems: [{ userId: 1 }, { userId: 2 }, { userId: 3 }],
};
},
propsData,
localVue,
vuetify,
});
await flushPromises();
// assert the component loads without error
expect(wrapper.find(".error").exists()).toBe(false);
});
it("should catch error with fetchData", async () => {
TrainingService.getCoursePendingRegistrationsById = jest
.fn()
.mockRejectedValue("mock-error");
// render the component
const wrapper = shallowMount(PendingRequests, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
loading: true,
initialLoad: true,
listItems: [{ userId: 1 }, { userId: 2 }, { userId: 3 }],
};
},
propsData,
localVue,
vuetify,
});
await flushPromises();
expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(
SET_ERROR_MESSAGE,
"mock-error"
);
});
it("should fetch debounced", async () => {
TrainingService.getCourseRegistrationsById = jest
.fn()
.mockResolvedValue({ users: [{ name: "user1" }] });
PendingRequests.fetchData = jest.fn();
const wrapper = shallowMount(PendingRequests, {
mocks: {
$route: {
params: {
trainingId: 1,
},
},
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
fetchingData: false,
listItems: [{ userId: 1 }, { userId: 2 }, { userId: 3 }],
};
},
propsData,
localVue,
vuetify,
});
expect(wrapper.vm.fetchingData).toBe(false);
await flushPromises();
await wrapper.vm.fetchDebounced();
expect(wrapper.vm.fetchingData).toBe(true);
});
it("should call fetchReload", async () => {
TrainingService.getCourseRegistrationsById = jest
.fn()
.mockResolvedValue({ users: [{ name: "user1" }] });
const wrapper = shallowMount(PendingRequests, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
selectedUsers: [{ userId: 1 }, { userId: 2 }],
listItems: [{ userId: 1 }, { userId: 2 }, { userId: 3 }],
};
},
propsData,
localVue,
vuetify,
});
await flushPromises();
wrapper.vm.fetchDebounced = jest.fn();
await wrapper.vm.$nextTick();
await wrapper.vm.fetchReload(true);
expect(wrapper.vm.fetchDebounced).toHaveBeenCalledTimes(1);
});
it("should call acceptPendingRegistration", async () => {
TrainingService.putCoursePendingRegistrationsById = jest
.fn()
.mockResolvedValue({ result: "true" });
// render the component
const wrapper = shallowMount(PendingRequests, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
loading: true,
initialLoad: true,
selectedUsers: [{ userId: 1 }, { userId: 2 }],
listItems: [{ userId: 1 }, { userId: 2 }, { userId: 3 }],
};
},
propsData,
localVue,
vuetify,
});
await wrapper.vm.acceptPendingRegistration();
await flushPromises();
expect(
TrainingService.putCoursePendingRegistrationsById
).toHaveBeenCalledTimes(1);
});
it("should catch acceptPendingRegistration errors", async () => {
TrainingService.putCoursePendingRegistrationsById = jest
.fn()
.mockRejectedValue("mock-error");
// render the component
const wrapper = shallowMount(PendingRequests, {
mocks: {
$store: {
state: {
error: {},
},
commit: jest.fn(),
},
},
data() {
return {
loading: true,
initialLoad: true,
listItems: [{ userId: 1 }, { userId: 2 }, { userId: 3 }],
};
},
propsData,
localVue,
vuetify,
});
await wrapper.vm.acceptPendingRegistration();
await flushPromises();
expect(wrapper.vm.$store.commit).toHaveBeenCalledWith(
SET_ERROR_MESSAGE,
"mock-error"
);
});
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment