8144-devToTest_2444 #852

Merged
carlossa merged 86 commits from 8144-devToTest_2444 into test 2024-10-22 08:55:44 +00:00
8 changed files with 153 additions and 33 deletions
Showing only changes of commit 0afcba0b07 - Show all commits

View File

@ -2,9 +2,11 @@ import axios from 'axios';
import { useSession } from 'src/composables/useSession';
import { Router } from 'src/router';
import useNotify from 'src/composables/useNotify.js';
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
const session = useSession();
const { notify } = useNotify();
const stateQuery = useStateQueryStore();
const baseUrl = '/api/';
axios.defaults.baseURL = baseUrl;
@ -15,7 +17,7 @@ const onRequest = (config) => {
if (token.length && !config.headers.Authorization) {
config.headers.Authorization = token;
}
stateQuery.add(config);
return config;
};
@ -24,10 +26,10 @@ const onRequestError = (error) => {
};
const onResponse = (response) => {
const { method } = response.config;
const config = response.config;
stateQuery.remove(config);
const isSaveRequest = method === 'patch';
if (isSaveRequest) {
if (config.method === 'patch') {
notify('globals.dataSaved', 'positive');
}
@ -35,6 +37,8 @@ const onResponse = (response) => {
};
const onResponseError = (error) => {
stateQuery.remove(error.config);
let message = '';
const response = error.response;

View File

@ -3,6 +3,7 @@ import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useState } from 'src/composables/useState';
import { useStateStore } from 'stores/useStateStore';
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
import { useQuasar } from 'quasar';
import PinnedModules from './PinnedModules.vue';
import UserPanel from 'components/UserPanel.vue';
@ -12,6 +13,7 @@ import VnAvatar from './ui/VnAvatar.vue';
const { t } = useI18n();
const stateStore = useStateStore();
const quasar = useQuasar();
const stateQuery = useStateQueryStore();
const state = useState();
const user = state.getUser();
const appName = 'Lilium';
@ -50,6 +52,14 @@ const pinnedModulesRef = ref();
</QBtn>
</RouterLink>
<VnBreadcrumbs v-if="$q.screen.gt.sm" />
<QSpinner
color="primary"
class="q-ml-md"
:class="{
'no-visible': !stateQuery.isLoading().value,
}"
size="xs"
/>
<QSpace />
<div id="searchbar" class="searchbar"></div>
<QSpace />

View File

@ -288,3 +288,7 @@ input::-webkit-inner-spin-button {
color: $info;
}
}
.no-visible {
visibility: hidden;
}

View File

