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:
commit
0fc1fc58e6
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -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 ./"
|
||||
|
|
|
@ -240,6 +240,7 @@ defineExpose({
|
|||
flat
|
||||
:disabled="!showBottomActions && !updatedColumns.length"
|
||||
@click="submit()"
|
||||
data-testid="formDefaultSaveButton"
|
||||
>
|
||||
<QTooltip>{{ t('save') }}</QTooltip>
|
||||
</QBtn>
|
||||
|
|
|
@ -91,6 +91,7 @@ async function confirm() {
|
|||
@click="confirm()"
|
||||
unelevated
|
||||
autofocus
|
||||
data-testid="confirmDialogButton"
|
||||
/>
|
||||
</QCardActions>
|
||||
</QCard>
|
||||
|
|
|
@ -71,6 +71,7 @@ onMounted(() => {
|
|||
is-outlined
|
||||
:clearable="false"
|
||||
class="searchbar"
|
||||
data-testid="searchBar"
|
||||
>
|
||||
<template #prepend>
|
||||
<QIcon name="search" class="cursor-pointer" @click="search()" />
|
||||
|
|
|
@ -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`
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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') }}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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') }}
|
||||
|
|
|
@ -122,6 +122,7 @@ const fetchData = async () => {
|
|||
:to="{ name: 'confirm', params: { id: orderId } }"
|
||||
rounded
|
||||
no-caps
|
||||
data-testid="basketToConfirmBtn"
|
||||
>
|
||||
<QTooltip>{{ t('checkout') }}</QTooltip>
|
||||
</QBtn>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'}`) }}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 |
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
});
|
|
@ -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'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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!');
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -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!');
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
});
|
|
@ -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')
|
|
@ -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 */
|
Loading…
Reference in New Issue