Merge branch 'dev' into 5357-vnLog-component
gitea/salix-front/pipeline/head This commit looks good Details

This commit is contained in:
Alexandre Riera 2023-05-22 10:36:30 +00:00
commit a2cdddb17e
31 changed files with 633 additions and 186 deletions

View File

@ -11,4 +11,7 @@ services:
replicas: ${FRONT_REPLICAS:?}
placement:
constraints:
- node.role == worker
- node.role == worker
resources:
limits:
memory: 1G

View File

@ -58,9 +58,12 @@ const onResponseError = (error) => {
break;
}
if (session.isLoggedIn && response.status === 401) {
if (session.isLoggedIn() && response.status === 401) {
session.destroy();
Router.push({ path: '/login' });
} else if(!session.isLoggedIn())
{
message = 'login.loginError';
}
Notify.create({

View File

@ -127,7 +127,7 @@ async function togglePinned(item, event) {
<QBtn
v-if="item.isPinned === true"
@click="togglePinned(item, $event)"
icon="vn:pin_off"
icon="remove_circle"
size="xs"
flat
round
@ -139,7 +139,7 @@ async function togglePinned(item, event) {
<QBtn
v-if="item.isPinned === false"
@click="togglePinned(item, $event)"
icon="vn:pin"
icon="push_pin"
size="xs"
flat
round
@ -163,7 +163,7 @@ async function togglePinned(item, event) {
<QBtn
v-if="item.isPinned === true"
@click="togglePinned(item, $event)"
icon="vn:pin_off"
icon="remove_circle"
size="xs"
flat
round
@ -175,7 +175,7 @@ async function togglePinned(item, event) {
<QBtn
v-if="item.isPinned === false"
@click="togglePinned(item, $event)"
icon="vn:pin"
icon="push_pin"
size="xs"
flat
round
@ -200,13 +200,11 @@ async function togglePinned(item, event) {
</template>
<style>
.pinned .icon-pin,
.pinned .icon-pin_off {
.pinned .q-btn {
visibility: hidden;
}
.pinned:hover .icon-pin,
.pinned:hover .icon-pin_off {
.pinned:hover .q-btn {
visibility: visible;
}
</style>

View File

@ -70,7 +70,7 @@ async function saveDarkMode(value) {
}
async function saveLanguage(value) {
const query = `/Accounts/${user.value.id}`;
const query = `/VnUsers/${user.value.id}`;
await axios.patch(query, {
lang: value,
});

19
src/composables/getUrl.js Normal file
View File

@ -0,0 +1,19 @@
import axios from 'axios';
export async function getUrl(route, appName = 'salix') {
let url;
const env = process.env.NODE_ENV === 'development' ? 'dev' : process.env.NODE_ENV;
const filter = {
where: {and: [
{appName: appName},
{environment: env}
]}
};
await axios.get('Urls/findOne', {params: {filter}})
.then(res => {
url = res.data.url + route;
});
return url;
}

View File

@ -5,7 +5,7 @@ export function useRole() {
const state = useState();
async function fetch() {
const { data } = await axios.get('Accounts/acl');
const { data } = await axios.get('VnUsers/acl');
const roles = data.roles.map((userRoles) => userRoles.role.name);
const userData = {
@ -13,7 +13,6 @@ export function useRole() {
name: data.user.name,
nickname: data.user.nickname,
lang: data.user.lang || 'es',
darkMode: data.user.userConfig.darkMode,
};
state.setUser(userData);
state.setRoles(roles);

View File

@ -1,5 +1,7 @@
import { useState } from './useState';
import { useRole } from './useRole';
import { useUserConfig } from './useUserConfig';
export function useSession() {
function getToken() {
@ -36,11 +38,10 @@ export function useSession() {
}
async function login(token, keepLogin) {
const { fetch } = useRole();
setToken({ token, keepLogin });
await fetch();
await useRole().fetch();
await useUserConfig().fetch();
}
function isLoggedIn() {

View File

@ -0,0 +1,17 @@
import axios from 'axios';
import { useState } from './useState';
export function useUserConfig() {
const state = useState();
async function fetch() {
const { data } = await axios.get('UserConfigs/getUserConfig');
const user = state.getUser().value;
user.darkMode = data.darkMode;
state.setUser(user);
}
return {
fetch,
};
}

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Binary file not shown.

View File

@ -26,12 +26,6 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-pin:before {
content: '\e950';
}
.icon-pin_off:before {
content: '\e95b';
}
.icon-frozen:before {
content: '\e900';
}
@ -56,7 +50,7 @@
.icon-greenery:before {
content: '\e907';
}
.icon-planta:before {
.icon-plant:before {
content: '\e908';
}
.icon-handmade:before {
@ -401,3 +395,18 @@
.icon-trolley:before {
content: '\e95c';
}
.icon-agency-term:before {
content: '\e950';
}
.icon-client-unpaid:before {
content: '\e95b';
}
.icon-trolley:before {
content: '\e95c';
}
.icon-grafana:before {
content: '\e965';
}
.icon-troncales:before {
content: '\e967';
}

View File

@ -361,6 +361,7 @@ export default {
list: 'List',
basicData: 'Basic data',
summary: 'Summary',
notifications: 'Notifications',
},
list: {
name: 'Name',
@ -394,6 +395,12 @@ export default {
role: 'Role',
sipExtension: 'Extension',
},
notificationsManager: {
activeNotifications: 'Active notifications',
availableNotifications: 'Available notifications',
subscribed: 'Subscribed to the notification',
unsubscribed: 'Unsubscribed from the notification',
},
imageNotFound: 'Image not found',
},
wagon: {

View File

@ -361,6 +361,7 @@ export default {
list: 'Listado',
basicData: 'Datos básicos',
summary: 'Resumen',
notifications: 'Notificaciones',
},
list: {
name: 'Nombre',
@ -394,6 +395,12 @@ export default {
role: 'Rol',
sipExtension: 'Extensión',
},
notificationsManager: {
activeNotifications: 'Notificaciones activas',
availableNotifications: 'Notificaciones disponibles',
subscribed: 'Se ha suscrito a la notificación',
unsubscribed: 'Se ha dado de baja de la notificación',
},
imageNotFound: 'No se ha encontrado la imagen',
},
wagon: {

View File

@ -1,12 +1,36 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import ClaimDescriptor from './ClaimDescriptor.vue';
import LeftMenu from 'components/LeftMenu.vue';
import { getUrl } from 'composables/getUrl';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { useStateStore } from 'stores/useStateStore';
import { computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import ClaimDescriptor from './ClaimDescriptor.vue';
const stateStore = useStateStore();
const { t } = useI18n();
const route = useRoute();
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const entityId = computed(() => {
return $props.id || route.params.id;
});
const claimSections = [
{ name: 'Notes', url: '/note/index', icon: 'draft' },
{ name: 'Development', url: '/development', icon: 'vn:traceability' },
{ name: 'Action', url: '/action', icon: 'vn:actions' },
];
let salixUrl;
onMounted(async () => {
salixUrl = await getUrl(`claim/${entityId.value}`);
});
</script>
<template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
@ -22,6 +46,22 @@ const { t } = useI18n();
<ClaimDescriptor />
<QSeparator />
<LeftMenu source="card" />
<QSeparator />
<QList>
<QItem
v-for="section in claimSections"
:key="section.name"
active-class="text-primary"
:href="salixUrl + section.url"
clickable
v-ripple
>
<QItemSection avatar>
<QIcon :name="section.icon" />
</QItemSection>
<QItemSection> {{ t(section.name) }} </QItemSection>
</QItem>
</QList>
</QScrollArea>
</QDrawer>
<QPageContainer>
@ -35,4 +75,8 @@ const { t } = useI18n();
es:
Search claim: Buscar reclamación
You can search by claim id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
Details: Detalles
Notes: Notas
Development: Trazabilidad
Action: Acción
</i18n>

View File

@ -48,25 +48,29 @@ const password = ref('');
const keepLogin = ref(true);
async function onSubmit() {
const { data } = await axios.post('Accounts/login', {
user: username.value,
password: password.value,
});
try {
const { data } = await axios.post('Accounts/login', {
user: username.value,
password: password.value,
});
if (!data) return;
if (!data) return;
await session.login(data.token, keepLogin.value);
await session.login(data.token, keepLogin.value);
quasar.notify({
message: t('login.loginSuccess'),
type: 'positive',
});
quasar.notify({
message: t('login.loginSuccess'),
type: 'positive',
});
const currentRoute = router.currentRoute.value;
if (currentRoute.query && currentRoute.query.redirect) {
router.push(currentRoute.query.redirect);
} else {
router.push({ name: 'Dashboard' });
const currentRoute = router.currentRoute.value;
if (currentRoute.query && currentRoute.query.redirect) {
router.push(currentRoute.query.redirect);
} else {
router.push({ name: 'Dashboard' });
}
} catch (e) {
//
}
}
</script>

View File

@ -1,9 +1,9 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { computed, ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import axios from 'axios';
import { date, useQuasar } from 'quasar';
import { computed, onMounted, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
const router = useRouter();
const { t } = useI18n();
@ -21,22 +21,25 @@ const expeditions = ref({});
const lastExpedition = ref();
const slide = ref(null);
const videoList = ref([]);
const time = ref({
const time = reactive({
min: 0,
max: 24,
});
async function fetch() {
const filter = {
where: {
ticketFk: entityId.value,
},
};
const { data } = await axios.get(`/Expeditions/filter`, {
params: { filter },
});
try {
const filter = {
where: {
ticketFk: entityId.value,
},
};
const { data } = await axios.get(`/Expeditions/filter`, {
params: { filter },
});
if (data) expeditions.value = data;
if (data) expeditions.value = data;
} catch (e) {
//
}
}
async function getVideoList(expeditionId, timed) {
@ -46,115 +49,108 @@ async function getVideoList(expeditionId, timed) {
};
if (timed) {
if (timed.max == timed.min) {
if (timed.max != 24) timed.max += 1;
else timed.min -= 1;
}
Object.assign(params, { from: timed.min, to: timed.max });
}
const { data } = await axios.get(`/Boxings/getVideoList`, { params: params });
await axios.get(`/Boxings/getVideoList`, { params: params }).then((res) => {
const data = res.data;
if (!data.length) {
return quasar.notify({
message: t('ticket.boxing.notFound'),
type: 'negative',
});
}
const list = [];
for (const video of data) {
const videoName = video.split('.')[0].split('T')[1].replace(/-/g, ':');
list.push({
label: videoName,
value: video,
url: `api/Boxings/getVideo?id=${expeditionId}&filename=${video}`,
});
}
const list = [];
for (const video of data) {
const videName = video.split('.')[0].split('T')[1].replaceAll('-', ':');
list.push({
label: videName,
value: video,
url: `api/Boxings/getVideo?id=${expeditionId}&filename=${video}`,
});
}
videoList.value = list.reverse();
if (list[0]) {
slide.value = list[0].value;
time.value = {
min: parseInt(list[0].label.split(':')[0]),
max: parseInt(list[list.length - 1].label.split(':')[0]),
};
}
if (!data.length) {
return quasar.notify({
message: t('ticket.boxing.notFound'),
type: 'negative',
});
}
videoList.value = list.reverse();
if (list[0]) {
slide.value = list[0].value;
time.min = parseInt(list[0].label.split(':')[0]);
time.max = parseInt(list[list.length - 1].label.split(':')[0]) + 1;
}
});
}
</script>
<template>
<teleport to=".q-layout">
<QDrawer show-if-above side="right">
<QScrollArea class="fit">
<QList bordered separator style="max-width: 318px">
<QItem v-if="lastExpedition && videoList.length">
<QItemSection>
<QItemLabel class="text-h6">
{{ t('ticket.boxing.selectTime') }} ({{ time.min }}-{{
time.max
}})
</QItemLabel>
<QRange
v-model="time"
@change="getVideoList(lastExpedition, time)"
:min="0"
:max="24"
:step="1"
:left-label-value="time.min + ':00'"
:right-label-value="time.max + ':00'"
label
markers
snap
color="orange"
/>
</QItemSection>
</QItem>
<QItem v-if="lastExpedition && videoList.length">
<QItemSection>
<QSelect
color="orange"
v-model="slide"
:options="videoList"
:label="t('ticket.boxing.selectVideo')"
emit-value
map-options
>
<template #prepend>
<QIcon name="schedule" />
</template>
</QSelect>
</QItemSection>
</QItem>
<QItem
v-for="expedition in expeditions"
:key="expedition.id"
@click="getVideoList(expedition.id)"
clickable
v-ripple
>
<QItemSection>
<QItemLabel class="text-h6">#{{ expedition.id }}</QItemLabel>
</QItemSection>
<QItemSection>
<QItemLabel caption>{{
t('ticket.boxing.created')
}}</QItemLabel>
<QItemLabel>
{{
date.formatDate(
expedition.created,
'YYYY-MM-DD HH:mm:ss'
)
}}
</QItemLabel>
<QItemLabel caption>{{ t('ticket.boxing.item') }}</QItemLabel>
<QItemLabel>{{ expedition.packagingItemFk }}</QItemLabel>
<QItemLabel caption>{{
t('ticket.boxing.worker')
}}</QItemLabel>
<QItemLabel>{{ expedition.userName }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QScrollArea>
</QDrawer>
</teleport>
<QDrawer show-if-above side="right">
<QScrollArea class="fit">
<QList bordered separator style="max-width: 318px">
<QItem v-if="lastExpedition && videoList.length">
<QItemSection>
<QItemLabel class="text-h6">
{{ t('ticket.boxing.selectTime') }} ({{ time.min }}-{{
time.max
}})
</QItemLabel>
<QRange
v-model="time"
@change="getVideoList(lastExpedition, time)"
:min="0"
:max="24"
:step="1"
:left-label-value="time.min + ':00'"
:right-label-value="time.max + ':00'"
label
markers
snap
color="orange"
/>
</QItemSection>
</QItem>
<QItem v-if="lastExpedition && videoList.length">
<QItemSection>
<QSelect
color="orange"
v-model="slide"
:options="videoList"
:label="t('ticket.boxing.selectVideo')"
emit-value
map-options
>
<template #prepend>
<QIcon name="schedule" />
</template>
</QSelect>
</QItemSection>
</QItem>
<QItem
v-for="expedition in expeditions"
:key="expedition.id"
@click="getVideoList(expedition.id)"
clickable
v-ripple
>
<QItemSection>
<QItemLabel class="text-h6">#{{ expedition.id }}</QItemLabel>
</QItemSection>
<QItemSection>
<QItemLabel caption>{{ t('ticket.boxing.created') }}</QItemLabel>
<QItemLabel>
{{
date.formatDate(expedition.created, 'YYYY-MM-DD HH:mm:ss')
}}
</QItemLabel>
<QItemLabel caption>{{ t('ticket.boxing.item') }}</QItemLabel>
<QItemLabel>{{ expedition.packagingItemFk }}</QItemLabel>
<QItemLabel caption>{{ t('ticket.boxing.worker') }}</QItemLabel>
<QItemLabel>{{ expedition.userName }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QScrollArea>
</QDrawer>
<QCard>
<QCarousel animated v-model="slide" height="max-content">

View File

@ -0,0 +1,142 @@
<script setup>
import axios from 'axios';
import { useQuasar } from 'quasar';
import { computed, onMounted, onUpdated, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const entityId = computed(() => $props.id || route.params.id);
onMounted(() => fetch());
onUpdated(() => fetch());
const route = useRoute();
const { t } = useI18n();
const quasar = useQuasar();
const notifications = ref([]);
async function fetch() {
try {
await axios
.get(`NotificationSubscriptions/${entityId.value}/getList`)
.then(async (res) => {
if (res.data) {
notifications.value = res.data;
}
});
} catch (e) {
//
}
}
async function disableNotification(notification) {
await axios
.delete(`NotificationSubscriptions/${notification.id}`)
.catch(() => (notification.active = true))
.then((res) => {
if (res.data) {
notification.id = null;
notification.active = false;
quasar.notify({
type: 'positive',
message: t('worker.notificationsManager.unsubscribed'),
});
}
});
}
async function toggleNotification(notification) {
if (!notification.active) {
await disableNotification(notification);
} else {
await axios
.post(`NotificationSubscriptions`, {
notificationFk: notification.notificationFk,
userFk: entityId.value,
})
.catch(() => (notification.active = false))
.then((res) => {
if (res.data) {
notification.id = res.data.id;
quasar.notify({
type: 'positive',
message: t('worker.notificationsManager.subscribed'),
});
}
});
}
}
</script>
<template>
<QPage>
<QCard class="q-pa-md">
<QList>
<div
v-show="
notifications.filter(
(notification) => notification.active == true
).length
"
>
<QItemLabel header class="text-h6">
{{ t('worker.notificationsManager.activeNotifications') }}
</QItemLabel>
<QItem>
<div
v-for="notification in notifications.filter(
(notification) => notification.active == true
)"
:key="notification.id"
>
<QChip
:key="notification.id"
:label="notification.name"
text-color="white"
color="primary"
class="q-mr-sm"
removable
@remove="disableNotification(notification)"
/>
</div>
</QItem>
</div>
<div v-show="notifications.length">
<QItemLabel header class="text-h6">
{{ t('worker.notificationsManager.availableNotifications') }}
</QItemLabel>
<div class="row">
<QItem
class="col-3"
:key="notification.notificationFk"
v-for="notification in notifications"
>
<QItemSection>
<QItemLabel>{{ notification.name }}</QItemLabel>
<QItemLabel caption>{{
notification.description
}}</QItemLabel>
</QItemSection>
<QItemSection side top>
<QToggle
checked-icon="check"
unchecked-icon="close"
indeterminate-icon="block"
v-model="notification.active"
@update:model-value="toggleNotification(notification)"
/>
</QItemSection>
</QItem>
</div>
</div>
</QList>
</QCard>
</QPage>
</template>

View File

@ -10,11 +10,11 @@ import { i18n } from 'src/boot/i18n';
import { useState } from 'src/composables/useState';
import { useSession } from 'src/composables/useSession';
import { useRole } from 'src/composables/useRole';
import { useUserConfig } from 'src/composables/useUserConfig';
import { toLowerCamel } from 'src/filters';
const state = useState();
const session = useSession();
const role = useRole();
const { t } = i18n.global;
const createHistory = process.env.SERVER
@ -53,12 +53,13 @@ export default route(function (/* { store, ssrContext } */) {
if (isLoggedIn()) {
const stateRoles = state.getRoles().value;
if (stateRoles.length === 0) {
await role.fetch();
await useRole().fetch();
await useUserConfig().fetch();
}
const matches = to.matched;
const hasRequiredRoles = matches.every((route) => {
const meta = route.meta;
if (meta && meta.roles) return role.hasAny(meta.roles);
if (meta && meta.roles) return useRole().hasAny(meta.roles);
return true;
});

View File

@ -11,7 +11,7 @@ export default {
redirect: { name: 'WorkerMain' },
menus: {
main: ['WorkerList'],
card: [],
card: ['WorkerNotificationsManager'],
},
children: [
{
@ -42,9 +42,19 @@ export default {
path: 'summary',
meta: {
title: 'summary',
icon: 'launch',
},
component: () => import('src/pages/Worker/Card/WorkerSummary.vue'),
},
{
name: 'WorkerNotificationsManager',
path: 'notifications',
meta: {
title: 'notifications',
icon: 'notifications',
},
component: () => import('src/pages/Worker/Card/WorkerNotificationsManager.vue'),
},
],
},
],

View File

@ -43,13 +43,13 @@ describe('ClaimPhoto', () => {
cy.get(
'.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon'
).click();
cy.get('.q-btn--standard > .q-btn__content > .block').click();
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
cy.get('.q-notification__message').should('have.text', 'Data deleted');
cy.get(
'.multimediaParent > :nth-child(3) > .q-btn > .q-btn__content > .q-icon'
).click();
cy.get('.q-btn--standard > .q-btn__content > .block').click();
cy.get('.q-btn--unelevated > .q-btn__content > .block').click();
cy.get('.q-notification__message').should('have.text', 'Data deleted');
});
});

View File

@ -1,5 +1,5 @@
/// <reference types="cypress" />
xdescribe('TicketBoxing', () => {
describe('TicketBoxing', () => {
beforeEach(() => {
const ticketId = 1;
cy.viewport(1280, 720);
@ -26,9 +26,9 @@ xdescribe('TicketBoxing', () => {
},
['2022-01-01T01-01-00.mp4', '2022-02-02T02-02-00.mp4', '2022-03-03T03-03-00.mp4']
).as('getVideoList');
cy.get('.q-list > :nth-child(3)').click();
cy.get('.q-list').eq(3).find('.q-item').eq(2).click();
cy.get('.q-list > :nth-child(1)').should('be.visible');
cy.get('.q-list > :nth-child(2)').should('be.visible');
cy.get('.q-list').eq(3).find('.q-item').eq(0).find('.q-range');
cy.get('.q-list').eq(3).find('.q-item').eq(1).find('.q-select');
});
});

View File

@ -12,7 +12,10 @@ describe('WorkerList', () => {
});
it('should open the worker summary', () => {
cy.get('div[class="q-item__section column q-item__section--side justify-center q-pa-md"]').eq(0).click();
cy.get('div.text-h6')
.parentsUntil('div.q-card')
.find('div.q-card__actions')
.find('button').first().click();
cy.get('div[class="header bg-primary q-pa-sm q-mb-md"').should('have.text', '1110 - Jessica Jones');
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(0).should('have.text', 'Basic data');
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(1).should('have.text', 'User data');

View File

@ -0,0 +1,27 @@
describe('WorkerNotificationsManager', () => {
beforeEach(() => {
const workerId = 1110;
cy.viewport(1280, 720);
cy.login('salesBoss');
cy.visit(`/#/worker/${workerId}/notifications`);
});
it('should unsubscribe 2 notifications, check the unsubscription has been saved, subscribe to other one and should check the data has been saved', () => {
cy.get('.q-chip').should('have.length', 3);
cy.get('.q-toggle__thumb').eq(0).click();
cy.get('.q-notification__message').should('have.text', 'Unsubscribed from the notification');
cy.get('.q-chip > .q-icon').eq(0).click();
cy.reload();
cy.get('.q-chip').should('have.length', 1);
cy.get('.q-toggle__thumb').should('have.length', 3).eq(0).click();
cy.get('.q-notification__message').should('have.text', 'Subscribed to the notification');
cy.get('.q-toggle__thumb').should('have.length', 3).eq(1).click();
cy.get('.q-notification__message').should('have.text', 'Subscribed to the notification');
cy.reload();
cy.get('.q-chip').should('have.length', 3);
});
});

View File

@ -6,10 +6,10 @@ describe('WorkerSummary', () => {
});
it('should load worker summary', () => {
cy.get('div[class="header bg-primary q-pa-sm q-mb-md"').should('have.text', '19 - salesBoss');
cy.get('div[class="header bg-primary q-pa-sm q-mb-md"').should('have.text', '19 - salesBoss salesBoss');
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(0).should('have.text', 'Basic data');
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(1).should('have.text', 'User data');
cy.get('div[class="q-item__section column q-item__section--main justify-center"]').eq(0).should('have.text', 'NamesalesBossNick');
});
});
});

View File

@ -1,15 +1,15 @@
import { vi, describe, expect, it } from 'vitest';
import { Notify } from 'quasar';
import { onRequest, onResponseError } from 'src/boot/axios';
import { Notify } from 'quasar'
import { describe, expect, it, vi } from 'vitest';
vi.mock('src/composables/useSession', () => ({
useSession: () => ({
getToken: () => 'DEFAULT_TOKEN'
}),
getToken: () => 'DEFAULT_TOKEN',
isLoggedIn: () => vi.fn(),
destroy: () => vi.fn(),
})
}));
vi.mock('src/router', () => ({}));
describe('Axios boot', () => {
describe('onRequest()', async () => {

View File

@ -0,0 +1,58 @@
import { createWrapper } from 'app/test/vitest/helper';
import VnSmsDialog from 'components/common/VnSmsDialog.vue';
import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest';
describe('VnSmsDialog', () => {
let vm;
const orderId = 1;
const shipped = new Date();
const phone = '012345678';
const promise = (response) => {return response;};
const template = 'minAmount';
const locale = 'en';
beforeAll(() => {
vm = createWrapper(VnSmsDialog, {
propsData: {
data: {
orderId,
shipped
},
template,
locale,
phone,
promise
}
}).vm;
});
afterEach(() => {
vi.clearAllMocks();
});
describe('updateMessage()', () => {
it('should update the message value with the correct template and parameters', () => {
vm.updateMessage();
expect(vm.message).toEqual(`A minimum amount of 50€ (VAT excluded) is required for your order ${orderId} of ${shipped} to receive it without additional shipping costs.`);
});
});
describe('send()', async () => {
it('should send the message', async () => {
vi.spyOn(vm.props, 'promise');
vm.message = 'Example message';
const response = {
orderId,
shipped,
destination: phone,
message: vm.message
};
await vm.send();
expect(vm.isLoading).toEqual(false);
expect(vm.props.promise).toHaveBeenCalledWith(response);
});
});
});

View File

@ -23,21 +23,18 @@ describe('useRole', () => {
name: `T'Challa`,
nickname: 'Black Panther',
lang: 'en',
userConfig: {
darkMode: false,
},
};
const expectedUser = {
id: 999,
name: `T'Challa`,
nickname: 'Black Panther',
lang: 'en',
darkMode: false,
};
const expectedRoles = ['salesPerson', 'admin'];
vi.spyOn(axios, 'get').mockResolvedValue({
vi.spyOn(axios, 'get')
.mockResolvedValueOnce({
data: { roles: rolesData, user: fetchedUser },
});
})
vi.spyOn(role.state, 'setUser');
vi.spyOn(role.state, 'setRoles');

View File

@ -4,7 +4,7 @@ import TicketBoxing from 'pages/Ticket/Card/TicketBoxing.vue';
// #4836 - Investigate how to test q-drawer outside
// q-layout or how to teleport q-drawer inside
describe.skip('TicketBoxing', () => {
describe('TicketBoxing', () => {
let vm;
beforeAll(() => {
vm = createWrapper(TicketBoxing).vm;

View File

@ -0,0 +1,100 @@
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
import { createWrapper, axios } from 'app/test/vitest/helper';
import WorkerNotificationsManager from 'src/pages/Worker/Card/WorkerNotificationsManager.vue';
describe('WorkerNotificationsManager', () => {
let vm;
const entityId = 1110;
beforeAll(() => {
vm = createWrapper(WorkerNotificationsManager, {
propsData: {
id: entityId,
},
}).vm;
});
afterEach(() => {
vi.clearAllMocks();
});
describe('fetch()', () => {
it('should fetch notification subscriptions and role mappings', async () => {
vi.spyOn(axios, 'get')
.mockResolvedValueOnce({
data: [
{
id: 1,
name: 'Name 1',
description: 'Description 1',
notificationFk: 1,
active: true
},
],
});
await vm.fetch();
expect(axios.get).toHaveBeenCalledWith(`NotificationSubscriptions/${entityId}/getList`);
expect(vm.notifications).toEqual([
{
id: 1,
notificationFk: 1,
name: 'Name 1',
description: 'Description 1',
active: true,
},
]);
});
});
describe('disableNotification()', () => {
it('should disable the notification', async () => {
vi.spyOn(axios, 'delete').mockResolvedValue({ data: { count: 1 } });
vi.spyOn(vm.quasar, 'notify');
const subscriptionId = 1;
vm.notifications = [{ id: 1, active: true }];
await vm.disableNotification(vm.notifications[0]);
expect(axios.delete).toHaveBeenCalledWith(
`NotificationSubscriptions/${subscriptionId}`
);
expect(vm.notifications[0].id).toBeNull();
expect(vm.notifications[0].id).toBeFalsy();
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'positive' })
);
});
});
describe('toggleNotification()', () => {
it('should activate the notification', async () => {
vi.spyOn(axios, 'post').mockResolvedValue({
data: { id: 1, notificationFk: 1 },
});
vm.notifications = [{ id: null, active: true, notificationFk: 1 }];
await vm.toggleNotification(vm.notifications[0]);
expect(axios.post).toHaveBeenCalledWith('NotificationSubscriptions', {
notificationFk: 1,
userFk: entityId,
});
expect(vm.notifications[0].id).toBe(1);
expect(vm.notifications[0].active).toBeTruthy();
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'positive' })
);
});
it('should disable the notification', async () => {
vi.spyOn(vm, 'disableNotification');
vm.notifications = [{ id: 1, active: false, notificationFk: 1 }];
await vm.toggleNotification(vm.notifications[0]);
expect(vm.notifications[0].id).toBe(null);
expect(vm.notifications[0].active).toBeFalsy();
});
});
});