@ -229,7 +229,7 @@ onBeforeMount(() => {
>
<template #body-cell-id="{ row }">
<QTd>
<QBtn flat color="primary"> {{ row.ticketFk }}</QBtn>
<QBtn flat class="link"> {{ row.ticketFk }}</QBtn>
<TicketDescriptorProxy :id="row.ticketFk" />
</QTd>
</template>
@ -251,7 +251,7 @@ onBeforeMount(() => {
</template>
<template #body-cell-requester="{ row }">
<QTd>
<QBtn flat dense color="primary"> {{ row.requesterName }}</QBtn>
<QBtn flat dense class="link"> {{ row.requesterName }}</QBtn>
<WorkerDescriptorProxy :id="row.requesterFk" />
</QTd>
</template>
@ -292,7 +292,7 @@ onBeforeMount(() => {
</template>
<template #body-cell-concept="{ row }">
<QTd>
<QBtn flat dense color="primary"> {{ row.itemDescription }}</QBtn>
<QBtn flat dense class="link"> {{ row.itemDescription }}</QBtn>
<ItemDescriptorProxy :id="row.itemFk" />
</QTd>
</template>

View File

@ -174,6 +174,16 @@ const decrement = (paramsObj, key) => {
</VnSelect>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QCheckbox
v-model="params.myTeam"
:label="t('params.myTeam')"
@update:model-value="searchFn()"
toggle-indeterminate
/>
</QItemSection>
</QItem>
<QCard bordered>
<QItem>
<QItemSection>
@ -274,11 +284,11 @@ en:
to: To
mine: For me
state: State
myTeam: My team
dateFiltersTooltip: Cannot choose a range of dates and days onward at the same time
denied: Denied
accepted: Accepted
pending: Pending
es:
params:
search: Búsqueda general
@ -291,6 +301,7 @@ es:
to: Hasta
mine: Para mi
state: Estado
myTeam: Mi equipo
dateFiltersTooltip: No se puede seleccionar un rango de fechas y días en adelante a la vez
denied: Denegada
accepted: Aceptada

View File

@ -0,0 +1,31 @@
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';
export const useStateQueryStore = defineStore('stateQueryStore', () => {
const queries = ref(new Set());
function add(query) {
queries.value.add(query);
return query;
}
function isLoading() {
return computed(() => queries.value.size);
}
function remove(query) {
queries.value.delete(query);
}
function reset() {
queries.value = new Set();
}
return {
add,
isLoading,
remove,
queries,
reset,
};
});

View File

@ -7,41 +7,46 @@ vi.mock('src/composables/useSession', () => ({
getToken: () => 'DEFAULT_TOKEN',
isLoggedIn: () => vi.fn(),
destroy: () => vi.fn(),
})
}),
}));
vi.mock('src/stores/useStateQueryStore', () => ({
useStateQueryStore: () => ({
add: () => vi.fn(),
remove: () => vi.fn(),
}),
}));
describe('Axios boot', () => {
describe('onRequest()', async () => {
it('should set the "Authorization" property on the headers', async () => {
const config = { headers: {} };
const resultConfig = onRequest(config);
expect(resultConfig).toEqual(expect.objectContaining({
expect(resultConfig).toEqual(
expect.objectContaining({
headers: {
Authorization: 'DEFAULT_TOKEN'
}
}));
});
Authorization: 'DEFAULT_TOKEN',
},
})
);
});
});
describe('onResponseError()', async () => {
it('should call to the Notify plugin with a message error for an status code "500"', async () => {
Notify.create = vi.fn()
Notify.create = vi.fn();
const error = {
response: {
status: 500
}
status: 500,
},
};
const result = onResponseError(error);
expect(result).rejects.toEqual(
expect.objectContaining(error)
);
expect(result).rejects.toEqual(expect.objectContaining(error));
expect(Notify.create).toHaveBeenCalledWith(
expect.objectContaining({
message: 'An internal server error has ocurred',
@ -51,25 +56,22 @@ describe('Axios boot', () => {
});
it('should call to the Notify plugin with a message from the response property', async () => {
Notify.create = vi.fn()
Notify.create = vi.fn();
const error = {
response: {
status: 401,
data: {
error: {
message: 'Invalid user or password'
}
}
}
message: 'Invalid user or password',
},
},
},
};
const result = onResponseError(error);
expect(result).rejects.toEqual(
expect.objectContaining(error)
);
expect(result).rejects.toEqual(expect.objectContaining(error));
expect(Notify.create).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Invalid user or password',
@ -77,5 +79,5 @@ describe('Axios boot', () => {
})
);
});
})
});
});

View File

@ -0,0 +1,58 @@
import { describe, expect, it, beforeEach, beforeAll } from 'vitest';
import { createWrapper } from 'app/test/vitest/helper';
import { useStateQueryStore } from 'src/stores/useStateQueryStore';
describe('useStateQueryStore', () => {
beforeAll(() => {
createWrapper({}, {});
});
const stateQueryStore = useStateQueryStore();
const { add, isLoading, remove, reset } = useStateQueryStore();
const firstQuery = { url: 'myQuery' };
function getQueries() {
return stateQueryStore.queries;
}
beforeEach(() => {
reset();
expect(getQueries().size).toBeFalsy();
});
it('should add two queries', async () => {
expect(getQueries().size).toBeFalsy();
add(firstQuery);
expect(getQueries().size).toBeTruthy();
expect(getQueries().has(firstQuery)).toBeTruthy();
add();
expect(getQueries().size).toBe(2);
});
it('should add and remove loading state', async () => {
expect(isLoading().value).toBeFalsy();
add(firstQuery);
expect(isLoading().value).toBeTruthy();
remove(firstQuery);
expect(isLoading().value).toBeFalsy();
});
it('should add and remove query', async () => {
const secondQuery = { ...firstQuery };
const thirdQuery = { ...firstQuery };
add(firstQuery);
add(secondQuery);
const beforeCount = getQueries().size;
add(thirdQuery);
expect(getQueries().has(thirdQuery)).toBeTruthy();
remove(thirdQuery);
expect(getQueries().has(thirdQuery)).toBeFalsy();
expect(getQueries().size).toBe(beforeCount);
});
});