From f81d86f0ada722f724ca394c5a636d429ac6deeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Tue, 19 Mar 2024 08:28:22 +0100 Subject: [PATCH 1/7] New `volto-update-deps` documentation (#5892) Co-authored-by: ichim-david Co-authored-by: Steve Piercy --- docs/source/upgrade-guide/index.md | 67 ++++++++++++++++++++++++++ packages/volto/news/5892.documentation | 1 + 2 files changed, 68 insertions(+) create mode 100644 packages/volto/news/5892.documentation diff --git a/docs/source/upgrade-guide/index.md b/docs/source/upgrade-guide/index.md index f419bd0e93..620c5ea125 100644 --- a/docs/source/upgrade-guide/index.md +++ b/docs/source/upgrade-guide/index.md @@ -28,6 +28,73 @@ Thus it is safe to run it on top of your project and answer the prompts. ## Upgrading to Volto 18.x.x +### Volto's internal `dependencies` and `devDependencies` are now properly sorted out + +Volto internal `dependencies` and `devDependencies` have been correctly sorted out. +This means that Volto no longer will force `devDependencies` as `dependencies` just to make sure that they get installed in Volto projects. +This provoked undesired hoisting problems, and forced the build to not behave correctly in some situations. +This also aligns with the best practices in the JavaScript world, and will make the packagers work better. + +This change means that your projects will now have to declare all their dependencies. +For this purpose, we have developed a {ref}`new utility ` that synchronizes the `dependencies` and `devDependencies` of your projects with those in Volto core. +It is mandatory that you run the utility to make Volto version 18.0.0-alpha.21 or later work in your projects. +This opens the door to use {term}`pnpm` in projects, too, and other goodies. + +(upgrade-guide-new-dependencies-synchronizer-label)= + +### New dependencies synchronizer + +```{versionadded} 18.0.0-alpha.21 +``` + +```{versionadded} @plone/scripts 3.6.1 +``` + +Volto now has a script to ease the upgrades in Volto projects, called `volto-update-deps`. +It's included as part of the `@plone/scripts` package. +This script synchronizes the local dependencies of your project with those in Volto core. +It preserves your dependencies. + +To run the procedure, in your project's `package.json`, update the `@plone/volto` version to the one to which you want to upgrade, such as `18.0.0-alpha.21`. +Then update the version of `@plone/scripts` to at least version `3.6.1`. +The following example shows the minimum valid versions to use under the `dependencies` key. + +```json +dependencies: { + "@plone/volto": "18.0.0-alpha.21", + "@plone/scripts": "^3.6.1" +} +``` + +Then run `yarn` in your project to update the packages. + +```shell +yarn +``` + +After this, the `volto-update-deps` script will be available in your environment. +Now you can run the script to syncrhonize dependencies: + +```shell +yarn volto-update-deps +``` + +It should synchronize the versions in your project's `dependencies` and `devDependencies` with those in Volto core. +It will add the missing ones, and update the current ones. +It will preserve the existing ones. +It is recommended that you check the resultant changes to assess that everything is fine. +Run yarn again to update the versions. + +```shell +yarn +``` + +Verify that your project works well by running the development server. + +```shell +yarn start +``` + ### Volto runs now on React 18.2.0 We have updated Volto to use React 18. diff --git a/packages/volto/news/5892.documentation b/packages/volto/news/5892.documentation new file mode 100644 index 0000000000..a519ec9999 --- /dev/null +++ b/packages/volto/news/5892.documentation @@ -0,0 +1 @@ +`Volto 18.0.0-alpha.21` and `volto-update-deps` documentation @sneridagh From 23f071ee3a4839b90f0da4b12e60c289062a5e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Tue, 19 Mar 2024 08:54:57 +0100 Subject: [PATCH 2/7] Cross-package manager Volto path resolver in webpack-relative-resolver (#5893) --- packages/registry/news/5893.bugfix | 1 + packages/registry/src/addon-registry.js | 1 + packages/volto/__tests__/webpack-relative-resolver.test.js | 4 +++- packages/volto/news/5893.bugfix | 1 + packages/volto/package.json | 1 + packages/volto/webpack-plugins/webpack-relative-resolver.js | 5 ++++- 6 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 packages/registry/news/5893.bugfix create mode 100644 packages/volto/news/5893.bugfix diff --git a/packages/registry/news/5893.bugfix b/packages/registry/news/5893.bugfix new file mode 100644 index 0000000000..ece8b0dd9a --- /dev/null +++ b/packages/registry/news/5893.bugfix @@ -0,0 +1 @@ +Cross-package manager Volto path resolver in `webpack-relative-resolver` @sneridagh diff --git a/packages/registry/src/addon-registry.js b/packages/registry/src/addon-registry.js index 61980f9928..8d0d9b3d17 100644 --- a/packages/registry/src/addon-registry.js +++ b/packages/registry/src/addon-registry.js @@ -113,6 +113,7 @@ class AddonConfigurationRegistry { } this.projectRootPath = projectRootPath; + this.isVoltoProject = packageJson.name !== '@plone/volto'; this.voltoPath = packageJson.name === '@plone/volto' ? `${projectRootPath}` diff --git a/packages/volto/__tests__/webpack-relative-resolver.test.js b/packages/volto/__tests__/webpack-relative-resolver.test.js index 729b197f55..429946a216 100644 --- a/packages/volto/__tests__/webpack-relative-resolver.test.js +++ b/packages/volto/__tests__/webpack-relative-resolver.test.js @@ -79,8 +79,10 @@ describe('WebpackRelativeResolver', () => { it('handles "installed Volto" resolve requests', () => { const registry = makeRegistry(); - registry.voltoPath = '/myvoltoproject/node_modules/@plone/volto'; + registry.isVoltoProject = true; const resolver = new WebpackRelativeResolver(registry); + resolver.voltoPaths['@plone/volto/'] = + '/myvoltoproject/node_modules/@plone/volto/src'; const req = makeInstalledVoltoRequest(); const resolvePath = resolver.getResolvePath(req); diff --git a/packages/volto/news/5893.bugfix b/packages/volto/news/5893.bugfix new file mode 100644 index 0000000000..ece8b0dd9a --- /dev/null +++ b/packages/volto/news/5893.bugfix @@ -0,0 +1 @@ +Cross-package manager Volto path resolver in `webpack-relative-resolver` @sneridagh diff --git a/packages/volto/package.json b/packages/volto/package.json index 235210d079..a275c7ccfd 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -87,6 +87,7 @@ "@plone/volto-slate/(.*)$": "/../volto-slate/src/$1", "@plone/registry": "/../registry/src", "@plone/registry/(.*)$": "/../registry/src/$1", + "@plone/volto": "/src/index.js", "~/config": "/src/config", "~/../locales/${lang}.json": "/locales/en.json", "(.*)/locales/(.*)": "/locales/$2", diff --git a/packages/volto/webpack-plugins/webpack-relative-resolver.js b/packages/volto/webpack-plugins/webpack-relative-resolver.js index 4099305fa3..acbb5dd017 100644 --- a/packages/volto/webpack-plugins/webpack-relative-resolver.js +++ b/packages/volto/webpack-plugins/webpack-relative-resolver.js @@ -5,8 +5,11 @@ class RelativeResolverPlugin { this.source = source || 'resolve'; this.target = target || 'resolve'; this.registry = registry; + this.voltoModulePath = registry.isVoltoProject + ? require.resolve('@plone/volto').split('/').slice(0, -1).join('/') + : `${registry.voltoPath}/src`; this.voltoPaths = Object.assign( - { '@plone/volto/': `${registry.voltoPath}/src` }, + { '@plone/volto/': this.voltoModulePath }, ...Object.keys(registry.packages).map((k) => ({ [k]: registry.packages[k].modulePath, })), From e1b495cc2ed5cd4a8419c0d19d196ddde7cbdf1e Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 19 Mar 2024 09:47:57 +0100 Subject: [PATCH 3/7] Release @plone/registry 1.5.3 --- packages/registry/CHANGELOG.md | 6 ++++++ packages/registry/news/5893.bugfix | 1 - packages/registry/package.json | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) delete mode 100644 packages/registry/news/5893.bugfix diff --git a/packages/registry/CHANGELOG.md b/packages/registry/CHANGELOG.md index 5d523daeb3..a9328a363e 100644 --- a/packages/registry/CHANGELOG.md +++ b/packages/registry/CHANGELOG.md @@ -8,6 +8,12 @@ +## 1.5.3 (2024-03-19) + +### Bugfix + +- Cross-package manager Volto path resolver in `webpack-relative-resolver` @sneridagh [#5893](https://github.com/plone/volto/issues/5893) + ## 1.5.2 (2024-03-05) ### Bugfix diff --git a/packages/registry/news/5893.bugfix b/packages/registry/news/5893.bugfix deleted file mode 100644 index ece8b0dd9a..0000000000 --- a/packages/registry/news/5893.bugfix +++ /dev/null @@ -1 +0,0 @@ -Cross-package manager Volto path resolver in `webpack-relative-resolver` @sneridagh diff --git a/packages/registry/package.json b/packages/registry/package.json index 184d25a6b1..051cf5d9fd 100644 --- a/packages/registry/package.json +++ b/packages/registry/package.json @@ -9,7 +9,7 @@ ], "funding": "https://github.com/sponsors/plone", "license": "MIT", - "version": "1.5.2", + "version": "1.5.3", "repository": { "type": "git", "url": "https://github.com/plone/volto.git" From 9e5edf1ef0794a3d1ada9d7a06ce9b8314b95937 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 19 Mar 2024 09:49:51 +0100 Subject: [PATCH 4/7] Release 18.0.0-alpha.22 --- docs/source/release-notes/index.md | 11 +++++++++++ packages/volto/CHANGELOG.md | 11 +++++++++++ packages/volto/news/5864.bugfix | 1 - packages/volto/news/5892.documentation | 1 - packages/volto/news/5893.bugfix | 1 - packages/volto/package.json | 2 +- .../manage/Blocks/Search/components/Facets.d.ts | 1 + 7 files changed, 24 insertions(+), 4 deletions(-) delete mode 100644 packages/volto/news/5864.bugfix delete mode 100644 packages/volto/news/5892.documentation delete mode 100644 packages/volto/news/5893.bugfix diff --git a/docs/source/release-notes/index.md b/docs/source/release-notes/index.md index 32e82939cb..b55351be71 100644 --- a/docs/source/release-notes/index.md +++ b/docs/source/release-notes/index.md @@ -17,6 +17,17 @@ myst: +## 18.0.0-alpha.22 (2024-03-19) + +### Bugfix + +- Correctly sort facet values if they are numbers @erral [#5864](https://github.com/plone/volto/issues/5864) +- Cross-package manager Volto path resolver in `webpack-relative-resolver` @sneridagh [#5893](https://github.com/plone/volto/issues/5893) + +### Documentation + +- `Volto 18.0.0-alpha.21` and `volto-update-deps` documentation @sneridagh [#5892](https://github.com/plone/volto/issues/5892) + ## 18.0.0-alpha.21 (2024-03-18) ### Breaking diff --git a/packages/volto/CHANGELOG.md b/packages/volto/CHANGELOG.md index 32e82939cb..b55351be71 100644 --- a/packages/volto/CHANGELOG.md +++ b/packages/volto/CHANGELOG.md @@ -17,6 +17,17 @@ myst: +## 18.0.0-alpha.22 (2024-03-19) + +### Bugfix + +- Correctly sort facet values if they are numbers @erral [#5864](https://github.com/plone/volto/issues/5864) +- Cross-package manager Volto path resolver in `webpack-relative-resolver` @sneridagh [#5893](https://github.com/plone/volto/issues/5893) + +### Documentation + +- `Volto 18.0.0-alpha.21` and `volto-update-deps` documentation @sneridagh [#5892](https://github.com/plone/volto/issues/5892) + ## 18.0.0-alpha.21 (2024-03-18) ### Breaking diff --git a/packages/volto/news/5864.bugfix b/packages/volto/news/5864.bugfix deleted file mode 100644 index b38adf55a6..0000000000 --- a/packages/volto/news/5864.bugfix +++ /dev/null @@ -1 +0,0 @@ -Correctly sort facet values if they are numbers @erral diff --git a/packages/volto/news/5892.documentation b/packages/volto/news/5892.documentation deleted file mode 100644 index a519ec9999..0000000000 --- a/packages/volto/news/5892.documentation +++ /dev/null @@ -1 +0,0 @@ -`Volto 18.0.0-alpha.21` and `volto-update-deps` documentation @sneridagh diff --git a/packages/volto/news/5893.bugfix b/packages/volto/news/5893.bugfix deleted file mode 100644 index ece8b0dd9a..0000000000 --- a/packages/volto/news/5893.bugfix +++ /dev/null @@ -1 +0,0 @@ -Cross-package manager Volto path resolver in `webpack-relative-resolver` @sneridagh diff --git a/packages/volto/package.json b/packages/volto/package.json index a275c7ccfd..45c7c8b12c 100644 --- a/packages/volto/package.json +++ b/packages/volto/package.json @@ -9,7 +9,7 @@ } ], "license": "MIT", - "version": "18.0.0-alpha.21", + "version": "18.0.0-alpha.22", "repository": { "type": "git", "url": "git@github.com:plone/volto.git" diff --git a/packages/volto/types/components/manage/Blocks/Search/components/Facets.d.ts b/packages/volto/types/components/manage/Blocks/Search/components/Facets.d.ts index 66d3330745..f31b851180 100644 --- a/packages/volto/types/components/manage/Blocks/Search/components/Facets.d.ts +++ b/packages/volto/types/components/manage/Blocks/Search/components/Facets.d.ts @@ -1,2 +1,3 @@ +export function sortFacetChoices(choices: any): any; export default Facets; declare function Facets(props: any): import("react/jsx-runtime").JSX.Element; From edf2af3274d03ddf6fd413b7092a882476d16a0a Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Tue, 19 Mar 2024 11:35:15 +0100 Subject: [PATCH 5/7] Add new types generator, don't know why they don't get included when releasing --- .../components/manage/Blocks/Search/components/Facets.test.d.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/volto/types/components/manage/Blocks/Search/components/Facets.test.d.ts diff --git a/packages/volto/types/components/manage/Blocks/Search/components/Facets.test.d.ts b/packages/volto/types/components/manage/Blocks/Search/components/Facets.test.d.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/volto/types/components/manage/Blocks/Search/components/Facets.test.d.ts @@ -0,0 +1 @@ +export {}; From 6f49e38c0539d29ea9df4c917748cf0a3617a366 Mon Sep 17 00:00:00 2001 From: ichim-david Date: Tue, 19 Mar 2024 13:46:56 +0200 Subject: [PATCH 6/7] #5341 code analysis documentation (#5882) Co-authored-by: Steve Piercy --- docs/source/contributing/acceptance-tests.md | 95 +++++++++++----- docs/source/contributing/developing-core.md | 44 +++++--- docs/source/contributing/linting.md | 112 +++++++++++++++---- docs/source/contributing/testing.md | 64 +++++++++-- docs/source/development/overview.md | 6 +- packages/volto/news/5341.documentation | 1 + 6 files changed, 245 insertions(+), 77 deletions(-) create mode 100644 packages/volto/news/5341.documentation diff --git a/docs/source/contributing/acceptance-tests.md b/docs/source/contributing/acceptance-tests.md index de27105552..f777d12a09 100644 --- a/docs/source/contributing/acceptance-tests.md +++ b/docs/source/contributing/acceptance-tests.md @@ -7,80 +7,116 @@ myst: "keywords": "Volto, Plone, frontend, React, helper command, redux, acceptance, tests, Cypress" --- -# Acceptance testing +# Acceptance tests -Volto uses [Cypress](https://www.cypress.io/) for browser-based acceptance testing. +Volto uses [Cypress](https://www.cypress.io/) for browser-based acceptance tests. -There are a number of fixtures available covering all the configuration use cases. -These fixtures have both a specific backend and frontend configuration setup and a related set of tests. -The CI infrastructure runs them all automatically on every push to a branch or PR. +There are a number of tests available covering all the configuration use cases. +These tests have both a specific backend and frontend configuration setup and a related set of tests. +The continuous integration infrastructure runs them all automatically on every push to a branch or a pull request. The tests can be run in headless mode (same as the CI does), or within the Cypress user interface. The latter is the one that you run under development. + ## How to run acceptance tests locally (during development) When writing new acceptance tests, you usually want to minimize the time it takes to run the tests, while also being able to debug or inspect what's going on. Being able to restart individual components also comes in handy. It's recommended to start three individual terminal sessions, one each for running the Plone backend, the Volto frontend, and the acceptance tests. +All sessions should start from the `packages/volto` directory. -1. Run the backend fixture. +1. In the first session, start the backend server. ```shell make start-test-acceptance-server ``` -2. Run the frontend fixture. +1. In the second session, start the frontend server. ```shell make start-test-acceptance-frontend-dev ``` -3. Run the Cypress tests for that fixture. +1. In the third session, start the Cypress tests runner. ```shell make test-acceptance ``` -Available fixtures: +1. In the Cypress pop-up test style, choose `E2E Testing`, since Volto's tests are end-to-end tests. + +1. In the next section, select the browser you want Cypress to run in. + Although the core tests use `headless Electron` by default, you can choose your preferred browser for the tests development. + +1. In the main Cypress runner section, you will see all of the test specs that Volto developers have written to test Volto and its packages. + +1. To run a test, interact with the file based tree that displays all possible tests to run, and click on the test spec you need to run. + +We provide the following major test specs: -- Core (`core` or not special naming in the test commands) -- Multilingual (`multilingual`) -- Working Copy (`workingCopy`) -- Core Sandbox (`coresandbox`) +- Core (`core` used to test the core functionality of Volto) +- Multilingual (`multilingual` tests the multilingual support of Volto) +- Working Copy (`workingCopy` tests the working copy feature of Volto) +- Core Sandbox (`coresandbox` tests Volto using configuration and elements that are not present in vanilla Volto) -There are convenience commands for each of these fixtures. See `Makefile` for more information. +There are convenience commands for each of these specs. +See `Makefile` at the root of the repository for more information. -### Writing new acceptance tests -Go to the `cypress/tests` folder to see existing tests. -There is a directory per fixture. +### Write new acceptance tests + +Go to the folder `packages/volto/cypress/tests` to see existing tests. +There is a directory per spec. This directory is hot reloaded with your changes as you write the tests. -For more information on how to write Cypress tests: - https://docs.cypress.io +```{seealso} +[Cypress documentation](https://docs.cypress.io/guides/overview/why-cypress) +``` ## Helper commands -There are some artifacts available for the acceptance tests made accessible to Cypress. +There are some helper commands in {file}`packages/volto/cypress/support/commands.js` written by Volto contributors and made available for the acceptance tests using Cypress. -### Access History, Redux Store and Settings +Volto core makes heavy use of these helpers in the core tests to avoid verbose duplication, and they can make your life easier. +The following is an example of commands used in tests: -We expose the History, {term}`Redux` Store and Settings from the app (only for Cypress environments) so we can easily access them and execute actions (like navigate using the router), dispatch Redux actions or change app settings "on the fly". +```js + beforeEach(() => { + cy.autologin(); + cy.createContent({ + contentType: 'Document', + contentId: 'my-page-1', + contentTitle: 'My Page-1', + allow_discussion: true, + }); + cy.visit('/contents'); + }); +``` -#### Navigate using React Router +`cy.autologin` and `cy.createContent` are commands that will auto login, then create the entered content before each test will run. +This makes it easier to focus on the `act` and `assert` actions of the tests that make use of this test hook. -You can navigate using the React Router (ie. not reloading the page) with this command: + +### Access history, Redux store, and settings + +We expose the history, {term}`Redux` store, and settings from the app (only for Cypress environments) so we can easily access them and execute actions (like navigate using the router), dispatch Redux actions, or change app settings "on the fly". + + +#### Navigate using React router + +You can navigate using the React router without reloading the page with the following command: ```js cy.navigate('/events'); ``` -#### Redux Store -You can access the Redux store and check for specific states and dispatch actions by: +#### Redux store + +You can access the Redux store and check for specific states and dispatch actions as shown: ```js import { updateIntl } from 'react-intl-redux'; @@ -100,11 +136,14 @@ dispatch( ); ``` -More information about this: https://www.cypress.io/blog/2018/11/14/testing-redux-store/ +```{seealso} +[Testing Redux Store](https://www.cypress.io/blog/2018/11/14/testing-redux-store/) +``` + #### Volto settings -You can modify on the fly the main Volto settings like this: +You can modify the main Volto settings on the fly. ```js cy.settings().then(settings => { diff --git a/docs/source/contributing/developing-core.md b/docs/source/contributing/developing-core.md index 0cfec29ea9..690663c3bd 100644 --- a/docs/source/contributing/developing-core.md +++ b/docs/source/contributing/developing-core.md @@ -16,6 +16,8 @@ To create a full Plone project with both frontend and backend, see {doc}`plone:i ``` +(developing-core-monorepo-structure-label)= + ## Monorepo structure The Volto core repository has the shape of a monorepo, where "mono" means "single" and "repo" is short for "repository". @@ -23,15 +25,9 @@ This means that several apps and libraries related to each other are stored in t They are managed together but released individually. This allows the code to be shared effectively, and unifies tracking of changes across all of the apps and libraries. -This monorepo uses pnpm as a package manager, extensively using the workspaces feature. +This monorepo uses pnpm as a package manager, extensively using its {term}`workspace` feature. It's organized in two folders, depending on whether it's a library (package) or an app. -They are located in the `packages` or `apps` folder. -They are declared as pnpm workspaces. -This means they can be managed from the root, using the package manager `--filter` feature. - -```{seealso} -For more information about pnpm workspaces, read the [documentation of pnpm workspaces](https://pnpm.io/workspaces). -``` +The workspaces are located in the `packages` or `apps` folder. ### Folder layout @@ -153,7 +149,6 @@ pnpm --version Compare the output to the [latest pnpm release number](https://www.npmjs.com/package/pnpm). - ```{seealso} [pnpm installation](https://pnpm.io/installation). ``` @@ -220,20 +215,37 @@ Browse to the frontend running at http://localhost:3000. To stop either the backend or frontend, use {kbd}`ctrl-c`. -## Running commands +(developing-core-run-commands-for-pnpm-workspaces-label)= + +## Run commands for pnpm workspaces + +As mentioned in {ref}`developing-core-monorepo-structure-label`, pnpm has the concept of {term}`workspace`. +Every package or app located in the `packages` or `apps` folders is declared as a pnpm workspace. -pnpm has the concept of `workspaces`. -Every package or app located in the `packages` or `apps` folders are declared as a pnpm workspace. -They can be managed using the pnpm `--filter` feature, with either of the following commands: +When developing Volto, you can run pnpm commands from either the repository root or inside the package's or app's workspace in `packages/` or `apps/`. + +pnpm commands will apply in the context from which they are run. +That means when you run a pnpm command from the repository root, it will apply to all workspaces. +It also means when you run a pnpm command from inside a workspace, it will apply only to that workspace. + +You can also use the pnpm `--filter` feature from the repository root to apply to only the specified workspaces, as shown in the following examples. ```shell pnpm --filter @plone/volto start ``` +The above command when run from the repository root will start Volto. + ```shell pnpm --filter @plone/registry build ``` +The above command when run from the repository root will build the Volto registry. + +```{seealso} +For more information about pnpm workspaces, read the [documentation of pnpm workspaces](https://pnpm.io/workspaces). +``` + ## Developing Volto @@ -250,10 +262,10 @@ pnpm start ``` ```` -You can also run commands of specific workspaces using the `--filter` feature as shown in the previous section. +You can also run commands for a specific workspace using the `--filter` feature as shown in the previous section, {ref}`developing-core-run-commands-for-pnpm-workspaces-label`. -## Developing other libraries +## Develop other libraries in a workspace If a package is a dependency of another package in the monorepo, and it's declared as a workspace, they can be declared as usual in the {file}`package.json` as follows: @@ -264,7 +276,7 @@ If a package is a dependency of another package in the monorepo, and it's declar ``` ```{seealso} -[pnpm workspaces](https://pnpm.io/workspaces) +[Documentation of pnpm workspaces](https://pnpm.io/workspaces). ``` diff --git a/docs/source/contributing/linting.md b/docs/source/contributing/linting.md index 0bb1ac4350..cd291fd556 100644 --- a/docs/source/contributing/linting.md +++ b/docs/source/contributing/linting.md @@ -9,29 +9,103 @@ myst: # Linting -```{note} -This documentation is a work in progress. Any help is welcome to fill in the -gaps! +Volto developers can enjoy a lot of freedom in their choice of text editors and IDEs, thanks to the strong tooling provided by the JavaScript ecosystem. + +Volto uses {term}`ESLint`, the advanced JavaScript linting and formatting tool, {term}`Stylelint`, and {term}`Prettier`. + + +(linting-editor-integration-label)= + +## Editor integration + +For Visual Studio Code, you'll need to install [VS Code ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). + +For Vim and NeoVim, you can use [Asynchronous Lint Engine (ALE)](https://github.com/dense-analysis/ale) +It provides out-of-the box integration with all the tooling provided by Volto. + +PyCharm Professional includes [ESLint](https://www.jetbrains.com/help/pycharm/eslint.html), [Stylelint](https://www.jetbrains.com/help/pycharm/using-stylelint-code-quality-tool.html), and [Prettier](https://www.jetbrains.com/help/pycharm/prettier.html). + +Use this checklist to make sure you have correctly configured your editor, most importantly for `.js` and `.jsx` files, the editor should automatically: + +- flag syntax errors +- flag unused imports +- properly flag imported modules that are not found +- format code on save + + +## Lint Volto core + +If you want to contribute to Volto core, you must perform several code quality control tasks. +Your commits must not break the automated tests, or {term}`GitHub workflows`, that Volto performs on every push or pull request. + +Code linting is the first step in the testing hierarchy. + +Volto core should automatically perform linting commands when you commit locally, as provided by the {term}`husky` integration. +However if the automated check doesn't happen when performing a commit, or you want to get less information, you can also run each linting task manually, as described in {ref}`linting-eslint-prettier-and-stylelint-label`. + +If you want to see exactly which linting commands are set to run after a commit, look at the {file}`.lintstagedrc` at the root of the repository. + +Volto core performs linting using the following commands: + +`eslint` +: For finding problems in the project's code files. + +`prettier` +: For uniform code formatting. + +`stylelint` +: For uniform style formatting. + +Although we can run the linting commands from the root of the repository, it is easier to run the commands only for Volto core within the Volto core folder: + +```shell +cd packages/volto +``` + +From here we will have access to the commands to check for errors and to fix them. + +```{seealso} +{ref}`developing-core-run-commands-for-pnpm-workspaces-label` +``` + + +(linting-eslint-prettier-and-stylelint-label)= + +### Eslint, Prettier, and Stylelint + +You can run the pnpm `eslint`, `prettier`, and `stylelint` commands from the Volto package folder: + +```shell +pnpm lint +pnpm prettier +pnpm stylelint ``` -Volto developers can enjoy a lot of freedom in their choice of text editors and -IDEs, thanks to the strong tooling provided by the JavaScript ecosystem. +If we get any errors, some of them can be solved automatically by running pnpm commands as `:fix`: -At the core of these capabilities is ESLint, the advanced JavaScript linting -and formatting tool. Also included with Volto is integration with Stylelint and -Prettier. +```shell +pnpm lint:fix +pnpm prettier:fix +pnpm stylelint:fix +``` -For Visual Studio Code you'll need to integrate an -[ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). +````{note} +The same commands can be found in your Volto add-on projects, as seen in the [`package.json.tpl`](https://github.com/plone/volto/blob/main/packages/generator-volto/generators/app/templates/package.json.tpl#L10) file. -For VIM you can use the [awesome ALE](https://github.com/dense-analysis/ale), -which provides out-of-the box integration with all the tooling provided by -Volto. +You will use similar commands to run the linting commands, but with `yarn` instead of `pnpm`: -Use this checklist to make sure your editor integration is properly integrated, -most importantly for .js/jsx files: +```shell +yarn lint +yarn lint:fix +yarn prettier +yarn prettier:fix +yarn stylelint +yarn stylelint:fix +``` +```` -- The editor should automatically flag syntax errors -- The editor should automatically flag unused imports -- The editor should automatically (and properly) flag imported modules that are not found -- The editor should provide automatic code formatting on save +```{important} +If the `fix` commands cannot fix the errors given by the linting commands, you will need to fix the errors manually. +Do not push commits that do not pass lint checks. +It will not pass GitHub workflow checks, and your contribution will not be merged. +``` diff --git a/docs/source/contributing/testing.md b/docs/source/contributing/testing.md index 6f8d810382..d2f0546333 100644 --- a/docs/source/contributing/testing.md +++ b/docs/source/contributing/testing.md @@ -9,20 +9,61 @@ myst: # Testing -We use Jest for unit testing in Volto. The popular `@testing-library/react` is also -available for writing your tests. For every feature or component, a unit test is -mandatory in Volto core. +We use {term}`Jest` for unit testing in Volto. +The popular `@testing-library/react` is also available for writing your tests. +For every feature or component, a unit test is mandatory in Volto core. ## Jest configuration -Jest is configured in `package.json`, under the "jest" key. +Jest is configured in `package.json` under the `jest` key. + +## Run Jest tests on Volto core + +```{note} +All commands in this documentation run from inside the `packages/volto` directory. +See {ref}`developing-core-run-commands-for-pnpm-workspaces-label` for other options to run tests. +``` + +Jest tests must pass locally before you push commits to the remote Volto repository. +Jest has several modes to run unit tests locally. +You can run Jest in watch mode, run only failed tests, or run specific tests only. + +To get to the test runner modes choices, run the following command. + +```shell +pnpm test +``` + +Then you can follow the Jest prompts for keys that you can enter to trigger test execution. + +```console +No tests found related to files changed since last commit. +Press `a` to run all tests, or run Jest with `--watchAll`. + +Watch Usage + › Press a to run all tests. + › Press f to run only failed tests. + › Press p to filter by a filename regex pattern. + › Press t to filter by a test name regex pattern. + › Press q to quit watch mode. + › Press Enter to trigger a test run. +``` + +You can also run only specific tests using the following commands. + +```shell +pnpm test src/components/theme/Image +# will run only the Image components tests +``` + +If a certain test fails, you can run that test only in watch mode. +This makes it faster and easier to test code changes. ## Jest configuration override -In CI or for testing addons, it's interesting to provide an alternate Jest configuration -or slightly modify it. Volto provide a way to do it using a `jest.config.js` file or -pointing the test runner to a file of your choice, using `RAZZLE_JEST_CONFIG` -environment variable. +In GitHub workflows or for testing add-ons, it's useful to use an alternate Jest configuration. +Volto provides a way to do so using a file {file}`jest.config.js`, or pointing the test runner to a file of your choice, using the `RAZZLE_JEST_CONFIG` environment variable. +Because the Volto add-ons and Volto add-ons projects still use `yarn`, you must run the test command using `yarn` instead of `pnpm`. ```shell RAZZLE_JEST_CONFIG=my-custom-jest-config.js yarn test @@ -32,15 +73,16 @@ RAZZLE_JEST_CONFIG=my-custom-jest-config.js yarn test Both configurations are merged in a way that the keys of the config provided override the initial (`package.json`) default config, either in Volto or in your projects. ``` -This is specially useful in CI while developing add-ons, so you can pass an specific configuration that deals with the addon config properly. +This is especially useful in GitHub workflows while developing add-ons. +You can pass a specific configuration file that properly deals with the add-on configuration. ## Add add-ons via environment variable for testing purposes -Sometimes you need to enable different configurations and enable optional components (for example, testing purposes). +Sometimes you need to enable different configurations and enable optional components, for example, testing purposes. You can use the `ADDONS` environment variable to define them. ```bash ADDONS=test-addon,test-addon2 yarn start ``` - See {doc}`../configuration/environmentvariables` for more information. +See {doc}`../configuration/environmentvariables` for more information. diff --git a/docs/source/development/overview.md b/docs/source/development/overview.md index 492a8fcc20..e561b0a715 100644 --- a/docs/source/development/overview.md +++ b/docs/source/development/overview.md @@ -40,7 +40,7 @@ As is the case with similar modern JavaScript-based applications, you should be Once you've {doc}`bootstrapped your Volto project `, you can immediately start hacking. The following is a list of some the things you can do at this stage. -- Configure your text editor for JavaScript and Volto development +- {ref}`Configure your text editor for JavaScript and Volto development ` - Volto project-based development - Understand and debug React errors - Understand the React component lifecycle @@ -68,8 +68,8 @@ While developing a project using Volto, you might perform the following routine - Integrate an add-on's LESS statements with Volto's theme variables - Use schema-based forms to write block components - Write new form widgets -- Write tests in Jest -- Write integration tests in Cypress +- {doc}`Write tests in Jest <../contributing/testing>` +- {doc}`Write integration tests in Cypress <../contributing/acceptance-tests>` - Split your code and lazy-load libraries to improve performance - Use more advanced React concepts, including hooks, context providers, and other topics - Make your code reusable by separating business logic and repeated behaviors as Higher Order Components (HOCs) diff --git a/packages/volto/news/5341.documentation b/packages/volto/news/5341.documentation new file mode 100644 index 0000000000..6451532b92 --- /dev/null +++ b/packages/volto/news/5341.documentation @@ -0,0 +1 @@ +Updated testing and code quality with information on how to contribute to Volto core. @ichim-david \ No newline at end of file From 482bced02d01d9336e7db10aa4057cfcf0867ceb Mon Sep 17 00:00:00 2001 From: Steffen Ring <54619639+steffenri@users.noreply.github.com> Date: Tue, 19 Mar 2024 12:48:48 +0100 Subject: [PATCH 7/7] fix reordering of slots function (#5840) --- packages/registry/news/5840.bugfix | 1 + packages/registry/src/index.ts | 18 +- packages/registry/src/registry.test.tsx | 244 ++++++++++++++++++++---- 3 files changed, 221 insertions(+), 42 deletions(-) create mode 100644 packages/registry/news/5840.bugfix diff --git a/packages/registry/news/5840.bugfix b/packages/registry/news/5840.bugfix new file mode 100644 index 0000000000..a6cf3d741d --- /dev/null +++ b/packages/registry/news/5840.bugfix @@ -0,0 +1 @@ +- Fix slots reordering function for "before" and "after" keyword @steffenri \ No newline at end of file diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts index 9c838419fd..d71a56560a 100644 --- a/packages/registry/src/index.ts +++ b/packages/registry/src/index.ts @@ -360,11 +360,21 @@ class Config { } switch (action) { case 'after': - result.splice(targetIdx, 0, removed); - break; + if (targetIdx < origin) { + result.splice(targetIdx + 1, 0, removed); + break; + } else { + result.splice(targetIdx, 0, removed); + break; + } case 'before': - result.splice(targetIdx - 1, 0, removed); - break; + if (targetIdx > origin) { + result.splice(targetIdx - 1, 0, removed); + break; + } else { + result.splice(targetIdx, 0, removed); + break; + } case 'last': result.push(removed); break; diff --git a/packages/registry/src/registry.test.tsx b/packages/registry/src/registry.test.tsx index d69af11cdb..4763b93946 100644 --- a/packages/registry/src/registry.test.tsx +++ b/packages/registry/src/registry.test.tsx @@ -474,24 +474,24 @@ describe('Slots registry', () => { expect(config.getSlotComponents('toolbar')).toEqual(['edit', 'save']); }); - it('reorderSlotComponent - after', () => { + it('reorderSlotComponent - after (target after origin)', () => { config.registerSlotComponent({ slot: 'toolbar', - name: 'save', + name: '1', component: 'this is a toolbar save component with a true predicate', predicates: [RouteConditionTrue('/de')], }); config.registerSlotComponent({ slot: 'toolbar', - name: 'edit', + name: '2', component: 'this is a toolbar component with a false predicate', predicates: [RouteConditionFalse('/de')], }); config.registerSlotComponent({ slot: 'toolbar', - name: 'cancel', + name: '3', component: 'this is a toolbar edit component with true predicate', predicates: [ RouteConditionTrue('/folder/path'), @@ -501,7 +501,7 @@ describe('Slots registry', () => { config.registerSlotComponent({ slot: 'toolbar', - name: 'bold', + name: '4', component: 'this is a toolbar edit component with true predicate', predicates: [ RouteConditionTrue('/folder/path'), @@ -509,44 +509,34 @@ describe('Slots registry', () => { ], }); - expect(config.getSlotComponents('toolbar')).toEqual([ - 'save', - 'edit', - 'cancel', - 'bold', - ]); + expect(config.getSlotComponents('toolbar')).toEqual(['1', '2', '3', '4']); config.reorderSlotComponent({ slot: 'toolbar', - name: 'save', + name: '1', action: 'after', - target: 'cancel', + target: '3', }); - expect(config.getSlotComponents('toolbar')).toEqual([ - 'edit', - 'cancel', - 'save', - 'bold', - ]); + expect(config.getSlotComponents('toolbar')).toEqual(['2', '3', '1', '4']); }); - it('reorderSlotComponent - before', () => { + it('reorderSlotComponent - after (target before origin)', () => { config.registerSlotComponent({ slot: 'toolbar', - name: 'save', + name: '1', component: 'this is a toolbar save component with a true predicate', predicates: [RouteConditionTrue('/de')], }); config.registerSlotComponent({ slot: 'toolbar', - name: 'edit', + name: '2', component: 'this is a toolbar component with a false predicate', predicates: [RouteConditionFalse('/de')], }); config.registerSlotComponent({ slot: 'toolbar', - name: 'cancel', + name: '3', component: 'this is a toolbar edit component with true predicate', predicates: [ RouteConditionTrue('/folder/path'), @@ -556,7 +546,7 @@ describe('Slots registry', () => { config.registerSlotComponent({ slot: 'toolbar', - name: 'bold', + name: '4', component: 'this is a toolbar edit component with true predicate', predicates: [ RouteConditionTrue('/folder/path'), @@ -564,24 +554,202 @@ describe('Slots registry', () => { ], }); - expect(config.getSlotComponents('toolbar')).toEqual([ - 'save', - 'edit', - 'cancel', - 'bold', - ]); + expect(config.getSlotComponents('toolbar')).toEqual(['1', '2', '3', '4']); + // Reorder Slot with original position 1 before Slot with position 3 + // Before reordering the positions should be ["1", "2", "3", "4"] + // After the reordering the positions should be ["2", "1", "3", "4"] config.reorderSlotComponent({ slot: 'toolbar', - name: 'save', + name: '3', + action: 'after', + target: '1', + }); + + expect(config.getSlotComponents('toolbar')).toEqual(['1', '3', '2', '4']); + }); + + it('reorderSlotComponent - after (target = origin)', () => { + config.registerSlotComponent({ + slot: 'toolbar', + name: '1', + component: 'this is a toolbar save component with a true predicate', + predicates: [RouteConditionTrue('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '2', + component: 'this is a toolbar component with a false predicate', + predicates: [RouteConditionFalse('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '3', + component: 'this is a toolbar edit component with true predicate', + predicates: [ + RouteConditionTrue('/folder/path'), + ContentTypeConditionTrue(['News Item']), + ], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '4', + component: 'this is a toolbar edit component with true predicate', + predicates: [ + RouteConditionTrue('/folder/path'), + ContentTypeConditionTrue(['News Item']), + ], + }); + + expect(config.getSlotComponents('toolbar')).toEqual(['1', '2', '3', '4']); + // Reorder Slot with original position 1 before Slot with position 3 + // Before reordering the positions should be ["1", "2", "3", "4"] + // After the reordering the positions should be ["2", "1", "3", "4"] + config.reorderSlotComponent({ + slot: 'toolbar', + name: '3', + action: 'after', + target: '3', + }); + + expect(config.getSlotComponents('toolbar')).toEqual(['1', '2', '3', '4']); + }); + + it('reorderSlotComponent - before (target before origin)', () => { + config.registerSlotComponent({ + slot: 'toolbar', + name: '1', + component: 'this is a toolbar save component with a true predicate', + predicates: [RouteConditionTrue('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '2', + component: 'this is a toolbar component with a false predicate', + predicates: [RouteConditionFalse('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '3', + component: 'this is a toolbar edit component with true predicate', + predicates: [RouteConditionFalse('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '4', + component: 'this is a toolbar edit component with true predicate', + predicates: [RouteConditionFalse('/de')], + }); + + expect(config.getSlotComponents('toolbar')).toEqual(['1', '2', '3', '4']); + // Reorder Slot with original position 4 before Slot with position 3 + // Before reordering the positions should be ["1", "2", "3", "4"] + // After the reordering the positions should be ["1", "2", "4", "3 "] + config.reorderSlotComponent({ + slot: 'toolbar', + name: '3', action: 'before', - target: 'cancel', + target: '1', }); - expect(config.getSlotComponents('toolbar')).toEqual([ - 'edit', - 'save', - 'cancel', - 'bold', - ]); + + expect(config.getSlotComponents('toolbar')).toEqual(['3', '1', '2', '4']); + }); + + it('reorderSlotComponent - before (target after origin)', () => { + config.registerSlotComponent({ + slot: 'toolbar', + name: '1', + component: 'this is a toolbar save component with a true predicate', + predicates: [RouteConditionTrue('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '2', + component: 'this is a toolbar component with a false predicate', + predicates: [RouteConditionFalse('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '3', + component: 'this is a toolbar edit component with true predicate', + predicates: [ + RouteConditionTrue('/folder/path'), + ContentTypeConditionTrue(['News Item']), + ], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '4', + component: 'this is a toolbar edit component with true predicate', + predicates: [ + RouteConditionTrue('/folder/path'), + ContentTypeConditionTrue(['News Item']), + ], + }); + + expect(config.getSlotComponents('toolbar')).toEqual(['1', '2', '3', '4']); + // Reorder Slot with original position 1 before Slot with position 3 + // Before reordering the positions should be ["1", "2", "3", "4"] + // After the reordering the positions should be ["2", "1", "3", "4"] + config.reorderSlotComponent({ + slot: 'toolbar', + name: '1', + action: 'before', + target: '3', + }); + + expect(config.getSlotComponents('toolbar')).toEqual(['2', '1', '3', '4']); + }); + + it('reorderSlotComponent - before (target = origin)', () => { + config.registerSlotComponent({ + slot: 'toolbar', + name: '1', + component: 'this is a toolbar save component with a true predicate', + predicates: [RouteConditionTrue('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '2', + component: 'this is a toolbar component with a false predicate', + predicates: [RouteConditionFalse('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '3', + component: 'this is a toolbar edit component with true predicate', + predicates: [RouteConditionFalse('/de')], + }); + + config.registerSlotComponent({ + slot: 'toolbar', + name: '4', + component: 'this is a toolbar edit component with true predicate', + predicates: [RouteConditionFalse('/de')], + }); + + expect(config.getSlotComponents('toolbar')).toEqual(['1', '2', '3', '4']); + // Reorder Slot with original position 4 before Slot with position 3 + // Before reordering the positions should be ["1", "2", "3", "4"] + // After the reordering the positions should be ["1", "2", "4", "3 "] + config.reorderSlotComponent({ + slot: 'toolbar', + name: '3', + action: 'before', + target: '3', + }); + + expect(config.getSlotComponents('toolbar')).toEqual(['1', '2', '3', '4']); }); it('reorderSlotComponent - last', () => {