Cypress initial config and tests #90

Merged
jsegarra merged 26 commits from wbuezas/hedera-web-mindshore:feature/CypressInit into 4922-vueMigration 2024-11-12 12:24:41 +00:00
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",

no entiendo la diferencia entre hedera-web y hedera-web-mindshore

no entiendo la diferencia entre hedera-web y hedera-web-mindshore

Que uno luego del gulp docker va al proyecto de hedera-web y el otro al proyecto hedera-web-mindshore (fork), eso luego pueden eliminarlo, lo uso para poder desarrollar en el fork

Que uno luego del `gulp docker` va al proyecto de `hedera-web` y el otro al proyecto `hedera-web-mindshore` (fork), eso luego pueden eliminarlo, lo uso para poder desarrollar en el fork
"cy:open-mindshore": "npm run resetDatabase && cd ../hedera-web-mindshore && cypress open",
"test:e2e-mindshore": "npm run resetDatabase && cd ../hedera-web-mindshore && cypress run",

si definimos este script, no podemos usarlo en el resto?

si definimos este script, no podemos usarlo en el resto?

Si, claro que se puede, ahí lo apliqué.

Commit: 40444ac46a

Si, claro que se puede, ahí lo apliqué. Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/40444ac46a8fe7d223684268a313ca0432af5ecf
"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"
Review

y si le concatenamos el indice?

y si le concatenamos el indice?
>
<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';
Review

IR a remember-password no va,

IR a remember-password no va, ❌
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();
Review

Este comando lo veo bien, pero cuando sea un before:run https://docs.cypress.io/api/node-events/before-run-api
No entiendo la necesidad de hacerlo en cada test, e incluso CatalogView lo tiene 2 veces. Es para que al tirarlo de manera individual, se refresque?

Este comando lo veo bien, pero cuando sea un before:run https://docs.cypress.io/api/node-events/before-run-api No entiendo la necesidad de hacerlo en cada test, e incluso CatalogView lo tiene 2 veces. Es para que al tirarlo de manera individual, se refresque?
Review

Entiendo lo que comentas, en base a esto, se te ocurre alguna alternativa para poder ejecutar diferentes tests sin que fallen los siguientes por haber creado / modificado data previamente?

Entiendo lo que comentas, en base a esto, se te ocurre alguna alternativa para poder ejecutar diferentes tests sin que fallen los siguientes por haber creado / modificado data previamente?
});
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');

podemos hacer cy.dataCy(lo que sea).find('input')
Lo he tirado y en verde

podemos hacer cy.dataCy(lo que sea).find('input') Lo he tirado y en verde

Concuerdo con la propuesta, aplicada.

Commmit: f70b98b514

Concuerdo con la propuesta, aplicada. Commmit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/f70b98b514741336dba895a5900ff45ef6bc68bd
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');

Esta podría ser candidata a irse a un comando global

Esta podría ser candidata a irse a un comando global

Command de notify creado y aplicado.

Commit: a142ceb151

Command de notify creado y aplicado. Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/a142ceb1514c08ce169d0841e926f26d1c6b65f0
});

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();

Estas 2 podrian ser candidatas a irse a un comando global

Estas 2 podrian ser candidatas a irse a un comando global

Coincido, aplicado.

Commit: e4bc14ef3b

Coincido, aplicado. Commit: https://gitea.verdnatura.es/verdnatura/hedera-web/commit/e4bc14ef3b7b8eb5f4951088c9b077a8b022363f
};
// 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 */