Merge pull request 'Cypress initial config and tests' (!90) from wbuezas/hedera-web-mindshore:feature/CypressInit into 4922-vueMigration

Reviewed-on: #90
Reviewed-by: Javier Segarra <jsegarra@verdnatura.es>
This commit is contained in:
Javier Segarra 2024-11-12 12:24:41 +00:00
commit 0fc1fc58e6
50 changed files with 2716 additions and 360 deletions

View File

@ -68,7 +68,8 @@ module.exports = {
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
// https://github.com/prettier/eslint-config-prettier#installation
// usage with Prettier, provided by 'eslint-config-prettier'.
'prettier'
'prettier',
'plugin:cypress/recommended'
],
rules: {
semi: 'off',

21
cypress.config.js Normal file
View File

@ -0,0 +1,21 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:8080/',
supportFile: 'src/test/cypress/support/index.js',
fixturesFolder: 'src/test/cypress/fixtures',
specPattern: 'src/test/cypress/integration/**/*.spec.js',
viewportHeight: 660,
viewportWidth: 1240,
experimentalMemoryManagement: true,
numTestsKeptInMemory: 0,
video: false,
screenshotOnRunFailure: false,
setupNodeEvents(on, config) {
on('after:spec', (spec, results) => {
console.log('Finished running', spec.relative);
});
}
}
});

