@eeacms/volto-slate-footnote 6.1.8 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,7 +4,35 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
- ### [6.1.8](https://github.com/eea/volto-slate-footnote/compare/6.1.7...6.1.8) - 19 October 2023
7
+ ### [6.2.1](https://github.com/eea/volto-slate-footnote/compare/6.2.0...6.2.1) - 18 January 2024
8
+
9
+ #### :bug: Bug Fixes
10
+
11
+ - fix: check if element exists when rendering extensions - refs #263143 [ana-oprea - [`9677db5`](https://github.com/eea/volto-slate-footnote/commit/9677db52435ab710e8a4d54720583eda0db7bd69)]
12
+
13
+ ### [6.2.0](https://github.com/eea/volto-slate-footnote/compare/6.1.8...6.2.0) - 17 January 2024
14
+
15
+ #### :rocket: New Features
16
+
17
+ - feat: Allow footnotes work on complex blocks such as statistics, accordion, tabs, where nested data is present - refs #261770 [dobri1408 - [`66363d3`](https://github.com/eea/volto-slate-footnote/commit/66363d3d64cde426c99f0e9f0ecad59816a80552)]
18
+ - feat(popup): is now accessible by keyboard tabbing [David Ichim - [`3649da0`](https://github.com/eea/volto-slate-footnote/commit/3649da0d381ae8a76b77c8a34a6217a72a0c3709)]
19
+
20
+ #### :bug: Bug Fixes
21
+
22
+ - fix: footnote error in statistic block - refs #261770 [dobri1408 - [`3cf696c`](https://github.com/eea/volto-slate-footnote/commit/3cf696c44dbb84fc54935acf3d5951ac2c198d88)]
23
+
24
+ #### :house: Internal changes
25
+
26
+ - chore: [JENKINS] Refactor automated testing [valentinab25 - [`5a5e1ad`](https://github.com/eea/volto-slate-footnote/commit/5a5e1adf997af84e93a4015dc66b7299abba0874)]
27
+
28
+ #### :hammer_and_wrench: Others
29
+
30
+ - test: [JENKINS] Use java17 for sonarqube scanner [valentinab25 - [`af6d78f`](https://github.com/eea/volto-slate-footnote/commit/af6d78f60dd040fe61ebb225f4a689ac21ffb55c)]
31
+ - test: [JENKINS] Run cypress in started frontend container [valentinab25 - [`7f4c07f`](https://github.com/eea/volto-slate-footnote/commit/7f4c07f1fc2e9893d1f925152a473855a7e0f064)]
32
+ - test: [JENKINS] Add cpu limit on cypress docker [valentinab25 - [`0670217`](https://github.com/eea/volto-slate-footnote/commit/0670217cca01571e8a3bd64ee3d85bb53ce447be)]
33
+ - test: [JENKINS] Increase shm-size to cypress docker [valentinab25 - [`ec08749`](https://github.com/eea/volto-slate-footnote/commit/ec08749f9547d2c133b2174ab8d60cf2bdb1b23a)]
34
+ - test: [JENKINS] Improve cypress time [valentinab25 - [`237e6a3`](https://github.com/eea/volto-slate-footnote/commit/237e6a331e3608aa35d1c93a03c435daf330c2bd)]
35
+ ### [6.1.8](https://github.com/eea/volto-slate-footnote/compare/6.1.7...6.1.8) - 22 October 2023
8
36
 
9
37
  #### :house: Internal changes
10
38
 
package/Dockerfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # syntax=docker/dockerfile:1
2
2
  ARG VOLTO_VERSION
3
- FROM plone/frontend-builder:${VOLTO_VERSION}
3
+ FROM eeacms/frontend-builder:${VOLTO_VERSION}
4
4
 
5
5
  ARG ADDON_NAME
6
6
  ARG ADDON_PATH
package/Jenkinsfile CHANGED
@@ -1,16 +1,23 @@
1
1
  pipeline {
2
- agent any
2
+ tools {
3
+ jdk 'Java17'
4
+ }
5
+ agent {
6
+ node { label 'docker-host' }
7
+ }
3
8
 
4
9
  environment {
5
- GIT_NAME = "volto-slate-footnote"
6
- NAMESPACE = "@eeacms"
7
- SONARQUBE_TAGS = "volto.eea.europa.eu,biodiversity.europa.eu,www.eea.europa.eu-ims,climate-energy.eea.europa.eu,sustainability.eionet.europa.eu,forest.eea.europa.eu,clms.land.copernicus.eu,industry.eea.europa.eu,water.europa.eu-freshwater,demo-www.eea.europa.eu,clmsdemo.devel6cph.eea.europa.eu,water.europa.eu-marine,climate-adapt.eea.europa.eu,climate-advisory-board.devel4cph.eea.europa.eu,climate-advisory-board.europa.eu,www.eea.europa.eu-en"
8
- DEPENDENCIES = ""
9
- VOLTO = "16"
10
- }
10
+ GIT_NAME = "volto-slate-footnote"
11
+ NAMESPACE = "@eeacms"
12
+ SONARQUBE_TAGS = "volto.eea.europa.eu,biodiversity.europa.eu,www.eea.europa.eu-ims,climate-energy.eea.europa.eu,sustainability.eionet.europa.eu,forest.eea.europa.eu,clms.land.copernicus.eu,industry.eea.europa.eu,water.europa.eu-freshwater,demo-www.eea.europa.eu,clmsdemo.devel6cph.eea.europa.eu,water.europa.eu-marine,climate-adapt.eea.europa.eu,climate-advisory-board.devel4cph.eea.europa.eu,climate-advisory-board.europa.eu,www.eea.europa.eu-en"
13
+ DEPENDENCIES = ""
14
+ BACKEND_PROFILES = "eea.kitkat:testing"
15
+ BACKEND_ADDONS = ""
16
+ VOLTO = "16"
17
+ IMAGE_NAME = BUILD_TAG.toLowerCase()
18
+ }
11
19
 
12
20
  stages {
13
-
14
21
  stage('Release') {
15
22
  when {
16
23
  allOf {
@@ -20,52 +27,41 @@ pipeline {
20
27
  }
21
28
  steps {
22
29
  node(label: 'docker') {
23
- withCredentials([string(credentialsId: 'eea-jenkins-token', variable: 'GITHUB_TOKEN'),string(credentialsId: 'eea-jenkins-npm-token', variable: 'NPM_TOKEN')]) {
24
- sh '''docker pull eeacms/gitflow'''
25
- sh '''docker run -i --rm --name="$BUILD_TAG-gitflow-master" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" -e GIT_TOKEN="$GITHUB_TOKEN" -e NPM_TOKEN="$NPM_TOKEN" -e LANGUAGE=javascript eeacms/gitflow'''
30
+ withCredentials([string(credentialsId: 'eea-jenkins-token', variable: 'GITHUB_TOKEN'), string(credentialsId: 'eea-jenkins-npm-token', variable: 'NPM_TOKEN')]) {
31
+ sh '''docker run -i --rm --pull always --name="$IMAGE_NAME-gitflow-master" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" -e GIT_TOKEN="$GITHUB_TOKEN" -e NPM_TOKEN="$NPM_TOKEN" -e LANGUAGE=javascript eeacms/gitflow'''
26
32
  }
27
33
  }
28
34
  }
29
35
  }
30
36
 
31
- stage('Code') {
37
+ stage('Check if testing needed') {
32
38
  when {
33
39
  allOf {
34
- environment name: 'CHANGE_ID', value: ''
35
- not { changelog '.*^Automated release [0-9\\.]+$' }
36
40
  not { branch 'master' }
41
+ not { branch 'develop' }
42
+ environment name: 'CHANGE_ID', value: ''
37
43
  }
38
44
  }
39
45
  steps {
40
- parallel(
41
-
42
- "ES lint": {
43
- node(label: 'docker') {
44
- sh '''docker run -i --rm --name="$BUILD_TAG-eslint" -e NAMESPACE="$NAMESPACE" -e VOLTO=$VOLTO -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e VOLTO=$VOLTO plone/volto-addon-ci eslint'''
45
- }
46
- },
47
-
48
- "Style lint": {
49
- node(label: 'docker') {
50
- sh '''docker run -i --rm --name="$BUILD_TAG-stylelint" -e NAMESPACE="$NAMESPACE" -e VOLTO=$VOLTO -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e VOLTO=$VOLTO plone/volto-addon-ci stylelint'''
51
- }
52
- },
46
+ script {
47
+ withCredentials([string(credentialsId: 'eea-jenkins-token', variable: 'GITHUB_TOKEN')]) {
48
+ check_result = sh script: '''docker run --pull always -i --rm --name="$IMAGE_NAME-gitflow-check" -e GIT_TOKEN="$GITHUB_TOKEN" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_ORG="$GIT_ORG" -e GIT_NAME="$GIT_NAME" eeacms/gitflow /check_if_testing_needed.sh''', returnStatus: true
53
49
 
54
- "Prettier": {
55
- node(label: 'docker') {
56
- sh '''docker run -i --rm --name="$BUILD_TAG-prettier" -e VOLTO=$VOLTO -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e VOLTO=$VOLTO plone/volto-addon-ci prettier'''
50
+ if (check_result == 0) {
51
+ env.SKIP_TESTS = 'yes'
52
+ }
57
53
  }
58
- }
59
- )
54
+ }
60
55
  }
61
56
  }
62
57
 
63
- stage('Tests') {
58
+ stage('Testing') {
64
59
  when {
65
60
  anyOf {
66
61
  allOf {
67
62
  not { environment name: 'CHANGE_ID', value: '' }
68
63
  environment name: 'CHANGE_TARGET', value: 'develop'
64
+ environment name: 'SKIP_TESTS', value: ''
69
65
  }
70
66
  allOf {
71
67
  environment name: 'CHANGE_ID', value: ''
@@ -73,26 +69,76 @@ pipeline {
73
69
  not { changelog '.*^Automated release [0-9\\.]+$' }
74
70
  branch 'master'
75
71
  }
72
+ environment name: 'SKIP_TESTS', value: ''
76
73
  }
77
74
  }
78
75
  }
79
- steps {
80
- parallel(
76
+ stages {
77
+ stage('Build test image') {
78
+ steps {
79
+ checkout scm
80
+ sh '''docker build --pull --build-arg="VOLTO_VERSION=$VOLTO" --build-arg="ADDON_NAME=$NAMESPACE/$GIT_NAME" --build-arg="ADDON_PATH=$GIT_NAME" . -t $IMAGE_NAME-frontend'''
81
+ }
82
+ }
83
+
84
+ stage('Fix code') {
85
+ when {
86
+ environment name: 'CHANGE_ID', value: ''
87
+ not { branch 'master' }
88
+ }
89
+ steps {
90
+ script {
91
+ fix_result = sh(script: '''docker run --name="$IMAGE_NAME-fix" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend ci-fix''', returnStatus: true)
92
+ sh '''docker cp $IMAGE_NAME-fix:/app/src/addons/$GIT_NAME/src .'''
93
+ sh '''docker rm -v $IMAGE_NAME-fix'''
94
+ FOUND_FIX = sh(script: '''git diff | wc -l''', returnStdout: true).trim()
81
95
 
82
- "Volto": {
83
- node(label: 'docker') {
84
- script {
85
- try {
86
- sh '''docker pull plone/volto-addon-ci'''
87
- sh '''docker run -i --name="$BUILD_TAG-volto" -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e VOLTO=$VOLTO plone/volto-addon-ci'''
88
- sh '''rm -rf xunit-reports'''
89
- sh '''mkdir -p xunit-reports'''
90
- sh '''docker cp $BUILD_TAG-volto:/opt/frontend/my-volto-project/coverage xunit-reports/'''
91
- sh '''docker cp $BUILD_TAG-volto:/opt/frontend/my-volto-project/junit.xml xunit-reports/'''
92
- sh '''docker cp $BUILD_TAG-volto:/opt/frontend/my-volto-project/unit_tests_log.txt xunit-reports/'''
93
- stash name: "xunit-reports", includes: "xunit-reports/**"
94
- archiveArtifacts artifacts: "xunit-reports/unit_tests_log.txt", fingerprint: true
95
- publishHTML (target : [
96
+ if (FOUND_FIX != '0') {
97
+ withCredentials([string(credentialsId: 'eea-jenkins-token', variable: 'GITHUB_TOKEN')]) {
98
+ sh '''sed -i "s|url = .*|url = https://eea-jenkins:$GITHUB_TOKEN@github.com/eea/$GIT_NAME.git|" .git/config'''
99
+ }
100
+ sh '''git fetch origin $GIT_BRANCH:$GIT_BRANCH'''
101
+ sh '''git checkout $GIT_BRANCH'''
102
+ sh '''git add src/'''
103
+ sh '''git commit -m "style: Automated code fix" '''
104
+ sh '''git push --set-upstream origin $GIT_BRANCH'''
105
+ sh '''exit 1'''
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ stage('ES lint') {
112
+ steps {
113
+ sh '''docker run --rm --name="$IMAGE_NAME-eslint" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend lint'''
114
+ }
115
+ }
116
+
117
+ stage('Style lint') {
118
+ steps {
119
+ sh '''docker run --rm --name="$IMAGE_NAME-stylelint" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend stylelint'''
120
+ }
121
+ }
122
+
123
+ stage('Prettier') {
124
+ steps {
125
+ sh '''docker run --rm --name="$IMAGE_NAME-prettier" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend prettier'''
126
+ }
127
+ }
128
+
129
+ stage('Coverage Tests') {
130
+ parallel {
131
+
132
+ stage('Unit tests') {
133
+ steps {
134
+ script {
135
+ try {
136
+ sh '''docker run --name="$IMAGE_NAME-volto" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend test-ci'''
137
+ sh '''rm -rf xunit-reports'''
138
+ sh '''mkdir -p xunit-reports'''
139
+ sh '''docker cp $IMAGE_NAME-volto:/app/coverage xunit-reports/'''
140
+ sh '''docker cp $IMAGE_NAME-volto:/app/junit.xml xunit-reports/'''
141
+ publishHTML(target : [
96
142
  allowMissing: false,
97
143
  alwaysLinkToLastBuild: true,
98
144
  keepAll: true,
@@ -105,75 +151,67 @@ pipeline {
105
151
  catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
106
152
  junit testResults: 'xunit-reports/junit.xml', allowEmptyResults: true
107
153
  }
108
- sh script: '''docker rm -v $BUILD_TAG-volto''', returnStatus: true
154
+ sh script: '''docker rm -v $IMAGE_NAME-volto''', returnStatus: true
155
+ }
109
156
  }
110
157
  }
111
158
  }
112
- }
113
- )
114
- }
115
- }
159
+
160
+ stage('Integration tests') {
161
+ steps {
162
+ script {
163
+ try {
164
+ sh '''docker run --pull always --rm -d --name="$IMAGE_NAME-plone" -e SITE="Plone" -e PROFILES="$BACKEND_PROFILES" -e ADDONS="$BACKEND_ADDONS" eeacms/plone-backend'''
165
+ sh '''docker run -d --shm-size=3g --link $IMAGE_NAME-plone:plone --name="$IMAGE_NAME-cypress" -e "RAZZLE_INTERNAL_API_PATH=http://plone:8080/Plone" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend start-ci'''
166
+ sh '''timeout -s 9 1800 docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress make cypress-ci'''
167
+ } finally {
168
+ try {
169
+ sh '''rm -rf cypress-videos cypress-results cypress-coverage cypress-screenshots'''
170
+ sh '''mkdir -p cypress-videos cypress-results cypress-coverage cypress-screenshots'''
171
+ videos = sh script: '''docker cp $IMAGE_NAME-cypress:/app/src/addons/$GIT_NAME/cypress/videos cypress-videos/''', returnStatus: true
172
+ sh '''docker cp $IMAGE_NAME-cypress:/app/src/addons/$GIT_NAME/cypress/reports cypress-results/'''
173
+ screenshots = sh script: '''docker cp $IMAGE_NAME-cypress:/app/src/addons/$GIT_NAME/cypress/screenshots cypress-screenshots''', returnStatus: true
116
174
 
117
- stage('Integration tests') {
118
- when {
119
- anyOf {
120
- allOf {
121
- not { environment name: 'CHANGE_ID', value: '' }
122
- environment name: 'CHANGE_TARGET', value: 'develop'
123
- }
124
- allOf {
125
- environment name: 'CHANGE_ID', value: ''
126
- anyOf {
127
- not { changelog '.*^Automated release [0-9\\.]+$' }
128
- branch 'master'
129
- }
130
- }
131
- }
132
- }
133
- steps {
134
- parallel(
175
+ archiveArtifacts artifacts: 'cypress-screenshots/**', fingerprint: true, allowEmptyArchive: true
135
176
 
136
- "Cypress": {
137
- node(label: 'docker') {
138
- script {
139
- try {
140
- sh '''docker pull eeacms/plone-backend; docker run --rm -d --name="$BUILD_TAG-plone" -e SITE="Plone" -e PROFILES="eea.kitkat:testing" eeacms/plone-backend'''
141
- sh '''docker pull plone/volto-addon-ci; docker run -i --name="$BUILD_TAG-cypress" --link $BUILD_TAG-plone:plone -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e DEPENDENCIES="$DEPENDENCIES" -e NODE_ENV=development -e VOLTO="$VOLTO" plone/volto-addon-ci cypress'''
142
- } finally {
143
- try {
144
- sh '''rm -rf cypress-reports cypress-results cypress-coverage'''
145
- sh '''mkdir -p cypress-reports cypress-results cypress-coverage'''
146
- sh '''docker cp $BUILD_TAG-cypress:/opt/frontend/my-volto-project/src/addons/$GIT_NAME/cypress/videos cypress-reports/'''
147
- sh '''docker cp $BUILD_TAG-cypress:/opt/frontend/my-volto-project/src/addons/$GIT_NAME/cypress/reports cypress-results/'''
148
- coverage = sh script: '''docker cp $BUILD_TAG-cypress:/opt/frontend/my-volto-project/src/addons/$GIT_NAME/coverage cypress-coverage/''', returnStatus: true
149
- if ( coverage == 0 ) {
150
- publishHTML (target : [allowMissing: false,
177
+ coverage = sh script: '''docker cp $IMAGE_NAME-cypress:/app/src/addons/$GIT_NAME/coverage cypress-coverage''', returnStatus: true
178
+
179
+ if ( coverage == 0 ) {
180
+ publishHTML(target : [allowMissing: false,
151
181
  alwaysLinkToLastBuild: true,
152
182
  keepAll: true,
153
183
  reportDir: 'cypress-coverage/coverage/lcov-report',
154
184
  reportFiles: 'index.html',
155
185
  reportName: 'CypressCoverage',
156
186
  reportTitles: 'Integration Tests Code Coverage'])
157
- }
158
- sh '''touch empty_file; for ok_test in $(grep -E 'file=.*failures="0"' $(grep 'testsuites .*failures="0"' $(find cypress-results -name *.xml) empty_file | awk -F: '{print $1}') empty_file | sed 's/.* file="\\(.*\\)" time.*/\\1/' | sed 's#^cypress/integration/##g' | sed 's#^../../../node_modules/@eeacms/##g'); do rm -f cypress-reports/videos/$ok_test.mp4; rm -f cypress-reports/$ok_test.mp4; done'''
159
- archiveArtifacts artifacts: 'cypress-reports/**/*.mp4', fingerprint: true, allowEmptyArchive: true
160
- stash name: "cypress-coverage", includes: "cypress-coverage/**", allowEmpty: true
161
- }
162
- finally {
163
- catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
187
+ }
188
+ if ( videos == 0 ) {
189
+ sh '''for file in $(find cypress-results -name *.xml); do if [ $(grep -E 'failures="[1-9].*"' $file | wc -l) -eq 0 ]; then testname=$(grep -E 'file=.*failures="0"' $file | sed 's#.* file=".*\\/\\(.*\\.[jsxt]\\+\\)" time.*#\\1#' ); rm -f cypress-videos/videos/$testname.mp4; fi; done'''
190
+ archiveArtifacts artifacts: 'cypress-videos/**/*.mp4', fingerprint: true, allowEmptyArchive: true
191
+ }
192
+ } finally {
193
+ catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
164
194
  junit testResults: 'cypress-results/**/*.xml', allowEmptyResults: true
195
+ }
196
+ catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
197
+ sh '''docker logs $IMAGE_NAME-cypress'''
198
+ }
199
+ sh script: "docker stop $IMAGE_NAME-cypress", returnStatus: true
200
+ sh script: "docker stop $IMAGE_NAME-plone", returnStatus: true
201
+ sh script: "docker rm -v $IMAGE_NAME-plone", returnStatus: true
202
+ sh script: "docker rm -v $IMAGE_NAME-cypress", returnStatus: true
165
203
  }
166
- sh script: "docker stop $BUILD_TAG-plone", returnStatus: true
167
- sh script: "docker rm -v $BUILD_TAG-plone", returnStatus: true
168
- sh script: "docker rm -v $BUILD_TAG-cypress", returnStatus: true
169
-
170
204
  }
171
205
  }
172
206
  }
173
207
  }
174
208
  }
175
-
176
- )
209
+ }
210
+ }
211
+ post {
212
+ always {
213
+ sh script: "docker rmi $IMAGE_NAME-frontend", returnStatus: true
214
+ }
177
215
  }
178
216
  }
179
217
 
@@ -197,19 +235,14 @@ pipeline {
197
235
  }
198
236
  }
199
237
  steps {
200
- node(label: 'swarm') {
201
- script{
202
- checkout scm
203
- unstash "xunit-reports"
204
- unstash "cypress-coverage"
205
- def scannerHome = tool 'SonarQubeScanner';
206
- def nodeJS = tool 'NodeJS';
207
- withSonarQubeEnv('Sonarqube') {
208
- sh '''sed -i "s#/opt/frontend/my-volto-project/src/addons/${GIT_NAME}/##g" xunit-reports/coverage/lcov.info'''
209
- sh '''sed -i "s#src/addons/${GIT_NAME}/##g" xunit-reports/coverage/lcov.info'''
210
- sh "export PATH=${scannerHome}/bin:${nodeJS}/bin:$PATH; sonar-scanner -Dsonar.javascript.lcov.reportPaths=./xunit-reports/coverage/lcov.info,./cypress-coverage/coverage/lcov.info -Dsonar.sources=./src -Dsonar.projectKey=$GIT_NAME-$BRANCH_NAME -Dsonar.projectVersion=$BRANCH_NAME-$BUILD_NUMBER"
211
- sh '''try=2; while [ \$try -gt 0 ]; do curl -s -XPOST -u "${SONAR_AUTH_TOKEN}:" "${SONAR_HOST_URL}api/project_tags/set?project=${GIT_NAME}-${BRANCH_NAME}&tags=${SONARQUBE_TAGS},${BRANCH_NAME}" > set_tags_result; if [ \$(grep -ic error set_tags_result ) -eq 0 ]; then try=0; else cat set_tags_result; echo "... Will retry"; sleep 60; try=\$(( \$try - 1 )); fi; done'''
212
- }
238
+ script {
239
+ def scannerHome = tool 'SonarQubeScanner'
240
+ def nodeJS = tool 'NodeJS'
241
+ withSonarQubeEnv('Sonarqube') {
242
+ sh '''sed -i "s#/app/src/addons/${GIT_NAME}/##g" xunit-reports/coverage/lcov.info'''
243
+ sh '''sed -i "s#src/addons/${GIT_NAME}/##g" xunit-reports/coverage/lcov.info'''
244
+ sh "export PATH=${scannerHome}/bin:${nodeJS}/bin:$PATH; sonar-scanner -Dsonar.javascript.lcov.reportPaths=./xunit-reports/coverage/lcov.info,./cypress-coverage/coverage/lcov.info -Dsonar.sources=./src -Dsonar.projectKey=$GIT_NAME-$BRANCH_NAME -Dsonar.projectVersion=$BRANCH_NAME-$BUILD_NUMBER"
245
+ sh '''try=5; while [ \$try -gt 0 ]; do curl -s -XPOST -u "${SONAR_AUTH_TOKEN}:" "${SONAR_HOST_URL}api/project_tags/set?project=${GIT_NAME}-${BRANCH_NAME}&tags=${SONARQUBE_TAGS},${BRANCH_NAME}" > set_tags_result; if [ \$(grep -ic error set_tags_result ) -eq 0 ]; then try=0; else cat set_tags_result; echo "... Will retry"; sleep 15; try=\$(( \$try - 1 )); fi; done'''
213
246
  }
214
247
  }
215
248
  }
@@ -230,18 +263,15 @@ pipeline {
230
263
  }
231
264
  }
232
265
  steps {
233
- node(label: 'docker') {
234
- script {
235
- sh '''docker pull eeacms/gitflow'''
236
- sh '''echo "Error" > checkresult.txt'''
237
- catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
238
- sh '''set -o pipefail; docker run -i --rm --name="$BUILD_TAG-gitflow-sn" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" eeacms/gitflow /checkSonarqubemaster.sh | grep -v "Found script" | tee checkresult.txt'''
239
- }
240
-
241
- publishChecks name: 'SonarQube', title: 'Sonarqube Code Quality Check', summary: "Quality check on the SonarQube metrics from branch develop, comparing it with the ones from master branch. No bugs are allowed",
242
- text: readFile(file: 'checkresult.txt'), conclusion: "${currentBuild.currentResult}",
243
- detailsURL: "${env.BUILD_URL}display/redirect"
266
+ script {
267
+ sh '''echo "Error" > checkresult.txt'''
268
+ catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
269
+ sh '''set -o pipefail; docker run -i --rm --pull always --name="$IMAGE_NAME-gitflow-sn" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_NAME="$GIT_NAME" eeacms/gitflow /checkSonarqubemaster.sh | grep -v "Found script" | tee checkresult.txt'''
244
270
  }
271
+
272
+ publishChecks name: 'SonarQube', title: 'Sonarqube Code Quality Check', summary: 'Quality check on the SonarQube metrics from branch develop, comparing it with the ones from master branch. No bugs are allowed',
273
+ text: readFile(file: 'checkresult.txt'), conclusion: "${currentBuild.currentResult}",
274
+ detailsURL: "${env.BUILD_URL}display/redirect"
245
275
  }
246
276
  }
247
277
  }
@@ -254,20 +284,16 @@ pipeline {
254
284
  environment name: 'CHANGE_TARGET', value: 'master'
255
285
  }
256
286
  steps {
257
- node(label: 'docker') {
258
- script {
259
- if ( env.CHANGE_BRANCH != "develop" ) {
260
- error "Pipeline aborted due to PR not made from develop branch"
261
- }
262
- withCredentials([string(credentialsId: 'eea-jenkins-token', variable: 'GITHUB_TOKEN')]) {
263
- sh '''docker pull eeacms/gitflow'''
264
- sh '''docker run -i --rm --name="$BUILD_TAG-gitflow-pr" -e GIT_CHANGE_TARGET="$CHANGE_TARGET" -e GIT_CHANGE_BRANCH="$CHANGE_BRANCH" -e GIT_CHANGE_AUTHOR="$CHANGE_AUTHOR" -e GIT_CHANGE_TITLE="$CHANGE_TITLE" -e GIT_TOKEN="$GITHUB_TOKEN" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e GIT_ORG="$GIT_ORG" -e GIT_NAME="$GIT_NAME" -e LANGUAGE=javascript eeacms/gitflow'''
265
- }
287
+ script {
288
+ if (env.CHANGE_BRANCH != 'develop') {
289
+ error 'Pipeline aborted due to PR not made from develop branch'
290
+ }
291
+ withCredentials([string(credentialsId: 'eea-jenkins-token', variable: 'GITHUB_TOKEN')]) {
292
+ sh '''docker run --pull always -i --rm --name="$IMAGE_NAME-gitflow-pr" -e GIT_CHANGE_TARGET="$CHANGE_TARGET" -e GIT_CHANGE_BRANCH="$CHANGE_BRANCH" -e GIT_CHANGE_AUTHOR="$CHANGE_AUTHOR" -e GIT_CHANGE_TITLE="$CHANGE_TITLE" -e GIT_TOKEN="$GITHUB_TOKEN" -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e GIT_ORG="$GIT_ORG" -e GIT_NAME="$GIT_NAME" -e LANGUAGE=javascript eeacms/gitflow'''
266
293
  }
267
294
  }
268
295
  }
269
296
  }
270
-
271
297
  }
272
298
 
273
299
  post {
package/Makefile CHANGED
@@ -50,6 +50,11 @@ VOLTO_VERSION?=16
50
50
  ADDON_PATH="${DIR}"
51
51
  ADDON_NAME="@eeacms/${ADDON_PATH}"
52
52
  DOCKER_COMPOSE=PLONE_VERSION=${PLONE_VERSION} VOLTO_VERSION=${VOLTO_VERSION} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} docker compose
53
+ RAZZLE_INTERNAL_API_PATH?="http://localhost:8080/Plone"
54
+ RAZZLE_DEV_PROXY_API_PATH?="${RAZZLE_INTERNAL_API_PATH}"
55
+ CYPRESS_API_PATH="${RAZZLE_DEV_PROXY_API_PATH}"
56
+
57
+
53
58
 
54
59
  # Top-level targets
55
60
  .PHONY: all
@@ -77,11 +82,11 @@ shell: ## Start a shell in the frontend container
77
82
 
78
83
  .PHONY: cypress-open
79
84
  cypress-open: ## Open cypress integration tests
80
- NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress open
85
+ CYPRESS_API_PATH="${RAZZLE_DEV_PROXY_API_PATH}" NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress open
81
86
 
82
87
  .PHONY: cypress-run
83
88
  cypress-run: ## Run cypress integration tests
84
- NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress run
89
+ CYPRESS_API_PATH="${RAZZLE_DEV_PROXY_API_PATH}" NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress run --browser chromium
85
90
 
86
91
  .PHONY: test
87
92
  test: ## Run jest tests
@@ -129,3 +134,29 @@ i18n: ## i18n
129
134
  help: ## Show this help.
130
135
  @echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)"
131
136
  head -n 14 Makefile
137
+
138
+ .PHONY: ci-fix
139
+ ci-fix:
140
+ echo "Running lint-fix"
141
+ make lint-fix
142
+ echo "Running prettier-fix"
143
+ make prettier-fix
144
+ echo "Running stylelint-fix"
145
+ make stylelint-fix
146
+
147
+ .PHONY: test-ci
148
+ test-ci:
149
+ cd /app
150
+ RAZZLE_JEST_CONFIG=src/addons/${ADDON_PATH}/jest-addon.config.js CI=true yarn test src/addons/${ADDON_PATH}/src --watchAll=false --reporters=default --reporters=jest-junit --collectCoverage --coverageReporters lcov cobertura text
151
+
152
+ .PHONY: start-ci
153
+ start-ci:
154
+ cp .coverage.babel.config.js /app/babel.config.js
155
+ cd ../..
156
+ yarn start
157
+
158
+ .PHONY: cypress-ci
159
+ cypress-ci:
160
+ $(NODE_MODULES)/.bin/wait-on -t 240000 http://localhost:3000
161
+ NODE_ENV=development make cypress-run
162
+
package/cypress.config.js CHANGED
@@ -2,12 +2,12 @@ const { defineConfig } = require('cypress');
2
2
 
3
3
  module.exports = defineConfig({
4
4
  viewportWidth: 1280,
5
- defaultCommandTimeout: 5000,
5
+ defaultCommandTimeout: 8888,
6
6
  chromeWebSecurity: false,
7
7
  reporter: 'junit',
8
- video: true,
8
+ video: false,
9
9
  retries: {
10
- runMode: 1,
10
+ runMode: 2,
11
11
  openMode: 0,
12
12
  },
13
13
  reporterOptions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-slate-footnote",
3
- "version": "6.1.8",
3
+ "version": "6.2.1",
4
4
  "description": "volto-slate-footnote: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -20,24 +20,70 @@ const alphabet = 'abcdefghijklmnopqrstuvwxyz';
20
20
  * received from the Volto form.
21
21
  */
22
22
  const FootnotesBlockView = (props) => {
23
- const { data, properties } = props;
23
+ const { data, properties, tabData, content } = props;
24
24
  const { title, global, placeholder = 'Footnotes' } = data;
25
+
25
26
  const metadata = props.metadata ? props.metadata : properties;
26
- const globalMetadata = global ? metadata : properties;
27
- const blocks = getAllBlocksAndSlateFields(globalMetadata);
27
+
28
+ const localMetadata = content
29
+ ? content
30
+ : tabData
31
+ ? tabData
32
+ : global
33
+ ? metadata
34
+ : properties;
35
+ const blocks = getAllBlocksAndSlateFields(localMetadata);
28
36
  const notesObj = makeFootnoteListOfUniqueItems(blocks);
37
+ let startList = 1;
38
+ if (Object.keys(notesObj).length > 0) {
39
+ const noteId = Object.keys(notesObj)[0];
40
+ const note = notesObj[noteId];
41
+ const { zoteroId } = note;
42
+
43
+ const notesGlobalResult = makeFootnoteListOfUniqueItems(
44
+ getAllBlocksAndSlateFields(metadata),
45
+ );
46
+
47
+ const indiceIfZoteroId = note.extra
48
+ ? [
49
+ Object.keys(notesGlobalResult).indexOf(zoteroId) + 1, // parent footnote
50
+ ...note.extra.map(
51
+ // citations from extra
52
+ (zoteroObj, _index) =>
53
+ // all zotero citation are indexed by zoteroId in notesGlobalResult
54
+
55
+ Object.keys(notesGlobalResult).indexOf(zoteroObj.zoteroId) + 1,
56
+ ),
57
+ ]
58
+ : // no extra citations (no multiples)
59
+ Object.keys(notesGlobalResult).indexOf(zoteroId) + 1;
60
+ const citationIndice = zoteroId // ZOTERO
61
+ ? indiceIfZoteroId
62
+ : // FOOTNOTES
63
+ // parent footnote
64
+ [note, ...(note.extra || [])].map((footnoteObj, _index) => {
65
+ return (
66
+ Object.keys(notesGlobalResult).indexOf(
67
+ Object.keys(notesGlobalResult).find(
68
+ (key) =>
69
+ notesGlobalResult[key].footnote === footnoteObj.footnote,
70
+ ),
71
+ ) + 1
72
+ );
73
+ });
74
+ startList = citationIndice;
75
+ }
29
76
 
30
77
  return (
31
78
  <div className="footnotes-listing-block">
32
79
  <h3 title={placeholder}>{title}</h3>
33
80
  {notesObj && (
34
- <ol>
81
+ <ol start={startList}>
35
82
  {Object.keys(notesObj).map((noteId) => {
36
83
  const note = notesObj[noteId];
37
84
  const { uid, footnote, zoteroId, parentUid } = note;
38
85
  const { refs } = note;
39
86
  const refsList = refs ? Object.keys(refs) : null;
40
-
41
87
  return (
42
88
  <li
43
89
  key={`footnote-${zoteroId || uid}`}
@@ -41,7 +41,6 @@ const FootnoteEditor = (props) => {
41
41
  const metadataBlocks = getAllBlocksAndSlateFields(metadata);
42
42
  const storeBlocks = getAllBlocksAndSlateFields(initialFormData);
43
43
  const uniqueFootnoteBlocks = [];
44
-
45
44
  const flatAllBlocks = isEmpty(metadata) ? storeBlocks : metadataBlocks;
46
45
  /**
47
46
  * Will add only the items that are unique by text
@@ -6,12 +6,11 @@ export const withFootnote = (editor) => {
6
6
  const { normalizeNode, isInline } = editor;
7
7
 
8
8
  editor.isInline = (element) => {
9
- return element.type === FOOTNOTE ? true : isInline(element);
9
+ return element && element.type === FOOTNOTE ? true : isInline(element);
10
10
  };
11
11
 
12
12
  editor.normalizeNode = (entry) => {
13
13
  const [node, path] = entry;
14
-
15
14
  if (node.type === FOOTNOTE && !node.data?.uid) {
16
15
  Transforms.setNodes(
17
16
  editor,
@@ -24,6 +24,7 @@ export const FootnoteElement = (props) => {
24
24
  const { data = {} } = element;
25
25
  const { uid, zoteroId } = data;
26
26
  const editor = useEditorContext();
27
+ const ref = React.useRef(null);
27
28
 
28
29
  const initialFormData = useSelector((state) => state?.content?.data || {});
29
30
  const blockProps = editor?.getBlockProps ? editor.getBlockProps() : null;
@@ -36,7 +37,6 @@ export const FootnoteElement = (props) => {
36
37
  const notesObjResult = isEmpty(metadata)
37
38
  ? makeFootnoteListOfUniqueItems(storeBlocks)
38
39
  : makeFootnoteListOfUniqueItems(blocks);
39
-
40
40
  // will cosider zotero citations and footnote
41
41
  // notesObjResult contains all zotero/footnote as unique, and contain refs for other zotero/footnote
42
42
  const indiceIfZoteroId = data.extra
@@ -58,12 +58,15 @@ export const FootnoteElement = (props) => {
58
58
  // parent footnote
59
59
  [data, ...(data.extra || [])]
60
60
  .map((footnoteObj, _index) => {
61
+ const indexInNotesObjResult = Object.keys(notesObjResult).indexOf(
62
+ Object.keys(notesObjResult).find(
63
+ (key) => notesObjResult[key].footnote === footnoteObj.footnote,
64
+ ),
65
+ );
61
66
  return `[${
62
- Object.keys(notesObjResult).indexOf(
63
- Object.keys(notesObjResult).find(
64
- (key) => notesObjResult[key].footnote === footnoteObj.footnote,
65
- ),
66
- ) + 1
67
+ indexInNotesObjResult === -1
68
+ ? Object.keys(notesObjResult).length + 1
69
+ : indexInNotesObjResult + 1
67
70
  }]`;
68
71
  })
69
72
  .join('');
@@ -83,15 +86,20 @@ export const FootnoteElement = (props) => {
83
86
  return (
84
87
  <>
85
88
  {mode === 'view' ? (
86
- <span id={`ref-${uid}`} aria-describedby="footnote-label">
89
+ <span id={`ref-${uid}`} aria-describedby="footnote-label" ref={ref}>
87
90
  <Popup
88
91
  position="bottom left"
92
+ pinned={true}
93
+ mountNode={ref.current}
94
+ on={['click', 'hover', 'focus']}
89
95
  trigger={
90
96
  <span
91
97
  id={`cite_ref-${uid}`}
92
98
  {...attributes}
93
99
  className="citation-item"
94
100
  data-footnote-indice={citationIndice}
101
+ tabIndex={0}
102
+ role={'presentation'}
95
103
  >
96
104
  {children}
97
105
  </span>
@@ -10,6 +10,37 @@ import { getAllBlocks } from '@plone/volto-slate/utils';
10
10
  export const makeFootnote = (footnote) => {
11
11
  return footnote ? footnote.replace('<?xml version="1.0"?>', '') : '';
12
12
  };
13
+ /**
14
+ * retrive all slate children of nested objects
15
+ * @param {object} path - the keys that we want to extract the slate children from
16
+ * @param {*} value - the source that we want to extract the slate children from
17
+ * Exemple of parameters
18
+ * path:{items:'value'}
19
+ * @returns string
20
+ */
21
+ const retriveValuesOfSlateFromNestedPath = (path, value) => {
22
+ if (Array.isArray(value)) {
23
+ let allSlateValue = [];
24
+ value.forEach((element) => {
25
+ allSlateValue = [
26
+ ...allSlateValue,
27
+ ...retriveValuesOfSlateFromNestedPath(path, element),
28
+ ];
29
+ });
30
+ return allSlateValue;
31
+ }
32
+ if (typeof path === 'string' && value) {
33
+ if (value[path]?.length > 0) return [...value[path]];
34
+ return [];
35
+ }
36
+ if (typeof path === 'object' && Object.keys(path).length > 0) {
37
+ return retriveValuesOfSlateFromNestedPath(
38
+ path[Object.keys(path)[0]],
39
+ value[Object.keys(path)[0]],
40
+ );
41
+ }
42
+ return [];
43
+ };
13
44
 
14
45
  /**
15
46
  * Will open accordion if contains footnote reference
@@ -46,6 +77,7 @@ const blockTypesOperations = {
46
77
  return [...accumulator, ...propertiesBlocks];
47
78
  }, []);
48
79
  },
80
+
49
81
  metadata: (block, properties) => {
50
82
  const fId = block?.data?.id;
51
83
  return block?.data?.widget === 'slate'
@@ -80,6 +112,7 @@ const blockTypesOperations = {
80
112
  */
81
113
  export const getAllBlocksAndSlateFields = (properties) => {
82
114
  const blocks = getAllBlocks(properties, []);
115
+
83
116
  return blocks.reduce((accumulator, currentblock) => {
84
117
  return [
85
118
  ...accumulator,
@@ -111,45 +144,48 @@ export const makeFootnoteListOfUniqueItems = (blocks) => {
111
144
  ] || ['value'];
112
145
 
113
146
  mapping.forEach((key) => {
114
- const value = element[key];
147
+ const value = retriveValuesOfSlateFromNestedPath(key, element);
148
+
115
149
  if (!value) return;
116
150
 
117
- value.forEach((item) => {
118
- // Node.elements(item) returns an iterable generator of nodes
119
- Array.from(Node.elements(item)).forEach(([node]) => {
120
- if (footnotes.includes(node.type) && node.data) {
121
- // for citations (Zotero items) create refs for same zoteroId
122
- if (node.data.zoteroId) {
123
- iterateZoteroObj(notesObjResult, node.data);
124
- // itereate the extra obj for multiple citations
125
- if (node.data.extra) {
126
- node.data.extra.forEach((zoteroObjItem) =>
127
- // send the uid of the parent
128
- // of the word the will have the reference indice
129
- iterateZoteroObj(
130
- notesObjResult,
131
- zoteroObjItem,
132
- node.data.uid,
133
- ),
134
- );
135
- }
136
- // for footnotes - create refs, on identical text
137
- } else {
138
- iterateFootnoteObj(notesObjResult, node.data);
139
- if (node.data.extra) {
140
- node.data.extra.forEach((footnoteObjItem) =>
141
- // since is called in case of extra, the parent is needed
142
- iterateFootnoteObj(
143
- notesObjResult,
144
- footnoteObjItem,
145
- node.data.uid,
146
- ),
147
- );
151
+ value
152
+ .filter((val) => val.children)
153
+ .forEach((item) => {
154
+ // Node.elements(item) returns an iterable generator of nodes
155
+ Array.from(Node.elements(item)).forEach(([node]) => {
156
+ if (footnotes.includes(node.type) && node.data) {
157
+ // for citations (Zotero items) create refs for same zoteroId
158
+ if (node.data.zoteroId) {
159
+ iterateZoteroObj(notesObjResult, node.data);
160
+ // itereate the extra obj for multiple citations
161
+ if (node.data.extra) {
162
+ node.data.extra.forEach((zoteroObjItem) =>
163
+ // send the uid of the parent
164
+ // of the word the will have the reference indice
165
+ iterateZoteroObj(
166
+ notesObjResult,
167
+ zoteroObjItem,
168
+ node.data.uid,
169
+ ),
170
+ );
171
+ }
172
+ // for footnotes - create refs, on identical text
173
+ } else {
174
+ iterateFootnoteObj(notesObjResult, node.data);
175
+ if (node.data.extra) {
176
+ node.data.extra.forEach((footnoteObjItem) =>
177
+ // since is called in case of extra, the parent is needed
178
+ iterateFootnoteObj(
179
+ notesObjResult,
180
+ footnoteObjItem,
181
+ node.data.uid,
182
+ ),
183
+ );
184
+ }
148
185
  }
149
186
  }
150
- }
187
+ });
151
188
  });
152
- });
153
189
  });
154
190
  });
155
191
 
@@ -204,6 +240,7 @@ const iterateFootnoteObj = (notesObjResultTemp, node, parentUid) => {
204
240
  return notesObjResultTemp[noteId].footnote === node.footnote;
205
241
  });
206
242
  // has not yet been added
243
+
207
244
  if (!found) {
208
245
  // will use the parentUid instead of own uid for render to be able to reference to the correct element
209
246
  //(word containing the footnotes)