@plone/volto 18.32.4 → 18.33.0

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.
Files changed (163) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/locales/af/LC_MESSAGES/volto.po +10 -0
  3. package/locales/af.json +1 -1
  4. package/locales/ar/LC_MESSAGES/volto.po +10 -0
  5. package/locales/ar.json +1 -1
  6. package/locales/bg/LC_MESSAGES/volto.po +10 -0
  7. package/locales/bg.json +1 -1
  8. package/locales/bn/LC_MESSAGES/volto.po +10 -0
  9. package/locales/bn.json +1 -1
  10. package/locales/ca/LC_MESSAGES/volto.po +10 -0
  11. package/locales/ca.json +1 -1
  12. package/locales/cs/LC_MESSAGES/volto.po +10 -0
  13. package/locales/cs.json +1 -1
  14. package/locales/cy/LC_MESSAGES/volto.po +10 -0
  15. package/locales/cy.json +1 -1
  16. package/locales/da/LC_MESSAGES/volto.po +10 -0
  17. package/locales/da.json +1 -1
  18. package/locales/de/LC_MESSAGES/volto.po +10 -0
  19. package/locales/de.json +1 -1
  20. package/locales/el/LC_MESSAGES/volto.po +10 -0
  21. package/locales/el.json +1 -1
  22. package/locales/en/LC_MESSAGES/volto.po +10 -0
  23. package/locales/en.json +1 -1
  24. package/locales/en_AU/LC_MESSAGES/volto.po +10 -0
  25. package/locales/en_AU.json +1 -1
  26. package/locales/en_GB/LC_MESSAGES/volto.po +10 -0
  27. package/locales/en_GB.json +1 -1
  28. package/locales/eo/LC_MESSAGES/volto.po +10 -0
  29. package/locales/eo.json +1 -1
  30. package/locales/es/LC_MESSAGES/volto.po +23 -13
  31. package/locales/es.json +1 -1
  32. package/locales/et/LC_MESSAGES/volto.po +10 -0
  33. package/locales/et.json +1 -1
  34. package/locales/eu/LC_MESSAGES/volto.po +22 -12
  35. package/locales/eu.json +1 -1
  36. package/locales/fa/LC_MESSAGES/volto.po +10 -0
  37. package/locales/fa.json +1 -1
  38. package/locales/fi/LC_MESSAGES/volto.po +10 -0
  39. package/locales/fi.json +1 -1
  40. package/locales/fr/LC_MESSAGES/volto.po +10 -0
  41. package/locales/fr.json +1 -1
  42. package/locales/fu/LC_MESSAGES/volto.po +10 -0
  43. package/locales/fu.json +1 -1
  44. package/locales/gl/LC_MESSAGES/volto.po +1013 -1002
  45. package/locales/gl.json +1 -1
  46. package/locales/he/LC_MESSAGES/volto.po +10 -0
  47. package/locales/he.json +1 -1
  48. package/locales/hi/LC_MESSAGES/volto.po +10 -0
  49. package/locales/hi.json +1 -1
  50. package/locales/hr/LC_MESSAGES/volto.po +10 -0
  51. package/locales/hr.json +1 -1
  52. package/locales/hu/LC_MESSAGES/volto.po +10 -0
  53. package/locales/hu.json +1 -1
  54. package/locales/hy/LC_MESSAGES/volto.po +10 -0
  55. package/locales/hy.json +1 -1
  56. package/locales/id/LC_MESSAGES/volto.po +10 -0
  57. package/locales/id.json +1 -1
  58. package/locales/it/LC_MESSAGES/volto.po +13 -3
  59. package/locales/it.json +1 -1
  60. package/locales/ja/LC_MESSAGES/volto.po +10 -0
  61. package/locales/ja.json +1 -1
  62. package/locales/ka/LC_MESSAGES/volto.po +10 -0
  63. package/locales/ka.json +1 -1
  64. package/locales/kn/LC_MESSAGES/volto.po +10 -0
  65. package/locales/kn.json +1 -1
  66. package/locales/ko/LC_MESSAGES/volto.po +10 -0
  67. package/locales/ko.json +1 -1
  68. package/locales/lt/LC_MESSAGES/volto.po +10 -0
  69. package/locales/lt.json +1 -1
  70. package/locales/lv/LC_MESSAGES/volto.po +10 -0
  71. package/locales/lv.json +1 -1
  72. package/locales/mi/LC_MESSAGES/volto.po +10 -0
  73. package/locales/mi.json +1 -1
  74. package/locales/mk/LC_MESSAGES/volto.po +10 -0
  75. package/locales/mk.json +1 -1
  76. package/locales/my/LC_MESSAGES/volto.po +10 -0
  77. package/locales/my.json +1 -1
  78. package/locales/nb_NO/LC_MESSAGES/volto.po +10 -0
  79. package/locales/nb_NO.json +1 -1
  80. package/locales/nl/LC_MESSAGES/volto.po +25 -15
  81. package/locales/nl.json +1 -1
  82. package/locales/nn/LC_MESSAGES/volto.po +10 -0
  83. package/locales/nn.json +1 -1
  84. package/locales/pl/LC_MESSAGES/volto.po +10 -0
  85. package/locales/pl.json +1 -1
  86. package/locales/pt/LC_MESSAGES/volto.po +10 -0
  87. package/locales/pt.json +1 -1
  88. package/locales/pt_BR/LC_MESSAGES/volto.po +10 -0
  89. package/locales/pt_BR.json +1 -1
  90. package/locales/rm/LC_MESSAGES/volto.po +10 -0
  91. package/locales/rm.json +1 -1
  92. package/locales/ro/LC_MESSAGES/volto.po +10 -0
  93. package/locales/ro.json +1 -1
  94. package/locales/ru/LC_MESSAGES/volto.po +10 -0
  95. package/locales/ru.json +1 -1
  96. package/locales/sk/LC_MESSAGES/volto.po +10 -0
  97. package/locales/sk.json +1 -1
  98. package/locales/sl/LC_MESSAGES/volto.po +10 -0
  99. package/locales/sl.json +1 -1
  100. package/locales/sm/LC_MESSAGES/volto.po +10 -0
  101. package/locales/sm.json +1 -1
  102. package/locales/sq/LC_MESSAGES/volto.po +10 -0
  103. package/locales/sq.json +1 -1
  104. package/locales/sr/LC_MESSAGES/volto.po +10 -0
  105. package/locales/sr.json +1 -1
  106. package/locales/sr@cyrl/LC_MESSAGES/volto.po +10 -0
  107. package/locales/sr@cyrl.json +1 -1
  108. package/locales/sr@latn/LC_MESSAGES/volto.po +10 -0
  109. package/locales/sr@latn.json +1 -1
  110. package/locales/sv/LC_MESSAGES/volto.po +10 -0
  111. package/locales/sv.json +1 -1
  112. package/locales/ta/LC_MESSAGES/volto.po +10 -0
  113. package/locales/ta.json +1 -1
  114. package/locales/te/LC_MESSAGES/volto.po +10 -0
  115. package/locales/te.json +1 -1
  116. package/locales/th/LC_MESSAGES/volto.po +10 -0
  117. package/locales/th.json +1 -1
  118. package/locales/to/LC_MESSAGES/volto.po +10 -0
  119. package/locales/to.json +1 -1
  120. package/locales/tr/LC_MESSAGES/volto.po +35 -25
  121. package/locales/tr.json +1 -1
  122. package/locales/uk/LC_MESSAGES/volto.po +10 -0
  123. package/locales/uk.json +1 -1
  124. package/locales/vi/LC_MESSAGES/volto.po +10 -0
  125. package/locales/vi.json +1 -1
  126. package/locales/volto.pot +11 -1
  127. package/locales/zh_CN/LC_MESSAGES/volto.po +10 -0
  128. package/locales/zh_CN.json +1 -1
  129. package/locales/zh_Hant/LC_MESSAGES/volto.po +10 -0
  130. package/locales/zh_Hant.json +1 -1
  131. package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +10 -0
  132. package/locales/zh_Hant_HK.json +1 -1
  133. package/package.json +12 -25
  134. package/razzle.config.js +20 -5
  135. package/src/components/manage/Add/Add.jsx +9 -6
  136. package/src/components/manage/Blocks/Title/Edit.jsx +5 -0
  137. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +4 -5
  138. package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +57 -11
  139. package/src/components/manage/Multilingual/CompareLanguages.jsx +10 -10
  140. package/src/components/manage/Multilingual/CreateTranslation.jsx +8 -5
  141. package/src/components/manage/Multilingual/ManageTranslations.jsx +9 -7
  142. package/src/components/manage/Multilingual/TranslationObject.jsx +11 -8
  143. package/src/components/manage/Preferences/PersonalPreferences.jsx +8 -5
  144. package/src/components/manage/Toolbar/Types.crash.test.jsx +46 -0
  145. package/src/components/manage/Toolbar/Types.jsx +9 -7
  146. package/src/components/manage/Widgets/FileWidget.jsx +7 -0
  147. package/src/components/theme/LanguageSelector/LanguageSelector.tsx +7 -5
  148. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +12 -7
  149. package/src/express-middleware/devproxy.js +3 -1
  150. package/src/express-middleware/files.js +1 -0
  151. package/src/express-middleware/files.test.js +59 -0
  152. package/src/express-middleware/images.js +1 -0
  153. package/src/express-middleware/images.test.js +50 -0
  154. package/src/helpers/Blocks/Blocks.js +6 -6
  155. package/src/helpers/Utils/Utils.jsx +17 -0
  156. package/src/helpers/Utils/Utils.test.jsx +39 -0
  157. package/src/middleware/api.js +7 -3
  158. package/src/server.jsx +14 -12
  159. package/test-setup-globals-vitest.js +25 -0
  160. package/theme/themes/pastanaga/extras/widgets.less +17 -0
  161. package/types/components/manage/Toolbar/Types.crash.test.d.ts +1 -0
  162. package/types/helpers/Utils/Utils.d.ts +1 -0
  163. package/vitest.config.mjs +86 -40