1955
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
"babel-loader": "^9.1.0",
"bundle-loader": "^0.5.6",
"css-loader": "^5.2.7",
"cypress": "^13.15.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.0.0",
@ -53,6 +54,7 @@
"@quasar/extras": "^1.0.0",
"axios": "^0.21.1",
"core-js": "^3.6.5",
"eslint-plugin-cypress": "^2.13.3",
"js-yaml": "^3.12.1",
"mootools": "^1.5.2",
"pinia": "^2.0.11",
@ -67,6 +69,11 @@
"scripts": {
"front": "webpack serve --open",
"back": "cd ../vn-database && myvc start && cd ../salix && gulp backOnly",
"resetDatabase": "cd ../salix && gulp docker",
"cy:open": "npm run resetDatabase && cd ../hedera-web && cypress open",
"test:e2e": "npm run resetDatabase && cd ../hedera-web && cypress run",
"cy:open-mindshore": "npm run resetDatabase && cd ../hedera-web-mindshore && cypress open",
"test:e2e-mindshore": "npm run resetDatabase && cd ../hedera-web-mindshore && cypress run",
"build": "rm -rf build/ ; webpack",
"clean": "rm -rf build/",
"lint": "eslint --ext .js,.vue ./"

View File

@ -240,6 +240,7 @@ defineExpose({
flat
:disabled="!showBottomActions && !updatedColumns.length"
@click="submit()"
data-testid="formDefaultSaveButton"
>
<QTooltip>{{ t('save') }}</QTooltip>
</QBtn>

View File

@ -91,6 +91,7 @@ async function confirm() {
@click="confirm()"
unelevated
autofocus
data-testid="confirmDialogButton"
/>
</QCardActions>
</QCard>

View File

@ -71,6 +71,7 @@ onMounted(() => {
is-outlined
:clearable="false"
class="searchbar"
data-testid="searchBar"
>
<template #prepend>
<QIcon name="search" class="cursor-pointer" @click="search()" />

View File

@ -12,7 +12,10 @@ export default function useNotify() {
Notify.create({
message: i18n.global.t(message),
type,
icon: icon || defaultIcons[type]
icon: icon || defaultIcons[type],
attrs: {
'data-testid': `${type}Notify`
}
});
};

View File

@ -14,6 +14,7 @@ const appStore = useAppStore();
const refreshContentKey = ref(0);
const { mainUser, supplantedUser } = storeToRefs(userStore);
const {
customTitle,
menuTitle,
subtitle,
useRightDrawer,
@ -58,7 +59,7 @@ const logoutSupplantedUser = async () => {
@click="toggleLeftDrawer"
/>
<QToolbarTitle>
{{ menuTitle }}
{{ customTitle || menuTitle }}
<div v-if="subtitle" class="subtitle text-caption">
{{ subtitle }}
</div>
@ -76,16 +77,34 @@ const logoutSupplantedUser = async () => {
</QBtn>
</QToolbar>
</QHeader>
<QDrawer v-model="leftDrawerOpen" :width="250" show-if-above>
<QDrawer
v-model="leftDrawerOpen"
:width="250"
show-if-above
data-testid="layoutMenuDrawer"
>
<QToolbar class="logo">
<img src="statics/logo-dark.svg" />
</QToolbar>
<div class="user-info">
<div>
<span id="user-name">{{ mainUser?.nickname }}</span>
<QBtn flat icon="logout" alt="_Exit" @click="logout()" />
<span id="user-name" data-testid="layoutUserName">
{{ mainUser?.nickname }}
</span>
<QBtn
flat
icon="logout"
alt="_Exit"
@click="logout()"
data-testid="logoutButton"
/>
</div>
<div v-if="supplantedUser" id="supplant" class="supplant">
<div
v-if="supplantedUser"
id="supplant"
class="supplant"
data-testid="layoutSupplantedUserName"
>
<span id="supplanted">
{{ supplantedUser?.nickname }}
</span>

View File

@ -115,6 +115,7 @@ onMounted(() => fetchLanguagesSql());
:label="t('nickname')"
@keyup.enter="updateUserNickname(data.nickname)"
@blur="updateUserNickname(data.nickname)"
data-testid="configViewNickname"
/>
<VnSelect
v-model="data.lang"

View File

@ -90,24 +90,39 @@ onMounted(() => getCountries());
@on-data-saved="goBack()"
>
<template #form="{ data }">
<VnInput v-model="data.nickname" :label="t('name')" />
<VnInput v-model="data.street" :label="t('address')" />
<VnInput v-model="data.city" :label="t('city')" />
<VnInput
v-model="data.nickname"
:label="t('name')"
data-testid="addressFormNickname"
/>
<VnInput
v-model="data.street"
:label="t('address')"
data-testid="addressFormStreet"
/>
<VnInput
v-model="data.city"
:label="t('city')"
data-testid="addressFormCity"
/>
<VnInput
v-model="data.postalCode"
type="number"
:label="t('postalCode')"
data-testid="addressFormPostcode"
/>
<VnSelect
v-model="data.countryFk"
:label="t('country')"
:options="countriesOptions"
@update:model-value="data.provinceFk = null"
data-testid="addressFormCountry"
/>
<VnSelect
v-model="data.provinceFk"
:label="t('province')"
:options="provincesOptions"
data-testid="addressFormProvince"
/>
</template>
</VnForm>

View File

@ -98,6 +98,7 @@ onMounted(async () => {
@click="goToAddressDetails()"
rounded
no-caps
data-testid="newAddressBtn"
>
<QTooltip>
{{ t('addAddress') }}
@ -109,6 +110,7 @@ onMounted(async () => {
class="rounded-borders shadow-1 shadow-transition"
separator
:rows="addresses"
data-testid="addressCardList"
>
<CardList
v-for="(address, index) in addresses"
@ -156,6 +158,7 @@ onMounted(async () => {
flat
rounded
@click.stop="goToAddressDetails(address.id)"
data-testid="editAddressBtn"
>
<QTooltip>
{{ t('editAddress') }}

View File

@ -44,11 +44,13 @@ const onSearch = data => (items.value = data || []);
empty-icon="refresh"
:loading="loading"
:rows="items"
data-testid="itemsViewList"
>
<CardList
v-for="(item, index) in items"
:key="index"
:clickable="false"
data-testid="itemsViewCard"
>
<template #prepend>
<VnImg

View File

@ -106,6 +106,7 @@ onMounted(async () => {
v-model="data.title"
:label="t('title')"
:clearable="false"
data-testid="newsTitleInput"
/>
<div class="row justify-between q-gutter-x-md">
<VnSelect
@ -115,12 +116,14 @@ onMounted(async () => {
option-value="name"
:options="newsTags"
class="col"
data-testid="newsTagSelect"
/>
<VnInput
v-model="data.priority"
:label="t('priority')"
:clearable="false"
class="col"
data-testid="newsPriorityInput"
/>
</div>
<QEditor

View File

@ -64,6 +64,7 @@ onMounted(async () => getNews());
:to="{ name: 'adminNewsDetails' }"
rounded
no-caps
data-testid="addNewBtn"
>
<QTooltip>{{ t('addNew') }}</QTooltip>
</QBtn>
@ -74,6 +75,7 @@ onMounted(async () => getNews());
v-for="(newsItem, index) in news"
:key="index"
:to="{ name: 'adminNewsDetails', params: { id: newsItem.id } }"
data-testid="newsCard"
>
<template #prepend>
<VnImg
@ -106,6 +108,7 @@ onMounted(async () => getNews());
() => deleteNew(newsItem.id, index)
)
"
data-testid="deleteNewBtn"
>
<QTooltip>{{ t('remove') }}</QTooltip>
</QBtn>

View File

@ -153,6 +153,7 @@ onMounted(async () => getImageCollections());
option-label="desc"
option-value="name"
:options="imageCollections"
data-testid="photoCollectionSelect"
/>
<QUploader
ref="fileUploaderRef"
@ -164,13 +165,19 @@ onMounted(async () => getImageCollections());
bordered
hide-upload-btn
@added="onFilesAdded"
data-testid="photoUploader"
>
<template #list="scope">
<QList v-if="addedFiles.length" separator>
<QList
v-if="addedFiles.length"
separator
data-testid="photoUploaderList"
>
<QItem
v-for="(file, index) in scope.files"
:key="file.__key"
class="flex full-width row items-center justify-center"
data-testid="photoUploaderItem"
>
<img
:src="file.__img.src"
@ -204,6 +211,7 @@ onMounted(async () => getImageCollections());
].icon
"
size="sm"
data-testid="photoUploaderItemsStatusIcon"
>
<QTooltip>
{{
@ -229,6 +237,7 @@ onMounted(async () => getImageCollections());
round
icon="delete"
@click="removeFile(file, index)"
data-testid="photoUploaderItemsDeleteBtn"
>
<QTooltip>{{ t('remove') }}</QTooltip>
</QBtn>
@ -248,6 +257,7 @@ onMounted(async () => getImageCollections());
no-caps
flat
@click="clearFiles()"
data-testid="photoUploaderClearBtn"
/>
<QBtn
:label="t('uploadFiles')"
@ -256,6 +266,7 @@ onMounted(async () => getImageCollections());
flat
:disable="!isSubmitable"
@click="onSubmit(data)"
data-testid="photoUploadSubmitBtn"
/>
</template>
</VnForm>

View File

@ -47,6 +47,7 @@ const supplantUser = async user => {
search-field="user"
@on-search="onSearch"
@on-search-error="users = []"
data-testid="usersViewSearchBar"
/>
</Teleport>
<QPage class="vn-w-xs">
@ -56,11 +57,13 @@ const supplantUser = async user => {
empty-icon="refresh"
:loading="loading"
:rows="users"
data-testid="usersViewList"
>
<CardList
v-for="(user, index) in users"
:key="index"
:to="{ name: 'accessLog', params: { id: user.id } }"
data-testid="userViewCard"
>
<template #content>
<span class="text-bold q-mb-sm">
@ -75,6 +78,7 @@ const supplantUser = async user => {
flat
rounded
@click.stop.prevent="supplantUser(user.name)"
data-testid="usersViewSupplantUserBtn"
>
<QTooltip>
{{ t('Impersonate user') }}

View File

@ -122,6 +122,7 @@ const fetchData = async () => {
:to="{ name: 'confirm', params: { id: orderId } }"
rounded
no-caps
data-testid="basketToConfirmBtn"
>
<QTooltip>{{ t('checkout') }}</QTooltip>
</QBtn>

View File

@ -15,7 +15,12 @@ const { t } = useI18n();
</script>
<template>
<QCard v-if="viewMode === 'grid'" v-ripple class="catalog-card">
<QCard
v-if="viewMode === 'grid'"
v-ripple
class="catalog-card"
data-testid="catalogCardGrid"
>
<VnImg
storage="catalog"
size="200x200"
@ -23,7 +28,11 @@ const { t } = useI18n();
height="210px"
rounded="bottom"
/>
<div class="column" style="height: 205px; padding: 10px">
<div
class="column"
style="height: 205px; padding: 10px"
data-testid="catalogCardGridBody"
>
<div class="column" style="margin-bottom: auto">
<div class="text-subtitle2 ellipsis-2-lines">
{{ item.item }}
@ -98,7 +107,7 @@ const { t } = useI18n();
</div>
</div>
</QCard>
<CardList v-else class="vn-w-sm">
<CardList v-else class="vn-w-sm" data-testid="catalogCardList">
<template #prepend>
<VnImg
storage="catalog"

View File

@ -19,6 +19,7 @@
@click="redirectToBasket()"
rounded
no-caps
data-testid="catalogGoToBasketButton"
>
<QTooltip>
{{ t('shoppingCart') }}
@ -36,7 +37,7 @@
</div>
</Teleport>
<div style="padding-bottom: 5em">
<QDrawer v-model="rightDrawerOpen" side="right" :width="250">
<QDrawer v-model="rightDrawerOpen" side="right" :width="250" persistent>
<div class="q-pa-md">
<div class="basket-info q-gutter-y-sm">
<span v-if="order?.nickname">{{ order.nickname }}</span>
@ -49,7 +50,12 @@
})
}}
</span>
<QBtn rounded no-caps @click="redirectToCheckout()">
<QBtn
rounded
no-caps
@click="redirectToCheckout()"
data-testid="orderModifyButton"
>
{{ t('modify') }}
</QBtn>
</div>
@ -74,6 +80,7 @@
:class="{ active: category == cat.id }"
:key="cat.id"
@click="selectedCategory = cat.id"
data-testid="catalogCategoryButton"
>
<img :src="`statics/category/${cat.code}.svg`" />
<QTooltip>{{ cat.name }}</QTooltip>
@ -91,6 +98,7 @@
:options="itemFamilies"
:disable="!category"
:label="t('family')"
data-testid="catalogFamilySelect"
/>
<VnSelect
v-model="selectedColor"
@ -243,6 +251,7 @@
flat
dense
@click="onAddLotClick(lot)"
data-testid="addItemQuantityButton"
>
<QTooltip>{{ t('add') }}</QTooltip>
</QBtn>
@ -267,6 +276,7 @@
flat
color="white"
@click="onConfirmClick()"
data-testid="catalogAddToBasketButton"
>
<QTooltip>{{ t('confirm') }}</QTooltip>
</QBtn>
@ -278,7 +288,14 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { inject, onBeforeMount, ref, computed, watch } from 'vue';
import {
inject,
onBeforeMount,
ref,
computed,
watch,
onBeforeUnmount
} from 'vue';
import { useRoute, useRouter } from 'vue-router';
import VnImg from 'src/components/ui/VnImg.vue';
@ -880,29 +897,31 @@ const onConfirmClick = async params => {
const refreshTitle = () => {
const { meta } = route;
let title = t(meta.title);
const title = t(meta.title);
let subtitle;
let customTitle;
if (selectedCategory.value) {
const _category = categories.value.find(
i => i.id === selectedCategory.value
);
if (_category) {
title = _category.name;
const categoryName = _category.name;
customTitle = categoryName;
if (selectedType.value) {
const _type = itemFamilies.value.find(
i => i.id === selectedType.value
);
if (_type) {
subtitle = title;
title = _type.name;
subtitle = categoryName;
customTitle = _type.name;
}
}
}
}
appStore.$patch({ title, subtitle });
appStore.$patch({ title, subtitle, customTitle });
};
const onViewModeClick = () => {
@ -955,6 +974,8 @@ onBeforeMount(async () => {
selectedCategory.value = Number(route.params.category);
if (route.params.type) selectedType.value = Number(route.params.type);
});
onBeforeUnmount(() => appStore.resetCustomTitle());
</script>
<style lang="scss" scoped>

View File

@ -30,7 +30,7 @@ const currentStep = ref('method');
const id = route.params.id;
const orderForm = ref({
method: 'AGENCY',
date: '',
date: formatDate(Date.vnNew(), 'YYYY/MM/DD'),
address: ''
});
@ -322,6 +322,7 @@ onMounted(async () => {
:flat="isMobile"
contracted
class="default-radius stepper-container"
data-testid="checkoutStepper"
>
<QStep
v-for="(step, stepIndex) in steps[orderForm.method]"
@ -374,6 +375,7 @@ onMounted(async () => {
<QList
v-if="step.name === 'address'"
class="vn-w-xs q-gutter-y-sm column"
data-testid="checkoutAddressStep"
>
<span class="text-h6 step-title">
{{
@ -417,6 +419,7 @@ onMounted(async () => {
option-label="description"
option-value="id"
:options="agencies"
data-testid="agencyStepSelect"
/>
</div>
<div
@ -431,6 +434,7 @@ onMounted(async () => {
option-label="description"
option-value="id"
:options="warehouses"
data-testid="pickupStepSelect"
/>
</div>
<!-- Confirm step -->
@ -457,6 +461,7 @@ onMounted(async () => {
icon="arrow_back"
dense
class="left-navigation-button"
data-testid="checkoutStepperLeftButton"
>
<QTooltip>
{{ t(`${step.backButtonLabel || 'back'}`) }}
@ -471,6 +476,7 @@ onMounted(async () => {
"
dense
class="right-navigation-button"
data-testid="checkoutStepperRightButton"
>
<QTooltip>
{{ t(`${step.nextButtonLabel || 'next'}`) }}

View File

@ -73,6 +73,7 @@ const onConfirmPay = async () => {
@click="onPayClick()"
rounded
no-caps
data-testid="makePaymentButton"
>
<QTooltip>
{{ t('makePayment') }}
@ -124,6 +125,7 @@ const onConfirmPay = async () => {
v-model="showAmountToPayDialog"
message=" "
:promise="onConfirmPay"
data-testid="payAmountDialog"
>
<template #customHTML>
<VnInput
@ -133,6 +135,7 @@ const onConfirmPay = async () => {
type="number"
min="0"
:max="debt * -1"
data-testid="payAmountInput"
>
<template #append></template>
</VnInput>

View File

@ -71,6 +71,7 @@ onMounted(async () => {
<template>
<Teleport v-if="isHeaderMounted" to="#actions">
<QBtn
data-testid="pendingOrdersNewOrder"
:to="{ name: 'checkout' }"
icon="add_shopping_cart"
:label="t('newOrder')"
@ -83,11 +84,16 @@ onMounted(async () => {
</QBtn>
</Teleport>
<QPage class="vn-w-sm">
<VnList :rows="orders" :loading="loading">
<VnList
:rows="orders"
:loading="loading"
data-testid="pendingOrdersList"
>
<CardList
v-for="(order, index) in orders"
:key="index"
:to="{ name: 'basket', params: { id: order.id } }"
data-testid="pendingOrderCard"
>
<template #content>
<QItemLabel class="text-bold q-mb-sm">
@ -110,6 +116,7 @@ onMounted(async () => {
() => removeOrder(order.id, index)
)
"
data-testid="pendingOrderCardDelete"
>
<QTooltip>{{ t('deleteOrder') }}</QTooltip>
</QBtn>
@ -118,6 +125,7 @@ onMounted(async () => {
flat
rounded
@click.stop.prevent="loadOrder(order.id)"
data-testid="addOrderToBasket"
>
<QTooltip>{{ t('loadOrderIntoCart') }}</QTooltip>
</QBtn>

View File

@ -111,7 +111,7 @@ const deleteRow = id => {
</QCardSection>
<QSeparator v-if="showItems" inset />
<QList v-for="(row, index) in rows" :key="index">
<QItem v-if="row">
<QItem v-if="row" data-testid="basketItemRow">
<QItemSection v-if="canDeleteItems" avatar>
<QBtn
icon="delete"

View File

@ -47,7 +47,7 @@ onMounted(() => {
const onLogin = async () => {
await userStore.fetchUser();
await router.push('/');
await router.push({ name: 'home' });
};
const login = async () => {
@ -69,14 +69,21 @@ const loginAsGuest = async () => {
</div>
<QForm @submit="login()" class="q-gutter-y-md">
<div class="q-gutter-y-sm">
<QInput v-model="email" :label="$t('user')" autofocus />
<QInput
v-model="email"
:label="$t('user')"
autofocus
data-testid="loginUserInput"
/>
<QInput
v-model="password"
:label="$t('password')"
:type="!showPwd ? 'password' : 'text'"
data-testid="loginPasswordInput"
>
<template #append>
<QIcon
data-testid="showPasswordIcon"
:name="showPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="showPwd = !showPwd"
@ -85,12 +92,14 @@ const loginAsGuest = async () => {
</QInput>
<div class="row justify-between text-center">
<QCheckbox
data-testid="rememberCheckbox"
v-model="remember"
:label="$t('remindMe')"
dense
class="col"
/>
<QSelect
data-testid="switchLanguage"
v-model="selectedLocaleValue"
:options="localeOptions"
:label="t('language')"
@ -117,6 +126,7 @@ const loginAsGuest = async () => {
</div>
<div class="justify-center">
<QBtn
data-testid="loginAsGuestButton"
@click="loginAsGuest()"
:label="$t('logInAsGuest')"
class="full-width"

View File

@ -1,5 +1,6 @@
import { route } from 'quasar/wrappers';
import { useAppStore } from 'stores/app';
import { useUserStore } from 'stores/user';
import {
createRouter,
createMemoryHistory,
@ -36,6 +37,20 @@ export default route(function (/* { store, ssrContext } */) {
)
});
Router.beforeEach((to, from, next) => {
const userStore = useUserStore();
if (
!userStore.storage.getItem('token') &&
to.name !== 'login' &&
!userStore.isGuest
) {
return next({ name: 'login' });
}
next();
});
Router.afterEach((to, from) => {
if (from.name === to.name) return;
const app = useAppStore();

View File

@ -10,6 +10,7 @@ const { t } = i18n.global;
export const useAppStore = defineStore('hedera', {
state: () => ({
customTitle: null,
title: null,
subtitle: null,
imageUrl: '',
@ -160,6 +161,10 @@ export const useAppStore = defineStore('hedera', {
i18n.global.locale.value = _locale;
this.siteLang = _locale;
localStorage.setItem('siteLang', _locale);
},
resetCustomTitle() {
this.customTitle = null;
}
},
getters: {

View File

@ -22,16 +22,16 @@ export const useUserStore = defineStore('user', () => {
const tokenConfig = ref(null);
let router;
const loggedIn = computed(() => !!token.value);
const storage = computed(() =>
keepLogin.value ? localStorage : sessionStorage
);
const isLoggedIn = computed(() => !!storage.value.getItem(TOKEN));
const init = async _router => {
router = _router;
isGuest.value = localStorage.getItem('hederaGuest') || false;
await getToken();
if (!loggedIn.value) {
if (!isLoggedIn.value) {
const autoLoginStatus = await tryAutoLogin();
if (!autoLoginStatus) {
router.push({ name: 'login' });
@ -285,7 +285,6 @@ export const useUserStore = defineStore('user', () => {
watch(
[mainUser, supplantedUser],
() => (user.value = supplantedUser.value || mainUser.value),
{ immediate: true }
);
@ -300,7 +299,7 @@ export const useUserStore = defineStore('user', () => {
intervalId,
isCheckingToken,
tokenConfig,
loggedIn,
isLoggedIn,
storage,
getToken,
getTokenMultimedia,

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,25 @@
describe('User flow: Login, create a new order, add item to basket and go to confirm view', () => {
before(() => {
cy.resetDB();
});
it('success', () => {
// 1- Loguear como empleado
cy.login('employee');
// 2- Crear una orden
cy.createOrderReceiveFlow();
// 3- Filtrar items y agregar item al carrito
cy.addItemToBasketFlow();
// 4- Ir al carrito
cy.dataCy('catalogGoToBasketButton').should('exist');
cy.dataCy('catalogGoToBasketButton').click();
cy.url().should('contain', '/#/ecomerce/basket');
// 5- Verificar que el item se agregó al carrito
cy.dataCy('basketItemRow').should('exist');
cy.dataCy('basketItemRow').should('have.length', 1);
// 6- Ir a la vista de confirmación
cy.dataCy('basketToConfirmBtn').should('exist');
cy.dataCy('basketToConfirmBtn').click();
cy.url().should('contain', '/#/ecomerce/confirm');
});
});

View File

@ -0,0 +1,26 @@
describe('NewsView', () => {
beforeEach(() => {
cy.login('developer');
cy.visit('/#/admin/items');
});
it('shows empty state', () => {
cy.dataCy('itemsViewList').should('exist');
cy.dataCy('itemsViewList').should(
'contain',
'Introduce un término de búsqueda'
);
cy.dataCy('itemsViewCard').should('not.exist');
});
it('search for an item', () => {
cy.dataCy('searchBar').should('exist');
cy.dataCy('searchBar').find('input').type('Bolas de madera{enter}');
cy.dataCy('itemsViewList').should(
'not.contain',
'Introduce un término de búsqueda'
);
cy.dataCy('itemsViewCard').should('exist');
cy.dataCy('itemsViewCard').should('contain', 'Bolas de madera');
});
});

View File

@ -0,0 +1,42 @@
describe('NewsView', () => {
before(() => {
cy.resetDB();
});
beforeEach(() => {
cy.login('developer');
cy.visit('/#/news/news');
});
it('should delete a new', () => {
cy.dataCy('newsCard').should('exist');
cy.dataCy('newsCard').should('have.length', 3);
cy.dataCy('newsCard')
.first()
.within(() => {
cy.dataCy('deleteNewBtn').click();
});
cy.setConfirmDialog();
cy.checkNotify('positive', 'Datos guardados');
cy.dataCy('newsCard').should('have.length', 2);
});
it('should create a new', () => {
cy.dataCy('addNewBtn').should('exist');
cy.dataCy('addNewBtn').click();
cy.dataCy('formDefaultSaveButton').should('exist');
cy.dataCy('formDefaultSaveButton').should('be.disabled');
cy.dataCy('newsTitleInput').should('exist');
cy.dataCy('newsTitleInput').find('input').type('Test new');
cy.dataCy('newsTagSelect').should('exist');
cy.selectOption('[data-testid="newsTagSelect"]', 'Curso');
cy.dataCy('newsPriorityInput').should('exist');
cy.dataCy('newsPriorityInput').find('input').type('2');
cy.dataCy('formDefaultSaveButton').should('not.be.disabled');
cy.dataCy('formDefaultSaveButton').click();
cy.checkNotify('positive', 'Datos guardados');
cy.dataCy('newsCard').should('exist');
cy.dataCy('newsCard').should('contain', 'Test new');
cy.dataCy('newsCard').should('contain', '2');
});
});

View File

@ -0,0 +1,80 @@
describe('Photo Uploader Component', () => {
beforeEach(() => {
cy.login('developer');
cy.visit('/#/admin/photos');
});
const uploadFile = fileName => {
cy.get('.q-uploader__input').selectFile(fileName);
};
it('should display the photo collection select', () => {
// Verificar que el select de colección esté presente
cy.dataCy('photoCollectionSelect').should('exist');
});
it('should allow selecting a photo collection', () => {
// Simular la selección de una colección de fotos
cy.selectOption('[data-testid="photoCollectionSelect"]', 'Enlace');
cy.getValue('[data-testid="photoCollectionSelect"]').should(
'equal',
'Enlace'
);
});
it('submit button should be disabled if no files are selected', () => {
cy.dataCy('photoUploadSubmitBtn').should('be.disabled');
});
it('should allow adding files', () => {
cy.dataCy('photoUploaderItem').should('not.exist');
uploadFile('src/test/cypress/fixtures/lowres.jpg');
cy.dataCy('photoUploaderItem').should('exist');
cy.dataCy('photoUploaderItem').should('have.length', 1);
});
it('should remove a file when delete button is clicked', () => {
uploadFile('src/test/cypress/fixtures/lowres.jpg');
cy.dataCy('photoUploaderItem').should('exist');
cy.dataCy('photoUploaderItemsDeleteBtn').click();
cy.dataCy('photoUploaderItem').should('not.exist');
});
it('should enable the upload button when files are pending', () => {
uploadFile('src/test/cypress/fixtures/lowres.jpg');
cy.dataCy('photoUploadSubmitBtn').should('not.be.disabled');
});
it('should upload files when submit button is clicked', () => {
uploadFile('src/test/cypress/fixtures/lowres.jpg');
cy.dataCy('photoUploadSubmitBtn').click();
cy.checkNotify('positive', 'Imágenes subidas correctamente');
});
it('should show the correct status icon', () => {
uploadFile('src/test/cypress/fixtures/lowres.jpg');
cy.dataCy('photoUploaderItemsStatusIcon').should('have.text', 'add');
cy.dataCy('photoUploadSubmitBtn').click();
cy.dataCy('photoUploaderItemsStatusIcon').should(
'have.text',
'cloud_done'
);
});
it('should clear all files when clear button is clicked', () => {
uploadFile('src/test/cypress/fixtures/lowres.jpg');
cy.dataCy('photoUploaderItem').should('exist');
cy.dataCy('photoUploaderClearBtn').click();
cy.dataCy('photoUploaderItem').should('not.exist');
});
it('should display error notification if an upload fails', () => {
uploadFile('src/test/cypress/fixtures/low-res.jpg');
cy.dataCy('photoUploadSubmitBtn').click();
cy.checkNotify(
'negative',
'Ocurrieron errores al subir alguna de las imágenes'
);
});
});

View File

@ -0,0 +1,10 @@
Cypress.Commands.add('userViewSupplant', user => {
cy.dataCy('usersViewSearchBar').should('exist');
cy.dataCy('usersViewSearchBar').find('input').type(`${user}{enter}`);
cy.dataCy('layoutSupplantedUserName').should('not.exist');
cy.dataCy('usersViewList').should('not.contain', 'Sin datos');
cy.dataCy('userViewCard').should('exist');
cy.dataCy('usersViewSupplantUserBtn').click();
cy.dataCy('layoutSupplantedUserName').should('exist');
cy.dataCy('layoutSupplantedUserName').should('contain', user);
});

View File

@ -0,0 +1,31 @@
describe('UsersView', () => {
before(() => {
cy.resetDB();
});
beforeEach(() => {
cy.login('adminboss');
cy.visit('/#/admin/users');
});
it('should show empty state when entering the view', () => {
cy.dataCy('usersViewList').should('contain', 'Sin datos');
});
it('supplants user', () => {
cy.userViewSupplant('Bruce Wayne');
cy.getSessionStorage('supplantUser').should('equal', 'brucewayne');
});
it('makes actions for the supplanted user', () => {
cy.userViewSupplant('Bruce Wayne');
cy.wait(100);
cy.visit('/#/account/conf');
cy.url().should('contain', '/#/account/conf');
cy.changeUserNickname('Bruce Wayne', 'New test nickname');
cy.dataCy('layoutSupplantedUserName').should(
'contain',
'New test nickname'
);
});
});

View File

@ -0,0 +1,22 @@
Cypress.Commands.add('addItemToBasketFlow', () => {
// 1- Seleccionar categoría
cy.dataCy('catalogCategoryButton').should('exist');
cy.get('[data-testid="catalogCategoryButton"]:first').click();
// 2- Seleccionar familia
cy.dataCy('catalogFamilySelect').should('exist');
cy.selectOption('[data-testid="catalogFamilySelect"]', 'Anthurium');
cy.getValue('[data-testid="catalogFamilySelect"]').should(
'equal',
'Anthurium'
);
cy.dataCy('catalogFamilySelect').should('exist');
// 3- Seleccionar item
cy.dataCy('catalogCardGridBody').should('exist');
cy.get('[data-testid="catalogCardGridBody"]:first').click();
// 4- Añadir item al carrito
cy.dataCy('addItemQuantityButton').should('exist');
cy.get('[data-testid="addItemQuantityButton"]:first').click();
cy.dataCy('catalogAddToBasketButton').should('exist');
cy.dataCy('catalogAddToBasketButton').click();
cy.checkNotify('positive', 'Añadido');
});

View File

@ -0,0 +1,45 @@
describe('CatalogView', () => {
before(() => {
cy.resetDB();
});
beforeEach(() => {
cy.login('developer');
});
it('if there is no pending order created and we go to catalog, it should return checkout view', () => {
cy.visit('/#/ecomerce/pending');
// Comprobamos que no existe ninguna orden pendiente
cy.dataCy('pendingOrdersList').should('contain', 'Lista vacía');
// Visitamos el catalogo
cy.visit('/#/ecomerce/catalog');
// Debería redirigirnos al checkout
cy.url().should('contain', '/#/ecomerce/checkout');
});
it('if there is a pending order created and we somehow remove order from basket it should return pending orders view', () => {
cy.createOrderReceiveFlow();
// Una manera de perder la información de la orden es deslogueando
cy.logoutFlow();
// Volvemos a loguear con el mismo usuario
cy.loginFlow('developer');
// Visitamos catalog
cy.visit('/#/ecomerce/catalog');
// Debería redirigirnos a pending orders con un mensaje de advertencia
cy.url().should('contain', '/#/ecomerce/pending');
cy.checkNotify(
'warning',
'Por favor carga un pedido pendiente en la cesta o empieza uno nuevo'
);
});
it('Adds item to basket and goes to basket', () => {
cy.resetDB();
cy.login('developer');
cy.createOrderReceiveFlow();
cy.addItemToBasketFlow();
cy.dataCy('catalogGoToBasketButton').should('exist');
cy.dataCy('catalogGoToBasketButton').click();
cy.url().should('contain', '/#/ecomerce/basket');
});
});

View File

@ -0,0 +1,85 @@
const checkoutNextStep = () => {
cy.dataCy('checkoutStepperRightButton').should('be.visible').click();
};
Cypress.Commands.add('createOrderReceive', () => {
cy.dataCy('checkoutStepper').should('exist');
cy.dataCy('checkoutStepper').should(
'contain',
'¿Quieres recibir o recoger el pedido?'
);
cy.dataCy('Recibir en mi tienda', 'aria-label').click();
checkoutNextStep();
cy.dataCy('checkoutStepper').should(
'contain',
'¿Qué día quieres recibir el pedido?'
);
checkoutNextStep();
cy.dataCy('checkoutStepper').should(
'contain',
'¿Dónde quieres recibir el pedido?'
);
cy.dataCy('checkoutAddressStep').within(() => {
cy.get('label:first').click();
});
checkoutNextStep();
cy.dataCy('checkoutStepper').should(
'contain',
'¿Cómo quieres recibir el pedido?'
);
cy.dataCy('agencyStepSelect').should('exist');
cy.selectOption('[data-testid="agencyStepSelect"]', 'Other agency');
checkoutNextStep();
checkoutNextStep();
cy.url().should('contain', '/#/ecomerce/catalog');
});
Cypress.Commands.add('createOrderReceiveFlow', () => {
cy.visit('/#/ecomerce/pending');
cy.dataCy('pendingOrdersNewOrder').should('exist');
cy.dataCy('pendingOrdersNewOrder').click();
cy.createOrderReceive();
cy.checkNotify('positive', '¡Pedido cargado en la cesta!');
});
Cypress.Commands.add('createOrderPickup', () => {
cy.dataCy('checkoutStepper').should('exist');
cy.dataCy('checkoutStepper').should(
'contain',
'¿Quieres recibir o recoger el pedido?'
);
cy.dataCy('Recoger en almacén', 'aria-label').click();
checkoutNextStep();
cy.dataCy('checkoutStepper').should(
'contain',
'¿Qué día quieres recibir el pedido?'
);
checkoutNextStep();
cy.dataCy('checkoutStepper').should(
'contain',
'¿A qué dirección quieres asociar el pedido? (Opcional)'
);
cy.dataCy('checkoutAddressStep').within(() => {
cy.get('label:first').click();
});
checkoutNextStep();
cy.dataCy('checkoutStepper').should(
'contain',
'¿En qué almacén quieres recoger tu pedido?'
);
cy.dataCy('pickupStepSelect').should('exist');
cy.selectOption('[data-testid="pickupStepSelect"]', 'Teleportation device');
checkoutNextStep();
checkoutNextStep();
cy.url().should('contain', '/#/ecomerce/catalog');
});
Cypress.Commands.add('createOrderPickupFlow', (changeLanguage = false) => {
if (changeLanguage) cy.changeLanguageFlow('es');
cy.visit('/#/ecomerce/pending');
cy.dataCy('pendingOrdersNewOrder').should('exist');
cy.dataCy('pendingOrdersNewOrder').click();
cy.createOrderPickup();
cy.checkNotify('positive', '¡Pedido cargado en la cesta!');
});

View File

@ -0,0 +1,24 @@
describe('CheckoutStepper', () => {
before(() => {
cy.resetDB();
});
beforeEach(() => {
cy.login('employee');
});
it('Creates new order to receive', () => cy.createOrderReceiveFlow());
it('Creates new pickup order', () => cy.createOrderPickupFlow());
it('Modifies an order', () => {
cy.createOrderReceiveFlow();
cy.dataCy('orderModifyButton').click();
cy.checkNotify(
'warning',
'Recuerda que si vuelves a configurar el pedido los precios o cantidades de tus artículos podrían cambiar'
);
cy.createOrderPickup(false);
cy.checkNotify('positive', 'Pedido actualizado');
});
});

View File

@ -0,0 +1,12 @@
Cypress.Commands.add('changeUserNickname', (oldNickname, newNickname) => {
cy.dataCy('configViewNickname').find('input').should('exist');
cy.getValue('input[data-testid="configViewNickname"]').should(
'equal',
oldNickname
);
cy.dataCy('configViewNickname').find('input').clear();
cy.dataCy('configViewNickname').find('input').type(newNickname);
cy.dataCy('configViewNickname').find('input').blur();
cy.checkNotify('positive', 'Datos guardados');
});

View File

@ -0,0 +1,11 @@
describe('Changes user nickname', () => {
beforeEach(() => {
cy.login('brucewayne');
cy.visit('/#/account/conf');
});
it('success', () => {
cy.changeUserNickname('Bruce Wayne', 'New test nickname');
cy.resetDB();
});
});

View File

@ -0,0 +1,80 @@
describe('PendingOrders', () => {
beforeEach(() => {
cy.login('developer');
cy.visit('/#/account/address-list');
});
const getRandomAddressFormData = () => {
const randomString = () => Math.random().toString(36).substring(2, 15);
return {
nickname: `Nickname-${randomString()}`,
street: `Street-${randomString()}`,
city: `City-${randomString()}`,
postcode: Math.floor(Math.random() * 90000) + 10000
};
};
const fillFormWithData = data => {
cy.dataCy('addressFormNickname').find('input').click();
cy.dataCy('addressFormNickname').find('input').type(data.nickname);
cy.dataCy('addressFormStreet').find('input').click();
cy.dataCy('addressFormStreet').find('input').type(data.street);
cy.dataCy('addressFormCity').find('input').click();
cy.dataCy('addressFormCity').find('input').type(data.city);
cy.dataCy('addressFormPostcode').find('input').click();
cy.dataCy('addressFormPostcode').find('input').type(data.postcode);
cy.selectOption('[data-testid="addressFormCountry"]', 'España');
cy.selectOption('[data-testid="addressFormProvince"]', 'Province one');
};
const verifyAddressCardData = data => {
cy.dataCy('addressCardList')
.children()
.last()
.should('contain', data.nickname);
cy.dataCy('addressCardList')
.children()
.last()
.should('contain', data.street);
cy.dataCy('addressCardList')
.children()
.last()
.should('contain', data.city);
cy.dataCy('addressCardList')
.children()
.last()
.should('contain', data.postcode);
};
it('should create a new address', () => {
cy.dataCy('newAddressBtn').should('exist');
cy.dataCy('newAddressBtn').click();
cy.dataCy('formDefaultSaveButton').should('exist');
cy.dataCy('formDefaultSaveButton').should('be.disabled');
const addressFormData = getRandomAddressFormData();
fillFormWithData(addressFormData);
cy.dataCy('formDefaultSaveButton').should('not.be.disabled');
cy.dataCy('formDefaultSaveButton').click();
cy.checkNotify('positive', 'Datos guardados');
verifyAddressCardData(addressFormData);
});
it('should edit an existent address', () => {
cy.dataCy('addressCardList')
.children()
.last()
.find('[data-testid="editAddressBtn"]')
.click();
// Clear form data
cy.get('form input').each(input => {
cy.wrap(input).clear(); // Limpia el valor de cada campo de entrada
});
// Fill form with new data
const addressFormData = getRandomAddressFormData();
fillFormWithData(addressFormData);
cy.dataCy('formDefaultSaveButton').should('not.be.disabled');
cy.dataCy('formDefaultSaveButton').click();
cy.checkNotify('positive', 'Datos guardados');
verifyAddressCardData(addressFormData);
});
});

View File

@ -0,0 +1,70 @@
// Login view commands
Cypress.Commands.add('login', user => {
cy.request({
method: 'POST',
url: '/api/Accounts/login',
body: {
user,
password: 'nightmare'
}
}).then(response => {
window.sessionStorage.setItem('token', response.body.token);
cy.request({
method: 'GET',
url: '/api/VnUsers/ShareToken',
headers: {
Authorization: window.sessionStorage.getItem('token')
}
}).then(({ body }) => {
window.sessionStorage.setItem(
'tokenMultimedia',
body.multimediaToken.id
);
});
});
});
Cypress.Commands.add('logout', user => {
cy.request({
method: 'POST',
url: '/api/Accounts/logout',
headers: {
Authorization: window.localStorage.getItem('token')
}
}).then(response => {
localStorage.clear();
sessionStorage.clear();
});
});
Cypress.Commands.add('loginFlow', (user, visitLogin = true) => {
if (visitLogin) cy.visit('/#/login');
cy.dataCy('loginUserInput').type(user);
cy.getValue('[data-testid="loginUserInput"]').should('equal', user);
cy.dataCy('loginPasswordInput').type('nightmare');
cy.getValue('[data-testid="loginPasswordInput"]').should(
'equal',
'nightmare'
);
cy.get('button[type="submit"]').click();
cy.url().should('contain', '/#/cms/home');
});
Cypress.Commands.add('changeLanguage', language => {
const languagesOrder = ['en', 'es', 'ca', 'fr', 'pt'];
const index = languagesOrder.indexOf(language);
cy.waitForElement('[data-testid="switchLanguage"]');
cy.dataCy('switchLanguage').click();
cy.get('.q-menu .q-item').eq(index).click(); // Selecciona y hace clic en el tercer elemento "index" de la lista
});
Cypress.Commands.add('changeLanguageFlow', language => {
cy.visit('/#/login');
cy.changeLanguage(language);
});
Cypress.Commands.add('logoutFlow', language => {
cy.dataCy('logoutButton').should('exist');
cy.dataCy('logoutButton').click();
});

View File

@ -0,0 +1,100 @@
describe('Login Tests', () => {
const rememberCheckbox = '[data-testid="rememberCheckbox"]';
beforeEach(() => {
cy.visit('/#/login');
});
it('should toggle password visibility', () => {
cy.dataCy('showPasswordIcon').should('exist');
cy.dataCy('showPasswordIcon').click();
cy.dataCy('showPasswordIcon').should('have.text', 'visibility_off');
cy.dataCy('showPasswordIcon').click();
cy.dataCy('showPasswordIcon').should('have.text', 'visibility');
});
it('should select a language from the dropdown', () => {
cy.dataCy('switchLanguage').should('exist');
cy.changeLanguage('en');
cy.get('button[type="submit"]').should('contain', 'Log in');
cy.changeLanguage('es');
cy.get('button[type="submit"]').should('contain', 'Iniciar sesión');
});
it('should fail to log in using wrong user', () => {
cy.dataCy('loginUserInput').type('incorrectUser');
cy.dataCy('loginPasswordInput').type('nightmare');
cy.get('button[type="submit"]').click();
cy.checkNotify('negative', 'login failed');
});
it('should fail to log in using wrong password', () => {
cy.dataCy('loginUserInput').type('incorrectUser');
cy.dataCy('loginPasswordInput').type('nightmare');
cy.get('button[type="submit"]').click();
cy.checkNotify('negative', 'login failed');
});
it('should log in', () => {
cy.loginFlow('employee');
});
it('should log in as guest', () => {
cy.dataCy('loginAsGuestButton').should('exist');
cy.dataCy('loginAsGuestButton').click();
cy.url().should('contain', '/#/cms/home');
});
it('should save localStorage keepLogin true value if remember checkbox is checked', () => {
cy.dataCy('rememberCheckbox').should('exist');
cy.dataCy('rememberCheckbox').click();
cy.getValue(rememberCheckbox).should('equal', 'true');
cy.loginFlow('employee', false);
cy.window().then(window => {
expect(window.localStorage.getItem('keepLogin')).to.eq('true');
});
});
it('should save localStorage keepLogin false value if remember checkbox is not checked', () => {
cy.dataCy('rememberCheckbox').should('exist');
cy.getValue(rememberCheckbox).should('equal', 'false');
cy.loginFlow('employee', false);
cy.window().then(window => {
expect(window.localStorage.getItem('keepLogin')).to.eq('false');
});
});
it('saves data in session storage if remember is not checked', () => {
cy.dataCy('rememberCheckbox').should('exist');
cy.getValue(rememberCheckbox).should('equal', 'false');
cy.loginFlow('employee', false);
cy.window().then(window => {
expect(window.sessionStorage.getItem('hederaLastUser')).to.eq(
'employee'
);
expect(window.localStorage.getItem('hederaLastUser')).to.eq(null);
});
});
it('saves data in local storage if remember is checked', () => {
cy.dataCy('rememberCheckbox').should('exist');
cy.dataCy('rememberCheckbox').click();
cy.getValue(rememberCheckbox).should('equal', 'true');
cy.loginFlow('employee', false);
cy.window().then(window => {
expect(window.localStorage.getItem('hederaLastUser')).to.eq(
'employee'
);
expect(window.sessionStorage.getItem('hederaLastUser')).to.eq(null);
});
});
it('should redirect to login if storage token is removed', () => {
cy.loginFlow('employee', false);
cy.window().then(window => {
window.sessionStorage.removeItem('token');
cy.visit('/#/account/conf');
cy.url().should('contain', '/#/login');
});
});
});

View File

@ -0,0 +1,31 @@
describe('PendingOrders', () => {
beforeEach(() => {
cy.login('brucewayne');
cy.visit('/#/ecomerce/orders');
});
const makePayment = typeAmount => {
cy.dataCy('makePaymentButton').should('exist');
cy.dataCy('makePaymentButton').click();
cy.dataCy('payAmountDialog').should('exist');
cy.dataCy('payAmountInput').find('input').should('exist');
cy.dataCy('payAmountInput').find('input').clear();
if (typeAmount || typeAmount === 0)
cy.dataCy('payAmountInput').find('input').type(typeAmount);
cy.setConfirmDialog();
};
// it('confirm payment redirects to payment site', () => {
// makePayment('100');
// TODO: Ver como hacer para verificar redirección a sitio externo
// });
it('fails payment if pay amount is 0', () => {
cy.visit('/#/ecomerce/orders');
makePayment(0);
cy.checkNotify(
'negative',
'La cantidad debe ser un número positivo e inferior o igual al importe pendiente'
);
});
});

View File

@ -0,0 +1,55 @@
describe('PendingOrders', () => {
before(() => {
// Esto se ejecuta una vez antes de todas las pruebas
cy.resetDB();
});
beforeEach(() => {
cy.login('developer');
});
const checkEmptyList = () => {
cy.dataCy('pendingOrdersList').should('exist');
cy.dataCy('pendingOrdersList').should('contain', 'Lista vacía');
cy.dataCy('pendingOrderCard').should('not.exist');
};
const checkExistingOrder = () => {
cy.dataCy('pendingOrdersList').should('exist');
cy.dataCy('pendingOrdersList').should('not.contain', 'Lista vacía');
cy.dataCy('pendingOrderCard').should('exist');
};
it('shows empty state', () => {
// Cuando la db se reinicia el usuario developer no tiene ordenes pendientes
cy.visit('/#/ecomerce/pending');
checkEmptyList();
});
it('creates a new order', () => {
cy.createOrderReceiveFlow();
cy.visit('/#/ecomerce/pending');
checkExistingOrder();
cy.dataCy('pendingOrderCard').should('have.length', 1);
cy.resetDB();
});
it('deletes an order', () => {
cy.createOrderReceiveFlow();
cy.visit('/#/ecomerce/pending');
checkExistingOrder();
cy.dataCy('pendingOrderCard').should('have.length', 1);
cy.dataCy('pendingOrderCardDelete').click();
cy.setConfirmDialog();
cy.checkNotify('positive', 'Datos guardados');
checkEmptyList();
});
it('adds order to basket', () => {
cy.createOrderReceiveFlow();
cy.visit('/#/ecomerce/pending');
cy.dataCy('addOrderToBasket').should('exist');
cy.dataCy('addOrderToBasket').click();
cy.checkNotify('positive', '¡Pedido cargado en la cesta!');
});
});

View File

@ -0,0 +1,93 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
// Importar dinámicamente todos los archivos con el sufijo .commands.js dentro de la carpeta src/test/cypress/integration
const requireCommands = require.context(
'../integration',
true,
/\.commands\.js$/
);
// Iterar sobre cada archivo y requerirlo
requireCommands.keys().forEach(requireCommands);
// Common commands
Cypress.Commands.add('selectOption', (selector, option) => {
cy.waitForElement(selector);
cy.get(selector).click();
cy.get('.q-menu .q-item').contains(option).click();
});
Cypress.Commands.add('getValue', selector => {
cy.get(selector).then($el => {
if ($el.find('.q-checkbox__inner').length > 0) {
// retornar el valor del atributo aria-checked
return cy.get(selector).invoke('attr', 'aria-checked');
} else {
return cy.get(selector).invoke('val');
}
});
});
Cypress.Commands.add('waitForElement', (element, timeout = 5000) => {
cy.get(element, { timeout }).should('be.visible');
});
Cypress.Commands.add('dataCy', (dataTestId, attr = 'data-testid') => {
return cy.get(`[${attr}="${dataTestId}"]`);
});
Cypress.Commands.add('getSessionStorage', key => {
cy.window().then(window => window.sessionStorage.getItem(key));
});
Cypress.Commands.add('getLocalStorage', key => {
cy.window().then(window => window.localStorage.getItem(key));
});
Cypress.Commands.add('setLocalStorage', (key, value) => {
cy.window().then(window => {
window.localStorage.setItem(key, value);
});
});
Cypress.Commands.add('setSessionStorage', (key, value) => {
cy.window().then(window => {
window.sessionStorage.setItem(key, value);
});
});
Cypress.Commands.add('resetDB', () => {
cy.exec('npm run resetDatabase');
});
Cypress.Commands.add('setConfirmDialog', () => {
cy.dataCy('confirmDialogButton').should('exist');
cy.dataCy('confirmDialogButton').click();
});
Cypress.Commands.add('checkNotify', (status, content) => {
cy.dataCy(`${status}Notify`).should('contain', content);
});

View File

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@ -0,0 +1,24 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your e2e test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
import './commands';
/* eslint-disable */
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false;
});
/* eslint-enable */