diff --git a/Jenkinsfile b/Jenkinsfile index 63577dad5e9..7f4144a5485 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -115,11 +115,14 @@ pipeline { steps { script { sh 'rm -f junit/e2e-*.xml' + sh 'rm -rf test/cypress/screenshots' env.COMPOSE_TAG = PROTECTED_BRANCH.contains(env.CHANGE_TARGET) ? env.CHANGE_TARGET : 'dev' def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs') sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY' + sh "docker-compose ${env.COMPOSE_PARAMS} pull back" + sh "docker-compose ${env.COMPOSE_PARAMS} pull db" sh "docker-compose ${env.COMPOSE_PARAMS} up -d" image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") { @@ -130,6 +133,7 @@ pipeline { post { always { sh "docker-compose ${env.COMPOSE_PARAMS} down -v" + archiveArtifacts artifacts: 'test/cypress/screenshots/**/*', allowEmptyArchive: true junit( testResults: 'junit/e2e-*.xml', allowEmptyResults: true diff --git a/README.md b/README.md index 262e12e5834..d280e29ceb5 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,18 @@ pnpm run test:front pnpm run test:e2e ``` +### Run e2e parallel + +```bash +pnpm run test:e2e:parallel +``` + +### View e2e parallel report + +```bash +pnpm run test:e2e:summary +``` + ### Build the app for production ```bash diff --git a/docs/Dockerfile.dev b/docs/Dockerfile.dev index 84a4d80bc37..3117e2c2050 100644 --- a/docs/Dockerfile.dev +++ b/docs/Dockerfile.dev @@ -25,6 +25,8 @@ RUN apt-get update \ libnss3 \ libxss1 \ libxtst6 \ + mesa-vulkan-drivers \ + vulkan-tools \ xauth \ xvfb \ && apt-get clean \ diff --git a/package.json b/package.json index 33b730b9e17..017412ef2ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salix-front", - "version": "25.12.0", + "version": "25.14.0", "description": "Salix frontend", "productName": "Salix", "author": "Verdnatura", @@ -13,7 +13,7 @@ "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore", "test:e2e": "cypress open", "test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run", - "test:e2e:parallel": "bash ./test/cypress/cypressParallel.sh", + "test:e2e:parallel": "bash ./test/cypress/run.sh", "test:e2e:summary": "bash ./test/cypress/summary.sh", "test": "echo \"See package.json => scripts for available tests.\" && exit 0", "test:front": "vitest", @@ -56,6 +56,7 @@ "eslint-plugin-cypress": "^4.1.0", "eslint-plugin-vue": "^9.32.0", "husky": "^8.0.0", + "junit-merge": "^2.0.0", "mocha": "^11.1.0", "postcss": "^8.4.23", "prettier": "^3.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 168fb9e0d4e..51fc7546973 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,6 +91,9 @@ devDependencies: husky: specifier: ^8.0.0 version: 8.0.3 + junit-merge: + specifier: ^2.0.0 + version: 2.0.0 mocha: specifier: ^11.1.0 version: 11.1.0 @@ -4860,6 +4863,10 @@ packages: universalify: 2.0.1 dev: true + /fs-readdir-recursive@1.1.0: + resolution: {integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==} + dev: true + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -5640,6 +5647,16 @@ packages: verror: 1.10.0 dev: true + /junit-merge@2.0.0: + resolution: {integrity: sha512-qwENzBWcdHPazNqPO0fKyFIqEyaSKyO0iyBeIU4Y/scjkXYpwTi88P2S/PWecqgMhzG2MOCwXk8QB9ucvXeIPw==} + hasBin: true + dependencies: + commander: 2.20.3 + fs-readdir-recursive: 1.1.0 + mkdirp: 0.5.6 + xmldoc: 1.3.0 + dev: true + /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: @@ -6037,7 +6054,6 @@ packages: hasBin: true dependencies: minimist: 1.2.8 - dev: false /mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} @@ -7253,6 +7269,10 @@ packages: resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==} dev: true + /sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + dev: true + /scheduler@0.25.0: resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} dev: true @@ -8689,6 +8709,12 @@ packages: engines: {node: '>=4.0'} dev: true + /xmldoc@1.3.0: + resolution: {integrity: sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==} + dependencies: + sax: 1.4.1 + dev: true + /xunit-viewer@10.6.1(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.3)(codemirror@6.0.1)(react-dom@19.0.0)(react@19.0.0): resolution: {integrity: sha512-ZMprLPVhCQJf2KD56tv2hlOjc4T+KnUe1E9DkEBHnuliOq7IOXWJf61pxyBMo/7H83B7Ln0DIeWNMMbx/3I7Jg==} hasBin: true diff --git a/src/components/CrudModel.vue b/src/components/CrudModel.vue index 8c4f70f3b9f..6303f48ae93 100644 --- a/src/components/CrudModel.vue +++ b/src/components/CrudModel.vue @@ -181,9 +181,8 @@ async function saveChanges(data) { return; } let changes = data || getChanges(); - if ($props.beforeSaveFn) { - changes = await $props.beforeSaveFn(changes, getChanges); - } + if ($props.beforeSaveFn) changes = await $props.beforeSaveFn(changes, getChanges); + try { if (changes?.creates?.length === 0 && changes?.updates?.length === 0) { return; @@ -194,7 +193,7 @@ async function saveChanges(data) { isLoading.value = false; } originalData.value = JSON.parse(JSON.stringify(formData.value)); - if (changes.creates?.length) await vnPaginateRef.value.fetch(); + if (changes?.creates?.length) await vnPaginateRef.value.fetch(); hasChanges.value = false; emit('saveChanges', data); diff --git a/src/components/FilterItemForm.vue b/src/components/FilterItemForm.vue index cacfde1b39a..cca8d80c3ef 100644 --- a/src/components/FilterItemForm.vue +++ b/src/components/FilterItemForm.vue @@ -188,7 +188,7 @@ const selectItem = ({ id }) => { > diff --git a/src/components/FilterTravelForm.vue b/src/components/FilterTravelForm.vue index c522d026960..4aad327b246 100644 --- a/src/components/FilterTravelForm.vue +++ b/src/components/FilterTravelForm.vue @@ -196,7 +196,7 @@ const selectTravel = ({ id }) => { > diff --git a/src/components/LeftMenu.vue b/src/components/LeftMenu.vue index 9a994949943..8e83bf5790f 100644 --- a/src/components/LeftMenu.vue +++ b/src/components/LeftMenu.vue @@ -77,6 +77,7 @@ watch( function findMatches(search, item) { const matches = []; function findRoute(search, item) { + if (!item?.children) return; for (const child of item.children) { if (search?.indexOf(child.name) > -1) { matches.push(child); @@ -92,7 +93,7 @@ function findMatches(search, item) { } function addChildren(module, route, parent) { - const menus = route?.meta?.menu ?? route?.menus?.[props.source]; //backwards compatible + const menus = route?.meta?.menu; if (!menus) return; const matches = findMatches(menus, route); @@ -107,11 +108,7 @@ function getRoutes() { main: getMainRoutes, card: getCardRoutes, }; - try { - handleRoutes[props.source](); - } catch (error) { - throw new Error(`Method is not defined`); - } + handleRoutes[props.source](); } function getMainRoutes() { const modules = Object.assign([], navigation.getModules().value); @@ -122,7 +119,6 @@ function getMainRoutes() { ); if (!moduleDef) continue; item.children = []; - addChildren(item.module, moduleDef, item.children); } @@ -132,21 +128,16 @@ function getMainRoutes() { function getCardRoutes() { const currentRoute = route.matched[1]; const currentModule = toLowerCamel(currentRoute.name); - let moduleDef = routes.find((route) => toLowerCamel(route.name) === currentModule); + let moduleDef; - if (!moduleDef) return; - if (!moduleDef?.menus) moduleDef = betaGetRoutes(); - addChildren(currentModule, moduleDef, items.value); -} - -function betaGetRoutes() { - let menuRoute; let index = route.matched.length - 1; - while (!menuRoute && index > 0) { - if (route.matched[index]?.meta?.menu) menuRoute = route.matched[index]; + while (!moduleDef && index > 0) { + if (route.matched[index]?.meta?.menu) moduleDef = route.matched[index]; index--; } - return menuRoute; + + if (!moduleDef) return; + addChildren(currentModule, moduleDef, items.value); } async function togglePinned(item, event) { diff --git a/src/components/TicketProblems.vue b/src/components/TicketProblems.vue index 255bea9cd68..59be9503594 100644 --- a/src/components/TicketProblems.vue +++ b/src/components/TicketProblems.vue @@ -28,6 +28,17 @@ defineProps({ row: { type: Object, required: true } }); {{ t('ticketSale.reserved') }} + + + {{ t('Ticket deleted') }} + + Object.keys(change.data).length > 0); } - - data.updates = changes.filter((change) => Object.keys(change.data).length > 0); - if ($attrs?.beforeSaveFn) data = $attrs.beforeSaveFn(data, getChanges); return data; @@ -685,7 +684,7 @@ const rowCtrlClickFunction = computed(() => { @update:selected="emit('update:selected', $event)" @selection="(details) => handleSelection(details, rows)" :hide-selected-banner="true" - :data-cy="$props.dataCy ?? 'vnTable'" + :data-cy >