@@ -78,11 +78,13 @@ export default function devProxyMiddleware() {
78
78
  },
79
79
  pathRewrite: (path, req) => {
80
80
  const { apiPathURL, instancePath } = getEnv();
81
+ const port =
82
+ apiPathURL.port || (apiPathURL.protocol === 'https:' ? 443 : 80);
81
83
  const target =
82
84
  config.settings.proxyRewriteTarget ||
83
85
  `/VirtualHostBase/${apiPathURL.protocol.slice(0, -1)}/${
84
86
  apiPathURL.hostname
85
- }:${apiPathURL.port}${instancePath}/++api++/VirtualHostRoot`;
87
+ }:${port}${instancePath}/++api++/VirtualHostRoot`;
86
88
 
87
89
  return `${target}${path.replace('/++api++', '')}`;
88
90
  },
@@ -3,6 +3,7 @@ import { getAPIResourceWithAuth } from '@plone/volto/helpers/Api/APIResourceWith
3
3
 
4
4
  const HEADERS = [
5
5
  'accept-ranges',
6
+ 'cache-status',
6
7
  'cache-control',
7
8
  'content-disposition',
8
9
  'content-range',
@@ -0,0 +1,59 @@
1
+ import filesMiddleware from './files';
2
+ import { getAPIResourceWithAuth } from '@plone/volto/helpers/Api/APIResourceWithAuth';
3
+
4
+ vi.mock('@plone/volto/helpers/Api/APIResourceWithAuth', () => ({
5
+ getAPIResourceWithAuth: vi.fn(),
6
+ }));
7
+
8
+ describe('files middleware', () => {
9
+ it('handles requests containing @@download and forwards headers/body', async () => {
10
+ getAPIResourceWithAuth.mockResolvedValue({
11
+ headers: {
12
+ 'content-type': 'text/plain',
13
+ 'cache-status': 'Souin; hit; ttl=664',
14
+ },
15
+ body: 'OK',
16
+ get(name) {
17
+ return this.headers[name];
18
+ },
19
+ statusCode: 200,
20
+ });
21
+
22
+ const mw = filesMiddleware();
23
+ const layer = mw.stack.find(
24
+ (l) => l.regexp && l.regexp.source.includes('@@download'),
25
+ );
26
+ expect(layer).toBeTruthy();
27
+
28
+ const req = {
29
+ path: '/some/@@download',
30
+ method: 'GET',
31
+ app: { locals: {} },
32
+ };
33
+ const res = {
34
+ headers: {},
35
+ set(name, val) {
36
+ this.headers[name] = val;
37
+ },
38
+ status(code) {
39
+ this.statusCode = code;
40
+ return this;
41
+ },
42
+ send(body) {
43
+ this.body = body;
44
+ },
45
+ };
46
+
47
+ // invoke the middleware handler for that layer
48
+ layer.handle(req, res, (err) => {
49
+ if (err) throw err;
50
+ });
51
+
52
+ await new Promise(process.nextTick);
53
+
54
+ expect(res.body).toBe('OK');
55
+ expect(res.statusCode).toBe(200);
56
+ expect(res.headers['content-type']).toBe('text/plain');
57
+ expect(res.headers['cache-status']).toBe('Souin; hit; ttl=664');
58
+ });
59
+ });
@@ -4,6 +4,7 @@ import { getAPIResourceWithAuth } from '@plone/volto/helpers/Api/APIResourceWith
4
4
  const HEADERS = [
5
5
  'content-type',
6
6
  'content-disposition',
7
+ 'cache-status',
7
8
  'cache-control',
8
9
  'x-sendfile',
9
10
  'x-accel-redirect',
@@ -0,0 +1,50 @@
1
+ import imagesMiddleware from './images';
2
+ import { getAPIResourceWithAuth } from '@plone/volto/helpers/Api/APIResourceWithAuth';
3
+
4
+ vi.mock('@plone/volto/helpers/Api/APIResourceWithAuth', () => ({
5
+ getAPIResourceWithAuth: vi.fn(),
6
+ }));
7
+
8
+ describe('images middleware', () => {
9
+ it('handles requests containing @@images and forwards headers/body', async () => {
10
+ getAPIResourceWithAuth.mockResolvedValue({
11
+ headers: {
12
+ 'content-type': 'image/png',
13
+ 'cache-status': 'Souin; hit; ttl=664',
14
+ },
15
+ body: 'OK',
16
+ });
17
+
18
+ const mw = imagesMiddleware();
19
+ const layer = mw.stack.find(
20
+ (l) => l.regexp && l.regexp.source.includes('@@images'),
21
+ );
22
+ expect(layer).toBeTruthy();
23
+
24
+ const req = {
25
+ path: '/some/@@images/image',
26
+ method: 'GET',
27
+ app: { locals: {} },
28
+ };
29
+ const res = {
30
+ headers: {},
31
+ set(name, val) {
32
+ this.headers[name] = val;
33
+ },
34
+ send(body) {
35
+ this.body = body;
36
+ },
37
+ };
38
+
39
+ // invoke the middleware handler for that layer
40
+ layer.handle(req, res, (err) => {
41
+ if (err) throw err;
42
+ });
43
+
44
+ await new Promise(process.nextTick);
45
+
46
+ expect(res.body).toBe('OK');
47
+ expect(res.headers['content-type']).toBe('image/png');
48
+ expect(res.headers['cache-status']).toBe('Souin; hit; ttl=664');
49
+ });
50
+ });
@@ -852,12 +852,12 @@ export const getBlocksHierarchy = (properties) => {
852
852
  const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
853
853
  return properties[blocksLayoutFieldname]?.items?.map((n) => ({
854
854
  id: n,
855
- title: properties[blocksFieldName][n]?.['@type'],
856
- data: properties[blocksFieldName][n],
857
- children: isBlockContainer(properties[blocksFieldName][n])
858
- ? properties[blocksFieldName][n].data
859
- ? getBlocksHierarchy(properties[blocksFieldName][n].data)
860
- : getBlocksHierarchy(properties[blocksFieldName][n])
855
+ title: properties?.[blocksFieldName]?.[n]?.['@type'],
856
+ data: properties?.[blocksFieldName]?.[n],
857
+ children: isBlockContainer(properties?.[blocksFieldName]?.[n])
858
+ ? properties?.[blocksFieldName]?.[n]?.data
859
+ ? getBlocksHierarchy(properties?.[blocksFieldName]?.[n]?.data)
860
+ : getBlocksHierarchy(properties?.[blocksFieldName]?.[n])
861
861
  : [],
862
862
  }));
863
863
  };
@@ -37,6 +37,23 @@ export const safeWrapper = (func) => (config) => {
37
37
  return res;
38
38
  };
39
39
 
40
+ /**
41
+ * Extract a readable error message from several possible error shapes
42
+ * @param {object} error
43
+ * @returns {string} message
44
+ */
45
+ export const getErrorMessage = (error) => {
46
+ const respBody = error?.response?.body;
47
+ if (respBody?.error?.message) return respBody.error.message;
48
+ if (respBody?.message) return respBody.message;
49
+ if (error?.message) return error.message;
50
+ try {
51
+ return JSON.stringify(error);
52
+ } catch (e) {
53
+ return String(error);
54
+ }
55
+ };
56
+
40
57
  /**
41
58
  * A helper to pipe a configuration object through configuration loaders
42
59
  *
@@ -3,6 +3,7 @@ import config from '@plone/volto/registry';
3
3
  import {
4
4
  applyConfig,
5
5
  difference,
6
+ getErrorMessage,
6
7
  getColor,
7
8
  getInitials,
8
9
  hasApiExpander,
@@ -246,6 +247,44 @@ describe('Utils tests', () => {
246
247
  });
247
248
  });
248
249
 
250
+ describe('getErrorMessage', () => {
251
+ it('returns nested backend error message', () => {
252
+ const error = {
253
+ response: {
254
+ body: {
255
+ error: {
256
+ message: 'Backend nested message',
257
+ },
258
+ },
259
+ },
260
+ };
261
+
262
+ expect(getErrorMessage(error)).toEqual('Backend nested message');
263
+ });
264
+
265
+ it('returns body message when present', () => {
266
+ const error = {
267
+ response: {
268
+ body: {
269
+ message: 'Backend body message',
270
+ },
271
+ },
272
+ };
273
+
274
+ expect(getErrorMessage(error)).toEqual('Backend body message');
275
+ });
276
+
277
+ it('returns generic error.message fallback', () => {
278
+ expect(getErrorMessage(new Error('Generic message'))).toEqual(
279
+ 'Generic message',
280
+ );
281
+ });
282
+
283
+ it('returns stringified object as final fallback', () => {
284
+ expect(getErrorMessage({ foo: 'bar' })).toEqual('{"foo":"bar"}');
285
+ });
286
+ });
287
+
249
288
  describe('safeWrapper', () => {
250
289
  it('calls the function with config', () => {
251
290
  expect(
@@ -251,9 +251,13 @@ const apiMiddlewareFactory =
251
251
  const langFileName = toGettextLang(lang);
252
252
  import(
253
253
  /* @vite-ignore */ '@root/../locales/' + langFileName + '.json'
254
- ).then((locale) => {
255
- dispatch(changeLanguage(lang, locale.default));
256
- });
254
+ )
255
+ .then((locale) => {
256
+ dispatch(changeLanguage(lang, locale.default));
257
+ })
258
+ .catch(() => {
259
+ dispatch(changeLanguage(lang, {}));
260
+ });
257
261
  }
258
262
  }
259
263
 
package/src/server.jsx CHANGED
@@ -8,7 +8,6 @@ import express from 'express';
8
8
  import { renderToString } from 'react-dom/server';
9
9
  import { createMemoryHistory } from 'history';
10
10
  import { parse as parseUrl } from 'url';
11
- import keys from 'lodash/keys';
12
11
  import locale from 'locale';
13
12
  import { detect } from 'detect-browser';
14
13
  import path from 'path';
@@ -33,12 +32,8 @@ import {
33
32
  import { changeLanguage } from '@plone/volto/actions/language/language';
34
33
 
35
34
  import userSession from '@plone/volto/reducers/userSession/userSession';
36
-
37
- import ErrorPage from '@plone/volto/error';
38
-
39
- import languages from '@plone/volto/constants/Languages.cjs';
40
-
41
35
  import configureStore from '@plone/volto/store';
36
+ import ErrorPage from '@plone/volto/error';
42
37
  import { ReduxAsyncConnect, loadOnServer } from './helpers/AsyncConnect';
43
38
 
44
39
  let locales = {};
@@ -46,11 +41,15 @@ let locales = {};
46
41
  if (config.settings) {
47
42
  config.settings.supportedLanguages.forEach((lang) => {
48
43
  const langFileName = toGettextLang(lang);
49
- import(
50
- /* @vite-ignore */ '@root/../locales/' + langFileName + '.json'
51
- ).then((locale) => {
52
- locales = { ...locales, [toReactIntlLang(lang)]: locale.default };
53
- });
44
+ import(/* @vite-ignore */ '@root/../locales/' + langFileName + '.json')
45
+ .then((locale) => {
46
+ locales = { ...locales, [toReactIntlLang(lang)]: locale.default };
47
+ })
48
+ .catch(() => {
49
+ debug('i18n')(
50
+ `Locale file for ${lang} not found, using empty messages.`,
51
+ );
52
+ });
54
53
  });
55
54
  }
56
55
 
@@ -58,7 +57,10 @@ function reactIntlErrorHandler(error) {
58
57
  debug('i18n')(error);
59
58
  }
60
59
 
61
- const supported = new locale.Locales(keys(languages), 'en');
60
+ const supported = new locale.Locales(
61
+ config.settings.supportedLanguages,
62
+ config.settings.defaultLanguage,
63
+ );
62
64
 
63
65
  const server = express()
64
66
  .disable('x-powered-by')
@@ -44,3 +44,28 @@ vi.stubGlobal('sessionStorage', {
44
44
  removeItem: vi.fn(),
45
45
  clear: vi.fn(),
46
46
  });
47
+
48
+ global.jest = {
49
+ fn: vi.fn.bind(vi),
50
+ };
51
+
52
+ // Mock IntersectionObserver for Vitest
53
+ const IntersectionObserverMock = vi.fn((callback, options = {}) => {
54
+ const instance = {
55
+ thresholds: Array.isArray(options.threshold)
56
+ ? options.threshold
57
+ : [options.threshold ?? 0],
58
+ root: options.root ?? null,
59
+ rootMargin: options.rootMargin ?? '',
60
+ observe: vi.fn(),
61
+ unobserve: vi.fn(),
62
+ disconnect: vi.fn(),
63
+ takeRecords: vi.fn(() => []),
64
+ };
65
+
66
+ return instance;
67
+ });
68
+
69
+ vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);
70
+ global.IntersectionObserver = IntersectionObserverMock;
71
+ window.IntersectionObserver = IntersectionObserverMock;
@@ -280,6 +280,23 @@ body.babel-view .field.language-independent-field {
280
280
  }
281
281
  }
282
282
  }
283
+ // FileWidget
284
+ .file-widget-dropzone {
285
+ .label-file-widget-input {
286
+ .visually-hidden {
287
+ position: absolute;
288
+ overflow: hidden;
289
+ width: 1px;
290
+ height: 1px;
291
+ padding: 0;
292
+ border: 0;
293
+ margin: -1px;
294
+ clip: rect(1px, 1px, 1px, 1px); /* IE-style CSS for compatibility */
295
+ white-space: nowrap;
296
+ word-wrap: normal;
297
+ }
298
+ }
299
+ }
283
300
 
284
301
  // ### FormFieldWrapper ###
285
302
  .language-independent-field .wrapper > label {
@@ -35,6 +35,7 @@ export function normalizeString(str: string): string;
35
35
  */
36
36
  export function isInteractiveElement(element: node, interactiveElements?: string[]): boolean;
37
37
  export function safeWrapper(func: Function): (config: any) => any;
38
+ export function getErrorMessage(error: object): string;
38
39
  export function getInitials(title: any, limit: any): string;
39
40
  export function getColor(name: any): any;
40
41
  export function parseDateTime(locale: string, value: string, format: string, moment: any): any | string;
package/vitest.config.mjs CHANGED
@@ -12,58 +12,103 @@ const __dirname = path.dirname(__filename);
12
12
 
13
13
  const projectRoot = path.resolve(__dirname);
14
14
 
15
- export default defineConfig({
16
- plugins: [
17
- react(),
18
- svgLoader({
19
- svgoConfig: {
20
- plugins: [
21
- {
22
- name: 'preset-default',
23
- params: {
24
- overrides: {
25
- convertPathData: false,
26
- removeViewBox: false,
27
- },
15
+ const sharedAliases = {
16
+ '@plone/volto': path.resolve(__dirname, 'src'),
17
+ '@plone/volto-slate': path.resolve(__dirname, '../volto-slate/src'),
18
+ '@root': path.resolve(__dirname, 'src'),
19
+ '@plone/components': path.resolve(__dirname, '../components/src'),
20
+ 'promise-file-reader': require.resolve('promise-file-reader'),
21
+ 'react-dropzone': require.resolve('react-dropzone'),
22
+ 'prop-types': require.resolve('prop-types'),
23
+ 'react-intl-redux': require.resolve('react-intl-redux'),
24
+ };
25
+
26
+ // volto-slate specific aliases
27
+ const voltoSlateAliases = {
28
+ ...sharedAliases,
29
+ 'react-test-renderer': require.resolve('react-test-renderer'),
30
+ 'redux': require.resolve('redux'),
31
+ };
32
+
33
+ const sharedPlugins = [
34
+ react(),
35
+ svgLoader({
36
+ svgoConfig: {
37
+ plugins: [
38
+ {
39
+ name: 'preset-default',
40
+ params: {
41
+ overrides: {
42
+ convertPathData: false,
43
+ removeViewBox: false,
28
44
  },
29
45
  },
30
- 'removeTitle',
31
- 'removeUselessStrokeAndFill',
32
- ],
33
- },
34
- }),
35
- ],
36
- resolve: {
37
- alias: {
38
- '@plone/volto': path.resolve(__dirname, 'src'),
39
- '@plone/volto-slate': path.resolve(__dirname, '../volto-slate/src'),
40
- '@root': path.resolve(__dirname, 'src'),
41
- 'promise-file-reader': require.resolve('promise-file-reader'),
42
- 'react-dropzone': require.resolve('react-dropzone'),
43
- 'prop-types': require.resolve('prop-types'),
46
+ },
47
+ 'removeTitle',
48
+ 'removeUselessStrokeAndFill',
49
+ ],
44
50
  },
51
+ }),
52
+ ];
53
+
54
+ export default defineConfig({
55
+ plugins: sharedPlugins,
56
+ resolve: {
57
+ alias: sharedAliases,
45
58
  },
46
59
  test: {
47
- isolate: true,
48
- deps: {
49
- moduleDirectories: ['node_modules'],
50
- },
51
- snapshotFormat: { printBasicPrototype: false },
52
- globals: true,
53
- environment: 'jsdom',
54
- setupFiles: [
55
- `${projectRoot}/test-setup-globals-vitest.js`,
56
- `${projectRoot}/test-setup-config.jsx`,
57
- `${projectRoot}/jest-setup-afterenv.js`,
58
- `${projectRoot}/jest-addons-loader.js`,
60
+ projects: [
61
+ // Volto main project
62
+ {
63
+ test: {
64
+ name: 'volto',
65
+ root: '.',
66
+ isolate: true,
67
+ globals: true,
68
+ environment: 'jsdom',
69
+ setupFiles: [
70
+ `${projectRoot}/test-setup-globals-vitest.js`,
71
+ `${projectRoot}/test-setup-config.jsx`,
72
+ `${projectRoot}/jest-setup-afterenv.js`,
73
+ `${projectRoot}/jest-addons-loader.js`,
74
+ ],
75
+ globalSetup: `${projectRoot}/global-test-setup.js`,
76
+ },
77
+ resolve: {
78
+ alias: sharedAliases,
79
+ },
80
+ plugins: sharedPlugins,
81
+ },
82
+ // volto-slate project
83
+ {
84
+ test: {
85
+ name: 'volto-slate',
86
+ root: '../volto-slate',
87
+ isolate: true,
88
+ globals: true,
89
+ environment: 'jsdom',
90
+ setupFiles: [
91
+ `${projectRoot}/test-setup-globals-vitest.js`,
92
+ `${projectRoot}/test-setup-config.jsx`,
93
+ `${projectRoot}/jest-setup-afterenv.js`,
94
+ `${projectRoot}/jest-addons-loader.js`,
95
+ ],
96
+ globalSetup: `${projectRoot}/global-test-setup.js`,
97
+ },
98
+ resolve: {
99
+ alias: voltoSlateAliases,
100
+ },
101
+ plugins: sharedPlugins,
102
+ },
59
103
  ],
60
- globalSetup: `${projectRoot}/global-test-setup.js`,
104
+ snapshotFormat: { printBasicPrototype: false },
61
105
  coverage: {
62
106
  provider: 'v8',
63
107
  reporter: ['text', 'json', 'html'],
64
108
  include: [
65
109
  'src/**/*.{test,spec}.{js,ts,jsx,tsx}',
66
110
  '__test__/**/*.{test,spec}.{js,ts,jsx,tsx}',
111
+ '../volto-slate/src/**/*.{test,spec}.{js,ts,jsx,tsx}',
67
112
  ],
68
113
  exclude: [
69
114
  'node_modules/**',
@@ -75,3 +120,4 @@ export default defineConfig({
75
120
  css: true,
76
121
  },
77
122
  });
123
+