Skip to content

Commit 51279dd

Browse files
authoredApr 18, 2024··
feat: Volto 17 support - refs #264527
1 parent 1d606ff commit 51279dd

16 files changed

+360
-147
lines changed
 

‎.env

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Jest configuration variables
2+
# - possible values: ON, OFF
3+
JEST_USE_SETUP=OFF

‎.eslintrc.js

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const projectRootPath = fs.realpathSync(__dirname + '/../../../');
4+
5+
let voltoPath = path.join(projectRootPath, 'node_modules/@plone/volto');
6+
let configFile;
7+
if (fs.existsSync(`${projectRootPath}/tsconfig.json`))
8+
configFile = `${projectRootPath}/tsconfig.json`;
9+
else if (fs.existsSync(`${projectRootPath}/jsconfig.json`))
10+
configFile = `${projectRootPath}/jsconfig.json`;
11+
12+
if (configFile) {
13+
const jsConfig = require(configFile).compilerOptions;
14+
const pathsConfig = jsConfig.paths;
15+
if (pathsConfig['@plone/volto'])
16+
voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
17+
}
18+
19+
const AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`);
20+
const reg = new AddonConfigurationRegistry(projectRootPath);
21+
22+
// Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
23+
const addonAliases = Object.keys(reg.packages).map((o) => [
24+
o,
25+
reg.packages[o].modulePath,
26+
]);
27+
28+
const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
29+
30+
const defaultConfig = {
31+
extends: `${voltoPath}/.eslintrc`,
32+
settings: {
33+
'import/resolver': {
34+
alias: {
35+
map: [
36+
['@plone/volto', '@plone/volto/src'],
37+
['@plone/volto-slate', '@plone/volto/packages/volto-slate/src'],
38+
...addonAliases,
39+
['@package', `${__dirname}/src`],
40+
['@root', `${__dirname}/src`],
41+
['~', `${__dirname}/src`],
42+
],
43+
extensions: ['.js', '.jsx', '.json'],
44+
},
45+
'babel-plugin-root-import': {
46+
rootPathSuffix: 'src',
47+
},
48+
},
49+
},
50+
rules: {
51+
'react/jsx-no-target-blank': [
52+
'error',
53+
{
54+
allowReferrer: true,
55+
},
56+
],
57+
}
58+
};
59+
60+
const config = addonExtenders.reduce(
61+
(acc, extender) => extender.modify(acc),
62+
defaultConfig,
63+
);
64+
65+
module.exports = config;

‎.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
.vscode/
22
.history
3-
.eslintrc.js
43
.nyc_output
54
project
65
coverage

‎.project.eslintrc.js

-48
This file was deleted.

‎Jenkinsfile

+143-45
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ pipeline {
1313
DEPENDENCIES = ""
1414
BACKEND_PROFILES = "eea.kitkat:testing"
1515
BACKEND_ADDONS = ""
16-
VOLTO = "16"
16+
VOLTO = "17"
17+
VOLTO16_BREAKING_CHANGES = "no"
1718
IMAGE_NAME = BUILD_TAG.toLowerCase()
1819
}
1920

@@ -44,6 +45,7 @@ pipeline {
4445
}
4546
steps {
4647
script {
48+
checkout scm
4749
withCredentials([string(credentialsId: 'eea-jenkins-token', variable: 'GITHUB_TOKEN')]) {
4850
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
4951

@@ -61,33 +63,34 @@ pipeline {
6163
allOf {
6264
not { environment name: 'CHANGE_ID', value: '' }
6365
environment name: 'CHANGE_TARGET', value: 'develop'
64-
environment name: 'SKIP_TESTS', value: ''
6566
}
6667
allOf {
6768
environment name: 'CHANGE_ID', value: ''
6869
anyOf {
6970
not { changelog '.*^Automated release [0-9\\.]+$' }
7071
branch 'master'
7172
}
72-
environment name: 'SKIP_TESTS', value: ''
7373
}
7474
}
7575
}
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'''
76+
parallel {
77+
78+
stage('Volto 17') {
79+
agent { node { label 'docker-1.13'} }
80+
stages {
81+
stage('Build test image') {
82+
steps {
83+
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'''
84+
}
8185
}
82-
}
83-
84-
stage('Fix code') {
85-
when {
86+
87+
stage('Fix code') {
88+
when {
8689
environment name: 'CHANGE_ID', value: ''
8790
not { branch 'master' }
88-
}
89-
steps {
90-
script {
91+
}
92+
steps {
93+
script {
9194
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)
9295
sh '''docker cp $IMAGE_NAME-fix:/app/src/addons/$GIT_NAME/src .'''
9396
sh '''docker rm -v $IMAGE_NAME-fix'''
@@ -105,31 +108,31 @@ pipeline {
105108
sh '''exit 1'''
106109
}
107110
}
111+
}
108112
}
109-
}
110113

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+
stage('ES lint') {
115+
when { environment name: 'SKIP_TESTS', value: '' }
116+
steps {
117+
sh '''docker run --rm --name="$IMAGE_NAME-eslint" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend lint'''
118+
}
114119
}
115-
}
116120

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'''
121+
stage('Style lint') {
122+
when { environment name: 'SKIP_TESTS', value: '' }
123+
steps {
124+
sh '''docker run --rm --name="$IMAGE_NAME-stylelint" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend stylelint'''
125+
}
120126
}
121-
}
122127

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'''
128+
stage('Prettier') {
129+
when { environment name: 'SKIP_TESTS', value: '' }
130+
steps {
131+
sh '''docker run --rm --name="$IMAGE_NAME-prettier" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend prettier'''
132+
}
126133
}
127-
}
128-
129-
stage('Coverage Tests') {
130-
parallel {
131-
132-
stage('Unit tests') {
134+
stage('Unit tests') {
135+
when { environment name: 'SKIP_TESTS', value: '' }
133136
steps {
134137
script {
135138
try {
@@ -155,17 +158,24 @@ pipeline {
155158
}
156159
}
157160
}
158-
}
161+
}
159162

160-
stage('Integration tests') {
163+
stage('Integration tests') {
164+
when { environment name: 'SKIP_TESTS', value: '' }
161165
steps {
162166
script {
163167
try {
164168
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'''
169+
sh '''docker run -d --shm-size=4g --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'''
170+
frontend = sh script:'''docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress make check-ci''', returnStatus: true
171+
if ( frontend != 0 ) {
172+
sh '''docker logs $IMAGE_NAME-cypress; exit 1'''
173+
}
174+
166175
sh '''timeout -s 9 1800 docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress make cypress-ci'''
167176
} finally {
168177
try {
178+
if ( frontend == 0 ) {
169179
sh '''rm -rf cypress-videos cypress-results cypress-coverage cypress-screenshots'''
170180
sh '''mkdir -p cypress-videos cypress-results cypress-coverage cypress-screenshots'''
171181
videos = sh script: '''docker cp $IMAGE_NAME-cypress:/app/src/addons/$GIT_NAME/cypress/videos cypress-videos/''', returnStatus: true
@@ -189,6 +199,7 @@ pipeline {
189199
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'''
190200
archiveArtifacts artifacts: 'cypress-videos/**/*.mp4', fingerprint: true, allowEmptyArchive: true
191201
}
202+
}
192203
} finally {
193204
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
194205
junit testResults: 'cypress-results/**/*.xml', allowEmptyResults: true
@@ -204,26 +215,19 @@ pipeline {
204215
}
205216
}
206217
}
207-
}
208218
}
209-
}
210-
}
211-
post {
212-
always {
213-
sh script: "docker rmi $IMAGE_NAME-frontend", returnStatus: true
214-
}
215-
}
216-
}
217219

218220
stage('Report to SonarQube') {
219221
when {
220222
anyOf {
221223
allOf {
222224
not { environment name: 'CHANGE_ID', value: '' }
223225
environment name: 'CHANGE_TARGET', value: 'develop'
226+
environment name: 'SKIP_TESTS', value: ''
224227
}
225228
allOf {
226229
environment name: 'CHANGE_ID', value: ''
230+
environment name: 'SKIP_TESTS', value: ''
227231
anyOf {
228232
allOf {
229233
branch 'develop'
@@ -248,14 +252,107 @@ pipeline {
248252
}
249253
}
250254

255+
256+
}
257+
}
258+
259+
stage('Volto 16') {
260+
agent { node { label 'integration'} }
261+
when {
262+
environment name: 'SKIP_TESTS', value: ''
263+
not { environment name: 'VOLTO16_BREAKING_CHANGES', value: 'yes' }
264+
}
265+
stages {
266+
stage('Build test image') {
267+
steps {
268+
sh '''docker build --pull --build-arg="VOLTO_VERSION=16" --build-arg="ADDON_NAME=$NAMESPACE/$GIT_NAME" --build-arg="ADDON_PATH=$GIT_NAME" . -t $IMAGE_NAME-frontend16'''
269+
}
270+
}
271+
272+
stage('Unit tests Volto 16') {
273+
steps {
274+
script {
275+
try {
276+
sh '''docker run --name="$IMAGE_NAME-volto16" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend16 test-ci'''
277+
sh '''rm -rf xunit-reports16'''
278+
sh '''mkdir -p xunit-reports16'''
279+
sh '''docker cp $IMAGE_NAME-volto16:/app/junit.xml xunit-reports16/'''
280+
} finally {
281+
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
282+
junit testResults: 'xunit-reports16/junit.xml', allowEmptyResults: true
283+
}
284+
sh script: '''docker rm -v $IMAGE_NAME-volto16''', returnStatus: true
285+
}
286+
}
287+
}
288+
}
289+
290+
stage('Integration tests Volto 16') {
291+
steps {
292+
script {
293+
try {
294+
sh '''docker run --pull always --rm -d --name="$IMAGE_NAME-plone16" -e SITE="Plone" -e PROFILES="$BACKEND_PROFILES" -e ADDONS="$BACKEND_ADDONS" eeacms/plone-backend'''
295+
sh '''docker run -d --shm-size=4g --link $IMAGE_NAME-plone16:plone --name="$IMAGE_NAME-cypress16" -e "RAZZLE_INTERNAL_API_PATH=http://plone:8080/Plone" --entrypoint=make --workdir=/app/src/addons/$GIT_NAME $IMAGE_NAME-frontend16 start-ci'''
296+
frontend = sh script:'''docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress16 make check-ci''', returnStatus: true
297+
if ( frontend != 0 ) {
298+
sh '''docker logs $IMAGE_NAME-cypress16; exit 1'''
299+
}
300+
sh '''timeout -s 9 1800 docker exec --workdir=/app/src/addons/${GIT_NAME} $IMAGE_NAME-cypress16 make cypress-ci'''
301+
} finally {
302+
try {
303+
if ( frontend == 0 ) {
304+
sh '''rm -rf cypress-videos16 cypress-results16 cypress-coverage16 cypress-screenshots16'''
305+
sh '''mkdir -p cypress-videos16 cypress-results16 cypress-coverage16 cypress-screenshots16'''
306+
videos = sh script: '''docker cp $IMAGE_NAME-cypress16:/app/src/addons/$GIT_NAME/cypress/videos cypress-videos16/''', returnStatus: true
307+
sh '''docker cp $IMAGE_NAME-cypress16:/app/src/addons/$GIT_NAME/cypress/reports cypress-results16/'''
308+
screenshots = sh script: '''docker cp $IMAGE_NAME-cypress16:/app/src/addons/$GIT_NAME/cypress/screenshots cypress-screenshots16''', returnStatus: true
309+
310+
archiveArtifacts artifacts: 'cypress-screenshots16/**', fingerprint: true, allowEmptyArchive: true
311+
312+
if ( videos == 0 ) {
313+
sh '''for file in $(find cypress-results16 -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-videos16/videos/$testname.mp4; fi; done'''
314+
archiveArtifacts artifacts: 'cypress-videos16/**/*.mp4', fingerprint: true, allowEmptyArchive: true
315+
}
316+
}
317+
} finally {
318+
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
319+
junit testResults: 'cypress-results16/**/*.xml', allowEmptyResults: true
320+
}
321+
catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') {
322+
sh '''docker logs $IMAGE_NAME-cypress16'''
323+
}
324+
sh script: "docker stop $IMAGE_NAME-cypress16", returnStatus: true
325+
sh script: "docker stop $IMAGE_NAME-plone16", returnStatus: true
326+
sh script: "docker rm -v $IMAGE_NAME-plone16", returnStatus: true
327+
sh script: "docker rm -v $IMAGE_NAME-cypress16", returnStatus: true
328+
}
329+
}
330+
}
331+
}
332+
}
333+
334+
}
335+
}
336+
}
337+
post {
338+
always {
339+
sh script: "docker rmi $IMAGE_NAME-frontend", returnStatus: true
340+
sh script: "docker rmi $IMAGE_NAME-frontend16", returnStatus: true
341+
}
342+
}
343+
}
344+
345+
251346
stage('SonarQube compare to master') {
252347
when {
253348
anyOf {
254349
allOf {
255350
not { environment name: 'CHANGE_ID', value: '' }
256351
environment name: 'CHANGE_TARGET', value: 'develop'
352+
environment name: 'SKIP_TESTS', value: ''
257353
}
258354
allOf {
355+
environment name: 'SKIP_TESTS', value: ''
259356
environment name: 'CHANGE_ID', value: ''
260357
branch 'develop'
261358
not { changelog '.*^Automated release [0-9\\.]+$' }
@@ -316,3 +413,4 @@ pipeline {
316413
}
317414
}
318415
}
416+

‎Makefile

+11-8
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ endif
4646
DIR=$(shell basename $$(pwd))
4747
NODE_MODULES?="../../../node_modules"
4848
PLONE_VERSION?=6
49-
VOLTO_VERSION?=16
49+
VOLTO_VERSION?=17
5050
ADDON_PATH="${DIR}"
5151
ADDON_NAME="@eeacms/${ADDON_PATH}"
5252
DOCKER_COMPOSE=PLONE_VERSION=${PLONE_VERSION} VOLTO_VERSION=${VOLTO_VERSION} ADDON_NAME=${ADDON_NAME} ADDON_PATH=${ADDON_PATH} docker compose
@@ -86,7 +86,7 @@ cypress-open: ## Open cypress integration tests
8686

8787
.PHONY: cypress-run
8888
cypress-run: ## Run cypress integration tests
89-
CYPRESS_API_PATH="${RAZZLE_DEV_PROXY_API_PATH}" NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress run --browser chromium
89+
CYPRESS_API_PATH="${RAZZLE_DEV_PROXY_API_PATH}" NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress run
9090

9191
.PHONY: test
9292
test: ## Run jest tests
@@ -98,15 +98,15 @@ test-update: ## Update jest tests snapshots
9898

9999
.PHONY: stylelint
100100
stylelint: ## Stylelint
101-
$(NODE_MODULES)/stylelint/bin/stylelint.js --allow-empty-input 'src/**/*.{css,less}'
101+
$(NODE_MODULES)/.bin/stylelint --allow-empty-input 'src/**/*.{css,less}'
102102

103103
.PHONY: stylelint-overrides
104104
stylelint-overrides:
105105
$(NODE_MODULES)/.bin/stylelint --custom-syntax less --allow-empty-input 'theme/**/*.overrides' 'src/**/*.overrides'
106106

107107
.PHONY: stylelint-fix
108108
stylelint-fix: ## Fix stylelint
109-
$(NODE_MODULES)/stylelint/bin/stylelint.js --allow-empty-input 'src/**/*.{css,less}' --fix
109+
$(NODE_MODULES)/.bin/stylelint --allow-empty-input 'src/**/*.{css,less}' --fix
110110
$(NODE_MODULES)/.bin/stylelint --custom-syntax less --allow-empty-input 'theme/**/*.overrides' 'src/**/*.overrides' --fix
111111

112112
.PHONY: prettier
@@ -119,11 +119,11 @@ prettier-fix: ## Fix prettier
119119

120120
.PHONY: lint
121121
lint: ## ES Lint
122-
$(NODE_MODULES)/eslint/bin/eslint.js --max-warnings=0 'src/**/*.{js,jsx}'
122+
$(NODE_MODULES)/.bin/eslint --max-warnings=0 'src/**/*.{js,jsx}'
123123

124124
.PHONY: lint-fix
125125
lint-fix: ## Fix ES Lint
126-
$(NODE_MODULES)/eslint/bin/eslint.js --fix 'src/**/*.{js,jsx}'
126+
$(NODE_MODULES)/.bin/eslint --fix 'src/**/*.{js,jsx}'
127127

128128
.PHONY: i18n
129129
i18n: ## i18n
@@ -155,8 +155,11 @@ start-ci:
155155
cd ../..
156156
yarn start
157157

158+
.PHONY: check-ci
159+
check-ci:
160+
$(NODE_MODULES)/.bin/wait-on -t 240000 http://localhost:3000
161+
158162
.PHONY: cypress-ci
159163
cypress-ci:
160164
$(NODE_MODULES)/.bin/wait-on -t 240000 http://localhost:3000
161-
NODE_ENV=development make cypress-run
162-
165+
CYPRESS_API_PATH="${RAZZLE_DEV_PROXY_API_PATH}" NODE_ENV=development $(NODE_MODULES)/cypress/bin/cypress run --browser chromium

‎cypress/e2e/02-dexterity-controlpanel-layout.cy.js

+30-18
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ describe('ControlPanel: Dexterity Content-Types Layout', () => {
66

77
it('Edit Blocks Layout for Book', () => {
88
cy.visit('/controlpanel/dexterity-types');
9-
cy.waitForResourceToLoad('@navigation');
10-
cy.waitForResourceToLoad('@breadcrumbs');
11-
cy.waitForResourceToLoad('@actions');
12-
cy.waitForResourceToLoad('@types');
139

1410
cy.get('a[href="/controlpanel/dexterity-types/book"]').should(
1511
'have.text',
@@ -40,10 +36,6 @@ describe('ControlPanel: Dexterity Content-Types Layout', () => {
4036
cy.get('#toolbar-save').click();
4137

4238
cy.visit('/cypress');
43-
cy.waitForResourceToLoad('@navigation');
44-
cy.waitForResourceToLoad('@breadcrumbs');
45-
cy.waitForResourceToLoad('@actions');
46-
cy.waitForResourceToLoad('@types');
4739

4840
cy.get('button[class="add"]').click();
4941
cy.get('#toolbar-add-book').click();
@@ -58,21 +50,39 @@ describe('ControlPanel: Dexterity Content-Types Layout', () => {
5850
cy.get('.add-item-button-wrapper .button').click();
5951

6052
// Add first item
61-
cy.get('.field-wrapper-value-0-items-0 .slate-editor div[role="textbox"]').click().type('5{enter}');
62-
cy.get('.field-wrapper-label-1-items-0 .slate-editor div[role="textbox"]').click().type('First Label{enter}');
63-
cy.get('.field-wrapper-info-2-items-0 .slate-editor div[role="textbox"]').click().type('First Extra Info{enter}');
53+
cy.get('.field-wrapper-value-0-items-0 .slate-editor div[role="textbox"]')
54+
.click()
55+
.type('5{enter}');
56+
cy.get('.field-wrapper-label-1-items-0 .slate-editor div[role="textbox"]')
57+
.click()
58+
.type('First Label{enter}');
59+
cy.get('.field-wrapper-info-2-items-0 .slate-editor div[role="textbox"]')
60+
.click()
61+
.type('First Extra Info{enter}');
6462

6563
// Add second item
6664
cy.get('.add-item-button-wrapper .button').click();
67-
cy.get('.field-wrapper-value-0-items-1 .slate-editor div[role="textbox"]').click().type('10{enter}');
68-
cy.get('.field-wrapper-label-1-items-1 .slate-editor div[role="textbox"]').click().type('Second Label{enter}');
69-
cy.get('.field-wrapper-info-2-items-1 .slate-editor div[role="textbox"]').click().type('Second Extra Info{enter}');
65+
cy.get('.field-wrapper-value-0-items-1 .slate-editor div[role="textbox"]')
66+
.click()
67+
.type('10{enter}');
68+
cy.get('.field-wrapper-label-1-items-1 .slate-editor div[role="textbox"]')
69+
.click()
70+
.type('Second Label{enter}');
71+
cy.get('.field-wrapper-info-2-items-1 .slate-editor div[role="textbox"]')
72+
.click()
73+
.type('Second Extra Info{enter}');
7074

7175
// Add third item
7276
cy.get('.add-item-button-wrapper .button').click();
73-
cy.get('.field-wrapper-value-0-items-2 .slate-editor div[role="textbox"]').click().type('15{enter}');
74-
cy.get('.field-wrapper-label-1-items-2 .slate-editor div[role="textbox"]').click().type('Third Label{enter}');
75-
cy.get('.field-wrapper-info-2-items-2 .slate-editor div[role="textbox"]').click().type('Third Extra Info{enter}');
77+
cy.get('.field-wrapper-value-0-items-2 .slate-editor div[role="textbox"]')
78+
.click()
79+
.type('15{enter}');
80+
cy.get('.field-wrapper-label-1-items-2 .slate-editor div[role="textbox"]')
81+
.click()
82+
.type('Third Label{enter}');
83+
cy.get('.field-wrapper-info-2-items-2 .slate-editor div[role="textbox"]')
84+
.click()
85+
.type('Third Extra Info{enter}');
7686

7787
// Three columns
7888
cy.get('#field-widths').click().type('Three{enter}');
@@ -98,7 +108,9 @@ describe('ControlPanel: Dexterity Content-Types Layout', () => {
98108

99109
// Check second item
100110
cy.get('.statistic:nth-child(2) .label').contains('Second Label');
101-
cy.get('.statistic:nth-child(2) .text-center').contains('Second Extra Info');
111+
cy.get('.statistic:nth-child(2) .text-center').contains(
112+
'Second Extra Info',
113+
);
102114

103115
// Check third item
104116
cy.get('.statistic:nth-child(3) .label').contains('Third Label');

‎cypress/support/commands.js

+11-15
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ Cypress.Commands.add(
124124
})
125125
.then(() => console.log(`${contentType} created`));
126126
}
127-
}
127+
},
128128
);
129129

130130
// --- Add DX Content-Type ----------------------------------------------------------
@@ -217,7 +217,7 @@ Cypress.Commands.add('removeSlateJSONField', (type, name) => {
217217
body: {},
218218
})
219219
.then(() =>
220-
console.log(`${name} SlateJSONField field removed from ${type}`)
220+
console.log(`${name} SlateJSONField field removed from ${type}`),
221221
);
222222
});
223223

@@ -251,7 +251,7 @@ Cypress.Commands.add('typeInSlate', { prevSubject: true }, (subject, text) => {
251251
new InputEvent('beforeinput', {
252252
inputType: 'insertText',
253253
data: text,
254-
})
254+
}),
255255
);
256256
return subject;
257257
})
@@ -267,7 +267,7 @@ Cypress.Commands.add('lineBreakInSlate', { prevSubject: true }, (subject) => {
267267
.wrap(subject)
268268
.then((subject) => {
269269
subject[0].dispatchEvent(
270-
new InputEvent('beforeinput', { inputType: 'insertLineBreak' })
270+
new InputEvent('beforeinput', { inputType: 'insertLineBreak' }),
271271
);
272272
return subject;
273273
})
@@ -315,7 +315,7 @@ Cypress.Commands.add(
315315
include_children: include_children,
316316
},
317317
});
318-
}
318+
},
319319
);
320320

321321
// --- waitForResourceToLoad ----------------------------------------------------------
@@ -375,7 +375,7 @@ Cypress.Commands.add(
375375
setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
376376
}
377377
});
378-
}
378+
},
379379
);
380380

381381
Cypress.Commands.add('getSlate', ({ createNewSlate = true } = {}) => {
@@ -390,7 +390,7 @@ Cypress.Commands.add('getSlate', ({ createNewSlate = true } = {}) => {
390390
cy.get('.block.inner').last().type('{moveToEnd}{enter}');
391391
}
392392
slate = cy.get(SLATE_SELECTOR, { timeout: 10000 }).last();
393-
}
393+
},
394394
);
395395
return slate;
396396
});
@@ -447,10 +447,6 @@ Cypress.Commands.add('toolbarSave', () => {
447447

448448
// Save
449449
cy.get('#toolbar-save').click();
450-
cy.waitForResourceToLoad('@navigation');
451-
cy.waitForResourceToLoad('@breadcrumbs');
452-
cy.waitForResourceToLoad('@actions');
453-
cy.waitForResourceToLoad('@types');
454450
cy.waitForResourceToLoad('my-page');
455451
cy.url().should('eq', Cypress.config().baseUrl + '/cypress/my-page');
456452
});
@@ -470,23 +466,23 @@ Cypress.Commands.add(
470466
});
471467
// Depending on what you're testing, you may need to chain a `.click()` here to ensure
472468
// further commands are picked up by whatever you're testing (this was required for Slate, for example).
473-
}
469+
},
474470
);
475471

476472
Cypress.Commands.add(
477473
'setCursorBefore',
478474
{ prevSubject: true },
479475
(subject, query) => {
480476
cy.wrap(subject).setCursor(query, true);
481-
}
477+
},
482478
);
483479

484480
Cypress.Commands.add(
485481
'setCursorAfter',
486482
{ prevSubject: true },
487483
(subject, query) => {
488484
cy.wrap(subject).setCursor(query);
489-
}
485+
},
490486
);
491487

492488
// Helper functions
@@ -532,5 +528,5 @@ Cypress.Commands.add(
532528
failAction();
533529
}
534530
});
535-
}
531+
},
536532
);

‎cypress/support/e2e.js

-4
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ export const slateBeforeEach = (contentType = 'Document') => {
3535
path: 'cypress',
3636
});
3737
cy.visit('/cypress/my-page');
38-
cy.waitForResourceToLoad('@navigation');
39-
cy.waitForResourceToLoad('@breadcrumbs');
40-
cy.waitForResourceToLoad('@actions');
41-
cy.waitForResourceToLoad('@types');
4238
cy.waitForResourceToLoad('my-page');
4339
cy.navigate('/cypress/my-page/edit');
4440
};

‎jest-addon.config.js

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require('dotenv').config({ path: __dirname + '/.env' })
2+
13
module.exports = {
24
testMatch: ['**/src/addons/**/?(*.)+(spec|test).[jt]s?(x)'],
35
collectCoverageFrom: [
@@ -9,18 +11,26 @@ module.exports = {
911
'@plone/volto/cypress': '<rootDir>/node_modules/@plone/volto/cypress',
1012
'@plone/volto/babel': '<rootDir>/node_modules/@plone/volto/babel',
1113
'@plone/volto/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
12-
'@package/(.*)$': '<rootDir>/src/$1',
13-
'@root/(.*)$': '<rootDir>/src/$1',
14+
'@package/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
15+
'@root/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
1416
'@plone/volto-quanta/(.*)$': '<rootDir>/src/addons/volto-quanta/src/$1',
17+
'@eeacms/search/(.*)$': '<rootDir>/src/addons/volto-searchlib/searchlib/$1',
18+
'@eeacms/search': '<rootDir>/src/addons/volto-searchlib/searchlib',
1519
'@eeacms/(.*?)/(.*)$': '<rootDir>/node_modules/@eeacms/$1/src/$2',
16-
'@plone/volto-slate':
20+
'@plone/volto-slate$':
1721
'<rootDir>/node_modules/@plone/volto/packages/volto-slate/src',
22+
'@plone/volto-slate/(.*)$':
23+
'<rootDir>/node_modules/@plone/volto/packages/volto-slate/src/$1',
1824
'~/(.*)$': '<rootDir>/src/$1',
1925
'load-volto-addons':
2026
'<rootDir>/node_modules/@plone/volto/jest-addons-loader.js',
2127
},
28+
transformIgnorePatterns: [
29+
'/node_modules/(?!(@plone|@root|@package|@eeacms)/).*/',
30+
],
2231
transform: {
2332
'^.+\\.js(x)?$': 'babel-jest',
33+
'^.+\\.ts(x)?$': 'babel-jest',
2434
'^.+\\.(png)$': 'jest-file',
2535
'^.+\\.(jpg)$': 'jest-file',
2636
'^.+\\.(svg)$': './node_modules/@plone/volto/jest-svgsystem-transform.js',
@@ -33,4 +43,9 @@ module.exports = {
3343
statements: 5,
3444
},
3545
},
36-
};
46+
...(process.env.JEST_USE_SETUP === 'ON' && {
47+
setupFilesAfterEnv: [
48+
'<rootDir>/node_modules/@eeacms/volto-statistic-block/jest.setup.js',
49+
],
50+
}),
51+
}

‎jest.setup.js

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { jest } from '@jest/globals';
2+
import configureStore from 'redux-mock-store';
3+
import thunk from 'redux-thunk';
4+
import { blocksConfig } from '@plone/volto/config/Blocks';
5+
import installSlate from '@plone/volto-slate/index';
6+
7+
var mockSemanticComponents = jest.requireActual('semantic-ui-react');
8+
var mockComponents = jest.requireActual('@plone/volto/components');
9+
var config = jest.requireActual('@plone/volto/registry').default;
10+
11+
config.blocks.blocksConfig = {
12+
...blocksConfig,
13+
...config.blocks.blocksConfig,
14+
};
15+
16+
jest.doMock('semantic-ui-react', () => ({
17+
__esModule: true,
18+
...mockSemanticComponents,
19+
Popup: ({ content, trigger }) => {
20+
return (
21+
<div className="popup">
22+
<div className="trigger">{trigger}</div>
23+
<div className="content">{content}</div>
24+
</div>
25+
);
26+
},
27+
}));
28+
29+
jest.doMock('@plone/volto/components', () => {
30+
return {
31+
__esModule: true,
32+
...mockComponents,
33+
SidebarPortal: ({ children }) => <div id="sidebar">{children}</div>,
34+
};
35+
});
36+
37+
jest.doMock('@plone/volto/registry', () =>
38+
[installSlate].reduce((acc, apply) => apply(acc), config),
39+
);
40+
41+
const mockStore = configureStore([thunk]);
42+
43+
global.fetch = jest.fn(() =>
44+
Promise.resolve({
45+
json: () => Promise.resolve({}),
46+
}),
47+
);
48+
49+
global.store = mockStore({
50+
intl: {
51+
locale: 'en',
52+
messages: {},
53+
formatMessage: jest.fn(),
54+
},
55+
content: {
56+
create: {},
57+
subrequests: [],
58+
},
59+
connected_data_parameters: {},
60+
screen: {
61+
page: {
62+
width: 768,
63+
},
64+
},
65+
});

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@cypress/code-coverage": "^3.10.0",
2525
"@plone/scripts": "*",
2626
"babel-plugin-transform-class-properties": "^6.24.1",
27+
"dotenv": "^16.3.2",
2728
"husky": "^8.0.3",
2829
"lint-staged": "^14.0.1",
2930
"md5": "^2.3.0"

‎src/StatisticBlock/animationSchema.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const messages = defineMessages({
2727
},
2828
});
2929

30-
export default (intl) => {
30+
const schema = (intl) => {
3131
return {
3232
fieldsets: [
3333
{
@@ -63,3 +63,5 @@ export default (intl) => {
6363
required: [],
6464
};
6565
};
66+
67+
export default schema;

‎src/StatisticBlock/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import StatisticBlockEdit from './Edit';
33
import StatisticBlockView from './View';
44
import editSchema from './schema';
55

6-
export default (config) => {
6+
const applyConfig = (config) => {
77
config.blocks.blocksConfig.statistic_block = {
88
id: 'statistic_block',
99
title: 'Statistic',
@@ -30,3 +30,5 @@ export default (config) => {
3030
};
3131
return config;
3232
};
33+
34+
export default applyConfig;

‎src/StatisticBlock/schema.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ const statisticSchema = (intl) => ({
143143
required: [],
144144
});
145145

146-
export default (intl) => ({
146+
const schema = (intl) => ({
147147
title: intl.formatMessage(messages.StatisticBlock),
148148

149149
fieldsets: [
@@ -215,3 +215,5 @@ export default (intl) => ({
215215

216216
required: [],
217217
});
218+
219+
export default schema;

‎src/StatisticBlock/stylesSchema.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const messages = defineMessages({
4343
},
4444
});
4545

46-
export default (intl) => {
46+
const schema = (intl) => {
4747
return {
4848
fieldsets: [
4949
{
@@ -112,3 +112,5 @@ export default (intl) => {
112112
required: [],
113113
};
114114
};
115+
116+
export default schema;

0 commit comments

Comments
 (0)
Please sign in to comment.