Merge pull request '#8581 invoiceIn e2e' (!1452) from 8581-invoiceinE2e into dev

Reviewed-on: #1452
Reviewed-by: Alex Moreno <alexm@verdnatura.es>
This commit is contained in:
Jorge Penadés 2025-03-20 08:57:59 +00:00
commit 25fce95265
28 changed files with 741 additions and 272 deletions

View File

@ -140,7 +140,7 @@ const $props = defineProps({
},
dataCy: {
type: String,
default: 'vn-table',
default: 'vnTable',
},
});
@ -684,7 +684,7 @@ const rowCtrlClickFunction = computed(() => {
@update:selected="emit('update:selected', $event)"
@selection="(details) => handleSelection(details, rows)"
:hide-selected-banner="true"
:data-cy="$props.dataCy ?? 'vnTable'"
:data-cy
>
<template #top-left v-if="!$props.withoutHeader">
<slot name="top-left"> </slot>
@ -781,6 +781,7 @@ const rowCtrlClickFunction = computed(() => {
text-overflow: ellipsis;
white-space: nowrap;
"
:data-cy="`vnTableCell_${col.name}`"
>
<slot
:name="`column-${col.name}`"

View File

@ -27,7 +27,11 @@ const checkboxModel = computed({
</script>
<template>
<div>
<QCheckbox v-bind="$attrs" v-model="checkboxModel" />
<QCheckbox
v-bind="$attrs"
v-model="checkboxModel"
:data-cy="$attrs['data-cy'] ?? `vnCheckbox${$attrs['label'] ?? ''}`"
/>
<QIcon
v-if="info"
v-bind="$attrs"

View File

@ -107,7 +107,7 @@ const manageDate = (date) => {
@click="isPopupOpen = !isPopupOpen"
@keydown="isPopupOpen = false"
hide-bottom-space
:data-cy="$attrs.dataCy ?? $attrs.label + '_inputDate'"
:data-cy="($attrs['data-cy'] ?? $attrs.label) + '_inputDate'"
>
<template #append>
<QIcon

View File

@ -151,7 +151,7 @@ const toModule = computed(() => {
</script>
<template>
<div class="descriptor">
<div class="descriptor" data-cy="cardDescriptor">
<template v-if="entity && !isLoading">
<div class="header bg-primary q-pa-sm justify-between">
<slot name="header-extra-action">
@ -213,18 +213,27 @@ const toModule = computed(() => {
<QList dense>
<QItemLabel header class="ellipsis text-h5" :lines="1">
<div class="title">
<span v-if="$props.title" :title="getValueFromPath(title)">
<span
v-if="$props.title"
:title="getValueFromPath(title)"
:data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_title`"
>
{{ getValueFromPath(title) ?? $props.title }}
</span>
<slot v-else name="description" :entity="entity">
<span :title="entity.name">
{{ entity.name }}
</span>
<span
:title="entity.name"
:data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_description`"
v-text="entity.name"
/>
</slot>
</div>
</QItemLabel>
<QItem>
<QItemLabel class="subtitle" data-cy="descriptor_id">
<QItemLabel
class="subtitle"
:data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_subtitle`"
>
#{{ getValueFromPath(subtitle) ?? entity.id }}
</QItemLabel>
<QBtn
@ -242,7 +251,10 @@ const toModule = computed(() => {
</QBtn>
</QItem>
</QList>
<div class="list-box q-mt-xs">
<div
class="list-box q-mt-xs"
:data-cy="`${$attrs['data-cy'] ?? 'cardDescriptor'}_listbox`"
>
<slot name="body" :entity="entity" />
</div>
</div>

View File

@ -249,7 +249,7 @@ const getLocale = (label) => {
:key="chip.label"
:removable="!unremovableParams?.includes(chip.label)"
@remove="remove(chip.label)"
data-cy="vnFilterPanelChip"
:data-cy="`vnFilterPanelChip_${chip.label}`"
>
<slot
name="tags"

View File

@ -28,7 +28,7 @@ function copyValueText() {
const val = computed(() => $props.value);
</script>
<template>
<div class="vn-label-value">
<div class="vn-label-value" :data-cy="`${$attrs['data-cy'] ?? 'vnLv'}${label ?? ''}`">
<QCheckbox
v-if="typeof value === 'boolean'"
v-model="val"

View File

@ -12,7 +12,7 @@
{{ $t('components.cardDescriptor.moreOptions') }}
</QTooltip>
<QMenu ref="menuRef" data-cy="descriptor-more-opts-menu">
<QList>
<QList data-cy="descriptor-more-opts_list">
<slot name="menu" :menu-ref="$refs.menuRef" />
</QList>
</QMenu>

View File

@ -121,25 +121,40 @@ function deleteFile(dmsFk) {
hide-selected
:is-clearable="false"
:required="true"
data-cy="invoiceInBasicDataSupplier"
/>
<VnInput
clearable
clear-icon="close"
:label="t('invoiceIn.supplierRef')"
v-model="data.supplierRef"
data-cy="invoiceInBasicDataSupplierRef"
/>
</VnRow>
<VnRow>
<VnInputDate :label="t('Expedition date')" v-model="data.issued" />
<VnInputDate
:label="t('Expedition date')"
v-model="data.issued"
data-cy="invoiceInBasicDataIssued"
/>
<VnInputDate
:label="t('Operation date')"
v-model="data.operated"
autofocus
data-cy="invoiceInBasicDataOperated"
/>
</VnRow>
<VnRow>
<VnInputDate :label="t('Entry date')" v-model="data.bookEntried" />
<VnInputDate :label="t('Accounted date')" v-model="data.booked" />
<VnInputDate
:label="t('Entry date')"
v-model="data.bookEntried"
data-cy="invoiceInBasicDatabookEntried"
/>
<VnInputDate
:label="t('Accounted date')"
v-model="data.booked"
data-cy="invoiceInBasicDataBooked"
/>
</VnRow>
<VnRow>
<VnSelect
@ -149,7 +164,7 @@ function deleteFile(dmsFk) {
option-value="id"
option-label="id"
:filter-options="['id', 'name']"
data-cy="UnDeductibleVatSelect"
data-cy="invoiceInBasicDataDeductibleExpenseFk"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
@ -182,6 +197,7 @@ function deleteFile(dmsFk) {
padding="xs"
round
@click="downloadFile(data.dmsFk)"
data-cy="invoiceInBasicDataDmsDownload"
/>
<QBtn
:class="{
@ -197,6 +213,7 @@ function deleteFile(dmsFk) {
documentDialogRef.dms = data.dms;
}
"
data-cy="invoiceInBasicDataDmsEdit"
>
<QTooltip>{{ t('Edit document') }}</QTooltip>
</QBtn>
@ -210,6 +227,7 @@ function deleteFile(dmsFk) {
padding="xs"
round
@click="deleteFile(data.dmsFk)"
data-cy="invoiceInBasicDataDmsDelete"
/>
</div>
<QBtn
@ -224,7 +242,7 @@ function deleteFile(dmsFk) {
delete documentDialogRef.dms;
}
"
data-cy="dms-create"
data-cy="invoiceInBasicDataDmsAdd"
>
<QTooltip>{{ t('Create document') }}</QTooltip>
</QBtn>
@ -237,9 +255,9 @@ function deleteFile(dmsFk) {
:label="t('Currency')"
v-model="data.currencyFk"
:options="currencies"
option-value="id"
option-label="code"
sort-by="id"
data-cy="invoiceInBasicDataCurrencyFk"
/>
<VnSelect
@ -249,8 +267,8 @@ function deleteFile(dmsFk) {
:label="t('Company')"
v-model="data.companyFk"
:options="companies"
option-value="id"
option-label="code"
data-cy="invoiceInBasicDataCompanyFk"
/>
</VnRow>
<VnRow>
@ -260,6 +278,7 @@ function deleteFile(dmsFk) {
:options="sageWithholdings"
option-value="id"
option-label="withholding"
data-cy="invoiceInBasicDataWithholdingSageFk"
/>
</VnRow>
</template>

View File

@ -1,22 +1,16 @@
<script setup>
import { ref, computed, capitalize } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'src/composables/useArrayData';
import CrudModel from 'src/components/CrudModel.vue';
import FetchData from 'src/components/FetchData.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
const route = useRoute();
const { t } = useI18n();
const arrayData = useArrayData();
const invoiceIn = computed(() => arrayData.store.data);
const invoiceInCorrectionRef = ref();
const filter = {
include: { relation: 'invoiceIn' },
where: { correctingFk: route.params.id },
};
const columns = computed(() => [
{
name: 'origin',
@ -92,7 +86,8 @@ const requiredFieldRule = (val) => val || t('globals.requiredField');
v-if="invoiceIn"
data-key="InvoiceInCorrection"
url="InvoiceInCorrections"
:filter="filter"
:user-filter="{ include: { relation: 'invoiceIn' } }"
:filter="{ where: { correctingFk: $route.params.id } }"
auto-load
primary-key="correctingFk"
:default-remove="false"
@ -115,6 +110,7 @@ const requiredFieldRule = (val) => val || t('globals.requiredField');
:option-label="col.optionLabel"
:disable="row.invoiceIn.isBooked"
:filter-options="['description']"
data-cy="invoiceInCorrective_type"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
@ -137,6 +133,7 @@ const requiredFieldRule = (val) => val || t('globals.requiredField');
:rules="[requiredFieldRule]"
:filter-options="['code', 'description']"
:disable="row.invoiceIn.isBooked"
data-cy="invoiceInCorrective_class"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps">
@ -161,6 +158,7 @@ const requiredFieldRule = (val) => val || t('globals.requiredField');
:option-label="col.optionLabel"
:rules="[requiredFieldRule]"
:disable="row.invoiceIn.isBooked"
data-cy="invoiceInCorrective_reason"
/>
</QTd>
</template>

View File

@ -17,10 +17,6 @@ const { t } = useI18n();
const cardDescriptorRef = ref();
const entityId = computed(() => $props.id || +currentRoute.value.params.id);
const totalAmount = ref();
const config = ref();
const cplusRectificationTypes = ref([]);
const siiTypeInvoiceIns = ref([]);
const invoiceCorrectionTypes = ref([]);
const invoiceInCorrection = reactive({ correcting: [], corrected: null });
const routes = reactive({
getSupplier: (id) => {
@ -30,7 +26,7 @@ const routes = reactive({
return {
name: 'InvoiceInList',
query: {
params: JSON.stringify({ supplierFk: id }),
table: JSON.stringify({ supplierFk: id }),
},
};
},
@ -39,7 +35,7 @@ const routes = reactive({
return {
name: 'InvoiceInList',
query: {
params: JSON.stringify({ correctedFk: entityId.value }),
table: JSON.stringify({ correctedFk: entityId.value }),
},
};
}
@ -108,7 +104,7 @@ async function setInvoiceCorrection(id) {
<VnLv :label="t('invoiceIn.list.amount')" :value="toCurrency(totalAmount)" />
<VnLv :label="t('invoiceIn.list.supplier')">
<template #value>
<span class="link">
<span class="link" data-cy="invoiceInDescriptor_supplier">
{{ entity?.supplier?.nickname }}
<SupplierDescriptorProxy :id="entity?.supplierFk" />
</span>

View File

@ -1,5 +1,5 @@
<script setup>
import { ref, computed, toRefs, reactive } from 'vue';
import { ref, computed, toRefs, reactive, onBeforeMount } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
@ -111,10 +111,9 @@ async function cloneInvoice() {
}
const isAgricultural = () => {
if (!config.value) return false;
return (
invoiceIn.value?.supplier?.sageFarmerWithholdingFk ===
config?.value[0]?.sageWithholdingFk
invoiceIn.value?.supplier?.sageWithholdingFk ==
config.value?.sageFarmerWithholdingFk
);
};
function showPdfInvoice() {
@ -153,162 +152,183 @@ const createInvoiceInCorrection = async () => {
);
push({ path: `/invoice-in/${correctingId}/summary` });
};
onBeforeMount(async () => {
config.value = (
await axios.get('invoiceinConfigs/findOne', {
params: { fields: ['sageFarmerWithholdingFk'] },
})
).data;
});
</script>
<template>
<FetchData
url="InvoiceCorrectionTypes"
@on-fetch="(data) => (invoiceCorrectionTypes = data)"
auto-load
/>
<FetchData
url="CplusRectificationTypes"
@on-fetch="(data) => (cplusRectificationTypes = data)"
auto-load
/>
<FetchData
url="SiiTypeInvoiceIns"
:where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceIns = data)"
auto-load
/>
<FetchData
url="InvoiceInConfigs"
:where="{ fields: ['sageWithholdingFk'] }"
auto-load
@on-fetch="(data) => (config = data)"
/>
<InvoiceInToBook>
<template #content="{ book }">
<QItem
v-if="!invoice?.isBooked && canEditProp('toBook')"
v-ripple
clickable
@click="book(entityId)"
<template v-if="config">
<FetchData
url="InvoiceCorrectionTypes"
@on-fetch="(data) => (invoiceCorrectionTypes = data)"
auto-load
/>
<FetchData
url="CplusRectificationTypes"
@on-fetch="(data) => (cplusRectificationTypes = data)"
auto-load
/>
<FetchData
url="SiiTypeInvoiceIns"
:where="{ code: { like: 'R%' } }"
@on-fetch="(data) => (siiTypeInvoiceIns = data)"
auto-load
/>
<InvoiceInToBook>
<template #content="{ book }">
<QItem
v-if="!invoice?.isBooked && canEditProp('toBook')"
v-ripple
clickable
@click="book(entityId)"
>
<QItemSection>{{ t('invoiceIn.descriptorMenu.book') }}</QItemSection>
</QItem>
</template>
</InvoiceInToBook>
<QItem
v-if="invoice?.isBooked && canEditProp('toUnbook')"
v-ripple
clickable
@click="triggerMenu('unbook')"
>
<QItemSection>
{{ t('invoiceIn.descriptorMenu.unbook') }}
</QItemSection>
</QItem>
<QItem
v-if="canEditProp('deleteById')"
v-ripple
clickable
@click="triggerMenu('delete')"
>
<QItemSection>{{ t('invoiceIn.descriptorMenu.deleteInvoice') }}</QItemSection>
</QItem>
<QItem
v-if="canEditProp('clone')"
v-ripple
clickable
@click="triggerMenu('clone')"
>
<QItemSection>{{ t('invoiceIn.descriptorMenu.cloneInvoice') }}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('showPdf')">
<QItemSection>{{
t('invoiceIn.descriptorMenu.showAgriculturalPdf')
}}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('sendPdf')">
<QItemSection
>{{ t('invoiceIn.descriptorMenu.sendAgriculturalPdf') }}...</QItemSection
>
<QItemSection>{{ t('invoiceIn.descriptorMenu.book') }}</QItemSection>
</QItem>
</template>
</InvoiceInToBook>
<QItem
v-if="invoice?.isBooked && canEditProp('toUnbook')"
v-ripple
clickable
@click="triggerMenu('unbook')"
>
<QItemSection>
{{ t('invoiceIn.descriptorMenu.unbook') }}
</QItemSection>
</QItem>
<QItem
v-if="canEditProp('deleteById')"
v-ripple
clickable
@click="triggerMenu('delete')"
>
<QItemSection>{{ t('invoiceIn.descriptorMenu.deleteInvoice') }}</QItemSection>
</QItem>
<QItem v-if="canEditProp('clone')" v-ripple clickable @click="triggerMenu('clone')">
<QItemSection>{{ t('invoiceIn.descriptorMenu.cloneInvoice') }}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('showPdf')">
<QItemSection>{{
t('invoiceIn.descriptorMenu.showAgriculturalPdf')
}}</QItemSection>
</QItem>
<QItem v-if="isAgricultural()" v-ripple clickable @click="triggerMenu('sendPdf')">
<QItemSection
>{{ t('invoiceIn.descriptorMenu.sendAgriculturalPdf') }}...</QItemSection
</QItem>
<QItem
v-if="!invoiceInCorrection.corrected"
v-ripple
clickable
@click="triggerMenu('correct')"
data-cy="createCorrectiveItem"
>
</QItem>
<QItem
v-if="!invoiceInCorrection.corrected"
v-ripple
clickable
@click="triggerMenu('correct')"
data-cy="createCorrectiveItem"
>
<QItemSection
>{{ t('invoiceIn.descriptorMenu.createCorrective') }}...</QItemSection
<QItemSection
>{{ t('invoiceIn.descriptorMenu.createCorrective') }}...</QItemSection
>
</QItem>
<QItem
v-if="invoice.dmsFk"
v-ripple
clickable
@click="downloadFile(invoice.dmsFk)"
>
</QItem>
<QItem v-if="invoice.dmsFk" v-ripple clickable @click="downloadFile(invoice.dmsFk)">
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
</QItem>
<QDialog ref="correctionDialogRef">
<QCard>
<QCardSection>
<QItem class="q-px-none">
<span class="text-primary text-h6 full-width">
{{ t('Create rectificative invoice') }}
</span>
<QBtn icon="close" flat round dense v-close-popup />
</QItem>
</QCardSection>
<QCardSection>
<QItem>
<QItemSection>
<QInput
:label="t('Original invoice')"
v-model="entityId"
readonly
/>
<VnSelect
:label="`${useCapitalize(t('globals.class'))}`"
v-model="correctionFormData.invoiceClass"
:options="siiTypeInvoiceIns"
option-value="id"
option-label="code"
:required="true"
/>
</QItemSection>
<QItemSection>
<VnSelect
:label="`${useCapitalize(t('globals.type'))}`"
v-model="correctionFormData.invoiceType"
:options="cplusRectificationTypes"
option-value="id"
option-label="description"
:required="true"
>
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel
>{{ opt.id }} -
{{ opt.description }}</QItemLabel
>
</QItemSection>
</QItem>
<div></div>
</template>
</VnSelect>
<QItemSection>{{ t('components.smartCard.downloadFile') }}</QItemSection>
</QItem>
<QDialog ref="correctionDialogRef">
<QCard data-cy="correctiveInvoiceDialog">
<QCardSection>
<QItem class="q-px-none">
<span class="text-primary text-h6 full-width">
{{ t('Create rectificative invoice') }}
</span>
<QBtn icon="close" flat round dense v-close-popup />
</QItem>
</QCardSection>
<QCardSection>
<QItem>
<QItemSection>
<QInput
:label="t('Original invoice')"
v-model="entityId"
readonly
/>
<VnSelect
:label="`${useCapitalize(t('globals.class'))}`"
v-model="correctionFormData.invoiceClass"
:options="siiTypeInvoiceIns"
option-value="id"
option-label="code"
:required="true"
data-cy="invoiceInDescriptorMenu_class"
/>
</QItemSection>
<QItemSection>
<VnSelect
:label="`${useCapitalize(t('globals.type'))}`"
v-model="correctionFormData.invoiceType"
:options="cplusRectificationTypes"
option-value="id"
option-label="description"
:required="true"
data-cy="invoiceInDescriptorMenu_type"
>
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
<QItemSection>
<QItemLabel
>{{ opt.id }} -
{{ opt.description }}</QItemLabel
>
</QItemSection>
</QItem>
<div></div>
</template>
</VnSelect>
<VnSelect
:label="`${useCapitalize(t('globals.reason'))}`"
v-model="correctionFormData.invoiceReason"
:options="invoiceCorrectionTypes"
option-value="id"
option-label="description"
:required="true"
/>
</QItemSection>
</QItem>
</QCardSection>
<QCardActions class="justify-end q-mr-sm">
<QBtn flat :label="t('globals.close')" color="primary" v-close-popup />
<QBtn
:label="t('globals.save')"
color="primary"
v-close-popup
@click="createInvoiceInCorrection"
:disable="isNotFilled"
/>
</QCardActions>
</QCard>
</QDialog>
<VnSelect
:label="`${useCapitalize(t('globals.reason'))}`"
v-model="correctionFormData.invoiceReason"
:options="invoiceCorrectionTypes"
option-value="id"
option-label="description"
:required="true"
data-cy="invoiceInDescriptorMenu_reason"
/>
</QItemSection>
</QItem>
</QCardSection>
<QCardActions class="justify-end q-mr-sm">
<QBtn
flat
:label="t('globals.close')"
color="primary"
v-close-popup
/>
<QBtn
:label="t('globals.save')"
color="primary"
v-close-popup
@click="createInvoiceInCorrection"
:disable="isNotFilled"
data-cy="saveCorrectiveInvoice"
/>
</QCardActions>
</QCard>
</QDialog>
</template>
</template>
<i18n>
en:
isNotLinked: The entry {bookEntry} has been deleted with {accountingEntries} entries

View File

@ -198,6 +198,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
color="orange-11"
text-color="black"
@click="book(entityId)"
data-cy="invoiceInSummary_book"
/>
</template>
</InvoiceIntoBook>
@ -219,7 +220,7 @@ const getLink = (param) => `#/invoice-in/${entityId.value}/${param}`;
:value="entity.supplier?.name"
>
<template #value>
<span class="link">
<span class="link" data-cy="invoiceInSummary_supplier">
{{ entity.supplier?.name }}
<SupplierDescriptorProxy :id="entity.supplierFk" />
</span>

View File

@ -202,6 +202,9 @@ function setCursor(ref) {
:option-label="col.optionLabel"
:filter-options="['id', 'name']"
:tooltip="t('Create a new expense')"
:acls="[
{ model: 'Expense', props: '*', accessType: 'WRITE' },
]"
@keydown.tab.prevent="
autocompleteExpense(
$event,

View File

@ -7,6 +7,7 @@ import VnInputNumber from 'src/components/common/VnInputNumber.vue';
import { dateRange } from 'src/filters';
import { date } from 'quasar';
import VnSelectSupplier from 'src/components/common/VnSelectSupplier.vue';
import VnCheckbox from 'src/components/common/VnCheckbox.vue';
defineProps({ dataKey: { type: String, required: true } });
const dateFormat = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
@ -147,13 +148,13 @@ function handleDaysAgo(params, daysAgo) {
</QItem>
<QItem>
<QItemSection>
<QCheckbox
<VnCheckbox
:label="$t('invoiceIn.isBooked')"
v-model="params.isBooked"
@update:model-value="searchFn()"
toggle-indeterminate
/>
<QCheckbox
<VnCheckbox
:label="getLocale('params.correctingFk')"
v-model="params.correctingFk"
@update:model-value="searchFn()"

View File

@ -4,7 +4,7 @@ import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
import { useArrayData } from 'src/composables/useArrayData';
import qs from 'qs';
const { notify, dialog } = useQuasar();
const { t } = useI18n();
@ -61,17 +61,15 @@ async function checkToBook(id) {
}
async function toBook(id) {
let type = 'positive';
let message = t('globals.dataSaved');
let err = false;
try {
await axios.post(`InvoiceIns/${id}/toBook`);
store.data.isBooked = true;
} catch (e) {
type = 'negative';
message = t('It was not able to book the invoice');
err = true;
throw e;
} finally {
notify({ type, message });
if (!err) notify({ type: 'positive', message: t('globals.dataSaved') });
}
}
</script>

View File

@ -55,9 +55,9 @@ describe('OrderCatalog', () => {
it('removes a secondary tag', () => {
cy.get(':nth-child(1) > [data-cy="catalogFilterCategory"]').click();
cy.selectOption('[data-cy="catalogFilterType"]', 'Anthurium');
cy.dataCy('vnFilterPanelChip').should('exist');
cy.dataCy('vnFilterPanelChip_typeFk').should('exist');
cy.get('[data-cy="catalogFilterCustomTag"] > .q-chip__icon--remove').click();
cy.dataCy('vnFilterPanelChip').should('not.exist');
cy.dataCy('vnFilterPanelChip_typeFk').should('not.exist');
});
it('Removes category tag', () => {

View File

@ -53,7 +53,7 @@ describe.skip('Client list', () => {
it('Client founded create ticket', () => {
const search = 'Jessica Jones';
cy.searchByLabel('Name', search);
cy.openActionDescriptor('Create ticket');
cy.selectDescriptorOption();
cy.waitForElement('#formModel');
cy.waitForElement('.q-form');
cy.checkValueForm(1, search);

View File

@ -1,26 +1,40 @@
/// <reference types="cypress" />
import moment from 'moment';
describe('InvoiceInBasicData', () => {
const firstFormSelect = '.q-card > .vn-row:nth-child(1) > .q-select';
const dialogInputs = '.q-dialog input';
const resetBtn = '.q-btn-group--push > .q-btn--flat';
const getDocumentBtns = (opt) => `[data-cy="dms-buttons"] > :nth-child(${opt})`;
const futureDate = moment().add(1, 'days').format('DD-MM-YYYY');
const mock = {
invoiceInBasicDataSupplier: { val: 'Bros nick', type: 'select' },
invoiceInBasicDataSupplierRef_input: 'mockInvoice41',
invoiceInBasicDataIssued: { val: futureDate, type: 'date' },
invoiceInBasicDataOperated: { val: futureDate, type: 'date' },
invoiceInBasicDatabookEntried: { val: futureDate, type: 'date' },
invoiceInBasicDataBooked: {
val: moment().add(5, 'days').format('DD-MM-YYYY'),
type: 'date',
},
invoiceInBasicDataDeductibleExpenseFk: {
val: '4751000000',
type: 'select',
},
invoiceInBasicDataCurrencyFk: { val: 'USD', type: 'select' },
invoiceInBasicDataCompanyFk: { val: 'CCs', type: 'select' },
invoiceInBasicDataWithholdingSageFk: {
val: 'Arrendamiento y subarrendamiento',
type: 'select',
},
};
beforeEach(() => {
cy.login('developer');
cy.login('administrative');
cy.visit(`/#/invoice-in/1/basic-data`);
});
it('should edit the provideer and supplier ref', () => {
cy.dataCy('UnDeductibleVatSelect').type('4751000000');
cy.get('.q-menu .q-item').contains('4751000000').click();
cy.get(resetBtn).click();
cy.waitForElement('#formModel').within(() => {
cy.dataCy('vnSupplierSelect').type('Bros nick');
})
cy.get('.q-menu .q-item').contains('Bros nick').click();
it('should edit every field', () => {
cy.fillInForm(mock, { attr: 'data-cy' });
cy.saveCard();
cy.get(`${firstFormSelect} input`).invoke('val').should('eq', 'Bros nick');
cy.validateForm(mock, { attr: 'data-cy' });
});
it('should edit, remove and create the dms data', () => {
@ -44,7 +58,7 @@ describe('InvoiceInBasicData', () => {
cy.checkNotification('Data saved');
//create
cy.get('[data-cy="dms-create"]').eq(0).click();
cy.get('[data-cy="invoiceInBasicDataDmsAdd"]').eq(0).click();
cy.get('[data-cy="VnDms_inputFile"').selectFile(
'test/cypress/fixtures/image.jpg',
{

View File

@ -1,22 +1,59 @@
/// <reference types="cypress" />
describe('InvoiceInCorrective', () => {
const saveDialog = '.q-card > .q-card__actions > .q-btn--standard ';
describe('invoiceInCorrective', () => {
beforeEach(() => cy.login('administrative'));
it('should create a correcting invoice', () => {
cy.viewport(1280, 720);
cy.login('developer');
cy.visit(`/#/invoice-in/1/summary`);
it('should modify the invoice', () => {
cy.visit('/#/invoice-in/1/summary');
cy.intercept('POST', '/api/InvoiceIns/corrective').as('corrective');
cy.intercept('POST', '/api/InvoiceInCorrections/crud').as('crud');
cy.intercept('GET', /InvoiceInCorrections\?filter=.+/).as('getCorrective');
cy.openActionsDescriptor();
cy.selectDescriptorOption(4);
cy.dataCy('saveCorrectiveInvoice').click();
cy.dataCy('createCorrectiveItem').click();
cy.get(saveDialog).click();
cy.wait('@corrective').then((interception) => {
const correctingId = interception.response.body;
cy.url().should('include', `/invoice-in/${correctingId}/summary`);
cy.visit(`/#/invoice-in/${correctingId}/corrective`);
cy.wait('@corrective').then(({ response }) => {
const correctingFk = response.body;
cy.url().should('include', `/invoice-in/${correctingFk}/summary`);
cy.visit(`/#/invoice-in/${correctingFk}/corrective`);
cy.selectOption('[data-cy="invoiceInCorrective_class"]', 'r4');
cy.selectOption('[data-cy="invoiceInCorrective_type"]', 'sustitución');
cy.selectOption('[data-cy="invoiceInCorrective_reason"]', 'vat');
cy.dataCy('crudModelDefaultSaveBtn').click();
cy.wait('@crud');
cy.reload();
cy.wait('@getCorrective');
cy.validateRow('tbody > :nth-of-type(1)', [
,
'S Por sustitución',
'R4',
'Error in VAT calculation',
]);
});
cy.get('tbody > tr:visible').should('have.length', 1);
});
it('should not be able to modify the invoice if the original invoice is booked', () => {
cy.intercept('POST', '/api/InvoiceIns/corrective').as('corrective');
cy.visit('/#/invoice-in/4/summary');
cy.selectDescriptorOption();
cy.dataCy('VnConfirm_confirm').click();
cy.selectDescriptorOption(4);
cy.dataCy('saveCorrectiveInvoice').click();
cy.wait('@corrective').then(({ response }) => {
const correctingFk = response.body;
cy.url().should('include', `/invoice-in/${correctingFk}/summary`);
cy.visit(`/#/invoice-in/${correctingFk}/corrective`);
cy.dataCy('invoiceInCorrective_class').should('be.disabled');
cy.dataCy('invoiceInCorrective_type').should('be.disabled');
cy.dataCy('invoiceInCorrective_reason').should('be.disabled');
});
});
it('should show/hide the section if it is a corrective invoice', () => {
cy.visit('/#/invoice-in/1/summary');
cy.get('[data-cy="InvoiceInCorrective-menu-item"]').should('not.exist');
cy.clicDescriptorAction(4);
cy.get('[data-cy="InvoiceInCorrective-menu-item"]').should('exist');
});
});

View File

@ -1,21 +1,147 @@
describe('InvoiceInDescriptor', () => {
const book = '.summaryHeader > .no-wrap > .q-btn';
const firstDescritorOpt = '.q-menu > .q-list > :nth-child(5) > .q-item__section';
const checkbox = ':nth-child(5) > .q-checkbox';
beforeEach(() => cy.login('administrative'));
it('should booking and unbooking the invoice properly', () => {
cy.viewport(1280, 720);
cy.login('developer');
cy.visit('/#/invoice-in/1/summary');
cy.waitForElement('.q-page');
describe('more options', () => {
it('should booking and unbooking the invoice properly', () => {
const checkbox = '[data-cy="vnLvIs booked"] > .q-checkbox';
cy.visit('/#/invoice-in/2/summary');
cy.selectDescriptorOption();
cy.dataCy('VnConfirm_confirm').click();
cy.validateCheckbox(checkbox);
cy.selectDescriptorOption();
cy.dataCy('VnConfirm_confirm').click();
cy.validateCheckbox(checkbox, false);
});
cy.get(book).click();
cy.dataCy('VnConfirm_confirm').click();
cy.get(checkbox).invoke('attr', 'aria-checked').should('eq', 'true');
it('should delete the invoice properly', () => {
cy.visit('/#/invoice-in/2/summary');
cy.selectDescriptorOption(2);
cy.clickConfirm();
cy.checkNotification('invoice deleted');
});
cy.dataCy('descriptor-more-opts').first().click();
cy.get(firstDescritorOpt).click();
cy.dataCy('VnConfirm_confirm').click();
cy.get(checkbox).invoke('attr', 'aria-checked').should('eq', 'false');
it('should clone the invoice properly', () => {
cy.visit('/#/invoice-in/3/summary');
cy.selectDescriptorOption(3);
cy.clickConfirm();
cy.checkNotification('Invoice cloned');
});
it('should show the agricultural PDF properly', () => {
cy.visit('/#/invoice-in/6/summary');
cy.validatePdfDownload(
/api\/InvoiceIns\/6\/invoice-in-pdf\?access_token=.*/,
() => cy.selectDescriptorOption(4),
);
});
it('should send the agricultural PDF properly', () => {
cy.intercept('POST', 'api/InvoiceIns/6/invoice-in-email').as('sendEmail');
cy.visit('/#/invoice-in/6/summary');
cy.selectDescriptorOption(5);
cy.dataCy('SendEmailNotifiactionDialogInput_input').type(
'{selectall}jorgito@gmail.mx',
);
cy.clickConfirm();
cy.checkNotification('Notification sent');
cy.wait('@sendEmail').then(({ request, response }) => {
expect(request.body).to.deep.equal({
recipientId: 2,
recipient: 'jorgito@gmail.mx',
});
expect(response.statusCode).to.equal(200);
});
});
// https://redmine.verdnatura.es/issues/8767
it.skip('should download the file properly', () => {
cy.visit('/#/invoice-in/1/summary');
cy.validateDownload(() => cy.selectDescriptorOption(5));
});
});
describe('buttons', () => {
beforeEach(() => cy.visit('/#/invoice-in/1/summary'));
it('should navigate to the supplier summary', () => {
cy.clicDescriptorAction(1);
cy.url().should('to.match', /supplier\/\d+\/summary/);
});
it('should navigate to the entry summary', () => {
cy.clicDescriptorAction(2);
cy.url().should('to.match', /entry\/\d+\/summary/);
});
it('should navigate to the invoiceIn list', () => {
cy.clicDescriptorAction(3);
cy.url().should('to.match', /invoice-in\/list\?table=\{.*supplierFk.+\}/);
});
});
describe('corrective', () => {
const originalId = 4;
beforeEach(() => cy.visit(`/#/invoice-in/${originalId}/summary`));
it('should create a correcting invoice and redirect to original invoice', () => {
createCorrective();
redirect(originalId);
});
it('should create a correcting invoice and navigate to list filtered by corrective', () => {
createCorrective();
redirect(originalId);
cy.clicDescriptorAction(4);
cy.validateVnTableRows({
cols: [
{
name: 'supplierRef',
val: '1237',
operation: 'include',
},
],
});
});
});
describe('link', () => {
it('should open the supplier descriptor popup', () => {
cy.visit('/#/invoice-in/1/summary');
cy.intercept('GET', /Suppliers\/\d+/).as('getSupplier');
cy.dataCy('invoiceInDescriptor_supplier').then(($el) => {
const alias = $el.text().trim();
$el.click();
cy.wait('@getSupplier').then(() =>
cy.validateDescriptor({ listbox: { 1: alias }, popup: true }),
);
});
});
});
});
function createCorrective() {
cy.intercept('POST', '/api/InvoiceIns/corrective').as('corrective');
cy.selectDescriptorOption(4);
cy.dataCy('saveCorrectiveInvoice').click();
cy.wait('@corrective').then(({ response }) => {
const correctingId = response.body;
cy.url().should('include', `/invoice-in/${correctingId}/summary`);
cy.visit(`/#/invoice-in/${correctingId}/corrective`);
cy.dataCy('invoiceInCorrective_class').should('contain.value', 'R2');
cy.dataCy('invoiceInCorrective_type').should('contain.value', 'diferencias');
cy.dataCy('invoiceInCorrective_reason').should('contain.value', 'sales details');
});
}
function redirect(subtitle) {
const regex = new RegExp(`InvoiceIns/${subtitle}\\?filter=.*`);
cy.intercept('GET', regex).as('getOriginal');
cy.clicDescriptorAction(4);
cy.wait('@getOriginal');
cy.validateDescriptor({ subtitle });
}

View File

@ -4,7 +4,7 @@ describe('InvoiceInDueDay', () => {
const addBtn = '.q-page-sticky > div > .q-btn > .q-btn__content';
beforeEach(() => {
cy.login('developer');
cy.login('administrative');
cy.visit(`/#/invoice-in/6/due-day`);
});

View File

@ -6,7 +6,7 @@ describe('InvoiceInIntrastat', () => {
const firstRowAmount = `${firstRow} > :nth-child(3)`;
beforeEach(() => {
cy.login('developer');
cy.login('administrative');
cy.visit(`/#/invoice-in/1/intrastat`);
});

View File

@ -1,13 +1,21 @@
/// <reference types="cypress" />
describe('InvoiceInList', () => {
const firstRow = 'tbody.q-virtual-scroll__content tr:nth-child(1)';
const firstId = `${firstRow} > td:nth-child(2) span`;
const firstDetailBtn = `${firstRow} .q-btn:nth-child(1)`;
const summaryHeaders = '.summaryBody .header-link';
const mockInvoiceRef = `createMockInvoice${Math.floor(Math.random() * 100)}`;
const mock = {
vnSupplierSelect: { val: 'farmer king', type: 'select' },
'Invoice nº_input': mockInvoiceRef,
Company_select: { val: 'orn', type: 'select' },
'Expedition date_inputDate': '16-11-2001',
};
beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer');
cy.login('administrative');
cy.visit(`/#/invoice-in/list`);
cy.get('#searchbar input').type('{enter}');
});
@ -27,4 +35,18 @@ describe('InvoiceInList', () => {
cy.get(summaryHeaders).eq(1).contains('Basic data');
cy.get(summaryHeaders).eq(4).contains('Vat');
});
it('should create a new Invoice', () => {
cy.dataCy('vnTableCreateBtn').click();
cy.fillInForm({ ...mock }, { attr: 'data-cy' });
cy.dataCy('FormModelPopup_save').click();
cy.intercept('GET', /\/api\/InvoiceIns\/\d+\/getTotals$/).as('invoice');
cy.wait('@invoice').then(() =>
cy.validateDescriptor({
title: mockInvoiceRef,
listBox: { 0: '11/16/2001', 3: 'The farmer' },
}),
);
cy.get('[data-cy="vnLvCompany"]').should('contain.text', 'ORN');
});
});

View File

@ -0,0 +1,23 @@
describe('InvoiceInSerial', () => {
beforeEach(() => {
cy.login('administrative');
cy.visit('#/invoice-in/serial');
});
it('should filter by serial number', () => {
cy.dataCy('serial_input').type('R{enter}');
cy.validateVnTableRows({ cols: [{ name: 'serial', val: 'r' }] });
});
it('should filter by last days ', () => {
let before;
cy.dataCy('vnTableCell_total')
.invoke('text')
.then((total) => (before = +total));
cy.dataCy('Last days_input').type('{selectall}1{enter}');
cy.dataCy('vnTableCell_total')
.invoke('text')
.then((total) => expect(+total).to.be.lessThan(before));
});
});

View File

@ -0,0 +1,24 @@
describe('InvoiceInSummary', () => {
beforeEach(() => {
cy.login('administrative');
cy.visit('/#/invoice-in/3/summary');
});
it('should booking and unbooking the invoice properly', () => {
const checkbox = '[data-cy="vnLvIs booked"] > .q-checkbox';
cy.dataCy('invoiceInSummary_book').click();
cy.dataCy('VnConfirm_confirm').click();
cy.validateCheckbox(checkbox);
});
it('should open the supplier descriptor popup', () => {
cy.intercept('GET', /Suppliers\/\d+/).as('getSupplier');
cy.dataCy('invoiceInSummary_supplier').then(($el) => {
const description = $el.text().trim();
$el.click();
cy.wait('@getSupplier').then(() =>
cy.validateDescriptor({ description, popup: true }),
);
});
});
});

View File

@ -8,7 +8,7 @@ describe('InvoiceInVat', () => {
const randomInt = Math.floor(Math.random() * 100);
beforeEach(() => {
cy.login('developer');
cy.login('administrative');
cy.visit(`/#/invoice-in/1/vat`);
});

View File

@ -25,7 +25,7 @@ describe('VnLog', () => {
it('should show claimDescriptor', () => {
cy.dataCy('iconLaunch-claimFk').first().click();
cy.dataCy('descriptor_id').contains('1');
cy.dataCy('cardDescriptor_subtitle').contains('1');
cy.dataCy('iconLaunch-claimFk').first().click();
});
});

View File

@ -27,8 +27,9 @@
// DO NOT REMOVE
// Imports Quasar Cypress AE predefined commands
// import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress';
import moment from 'moment';
import waitUntil from './waitUntil';
Cypress.Commands.add('waitUntil', { prevSubject: 'optional' }, waitUntil);
Cypress.Commands.add('resetDB', () => {
@ -122,9 +123,10 @@ function selectItem(selector, option, ariaControl, hasWrite = true) {
cy.waitSpinner();
getItems(ariaControl).then((items) => {
const matchingItem = items
.toArray()
.find((item) => item.innerText.includes(option));
const matchingItem = items.toArray().find((item) => {
const val = typeof option == 'string' ? option.toLowerCase() : option;
return item.innerText.toLowerCase().includes(val);
});
if (matchingItem) return cy.wrap(matchingItem).click();
if (hasWrite) cy.get(selector).clear().type(option);
@ -160,14 +162,20 @@ Cypress.Commands.add('countSelectOptions', (selector, option) => {
cy.get('.q-menu .q-item').should('have.length', option);
});
Cypress.Commands.add('fillInForm', (obj, form = '.q-form > .q-card') => {
Cypress.Commands.add('fillInForm', (obj, opts = {}) => {
cy.waitSpinner();
const { form = '.q-form > .q-card', attr = 'aria-label' } = opts;
cy.waitForElement(form);
cy.get(`${form} input`).each(([el]) => {
cy.wrap(el)
.invoke('attr', 'aria-label')
.then((ariaLabel) => {
const field = obj[ariaLabel];
.invoke('attr', attr)
.then((key) => {
const field = obj[key];
if (!field) return;
if (typeof field == 'string')
return cy
.wrap(el)
.type(`{selectall}{backspace}${field}`, { delay: 0 });
const { type, val } = field;
switch (type) {
@ -175,7 +183,9 @@ Cypress.Commands.add('fillInForm', (obj, form = '.q-form > .q-card') => {
cy.selectOption(el, val);
break;
case 'date':
cy.get(el).type(val.split('-').join(''));
cy.get(el).type(
`{selectall}{backspace}${val.split('-').join('')}`,
);
break;
case 'time':
cy.get(el).click();
@ -191,6 +201,40 @@ Cypress.Commands.add('fillInForm', (obj, form = '.q-form > .q-card') => {
});
});
Cypress.Commands.add('validateForm', (obj, opts = {}) => {
const { form = '.q-form > .q-card', attr = 'data-cy' } = opts;
cy.waitForElement(form);
cy.get(`${form} input`).each(([el]) => {
cy.wrap(el)
.invoke('attr', attr)
.then((key) => {
const field = obj[key];
if (!field) return;
const { type, val } = field;
cy.get(el)
.invoke('val')
.then((elVal) => {
if (typeof field == 'string')
expect(elVal.toLowerCase()).to.equal(field.toLowerCase());
else
switch (type) {
case 'date':
const elDate = moment(elVal, 'DD-MM-YYYY');
const mockDate = moment(val, 'DD-MM-YYYY');
expect(elDate.isSame(mockDate, 'day')).to.be.true;
break;
default:
expect(elVal.toLowerCase()).to.equal(
val.toLowerCase(),
);
break;
}
});
});
});
});
Cypress.Commands.add('checkOption', (selector) => {
cy.get(selector).find('.q-checkbox__inner').click();
});
@ -207,14 +251,17 @@ Cypress.Commands.add('saveCard', () => {
Cypress.Commands.add('resetCard', () => {
cy.get('[title="Reset"]').click();
});
Cypress.Commands.add('removeCard', () => {
cy.get('[title="Remove"]').click();
});
Cypress.Commands.add('addCard', () => {
cy.waitForElement('tbody');
cy.waitForElement('.q-page-sticky > div > .q-btn');
cy.get('.q-page-sticky > div > .q-btn').click();
});
Cypress.Commands.add('clickConfirm', () => {
cy.waitForElement('.q-dialog__inner > .q-card');
cy.get('.q-card__actions > .q-btn--unelevated > .q-btn__content > .block').click();
@ -295,6 +342,7 @@ Cypress.Commands.add('removeRow', (rowIndex) => {
});
});
});
Cypress.Commands.add('openListSummary', (row) => {
cy.get('.card-list-body .actions .q-btn:nth-child(2)').eq(row).click();
});
@ -322,20 +370,8 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => {
cy.get(selector).should('have.text', expectedValue);
});
Cypress.Commands.add('openActionDescriptor', (opt) => {
cy.openActionsDescriptor();
const listItem = '[role="menu"] .q-list .q-item';
cy.contains(listItem, opt).click();
});
Cypress.Commands.add('openActionsDescriptor', () => {
cy.get('[data-cy="descriptor-more-opts"]').click();
});
Cypress.Commands.add('clickButtonDescriptor', (id) => {
cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`)
.invoke('removeAttr', 'target')
.click();
cy.get('[data-cy="cardDescriptor"] [data-cy="descriptor-more-opts"]').click();
});
Cypress.Commands.add('openUserPanel', () => {
@ -424,3 +460,137 @@ Cypress.Commands.add('searchBtnFilterPanel', () => {
Cypress.Commands.add('waitRequest', (alias, cb) => {
cy.wait(alias).then(cb);
});
Cypress.Commands.add('validateDescriptor', (toCheck = {}) => {
const { title, description, subtitle, listbox = {}, popup = false } = toCheck;
const popupSelector = popup ? '[role="menu"] ' : '';
if (title) cy.get(`${popupSelector}[data-cy="cardDescriptor_title"]`).contains(title);
if (description)
cy.get(`${popupSelector}[data-cy="cardDescriptor_description"]`).contains(
description,
);
if (subtitle)
cy.get(`${popupSelector}[data-cy="cardDescriptor_subtitle"]`).contains(subtitle);
for (const index in listbox)
cy.get(`${popupSelector}[data-cy="cardDescriptor_listbox"] > *`)
.eq(index)
.should('contain.text', listbox[index]);
});
Cypress.Commands.add('validateVnTableRows', (opts = {}) => {
let { cols = [], rows = [] } = opts;
if (!Array.isArray(cols)) cols = [cols];
const rowSelector = rows.length
? rows.map((row) => `> :nth-child(${row})`).join(', ')
: '> *';
cy.get(`[data-cy="vnTable"] .q-virtual-scroll__content`).within(() => {
cy.get(`${rowSelector}`).each(($el) => {
for (const { name, type = 'string', val, operation = 'equal' } of cols) {
cy.wrap($el)
.find(`[data-cy="vnTableCell_${name}"]`)
.invoke('text')
.then((text) => {
if (type === 'string')
expect(text.trim().toLowerCase()).to[operation](
val.toLowerCase(),
);
if (type === 'number') cy.checkNumber(text, val, operation);
if (type === 'date') cy.checkDate(text, val, operation);
});
}
});
});
});
Cypress.Commands.add('checkDate', (rawDate, expectedVal, operation) => {
const date = moment(rawDate.trim(), 'MM/DD/YYYY');
const compareDate = moment(expectedVal, 'DD/MM/YYYY');
switch (operation) {
case 'equal':
expect(text.trim()).to.equal(compareDate);
break;
case 'before':
expect(date.isBefore(compareDate)).to.be.true;
break;
case 'after':
expect(date.isAfter(compareDate)).to.be.true;
}
});
Cypress.Commands.add('selectDescriptorOption', (opt = 1) => {
const listItem = '[data-cy="descriptor-more-opts_list"]';
cy.get('body').then(($body) => {
if (!$body.find(listItem).length) cy.openActionsDescriptor();
});
cy.waitForElement(listItem);
cy.get(`${listItem} > :not(template):nth-of-type(${opt})`).click();
});
Cypress.Commands.add('validateCheckbox', (selector, expectedVal = 'true') => {
cy.get(selector).should('have.attr', 'aria-checked', expectedVal.toString());
});
Cypress.Commands.add('validateDownload', (trigger, opts = {}) => {
const {
url = /api\/dms\/\d+\/downloadFile\?access_token=.+/,
types = ['text/plain', 'image/jpeg'],
alias = 'download',
} = opts;
cy.intercept('GET', url).as(alias);
trigger().then(() => {
cy.wait(`@${alias}`).then(({ response }) => {
expect(response.statusCode).to.equal(200);
const isValidType = types.some((type) =>
response.headers['content-type'].includes(type),
);
expect(isValidType).to.be.true;
});
});
});
Cypress.Commands.add('validatePdfDownload', (match, trigger) => {
cy.window().then((win) => {
cy.stub(win, 'open')
.callsFake(() => null)
.as('pdf');
});
trigger();
cy.get('@pdf')
.should('be.calledOnce')
.then((stub) => {
const [url] = stub.getCall(0).args;
expect(url).to.match(match);
cy.request(url).then((response) =>
expect(response.headers['content-type']).to.include('application/pdf'),
);
});
});
Cypress.Commands.add('clicDescriptorAction', (index = 1) => {
cy.get(`[data-cy="descriptor_actions"] .q-btn:nth-of-type(${index})`).click();
});
Cypress.Commands.add('checkQueryParams', (expectedParams = {}) => {
cy.url().then((url) => {
const urlParams = new URLSearchParams(url.split('?')[1]);
for (const key in expectedParams) {
const expected = expectedParams[key];
const param = JSON.parse(decodeURIComponent(urlParams.get(key)));
if (typeof expected === 'object') {
const { subkey, val } = expected;
expect(param[subkey]).to.equal(val);
} else expect(param).to.equal(expected);
}
});
});
Cypress.Commands.add('waitTableScrollLoad', () =>
cy.waitForElement('[data-q-vs-anchor]'),
);