From fd802d646ea1c98eabdb974adf35680c274106b1 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Sat, 29 Aug 2026 12:50:59 +0200 Subject: [PATCH 01/13] Test: Migrate Node unit tests to Vitest --- .github/workflows/static-checks.yml | 2 +- .github/workflows/unit-test.yml | 2 +- bin/unit-test-date.sh | 2 +- package-lock.json | 372 ++++++++------ packages/a11y/package.json | 3 +- .../src/shared/test/filter-message.test.ts | 1 + packages/abilities/package.json | 3 +- .../abilities/src/store/tests/actions.test.ts | 56 ++- .../abilities/src/store/tests/reducer.test.ts | 73 ++- .../src/store/tests/selectors.test.ts | 6 +- packages/abilities/src/tests/api.test.ts | 151 +++--- .../abilities/src/tests/validation.test.ts | 34 +- packages/admin-ui/package.json | 3 +- packages/annotations/package.json | 3 +- .../annotations/src/store/test/reducer.ts | 1 + packages/api-fetch/package.json | 3 +- .../middlewares/test/fetch-all-middleware.ts | 70 +-- .../api-fetch/src/middlewares/test/http-v1.ts | 1 + .../src/middlewares/test/media-upload.ts | 7 +- .../middlewares/test/namespace-endpoint.ts | 1 + .../api-fetch/src/middlewares/test/nonce.ts | 1 + .../src/middlewares/test/root-url.ts | 1 + .../src/middlewares/test/user-locale.ts | 1 + packages/api-fetch/src/test/exports.test.ts | 1 + packages/autop/package.json | 3 +- packages/autop/src/test/index.test.ts | 1 + .../package.json | 3 +- .../test/index.js | 1 + packages/babel-plugin-makepot/package.json | 3 +- packages/babel-plugin-makepot/test/index.js | 1 + packages/babel-preset-default/package.json | 3 +- .../test/__snapshots__/index.js.snap | 4 +- packages/babel-preset-default/test/index.js | 12 +- packages/blob/package.json | 3 +- packages/block-directory/package.json | 3 +- .../block-directory/src/store/test/reducer.js | 1 + packages/block-editor/package.json | 3 +- .../src/autocompleters/test/block.js | 1 + .../block-mover/test/mover-description.js | 1 + .../src/components/block-styles/test/utils.js | 1 + .../test/use-transformed.patterns.js | 1 + .../components/block-switcher/test/utils.js | 1 + .../components/block-visibility/test/utils.js | 3 +- .../border-radius-control/test/utils.js | 1 + .../src/components/colors/test/utils.js | 1 + .../components/font-sizes/test/fluid-utils.js | 1 + .../src/components/font-sizes/test/utils.js | 1 + .../global-styles/test/typography-utils.js | 1 + .../components/global-styles/test/utils.js | 1 + .../src/components/image-editor/test/index.js | 1 + .../inserter/test/get-appender-label.js | 23 +- .../components/inserter/test/search-items.js | 1 + .../link-control/test/is-url-like.js | 1 + .../list-view/test/use-list-view-drop-zone.js | 1 + .../src/components/list-view/test/utils.js | 1 + .../test/get-computed-accept-attribute.js | 1 + .../preset-input-control/test/utils.js | 1 + .../spacing-sizes-control/test/utils.js | 1 + .../use-block-drop-zone/test/index.js | 1 + .../use-on-block-drop/test/index.js | 59 ++- .../src/hooks/test/allowed-blocks.js | 1 + packages/block-editor/src/hooks/test/gap.js | 1 + .../test/generate-fields-from-attributes.js | 1 + .../src/hooks/test/generated-class-name.js | 1 + .../block-editor/src/hooks/test/metadata.js | 1 + .../src/hooks/test/preset-round-trip.js | 2 + .../block-editor/src/hooks/test/settings.jsx | 1 + .../src/hooks/test/state-utils.js | 1 + .../block-editor/src/layouts/test/utils.js | 1 + packages/block-editor/src/store/test/array.js | 1 + .../src/store/test/get-block-settings.js | 1 + .../src/store/test/performance.js | 3 +- .../src/store/test/private-actions.js | 1 + .../src/store/test/private-selectors.js | 16 +- .../block-editor/src/store/test/reducer.js | 43 +- .../src/utils/test/block-bindings.js | 1 + .../src/utils/test/color-values.js | 1 + .../src/utils/test/format-font-style.js | 1 + .../src/utils/test/format-font-weight.js | 1 + .../utils/test/get-font-styles-and-weights.js | 1 + packages/block-editor/src/utils/test/math.js | 1 + .../block-editor/src/utils/test/object.js | 1 + .../block-editor/src/utils/test/pasting.js | 1 + .../block-editor/src/utils/test/selection.js | 1 + .../block-editor/src/utils/test/sorting.js | 1 + .../src/utils/test/transform-styles.js | 3 +- packages/block-library/package.json | 3 +- .../test/get-updated-link-attributes.js | 1 + .../block-library/src/button/test/utils.js | 1 + packages/block-library/src/code/test/utils.js | 1 + .../block-library/src/columns/test/utils.js | 1 + .../src/comment-template/test/index.js | 1 + .../src/gallery/test/dynamic-source.js | 1 + .../src/gallery/test/responsive-styles.js | 1 + .../block-library/src/gallery/test/shared.js | 1 + .../src/gallery/test/variations.js | 1 + .../block-library/src/heading/test/shared.js | 1 + .../src/media-text/test/image-fill.js | 1 + .../shared/test/update-attributes.test.js | 103 ++-- .../navigation-link/test/block-labelling.js | 1 + .../navigation/edit/test/are-blocks-dirty.js | 1 + .../src/navigation/edit/test/utils.js | 1 + .../block-library/src/pattern/test/index.js | 1 + .../block-library/src/table/test/state.js | 1 + .../block-library/src/table/test/utils.js | 1 + .../src/utils/test/search-patterns.js | 1 + .../src/video/test/variations.js | 1 + .../package.json | 3 +- .../test/index.js | 13 +- .../package.json | 3 + .../shared-tests.js | 70 +-- .../test/index.js | 13 +- packages/blocks/package.json | 3 +- .../api/parser/test/convert-legacy-block.js | 1 + .../api/parser/test/serialize-raw-block.js | 1 + .../api/raw-handling/test/latex-to-math.js | 1 + .../raw-handling/test/markdown-converter.js | 1 + packages/blocks/src/api/test/factory.js | 27 +- packages/blocks/src/api/test/registration.jsx | 2 +- packages/blocks/src/api/test/serializer.jsx | 7 +- packages/blocks/src/api/test/templates.js | 7 +- packages/blocks/src/store/test/actions.js | 1 + .../src/store/test/private-selectors.js | 1 + packages/blocks/src/store/test/reducer.js | 1 + packages/browserslist-config/package.json | 3 +- .../browserslist-config/test/index.test.js | 1 + packages/components/package.json | 6 +- .../test/get-autocomplete-match.ts | 3 +- .../src/border-box-control/test/utils.ts | 11 + .../src/context/test/context-connect.tsx | 3 +- .../src/context/test/wordpress-component.tsx | 3 +- .../custom-gradient-picker/test/serializer.ts | 1 + .../src/date-time/test/utils.test.ts | 9 + .../src/font-size-picker/test/utils.ts | 1 + .../components/src/navigator/test/router.ts | 1 + .../src/query-controls/test/terms.ts | 1 + .../components/src/unit-control/test/utils.ts | 1 + packages/components/src/utils/test/colors.js | 1 + .../src/utils/test/get-node-text.tsx | 1 + packages/components/src/utils/test/math.js | 1 + packages/components/src/utils/test/rtl.js | 1 + packages/components/src/utils/test/space.js | 1 + packages/components/src/utils/test/strings.js | 1 + .../components/src/utils/test/unit-values.js | 1 + packages/compose/package.json | 3 +- .../compose/src/higher-order/test/compose.ts | 23 +- .../compose/src/higher-order/test/pipe.ts | 23 +- .../src/hooks/use-media-query/test/ssr.jsx | 1 + .../test/index.jsx | 1 + .../compose/src/utils/debounce/test/index.ts | 7 +- .../src/utils/observable-map/test/index.js | 5 +- .../compose/src/utils/throttle/test/index.ts | 35 +- packages/connectors/package.json | 3 +- packages/connectors/src/test/store.ts | 1 + packages/core-commands/package.json | 3 + .../test/order-entity-records-by-search.js | 1 + packages/core-data/package.json | 6 +- .../core-data/src/batch/test/create-batch.js | 3 +- .../src/batch/test/default-processor.js | 3 +- .../src/entity-types/test/attachment.test.ts | 11 +- .../__experimental-fetch-link-suggestions.js | 9 +- .../test/__experimental-fetch-url-data.js | 3 +- packages/core-data/src/locks/test/engine.js | 6 +- packages/core-data/src/locks/test/reducer.js | 1 + .../core-data/src/locks/test/selectors.js | 1 + packages/core-data/src/locks/test/utils.js | 1 + .../src/queried-data/test/actions.js | 1 + .../core-data/src/test/parsed-blocks-cache.js | 1 + .../src/utils/test/clear-unchanged-edits.js | 1 + .../src/utils/test/conservative-map-item.js | 1 + .../src/utils/test/get-nested-value.js | 1 + .../test/get-normalized-comma-separable.js | 1 + .../src/utils/test/get-pagination-meta.js | 1 + .../src/utils/test/get-template-info.js | 1 + .../src/utils/test/if-matching-action.js | 1 + .../core-data/src/utils/test/is-numeric-id.js | 1 + .../src/utils/test/log-entity-deprecation.js | 11 +- .../src/utils/test/replace-action.js | 1 + .../core-data/src/utils/test/save-crdt-doc.js | 9 +- .../src/utils/test/set-nested-value.js | 1 + .../src/utils/test/with-weak-map-cache.js | 3 +- packages/customize-widgets/package.json | 3 +- packages/data-controls/package.json | 3 + packages/data-controls/src/test/index.js | 11 +- packages/data/package.json | 3 +- .../persistence/storage/test/object.js | 1 + .../src/plugins/persistence/test/index.js | 7 +- .../src/redux-store/metadata/test/reducer.js | 1 + .../redux-store/metadata/test/selectors.js | 3 +- .../src/redux-store/metadata/test/utils.js | 1 + .../src/redux-store/test/combine-reducers.js | 1 + packages/data/src/redux-store/test/index.js | 24 +- .../src/redux-store/test/keyed-reducer.js | 1 + packages/data/src/test/controls.js | 1 + packages/data/src/test/privateAPIs.js | 1 + packages/data/src/test/registry-selectors.js | 1 + packages/data/src/test/registry.js | 86 ++-- packages/dataviews/package.json | 4 +- .../utils/test/get-calendar-locale.ts | 1 + .../dataform-layouts/test/normalize-form.ts | 6 +- .../utils/test/use-selection-props.ts | 1 + .../src/field-types/test/normalize-fields.ts | 1 + .../dataviews/src/field-types/test/time.ts | 1 + .../utils/test/filter-sort-and-paginate.js | 1 + packages/date/package.json | 3 + packages/date/src/test/index.js | 1 + .../package.json | 1 + .../test/__snapshots__/build.js.snap | 198 ++++---- .../test/build.js | 58 ++- .../test/util.js | 10 +- packages/deprecated/package.json | 3 +- packages/deprecated/src/test/index.ts | 1 + packages/design-system-mcp/package.json | 4 +- packages/design-system-mcp/src/test/data.ts | 5 +- packages/design-system-mcp/src/test/format.ts | 1 + .../src/test/parse-components.ts | 1 + .../src/tools/test/get-component-details.ts | 10 +- packages/docgen/package.json | 3 +- packages/docgen/test/engine.js | 3 +- packages/docgen/test/formatter-markdown.js | 3 +- packages/docgen/test/get-export-entries.js | 5 +- .../test/get-intermediate-representation.js | 5 +- packages/docgen/test/get-jsdoc-from-token.js | 5 +- packages/docgen/test/get-type-annotation.js | 5 +- packages/dom-ready/package.json | 3 +- packages/dom/package.json | 3 + packages/dom/src/test/utils.js | 1 + packages/edit-post/package.json | 3 +- packages/edit-post/src/store/test/reducer.js | 1 + packages/edit-site/package.json | 3 + .../utils/test/get-filtered-template-parts.js | 1 + packages/edit-widgets/package.json | 3 +- .../edit-widgets/src/store/test/reducer.js | 1 + packages/editor/package.json | 3 +- .../components/media-categories/test/index.js | 35 +- .../test/preserve-client-ids.js | 1 + .../components/post-text-editor/test/utils.js | 1 + .../components/style-book/test/categories.js | 3 +- .../test/format-style-value.js | 1 + .../test/style-labels.js | 1 + .../src/store/utils/test/notice-builder.js | 4 +- .../media-sideload-from-url/test/index.js | 17 +- packages/editor/src/utils/test/device-type.js | 1 + .../test/get-collaborator-display-name.ts | 11 +- .../editor/src/utils/test/media-upload.js | 27 +- .../src/utils/test/normalize-text-string.js | 1 + .../src/utils/test/sync-error-messages.js | 1 + packages/editor/src/utils/test/terms.js | 1 + packages/element/package.json | 3 +- packages/element/src/test/index.jsx | 4 +- packages/element/src/test/platform.jsx | 1 + packages/element/src/test/serialize.jsx | 4 +- packages/element/src/test/utils.js | 1 + .../__snapshots__/config-integration.js.snap | 10 +- .../lib/config/test/add-or-replace-port.js | 8 +- .../env/lib/config/test/config-integration.js | 84 ++-- .../lib/config/test/get-cache-directory.js | 33 +- packages/env/lib/config/test/merge-configs.js | 4 +- packages/env/lib/config/test/parse-config.js | 49 +- .../lib/config/test/parse-source-string.js | 17 +- .../lib/config/test/post-process-config.js | 8 +- .../lib/config/test/read-raw-config-file.js | 19 +- .../env/lib/config/test/validate-config.js | 4 +- .../env/lib/test/__snapshots__/md5.js.snap | 10 +- .../lib/test/build-docker-compose-config.js | 31 +- packages/env/lib/test/cache.js | 21 +- packages/env/lib/test/cli.js | 90 ++-- .../env/lib/test/execute-lifecycle-script.js | 10 +- packages/env/lib/test/md5.js | 4 +- packages/env/lib/test/parse-spx-mode.js | 4 +- packages/env/lib/test/parse-xdebug-mode.js | 4 +- packages/env/lib/test/port-utils.js | 25 +- .../env/lib/test/resolve-available-ports.js | 14 +- packages/env/package.json | 3 + packages/escape-html/package.json | 3 +- packages/escape-html/src/test/index.ts | 1 + packages/eslint-plugin/package.json | 3 +- .../components-no-missing-40px-size-prop.js | 5 +- .../components-no-unsafe-button-disabled.js | 5 +- .../data-no-store-string-literals.js | 5 +- .../rules/__tests__/dependency-group.js | 5 +- .../rules/__tests__/i18n-ellipsis.js | 5 +- .../rules/__tests__/i18n-hyphenated-range.js | 5 +- .../i18n-no-collapsible-whitespace.js | 5 +- .../__tests__/i18n-no-flanking-whitespace.js | 5 +- .../__tests__/i18n-no-placeholders-only.js | 5 +- .../rules/__tests__/i18n-no-variables.js | 5 +- .../rules/__tests__/i18n-text-domain.js | 5 +- .../__tests__/i18n-translator-comments.js | 5 +- .../no-base-control-with-label-without-id.js | 5 +- .../no-dom-globals-in-constructor.js | 8 +- .../no-dom-globals-in-module-scope.js | 8 +- .../no-dom-globals-in-react-cc-render.js | 8 +- .../__tests__/no-dom-globals-in-react-fc.js | 8 +- .../rules/__tests__/no-ds-tokens.js | 5 +- .../__tests__/no-global-active-element.js | 5 +- .../__tests__/no-global-get-selection.js | 5 +- .../rules/__tests__/no-i18n-in-save.js | 5 +- .../no-non-module-stylesheet-imports.js | 5 +- .../rules/__tests__/no-setting-ds-tokens.js | 5 +- .../__tests__/no-unguarded-get-range-at.js | 5 +- .../rules/__tests__/no-unknown-ds-tokens.js | 5 +- .../rules/__tests__/no-unmerged-classname.js | 5 +- .../rules/__tests__/no-unsafe-render-order.js | 5 +- .../rules/__tests__/no-unsafe-wp-apis.js | 5 +- .../__tests__/no-unused-vars-before-return.js | 5 +- .../rules/__tests__/no-wp-process-env.js | 5 +- .../__tests__/react-no-unsafe-timeout.js | 5 +- .../rules/__tests__/use-import-as.js | 5 +- .../__tests__/use-recommended-components.js | 5 +- .../rules/__tests__/valid-sprintf.js | 5 +- .../rules/__tests__/wp-global-usage.js | 5 +- .../test-utils/configure-rule-tester.js | 9 + .../utils/test/is-package-installed.js | 1 + packages/fields/package.json | 3 +- packages/format-library/package.json | 3 +- .../format-library/src/link/test/utils.js | 1 + packages/global-styles-engine/package.json | 2 +- .../test/get-global-styles-changes.test.ts | 2 + .../src/test/render.test.ts | 73 ++- .../src/test/resolve-style.test.ts | 30 +- .../src/test/style-state-back-compat.test.ts | 1 + .../src/test/typography-utils.test.ts | 23 +- .../src/test/utils.test.ts | 2 + .../src/test/variation.test.ts | 1 + .../src/test/viewport-utils.test.ts | 1 + packages/global-styles-ui/package.json | 3 +- .../font-library/utils/test/preview-styles.js | 1 + .../global-styles-ui/src/test/shadow-utils.js | 1 + packages/global-styles-ui/src/test/utils.js | 1 + packages/grid/package.json | 4 +- .../test/resolve-fill-widths.test.ts | 1 + .../test/lane-placement.test.ts | 1 + .../test/array-move-with-pinned.test.ts | 1 + .../grid/src/shared/test/resize-snap.test.ts | 1 + packages/hooks/package.json | 3 +- packages/i18n/package.json | 3 +- packages/i18n/src/test/create-i18n.ts | 2 + packages/i18n/src/test/default-i18n.ts | 1 + packages/i18n/src/test/sprintf.ts | 1 + packages/i18n/src/test/subscribe-i18n.ts | 1 + packages/icons/lib/test/generate-library.js | 3 + packages/icons/package.json | 3 +- packages/interactivity-router/package.json | 3 +- .../src/assets/test/scs.test.ts | 1 + packages/interactivity/package.json | 2 +- packages/interface/package.json | 3 +- packages/interface/src/store/test/actions.js | 1 + packages/interface/src/store/test/reducer.js | 1 + .../interface/src/store/test/selectors.js | 1 + packages/is-shallow-equal/package.json | 3 +- packages/is-shallow-equal/test/index.ts | 1 + packages/kebab-case/package.json | 3 + packages/kebab-case/src/test/index.js | 1 + packages/keycodes/package.json | 3 +- packages/keycodes/src/test/platform.ts | 1 + .../src/test/with-ignore-ime-events.ts | 13 +- packages/media-editor/package.json | 3 +- .../test/build-modifiers.test.ts | 2 + .../image-editor/core/math/test/sanitize.ts | 1 + .../src/image-editor/core/test/camera.ts | 5 +- .../core/test/interaction-controller.ts | 90 ++-- .../core/test/preview-export-parity.ts | 1 + .../core/test/render-camera-bridge.ts | 1 + .../src/image-editor/core/test/state.ts | 1 + .../image-editor/core/test/stencil-math.ts | 1 + .../core/transforms/test/pipeline.ts | 1 + .../media-editor/src/store/test/reducer.ts | 17 +- packages/media-fields/package.json | 3 +- .../src/filename/test/index.test.ts | 1 + .../src/filesize/test/index.test.tsx | 1 + .../src/media_dimensions/test/index.test.ts | 1 + packages/media-utils/package.json | 3 +- .../src/utils/test/flatten-form-data.ts | 1 + .../src/utils/test/get-mime-types-array.ts | 1 + .../utils/test/get-upload-error-message.ts | 1 + .../src/utils/test/upload-error.ts | 1 + packages/notices/package.json | 3 +- packages/notices/src/store/test/actions.ts | 1 + packages/notices/src/store/test/reducer.ts | 1 + packages/notices/src/store/test/selectors.ts | 1 + .../npm-package-json-lint-config/package.json | 3 + .../test/index.test.js | 1 + packages/nux/package.json | 3 +- packages/nux/src/store/test/actions.js | 1 + packages/nux/src/store/test/reducer.js | 1 + packages/nux/src/store/test/selectors.js | 1 + packages/plugins/package.json | 3 +- packages/plugins/src/api/test/index.js | 1 + packages/postcss-themes/package.json | 3 + .../test/__snapshots__/index.js.snap | 10 +- packages/postcss-themes/test/index.js | 1 + packages/preferences-persistence/package.json | 3 + .../src/create/test/debounce-async.js | 129 +++-- .../test/convert-edit-post-panels.js | 1 + .../legacy-local-storage-data/test/index.js | 1 + .../test/move-feature-preferences.js | 1 + .../test/move-individual-preference.js | 1 + .../test/move-interface-enable-items.js | 1 + .../move-third-party-feature-preferences.js | 1 + .../test/convert-complementary-areas.js | 1 + .../test/convert-editor-settings.js | 1 + .../preferences-package-data/test/index.js | 1 + packages/preferences/package.json | 3 +- .../preferences/src/store/test/actions.ts | 1 + .../preferences/src/store/test/reducer.ts | 15 +- .../preferences/src/store/test/selectors.ts | 1 + packages/prettier-config/package.json | 3 +- packages/prettier-config/test/index.js | 5 +- packages/priority-queue/package.json | 4 +- packages/priority-queue/src/test/index.ts | 60 ++- packages/private-apis/package.json | 3 +- packages/private-apis/src/test/index.jsx | 1 + .../lib/tasks/add-milestone/test/index.js | 53 +- .../tasks/assign-fixed-issues/test/index.js | 13 +- .../test/index.js | 57 ++- .../test/index.js | 48 +- .../lib/test/get-associated-pull-request.js | 5 +- .../package.json | 3 +- .../package.json | 1 + .../test/__snapshots__/build.js.snap | 10 +- .../test/build.js | 28 +- packages/redux-routine/package.json | 3 +- packages/redux-routine/src/test/index.js | 33 +- packages/redux-routine/src/test/is-action.js | 1 + .../redux-routine/src/test/is-generator.js | 1 + packages/report-flaky-tests/package.json | 4 +- .../__snapshots__/markdown.test.ts.snap | 6 +- .../__tests__/__snapshots__/run.test.ts.snap | 4 +- .../src/__tests__/markdown.test.ts | 1 + .../src/__tests__/run.test.ts | 79 +-- packages/rich-text/package.json | 3 +- packages/rich-text/src/store/test/actions.js | 1 + packages/rich-text/src/store/test/reducer.js | 1 + .../rich-text/src/store/test/selectors.js | 1 + packages/rich-text/src/test/apply-format.js | 1 + packages/rich-text/src/test/concat.js | 1 + .../rich-text/src/test/get-active-format.js | 1 + .../rich-text/src/test/get-active-object.js | 1 + .../rich-text/src/test/get-format-type.js | 7 +- .../rich-text/src/test/get-format-types.js | 7 +- packages/rich-text/src/test/helpers/index.js | 2 +- packages/rich-text/src/test/insert-object.js | 1 + packages/rich-text/src/test/insert.js | 1 + packages/rich-text/src/test/is-collapsed.js | 1 + packages/rich-text/src/test/is-empty.js | 1 + .../rich-text/src/test/is-format-equal.js | 2 +- packages/rich-text/src/test/join.js | 1 + .../rich-text/src/test/normalise-formats.js | 1 + packages/rich-text/src/test/remove-format.js | 1 + packages/rich-text/src/test/replace.js | 1 + packages/rich-text/src/test/slice.js | 1 + packages/rich-text/src/test/split.js | 1 + packages/rich-text/src/test/toggle-format.js | 1 + packages/scripts/package.json | 3 +- .../build-blocks-manifest.js.snap | 4 +- .../scripts/test/build-blocks-manifest.js | 58 ++- packages/scripts/utils/test/index.js | 92 ++-- packages/scripts/utils/test/license.js | 29 +- packages/server-side-render/package.json | 2 +- packages/server-side-render/src/test/index.ts | 1 + packages/shortcode/package.json | 3 +- packages/shortcode/src/test/index.ts | 1 + packages/style-engine/package.json | 3 + .../style-engine/src/styles/test/utils.js | 1 + packages/style-engine/src/test/index.js | 1 + packages/style-runtime/package.json | 3 +- packages/stylelint-config/package.json | 3 +- .../test/__snapshots__/commenting.js.snap | 4 +- .../test/__snapshots__/functions.js.snap | 4 +- .../test/__snapshots__/index.js.snap | 4 +- .../test/__snapshots__/media-queries.js.snap | 4 +- .../test/__snapshots__/properties.js.snap | 4 +- .../test/__snapshots__/scss.js.snap | 4 +- .../test/__snapshots__/selectors-scss.js.snap | 4 +- .../test/__snapshots__/selectors.js.snap | 4 +- .../test/__snapshots__/structure.js.snap | 4 +- .../test/__snapshots__/values.js.snap | 4 +- packages/stylelint-config/test/commenting.js | 4 +- packages/stylelint-config/test/functions.js | 4 +- packages/stylelint-config/test/index.js | 4 +- .../stylelint-config/test/media-queries.js | 4 +- packages/stylelint-config/test/properties.js | 4 +- packages/stylelint-config/test/scss.js | 4 +- .../stylelint-config/test/selectors-scss.js | 4 +- packages/stylelint-config/test/selectors.js | 4 +- packages/stylelint-config/test/structure.js | 4 +- packages/stylelint-config/test/themes.js | 4 +- packages/stylelint-config/test/utils/index.js | 54 +- packages/stylelint-config/test/values.js | 4 +- .../stylelint-config/test/vendor-prefixes.js | 4 +- packages/sync/package.json | 5 +- .../http-polling/test/config.test.ts | 29 +- .../providers/http-polling/test/utils.test.ts | 20 +- packages/sync/src/quill-delta/test/Delta.ts | 2 +- packages/sync/src/test/crdt-snapshot.ts | 2 +- packages/sync/src/test/performance.test.ts | 19 +- packages/sync/src/test/undo-manager.test.ts | 8 +- packages/sync/src/test/utils.ts | 2 +- .../test/index.test.ts | 3 +- .../test/index.test.ts | 3 +- packages/theme/package.json | 4 +- .../test/add-fallback-to-var.test.ts | 1 + .../test/ds-token-fallbacks.test.ts | 1 + .../test/__snapshots__/index.test.ts.snap | 6 +- .../theme/src/color-ramps/test/index.test.ts | 1 + .../src/color-ramps/test/seed-input.test.ts | 1 + .../build-plugin-parity.test.ts.snap | 12 +- .../src/test/build-plugin-parity.test.ts | 1 + packages/theme/src/test/private-apis.test.ts | 9 +- .../theme-provider-color-warnings.test.ts | 1 + ...etting-wpds-custom-properties.test.ts.snap | 4 +- .../no-token-fallback-values.test.ts.snap | 4 +- .../no-unknown-ds-tokens.test.ts.snap | 4 +- .../no-setting-wpds-custom-properties.test.ts | 1 + .../test/no-token-fallback-values.test.ts | 1 + .../test/no-unknown-ds-tokens.test.ts | 1 + packages/token-list/package.json | 3 +- packages/token-list/src/test/index.ts | 1 + packages/ui/package.json | 4 +- packages/ui/src/test/index.test.ts | 1 + .../ui/src/utils/test/theme-provider.test.ts | 68 ++- packages/undo-manager/package.json | 3 +- packages/undo-manager/src/test/index.js | 1 + packages/upload-media/package.json | 3 +- .../upload-media/src/store/test/actions.ts | 238 ++++----- .../src/store/test/private-actions.js | 152 +++--- .../upload-media/src/store/test/reducer.ts | 97 +++- packages/upload-media/src/store/test/retry.ts | 7 +- .../upload-media/src/store/test/selectors.ts | 68 ++- .../src/store/utils/test/video-conversion.ts | 184 ++++--- .../upload-media/src/test/canvas-utils.ts | 83 +-- .../src/test/convert-blob-to-file.ts | 1 + .../upload-media/src/test/error-messages.ts | 1 + .../src/test/get-file-basename.ts | 1 + .../src/test/get-file-extension.ts | 1 + .../src/test/get-file-name-from-url.ts | 1 + .../src/test/get-image-dimensions.ts | 1 + .../src/test/get-mime-types-array.ts | 1 + packages/upload-media/src/test/heic-parser.ts | 27 +- .../upload-media/src/test/is-animated-gif.ts | 1 + .../src/test/rename-and-clone-file.ts | 1 + .../upload-media/src/test/upload-error.ts | 1 + packages/url/package.json | 3 + packages/video-conversion/package.json | 3 +- packages/video-conversion/src/test/index.ts | 117 +++-- packages/viewport/package.json | 3 +- packages/viewport/src/store/test/reducer.js | 1 + packages/viewport/src/store/test/selectors.js | 1 + packages/views/package.json | 3 +- packages/views/src/test/resolve-view.ts | 1 + packages/vips/package.json | 3 +- .../vips/src/test/convert-image-format.ts | 54 +- packages/vips/src/test/has-transparency.ts | 64 ++- packages/vips/src/test/highbitdepth-avif.ts | 25 +- packages/vips/src/test/resize-image.ts | 104 ++-- .../vips/src/test/rotate-image-integration.ts | 45 +- packages/vips/src/test/rotate-image.ts | 106 ++-- packages/vips/src/test/ultrahdr.ts | 102 ++-- packages/warning/package.json | 3 +- packages/warning/src/test/index.js | 1 + packages/warning/test/babel-plugin.js | 3 +- packages/widget-dashboard/package.json | 4 +- .../src/test/create-dashboard-widget.test.ts | 1 + .../resolve-dashboard-column-count.test.ts | 1 + packages/widget-primitives/package.json | 3 +- .../src/field-types/test/field-types.ts | 1 + .../test/fixtures/string-icon-module.mjs | 12 + .../src/hooks/test/fixtures/widget-module.mjs | 27 + .../src/icon-resolver/test/icon-resolver.ts | 9 +- packages/wordcount/package.json | 3 +- packages/wordcount/src/test/index.test.ts | 1 + packages/worker-threads/package.json | 3 +- .../src/test/error-propagation.test.ts | 21 +- .../src/test/main-thread.test.ts | 5 +- .../src/test/worker-thread.test.ts | 29 +- packages/wp-build/lib/test/browserslist.js | 7 +- packages/wp-build/lib/test/package-utils.js | 15 +- packages/wp-build/lib/test/php-generator.js | 1 + packages/wp-build/lib/test/source-files.js | 1 + packages/wp-build/package.json | 3 +- routes/connectors-home/package.json | 3 +- routes/dashboard/package.json | 3 +- .../test/match-dashboard-href.test.ts | 1 + storybook/test/load-error-recovery.test.ts | 17 +- test/integration/blocks-schema.test.js | 10 +- .../helpers/wait-for-store-resolvers.js | 4 +- test/integration/helpers/with-fake-timers.js | 17 +- test/integration/package.json | 3 +- test/integration/theme-schema.test.js | 10 +- test/integration/wp-env-schema.test.js | 1 + test/unit/config/global-mocks.js | 2 + test/unit/config/testing-library.vitest.js | 4 +- test/unit/package.json | 1 - test/unit/scripts/test/resolver.js | 7 +- .../scripts/validate-vitest-conventions.mjs | 225 +++++++-- test/unit/test-migration.json | 472 +++++++++++++++++- test/unit/vitest.config.mjs | 18 +- tools/agents/package.json | 3 + tools/agents/test/setup-skills.test.js | 12 +- tools/build-scripts/package.json | 3 + .../test/check-esm-package-types.test.js | 3 +- .../test/process-windows.test.js | 11 +- tools/build-scripts/test/process.test.js | 1 + tools/eslint/config.mjs | 75 ++- .../test/__snapshots__/changelog.js.snap | 8 +- tools/release/commands/test/changelog.js | 95 ++-- tools/release/commands/test/common.js | 5 +- tools/release/commands/test/packages.js | 121 ++--- tools/release/lib/test/version.js | 5 +- tools/release/package.json | 3 + tools/release/test/cherry-pick.js | 13 +- tools/release/test/cli.js | 11 +- .../test/resolve-performance-branches.js | 9 +- tools/validation/package.json | 3 +- .../validate-package-contents.test.js | 16 +- tools/validation/validate-tsconfig.test.js | 17 +- typings/gutenberg-vitest-test-env/index.d.ts | 35 ++ 618 files changed, 4671 insertions(+), 2644 deletions(-) create mode 100644 packages/eslint-plugin/test-utils/configure-rule-tester.js create mode 100644 packages/widget-primitives/src/hooks/test/fixtures/string-icon-module.mjs create mode 100644 packages/widget-primitives/src/hooks/test/fixtures/widget-module.mjs create mode 100644 typings/gutenberg-vitest-test-env/index.d.ts diff --git a/.github/workflows/static-checks.yml b/.github/workflows/static-checks.yml index 460170fd669b7f..fe38378bef41a7 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/static-checks.yml @@ -105,7 +105,7 @@ jobs: run: npm run lint:js -- --format compact --quiet --prune-suppressions - name: Test agent skill setup - run: npm run test:unit -- tools/agents/test/setup-skills.test.js --runInBand + run: npm run test:unit:vitest -- --project=node tools/agents/test/setup-skills.test.js - name: Lint Styles if: ${{ matrix.annotations }} diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 544cdd5d47bef0..d14cca211bd675 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -166,7 +166,7 @@ jobs: # dimension so flakiness.io keeps a separate history per Node # version in the matrix. FK_ENV_NODEJS: ${{ matrix.node }} - run: npm run test:unit:date -- --ci --maxWorkers="$(nproc)" --cacheDirectory="$HOME/.jest-cache" + run: npm run test:unit:date -- --maxWorkers="$(nproc)" compute-previous-wordpress-version: name: Compute previous WordPress version diff --git a/bin/unit-test-date.sh b/bin/unit-test-date.sh index d21d9570933ef7..3286e9bb8b862a 100755 --- a/bin/unit-test-date.sh +++ b/bin/unit-test-date.sh @@ -17,7 +17,7 @@ for timezone in "${timezones[@]}"; do TZ=$timezone LANG=$locale \ FK_ENV_TZ=$timezone FK_ENV_LANG=$locale \ FLAKINESS_OUTPUT_DIR="flakiness-report-$timezone-$locale" \ - npm run test:unit -- packages/date "$@" & + npm run test:unit:vitest -- packages/date "$@" & pids+=($!) pidsTimezones+=($timezone) pidsLocales+=($locale) diff --git a/package-lock.json b/package-lock.json index 15b8eef3470b49..f4f0af9938aca1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48001,57 +48001,6 @@ } } }, - "node_modules/vite-plugin-commonjs": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/vite-plugin-commonjs/-/vite-plugin-commonjs-0.10.4.tgz", - "integrity": "sha512-eWQuvQKCcx0QYB5e5xfxBNjQKyrjEWZIR9UOkOV6JAgxVhtbZvCOF+FNC2ZijBJ3U3Px04ZMMyyMyFBVWIJ5+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.12.1", - "magic-string": "^0.30.11", - "vite-plugin-dynamic-import": "^1.6.0" - } - }, - "node_modules/vite-plugin-commonjs/node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/vite-plugin-dynamic-import": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.6.0.tgz", - "integrity": "sha512-TM0sz70wfzTIo9YCxVFwS8OA9lNREsh+0vMHGSkWDTZ7bgd1Yjs5RV8EgB634l/91IsXJReg0xtmuQqP0mf+rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.12.1", - "es-module-lexer": "^1.5.4", - "fast-glob": "^3.3.2", - "magic-string": "^0.30.11" - } - }, - "node_modules/vite-plugin-dynamic-import/node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/vite/node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -49980,7 +49929,8 @@ }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -49999,7 +49949,8 @@ "ajv-formats": "^3.0.1" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50044,7 +49995,8 @@ "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", "@wordpress/icons": "file:../icons", - "@wordpress/jest-console": "file:../jest-console" + "@wordpress/jest-console": "file:../jest-console", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50073,7 +50025,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/react": "^18.3.27" + "@types/react": "^18.3.27", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50093,7 +50046,8 @@ "@wordpress/url": "file:../url" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50114,7 +50068,8 @@ "version": "4.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50126,7 +50081,8 @@ "version": "5.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@babel/plugin-syntax-jsx": "^7.25.7" + "@babel/plugin-syntax-jsx": "^7.25.7", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50146,7 +50102,8 @@ "is-plain-object": "^5.1.0" }, "devDependencies": { - "@babel/traverse": "^7.25.7" + "@babel/traverse": "^7.25.7", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50175,7 +50132,8 @@ }, "devDependencies": { "core-js-builder": "^3.39.0", - "terser": "^5.37.0" + "terser": "^5.37.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50230,7 +50188,8 @@ "devDependencies": { "@testing-library/jest-dom": "^6.9.1", "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50269,7 +50228,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50343,7 +50303,8 @@ "@types/postcss-prefix-selector": "^1.16.3", "@wordpress/block-library": "file:../block-library", "deep-freeze": "^0.0.1", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50432,7 +50393,8 @@ "@testing-library/user-event": "^14.6.1", "@wordpress/integration-tests": "file:../../test/integration", "@wordpress/stylelint-tools": "file:../../tools/stylelint", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50454,7 +50416,8 @@ "version": "5.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@wordpress/block-serialization-spec-parser": "file:../block-serialization-spec-parser" + "@wordpress/block-serialization-spec-parser": "file:../block-serialization-spec-parser", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50469,6 +50432,9 @@ "pegjs": "^0.10.0", "phpegjs": "^1.0.0-beta7" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -50509,7 +50475,8 @@ }, "devDependencies": { "@types/react-is": "^18.3.1", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50582,7 +50549,8 @@ "version": "6.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "browserslist": "^4.28.4" + "browserslist": "^4.28.4", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50680,13 +50648,11 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@types/react-dom": "^18.3.1", - "@wordpress/jest-console": "file:../jest-console", "@wordpress/stylelint-tools": "file:../../tools/stylelint", - "snapshot-diff": "^0.10.0", "storybook": "^10.5.3", - "timezone-mock": "^1.3.6" + "timezone-mock": "^1.3.6", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50782,7 +50748,8 @@ "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", "mock-match-media": "^0.4.2", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -50810,7 +50777,8 @@ "@wordpress/private-apis": "file:../private-apis" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", @@ -50853,6 +50821,9 @@ "@wordpress/router": "file:../router", "@wordpress/url": "file:../url" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -50895,13 +50866,11 @@ "uuid": "^14.0.0" }, "devDependencies": { - "@jest/globals": "^30.5.0", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39", "@wordpress/block-library": "file:../block-library", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51030,7 +50999,8 @@ }, "devDependencies": { "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51082,7 +51052,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51107,6 +51078,9 @@ "@wordpress/data": "file:../data", "@wordpress/deprecated": "file:../deprecated" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -51150,10 +51124,10 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "esbuild": "^0.27.2", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51179,6 +51153,9 @@ "moment": "^2.29.4", "moment-timezone": "^0.5.40" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -51199,6 +51176,7 @@ "mini-css-extract-plugin": "^2.9.2", "mkdirp": "^3.0.1", "rimraf": "^5.0.10", + "vitest": "^4.1.10", "webpack": "^5.108.1" }, "engines": { @@ -51302,7 +51280,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@wordpress/jest-console": "file:../jest-console" + "@wordpress/jest-console": "file:../jest-console", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51322,8 +51301,8 @@ "design-system-mcp": "bin/design-system-mcp.mjs" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=20.10.0", @@ -51356,7 +51335,8 @@ "docgen": "bin/cli.js" }, "devDependencies": { - "@types/babel__core": "^7.20.5" + "@types/babel__core": "^7.20.5", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51370,6 +51350,9 @@ "dependencies": { "@wordpress/deprecated": "file:../deprecated" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -51380,7 +51363,8 @@ "version": "4.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51477,7 +51461,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@testing-library/user-event": "^14.6.1" + "@testing-library/user-event": "^14.6.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51533,6 +51518,9 @@ "change-case": "^4.1.2", "clsx": "^2.1.1" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": "^20.19.0 || >=22.13.0", "npm": ">=10.2.3" @@ -51596,7 +51584,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51676,7 +51665,8 @@ "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", "@wordpress/block-library": "file:../block-library", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51706,7 +51696,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", - "@wordpress/jest-console": "file:../jest-console" + "@wordpress/jest-console": "file:../jest-console", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51749,6 +51740,9 @@ "bin": { "wp-env": "bin/wp-env" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -51759,7 +51753,8 @@ "version": "3.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -51797,7 +51792,8 @@ "@types/estree": "^1.0.8", "@typescript-eslint/parser": "^8.68.0", "eslint": "^10.0.0", - "typescript": "npm:@typescript/typescript6@^6.0.2" + "typescript": "npm:@typescript/typescript6@^6.0.2", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52169,7 +52165,8 @@ "remove-accents": "^0.5.0" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52209,7 +52206,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@testing-library/user-event": "^14.6.1" + "@testing-library/user-event": "^14.6.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52237,7 +52235,7 @@ "memize": "^2.1.1" }, "devDependencies": { - "@types/jest": "^30.0.0" + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52272,7 +52270,8 @@ }, "devDependencies": { "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52306,10 +52305,10 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0", "@wordpress/icons": "file:../icons", "@wordpress/ui": "file:../ui", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "engines": { "node": ">=20.10.0", @@ -52331,7 +52330,8 @@ "version": "4.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "benchmark": "^2.1.4" + "benchmark": "^2.1.4", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52366,7 +52366,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "benchmark": "^2.1.4" + "benchmark": "^2.1.4", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52386,7 +52387,8 @@ "@storybook/react-vite": "^10.5.3", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52441,7 +52443,7 @@ "preact": "^10.29.1" }, "devDependencies": { - "@types/jest": "^30.0.0" + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52459,7 +52461,8 @@ }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52487,7 +52490,8 @@ }, "devDependencies": { "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52505,7 +52509,8 @@ "devDependencies": { "@types/jest": "^30.0.0", "@wordpress/lazy-import": "file:../lazy-import", - "benchmark": "^2.1.4" + "benchmark": "^2.1.4", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52643,6 +52648,9 @@ "dependencies": { "change-case": "^4.1.2" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=20.10.0", "npm": ">=10.2.3" @@ -52679,7 +52687,8 @@ "@wordpress/i18n": "file:../i18n" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52819,7 +52828,8 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52857,7 +52867,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52901,7 +52912,8 @@ "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", - "@wordpress/preferences": "file:../preferences" + "@wordpress/preferences": "file:../preferences", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52932,7 +52944,8 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52952,6 +52965,9 @@ "name": "@wordpress/npm-package-json-lint-config", "version": "5.54.0", "license": "GPL-2.0-or-later", + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -52970,7 +52986,8 @@ }, "devDependencies": { "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53030,7 +53047,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@types/react-is": "^18.3.1" + "@types/react-is": "^18.3.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53074,6 +53092,9 @@ "name": "@wordpress/postcss-themes", "version": "6.54.0", "license": "GPL-2.0-or-later", + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -53099,7 +53120,8 @@ "@wordpress/private-apis": "file:../private-apis" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53123,6 +53145,9 @@ "dependencies": { "@wordpress/api-fetch": "file:../api-fetch" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -53135,7 +53160,8 @@ "devDependencies": { "@types/node": "^20.19.39", "@types/prettier": "^2.4.4", - "prettier": "npm:wp-prettier@^3.0.3" + "prettier": "npm:wp-prettier@^3.0.3", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53175,8 +53201,8 @@ "requestidlecallback": "^0.3.0" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/requestidlecallback": "^0.3.4" + "@types/requestidlecallback": "^0.3.4", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53188,7 +53214,8 @@ "version": "1.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "react": "^18.3.1" + "react": "^18.3.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53207,7 +53234,8 @@ "@octokit/webhooks-types": "^5.8.0" }, "devDependencies": { - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53258,6 +53286,7 @@ "mkdirp": "^3.0.1", "rimraf": "^5.0.10", "terser-webpack-plugin": "^5.3.10", + "vitest": "^4.1.10", "webpack": "^5.108.1" }, "engines": { @@ -53362,7 +53391,8 @@ "rungen": "^0.3.2" }, "devDependencies": { - "redux": "^5.0.1" + "redux": "^5.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53383,8 +53413,8 @@ "jest-message-util": "^30.5.0" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53554,7 +53584,8 @@ "colord": "^2.9.3" }, "devDependencies": { - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -53675,7 +53706,8 @@ "wp-scripts": "bin/wp-scripts.js" }, "devDependencies": { - "rimraf": "^5.0.10" + "rimraf": "^5.0.10", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54020,7 +54052,7 @@ "@wordpress/url": "file:../url" }, "devDependencies": { - "@types/jest": "^30.0.0" + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54045,7 +54077,8 @@ "memize": "^2.1.1" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54059,6 +54092,9 @@ "dependencies": { "change-case": "^4.1.2" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -54078,7 +54114,8 @@ "license": "GPL-2.0-or-later", "devDependencies": { "@testing-library/jest-dom": "^6.9.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=20.10.0", @@ -54098,7 +54135,8 @@ "devDependencies": { "@wordpress/npm-package-json-lint-config": "file:../npm-package-json-lint-config", "stylelint": "^16.26.1", - "stylelint-scss": "^6.14.0" + "stylelint-scss": "^6.14.0", + "vitest": "^4.1.10" }, "engines": { "node": "^20.19.0 || >=22.13.0", @@ -54168,9 +54206,8 @@ "yjs": "^13.6.29" }, "devDependencies": { - "@jest/globals": "^30.5.0", - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54199,7 +54236,6 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "@wordpress/validation-tools": "file:../../tools/validation", "esbuild": "^0.27.2", @@ -54207,7 +54243,8 @@ "lightningcss": "^1.33.0", "prettier": "npm:wp-prettier@^3.0.3", "storybook": "^10.5.3", - "stylelint": "^16.26.1" + "stylelint": "^16.26.1", + "vitest": "^4.1.10" }, "engines": { "node": "^20.19.0 || >=22.13.0", @@ -54335,7 +54372,8 @@ "version": "3.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54371,12 +54409,12 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "@wordpress/base-styles": "file:../base-styles", "@wordpress/jest-console": "file:../jest-console", "fast-glob": "^3.2.7", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "engines": { "node": "^20.19.0 || >=22.13.0", @@ -54401,7 +54439,8 @@ "@wordpress/is-shallow-equal": "file:../is-shallow-equal" }, "devDependencies": { - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54426,7 +54465,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54450,6 +54490,9 @@ "dependencies": { "remove-accents": "^0.5.0" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" @@ -54464,7 +54507,8 @@ "mediabunny": "^1.45.2" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54483,7 +54527,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54516,7 +54561,8 @@ "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54533,7 +54579,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54545,7 +54592,8 @@ "version": "3.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@babel/core": "^7.25.7" + "@babel/core": "^7.25.7", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54581,9 +54629,9 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@wordpress/admin-ui": "file:../admin-ui", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "engines": { "node": ">=20.10.0", @@ -54616,7 +54664,8 @@ "@wordpress/components": "file:../components", "@wordpress/icons": "file:../icons", "@wordpress/ui": "file:../ui", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "engines": { "node": ">=20.10.0", @@ -54666,7 +54715,8 @@ "version": "4.54.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54681,7 +54731,8 @@ "comctx": "^1.4.3" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -54743,7 +54794,8 @@ "wp-build": "lib/build.mjs" }, "devDependencies": { - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=20.10.0", @@ -54793,7 +54845,8 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19" @@ -54823,7 +54876,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19" @@ -55256,7 +55310,8 @@ "ajv": "^8.20.0", "fast-glob": "^3.2.7", "react": "^18.3.1", - "rimraf": "^5.0.10" + "rimraf": "^5.0.10", + "vitest": "^4.1.10" } }, "test/performance": { @@ -55323,7 +55378,6 @@ "snapshot-diff": "^0.10.0", "timezone-mock": "^1.3.6", "typescript": "npm:@typescript/typescript6@^6.0.2", - "vite-plugin-commonjs": "^0.10.4", "vitest": "^4.1.10", "yargs": "^17.3.0" } @@ -55580,7 +55634,10 @@ "tools/agents": { "name": "@wordpress/agent-tools", "version": "0.0.0", - "license": "GPL-2.0-or-later" + "license": "GPL-2.0-or-later", + "devDependencies": { + "vitest": "^4.1.10" + } }, "tools/build-scripts": { "name": "@wordpress/build-scripts", @@ -55605,6 +55662,9 @@ "magic-string": "^0.30.21", "rimraf": "^5.0.10", "source-map": "^0.6.1" + }, + "devDependencies": { + "vitest": "^4.1.10" } }, "tools/docs": { @@ -55888,6 +55948,9 @@ }, "bin": { "release-cli": "cli.js" + }, + "devDependencies": { + "vitest": "^4.1.10" } }, "tools/stylelint": { @@ -55916,7 +55979,8 @@ "esbuild": "^0.27.2", "glob": "^13.0.6", "jsonc-parser": "^3.3.1", - "simple-git": "^3.32.3" + "simple-git": "^3.32.3", + "vitest": "^4.1.10" } }, "tools/validation/node_modules/balanced-match": { diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 610476d4902a90..620dd90fa472f7 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -50,7 +50,8 @@ }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/a11y/src/shared/test/filter-message.test.ts b/packages/a11y/src/shared/test/filter-message.test.ts index 34095d23c2b989..8923db209831e0 100644 --- a/packages/a11y/src/shared/test/filter-message.test.ts +++ b/packages/a11y/src/shared/test/filter-message.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import filterMessage from '../filter-message'; describe( 'filterMessage', () => { diff --git a/packages/abilities/package.json b/packages/abilities/package.json index 3c28b1de5a9ae5..3db8f7dbd8baf8 100644 --- a/packages/abilities/package.json +++ b/packages/abilities/package.json @@ -52,7 +52,8 @@ "ajv-formats": "^3.0.1" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/abilities/src/store/tests/actions.test.ts b/packages/abilities/src/store/tests/actions.test.ts index 716a21c8509ab2..fc590b9edfa493 100644 --- a/packages/abilities/src/store/tests/actions.test.ts +++ b/packages/abilities/src/store/tests/actions.test.ts @@ -1,6 +1,8 @@ /** * Tests for store actions. */ + +import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { registerAbility, unregisterAbility, @@ -22,10 +24,10 @@ import type { describe( 'Store Actions', () => { describe( 'registerAbility', () => { let mockSelect: any; - let mockDispatch: jest.Mock; + let mockDispatch: Mock; beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); const defaultCategories = [ { slug: 'test-category', @@ -40,11 +42,11 @@ describe( 'Store Actions', () => { ]; mockSelect = { - getAbility: jest.fn().mockReturnValue( null ), - getAbilityCategories: jest + getAbility: vi.fn().mockReturnValue( null ), + getAbilityCategories: vi .fn() .mockReturnValue( defaultCategories ), - getAbilityCategory: jest.fn().mockImplementation( ( slug ) => { + getAbilityCategory: vi.fn().mockImplementation( ( slug ) => { const categories: Record< string, any > = { 'test-category': { slug: 'test-category', @@ -60,7 +62,7 @@ describe( 'Store Actions', () => { return categories[ slug ] || null; } ), }; - mockDispatch = jest.fn(); + mockDispatch = vi.fn(); } ); it( 'should register a valid client ability', () => { @@ -81,7 +83,7 @@ describe( 'Store Actions', () => { success: { type: 'boolean' }, }, }, - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -124,7 +126,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: 'test-category', - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -151,7 +153,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: 'test-category', - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -180,7 +182,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: 'test-category', - callback: jest.fn(), + callback: vi.fn(), }; mockSelect.getAbility.mockReturnValue( null ); @@ -205,7 +207,7 @@ describe( 'Store Actions', () => { label: '', description: 'Test description', category: 'test-category', - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -222,7 +224,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: '', category: 'test-category', - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -239,7 +241,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: '', - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -267,7 +269,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: invalidCategory, - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -296,7 +298,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: validCategory, - callback: jest.fn(), + callback: vi.fn(), }; const categoriesForTest = [ @@ -357,7 +359,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: 'non-existent-category', - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -393,7 +395,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: 'data-retrieval', - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -433,7 +435,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test ability with custom scope', category: 'test-category', - callback: jest.fn(), + callback: vi.fn(), meta: { scope: 'editor', customProperty: 'customValue', @@ -471,7 +473,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: 'test-category', - callback: jest.fn(), + callback: vi.fn(), }; const action = registerAbility( ability ); @@ -507,15 +509,15 @@ describe( 'Store Actions', () => { describe( 'registerAbilityCategory', () => { let mockSelect: any; - let mockDispatch: jest.Mock; + let mockDispatch: Mock; beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); mockSelect = { - getAbilityCategory: jest.fn().mockReturnValue( null ), - getAbilityCategories: jest.fn().mockReturnValue( [] ), + getAbilityCategory: vi.fn().mockReturnValue( null ), + getAbilityCategories: vi.fn().mockReturnValue( [] ), }; - mockDispatch = jest.fn(); + mockDispatch = vi.fn(); } ); it( 'should register a valid category', () => { @@ -808,10 +810,10 @@ describe( 'Store Actions', () => { description: categoryArgs.description, }, ]; - mockSelect.getAbilityCategories = jest + mockSelect.getAbilityCategories = vi .fn() .mockReturnValue( categoriesWithNew ); - mockSelect.getAbility = jest.fn().mockReturnValue( null ); + mockSelect.getAbility = vi.fn().mockReturnValue( null ); mockDispatch.mockClear(); // Register an ability using the new category @@ -820,7 +822,7 @@ describe( 'Store Actions', () => { label: 'Test Ability', description: 'Test description', category: categorySlug, - callback: jest.fn(), + callback: vi.fn(), }; const abilityAction = registerAbility( ability ); diff --git a/packages/abilities/src/store/tests/reducer.test.ts b/packages/abilities/src/store/tests/reducer.test.ts index 9e7ed971139412..0fcc1d2f1bc886 100644 --- a/packages/abilities/src/store/tests/reducer.test.ts +++ b/packages/abilities/src/store/tests/reducer.test.ts @@ -1,6 +1,8 @@ /** * Tests for store reducer. */ + +import { describe, expect, it } from 'vitest'; import reducer from '../reducer'; import { REGISTER_ABILITY, @@ -19,16 +21,20 @@ describe( 'Store Reducer', () => { name: 'test/ability', label: 'Test Ability', description: 'Test ability', + category: 'test-category', callback: () => Promise.resolve( {} ), }; const action = { type: REGISTER_ABILITY, ability, - }; + } as const; const state = reducer( - { abilitiesByName: defaultState }, + { + abilitiesByName: defaultState, + categoriesBySlug: {}, + }, action ); @@ -45,6 +51,7 @@ describe( 'Store Reducer', () => { name: 'test/ability', label: 'Test Ability', description: 'Test ability', + category: 'test-category', callback: () => Promise.resolve( {} ), // Extra properties that should be filtered out _links: { self: { href: '/test' } }, @@ -54,10 +61,13 @@ describe( 'Store Reducer', () => { const action = { type: REGISTER_ABILITY, ability, - }; + } as const; const state = reducer( - { abilitiesByName: defaultState }, + { + abilitiesByName: defaultState, + categoriesBySlug: {}, + }, action ); const registeredAbility = @@ -80,6 +90,7 @@ describe( 'Store Reducer', () => { name: 'test/ability', label: 'Old Label', description: 'Old description', + category: 'test-category', }, }; @@ -87,16 +98,20 @@ describe( 'Store Reducer', () => { name: 'test/ability', label: 'New Label', description: 'New description', + category: 'test-category', input_schema: { type: 'string' }, }; const action = { type: REGISTER_ABILITY, ability, - }; + } as const; const state = reducer( - { abilitiesByName: initialState }, + { + abilitiesByName: initialState, + categoriesBySlug: {}, + }, action ); @@ -119,21 +134,26 @@ describe( 'Store Reducer', () => { name: 'test/ability1', label: 'Test Ability 1', description: 'First test ability', + category: 'test-category', }, 'test/ability2': { name: 'test/ability2', label: 'Test Ability 2', description: 'Second test ability', + category: 'test-category', }, }; const action = { type: UNREGISTER_ABILITY, name: 'test/ability1', - }; + } as const; const state = reducer( - { abilitiesByName: initialState }, + { + abilitiesByName: initialState, + categoriesBySlug: {}, + }, action ); @@ -153,16 +173,20 @@ describe( 'Store Reducer', () => { name: 'test/ability', label: 'Test Ability', description: 'Test ability', + category: 'test-category', }, }; const action = { type: UNREGISTER_ABILITY, name: 'test/non-existent', - }; + } as const; const state = reducer( - { abilitiesByName: initialState }, + { + abilitiesByName: initialState, + categoriesBySlug: {}, + }, action ); @@ -176,8 +200,13 @@ describe( 'Store Reducer', () => { }; const state = reducer( - { abilitiesByName: defaultState }, - action + { + abilitiesByName: defaultState, + categoriesBySlug: {}, + }, + // Intentionally bypass the static contract to cover the + // reducer's defensive handling of invalid runtime input. + action as never ); expect( state.abilitiesByName ).toEqual( defaultState ); @@ -199,7 +228,7 @@ describe( 'Store Reducer', () => { const action = { type: REGISTER_ABILITY_CATEGORY, category, - }; + } as const; const state = reducer( { categoriesBySlug: defaultState, abilitiesByName: {} }, @@ -225,7 +254,7 @@ describe( 'Store Reducer', () => { const action = { type: REGISTER_ABILITY_CATEGORY, category, - }; + } as const; const state = reducer( { categoriesBySlug: defaultState, abilitiesByName: {} }, @@ -251,7 +280,7 @@ describe( 'Store Reducer', () => { const action = { type: REGISTER_ABILITY_CATEGORY, category, - }; + } as const; const state = reducer( { categoriesBySlug: defaultState, abilitiesByName: {} }, @@ -294,7 +323,7 @@ describe( 'Store Reducer', () => { const action = { type: REGISTER_ABILITY_CATEGORY, category, - }; + } as const; const state = reducer( { categoriesBySlug: initialState, abilitiesByName: {} }, @@ -320,7 +349,9 @@ describe( 'Store Reducer', () => { const state = reducer( { categoriesBySlug: defaultState, abilitiesByName: {} }, - action + // Intentionally bypass the static contract to cover the + // reducer's defensive handling of invalid runtime input. + action as never ); expect( state.categoriesBySlug ).toEqual( defaultState ); @@ -345,7 +376,7 @@ describe( 'Store Reducer', () => { const action = { type: UNREGISTER_ABILITY_CATEGORY, slug: 'category1', - }; + } as const; const state = reducer( { categoriesBySlug: initialState, abilitiesByName: {} }, @@ -370,7 +401,7 @@ describe( 'Store Reducer', () => { const action = { type: UNREGISTER_ABILITY_CATEGORY, slug: 'non-existent', - }; + } as const; const state = reducer( { categoriesBySlug: initialState, abilitiesByName: {} }, @@ -396,7 +427,9 @@ describe( 'Store Reducer', () => { const state = reducer( { categoriesBySlug: initialState, abilitiesByName: {} }, - action + // Intentionally bypass the static contract to cover the + // reducer's defensive handling of invalid runtime input. + action as never ); expect( state.categoriesBySlug ).toEqual( initialState ); diff --git a/packages/abilities/src/store/tests/selectors.test.ts b/packages/abilities/src/store/tests/selectors.test.ts index 8e6de9f026c5ff..f6da2bb8cfa37d 100644 --- a/packages/abilities/src/store/tests/selectors.test.ts +++ b/packages/abilities/src/store/tests/selectors.test.ts @@ -1,6 +1,8 @@ /** * Tests for store selectors. */ + +import { describe, expect, it, vi } from 'vitest'; import { getAbilities, getAbility, @@ -29,7 +31,7 @@ describe( 'Store Selectors', () => { category: 'test-category', input_schema: { type: 'object' }, output_schema: { type: 'object' }, - callback: jest.fn(), + callback: vi.fn(), }, }, categoriesBySlug: {}, @@ -206,7 +208,7 @@ describe( 'Store Selectors', () => { category: 'test-category', input_schema: { type: 'object' }, output_schema: { type: 'object' }, - callback: jest.fn(), + callback: vi.fn(), }, }, categoriesBySlug: {}, diff --git a/packages/abilities/src/tests/api.test.ts b/packages/abilities/src/tests/api.test.ts index 5a748a3cfd8a2b..de6f1be3bac177 100644 --- a/packages/abilities/src/tests/api.test.ts +++ b/packages/abilities/src/tests/api.test.ts @@ -1,6 +1,8 @@ /** * Tests for API functions. */ + +import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { dispatch, select } from '@wordpress/data'; import { getAbilities, @@ -14,18 +16,37 @@ import { import { store } from '../store'; import type { Ability, AbilityCategory } from '../types'; -jest.mock( '@wordpress/data', () => ( { - select: jest.fn(), - dispatch: jest.fn(), +vi.mock( import( '@wordpress/data' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + select: vi.fn(), + dispatch: vi.fn(), } ) ); -jest.mock( '../store', () => ( { - store: 'abilities-api/store', +vi.mock( import( '../store' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + store: 'abilities-api/store' as unknown as typeof store, } ) ); +type AbilitySelectors = ReturnType< typeof select< typeof store > >; +type AbilityDispatch = ReturnType< typeof dispatch< typeof store > >; +const mockedSelect = vi.mocked( select ) as Mock< + ( storeDescriptor: typeof store ) => AbilitySelectors +>; +const mockedDispatch = vi.mocked( dispatch ) as Mock< + ( storeDescriptor: typeof store ) => AbilityDispatch +>; + +const mockSelect = ( selectors: Partial< AbilitySelectors > ) => { + mockedSelect.mockReturnValue( selectors as AbilitySelectors ); +}; + +const mockDispatch = ( actions: Partial< AbilityDispatch > ) => { + mockedDispatch.mockReturnValue( actions as AbilityDispatch ); +}; + describe( 'API functions', () => { beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); describe( 'getAbilities', () => { @@ -49,8 +70,8 @@ describe( 'API functions', () => { }, ]; - const mockGetAbilities = jest.fn().mockReturnValue( mockAbilities ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbilities = vi.fn().mockReturnValue( mockAbilities ); + mockSelect( { getAbilities: mockGetAbilities, } ); @@ -81,8 +102,8 @@ describe( 'API functions', () => { }, ]; - const mockGetAbilities = jest.fn().mockReturnValue( mockAbilities ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbilities = vi.fn().mockReturnValue( mockAbilities ); + mockSelect( { getAbilities: mockGetAbilities, } ); @@ -107,8 +128,8 @@ describe( 'API functions', () => { output_schema: { type: 'object' }, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -122,14 +143,14 @@ describe( 'API functions', () => { describe( 'registerAbility', () => { it( 'should register a client-side ability with a callback', () => { - const mockRegisterAbility = jest.fn(); - ( dispatch as jest.Mock ).mockReturnValue( { + const mockRegisterAbility = vi.fn(); + mockDispatch( { registerAbility: mockRegisterAbility, } ); // Mock select to return no existing ability - ( select as jest.Mock ).mockReturnValue( { - getAbility: jest.fn().mockReturnValue( null ), + mockSelect( { + getAbility: vi.fn().mockReturnValue( null ), } ); const ability = { @@ -139,7 +160,7 @@ describe( 'API functions', () => { category: 'test-category', input_schema: { type: 'object' }, output_schema: { type: 'object' }, - callback: jest.fn(), + callback: vi.fn(), }; registerAbility( ability ); @@ -151,8 +172,8 @@ describe( 'API functions', () => { describe( 'unregisterAbility', () => { it( 'should unregister an ability', () => { - const mockUnregisterAbility = jest.fn(); - ( dispatch as jest.Mock ).mockReturnValue( { + const mockUnregisterAbility = vi.fn(); + mockDispatch( { unregisterAbility: mockUnregisterAbility, } ); @@ -167,7 +188,7 @@ describe( 'API functions', () => { describe( 'executeAbility', () => { it( 'should execute a server-side ability via callback', async () => { - const mockServerCallback = jest + const mockServerCallback = vi .fn() .mockResolvedValue( { success: true, result: 'test' } ); const mockAbility: Ability = { @@ -187,8 +208,8 @@ describe( 'API functions', () => { meta: { annotations: { serverRegistered: true } }, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -203,9 +224,7 @@ describe( 'API functions', () => { } ); it( 'should execute a client-side ability locally', async () => { - const mockCallback = jest - .fn() - .mockResolvedValue( { success: true } ); + const mockCallback = vi.fn().mockResolvedValue( { success: true } ); const mockAbility: Ability = { name: 'test/client-ability', label: 'Client Ability', @@ -217,8 +236,8 @@ describe( 'API functions', () => { meta: { annotations: { clientRegistered: true } }, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -233,8 +252,8 @@ describe( 'API functions', () => { } ); it( 'should throw error if ability not found', async () => { - const mockGetAbility = jest.fn().mockReturnValue( null ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( null ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -244,7 +263,7 @@ describe( 'API functions', () => { } ); it( 'should validate input for client abilities', async () => { - const mockCallback = jest.fn(); + const mockCallback = vi.fn(); const mockAbility: Ability = { name: 'test/client-ability', label: 'Client Ability', @@ -262,8 +281,8 @@ describe( 'API functions', () => { meta: { annotations: { clientRegistered: true } }, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -273,7 +292,7 @@ describe( 'API functions', () => { } ); it( 'should execute a read-only server ability', async () => { - const mockServerCallback = jest + const mockServerCallback = vi .fn() .mockResolvedValue( { data: 'read-only data' } ); const mockAbility: Ability = { @@ -295,8 +314,8 @@ describe( 'API functions', () => { callback: mockServerCallback, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -308,7 +327,7 @@ describe( 'API functions', () => { } ); it( 'should execute a read-only ability with empty input', async () => { - const mockServerCallback = jest + const mockServerCallback = vi .fn() .mockResolvedValue( { data: 'read-only data' } ); const mockAbility: Ability = { @@ -324,8 +343,8 @@ describe( 'API functions', () => { callback: mockServerCallback, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -336,7 +355,7 @@ describe( 'API functions', () => { } ); it( 'should execute a destructive idempotent server ability', async () => { - const mockServerCallback = jest + const mockServerCallback = vi .fn() .mockResolvedValue( 'Item deleted successfully.' ); const mockAbility: Ability = { @@ -362,8 +381,8 @@ describe( 'API functions', () => { callback: mockServerCallback, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -375,11 +394,11 @@ describe( 'API functions', () => { } ); it( 'should handle errors in client ability execution', async () => { - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) - .mockImplementation(); + .mockImplementation( () => {} ); const executionError = new Error( 'Execution failed' ); - const mockCallback = jest.fn().mockRejectedValue( executionError ); + const mockCallback = vi.fn().mockRejectedValue( executionError ); const mockAbility: Ability = { name: 'test/client-ability', @@ -392,8 +411,8 @@ describe( 'API functions', () => { meta: { annotations: { clientRegistered: true } }, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -410,13 +429,11 @@ describe( 'API functions', () => { } ); it( 'should handle errors in server ability execution', async () => { - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) - .mockImplementation(); + .mockImplementation( () => {} ); const serverError = new Error( 'Server execution failed' ); - const mockServerCallback = jest - .fn() - .mockRejectedValue( serverError ); + const mockServerCallback = vi.fn().mockRejectedValue( serverError ); const mockAbility: Ability = { name: 'test/server-ability', @@ -429,8 +446,8 @@ describe( 'API functions', () => { meta: { annotations: { serverRegistered: true } }, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -447,7 +464,7 @@ describe( 'API functions', () => { } ); it( 'should execute ability without callback as server ability', async () => { - const mockServerCallback = jest + const mockServerCallback = vi .fn() .mockResolvedValue( { success: true } ); const mockAbility: Ability = { @@ -462,8 +479,8 @@ describe( 'API functions', () => { meta: { annotations: { serverRegistered: true } }, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -478,7 +495,7 @@ describe( 'API functions', () => { } ); it( 'should validate output for client abilities', async () => { - const mockCallback = jest + const mockCallback = vi .fn() .mockResolvedValue( { invalid: 'response' } ); const mockAbility: Ability = { @@ -498,8 +515,8 @@ describe( 'API functions', () => { meta: { annotations: { clientRegistered: true } }, }; - const mockGetAbility = jest.fn().mockReturnValue( mockAbility ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbility = vi.fn().mockReturnValue( mockAbility ); + mockSelect( { getAbility: mockGetAbility, } ); @@ -524,10 +541,10 @@ describe( 'API functions', () => { }, ]; - const mockGetAbilityCategories = jest + const mockGetAbilityCategories = vi .fn() .mockReturnValue( mockCategories ); - ( select as jest.Mock ).mockReturnValue( { + mockSelect( { getAbilityCategories: mockGetAbilityCategories, } ); @@ -539,8 +556,8 @@ describe( 'API functions', () => { } ); it( 'should return empty array when no categories exist', () => { - const mockGetAbilityCategories = jest.fn().mockReturnValue( [] ); - ( select as jest.Mock ).mockReturnValue( { + const mockGetAbilityCategories = vi.fn().mockReturnValue( [] ); + mockSelect( { getAbilityCategories: mockGetAbilityCategories, } ); @@ -558,10 +575,10 @@ describe( 'API functions', () => { description: 'Abilities that retrieve data', }; - const mockGetAbilityCategory = jest + const mockGetAbilityCategory = vi .fn() .mockReturnValue( mockCategory ); - ( select as jest.Mock ).mockReturnValue( { + mockSelect( { getAbilityCategory: mockGetAbilityCategory, } ); @@ -584,10 +601,10 @@ describe( 'API functions', () => { }, }; - const mockGetAbilityCategory = jest + const mockGetAbilityCategory = vi .fn() .mockReturnValue( mockCategory ); - ( select as jest.Mock ).mockReturnValue( { + mockSelect( { getAbilityCategory: mockGetAbilityCategory, } ); diff --git a/packages/abilities/src/tests/validation.test.ts b/packages/abilities/src/tests/validation.test.ts index a7b5847cbafe44..b02f34486da6a3 100644 --- a/packages/abilities/src/tests/validation.test.ts +++ b/packages/abilities/src/tests/validation.test.ts @@ -1,6 +1,8 @@ /** * Tests for schema validation utilities. */ + +import { describe, expect, it, vi } from 'vitest'; import { validateValueFromSchema } from '../validation'; describe( 'validateValueFromSchema', () => { @@ -133,9 +135,9 @@ describe( 'validateValueFromSchema', () => { describe( 'edge cases', () => { it( 'should pass validation when empty schema provided', () => { - const consoleSpy = jest + const consoleSpy = vi .spyOn( console, 'warn' ) - .mockImplementation(); + .mockImplementation( () => {} ); expect( validateValueFromSchema( 'anything', {} ) ).toBe( true ); expect( consoleSpy ).toHaveBeenCalledWith( @@ -146,9 +148,9 @@ describe( 'validateValueFromSchema', () => { } ); it( 'should warn when type is missing but still pass validation', () => { - const consoleSpy = jest + const consoleSpy = vi .spyOn( console, 'warn' ) - .mockImplementation(); + .mockImplementation( () => {} ); const schema = { properties: { name: { type: 'string' } } }; const result = validateValueFromSchema( { name: 'test' }, schema ); @@ -161,9 +163,9 @@ describe( 'validateValueFromSchema', () => { } ); it( 'should include param name in warning when provided', () => { - const consoleSpy = jest + const consoleSpy = vi .spyOn( console, 'warn' ) - .mockImplementation(); + .mockImplementation( () => {} ); const schema = { format: 'email' }; // Schema without type const result = validateValueFromSchema( 'test@example.com', @@ -449,9 +451,9 @@ describe( 'validateValueFromSchema', () => { describe( 'schema edge cases and errors', () => { it( 'should handle empty schema object as valid but warn about missing type', () => { - const consoleSpy = jest + const consoleSpy = vi .spyOn( console, 'warn' ) - .mockImplementation(); + .mockImplementation( () => {} ); // Empty object schema triggers warning about missing type expect( validateValueFromSchema( 'anything', {} ) ).toBe( true ); @@ -463,9 +465,9 @@ describe( 'validateValueFromSchema', () => { } ); it( 'should warn for invalid schema types but still pass validation', () => { - const consoleSpy = jest + const consoleSpy = vi .spyOn( console, 'warn' ) - .mockImplementation(); + .mockImplementation( () => {} ); // Testing edge cases where schema is not a valid object expect( @@ -506,9 +508,9 @@ describe( 'validateValueFromSchema', () => { it( 'should handle schema compilation errors', () => { // Pass an invalid schema that will cause compilation error const invalidSchema = { type: 'invalid-type' }; - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) - .mockImplementation(); + .mockImplementation( () => {} ); const result = validateValueFromSchema( 'test', invalidSchema ); @@ -566,9 +568,9 @@ describe( 'validateValueFromSchema', () => { }, }, }; - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) - .mockImplementation(); + .mockImplementation( () => {} ); expect( validateValueFromSchema( { name: 'hello' }, schema ) @@ -587,9 +589,9 @@ describe( 'validateValueFromSchema', () => { type: 'string', sanitize_callback: 'sanitize_text_field', }; - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) - .mockImplementation(); + .mockImplementation( () => {} ); expect( validateValueFromSchema( 'hello', schema ) ).toBe( 'Invalid schema provided for validation.' diff --git a/packages/admin-ui/package.json b/packages/admin-ui/package.json index 8067ae4a9f2c7a..23f7da71f352d2 100644 --- a/packages/admin-ui/package.json +++ b/packages/admin-ui/package.json @@ -60,7 +60,8 @@ "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", "@wordpress/icons": "file:../icons", - "@wordpress/jest-console": "file:../jest-console" + "@wordpress/jest-console": "file:../jest-console", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/annotations/package.json b/packages/annotations/package.json index bbd12672f0f7a2..9f8885acce090d 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -50,7 +50,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/react": "^18.3.27" + "@types/react": "^18.3.27", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19" diff --git a/packages/annotations/src/store/test/reducer.ts b/packages/annotations/src/store/test/reducer.ts index 91c7b1f0fb1d74..0d810c740d82dd 100644 --- a/packages/annotations/src/store/test/reducer.ts +++ b/packages/annotations/src/store/test/reducer.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { annotations } from '../reducer'; import type { AnnotationAction, AnnotationsState } from '../../types'; diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 268dc021e428eb..6e958ae2aee120 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -49,7 +49,8 @@ "@wordpress/url": "file:../url" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/api-fetch/src/middlewares/test/fetch-all-middleware.ts b/packages/api-fetch/src/middlewares/test/fetch-all-middleware.ts index d49def8066f136..a62cd68a2592da 100644 --- a/packages/api-fetch/src/middlewares/test/fetch-all-middleware.ts +++ b/packages/api-fetch/src/middlewares/test/fetch-all-middleware.ts @@ -1,52 +1,64 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import type { ApiFetch } from '../../index'; +import type { APIFetchOptions, FetchHandler } from '../../types'; + describe( 'Fetch All Middleware', () => { - beforeEach( jest.resetModules ); + beforeEach( vi.resetModules ); it( 'should defer with the same options to the next middleware', async () => { expect.hasAssertions(); const originalOptions = { path: '/posts' }; - const next = ( options ) => { + const next: FetchHandler = async ( options ) => { expect( options ).toBe( originalOptions ); - return Promise.resolve( 'ok' ); + return 'ok'; }; - await require( '../fetch-all-middleware' ).default( - originalOptions, - next + const { default: fetchAllMiddleware } = await import( + '../fetch-all-middleware' ); + + await fetchAllMiddleware( originalOptions, next ); } ); it( 'should paginate the request', async () => { expect.hasAssertions(); const originalOptions = { url: '/posts?per_page=-1' }; let counter = 1; - jest.doMock( '../../index.ts', () => ( options ) => { - const expectedUrl = - counter === 1 - ? '/posts?per_page=100' - : '/posts?per_page=100&page=2'; - expect( options.url ).toBe( expectedUrl ); - - const response = Promise.resolve( { - status: 200, - headers: { - get() { - return options.url === '/posts?per_page=100' - ? '; rel="next"' - : ''; + vi.doMock( import( '../../index' ), () => { + const mockApiFetch = ( ( options: APIFetchOptions ) => { + const expectedUrl = + counter === 1 + ? '/posts?per_page=100' + : '/posts?per_page=100&page=2'; + expect( options.url ).toBe( expectedUrl ); + + const response = Promise.resolve( { + status: 200, + headers: { + get() { + return options.url === '/posts?per_page=100' + ? '; rel="next"' + : ''; + }, }, - }, - json() { - return Promise.resolve( [ 'item' ] ); - }, - } ); + json() { + return Promise.resolve( [ 'item' ] ); + }, + } ); + + counter++; - counter++; + return response; + } ) as ApiFetch; - return response; + return { default: mockApiFetch }; } ); - const result = await require( '../fetch-all-middleware' ).default( + const { default: fetchAllMiddleware } = await import( + '../fetch-all-middleware' + ); + const result = await fetchAllMiddleware( originalOptions, - () => {} + async () => {} ); expect( result ).toEqual( [ 'item', 'item' ] ); diff --git a/packages/api-fetch/src/middlewares/test/http-v1.ts b/packages/api-fetch/src/middlewares/test/http-v1.ts index 023313b67081b5..89cabfce81bbd3 100644 --- a/packages/api-fetch/src/middlewares/test/http-v1.ts +++ b/packages/api-fetch/src/middlewares/test/http-v1.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { FetchHandler } from '../../types'; import httpV1Middleware from '../http-v1'; diff --git a/packages/api-fetch/src/middlewares/test/media-upload.ts b/packages/api-fetch/src/middlewares/test/media-upload.ts index bdf73fc3002bd4..cf43dd4520ada4 100644 --- a/packages/api-fetch/src/middlewares/test/media-upload.ts +++ b/packages/api-fetch/src/middlewares/test/media-upload.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { FetchHandler } from '../../types'; import mediaUploadMiddleware from '../media-upload'; @@ -17,15 +18,15 @@ describe( 'Media Upload Middleware', () => { expect.hasAssertions(); const requestOptions = { method: 'POST', path: '/wp/v2/media' }; - const next = ( options ) => { + const next: FetchHandler = async ( options ) => { expect( options.parse ).toBe( false ); - return Promise.resolve( { + return { status: 200, json() { return Promise.resolve( [ 'item' ] ); }, - } ); + }; }; mediaUploadMiddleware( requestOptions, next ); diff --git a/packages/api-fetch/src/middlewares/test/namespace-endpoint.ts b/packages/api-fetch/src/middlewares/test/namespace-endpoint.ts index db49b95857ea4a..5cf23bed5f98f9 100644 --- a/packages/api-fetch/src/middlewares/test/namespace-endpoint.ts +++ b/packages/api-fetch/src/middlewares/test/namespace-endpoint.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { FetchHandler } from '../../types'; import namespaceEndpointMiddleware from '../namespace-endpoint'; diff --git a/packages/api-fetch/src/middlewares/test/nonce.ts b/packages/api-fetch/src/middlewares/test/nonce.ts index 011e4b9aac8444..08dbc5b6b9c3db 100644 --- a/packages/api-fetch/src/middlewares/test/nonce.ts +++ b/packages/api-fetch/src/middlewares/test/nonce.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { FetchHandler } from '../../types'; import createNonceMiddleware from '../nonce'; diff --git a/packages/api-fetch/src/middlewares/test/root-url.ts b/packages/api-fetch/src/middlewares/test/root-url.ts index b1df116fcfdcfd..9c356374751e02 100644 --- a/packages/api-fetch/src/middlewares/test/root-url.ts +++ b/packages/api-fetch/src/middlewares/test/root-url.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { FetchHandler } from '../../types'; import createRootUrlMiddleware from '../root-url'; diff --git a/packages/api-fetch/src/middlewares/test/user-locale.ts b/packages/api-fetch/src/middlewares/test/user-locale.ts index 480239fa759862..2d03f247f9911d 100644 --- a/packages/api-fetch/src/middlewares/test/user-locale.ts +++ b/packages/api-fetch/src/middlewares/test/user-locale.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { FetchHandler } from '../../types'; import userLocaleMiddleware from '../user-locale'; diff --git a/packages/api-fetch/src/test/exports.test.ts b/packages/api-fetch/src/test/exports.test.ts index a133f1a8daf41c..e5bbc447c94223 100644 --- a/packages/api-fetch/src/test/exports.test.ts +++ b/packages/api-fetch/src/test/exports.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import apiFetch from '..'; describe( 'apiFetch exports', () => { diff --git a/packages/autop/package.json b/packages/autop/package.json index ac695a2e1d94a1..f7d7b3d8d3aa85 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -43,7 +43,8 @@ "types": "build-types", "sideEffects": false, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/autop/src/test/index.test.ts b/packages/autop/src/test/index.test.ts index e7bd1180624db7..f004cc634d5521 100644 --- a/packages/autop/src/test/index.test.ts +++ b/packages/autop/src/test/index.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { autop, removep } from '../'; test( 'empty string', () => { diff --git a/packages/babel-plugin-import-jsx-pragma/package.json b/packages/babel-plugin-import-jsx-pragma/package.json index c4b4cd488fc2e1..7944dfd85b5648 100644 --- a/packages/babel-plugin-import-jsx-pragma/package.json +++ b/packages/babel-plugin-import-jsx-pragma/package.json @@ -36,7 +36,8 @@ "./package.json": "./package.json" }, "devDependencies": { - "@babel/plugin-syntax-jsx": "^7.25.7" + "@babel/plugin-syntax-jsx": "^7.25.7", + "vitest": "^4.1.10" }, "peerDependencies": { "@babel/core": "^7.25.7" diff --git a/packages/babel-plugin-import-jsx-pragma/test/index.js b/packages/babel-plugin-import-jsx-pragma/test/index.js index 432cd37e60f75f..82824103555f32 100644 --- a/packages/babel-plugin-import-jsx-pragma/test/index.js +++ b/packages/babel-plugin-import-jsx-pragma/test/index.js @@ -1,4 +1,5 @@ import { transformSync } from '@babel/core'; +import { describe, expect, it } from 'vitest'; import plugin from '../'; describe( 'babel-plugin-import-jsx-pragma', () => { diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json index 17604fd4340db3..9863686600eb2c 100644 --- a/packages/babel-plugin-makepot/package.json +++ b/packages/babel-plugin-makepot/package.json @@ -38,7 +38,8 @@ "is-plain-object": "^5.1.0" }, "devDependencies": { - "@babel/traverse": "^7.25.7" + "@babel/traverse": "^7.25.7", + "vitest": "^4.1.10" }, "peerDependencies": { "@babel/core": "^7.25.7" diff --git a/packages/babel-plugin-makepot/test/index.js b/packages/babel-plugin-makepot/test/index.js index e62c323db50d89..3b9cd3c801ff55 100644 --- a/packages/babel-plugin-makepot/test/index.js +++ b/packages/babel-plugin-makepot/test/index.js @@ -1,5 +1,6 @@ import { transformSync } from '@babel/core'; import traverse from '@babel/traverse'; +import { describe, expect, it } from 'vitest'; import babelPlugin from '..'; describe( 'babel-plugin', () => { diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json index 9383cd5b404d74..26a12ab60aab44 100644 --- a/packages/babel-preset-default/package.json +++ b/packages/babel-preset-default/package.json @@ -52,7 +52,8 @@ }, "devDependencies": { "core-js-builder": "^3.39.0", - "terser": "^5.37.0" + "terser": "^5.37.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/babel-preset-default/test/__snapshots__/index.js.snap b/packages/babel-preset-default/test/__snapshots__/index.js.snap index c3a983d94f9c73..f06cfaadff8624 100644 --- a/packages/babel-preset-default/test/__snapshots__/index.js.snap +++ b/packages/babel-preset-default/test/__snapshots__/index.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Babel preset default transpilation works properly 1`] = ` +exports[`Babel preset default > transpilation works properly 1`] = ` "describe('Babel preset default', () => { async function* foo() { await 1; diff --git a/packages/babel-preset-default/test/index.js b/packages/babel-preset-default/test/index.js index d9906e3edf7e85..cacc1921c476ce 100644 --- a/packages/babel-preset-default/test/index.js +++ b/packages/babel-preset-default/test/index.js @@ -1,11 +1,15 @@ -import path from 'path'; -import { readFileSync } from 'fs'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { transform } from '@babel/core'; +import { describe, expect, test } from 'vitest'; import babelPresetDefault from '../'; +const currentDirectory = path.dirname( fileURLToPath( import.meta.url ) ); + describe( 'Babel preset default', () => { test( 'transpilation works properly', () => { - const filename = path.join( __dirname, '/fixtures/input.js' ); + const filename = path.join( currentDirectory, 'fixtures/input.js' ); const input = readFileSync( filename ); const output = transform( input, { @@ -19,7 +23,7 @@ describe( 'Babel preset default', () => { } ); test( 'transpilation includes magic comment when using the addPolyfillComments option', () => { - const filename = path.join( __dirname, '/fixtures/polyfill.js' ); + const filename = path.join( currentDirectory, 'fixtures/polyfill.js' ); const input = readFileSync( filename ); const output = transform( input, { diff --git a/packages/blob/package.json b/packages/blob/package.json index 41bb8537a3f218..f5612ddda4df32 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -45,7 +45,8 @@ "devDependencies": { "@testing-library/jest-dom": "^6.9.1", "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index 0230b95020e0ac..76abde6fbace97 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -68,7 +68,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19", diff --git a/packages/block-directory/src/store/test/reducer.js b/packages/block-directory/src/store/test/reducer.js index 2f14e3e1ad1033..581bdfa1aec6f2 100644 --- a/packages/block-directory/src/store/test/reducer.js +++ b/packages/block-directory/src/store/test/reducer.js @@ -1,4 +1,5 @@ import deepFreeze from 'deep-freeze'; +import { describe, expect, it } from 'vitest'; import { blockManagement, downloadableBlocks, errorNotices } from '../reducer'; import { blockTypeInstalled, downloadableBlock } from './fixtures'; diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index e6688daacd988b..96e44d46dccf61 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -117,7 +117,8 @@ "@types/postcss-prefix-selector": "^1.16.3", "@wordpress/block-library": "file:../block-library", "deep-freeze": "^0.0.1", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/block-editor/src/autocompleters/test/block.js b/packages/block-editor/src/autocompleters/test/block.js index 8237d6ed28fbe4..e2e92e9ed92430 100644 --- a/packages/block-editor/src/autocompleters/test/block.js +++ b/packages/block-editor/src/autocompleters/test/block.js @@ -1,3 +1,4 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; import blockCompleter from '../block'; diff --git a/packages/block-editor/src/components/block-mover/test/mover-description.js b/packages/block-editor/src/components/block-mover/test/mover-description.js index db842a880dd413..759e291f8b70bb 100644 --- a/packages/block-editor/src/components/block-mover/test/mover-description.js +++ b/packages/block-editor/src/components/block-mover/test/mover-description.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getBlockMoverDescription, getMultiBlockMoverDescription, diff --git a/packages/block-editor/src/components/block-styles/test/utils.js b/packages/block-editor/src/components/block-styles/test/utils.js index 3709f9e86ab706..0b38bc13625bd4 100644 --- a/packages/block-editor/src/components/block-styles/test/utils.js +++ b/packages/block-editor/src/components/block-styles/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getActiveStyle, getDefaultStyle, diff --git a/packages/block-editor/src/components/block-switcher/test/use-transformed.patterns.js b/packages/block-editor/src/components/block-switcher/test/use-transformed.patterns.js index 998ae829903315..fcc9b0d918399e 100644 --- a/packages/block-editor/src/components/block-switcher/test/use-transformed.patterns.js +++ b/packages/block-editor/src/components/block-switcher/test/use-transformed.patterns.js @@ -1,3 +1,4 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { unregisterBlockType, registerBlockType } from '@wordpress/blocks'; import { transformMatchingBlock, diff --git a/packages/block-editor/src/components/block-switcher/test/utils.js b/packages/block-editor/src/components/block-switcher/test/utils.js index 251d94c0edf2b1..e7fb7980615fed 100644 --- a/packages/block-editor/src/components/block-switcher/test/utils.js +++ b/packages/block-editor/src/components/block-switcher/test/utils.js @@ -1,3 +1,4 @@ +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { unregisterBlockType, registerBlockType } from '@wordpress/blocks'; import { getMatchingBlockByName, getRetainedBlockAttributes } from '../utils'; diff --git a/packages/block-editor/src/components/block-visibility/test/utils.js b/packages/block-editor/src/components/block-visibility/test/utils.js index 0a109a7451a470..2919b88cc7a90f 100644 --- a/packages/block-editor/src/components/block-visibility/test/utils.js +++ b/packages/block-editor/src/components/block-visibility/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { getBlockVisibilityViewportEntries, getViewportCheckboxState, @@ -76,7 +77,7 @@ describe( 'block-visibility utils', () => { it( 'should return null when some blocks are hidden for viewport', () => { // Suppress console.log from getViewportCheckboxState - const consoleSpy = jest + const consoleSpy = vi .spyOn( console, 'log' ) .mockImplementation( () => {} ); diff --git a/packages/block-editor/src/components/border-radius-control/test/utils.js b/packages/block-editor/src/components/border-radius-control/test/utils.js index 5cba37d2e7a5ac..a4a85ede7a1236 100644 --- a/packages/block-editor/src/components/border-radius-control/test/utils.js +++ b/packages/block-editor/src/components/border-radius-control/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getAllUnit, getAllValue, diff --git a/packages/block-editor/src/components/colors/test/utils.js b/packages/block-editor/src/components/colors/test/utils.js index d24a4489aec12c..546583a406e7b5 100644 --- a/packages/block-editor/src/components/colors/test/utils.js +++ b/packages/block-editor/src/components/colors/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getColorObjectByAttributeValues, getColorObjectByColorValue, diff --git a/packages/block-editor/src/components/font-sizes/test/fluid-utils.js b/packages/block-editor/src/components/font-sizes/test/fluid-utils.js index d9e573e5eed4b8..62a083179df681 100644 --- a/packages/block-editor/src/components/font-sizes/test/fluid-utils.js +++ b/packages/block-editor/src/components/font-sizes/test/fluid-utils.js @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it } from 'vitest'; import { logged } from '@wordpress/deprecated'; import { getComputedFluidTypographyValue, diff --git a/packages/block-editor/src/components/font-sizes/test/utils.js b/packages/block-editor/src/components/font-sizes/test/utils.js index d49105550192d4..ed41e3995a6d2f 100644 --- a/packages/block-editor/src/components/font-sizes/test/utils.js +++ b/packages/block-editor/src/components/font-sizes/test/utils.js @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it } from 'vitest'; import { logged } from '@wordpress/deprecated'; import { getFontSizeClass } from '../utils'; diff --git a/packages/block-editor/src/components/global-styles/test/typography-utils.js b/packages/block-editor/src/components/global-styles/test/typography-utils.js index b26eed27794d54..6d52762e6d902a 100644 --- a/packages/block-editor/src/components/global-styles/test/typography-utils.js +++ b/packages/block-editor/src/components/global-styles/test/typography-utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getMergedFontFamiliesAndFontFamilyFaces, findNearestFontWeight, diff --git a/packages/block-editor/src/components/global-styles/test/utils.js b/packages/block-editor/src/components/global-styles/test/utils.js index 1730fd36379159..883353aaca401b 100644 --- a/packages/block-editor/src/components/global-styles/test/utils.js +++ b/packages/block-editor/src/components/global-styles/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getDuotoneSlugFromPreset } from '../utils'; describe( 'global styles utils', () => { diff --git a/packages/block-editor/src/components/image-editor/test/index.js b/packages/block-editor/src/components/image-editor/test/index.js index 66419631d8fc73..cb0608c1d7d674 100644 --- a/packages/block-editor/src/components/image-editor/test/index.js +++ b/packages/block-editor/src/components/image-editor/test/index.js @@ -1,3 +1,4 @@ +import { expect, test } from 'vitest'; import { ratioToNumber } from '../aspect-ratio-dropdown'; test( 'ratioToNumber', () => { diff --git a/packages/block-editor/src/components/inserter/test/get-appender-label.js b/packages/block-editor/src/components/inserter/test/get-appender-label.js index 9e7c8479eca743..1beb4e67c24000 100644 --- a/packages/block-editor/src/components/inserter/test/get-appender-label.js +++ b/packages/block-editor/src/components/inserter/test/get-appender-label.js @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { getAppenderLabel } from '../get-appender-label'; describe( 'getAppenderLabel', () => { @@ -14,7 +15,7 @@ describe( 'getAppenderLabel', () => { it( 'should return null when defaultBlock.attributes is missing', () => { const defaultBlock = { name: 'core/test' }; const defaultBlockType = { - __experimentalLabel: jest.fn(), + __experimentalLabel: vi.fn(), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -52,7 +53,7 @@ describe( 'getAppenderLabel', () => { attributes: { type: 'page', kind: 'post-type' }, }; const defaultBlockType = { - __experimentalLabel: jest.fn( () => 'Add page' ), + __experimentalLabel: vi.fn( () => 'Add page' ), }; getAppenderLabel( defaultBlock, defaultBlockType ); @@ -70,7 +71,7 @@ describe( 'getAppenderLabel', () => { }; const fullLabel = 'Add page'; const defaultBlockType = { - __experimentalLabel: jest.fn( () => fullLabel ), + __experimentalLabel: vi.fn( () => fullLabel ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -84,7 +85,7 @@ describe( 'getAppenderLabel', () => { attributes: { foo: 'bar' }, }; const defaultBlockType = { - __experimentalLabel: jest.fn( () => 'This is a test' ), + __experimentalLabel: vi.fn( () => 'This is a test' ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -98,7 +99,7 @@ describe( 'getAppenderLabel', () => { attributes: { type: 'page' }, }; const defaultBlockType = { - __experimentalLabel: jest.fn( () => null ), + __experimentalLabel: vi.fn( () => null ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -112,7 +113,7 @@ describe( 'getAppenderLabel', () => { attributes: { type: 'page' }, }; const defaultBlockType = { - __experimentalLabel: jest.fn( () => undefined ), + __experimentalLabel: vi.fn( () => undefined ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -126,7 +127,7 @@ describe( 'getAppenderLabel', () => { attributes: { type: 'page' }, }; const defaultBlockType = { - __experimentalLabel: jest.fn( () => 123 ), + __experimentalLabel: vi.fn( () => 123 ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -140,7 +141,7 @@ describe( 'getAppenderLabel', () => { attributes: { type: 'page' }, }; const defaultBlockType = { - __experimentalLabel: jest.fn( () => ( { label: 'page' } ) ), + __experimentalLabel: vi.fn( () => ( { label: 'page' } ) ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -155,7 +156,7 @@ describe( 'getAppenderLabel', () => { }; const longLabel = 'a'.repeat( 50 ); const defaultBlockType = { - __experimentalLabel: jest.fn( () => longLabel ), + __experimentalLabel: vi.fn( () => longLabel ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -170,7 +171,7 @@ describe( 'getAppenderLabel', () => { }; const label49chars = 'a'.repeat( 49 ); const defaultBlockType = { - __experimentalLabel: jest.fn( () => label49chars ), + __experimentalLabel: vi.fn( () => label49chars ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); @@ -184,7 +185,7 @@ describe( 'getAppenderLabel', () => { attributes: { type: 'page' }, }; const defaultBlockType = { - __experimentalLabel: jest.fn( () => '' ), + __experimentalLabel: vi.fn( () => '' ), }; const result = getAppenderLabel( defaultBlock, defaultBlockType ); diff --git a/packages/block-editor/src/components/inserter/test/search-items.js b/packages/block-editor/src/components/inserter/test/search-items.js index 4f637c28ff6790..7a16fe5d24a426 100644 --- a/packages/block-editor/src/components/inserter/test/search-items.js +++ b/packages/block-editor/src/components/inserter/test/search-items.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import items, { categories, collections, diff --git a/packages/block-editor/src/components/link-control/test/is-url-like.js b/packages/block-editor/src/components/link-control/test/is-url-like.js index d4b26afb17c9da..f816eb81344403 100644 --- a/packages/block-editor/src/components/link-control/test/is-url-like.js +++ b/packages/block-editor/src/components/link-control/test/is-url-like.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import isURLLike, { isHashLink, isRelativePath } from '../is-url-like'; describe( 'isURLLike', () => { diff --git a/packages/block-editor/src/components/list-view/test/use-list-view-drop-zone.js b/packages/block-editor/src/components/list-view/test/use-list-view-drop-zone.js index 2ecf1d57258e9c..5cb3a9da012dcb 100644 --- a/packages/block-editor/src/components/list-view/test/use-list-view-drop-zone.js +++ b/packages/block-editor/src/components/list-view/test/use-list-view-drop-zone.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getListViewDropTarget, NESTING_LEVEL_INDENTATION, diff --git a/packages/block-editor/src/components/list-view/test/utils.js b/packages/block-editor/src/components/list-view/test/utils.js index c9b5c8d3ac5ea8..2ed93947dd727f 100644 --- a/packages/block-editor/src/components/list-view/test/utils.js +++ b/packages/block-editor/src/components/list-view/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getCommonDepthClientIds, getDragDisplacementValues } from '../utils'; describe( 'getCommonDepthClientIds', () => { diff --git a/packages/block-editor/src/components/media-placeholder/test/get-computed-accept-attribute.js b/packages/block-editor/src/components/media-placeholder/test/get-computed-accept-attribute.js index e2658953a2e163..e35f9536558a93 100644 --- a/packages/block-editor/src/components/media-placeholder/test/get-computed-accept-attribute.js +++ b/packages/block-editor/src/components/media-placeholder/test/get-computed-accept-attribute.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getComputedAcceptAttribute } from '../utils'; describe( 'getComputedAcceptAttribute', () => { diff --git a/packages/block-editor/src/components/preset-input-control/test/utils.js b/packages/block-editor/src/components/preset-input-control/test/utils.js index 17cff587b68c13..4a4161f69b2645 100644 --- a/packages/block-editor/src/components/preset-input-control/test/utils.js +++ b/packages/block-editor/src/components/preset-input-control/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isValuePreset, getPresetSlug, diff --git a/packages/block-editor/src/components/spacing-sizes-control/test/utils.js b/packages/block-editor/src/components/spacing-sizes-control/test/utils.js index 29a8d6b8d98d01..1c0217fe4a5c44 100644 --- a/packages/block-editor/src/components/spacing-sizes-control/test/utils.js +++ b/packages/block-editor/src/components/spacing-sizes-control/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { ALL_SIDES, getCustomValueFromPreset, diff --git a/packages/block-editor/src/components/use-block-drop-zone/test/index.js b/packages/block-editor/src/components/use-block-drop-zone/test/index.js index 4988ae478e506f..cfadf512572682 100644 --- a/packages/block-editor/src/components/use-block-drop-zone/test/index.js +++ b/packages/block-editor/src/components/use-block-drop-zone/test/index.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getDropTargetPosition } from '..'; const elementData = [ diff --git a/packages/block-editor/src/components/use-on-block-drop/test/index.js b/packages/block-editor/src/components/use-on-block-drop/test/index.js index b0b31fe7a6df33..2a5cf5c9bfdb37 100644 --- a/packages/block-editor/src/components/use-on-block-drop/test/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/test/index.js @@ -1,15 +1,14 @@ +import { describe, expect, it, vi } from 'vitest'; import { findTransform, pasteHandler } from '@wordpress/blocks'; import { parseDropEvent, onFilesDrop, onHTMLDrop, onBlockDrop } from '..'; const noop = () => {}; -jest.mock( '@wordpress/blocks/src/api/factory', () => ( { - findTransform: jest.fn(), - getBlockTransforms: jest.fn(), -} ) ); - -jest.mock( '@wordpress/blocks/src/api/raw-handling', () => ( { - pasteHandler: jest.fn(), +vi.mock( import( '@wordpress/blocks' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + findTransform: vi.fn(), + getBlockTransforms: vi.fn(), + pasteHandler: vi.fn(), } ) ); describe( 'parseDropEvent', () => { @@ -92,7 +91,7 @@ describe( 'onBlockDrop', () => { const targetBlockIndex = 0; const getBlockIndex = noop; const getClientIdsOfDescendants = noop; - const moveBlocks = jest.fn(); + const moveBlocks = vi.fn(); const event = { dataTransfer: { @@ -120,9 +119,9 @@ describe( 'onBlockDrop', () => { const targetRootClientId = '1'; const targetBlockIndex = 0; // Target and source block index is the same. - const getBlockIndex = jest.fn( () => targetBlockIndex ); + const getBlockIndex = vi.fn( () => targetBlockIndex ); const getClientIdsOfDescendants = noop; - const moveBlocks = jest.fn(); + const moveBlocks = vi.fn(); const event = { dataTransfer: { @@ -152,9 +151,9 @@ describe( 'onBlockDrop', () => { it( 'does nothing if the block is dropped as a child of itself', () => { const targetRootClientId = '1'; const targetBlockIndex = 0; - const getBlockIndex = jest.fn( () => 6 ); + const getBlockIndex = vi.fn( () => 6 ); const getClientIdsOfDescendants = noop; - const moveBlocks = jest.fn(); + const moveBlocks = vi.fn(); const event = { dataTransfer: { @@ -184,12 +183,10 @@ describe( 'onBlockDrop', () => { it( 'does nothing if the block is dropped as a descendant of itself', () => { const targetRootClientId = '1'; const targetBlockIndex = 0; - const getBlockIndex = jest.fn( () => 1 ); + const getBlockIndex = vi.fn( () => 1 ); // Dragged block is being dropped as a descendant of itself. - const getClientIdsOfDescendants = jest.fn( () => [ - targetRootClientId, - ] ); - const moveBlocks = jest.fn(); + const getClientIdsOfDescendants = vi.fn( () => [ targetRootClientId ] ); + const moveBlocks = vi.fn(); const event = { dataTransfer: { @@ -220,9 +217,9 @@ describe( 'onBlockDrop', () => { const sourceRootClientId = '0'; const targetRootClientId = '1'; const targetBlockIndex = 0; - const getBlockIndex = jest.fn( () => 1 ); + const getBlockIndex = vi.fn( () => 1 ); const getClientIdsOfDescendants = () => []; - const moveBlocks = jest.fn(); + const moveBlocks = vi.fn(); const event = { dataTransfer: { @@ -257,10 +254,10 @@ describe( 'onBlockDrop', () => { const sourceRootClientId = '0'; const targetRootClientId = sourceRootClientId; const targetBlockIndex = 5; - const getBlockIndex = jest.fn( () => 1 ); + const getBlockIndex = vi.fn( () => 1 ); // Dragged block is being dropped as a descendant of itself. const getClientIdsOfDescendants = () => []; - const moveBlocks = jest.fn(); + const moveBlocks = vi.fn(); const event = { dataTransfer: { @@ -296,11 +293,11 @@ describe( 'onBlockDrop', () => { describe( 'onFilesDrop', () => { it( 'does nothing if hasUploadPermissions is false', () => { - const updateBlockAttributes = jest.fn(); + const updateBlockAttributes = vi.fn(); const canInsertBlockType = noop; - const insertOrReplaceBlocks = jest.fn(); + const insertOrReplaceBlocks = vi.fn(); const targetRootClientId = '1'; - const getSettings = jest.fn( () => ( {} ) ); + const getSettings = vi.fn( () => ( {} ) ); const onFileDropHandler = onFilesDrop( targetRootClientId, @@ -320,10 +317,10 @@ describe( 'onFilesDrop', () => { // to have no return value. findTransform.mockImplementation( noop ); const updateBlockAttributes = noop; - const insertOrReplaceBlocks = jest.fn(); + const insertOrReplaceBlocks = vi.fn(); const canInsertBlockType = noop; const targetRootClientId = '1'; - const getSettings = jest.fn( () => ( { + const getSettings = vi.fn( () => ( { mediaUpload: true, } ) ); @@ -345,13 +342,13 @@ describe( 'onFilesDrop', () => { // of the transform isn't important just that there is a callable 'transform' // function that returns a value. const blocks = 'blocks'; - const transformation = { transform: jest.fn( () => blocks ) }; + const transformation = { transform: vi.fn( () => blocks ) }; findTransform.mockImplementation( () => transformation ); const updateBlockAttributes = noop; const canInsertBlockType = noop; - const insertOrReplaceBlocks = jest.fn(); + const insertOrReplaceBlocks = vi.fn(); const targetRootClientId = '1'; - const getSettings = jest.fn( () => ( { + const getSettings = vi.fn( () => ( { mediaUpload: true, } ) ); @@ -377,7 +374,7 @@ describe( 'onFilesDrop', () => { describe( 'onHTMLDrop', () => { it( 'does nothing if the HTML cannot be converted into blocks', () => { pasteHandler.mockImplementation( () => [] ); - const insertOrReplaceBlocks = jest.fn(); + const insertOrReplaceBlocks = vi.fn(); const eventHandler = onHTMLDrop( insertOrReplaceBlocks ); eventHandler(); @@ -388,7 +385,7 @@ describe( 'onHTMLDrop', () => { it( 'inserts blocks if the HTML can be converted into blocks', () => { const blocks = [ 'blocks' ]; pasteHandler.mockImplementation( () => blocks ); - const insertOrReplaceBlocks = jest.fn(); + const insertOrReplaceBlocks = vi.fn(); const eventHandler = onHTMLDrop( insertOrReplaceBlocks ); eventHandler(); diff --git a/packages/block-editor/src/hooks/test/allowed-blocks.js b/packages/block-editor/src/hooks/test/allowed-blocks.js index b9d5569b26afcd..1720c118f201f5 100644 --- a/packages/block-editor/src/hooks/test/allowed-blocks.js +++ b/packages/block-editor/src/hooks/test/allowed-blocks.js @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it } from 'vitest'; import { getBlockTypes, registerBlockType, diff --git a/packages/block-editor/src/hooks/test/gap.js b/packages/block-editor/src/hooks/test/gap.js index b45e994967e7bb..7026f3c36275c7 100644 --- a/packages/block-editor/src/hooks/test/gap.js +++ b/packages/block-editor/src/hooks/test/gap.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getGapCSSValue, getGapBoxControlValueFromStyle, diff --git a/packages/block-editor/src/hooks/test/generate-fields-from-attributes.js b/packages/block-editor/src/hooks/test/generate-fields-from-attributes.js index 30cb3ba668ad89..3a60088edf53b3 100644 --- a/packages/block-editor/src/hooks/test/generate-fields-from-attributes.js +++ b/packages/block-editor/src/hooks/test/generate-fields-from-attributes.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { generateFieldsFromAttributes } from '../generate-fields-from-attributes'; /** diff --git a/packages/block-editor/src/hooks/test/generated-class-name.js b/packages/block-editor/src/hooks/test/generated-class-name.js index 015bfd261a54a5..1a8ac9e8dd4a6d 100644 --- a/packages/block-editor/src/hooks/test/generated-class-name.js +++ b/packages/block-editor/src/hooks/test/generated-class-name.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { applyFilters } from '@wordpress/hooks'; import '../generated-class-name'; diff --git a/packages/block-editor/src/hooks/test/metadata.js b/packages/block-editor/src/hooks/test/metadata.js index 763125022dac9e..99e661fa6e116a 100644 --- a/packages/block-editor/src/hooks/test/metadata.js +++ b/packages/block-editor/src/hooks/test/metadata.js @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it } from 'vitest'; import { getBlockTypes, registerBlockType, diff --git a/packages/block-editor/src/hooks/test/preset-round-trip.js b/packages/block-editor/src/hooks/test/preset-round-trip.js index 9c4e9620a0cf9e..e4956b0470d62a 100644 --- a/packages/block-editor/src/hooks/test/preset-round-trip.js +++ b/packages/block-editor/src/hooks/test/preset-round-trip.js @@ -6,6 +6,8 @@ * preset references, clear the relevant reset surfaces, and treat explicit * empty values as non-contributing layers. */ + +import { describe, expect, it } from 'vitest'; import { getValueFromVariable, getPresetVariableFromValue, diff --git a/packages/block-editor/src/hooks/test/settings.jsx b/packages/block-editor/src/hooks/test/settings.jsx index 065af3a59a1b39..381e1aded4f56c 100644 --- a/packages/block-editor/src/hooks/test/settings.jsx +++ b/packages/block-editor/src/hooks/test/settings.jsx @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { applyFilters } from '@wordpress/hooks'; import '../settings'; diff --git a/packages/block-editor/src/hooks/test/state-utils.js b/packages/block-editor/src/hooks/test/state-utils.js index 5d7c128b84bf72..8bdcc09a537fc9 100644 --- a/packages/block-editor/src/hooks/test/state-utils.js +++ b/packages/block-editor/src/hooks/test/state-utils.js @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; import { getRelativeRootSelector, diff --git a/packages/block-editor/src/layouts/test/utils.js b/packages/block-editor/src/layouts/test/utils.js index 662f8686b12d55..d9d209c577fae6 100644 --- a/packages/block-editor/src/layouts/test/utils.js +++ b/packages/block-editor/src/layouts/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { appendSelectors, getBlockGapCSS } from '../utils'; const layoutDefinitions = { diff --git a/packages/block-editor/src/store/test/array.js b/packages/block-editor/src/store/test/array.js index e6bc5246549d4b..c55d26ae9d35d9 100644 --- a/packages/block-editor/src/store/test/array.js +++ b/packages/block-editor/src/store/test/array.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { insertAt, moveTo } from '../array'; describe( 'array', () => { diff --git a/packages/block-editor/src/store/test/get-block-settings.js b/packages/block-editor/src/store/test/get-block-settings.js index 714322d79c3063..943ff32b34929f 100644 --- a/packages/block-editor/src/store/test/get-block-settings.js +++ b/packages/block-editor/src/store/test/get-block-settings.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { addFilter, removeFilter } from '@wordpress/hooks'; import { getBlockSettings } from '../get-block-settings'; diff --git a/packages/block-editor/src/store/test/performance.js b/packages/block-editor/src/store/test/performance.js index 919089677e8ec3..c75e0891eb207b 100644 --- a/packages/block-editor/src/store/test/performance.js +++ b/packages/block-editor/src/store/test/performance.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import reducer from '../reducer'; describe( 'performance', () => { @@ -19,7 +20,7 @@ describe( 'performance', () => { blocks, } ); expect( preparedState ).toBeDefined(); - } ); + }, 30_000 ); it( 'should update blocks', () => { const updatedState = reducer( preparedState, { diff --git a/packages/block-editor/src/store/test/private-actions.js b/packages/block-editor/src/store/test/private-actions.js index 607163d7fe6fe4..92602b0c5dbee3 100644 --- a/packages/block-editor/src/store/test/private-actions.js +++ b/packages/block-editor/src/store/test/private-actions.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { hideBlockInterface, showBlockInterface, diff --git a/packages/block-editor/src/store/test/private-selectors.js b/packages/block-editor/src/store/test/private-selectors.js index a877d64f506364..40e93ab64c2ff2 100644 --- a/packages/block-editor/src/store/test/private-selectors.js +++ b/packages/block-editor/src/store/test/private-selectors.js @@ -1,3 +1,13 @@ +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; import { registerBlockType, unregisterBlockType, @@ -430,10 +440,10 @@ describe( 'private selectors', () => { derivedBlockEditingModes: new Map(), }; - const hasContentRoleAttribute = jest.fn( () => false ); - const get = jest.fn( () => 'edit' ); + const hasContentRoleAttribute = vi.fn( () => false ); + const get = vi.fn( () => 'edit' ); getBlockEditingMode.registry = { - select: jest.fn( () => ( { + select: vi.fn( () => ( { hasContentRoleAttribute, get, } ) ), diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index cabed1f108a00e..42848e60834017 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -1,3 +1,12 @@ +import { + afterAll, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; import deepFreeze from 'deep-freeze'; import { registerBlockType, @@ -40,18 +49,38 @@ import { sectionRootClientIdKey, isIsolatedEditorKey } from '.././private-keys'; const { isContentBlock } = unlock( privateApis ); -jest.mock( '@wordpress/data/src/select', () => { - const actualSelect = jest.requireActual( '@wordpress/data/src/select' ); +const { mockIsContentBlock } = vi.hoisted( () => ( { + mockIsContentBlock: vi.fn(), +} ) ); + +vi.mock( import( '@wordpress/data' ), async ( importOriginal ) => { + const actualData = await importOriginal(); return { - select: jest.fn( ( ...args ) => actualSelect.select( ...args ) ), + ...actualData, + select: vi.fn( ( ...args ) => actualData.select( ...args ) ), }; } ); -jest.mock( '@wordpress/blocks/src/api/utils', () => { +vi.mock( import( '../../lock-unlock' ), async ( importOriginal ) => { + const actualLockUnlock = await importOriginal(); + return { - ...jest.requireActual( '@wordpress/blocks/src/api/utils' ), - isContentBlock: jest.fn(), + ...actualLockUnlock, + unlock: ( value ) => { + const unlocked = actualLockUnlock.unlock( value ); + if ( + unlocked && + typeof unlocked === 'object' && + 'isContentBlock' in unlocked + ) { + return { + ...unlocked, + isContentBlock: mockIsContentBlock, + }; + } + return unlocked; + }, }; } ); @@ -3445,7 +3474,7 @@ describe( 'state', () => { describe( 'settings', () => { it( 'should warn about __unstableIsPreviewMode deprecation', () => { - const consoleWarn = jest + const consoleWarn = vi .spyOn( global.console, 'warn' ) .mockImplementation(); diff --git a/packages/block-editor/src/utils/test/block-bindings.js b/packages/block-editor/src/utils/test/block-bindings.js index b82fa449f5cb36..6235c1f8d60fe6 100644 --- a/packages/block-editor/src/utils/test/block-bindings.js +++ b/packages/block-editor/src/utils/test/block-bindings.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getBlockBindingsContext, hasPatternOverridesDefaultBinding, diff --git a/packages/block-editor/src/utils/test/color-values.js b/packages/block-editor/src/utils/test/color-values.js index 72c11d991c630f..cb3bf6f57643d1 100644 --- a/packages/block-editor/src/utils/test/color-values.js +++ b/packages/block-editor/src/utils/test/color-values.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { extractPresetSlug, encodeColorValueWithPalette, diff --git a/packages/block-editor/src/utils/test/format-font-style.js b/packages/block-editor/src/utils/test/format-font-style.js index c719f89996e7d8..9e55dc1145a54e 100644 --- a/packages/block-editor/src/utils/test/format-font-style.js +++ b/packages/block-editor/src/utils/test/format-font-style.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { formatFontStyle } from '../format-font-style'; describe( 'formatFontStyle', () => { diff --git a/packages/block-editor/src/utils/test/format-font-weight.js b/packages/block-editor/src/utils/test/format-font-weight.js index 4e734337cdfbea..d6661627abc7e4 100644 --- a/packages/block-editor/src/utils/test/format-font-weight.js +++ b/packages/block-editor/src/utils/test/format-font-weight.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { formatFontWeight } from '../format-font-weight'; describe( 'formatFontWeight', () => { diff --git a/packages/block-editor/src/utils/test/get-font-styles-and-weights.js b/packages/block-editor/src/utils/test/get-font-styles-and-weights.js index fbaad390ddaade..152d5ced9dae06 100644 --- a/packages/block-editor/src/utils/test/get-font-styles-and-weights.js +++ b/packages/block-editor/src/utils/test/get-font-styles-and-weights.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getFontStylesAndWeights } from '../get-font-styles-and-weights'; describe( 'getFontStylesAndWeights', () => { diff --git a/packages/block-editor/src/utils/test/math.js b/packages/block-editor/src/utils/test/math.js index 679a92889c71f8..3c817306d36109 100644 --- a/packages/block-editor/src/utils/test/math.js +++ b/packages/block-editor/src/utils/test/math.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getDistanceFromPointToEdge, getDistanceToNearestEdge } from '../math'; describe( 'getDistanceFromPointToEdge', () => { diff --git a/packages/block-editor/src/utils/test/object.js b/packages/block-editor/src/utils/test/object.js index edb970de8b24cd..5beb6219009daa 100644 --- a/packages/block-editor/src/utils/test/object.js +++ b/packages/block-editor/src/utils/test/object.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { setImmutably } from '../object'; describe( 'setImmutably', () => { diff --git a/packages/block-editor/src/utils/test/pasting.js b/packages/block-editor/src/utils/test/pasting.js index 92ee0d43297872..1ccb555a20e663 100644 --- a/packages/block-editor/src/utils/test/pasting.js +++ b/packages/block-editor/src/utils/test/pasting.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { shouldDismissPastedFiles } from '../pasting'; const mocks = { diff --git a/packages/block-editor/src/utils/test/selection.js b/packages/block-editor/src/utils/test/selection.js index 730d8e50d11be1..04b9fb95f4faa8 100644 --- a/packages/block-editor/src/utils/test/selection.js +++ b/packages/block-editor/src/utils/test/selection.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { retrieveSelectedAttribute, START_OF_SELECTED_AREA, diff --git a/packages/block-editor/src/utils/test/sorting.js b/packages/block-editor/src/utils/test/sorting.js index 1a26f23cbdfeaa..9c6b5fd264eabe 100644 --- a/packages/block-editor/src/utils/test/sorting.js +++ b/packages/block-editor/src/utils/test/sorting.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { orderBy } from '../sorting'; describe( 'orderBy', () => { diff --git a/packages/block-editor/src/utils/test/transform-styles.js b/packages/block-editor/src/utils/test/transform-styles.js index 8888d3d3959a82..9b6d736f5f200b 100644 --- a/packages/block-editor/src/utils/test/transform-styles.js +++ b/packages/block-editor/src/utils/test/transform-styles.js @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import transformStyles from '../transform-styles'; describe( 'transformStyles', () => { @@ -5,7 +6,7 @@ describe( 'transformStyles', () => { beforeEach( () => { // Intentionally suppress the expected console errors and warnings to reduce // noise in the test output. - jest.spyOn( console, 'warn' ).mockImplementation( jest.fn() ); + vi.spyOn( console, 'warn' ).mockImplementation( vi.fn() ); } ); it( 'should not throw error in case of invalid css', () => { diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 6e00ba18b0836f..d967dc0b86e87f 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -147,7 +147,8 @@ "@testing-library/user-event": "^14.6.1", "@wordpress/integration-tests": "file:../../test/integration", "@wordpress/stylelint-tools": "file:../../tools/stylelint", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/block-library/src/button/test/get-updated-link-attributes.js b/packages/block-library/src/button/test/get-updated-link-attributes.js index 461be6e2f0e81a..94ef708af7c0e0 100644 --- a/packages/block-library/src/button/test/get-updated-link-attributes.js +++ b/packages/block-library/src/button/test/get-updated-link-attributes.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getUpdatedLinkAttributes } from '../get-updated-link-attributes'; describe( 'getUpdatedLinkAttributes method', () => { diff --git a/packages/block-library/src/button/test/utils.js b/packages/block-library/src/button/test/utils.js index ec91007827c974..010b37d347a0cd 100644 --- a/packages/block-library/src/button/test/utils.js +++ b/packages/block-library/src/button/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getWidthClasses, isPercentageWidth } from '../utils'; describe( 'isPercentageWidth', () => { diff --git a/packages/block-library/src/code/test/utils.js b/packages/block-library/src/code/test/utils.js index 4ded013baa0d5d..53b2f7cb02e81e 100644 --- a/packages/block-library/src/code/test/utils.js +++ b/packages/block-library/src/code/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { escape } from '../utils'; describe( 'core/code', () => { diff --git a/packages/block-library/src/columns/test/utils.js b/packages/block-library/src/columns/test/utils.js index 778cd029382ca6..a90c0c1414fc91 100644 --- a/packages/block-library/src/columns/test/utils.js +++ b/packages/block-library/src/columns/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { toWidthPrecision, diff --git a/packages/block-library/src/comment-template/test/index.js b/packages/block-library/src/comment-template/test/index.js index 29de9f916ee49d..85cca738a67a79 100644 --- a/packages/block-library/src/comment-template/test/index.js +++ b/packages/block-library/src/comment-template/test/index.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { convertToTree } from '../util'; describe( 'Convert to tree', () => { diff --git a/packages/block-library/src/gallery/test/dynamic-source.js b/packages/block-library/src/gallery/test/dynamic-source.js index 13c342eb9762d9..02002fee716988 100644 --- a/packages/block-library/src/gallery/test/dynamic-source.js +++ b/packages/block-library/src/gallery/test/dynamic-source.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getSourceQuery } from '../dynamic-source'; describe( 'getSourceQuery', () => { diff --git a/packages/block-library/src/gallery/test/responsive-styles.js b/packages/block-library/src/gallery/test/responsive-styles.js index e258eaf1858b9d..605155960fe740 100644 --- a/packages/block-library/src/gallery/test/responsive-styles.js +++ b/packages/block-library/src/gallery/test/responsive-styles.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getGalleryResponsiveFlexCSS, getUpdatedGalleryStyle, diff --git a/packages/block-library/src/gallery/test/shared.js b/packages/block-library/src/gallery/test/shared.js index 09849bd2e971f8..bf8b490da610da 100644 --- a/packages/block-library/src/gallery/test/shared.js +++ b/packages/block-library/src/gallery/test/shared.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isGalleryFlexLayout } from '../shared'; describe( 'isGalleryFlexLayout', () => { diff --git a/packages/block-library/src/gallery/test/variations.js b/packages/block-library/src/gallery/test/variations.js index dbceb220119fcf..03afefa9aeb0f4 100644 --- a/packages/block-library/src/gallery/test/variations.js +++ b/packages/block-library/src/gallery/test/variations.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import variations from '../variations'; const getVariation = ( name ) => diff --git a/packages/block-library/src/heading/test/shared.js b/packages/block-library/src/heading/test/shared.js index e254466a59cade..0c7ea8fd18564d 100644 --- a/packages/block-library/src/heading/test/shared.js +++ b/packages/block-library/src/heading/test/shared.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getLevelFromHeadingNodeName } from '../shared'; describe( 'getLevelFromHeadingNodeName()', () => { diff --git a/packages/block-library/src/media-text/test/image-fill.js b/packages/block-library/src/media-text/test/image-fill.js index 3bb9371f4afc64..1f0acb7f6d0d39 100644 --- a/packages/block-library/src/media-text/test/image-fill.js +++ b/packages/block-library/src/media-text/test/image-fill.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { imageFillStyles } from '../image-fill'; describe( 'imageFillStyles()', () => { diff --git a/packages/block-library/src/navigation-link/shared/test/update-attributes.test.js b/packages/block-library/src/navigation-link/shared/test/update-attributes.test.js index 477abe0afd4ea9..b894fc0ff58a76 100644 --- a/packages/block-library/src/navigation-link/shared/test/update-attributes.test.js +++ b/packages/block-library/src/navigation-link/shared/test/update-attributes.test.js @@ -1,10 +1,11 @@ +import { describe, expect, it, vi } from 'vitest'; import { updateAttributes } from '../update-attributes'; describe( 'updateAttributes', () => { // Data shapes are linked to fetchLinkSuggestions from // core-data/src/fetch/__experimental-fetch-link-suggestions.js. it( 'can update a post link', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { opensInNewTab: false, id: 1337, @@ -26,7 +27,7 @@ describe( 'updateAttributes', () => { } ); it( 'can update a page link', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 2, kind: 'post-type', @@ -47,7 +48,7 @@ describe( 'updateAttributes', () => { } ); it( 'can update a tag link', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 15, kind: 'taxonomy', @@ -68,7 +69,7 @@ describe( 'updateAttributes', () => { } ); it( 'can update a category link', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 9, kind: 'taxonomy', @@ -89,7 +90,7 @@ describe( 'updateAttributes', () => { } ); it( 'can update a custom post type link', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 131, kind: 'post-type', @@ -110,7 +111,7 @@ describe( 'updateAttributes', () => { } ); it( 'can update a custom tag link', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 4, kind: 'taxonomy', @@ -131,7 +132,7 @@ describe( 'updateAttributes', () => { } ); it( 'can update a custom category link', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 2, kind: 'taxonomy', @@ -152,7 +153,7 @@ describe( 'updateAttributes', () => { } ); it( 'can update a post format and ignores id slug', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'video', kind: 'taxonomy', @@ -175,7 +176,7 @@ describe( 'updateAttributes', () => { describe( 'various link protocols save as custom links', () => { it( 'when typing a url, but not selecting a search suggestion', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { opensInNewTab: false, url: 'www.wordpress.org', @@ -190,7 +191,7 @@ describe( 'updateAttributes', () => { } ); it( 'url', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'www.wordpress.org', opensInNewTab: false, @@ -208,7 +209,7 @@ describe( 'updateAttributes', () => { } ); it( 'email', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'mailto:foo@example.com', opensInNewTab: false, @@ -227,7 +228,7 @@ describe( 'updateAttributes', () => { } ); it( 'anchor links (internal links)', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: '#foo', opensInNewTab: false, @@ -246,7 +247,7 @@ describe( 'updateAttributes', () => { } ); it( 'telephone', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'tel:5555555', opensInNewTab: false, @@ -268,7 +269,7 @@ describe( 'updateAttributes', () => { describe( 'link label', () => { // https://github.com/WordPress/gutenberg/pull/19461 it( 'sets the url as a label if title is not provided', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'www.wordpress.org/foo bar', opensInNewTab: false, @@ -285,7 +286,7 @@ describe( 'updateAttributes', () => { } ); } ); it( 'does not replace label when editing url without protocol', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'www.wordpress.org', opensInNewTab: false, @@ -302,7 +303,7 @@ describe( 'updateAttributes', () => { } ); } ); it( 'does not replace label when editing url with protocol', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'www.wordpress.org', opensInNewTab: false, @@ -320,7 +321,7 @@ describe( 'updateAttributes', () => { } ); // https://github.com/WordPress/gutenberg/pull/19679 it( 'url when escaped is still an actual link', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'http://wordpress.org/?s=', opensInNewTab: false, @@ -341,7 +342,7 @@ describe( 'updateAttributes', () => { describe( 'does not overwrite props when only some props are passed', () => { it( 'id is retained after toggling opensInNewTab', () => { const mockState = {}; - const setAttributes = jest.fn( ( attr ) => + const setAttributes = vi.fn( ( attr ) => Object.assign( mockState, attr ) ); const linkSuggestion = { @@ -382,7 +383,7 @@ describe( 'updateAttributes', () => { } ); it( 'id is removed after editing url', () => { const mockState = {}; - const setAttributes = jest.fn( ( attr ) => + const setAttributes = vi.fn( ( attr ) => Object.assign( mockState, attr ) ); const linkSuggestion = { @@ -427,7 +428,7 @@ describe( 'updateAttributes', () => { describe( 'ID handling when URL is manually changed', () => { describe( 'URL modifications that should sever the entity link', () => { it( 'should remove ID when URL path is changed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -456,7 +457,7 @@ describe( 'updateAttributes', () => { } ); it( 'should remove ID when changing to relative URL that does not match the path', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -485,7 +486,7 @@ describe( 'updateAttributes', () => { } ); it( 'should remove ID when URL domain is changed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -514,7 +515,7 @@ describe( 'updateAttributes', () => { } ); it( 'should remove ID when plain permalink post ID is changed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -543,7 +544,7 @@ describe( 'updateAttributes', () => { } ); it( 'should remove ID when changing from plain permalink to different plain permalink', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -574,7 +575,7 @@ describe( 'updateAttributes', () => { describe( 'URL modifications that should preserve the entity link', () => { it( 'should preserve ID when changing to relative URL that matches the path', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -606,7 +607,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when only query string is added', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -637,7 +638,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when only hash fragment is added', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -668,7 +669,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when both query string and hash are added', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -699,7 +700,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when query string is modified', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -730,7 +731,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when hash fragment is modified', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -761,7 +762,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when protocol changes from http to https', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -792,7 +793,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when protocol changes from https to http', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -823,7 +824,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when query string is removed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -854,7 +855,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when hash fragment is removed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -885,7 +886,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when both query string and hash are removed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -916,7 +917,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when adding query string to URL with hash fragment', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -947,7 +948,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when query string is partially removed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -979,7 +980,7 @@ describe( 'updateAttributes', () => { } ); it( 'should remove ID when URL is changed without new ID', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -1004,7 +1005,7 @@ describe( 'updateAttributes', () => { } ); it( 'should preserve ID when new ID is provided with URL', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -1028,7 +1029,7 @@ describe( 'updateAttributes', () => { } ); it( 'should not remove ID when only label is changed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -1052,7 +1053,7 @@ describe( 'updateAttributes', () => { } ); it( 'should not remove ID when only opensInNewTab is changed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -1076,7 +1077,7 @@ describe( 'updateAttributes', () => { } ); it( 'should handle case where block has no existing ID', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { type: 'page', kind: 'post-type', @@ -1103,7 +1104,7 @@ describe( 'updateAttributes', () => { } ); it( 'should handle non-integer ID values', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 'not-an-integer', type: 'page', @@ -1134,7 +1135,7 @@ describe( 'updateAttributes', () => { describe( 'Return value metadata', () => { describe( 'isEntityLink', () => { it( 'should return true for entity links with id and non-custom kind', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 123, kind: 'post-type', @@ -1155,7 +1156,7 @@ describe( 'updateAttributes', () => { } ); it( 'should return false for custom links even with id', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 123, kind: 'custom', @@ -1176,7 +1177,7 @@ describe( 'updateAttributes', () => { } ); it( 'should return false for links without id', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { url: 'https://example.com', title: 'Example', @@ -1194,7 +1195,7 @@ describe( 'updateAttributes', () => { } ); it( 'should return false when entity link is severed', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -1220,7 +1221,7 @@ describe( 'updateAttributes', () => { } ); it( 'should return true when entity link is preserved through query string change', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const blockAttributes = { id: 123, type: 'page', @@ -1246,7 +1247,7 @@ describe( 'updateAttributes', () => { } ); it( 'should return false for mailto links', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'mailto:test@example.com', type: 'mailto', @@ -1267,7 +1268,7 @@ describe( 'updateAttributes', () => { } ); it( 'should return false for tel links', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 'tel:5555555', type: 'tel', @@ -1288,7 +1289,7 @@ describe( 'updateAttributes', () => { } ); it( 'should return true for taxonomy links', () => { - const setAttributes = jest.fn(); + const setAttributes = vi.fn(); const linkSuggestion = { id: 5, kind: 'taxonomy', diff --git a/packages/block-library/src/navigation-link/test/block-labelling.js b/packages/block-library/src/navigation-link/test/block-labelling.js index 531904a1389910..2bb04dd21ec22b 100644 --- a/packages/block-library/src/navigation-link/test/block-labelling.js +++ b/packages/block-library/src/navigation-link/test/block-labelling.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { _x, sprintf } from '@wordpress/i18n'; /** diff --git a/packages/block-library/src/navigation/edit/test/are-blocks-dirty.js b/packages/block-library/src/navigation/edit/test/are-blocks-dirty.js index 39c93fd3a8e431..252b8209d9ae5d 100644 --- a/packages/block-library/src/navigation/edit/test/are-blocks-dirty.js +++ b/packages/block-library/src/navigation/edit/test/are-blocks-dirty.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { areBlocksDirty } from '../are-blocks-dirty'; describe( 'areBlocksDirty', () => { diff --git a/packages/block-library/src/navigation/edit/test/utils.js b/packages/block-library/src/navigation/edit/test/utils.js index 59e1f0d094190c..7675272281d33e 100644 --- a/packages/block-library/src/navigation/edit/test/utils.js +++ b/packages/block-library/src/navigation/edit/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getUniqueTemplatePartTitle, getCleanTemplatePartSlug } from '../utils'; describe( 'getUniqueTemplatePartTitle', () => { diff --git a/packages/block-library/src/pattern/test/index.js b/packages/block-library/src/pattern/test/index.js index e57b3926ac6164..c58936435dc84a 100644 --- a/packages/block-library/src/pattern/test/index.js +++ b/packages/block-library/src/pattern/test/index.js @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from 'vitest'; import { parsePatternDependencies, registerDependency, diff --git a/packages/block-library/src/table/test/state.js b/packages/block-library/src/table/test/state.js index b5be166d7c2813..95895cd23931a4 100644 --- a/packages/block-library/src/table/test/state.js +++ b/packages/block-library/src/table/test/state.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { createTable, diff --git a/packages/block-library/src/table/test/utils.js b/packages/block-library/src/table/test/utils.js index 67802f654ea9d1..c06c4d2305bac9 100644 --- a/packages/block-library/src/table/test/utils.js +++ b/packages/block-library/src/table/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { normalizeRowColSpan } from '../utils'; describe( 'normalizeRowColSpan', () => { diff --git a/packages/block-library/src/utils/test/search-patterns.js b/packages/block-library/src/utils/test/search-patterns.js index 63736135f8dcf0..9734a217b94588 100644 --- a/packages/block-library/src/utils/test/search-patterns.js +++ b/packages/block-library/src/utils/test/search-patterns.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { normalizeSearchInput, getPatternSearchRank, diff --git a/packages/block-library/src/video/test/variations.js b/packages/block-library/src/video/test/variations.js index d57d1c7b1ab797..10451903c8965f 100644 --- a/packages/block-library/src/video/test/variations.js +++ b/packages/block-library/src/video/test/variations.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import variations, { isGifVariation } from '../variations'; const GIF_ATTRIBUTES = { diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index 78dc7ee925dad9..8579e8f8560c87 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -44,7 +44,8 @@ "types": "build-types", "sideEffects": false, "devDependencies": { - "@wordpress/block-serialization-spec-parser": "file:../block-serialization-spec-parser" + "@wordpress/block-serialization-spec-parser": "file:../block-serialization-spec-parser", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/block-serialization-default-parser/test/index.js b/packages/block-serialization-default-parser/test/index.js index d02ba235933f21..a9b8f6983de42a 100644 --- a/packages/block-serialization-default-parser/test/index.js +++ b/packages/block-serialization-default-parser/test/index.js @@ -1,13 +1,20 @@ -import path from 'path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, test } from 'vitest'; import { jsTester, phpTester, } from '@wordpress/block-serialization-spec-parser/shared-tests'; import { parse } from '../src'; -describe( 'block-serialization-default-parser-js', jsTester( parse ) ); // eslint-disable-line jest/valid-describe-callback +const testRunner = { describe, expect, test }; + +describe( + 'block-serialization-default-parser-js', + jsTester( parse, testRunner ) +); phpTester( 'block-serialization-default-parser-php', - path.join( __dirname, 'test-parser.php' ) + fileURLToPath( new URL( './test-parser.php', import.meta.url ) ), + testRunner ); diff --git a/packages/block-serialization-spec-parser/package.json b/packages/block-serialization-spec-parser/package.json index 6dcbaa2157ffc2..d0bc6865c47a2d 100644 --- a/packages/block-serialization-spec-parser/package.json +++ b/packages/block-serialization-spec-parser/package.json @@ -36,6 +36,9 @@ "pegjs": "^0.10.0", "phpegjs": "^1.0.0-beta7" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" }, diff --git a/packages/block-serialization-spec-parser/shared-tests.js b/packages/block-serialization-spec-parser/shared-tests.js index 60b743203da254..f5a8e2a56c3a75 100644 --- a/packages/block-serialization-spec-parser/shared-tests.js +++ b/packages/block-serialization-spec-parser/shared-tests.js @@ -1,4 +1,8 @@ -export const jsTester = ( parse ) => () => { +import { spawnSync } from 'node:child_process'; + +export const jsTester = ( parse, runner ) => () => { + runner ??= globalThis; + const { describe, expect, test } = runner; describe( 'output structure', () => { test( 'output is an array', () => { expect( parse( '' ) ).toEqual( expect.any( Array ) ); @@ -196,7 +200,9 @@ export const jsTester = ( parse ) => () => { '

Break me

', ].forEach( ( input ) => expect( parse( input ) ).toEqual( [ - expect.objectContaining( { innerHTML: '

Break me

' } ), + expect.objectContaining( { + innerHTML: '

Break me

', + } ), expect.objectContaining( { blockName: 'core/block', innerHTML: '', @@ -233,7 +239,9 @@ export const jsTester = ( parse ) => () => { blockName: 'core/block', innerHTML: '', } ), - expect.objectContaining( { innerHTML: '

Break me

' } ), + expect.objectContaining( { + innerHTML: '

Break me

', + } ), ] ) ) ); } ); @@ -319,44 +327,39 @@ export const jsTester = ( parse ) => () => { const hasPHP = 'test' === process.env.NODE_ENV ? ( () => { - const process = require( 'child_process' ).spawnSync( - 'php', - [ '-r', 'echo 1;' ], - { - encoding: 'utf8', - } - ); + const phpProcess = spawnSync( 'php', [ '-r', 'echo 1;' ], { + encoding: 'utf8', + } ); - return process.status === 0 && process.stdout === '1'; + return phpProcess.status === 0 && phpProcess.stdout === '1'; } )() : false; // Skipping if `php` isn't available to us, such as in local dev without it // skipping preserves snapshots while commenting out or simply -// not injecting the tests prompts `jest` to remove "obsolete snapshots" -const makeTest = hasPHP - ? // eslint-disable-next-line jest/valid-describe-callback, jest/valid-title - ( ...args ) => describe( ...args ) - : // eslint-disable-next-line jest/no-disabled-tests, jest/valid-describe-callback, jest/valid-title - ( ...args ) => describe.skip( ...args ); +// not injecting the tests can prompt snapshot-aware runners to remove +// "obsolete snapshots" +const makeTest = ( describe ) => + hasPHP + ? ( ...args ) => describe( ...args ) + : ( ...args ) => describe.skip( ...args ); -export const phpTester = ( name, filename ) => - makeTest( +export const phpTester = ( name, filename, testRunner = globalThis ) => { + const { describe } = testRunner; + return makeTest( describe )( name, 'test' === process.env.NODE_ENV ? jsTester( ( doc ) => { - const process = require( 'child_process' ).spawnSync( - 'php', - [ '-f', filename ], - { - input: doc, - encoding: 'utf8', - timeout: 30 * 1000, // Abort after 30 seconds, that's too long anyway. - } - ); + const phpProcess = spawnSync( 'php', [ '-f', filename ], { + input: doc, + encoding: 'utf8', + timeout: 30 * 1000, // Abort after 30 seconds, that's too long anyway. + } ); - if ( process.status !== 0 ) { - throw new Error( process.stderr || process.stdout ); + if ( phpProcess.status !== 0 ) { + throw new Error( + phpProcess.stderr || phpProcess.stdout + ); } try { @@ -367,16 +370,17 @@ export const phpTester = ( name, filename ) => * This is an issue with the test runner, not with the parser. */ return JSON.parse( - process.stdout.replace( + phpProcess.stdout.replace( /"attrs":\s*\[\]/g, '"attrs":{}' ) ); } catch { throw new Error( - 'failed to parse JSON:\n' + process.stdout + 'failed to parse JSON:\n' + phpProcess.stdout ); } - } ) + }, testRunner ) : () => {} ); +}; diff --git a/packages/block-serialization-spec-parser/test/index.js b/packages/block-serialization-spec-parser/test/index.js index e327c45178ebc7..1d07e528de7b2f 100644 --- a/packages/block-serialization-spec-parser/test/index.js +++ b/packages/block-serialization-spec-parser/test/index.js @@ -1,10 +1,15 @@ -import path from 'path'; -import { parse } from '../'; +import { createRequire } from 'node:module'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, test } from 'vitest'; import { jsTester, phpTester } from '../shared-tests'; +const require = createRequire( import.meta.url ); +const { parse } = require( '../parser.js' ); +const testRunner = { describe, expect, test }; -describe( 'block-serialization-spec-parser-js', jsTester( parse ) ); // eslint-disable-line jest/valid-describe-callback +describe( 'block-serialization-spec-parser-js', jsTester( parse, testRunner ) ); phpTester( 'block-serialization-spec-parser-php', - path.join( __dirname, 'test-parser.php' ) + fileURLToPath( new URL( './test-parser.php', import.meta.url ) ), + testRunner ); diff --git a/packages/blocks/package.json b/packages/blocks/package.json index fdd9be815dc38a..389cc6b72852e7 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -80,7 +80,8 @@ }, "devDependencies": { "@types/react-is": "^18.3.1", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/blocks/src/api/parser/test/convert-legacy-block.js b/packages/blocks/src/api/parser/test/convert-legacy-block.js index 37cadc402ea35e..c74451007cc14d 100644 --- a/packages/blocks/src/api/parser/test/convert-legacy-block.js +++ b/packages/blocks/src/api/parser/test/convert-legacy-block.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { normalizeStyleStateAliases } from '../convert-legacy-block'; describe( 'normalizeStyleStateAliases', () => { diff --git a/packages/blocks/src/api/parser/test/serialize-raw-block.js b/packages/blocks/src/api/parser/test/serialize-raw-block.js index 7ae5f8633c8936..296f032a6581b1 100644 --- a/packages/blocks/src/api/parser/test/serialize-raw-block.js +++ b/packages/blocks/src/api/parser/test/serialize-raw-block.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { serializeRawBlock } from '../serialize-raw-block'; describe( 'serializeRawBlock', () => { diff --git a/packages/blocks/src/api/raw-handling/test/latex-to-math.js b/packages/blocks/src/api/raw-handling/test/latex-to-math.js index a62e52d5887138..a1c55d9730116b 100644 --- a/packages/blocks/src/api/raw-handling/test/latex-to-math.js +++ b/packages/blocks/src/api/raw-handling/test/latex-to-math.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import isLatexMathMode from '../latex-to-math'; describe( 'isLatexMathMode', () => { diff --git a/packages/blocks/src/api/raw-handling/test/markdown-converter.js b/packages/blocks/src/api/raw-handling/test/markdown-converter.js index ee44e022cd6a62..f1fc09c6239c87 100644 --- a/packages/blocks/src/api/raw-handling/test/markdown-converter.js +++ b/packages/blocks/src/api/raw-handling/test/markdown-converter.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import markdownConverter from '../markdown-converter'; describe( 'markdownConverter', () => { diff --git a/packages/blocks/src/api/test/factory.js b/packages/blocks/src/api/test/factory.js index 6367da92f48b60..a2462f999e7ad0 100644 --- a/packages/blocks/src/api/test/factory.js +++ b/packages/blocks/src/api/test/factory.js @@ -1,4 +1,6 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import deepFreeze from 'deep-freeze'; +import '../../store'; import { createBlock, createBlocksFromInnerBlocksTemplate, @@ -37,11 +39,6 @@ describe( 'block factory', () => { title: 'block title', }; - beforeAll( () => { - // Load blocks store. - require( '../../store' ); - } ); - beforeEach( () => { // Reset warning logging so deduped warnings fire within each test. warningLoggedSet.clear(); @@ -1079,7 +1076,7 @@ describe( 'block factory', () => { } ); it( 'for a non multiblock transform, the isMatch function receives the source block’s attributes object and the block object as its arguments', () => { - const isMatch = jest.fn(); + const isMatch = vi.fn(); registerBlockType( 'core/updated-text-block', { apiVersion: 3, @@ -1114,7 +1111,7 @@ describe( 'block factory', () => { } ); it( 'for a multiblock transform, the isMatch function receives an array containing every source block’s attributes and an array of source blocks as its arguments', () => { - const isMatch = jest.fn(); + const isMatch = vi.fn(); registerBlockType( 'core/updated-text-block', { apiVersion: 3, @@ -2003,7 +2000,7 @@ describe( 'block factory', () => { } ); it( 'should call "__experimentalConvert" with mixed block types and wildcard', () => { - const convertSpy = jest.fn( ( blocks ) => { + const convertSpy = vi.fn( ( blocks ) => { const groupInnerBlocks = blocks.map( ( { name, attributes, innerBlocks } ) => { return createBlock( name, attributes, innerBlocks ); @@ -2016,7 +2013,7 @@ describe( 'block factory', () => { groupInnerBlocks ); } ); - const transformSpy = jest.fn(); + const transformSpy = vi.fn(); registerBlockType( 'core/test-group-block', { apiVersion: 3, @@ -2067,7 +2064,7 @@ describe( 'block factory', () => { } ); it( 'should call "__experimentalConvert" with same block types', () => { - const convertSpy = jest.fn( ( blocks ) => { + const convertSpy = vi.fn( ( blocks ) => { const groupInnerBlocks = blocks.map( ( { name, attributes, innerBlocks } ) => { return createBlock( name, attributes, innerBlocks ); @@ -2080,7 +2077,7 @@ describe( 'block factory', () => { groupInnerBlocks ); } ); - const transformSpy = jest.fn(); + const transformSpy = vi.fn(); registerBlockType( 'core/test-group-block', { apiVersion: 3, @@ -2128,7 +2125,7 @@ describe( 'block factory', () => { } ); it( 'should not call "__experimentalConvert" with non-matching block types', () => { - const convertSpy = jest.fn( ( blocks ) => { + const convertSpy = vi.fn( ( blocks ) => { const groupInnerBlocks = blocks.map( ( { name, attributes, innerBlocks } ) => { return createBlock( name, attributes, innerBlocks ); @@ -2141,7 +2138,7 @@ describe( 'block factory', () => { groupInnerBlocks ); } ); - const transformSpy = jest.fn(); + const transformSpy = vi.fn(); registerBlockType( 'core/test-group-block', { apiVersion: 3, @@ -2189,7 +2186,7 @@ describe( 'block factory', () => { } ); it( 'should prefer "__experimentalConvert" method over "transform" method when running a transformation', () => { - const convertSpy = jest.fn( ( blocks ) => { + const convertSpy = vi.fn( ( blocks ) => { const groupInnerBlocks = blocks.map( ( { name, attributes, innerBlocks } ) => { return createBlock( name, attributes, innerBlocks ); @@ -2202,7 +2199,7 @@ describe( 'block factory', () => { groupInnerBlocks ); } ); - const transformSpy = jest.fn(); + const transformSpy = vi.fn(); registerBlockType( 'core/test-group-block', { apiVersion: 3, diff --git a/packages/blocks/src/api/test/registration.jsx b/packages/blocks/src/api/test/registration.jsx index 623a86eb1b526b..467accee008794 100644 --- a/packages/blocks/src/api/test/registration.jsx +++ b/packages/blocks/src/api/test/registration.jsx @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it, test } from 'vitest'; import { addFilter, removeAllFilters, removeFilter } from '@wordpress/hooks'; import { logged } from '@wordpress/deprecated'; import { select, dispatch } from '@wordpress/data'; @@ -862,7 +863,6 @@ describe( 'blocks', () => { // Verify that for deprecations, the filter is called with a merge of pre-filter // settings with deprecation keys omitted and the deprecation entry. if ( i > 0 ) { - // eslint-disable-next-line jest/no-conditional-expect expect( settings ).toEqual( { ...omit( { diff --git a/packages/blocks/src/api/test/serializer.jsx b/packages/blocks/src/api/test/serializer.jsx index 23eb9d32622e58..8cb2ff1b0bf9ee 100644 --- a/packages/blocks/src/api/test/serializer.jsx +++ b/packages/blocks/src/api/test/serializer.jsx @@ -1,4 +1,6 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { createElement, Component } from '@wordpress/element'; +import '../../store'; import serialize, { getCommentAttributes, getSaveContent, @@ -17,11 +19,6 @@ import { import { createBlock } from '../'; describe( 'block serializer', () => { - beforeAll( () => { - // Initialize the block store. - require( '../../store' ); - } ); - afterEach( () => { setFreeformContentHandlerName( undefined ); setUnregisteredTypeHandlerName( undefined ); diff --git a/packages/blocks/src/api/test/templates.js b/packages/blocks/src/api/test/templates.js index a7c105acd4634c..8d36dbd1affb1f 100644 --- a/packages/blocks/src/api/test/templates.js +++ b/packages/blocks/src/api/test/templates.js @@ -1,3 +1,5 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import '../../store'; import { createBlock } from '../factory'; import { getBlockTypes, @@ -12,11 +14,6 @@ import { const noop = () => {}; describe( 'templates', () => { - beforeAll( () => { - // Initialize the block store. - require( '../../store' ); - } ); - afterEach( () => { getBlockTypes().forEach( ( block ) => { unregisterBlockType( block.name ); diff --git a/packages/blocks/src/store/test/actions.js b/packages/blocks/src/store/test/actions.js index 51281998d4cae0..1984c7352c2c18 100644 --- a/packages/blocks/src/store/test/actions.js +++ b/packages/blocks/src/store/test/actions.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { addBlockVariations, removeBlockVariations } from '../actions'; describe( 'actions', () => { diff --git a/packages/blocks/src/store/test/private-selectors.js b/packages/blocks/src/store/test/private-selectors.js index fcdfca498225b0..3402956be612bc 100644 --- a/packages/blocks/src/store/test/private-selectors.js +++ b/packages/blocks/src/store/test/private-selectors.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { getBlockKeyboardShortcuts, diff --git a/packages/blocks/src/store/test/reducer.js b/packages/blocks/src/store/test/reducer.js index d6efbf08d9f21c..85b92b1d7da58e 100644 --- a/packages/blocks/src/store/test/reducer.js +++ b/packages/blocks/src/store/test/reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { addBlockVariations, diff --git a/packages/browserslist-config/package.json b/packages/browserslist-config/package.json index 2722b169e44256..fe3699c108371a 100644 --- a/packages/browserslist-config/package.json +++ b/packages/browserslist-config/package.json @@ -32,7 +32,8 @@ "./package.json": "./package.json" }, "devDependencies": { - "browserslist": "^4.28.4" + "browserslist": "^4.28.4", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/browserslist-config/test/index.test.js b/packages/browserslist-config/test/index.test.js index 609d32b5b5b634..ec2d63cf867fbe 100644 --- a/packages/browserslist-config/test/index.test.js +++ b/packages/browserslist-config/test/index.test.js @@ -1,4 +1,5 @@ import browserslist from 'browserslist'; +import { expect, it } from 'vitest'; import config from '../'; it( 'should export an array', () => { diff --git a/packages/components/package.json b/packages/components/package.json index 6803d8125db505..aa0f1b3fab6fb4 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -109,13 +109,11 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@types/react-dom": "^18.3.1", - "@wordpress/jest-console": "file:../jest-console", "@wordpress/stylelint-tools": "file:../../tools/stylelint", - "snapshot-diff": "^0.10.0", "storybook": "^10.5.3", - "timezone-mock": "^1.3.6" + "timezone-mock": "^1.3.6", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/components/src/autocomplete/test/get-autocomplete-match.ts b/packages/components/src/autocomplete/test/get-autocomplete-match.ts index 0cb1c5265514a1..a01cec6232a3d0 100644 --- a/packages/components/src/autocomplete/test/get-autocomplete-match.ts +++ b/packages/components/src/autocomplete/test/get-autocomplete-match.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { getAutocompleteMatch } from '../get-autocomplete-match'; import type { WPCompleter } from '../types'; @@ -174,7 +175,7 @@ describe( 'getAutocompleteMatch', () => { } ); it( 'should pass correct before/after text to allowContext', () => { - const allowContext = jest.fn().mockReturnValue( true ); + const allowContext = vi.fn().mockReturnValue( true ); const completers = [ createCompleter( { triggerPrefix: '@', diff --git a/packages/components/src/border-box-control/test/utils.ts b/packages/components/src/border-box-control/test/utils.ts index daa70500368e81..0f27a616d1f5ad 100644 --- a/packages/components/src/border-box-control/test/utils.ts +++ b/packages/components/src/border-box-control/test/utils.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getBorderDiff, getCommonBorder, @@ -44,12 +45,14 @@ describe( 'BorderBoxControl Utils', () => { describe( 'isEmptyBorder', () => { it( 'should determine a undefined, null, and {} to be empty', () => { expect( isEmptyBorder( undefined ) ).toBe( true ); + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `null` is outside the declared `Border | undefined` input; resilience is tested. expect( isEmptyBorder( null ) ).toBe( true ); expect( isEmptyBorder( {} ) ).toBe( true ); } ); it( 'should determine object missing all border props to be empty', () => { + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `nonBorder` has no properties in common with `Border`; resilience is tested. expect( isEmptyBorder( nonBorder ) ).toBe( true ); } ); @@ -109,12 +112,14 @@ describe( 'BorderBoxControl Utils', () => { describe( 'isCompleteBorder', () => { it( 'should determine a undefined, null, and {} to be incomplete', () => { expect( isCompleteBorder( undefined ) ).toBe( false ); + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `null` is outside the declared `Border | undefined` input; resilience is tested. expect( isCompleteBorder( null ) ).toBe( false ); expect( isCompleteBorder( {} ) ).toBe( false ); } ); it( 'should determine objects missing border props to be incomplete', () => { + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `nonBorder` has no properties in common with `Border`; resilience is tested. expect( isCompleteBorder( nonBorder ) ).toBe( false ); expect( isCompleteBorder( partialBorder ) ).toBe( false ); @@ -150,6 +155,7 @@ describe( 'BorderBoxControl Utils', () => { it( 'should determine undefined, non-border or empty object as not being mixed', () => { expect( hasMixedBorders( undefined ) ).toBe( false ); expect( hasMixedBorders( {} ) ).toBe( false ); + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `nonBorder` is not assignable to `AnyBorder`; resilience is tested. expect( hasMixedBorders( nonBorder ) ).toBe( false ); } ); @@ -170,12 +176,14 @@ describe( 'BorderBoxControl Utils', () => { describe( 'getSplitBorders', () => { it( 'should return undefined when no border provided', () => { expect( getSplitBorders( undefined ) ).toEqual( undefined ); + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `null` is outside the declared `Border | undefined` input; resilience is tested. expect( getSplitBorders( null ) ).toEqual( undefined ); } ); it( 'should return undefined when supplied border is empty', () => { expect( getSplitBorders( {} ) ).toEqual( undefined ); + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `nonBorder` has no properties in common with `Border`; resilience is tested. expect( getSplitBorders( nonBorder ) ).toEqual( undefined ); } ); @@ -197,6 +205,7 @@ describe( 'BorderBoxControl Utils', () => { } ); it( 'should only return differences for border related properties', () => { + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `nonBorder` has no properties in common with `Border`; resilience is tested. const diff = getBorderDiff( nonBorder, { caffeine: 'coffee' } ); expect( diff ).toEqual( {} ); @@ -206,6 +215,7 @@ describe( 'BorderBoxControl Utils', () => { const diff = getBorderDiff( completeBorder, { ...completeBorder, color: '#21759b', + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `caffeine` is not a declared `Border` property; the test checks it is ignored. caffeine: 'cola', } ); @@ -304,6 +314,7 @@ describe( 'BorderBoxControl Utils', () => { it( 'should return undefined when no border provided', () => { expect( getShorthandBorderStyle( undefined ) ).toEqual( undefined ); expect( getShorthandBorderStyle( {} ) ).toEqual( undefined ); + // Checking for extra resilience, even if not a valid type. // @ts-expect-error `nonBorder` has no properties in common with `Border`; resilience is tested. expect( getShorthandBorderStyle( nonBorder ) ).toEqual( undefined ); } ); diff --git a/packages/components/src/context/test/context-connect.tsx b/packages/components/src/context/test/context-connect.tsx index 3cd8c5bbdec2a4..9bdea5150be4b4 100644 --- a/packages/components/src/context/test/context-connect.tsx +++ b/packages/components/src/context/test/context-connect.tsx @@ -1,9 +1,9 @@ +import { describe, it } from 'vitest'; import type { ForwardedRef } from 'react'; import { contextConnect, contextConnectWithoutRef } from '../context-connect'; import type { WordPressComponentProps } from '../wordpress-component'; // Static TypeScript tests -/* eslint-disable jest/expect-expect */ describe( 'ref forwarding', () => { const ComponentWithRef = ( props: WordPressComponentProps< {}, 'div' >, @@ -47,4 +47,3 @@ describe( 'ref forwarding', () => { ; } ); } ); -/* eslint-enable jest/expect-expect */ diff --git a/packages/components/src/context/test/wordpress-component.tsx b/packages/components/src/context/test/wordpress-component.tsx index ff89660d728311..19cce0aff814a3 100644 --- a/packages/components/src/context/test/wordpress-component.tsx +++ b/packages/components/src/context/test/wordpress-component.tsx @@ -1,9 +1,9 @@ +import { describe, it } from 'vitest'; import type { ForwardedRef } from 'react'; import { forwardRef } from '@wordpress/element'; import type { WordPressComponentProps } from '../wordpress-component'; // Static TypeScript checks -/* eslint-disable jest/expect-expect */ describe( 'WordPressComponentProps', () => { it( 'should not accept a ref', () => { const Foo = ( props: WordPressComponentProps< {}, 'div' > ) => ( @@ -24,4 +24,3 @@ describe( 'WordPressComponentProps', () => { ; } ); } ); -/* eslint-enable jest/expect-expect */ diff --git a/packages/components/src/custom-gradient-picker/test/serializer.ts b/packages/components/src/custom-gradient-picker/test/serializer.ts index abd8ce692ee915..335a9070f153a0 100644 --- a/packages/components/src/custom-gradient-picker/test/serializer.ts +++ b/packages/components/src/custom-gradient-picker/test/serializer.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { serializeGradientColor, serializeGradientPosition, diff --git a/packages/components/src/date-time/test/utils.test.ts b/packages/components/src/date-time/test/utils.test.ts index 1d8f45f91b9025..01d0f322365fd9 100644 --- a/packages/components/src/date-time/test/utils.test.ts +++ b/packages/components/src/date-time/test/utils.test.ts @@ -1,3 +1,12 @@ +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, +} from 'vitest'; import timezoneMock from 'timezone-mock'; import { getSettings, setSettings, type DateSettings } from '@wordpress/date'; import { inputToDate, getDaysInMonth } from '../utils'; diff --git a/packages/components/src/font-size-picker/test/utils.ts b/packages/components/src/font-size-picker/test/utils.ts index bf7706b67d782b..c85778befeca4a 100644 --- a/packages/components/src/font-size-picker/test/utils.ts +++ b/packages/components/src/font-size-picker/test/utils.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { isSimpleCssValue } from '../utils'; describe( 'isSimpleCssValue', () => { diff --git a/packages/components/src/navigator/test/router.ts b/packages/components/src/navigator/test/router.ts index f1993e7121a312..01f29eacbc7c2f 100644 --- a/packages/components/src/navigator/test/router.ts +++ b/packages/components/src/navigator/test/router.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { patternMatch, findParent } from '../utils/router'; describe( 'patternMatch', () => { diff --git a/packages/components/src/query-controls/test/terms.ts b/packages/components/src/query-controls/test/terms.ts index 3a28fc5e23c155..191e9d1f702e9d 100644 --- a/packages/components/src/query-controls/test/terms.ts +++ b/packages/components/src/query-controls/test/terms.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { buildTermsTree } from '../terms'; describe( 'buildTermsTree()', () => { diff --git a/packages/components/src/unit-control/test/utils.ts b/packages/components/src/unit-control/test/utils.ts index 88d23505315076..eed1c1654ce5e0 100644 --- a/packages/components/src/unit-control/test/utils.ts +++ b/packages/components/src/unit-control/test/utils.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, test } from 'vitest'; import { filterUnitsWithSettings, useCustomUnits, diff --git a/packages/components/src/utils/test/colors.js b/packages/components/src/utils/test/colors.js index 85657c1d4de629..1a02f1ef224cd2 100644 --- a/packages/components/src/utils/test/colors.js +++ b/packages/components/src/utils/test/colors.js @@ -1,3 +1,4 @@ +import { describe, expect, it, test } from 'vitest'; import { getOptimalTextColor, getOptimalTextShade, rgba } from '../colors'; describe( 'rbga', () => { diff --git a/packages/components/src/utils/test/get-node-text.tsx b/packages/components/src/utils/test/get-node-text.tsx index 5c76a4767cbc70..8d7813f7fa9d4a 100644 --- a/packages/components/src/utils/test/get-node-text.tsx +++ b/packages/components/src/utils/test/get-node-text.tsx @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import getNodeText from '../get-node-text'; describe( 'getNodeText', () => { diff --git a/packages/components/src/utils/test/math.js b/packages/components/src/utils/test/math.js index 3f10b496aac11e..34d52f2471f9aa 100644 --- a/packages/components/src/utils/test/math.js +++ b/packages/components/src/utils/test/math.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { add, subtract, clamp, ensureValidStep } from '../math'; describe( 'add', () => { diff --git a/packages/components/src/utils/test/rtl.js b/packages/components/src/utils/test/rtl.js index ed82cec0421610..a472242f9d93ff 100644 --- a/packages/components/src/utils/test/rtl.js +++ b/packages/components/src/utils/test/rtl.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { convertLTRToRTL } from '../rtl'; describe( 'convertLTRToRTL', () => { diff --git a/packages/components/src/utils/test/space.js b/packages/components/src/utils/test/space.js index 595ebeee07542f..759cf2045e187e 100644 --- a/packages/components/src/utils/test/space.js +++ b/packages/components/src/utils/test/space.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { space } from '../space'; describe( 'space', () => { diff --git a/packages/components/src/utils/test/strings.js b/packages/components/src/utils/test/strings.js index c3d6b346d8613e..33803987106f9e 100644 --- a/packages/components/src/utils/test/strings.js +++ b/packages/components/src/utils/test/strings.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { normalizeTextString } from '../strings'; describe( 'normalizeTextString', () => { diff --git a/packages/components/src/utils/test/unit-values.js b/packages/components/src/utils/test/unit-values.js index b2d9969148b1b8..42bb84f84b1c4f 100644 --- a/packages/components/src/utils/test/unit-values.js +++ b/packages/components/src/utils/test/unit-values.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { createCSSUnitValue, parseCSSUnitValue } from '../unit-values'; describe( 'unit-values', () => { diff --git a/packages/compose/package.json b/packages/compose/package.json index 776952fc6a68ce..37e0df9def68e6 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -64,7 +64,8 @@ "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", "mock-match-media": "^0.4.2", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/compose/src/higher-order/test/compose.ts b/packages/compose/src/higher-order/test/compose.ts index d7e6caa641423c..deaeabd8a3a8fe 100644 --- a/packages/compose/src/higher-order/test/compose.ts +++ b/packages/compose/src/higher-order/test/compose.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import compose from '../compose'; describe( 'compose', () => { @@ -6,27 +7,27 @@ describe( 'compose', () => { } ); it( 'executes functions right-to-left when passed as separate arguments', () => { - const a = ( value ) => ( value += 'a' ); - const b = ( value ) => ( value += 'b' ); - const c = ( value ) => ( value += 'c' ); + const a = ( value: string ) => ( value += 'a' ); + const b = ( value: string ) => ( value += 'b' ); + const c = ( value: string ) => ( value += 'c' ); expect( compose( a, b, c )( 'test' ) ).toBe( 'testcba' ); } ); it( 'executes functions right-to-left when passed as a single array', () => { - const a = ( value ) => ( value += 'a' ); - const b = ( value ) => ( value += 'b' ); - const c = ( value ) => ( value += 'c' ); + const a = ( value: string ) => ( value += 'a' ); + const b = ( value: string ) => ( value += 'b' ); + const c = ( value: string ) => ( value += 'c' ); expect( compose( [ a, b, c ] )( 'test' ) ).toBe( 'testcba' ); } ); it( 'executes functions right-to-left when passed as a mix of separate arguments and arrays', () => { - const a = ( value ) => ( value += 'a' ); - const b = ( value ) => ( value += 'b' ); - const c = ( value ) => ( value += 'c' ); - const d = ( value ) => ( value += 'd' ); - const e = ( value ) => ( value += 'e' ); + const a = ( value: string ) => ( value += 'a' ); + const b = ( value: string ) => ( value += 'b' ); + const c = ( value: string ) => ( value += 'c' ); + const d = ( value: string ) => ( value += 'd' ); + const e = ( value: string ) => ( value += 'e' ); expect( compose( [ a, b ], c, [ d ], e )( 'test' ) ).toBe( 'testedcba' diff --git a/packages/compose/src/higher-order/test/pipe.ts b/packages/compose/src/higher-order/test/pipe.ts index c7d42ec6a2758a..2797cd406b953d 100644 --- a/packages/compose/src/higher-order/test/pipe.ts +++ b/packages/compose/src/higher-order/test/pipe.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import pipe from '../pipe'; describe( 'pipe', () => { @@ -6,27 +7,27 @@ describe( 'pipe', () => { } ); it( 'executes functions left-to-right when passed as separate arguments', () => { - const a = ( value ) => ( value += 'a' ); - const b = ( value ) => ( value += 'b' ); - const c = ( value ) => ( value += 'c' ); + const a = ( value: string ) => ( value += 'a' ); + const b = ( value: string ) => ( value += 'b' ); + const c = ( value: string ) => ( value += 'c' ); expect( pipe( a, b, c )( 'test' ) ).toBe( 'testabc' ); } ); it( 'executes functions left-to-right when passed as a single array', () => { - const a = ( value ) => ( value += 'a' ); - const b = ( value ) => ( value += 'b' ); - const c = ( value ) => ( value += 'c' ); + const a = ( value: string ) => ( value += 'a' ); + const b = ( value: string ) => ( value += 'b' ); + const c = ( value: string ) => ( value += 'c' ); expect( pipe( [ a, b, c ] )( 'test' ) ).toBe( 'testabc' ); } ); it( 'executes functions left-to-right when passed as a mix of separate arguments and arrays', () => { - const a = ( value ) => ( value += 'a' ); - const b = ( value ) => ( value += 'b' ); - const c = ( value ) => ( value += 'c' ); - const d = ( value ) => ( value += 'd' ); - const e = ( value ) => ( value += 'e' ); + const a = ( value: string ) => ( value += 'a' ); + const b = ( value: string ) => ( value += 'b' ); + const c = ( value: string ) => ( value += 'c' ); + const d = ( value: string ) => ( value += 'd' ); + const e = ( value: string ) => ( value += 'e' ); expect( pipe( [ a, b ], c, [ d ], e )( 'test' ) ).toBe( 'testabcde' ); } ); diff --git a/packages/compose/src/hooks/use-media-query/test/ssr.jsx b/packages/compose/src/hooks/use-media-query/test/ssr.jsx index cb2fff2c4e3c5a..6df065d8569dde 100644 --- a/packages/compose/src/hooks/use-media-query/test/ssr.jsx +++ b/packages/compose/src/hooks/use-media-query/test/ssr.jsx @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { renderToString } from 'react-dom/server'; import useMediaQuery from '../'; import useViewportMatch from '../../use-viewport-match'; diff --git a/packages/compose/src/utils/create-higher-order-component/test/index.jsx b/packages/compose/src/utils/create-higher-order-component/test/index.jsx index 1ea3bbbfbd227f..7cf1f3a3bf511b 100644 --- a/packages/compose/src/utils/create-higher-order-component/test/index.jsx +++ b/packages/compose/src/utils/create-higher-order-component/test/index.jsx @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { Component } from '@wordpress/element'; import { createHigherOrderComponent } from '../'; diff --git a/packages/compose/src/utils/debounce/test/index.ts b/packages/compose/src/utils/debounce/test/index.ts index 0249c03355fb2d..e98ebf6a07460b 100644 --- a/packages/compose/src/utils/debounce/test/index.ts +++ b/packages/compose/src/utils/debounce/test/index.ts @@ -1,6 +1,7 @@ +import { describe, expect, it } from 'vitest'; import { debounce } from '../index'; -const identity = ( value ) => value; +const identity = < T >( value: T ) => value; describe( 'debounce', () => { it( 'should debounce a function', () => { @@ -326,12 +327,12 @@ describe( 'debounce', () => { it( 'should invoke the trailing call with the correct arguments and `this` binding', () => { return new Promise( ( done ) => { - let actual; + let actual: unknown[] | undefined; let callCount = 0; const object = {}; const debounced = debounce( - function ( ...args ) { + function ( this: object, ...args: unknown[] ) { actual = [ this ]; Array.prototype.push.apply( actual, args ); return ++callCount !== 2; diff --git a/packages/compose/src/utils/observable-map/test/index.js b/packages/compose/src/utils/observable-map/test/index.js index 5292ea90a8345d..e9743b11e21f74 100644 --- a/packages/compose/src/utils/observable-map/test/index.js +++ b/packages/compose/src/utils/observable-map/test/index.js @@ -1,11 +1,12 @@ +import { describe, expect, test, vi } from 'vitest'; import { observableMap } from '..'; describe( 'ObservableMap', () => { test( 'should observe individual values', () => { const map = observableMap(); - const listenerA = jest.fn(); - const listenerB = jest.fn(); + const listenerA = vi.fn(); + const listenerB = vi.fn(); const unsubA = map.subscribe( 'a', listenerA ); const unsubB = map.subscribe( 'b', listenerB ); diff --git a/packages/compose/src/utils/throttle/test/index.ts b/packages/compose/src/utils/throttle/test/index.ts index ee3c89187c7d8d..2c04a56f598dbb 100644 --- a/packages/compose/src/utils/throttle/test/index.ts +++ b/packages/compose/src/utils/throttle/test/index.ts @@ -1,14 +1,15 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { throttle } from '../index'; -const identity = ( value ) => value; +const identity = < T >( value: T ) => value; describe( 'throttle', () => { beforeEach( () => { - jest.useFakeTimers(); + vi.useFakeTimers(); } ); afterEach( () => { - jest.useRealTimers(); + vi.useRealTimers(); } ); it( 'should throttle a function', () => { @@ -24,7 +25,7 @@ describe( 'throttle', () => { const lastCount = callCount; expect( callCount ).toBeGreaterThan( 0 ); - jest.advanceTimersByTime( 64 ); + vi.advanceTimersByTime( 64 ); expect( callCount ).toBeGreaterThan( lastCount ); } ); @@ -35,7 +36,7 @@ describe( 'throttle', () => { expect( results ).toStrictEqual( [ 'a', 'a' ] ); - jest.advanceTimersByTime( 64 ); + vi.advanceTimersByTime( 64 ); results = [ throttled( 'c' ), throttled( 'd' ) ]; expect( results[ 0 ] ).not.toBe( 'a' ); @@ -61,7 +62,7 @@ describe( 'throttle', () => { throttled(); throttled(); - jest.advanceTimersByTime( 64 ); + vi.advanceTimersByTime( 64 ); expect( callCount ).toBe( 2 ); global.Date.now = globalDateNow; @@ -76,7 +77,7 @@ describe( 'throttle', () => { throttled(); expect( callCount ).toBe( 1 ); - jest.advanceTimersByTime( 64 ); + vi.advanceTimersByTime( 64 ); expect( callCount ).toBe( 1 ); } ); @@ -100,11 +101,11 @@ describe( 'throttle', () => { const start = Date.now(); while ( Date.now() - start < limit ) { throttled(); - jest.advanceTimersByTime( 1 ); + vi.advanceTimersByTime( 1 ); } const actual = callCount; - jest.advanceTimersByTime( 1 ); + vi.advanceTimersByTime( 1 ); expect( actual ).toBeGreaterThan( 1 ); } @@ -124,16 +125,16 @@ describe( 'throttle', () => { throttled(); - jest.advanceTimersByTime( 192 ); + vi.advanceTimersByTime( 192 ); expect( callCount ).toBe( 1 ); throttled(); - jest.advanceTimersByTime( 64 ); + vi.advanceTimersByTime( 64 ); expect( callCount ).toBe( 1 ); - jest.advanceTimersByTime( 130 ); + vi.advanceTimersByTime( 130 ); expect( callCount ).toBe( 2 ); } ); @@ -152,7 +153,7 @@ describe( 'throttle', () => { throttled(); expect( callCount ).toBe( 1 ); - jest.advanceTimersByTime( 128 ); + vi.advanceTimersByTime( 128 ); expect( callCount ).toBe( 2 ); } ); @@ -193,7 +194,7 @@ describe( 'throttle', () => { expect( withoutTrailing( 'a' ) ).toBe( 'a' ); expect( withoutTrailing( 'b' ) ).toBe( 'a' ); - jest.advanceTimersByTime( 256 ); + vi.advanceTimersByTime( 256 ); expect( withCount ).toBe( 2 ); expect( withoutCount ).toBe( 1 ); @@ -213,12 +214,12 @@ describe( 'throttle', () => { throttled(); throttled(); - jest.advanceTimersByTime( 96 ); + vi.advanceTimersByTime( 96 ); throttled(); throttled(); - jest.advanceTimersByTime( 96 ); + vi.advanceTimersByTime( 96 ); expect( callCount ).toBeGreaterThan( 1 ); } ); @@ -245,7 +246,7 @@ describe( 'throttle', () => { expect( results ).toStrictEqual( [ 'a', 'a', 'a' ] ); expect( callCount ).toBe( 1 ); - jest.advanceTimersByTime( 64 ); + vi.advanceTimersByTime( 64 ); expect( callCount ).toBe( 2 ); global.Date.now = globalDateNow; diff --git a/packages/connectors/package.json b/packages/connectors/package.json index 4602800439f35c..47bbe3b3a41a70 100644 --- a/packages/connectors/package.json +++ b/packages/connectors/package.json @@ -37,7 +37,8 @@ "@wordpress/private-apis": "file:../private-apis" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/connectors/src/test/store.ts b/packages/connectors/src/test/store.ts index 3908924821dd58..900bf092e49bcb 100644 --- a/packages/connectors/src/test/store.ts +++ b/packages/connectors/src/test/store.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { createRegistry } from '@wordpress/data'; import { store, STORE_NAME } from '../store'; import { unlock } from '../lock-unlock'; diff --git a/packages/core-commands/package.json b/packages/core-commands/package.json index a986d8ef6435f9..d0aacc6f4ca624 100644 --- a/packages/core-commands/package.json +++ b/packages/core-commands/package.json @@ -53,6 +53,9 @@ "@wordpress/router": "file:../router", "@wordpress/url": "file:../url" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" diff --git a/packages/core-commands/src/utils/test/order-entity-records-by-search.js b/packages/core-commands/src/utils/test/order-entity-records-by-search.js index 364b28c795ec19..25b62d593e8eaf 100644 --- a/packages/core-commands/src/utils/test/order-entity-records-by-search.js +++ b/packages/core-commands/src/utils/test/order-entity-records-by-search.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { orderEntityRecordsBySearch } from '../order-entity-records-by-search'; const mockData = [ diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 55cec293cec5c5..048f298d9d89f1 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -76,13 +76,11 @@ "uuid": "^14.0.0" }, "devDependencies": { - "@jest/globals": "^30.5.0", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39", "@wordpress/block-library": "file:../block-library", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/core-data/src/batch/test/create-batch.js b/packages/core-data/src/batch/test/create-batch.js index 925ccbf608e217..806692cd200ca8 100644 --- a/packages/core-data/src/batch/test/create-batch.js +++ b/packages/core-data/src/batch/test/create-batch.js @@ -1,3 +1,4 @@ +import { describe, expect, test, vi } from 'vitest'; import createBatch from '../create-batch'; describe( 'createBatch', () => { @@ -68,7 +69,7 @@ describe( 'createBatch', () => { } ); test( 'running resets the batch when finished', async () => { - const processor = jest.fn( async ( inputs ) => + const processor = vi.fn( async ( inputs ) => inputs.map( ( input ) => ( { output: input, } ) ) diff --git a/packages/core-data/src/batch/test/default-processor.js b/packages/core-data/src/batch/test/default-processor.js index b7d3433bc3b11a..cd01a5a7960bf1 100644 --- a/packages/core-data/src/batch/test/default-processor.js +++ b/packages/core-data/src/batch/test/default-processor.js @@ -1,7 +1,8 @@ +import { describe, expect, it, vi } from 'vitest'; import apiFetch from '@wordpress/api-fetch'; import defaultProcessor from '../default-processor'; -jest.mock( '@wordpress/api-fetch' ); +vi.mock( '@wordpress/api-fetch' ); describe( 'defaultProcessor', () => { const preflightResponse = { diff --git a/packages/core-data/src/entity-types/test/attachment.test.ts b/packages/core-data/src/entity-types/test/attachment.test.ts index 5e3c1d6516d4c3..db282f46c0497c 100644 --- a/packages/core-data/src/entity-types/test/attachment.test.ts +++ b/packages/core-data/src/entity-types/test/attachment.test.ts @@ -1,3 +1,9 @@ +import { describe, expect, it } from 'vitest'; +import type { Attachment } from '../attachment'; +import imageAttachmentFixture from './fixtures/attachment-image.json'; +import zipAttachmentFixture from './fixtures/attachment-zip.json'; +import audioAttachmentFixture from './fixtures/attachment-audio.json'; +import videoAttachmentFixture from './fixtures/attachment-video.json'; /** * Tests for the Attachment type against real REST API responses. * @@ -6,11 +12,6 @@ * are from the REST API's edit context, which includes edit-only fields like * permalink_template, generated_slug, and missing_image_sizes. */ -import type { Attachment } from '../attachment'; -import imageAttachmentFixture from './fixtures/attachment-image.json'; -import zipAttachmentFixture from './fixtures/attachment-zip.json'; -import audioAttachmentFixture from './fixtures/attachment-audio.json'; -import videoAttachmentFixture from './fixtures/attachment-video.json'; describe( 'Attachment type', () => { describe( 'Image attachment', () => { diff --git a/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js b/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js index cef53f4fefc2e7..d9c9906aefe968 100644 --- a/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js +++ b/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js @@ -1,11 +1,12 @@ +import { describe, expect, it, vi } from 'vitest'; import { default as fetchLinkSuggestions, sortResults, tokenize, } from '../__experimental-fetch-link-suggestions'; -jest.mock( '@wordpress/api-fetch', () => - jest.fn( ( { path } ) => { +vi.mock( '@wordpress/api-fetch', () => ( { + default: vi.fn( ( { path } ) => { switch ( path ) { case '/wp/v2/search?search=&per_page=20&type=post': case '/wp/v2/search?search=Contact&per_page=20&type=post&subtype=page': @@ -93,8 +94,8 @@ jest.mock( '@wordpress/api-fetch', () => }, ] ); } - } ) -); + } ), +} ) ); describe( 'fetchLinkSuggestions', () => { it( 'filters suggestions by post-type', () => { diff --git a/packages/core-data/src/fetch/test/__experimental-fetch-url-data.js b/packages/core-data/src/fetch/test/__experimental-fetch-url-data.js index 3efacf8c7bbfa5..709adca99d2683 100644 --- a/packages/core-data/src/fetch/test/__experimental-fetch-url-data.js +++ b/packages/core-data/src/fetch/test/__experimental-fetch-url-data.js @@ -1,7 +1,8 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; import apiFetch from '@wordpress/api-fetch'; import fetchUrlData from '../__experimental-fetch-url-data'; -jest.mock( '@wordpress/api-fetch' ); +vi.mock( '@wordpress/api-fetch' ); describe( 'fetchUrlData', () => { afterEach( () => { diff --git a/packages/core-data/src/locks/test/engine.js b/packages/core-data/src/locks/test/engine.js index f215db382f3012..3cbb94c1e96cf1 100644 --- a/packages/core-data/src/locks/test/engine.js +++ b/packages/core-data/src/locks/test/engine.js @@ -1,8 +1,6 @@ +import { describe, expect, it } from 'vitest'; import createLocks from '../engine'; -// We correctly await all promises with expect calls, but the rule doesn't detect that. -/* eslint-disable jest/valid-expect-in-promise */ - describe( 'Locks engine', () => { it( 'does not grant two exclusive locks at once', async () => { const locks = createLocks(); @@ -126,5 +124,3 @@ describe( 'Locks engine', () => { expect( l2 ).not.toBeUndefined(); } ); } ); - -/* eslint-enable jest/valid-expect-in-promise */ diff --git a/packages/core-data/src/locks/test/reducer.js b/packages/core-data/src/locks/test/reducer.js index 71d06345bae9d8..aba9523c5de8bb 100644 --- a/packages/core-data/src/locks/test/reducer.js +++ b/packages/core-data/src/locks/test/reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import locks from '../reducer'; diff --git a/packages/core-data/src/locks/test/selectors.js b/packages/core-data/src/locks/test/selectors.js index 637a9a5c39dca0..ecf49db5e3452c 100644 --- a/packages/core-data/src/locks/test/selectors.js +++ b/packages/core-data/src/locks/test/selectors.js @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { getPendingLockRequests, isLockAvailable } from '../selectors'; import { deepCopyLocksTreePath, getNode } from '../utils'; diff --git a/packages/core-data/src/locks/test/utils.js b/packages/core-data/src/locks/test/utils.js index b1fe2a0e1c7f5d..2dca7f582dd834 100644 --- a/packages/core-data/src/locks/test/utils.js +++ b/packages/core-data/src/locks/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { getNode, diff --git a/packages/core-data/src/queried-data/test/actions.js b/packages/core-data/src/queried-data/test/actions.js index 3fc2134e78ba01..95f6673ea34bc6 100644 --- a/packages/core-data/src/queried-data/test/actions.js +++ b/packages/core-data/src/queried-data/test/actions.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { removeItems } from '../actions'; describe( 'removeItems', () => { diff --git a/packages/core-data/src/test/parsed-blocks-cache.js b/packages/core-data/src/test/parsed-blocks-cache.js index e65336665ee32d..b5272a89d62339 100644 --- a/packages/core-data/src/test/parsed-blocks-cache.js +++ b/packages/core-data/src/test/parsed-blocks-cache.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; import { getCachedBlocks, setCachedBlocks } from '../parsed-blocks-cache'; diff --git a/packages/core-data/src/utils/test/clear-unchanged-edits.js b/packages/core-data/src/utils/test/clear-unchanged-edits.js index 9afa08fba3ca92..aa8459a12c2a60 100644 --- a/packages/core-data/src/utils/test/clear-unchanged-edits.js +++ b/packages/core-data/src/utils/test/clear-unchanged-edits.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import clearUnchangedEdits from '../clear-unchanged-edits'; describe( 'clearUnchangedEdits', () => { diff --git a/packages/core-data/src/utils/test/conservative-map-item.js b/packages/core-data/src/utils/test/conservative-map-item.js index 3340aed1f00a96..10f239508204c0 100644 --- a/packages/core-data/src/utils/test/conservative-map-item.js +++ b/packages/core-data/src/utils/test/conservative-map-item.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import conservativeMapItem from '../conservative-map-item'; describe( 'conservativeMapItem', () => { diff --git a/packages/core-data/src/utils/test/get-nested-value.js b/packages/core-data/src/utils/test/get-nested-value.js index 6bf6b04a56e9be..7097f318a2911c 100644 --- a/packages/core-data/src/utils/test/get-nested-value.js +++ b/packages/core-data/src/utils/test/get-nested-value.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import getNestedValue from '../get-nested-value'; describe( 'getNestedValue', () => { diff --git a/packages/core-data/src/utils/test/get-normalized-comma-separable.js b/packages/core-data/src/utils/test/get-normalized-comma-separable.js index 0310ab27adc651..0e23050d6bd676 100644 --- a/packages/core-data/src/utils/test/get-normalized-comma-separable.js +++ b/packages/core-data/src/utils/test/get-normalized-comma-separable.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import getNormalizedCommaSeparable from '../get-normalized-comma-separable'; describe( 'getNormalizedCommaSeparable', () => { diff --git a/packages/core-data/src/utils/test/get-pagination-meta.js b/packages/core-data/src/utils/test/get-pagination-meta.js index 629f7ad1dc851d..773344f57e3db4 100644 --- a/packages/core-data/src/utils/test/get-pagination-meta.js +++ b/packages/core-data/src/utils/test/get-pagination-meta.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import getPaginationMeta from '../get-pagination-meta'; const createHeaders = ( values ) => ( { diff --git a/packages/core-data/src/utils/test/get-template-info.js b/packages/core-data/src/utils/test/get-template-info.js index d1d99e026d03a4..a93ab28eeebfee 100644 --- a/packages/core-data/src/utils/test/get-template-info.js +++ b/packages/core-data/src/utils/test/get-template-info.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { footer, header, layout } from '@wordpress/icons'; import { getTemplateInfo } from '../get-template-info'; diff --git a/packages/core-data/src/utils/test/if-matching-action.js b/packages/core-data/src/utils/test/if-matching-action.js index 142590757519c9..4b24d6f7b7edc4 100644 --- a/packages/core-data/src/utils/test/if-matching-action.js +++ b/packages/core-data/src/utils/test/if-matching-action.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import ifMatchingAction from '../if-matching-action'; diff --git a/packages/core-data/src/utils/test/is-numeric-id.js b/packages/core-data/src/utils/test/is-numeric-id.js index 52902c6f18c4f9..5fa5b8688a5953 100644 --- a/packages/core-data/src/utils/test/is-numeric-id.js +++ b/packages/core-data/src/utils/test/is-numeric-id.js @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import isNumericID from '../is-numeric-id'; describe( 'isNumericID', () => { diff --git a/packages/core-data/src/utils/test/log-entity-deprecation.js b/packages/core-data/src/utils/test/log-entity-deprecation.js index 5403673511d8fa..7115dae55ece04 100644 --- a/packages/core-data/src/utils/test/log-entity-deprecation.js +++ b/packages/core-data/src/utils/test/log-entity-deprecation.js @@ -1,10 +1,11 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import deprecated from '@wordpress/deprecated'; import logEntityDeprecation from '../log-entity-deprecation'; -jest.useFakeTimers(); +vi.useFakeTimers(); // Mock the deprecatedEntities import -jest.mock( '../../entities', () => ( { +vi.mock( '../../entities', () => ( { deprecatedEntities: { root: { media: { @@ -19,14 +20,14 @@ jest.mock( '../../entities', () => ( { } ) ); // Mock the deprecated function -jest.mock( '@wordpress/deprecated' ); +vi.mock( '@wordpress/deprecated' ); describe( 'logEntityDeprecation', () => { beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); // Ensure the timeout that prevents spurious logging is cleared. - jest.runAllTimers(); + vi.advanceTimersByTime( 0 ); } ); it( 'should call deprecated when entity is deprecated', () => { diff --git a/packages/core-data/src/utils/test/replace-action.js b/packages/core-data/src/utils/test/replace-action.js index 06bc52c47aacf6..3b6dd7faed2747 100644 --- a/packages/core-data/src/utils/test/replace-action.js +++ b/packages/core-data/src/utils/test/replace-action.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import replaceAction from '../replace-action'; describe( 'replaceAction', () => { diff --git a/packages/core-data/src/utils/test/save-crdt-doc.js b/packages/core-data/src/utils/test/save-crdt-doc.js index 129baa25e4bf31..7c0fa8e0b9f949 100644 --- a/packages/core-data/src/utils/test/save-crdt-doc.js +++ b/packages/core-data/src/utils/test/save-crdt-doc.js @@ -1,10 +1,11 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import apiFetch from '@wordpress/api-fetch'; import { getSyncManager } from '../../sync'; import { saveCRDTDoc } from '../save-crdt-doc'; -jest.mock( '@wordpress/api-fetch' ); -jest.mock( '../../sync', () => ( { - getSyncManager: jest.fn(), +vi.mock( '@wordpress/api-fetch' ); +vi.mock( '../../sync', () => ( { + getSyncManager: vi.fn(), } ) ); function createDeferred() { @@ -28,7 +29,7 @@ describe( 'saveCRDTDoc', () => { beforeEach( () => { apiFetch.mockReset(); syncManager = { - createPersistedCRDTDoc: jest.fn(), + createPersistedCRDTDoc: vi.fn(), }; getSyncManager.mockReturnValue( syncManager ); } ); diff --git a/packages/core-data/src/utils/test/set-nested-value.js b/packages/core-data/src/utils/test/set-nested-value.js index a674082104a37a..23fb452c5e1a72 100644 --- a/packages/core-data/src/utils/test/set-nested-value.js +++ b/packages/core-data/src/utils/test/set-nested-value.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import setNestedValue from '../set-nested-value'; describe( 'setNestedValue', () => { diff --git a/packages/core-data/src/utils/test/with-weak-map-cache.js b/packages/core-data/src/utils/test/with-weak-map-cache.js index 89b2ad42e6541c..784104e4248e09 100644 --- a/packages/core-data/src/utils/test/with-weak-map-cache.js +++ b/packages/core-data/src/utils/test/with-weak-map-cache.js @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import withWeakMapCache from '../with-weak-map-cache'; describe( 'withWeakMapCache', () => { @@ -11,7 +12,7 @@ describe( 'withWeakMapCache', () => { it( 'caches by weak reference', () => { const a = {}; const b = {}; - const fn = jest.fn().mockReturnValue( 'Called' ); + const fn = vi.fn().mockReturnValue( 'Called' ); const cachedFn = withWeakMapCache( fn ); cachedFn( a ); diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index 3a7ecd7540c5dd..2919c347e580d4 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -67,7 +67,8 @@ }, "devDependencies": { "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19", diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index 158db4e7bb1702..6f9178b5feb298 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -47,6 +47,9 @@ "@wordpress/data": "file:../data", "@wordpress/deprecated": "file:../deprecated" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "peerDependencies": { "react": "^18 || ^19" }, diff --git a/packages/data-controls/src/test/index.js b/packages/data-controls/src/test/index.js index 8e32ba2932e8ac..b22f3ca08990e2 100644 --- a/packages/data-controls/src/test/index.js +++ b/packages/data-controls/src/test/index.js @@ -1,19 +1,22 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; import triggerFetch from '@wordpress/api-fetch'; -jest.mock( '@wordpress/api-fetch' ); import { controls } from '../index'; +vi.mock( import( '@wordpress/api-fetch' ) ); + +const mockedTriggerFetch = vi.mocked( triggerFetch ); describe( 'controls', () => { describe( 'API_FETCH', () => { afterEach( () => { - triggerFetch.mockClear(); + mockedTriggerFetch.mockClear(); } ); it( 'invokes the triggerFetch function', () => { controls.API_FETCH( { request: '' } ); - expect( triggerFetch ).toHaveBeenCalledTimes( 1 ); + expect( mockedTriggerFetch ).toHaveBeenCalledTimes( 1 ); } ); it( 'invokes the triggerFetch function with the passed in request', () => { controls.API_FETCH( { request: 'foo' } ); - expect( triggerFetch ).toHaveBeenCalledWith( 'foo' ); + expect( mockedTriggerFetch ).toHaveBeenCalledWith( 'foo' ); } ); } ); } ); diff --git a/packages/data/package.json b/packages/data/package.json index 2ed6456b30c52c..6faf763f00a350 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -62,7 +62,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/data/src/plugins/persistence/storage/test/object.js b/packages/data/src/plugins/persistence/storage/test/object.js index f38fba9816bd1f..07cecd6723f246 100644 --- a/packages/data/src/plugins/persistence/storage/test/object.js +++ b/packages/data/src/plugins/persistence/storage/test/object.js @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from 'vitest'; import objectStorage from '../object'; describe( 'objectStorage', () => { diff --git a/packages/data/src/plugins/persistence/test/index.js b/packages/data/src/plugins/persistence/test/index.js index ee915c97846745..99bd37021b1674 100644 --- a/packages/data/src/plugins/persistence/test/index.js +++ b/packages/data/src/plugins/persistence/test/index.js @@ -1,4 +1,5 @@ import deepFreeze from 'deep-freeze'; +import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import plugin, { createPersistenceInterface, withLazySameState } from '../'; import objectStorage from '../storage/object'; import { createRegistry } from '../../../'; @@ -7,7 +8,7 @@ describe( 'persistence', () => { let registry; beforeAll( () => { - jest.spyOn( objectStorage, 'setItem' ); + vi.spyOn( objectStorage, 'setItem' ); } ); beforeEach( () => { @@ -343,7 +344,7 @@ describe( 'persistence', () => { describe( 'withLazySameState', () => { it( 'should call the original reducer if action.nextState differs from state', () => { - const reducer = jest + const reducer = vi .fn() .mockImplementation( ( state, action ) => action.nextState ); const enhanced = withLazySameState( reducer ); @@ -357,7 +358,7 @@ describe( 'persistence', () => { } ); it( 'should not call the original reducer if action.nextState equals state', () => { - const reducer = jest + const reducer = vi .fn() .mockImplementation( ( state, action ) => action.nextState ); const enhanced = withLazySameState( reducer ); diff --git a/packages/data/src/redux-store/metadata/test/reducer.js b/packages/data/src/redux-store/metadata/test/reducer.js index 5a9ab5e0184235..8cf22c301d326a 100644 --- a/packages/data/src/redux-store/metadata/test/reducer.js +++ b/packages/data/src/redux-store/metadata/test/reducer.js @@ -1,4 +1,5 @@ import deepFreeze from 'deep-freeze'; +import { describe, expect, it } from 'vitest'; import reducer from '../reducer'; describe( 'reducer', () => { diff --git a/packages/data/src/redux-store/metadata/test/selectors.js b/packages/data/src/redux-store/metadata/test/selectors.js index 6eeaea54ff1c53..198b88c31b93f7 100644 --- a/packages/data/src/redux-store/metadata/test/selectors.js +++ b/packages/data/src/redux-store/metadata/test/selectors.js @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createRegistry } from '@wordpress/data'; const getFooSelector = ( state ) => state; @@ -455,7 +456,7 @@ describe( 'Selector arguments normalization', () => { } ); it( 'should call normalization method on target selector if exists', () => { - const normalizationFunction = jest.fn( ( args ) => { + const normalizationFunction = vi.fn( ( args ) => { return args.map( Number ); } ); getFooSelector.__unstableNormalizeArgs = normalizationFunction; diff --git a/packages/data/src/redux-store/metadata/test/utils.js b/packages/data/src/redux-store/metadata/test/utils.js index 564c912e30d42e..42fe179faa0442 100644 --- a/packages/data/src/redux-store/metadata/test/utils.js +++ b/packages/data/src/redux-store/metadata/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { selectorArgsToStateKey } from '../utils'; describe( 'selectorArgsToStateKey', () => { diff --git a/packages/data/src/redux-store/test/combine-reducers.js b/packages/data/src/redux-store/test/combine-reducers.js index 1f0b1186421a16..7f59ced0fb3b50 100644 --- a/packages/data/src/redux-store/test/combine-reducers.js +++ b/packages/data/src/redux-store/test/combine-reducers.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { combineReducers } from '../combine-reducers'; function counterReducer( count = 0, action ) { diff --git a/packages/data/src/redux-store/test/index.js b/packages/data/src/redux-store/test/index.js index e470a2fb93324d..3ddf8225e48f42 100644 --- a/packages/data/src/redux-store/test/index.js +++ b/packages/data/src/redux-store/test/index.js @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createRegistry } from '../../registry'; import { createRegistryControl } from '../../factory'; @@ -10,7 +11,7 @@ describe( 'controls', () => { describe( 'should call registry-aware controls', () => { it( 'registers multiple selectors to the public API', () => { - const action1 = jest.fn( () => ( { type: 'NOTHING' } ) ); + const action1 = vi.fn( () => ( { type: 'NOTHING' } ) ); const action2 = function* () { yield { type: 'DISPATCH', store: 'store1', action: 'action1' }; }; @@ -79,7 +80,6 @@ describe( 'controls', () => { .hasFinishedResolution( 'getItems' ); if ( isFinished ) { const items = registry.select( 'store' ).getItems(); - // eslint-disable-next-line jest/no-conditional-expect expect( items ).toEqual( [ 1, 2, 3 ] ); } resolve(); @@ -91,7 +91,7 @@ describe( 'controls', () => { describe( 'selectors have expected value for the `hasResolver` property', () => { it( 'when custom store has resolvers defined', () => { registry.registerStore( 'store', { - reducer: jest.fn(), + reducer: vi.fn(), selectors: { getItems: ( state ) => state, getItem: ( state ) => state, @@ -111,7 +111,7 @@ describe( 'controls', () => { } ); it( 'when custom store does not have resolvers defined', () => { registry.registerStore( 'store', { - reducer: jest.fn(), + reducer: vi.fn(), selectors: { getItems: ( state ) => state, }, @@ -308,7 +308,7 @@ describe( 'resolveSelect', () => { } ); it( 'handles isFulfilled with arguments correctly', async () => { - const fulfilledResolver = jest.fn(); + const fulfilledResolver = vi.fn(); fulfilledResolver.isFulfilled = ( state, id ) => state.pages?.[ id ]; const resolvedState = { @@ -345,7 +345,7 @@ describe( 'resolveSelect', () => { } ); it( 'does not change Redux state when isFulfilled returns true', async () => { - const fulfill = jest.fn(); + const fulfill = vi.fn(); const isFulfilled = () => true; registry.registerStore( 'demo', { @@ -358,7 +358,7 @@ describe( 'resolveSelect', () => { }, } ); - const listener = jest.fn(); + const listener = vi.fn(); const unsubscribe = registry.subscribe( listener ); // Call the selector — isFulfilled is true, so no resolution should happen. @@ -374,11 +374,11 @@ describe( 'resolveSelect', () => { } ); it( 'calls resolver when isFulfilled returns false', async () => { - const fulfill = jest.fn().mockImplementation( () => ( { + const fulfill = vi.fn().mockImplementation( () => ( { type: 'SET_DATA', data: 'resolved data', } ) ); - const isFulfilled = jest.fn( ( state ) => state.hasData ); + const isFulfilled = vi.fn( ( state ) => state.hasData ); registry.registerStore( 'demo', { reducer: ( state = { hasData: false }, action ) => { @@ -414,7 +414,7 @@ describe( 'normalizing args', () => { const registry = createRegistry(); const selector = () => {}; - const normalizingFunction = jest.fn( ( ...args ) => args ); + const normalizingFunction = vi.fn( ( ...args ) => args ); selector.__unstableNormalizeArgs = normalizingFunction; @@ -442,7 +442,7 @@ describe( 'normalizing args', () => { const registry = createRegistry(); const selector = () => {}; - selector.__unstableNormalizeArgs = jest.fn( ( ...args ) => args ); + selector.__unstableNormalizeArgs = vi.fn( ( ...args ) => args ); registry.registerStore( 'store', { reducer: () => {}, @@ -464,7 +464,7 @@ describe( 'normalizing args', () => { const registry = createRegistry(); const selector = () => {}; - selector.__unstableNormalizeArgs = jest.fn( ( ...args ) => args ); + selector.__unstableNormalizeArgs = vi.fn( ( ...args ) => args ); registry.registerStore( 'store', { reducer: () => {}, diff --git a/packages/data/src/redux-store/test/keyed-reducer.js b/packages/data/src/redux-store/test/keyed-reducer.js index 3fea3540240108..5a74445287e005 100644 --- a/packages/data/src/redux-store/test/keyed-reducer.js +++ b/packages/data/src/redux-store/test/keyed-reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { keyedReducer } from '../keyed-reducer'; describe( 'keyedReducer', () => { diff --git a/packages/data/src/test/controls.js b/packages/data/src/test/controls.js index 4789cdd2016554..c47dbe400d6956 100644 --- a/packages/data/src/test/controls.js +++ b/packages/data/src/test/controls.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { createRegistry, controls } from '..'; describe( 'controls', () => { diff --git a/packages/data/src/test/privateAPIs.js b/packages/data/src/test/privateAPIs.js index de62fb786d1d23..dd30f3f6523cca 100644 --- a/packages/data/src/test/privateAPIs.js +++ b/packages/data/src/test/privateAPIs.js @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from 'vitest'; import { createRegistry } from '../registry'; import createReduxStore from '../redux-store'; import { unlock } from '../lock-unlock'; diff --git a/packages/data/src/test/registry-selectors.js b/packages/data/src/test/registry-selectors.js index 12a0ba97328e6e..4681b78ddb5ce4 100644 --- a/packages/data/src/test/registry-selectors.js +++ b/packages/data/src/test/registry-selectors.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { createRegistry } from '../registry'; import { createRegistrySelector } from '../factory'; import { createSelector } from '..'; diff --git a/packages/data/src/test/registry.js b/packages/data/src/test/registry.js index ba7cf17d9806f9..aeaca25e32c284 100644 --- a/packages/data/src/test/registry.js +++ b/packages/data/src/test/registry.js @@ -1,11 +1,9 @@ -/* eslint jest/expect-expect: ["warn", { "assertFunctionNames": ["expect", "subscribeUntil"] }] */ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createRegistry } from '../registry'; import { createRegistrySelector } from '../factory'; import createReduxStore from '../redux-store'; import coreDataStore from '../store'; -jest.useFakeTimers( { legacyFakeTimers: true } ); - describe( 'createRegistry', () => { let registry; @@ -28,6 +26,7 @@ describe( 'createRegistry', () => { } beforeEach( () => { + vi.useFakeTimers(); registry = createRegistry(); } ); @@ -36,6 +35,7 @@ describe( 'createRegistry', () => { while ( ( unsubscribe = unsubscribes.shift() ) ) { unsubscribe(); } + vi.useRealTimers(); } ); describe( 'registerGenericStore', () => { @@ -104,7 +104,7 @@ describe( 'createRegistry', () => { describe( 'getActions', () => { it( 'should make actions available via registry.dispatch', () => { - const dispatch = jest.fn(); + const dispatch = vi.fn(); function setPrice( itemName, price ) { return { type: 'SET_PRICE', itemName, price }; @@ -151,7 +151,7 @@ describe( 'createRegistry', () => { describe( 'subscribe', () => { it( 'should send out updates to listeners of the registry', () => { - const registryListener = jest.fn(); + const registryListener = vi.fn(); let listener = () => {}; const storeChanged = () => { @@ -247,7 +247,7 @@ describe( 'createRegistry', () => { } ); it( 'should behave as a side effect for the given selector, with arguments', () => { - const resolver = jest.fn(); + const resolver = vi.fn(); registry.registerStore( 'demo', { reducer: ( state = 'OK' ) => state, selectors: { @@ -259,19 +259,19 @@ describe( 'createRegistry', () => { } ); const value = registry.select( 'demo' ).getValue( 'arg1', 'arg2' ); - jest.runAllTimers(); + vi.runAllTimers(); expect( value ).toBe( 'OK' ); expect( resolver ).toHaveBeenCalledWith( 'arg1', 'arg2' ); registry.select( 'demo' ).getValue( 'arg1', 'arg2' ); - jest.runAllTimers(); + vi.runAllTimers(); expect( resolver ).toHaveBeenCalledTimes( 1 ); registry.select( 'demo' ).getValue( 'arg3', 'arg4' ); - jest.runAllTimers(); + vi.runAllTimers(); expect( resolver ).toHaveBeenCalledTimes( 2 ); } ); it( 'should support the object resolver descriptor', () => { - const resolver = jest.fn(); + const resolver = vi.fn(); registry.registerStore( 'demo', { reducer: ( state = 'OK' ) => state, selectors: { @@ -283,12 +283,12 @@ describe( 'createRegistry', () => { } ); const value = registry.select( 'demo' ).getValue( 'arg1', 'arg2' ); - jest.runAllTimers(); + vi.runAllTimers(); expect( value ).toBe( 'OK' ); } ); it( 'should use isFulfilled definition before calling the side effect', () => { - const fulfill = jest.fn().mockImplementation( ( state, page ) => { + const fulfill = vi.fn().mockImplementation( ( state, page ) => { return { type: 'SET_PAGE', page, result: [] }; } ); @@ -319,29 +319,29 @@ describe( 'createRegistry', () => { store.dispatch( { type: 'SET_PAGE', page: 4, result: [] } ); registry.select( 'demo' ).getPage( 1 ); - jest.runAllTimers(); + vi.runAllTimers(); registry.select( 'demo' ).getPage( 2 ); - jest.runAllTimers(); + vi.runAllTimers(); expect( fulfill ).toHaveBeenCalledTimes( 2 ); registry.select( 'demo' ).getPage( 1 ); - jest.runAllTimers(); + vi.runAllTimers(); registry.select( 'demo' ).getPage( 2 ); - jest.runAllTimers(); + vi.runAllTimers(); registry.select( 'demo' ).getPage( 3, {} ); - jest.runAllTimers(); + vi.runAllTimers(); // Expected: First and second page fulfillments already triggered, so // should only be one more than previous assertion set. expect( fulfill ).toHaveBeenCalledTimes( 3 ); registry.select( 'demo' ).getPage( 1 ); - jest.runAllTimers(); + vi.runAllTimers(); registry.select( 'demo' ).getPage( 2 ); - jest.runAllTimers(); + vi.runAllTimers(); registry.select( 'demo' ).getPage( 3, {} ); - jest.runAllTimers(); + vi.runAllTimers(); registry.select( 'demo' ).getPage( 4 ); // Expected: @@ -376,7 +376,7 @@ describe( 'createRegistry', () => { ] ); registry.select( 'demo' ).getValue(); - jest.runAllTimers(); + vi.runAllTimers(); return promise; } ); @@ -403,7 +403,7 @@ describe( 'createRegistry', () => { ] ); registry.select( 'demo' ).getValue(); - jest.runAllTimers(); + vi.runAllTimers(); return promise; } ); @@ -428,9 +428,9 @@ describe( 'createRegistry', () => { ); registry.select( 'demo' ).getValue(); - jest.runAllTimers(); + vi.runAllTimers(); registry.select( 'demo' ).getValue(); - jest.runAllTimers(); + vi.runAllTimers(); return promise; } ); @@ -461,7 +461,7 @@ describe( 'createRegistry', () => { () => registry.select( 'demo' ).getValue() === 'OK' ); registry.select( 'demo' ).getValue(); // Triggers resolver switches to OK. - jest.runAllTimers(); + vi.runAllTimers(); await promise; // Invalidate the cache @@ -471,7 +471,7 @@ describe( 'createRegistry', () => { () => registry.select( 'demo' ).getValue() === 'NOTOK' ); registry.select( 'demo' ).getValue(); // Triggers the resolver again and switch to NOTOK. - jest.runAllTimers(); + vi.runAllTimers(); await promise; } ); } ); @@ -525,8 +525,8 @@ describe( 'createRegistry', () => { describe( 'select', () => { it( 'registers multiple selectors to the public API', () => { - const selector1 = jest.fn( () => 'result1' ); - const selector2 = jest.fn( () => 'result2' ); + const selector1 = vi.fn( () => 'result1' ); + const selector2 = vi.fn( () => 'result2' ); const store = registry.registerStore( 'reducer1', { reducer: () => 'state1', selectors: { @@ -629,8 +629,8 @@ describe( 'createRegistry', () => { const store = registry.registerStore( 'myAwesomeReducer', { reducer: ( state = 0 ) => state + 1, } ); - const secondListener = jest.fn(); - const firstListener = jest.fn( () => { + const secondListener = vi.fn(); + const firstListener = vi.fn( () => { subscribeWithUnsubscribe( secondListener ); } ); @@ -645,10 +645,10 @@ describe( 'createRegistry', () => { const store = registry.registerStore( 'myAwesomeReducer', { reducer: ( state = 0 ) => state + 1, } ); - const firstListener = jest.fn( () => { + const firstListener = vi.fn( () => { secondUnsubscribe(); } ); - const secondListener = jest.fn(); + const secondListener = vi.fn(); subscribeWithUnsubscribe( firstListener ); const secondUnsubscribe = @@ -663,7 +663,7 @@ describe( 'createRegistry', () => { const store = registry.registerStore( 'unchanging', { reducer: ( state = {} ) => state, } ); - const listener = jest.fn(); + const listener = vi.fn(); subscribeWithUnsubscribe( listener ); store.dispatch( { type: 'dummy' } ); @@ -704,7 +704,7 @@ describe( 'createRegistry', () => { const store = registry.registerStore( 'myAwesomeReducer', { reducer: ( state = 0 ) => state + 1, } ); - const listener = jest.fn(); + const listener = vi.fn(); subscribeWithUnsubscribe( listener ); registry.batch( () => {} ); @@ -716,7 +716,7 @@ describe( 'createRegistry', () => { } ); expect( listener ).toHaveBeenCalledTimes( 1 ); - const listener2 = jest.fn(); + const listener2 = vi.fn(); // useSelect subscribes to the stores differently, // This test ensures batching works in this case as well. const unsubscribe = registry.subscribe( @@ -735,7 +735,7 @@ describe( 'createRegistry', () => { const store = registry.registerStore( 'myAwesomeReducer', { reducer: ( state = 0 ) => state + 1, } ); - const listener = jest.fn(); + const listener = vi.fn(); subscribeWithUnsubscribe( listener ); registry.batch( () => {} ); @@ -756,7 +756,7 @@ describe( 'createRegistry', () => { const store = registry.registerStore( 'myAwesomeReducer', { reducer: ( state = 0 ) => state + 1, } ); - const listener = jest.fn(); + const listener = vi.fn(); const error = new Error( 'Whoops' ); subscribeWithUnsubscribe( listener ); @@ -824,8 +824,8 @@ describe( 'createRegistry', () => { describe( 'parent registry', () => { it( 'should call parent registry selectors/actions if defined', () => { - const mySelector = jest.fn(); - const myAction = jest.fn(); + const mySelector = vi.fn(); + const myAction = vi.fn(); const getSelectors = () => ( { mySelector } ); const getActions = () => ( { myAction } ); const subscribe = () => {}; @@ -848,8 +848,8 @@ describe( 'createRegistry', () => { } ); it( 'should override existing store in parent registry', () => { - const mySelector = jest.fn(); - const myAction = jest.fn(); + const mySelector = vi.fn(); + const myAction = vi.fn(); const getSelectors = () => ( { mySelector } ); const getActions = () => ( { myAction } ); const subscribe = () => {}; @@ -863,8 +863,8 @@ describe( 'createRegistry', () => { } ); const subRegistry = createRegistry( {}, registry ); - const mySelector2 = jest.fn(); - const myAction2 = jest.fn(); + const mySelector2 = vi.fn(); + const myAction2 = vi.fn(); const getSelectors2 = () => ( { mySelector: mySelector2 } ); const getActions2 = () => ( { myAction: myAction2 } ); const subscribe2 = () => {}; diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index 853f25c36ff5ce..fac5cee3f37b30 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -81,10 +81,10 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "esbuild": "^0.27.2", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/dataviews/src/components/dataform-controls/utils/test/get-calendar-locale.ts b/packages/dataviews/src/components/dataform-controls/utils/test/get-calendar-locale.ts index 31ec1cddc24f98..8e0bde4727a0f4 100644 --- a/packages/dataviews/src/components/dataform-controls/utils/test/get-calendar-locale.ts +++ b/packages/dataviews/src/components/dataform-controls/utils/test/get-calendar-locale.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import getCalendarLocale from '../get-calendar-locale'; describe( 'getCalendarLocale', () => { diff --git a/packages/dataviews/src/components/dataform-layouts/test/normalize-form.ts b/packages/dataviews/src/components/dataform-layouts/test/normalize-form.ts index e4a0ada0469414..72ac2823253a25 100644 --- a/packages/dataviews/src/components/dataform-layouts/test/normalize-form.ts +++ b/packages/dataviews/src/components/dataform-layouts/test/normalize-form.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import normalizeForm from '../normalize-form'; import type { Form } from '../../../types'; @@ -305,16 +306,15 @@ describe( 'normalizeFormFields', () => { } ); it( 'card: enforces isOpened=true and summary=[] when withHeader=false', () => { - const form: Form = { + const form = { layout: { type: 'card', withHeader: false, - // @ts-expect-error With `withHeader: false`, `isOpened` must be `true`; normalization of `false` is tested. isOpened: false, summary: [ { id: 'field1', visibility: 'always' } ], }, fields: [ 'field1' ], - }; + } as unknown as Form; const result = normalizeForm( form ); expect( result ).toEqual( { layout: { diff --git a/packages/dataviews/src/components/dataviews-layouts/utils/test/use-selection-props.ts b/packages/dataviews/src/components/dataviews-layouts/utils/test/use-selection-props.ts index bdc471b771dc4a..15fe9b46076edb 100644 --- a/packages/dataviews/src/components/dataviews-layouts/utils/test/use-selection-props.ts +++ b/packages/dataviews/src/components/dataviews-layouts/utils/test/use-selection-props.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getClosestSelectedId, getRangeSelection, diff --git a/packages/dataviews/src/field-types/test/normalize-fields.ts b/packages/dataviews/src/field-types/test/normalize-fields.ts index 77b6f909dd67e8..09f4b7d337e3b2 100644 --- a/packages/dataviews/src/field-types/test/normalize-fields.ts +++ b/packages/dataviews/src/field-types/test/normalize-fields.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import normalizeFields from '../index'; import type { Field } from '../../types'; diff --git a/packages/dataviews/src/field-types/test/time.ts b/packages/dataviews/src/field-types/test/time.ts index 77d53099da7a89..11b1b2123adf89 100644 --- a/packages/dataviews/src/field-types/test/time.ts +++ b/packages/dataviews/src/field-types/test/time.ts @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it } from 'vitest'; import { setSettings, getSettings } from '@wordpress/date'; import normalizeFields from '../index'; import parseTime from '../utils/parse-time'; diff --git a/packages/dataviews/src/utils/test/filter-sort-and-paginate.js b/packages/dataviews/src/utils/test/filter-sort-and-paginate.js index 186ff571599a1b..e93325947245cf 100644 --- a/packages/dataviews/src/utils/test/filter-sort-and-paginate.js +++ b/packages/dataviews/src/utils/test/filter-sort-and-paginate.js @@ -1,4 +1,5 @@ import { subDays, subYears } from 'date-fns'; +import { describe, expect, it } from 'vitest'; import filterSortAndPaginate from '../filter-sort-and-paginate'; import { data, fields } from '../../dataviews/stories/fixtures'; diff --git a/packages/date/package.json b/packages/date/package.json index 0bf8f6592d5884..269e66cfaa4a93 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -46,6 +46,9 @@ "moment": "^2.29.4", "moment-timezone": "^0.5.40" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" } diff --git a/packages/date/src/test/index.js b/packages/date/src/test/index.js index fcc8f094cff8bc..87f75585da6ca6 100644 --- a/packages/date/src/test/index.js +++ b/packages/date/src/test/index.js @@ -1,4 +1,5 @@ import momentLib from 'moment'; +import { describe, expect, it, test } from 'vitest'; import { getSettings, date as dateNoI18n, diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json index caedcc5c561a43..fae32c7d176028 100644 --- a/packages/dependency-extraction-webpack-plugin/package.json +++ b/packages/dependency-extraction-webpack-plugin/package.json @@ -48,6 +48,7 @@ "mini-css-extract-plugin": "^2.9.2", "mkdirp": "^3.0.1", "rimraf": "^5.0.10", + "vitest": "^4.1.10", "webpack": "^5.108.1" }, "peerDependencies": { diff --git a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap index 195f1bd3848c3d..fac4da61f9711f 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -1,11 +1,11 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` should produce expected output: Asset file 'assets.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`combine-assets\` > should produce expected output > Asset file 'assets.php' should match snapshot 1`] = ` " array('dependencies' => array('@wordpress/blob', 'lodash'), 'version' => '270cb9e2d17f391e9270', 'type' => 'module'), 'fileB.mjs' => array('dependencies' => array('@wordpress/token-list'), 'version' => '4cf36c37afb98cbb1f86', 'type' => 'module')); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`combine-assets\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -25,12 +25,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`combine-assets\` sh ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dependency-graph\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`cyclic-dependency-graph\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('@wordpress/interactivity'), 'version' => '3432e19e5eaf6d6f4b77', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dependency-graph\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`cyclic-dependency-graph\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "module", @@ -40,12 +40,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dependency-g ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dynamic-dependency-graph\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`cyclic-dynamic-dependency-graph\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array(array('id' => '@wordpress/interactivity', 'import' => 'dynamic')), 'version' => '076698e41df140e724d8', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dynamic-dependency-graph\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`cyclic-dynamic-dependency-graph\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "module", @@ -55,12 +55,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-dynamic-depe ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-external-deps\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`cyclic-external-deps\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array(array('id' => '@wordpress/interactivity', 'import' => 'dynamic')), 'version' => '1241febf0f1b74e69d6a', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-external-deps\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`cyclic-external-deps\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "module", @@ -70,12 +70,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`cyclic-external-dep ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`dynamic-import\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array(array('id' => '@wordpress/blob', 'import' => 'dynamic')), 'version' => '3f8407fd25e98c44c5ff', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`dynamic-import\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -85,7 +85,7 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`dynamic-import\` sh ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`error\` should produce expected output 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`error\` > should produce expected output 1`] = ` "ERROR in ./index.js 1:0-23 Module not found: Error: Attempted to use WordPress script in a module: jquery, which is not supported yet. @@ -94,12 +94,12 @@ Module not found: Error: Attempted to use WordPress script in a module: @wordpre " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`function-output-filename\` > should produce expected output > Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', 'lodash'), 'version' => 'b68363f122fe0a26183e', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`function-output-filename\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -114,12 +114,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`function-output-fil ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffix\` should produce expected output: Asset file 'index.min.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`has-extension-suffix\` > should produce expected output > Asset file 'index.min.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', 'lodash'), 'version' => 'fd4b8dcc8741c1523219', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffix\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`has-extension-suffix\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -134,12 +134,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`has-extension-suffi ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`module-renames\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`module-renames\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('renamed--@my/module', 'renamed--other-module'), 'version' => '79d3c39076be7f6df4ea', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`module-renames\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`module-renames\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -154,12 +154,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`module-renames\` sh ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`nested-dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`nested-dynamic-import\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array(array('id' => '@wordpress/interactivity', 'import' => 'dynamic')), 'version' => 'f509a71faab9f56557c1', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`nested-dynamic-import\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`nested-dynamic-import\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "module", @@ -169,26 +169,26 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`nested-dynamic-impo ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-default\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`no-default\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array(), 'version' => '60d4e92bd289da4738ed', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-default\` should produce expected output: External modules should match snapshot 1`] = `[]`; +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`no-default\` > should produce expected output > External modules should match snapshot 1`] = `[]`; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-deps\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`no-deps\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array(), 'version' => '47abc9f11d44c8d2ea89', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`no-deps\` should produce expected output: External modules should match snapshot 1`] = `[]`; +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`no-deps\` > should produce expected output > External modules should match snapshot 1`] = `[]`; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`option-function-output-filename\` > should produce expected output > Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', 'lodash'), 'version' => 'b68363f122fe0a26183e', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`option-function-output-filename\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -203,12 +203,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-function-out ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-output-filename\` should produce expected output: Asset file 'main-foo.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`option-output-filename\` > should produce expected output > Asset file 'main-foo.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', 'lodash'), 'version' => 'b68363f122fe0a26183e', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`option-output-filename\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -223,9 +223,9 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`option-output-filen ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash"],"version":"11156aba3b369605e939","type":"module"}"`; +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`output-format-json\` > should produce expected output > Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash"],"version":"11156aba3b369605e939","type":"module"}"`; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`output-format-json\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -235,12 +235,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`output-format-json\ ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`overrides\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', '@wordpress/url', 'rxjs', 'rxjs/operators'), 'version' => 'af74b6f7d8a3f114b5b3', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`overrides\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -265,36 +265,36 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`overrides\` should ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`polyfill-magic-comment\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`polyfill-magic-comment\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-polyfill'), 'version' => 'ce3c6b76208e3e42fe21', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`polyfill-magic-comment\` should produce expected output: External modules should match snapshot 1`] = `[]`; +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`polyfill-magic-comment\` > should produce expected output > External modules should match snapshot 1`] = `[]`; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`polyfill-magic-comment-minified\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`polyfill-magic-comment-minified\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-polyfill'), 'version' => '31d6cfe0d16ae931b73c', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`polyfill-magic-comment-minified\` should produce expected output: External modules should match snapshot 1`] = `[]`; +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`polyfill-magic-comment-minified\` > should produce expected output > External modules should match snapshot 1`] = `[]`; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'a.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`runtime-chunk-single\` > should produce expected output > Asset file 'a.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob'), 'version' => '00ad69a407b9d8fe017b', 'type' => 'module', 'handle' => 'runtime-chunk-single-modules-a'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'b.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`runtime-chunk-single\` > should produce expected output > Asset file 'b.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', 'lodash'), 'version' => '8f3ca66c17c6a5908643', 'type' => 'module', 'handle' => 'runtime-chunk-single-modules-b'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'runtime.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`runtime-chunk-single\` > should produce expected output > Asset file 'runtime.asset.php' should match snapshot 1`] = ` " array(), 'version' => '7e4f3787fd93b418a0d4', 'type' => 'module', 'handle' => 'runtime-chunk-single-modules-runtime'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`runtime-chunk-single\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -309,12 +309,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-singl ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-cache-group\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`style-cache-group\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob'), 'version' => '24df66bb21cda0d08963', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-cache-group\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`style-cache-group\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -324,12 +324,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-cache-group\` ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`style-imports\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', 'lodash'), 'version' => '825b69f70b86760c73a3', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`style-imports\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -344,12 +344,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`style-imports\` sho ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`wordpress\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', 'lodash'), 'version' => 'b68363f122fe0a26183e', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`wordpress\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -364,12 +364,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`wordpress-interactivity\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('lodash', array('id' => '@wordpress/interactivity', 'import' => 'dynamic')), 'version' => '2f6f06cb476e5837f787', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`wordpress-interactivity\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -384,12 +384,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-interacti ] `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-require\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`wordpress-require\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('@wordpress/blob', 'lodash'), 'version' => '9dcb7bb56f3fd06c5087', 'type' => 'module'); " `; -exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-require\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin modules > Webpack \`wordpress-require\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "import", @@ -404,12 +404,12 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`wordpress-require\` ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`combine-assets\` should produce expected output: Asset file 'assets.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`combine-assets\` > should produce expected output > Asset file 'assets.php' should match snapshot 1`] = ` " array('dependencies' => array('lodash', 'wp-blob'), 'version' => '09504998b440d7f5861d'), 'fileB.js' => array('dependencies' => array('wp-token-list'), 'version' => 'f2d7349feffd62e5e874')); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`combine-assets\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`combine-assets\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -435,12 +435,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`combine-assets\` sh ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dependency-graph\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`cyclic-dependency-graph\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-interactivity'), 'version' => '2f1a57170044bbe4b31e'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dependency-graph\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`cyclic-dependency-graph\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -453,12 +453,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dependency-g ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dynamic-dependency-graph\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`cyclic-dynamic-dependency-graph\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-interactivity'), 'version' => 'e72ee7c17819745462a7'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dynamic-dependency-graph\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`cyclic-dynamic-dependency-graph\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -471,12 +471,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-dynamic-depe ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-external-deps\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`cyclic-external-deps\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-interactivity'), 'version' => '2a9d03bfae20128046c0'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-external-deps\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`cyclic-external-deps\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -489,12 +489,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`cyclic-external-dep ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`dynamic-import\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-blob'), 'version' => 'f4c47bb9efae7a108052'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`dynamic-import\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`dynamic-import\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -507,18 +507,18 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`dynamic-import\` sh ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`error\` should produce expected output 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`error\` > should produce expected output 1`] = ` "ERROR in main Module not found: Error: Ensure error in script build. " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`function-output-filename\` > should produce expected output > Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => 'e9b912dd9bf4a70e4cb0'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`function-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`function-output-filename\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -536,12 +536,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`function-output-fil ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`has-extension-suffix\` should produce expected output: Asset file 'index.min.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`has-extension-suffix\` > should produce expected output > Asset file 'index.min.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => 'd4f650de95657219702b'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`has-extension-suffix\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`has-extension-suffix\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -559,12 +559,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`has-extension-suffi ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`module-renames\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`module-renames\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('renamed--@my/module', 'renamed--other-module'), 'version' => '556545d20d0075ed699f'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`module-renames\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`module-renames\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -585,12 +585,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`module-renames\` sh ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`nested-dynamic-import\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`nested-dynamic-import\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-interactivity'), 'version' => 'a91c78cd3d9af44f15af'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`nested-dynamic-import\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`nested-dynamic-import\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -603,26 +603,26 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`nested-dynamic-impo ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`no-default\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`no-default\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array(), 'version' => '17ace6f7f49c7c077a6c'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`no-default\` should produce expected output: External modules should match snapshot 1`] = `[]`; +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`no-default\` > should produce expected output > External modules should match snapshot 1`] = `[]`; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`no-deps\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`no-deps\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array(), 'version' => 'b922a4320dd8b7a352f4'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`no-deps\` should produce expected output: External modules should match snapshot 1`] = `[]`; +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`no-deps\` > should produce expected output > External modules should match snapshot 1`] = `[]`; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`option-function-output-filename\` should produce expected output: Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`option-function-output-filename\` > should produce expected output > Asset file 'chunk--main--main.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => 'e9b912dd9bf4a70e4cb0'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`option-function-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`option-function-output-filename\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -640,12 +640,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`option-function-out ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`option-output-filename\` should produce expected output: Asset file 'main-foo.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`option-output-filename\` > should produce expected output > Asset file 'main-foo.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => 'e9b912dd9bf4a70e4cb0'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`option-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`option-output-filename\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -663,9 +663,9 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`option-output-filen ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`output-format-json\` should produce expected output: Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash"],"version":"3ffb54e851eb354c0170"}"`; +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`output-format-json\` > should produce expected output > Asset file 'main.asset.json' should match snapshot 1`] = `"{"dependencies":["lodash"],"version":"3ffb54e851eb354c0170"}"`; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`output-format-json\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`output-format-json\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -675,12 +675,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`output-format-json\ ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`overrides\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`overrides\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-blob', 'wp-script-handle-for-rxjs', 'wp-url'), 'version' => '96b889b779d0e972229e'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`overrides\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`overrides\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -714,36 +714,36 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`overrides\` should ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`polyfill-magic-comment\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`polyfill-magic-comment\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-polyfill'), 'version' => '48f720dd907eea75c1fd'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`polyfill-magic-comment\` should produce expected output: External modules should match snapshot 1`] = `[]`; +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`polyfill-magic-comment\` > should produce expected output > External modules should match snapshot 1`] = `[]`; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`polyfill-magic-comment-minified\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`polyfill-magic-comment-minified\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-polyfill'), 'version' => '31d6cfe0d16ae931b73c'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`polyfill-magic-comment-minified\` should produce expected output: External modules should match snapshot 1`] = `[]`; +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`polyfill-magic-comment-minified\` > should produce expected output > External modules should match snapshot 1`] = `[]`; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'a.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`runtime-chunk-single\` > should produce expected output > Asset file 'a.asset.php' should match snapshot 1`] = ` " array('wp-blob'), 'version' => 'e8bba1440f7003e4988b', 'handle' => 'runtime-chunk-single-scripts-a'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'b.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`runtime-chunk-single\` > should produce expected output > Asset file 'b.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => '8c8cea3e6127c51f6aac', 'handle' => 'runtime-chunk-single-scripts-b'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'runtime.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`runtime-chunk-single\` > should produce expected output > Asset file 'runtime.asset.php' should match snapshot 1`] = ` " array(), 'version' => '2028470a91d96d4f591d', 'handle' => 'runtime-chunk-single-scripts-runtime'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`runtime-chunk-single\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`runtime-chunk-single\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -761,12 +761,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`runtime-chunk-singl ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`style-cache-group\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`style-cache-group\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('wp-blob'), 'version' => '17feeb4acc899d3bd2d9'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`style-cache-group\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`style-cache-group\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -779,12 +779,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`style-cache-group\` ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`style-imports\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`style-imports\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => '3e9f6eea2f65b1419318'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`style-imports\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`style-imports\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -802,12 +802,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`style-imports\` sho ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`wordpress\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => 'e9b912dd9bf4a70e4cb0'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`wordpress\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -825,12 +825,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress\` should ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`wordpress-interactivity\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-interactivity'), 'version' => '6d6dd27ae8961236ce08'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interactivity\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`wordpress-interactivity\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", @@ -848,12 +848,12 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-interacti ] `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-require\` should produce expected output: Asset file 'main.asset.php' should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`wordpress-require\` > should produce expected output > Asset file 'main.asset.php' should match snapshot 1`] = ` " array('lodash', 'wp-blob'), 'version' => '21cb4044c3c0bc8f09f8'); " `; -exports[`DependencyExtractionWebpackPlugin scripts Webpack \`wordpress-require\` should produce expected output: External modules should match snapshot 1`] = ` +exports[`DependencyExtractionWebpackPlugin scripts > Webpack \`wordpress-require\` > should produce expected output > External modules should match snapshot 1`] = ` [ { "externalType": "window", diff --git a/packages/dependency-extraction-webpack-plugin/test/build.js b/packages/dependency-extraction-webpack-plugin/test/build.js index 54c517db56b606..a4f4bb6d1923b8 100644 --- a/packages/dependency-extraction-webpack-plugin/test/build.js +++ b/packages/dependency-extraction-webpack-plugin/test/build.js @@ -1,14 +1,31 @@ -const fs = require( 'fs' ); -const path = require( 'path' ); -const { globSync } = require( 'glob' ); -const mkdirp = require( 'mkdirp' ).mkdirp.sync; -const rimraf = require( 'rimraf' ).sync; -const webpack = require( 'webpack' ); - -const fixturesPath = path.join( __dirname, 'fixtures' ); +import fs from 'node:fs'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { globSync } from 'glob'; +import { mkdirpSync } from 'mkdirp'; +import { rimrafSync } from 'rimraf'; +import { + afterAll, + afterEach, + beforeEach, + describe, + expect, + test, +} from 'vitest'; +import webpack from 'webpack'; + +const require = createRequire( import.meta.url ); +const currentDirectory = path.dirname( fileURLToPath( import.meta.url ) ); +const fixturesPath = path.join( currentDirectory, 'fixtures' ); const configFixtures = fs.readdirSync( fixturesPath ).sort(); -afterAll( () => rimraf( path.join( __dirname, 'build' ) ) ); +afterAll( () => rimrafSync( path.join( currentDirectory, 'build' ) ) ); + +function expectCompilationError( stats ) { + expect( stats.hasErrors() ).toBe( true ); + expect( stats.toString( { errors: true, all: false } ) ).toMatchSnapshot(); +} describe.each( /** @type {const} */ ( [ 'scripts', 'modules' ] ) )( 'DependencyExtractionWebpackPlugin %s', @@ -16,19 +33,19 @@ describe.each( /** @type {const} */ ( [ 'scripts', 'modules' ] ) )( describe.each( configFixtures )( 'Webpack `%s`', ( configCase ) => { const testDirectory = path.join( fixturesPath, configCase ); const outputDirectory = path.join( - __dirname, + currentDirectory, 'build', moduleMode, configCase ); beforeEach( () => { - rimraf( outputDirectory ); - mkdirp( outputDirectory ); + rimrafSync( outputDirectory ); + mkdirpSync( outputDirectory ); } ); // This afterEach is necessary to prevent watched tests from retriggering on every run. - afterEach( () => rimraf( outputDirectory ) ); + afterEach( () => rimrafSync( outputDirectory ) ); test( 'should produce expected output', async () => { const options = Object.assign( @@ -69,15 +86,10 @@ describe.each( /** @type {const} */ ( [ 'scripts', 'modules' ] ) )( } ) ); - /* eslint-disable jest/no-conditional-expect */ if ( configCase.includes( 'error' ) ) { - expect( stats.hasErrors() ).toBe( true ); - expect( - stats.toString( { errors: true, all: false } ) - ).toMatchSnapshot(); + expectCompilationError( stats ); return; } - /* eslint-enable jest/no-conditional-expect */ if ( stats.hasErrors() ) { throw new Error( @@ -137,7 +149,7 @@ describe.each( /** @type {const} */ ( [ 'scripts', 'modules' ] ) )( ( moduleMode ) => { const fixtureDirectory = path.join( fixturesPath, 'style-cache-group' ); const workingDirectory = path.join( - __dirname, + currentDirectory, 'build', moduleMode, 'style-cache-group-version' @@ -145,8 +157,8 @@ describe.each( /** @type {const} */ ( [ 'scripts', 'modules' ] ) )( const sourceDirectory = path.join( workingDirectory, 'src' ); beforeEach( () => { - rimraf( workingDirectory ); - mkdirp( sourceDirectory ); + rimrafSync( workingDirectory ); + mkdirpSync( sourceDirectory ); for ( const file of [ 'index.js', 'style.css' ] ) { fs.copyFileSync( path.join( fixtureDirectory, file ), @@ -155,7 +167,7 @@ describe.each( /** @type {const} */ ( [ 'scripts', 'modules' ] ) )( } } ); - afterEach( () => rimraf( workingDirectory ) ); + afterEach( () => rimrafSync( workingDirectory ) ); const build = async ( outputDirectory ) => { const options = Object.assign( diff --git a/packages/dependency-extraction-webpack-plugin/test/util.js b/packages/dependency-extraction-webpack-plugin/test/util.js index baaa200b1c5d18..0b688cf06de110 100644 --- a/packages/dependency-extraction-webpack-plugin/test/util.js +++ b/packages/dependency-extraction-webpack-plugin/test/util.js @@ -1,8 +1,8 @@ -const { - camelCaseDash, - defaultRequestToExternal, - defaultRequestToHandle, -} = require( '../lib/util' ); +import { createRequire } from 'node:module'; +import { describe, expect, test } from 'vitest'; + +const { camelCaseDash, defaultRequestToExternal, defaultRequestToHandle } = + createRequire( import.meta.url )( '../lib/util' ); describe( 'camelCaseDash', () => { test( 'does not change a single word', () => { diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index f901c5e2ecdd27..a7430717c60164 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -48,7 +48,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@wordpress/jest-console": "file:../jest-console" + "@wordpress/jest-console": "file:../jest-console", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/deprecated/src/test/index.ts b/packages/deprecated/src/test/index.ts index 22d735d1b06260..f629cc03cb2c2e 100644 --- a/packages/deprecated/src/test/index.ts +++ b/packages/deprecated/src/test/index.ts @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it } from 'vitest'; import { didAction } from '@wordpress/hooks'; import deprecated, { logged } from '..'; diff --git a/packages/design-system-mcp/package.json b/packages/design-system-mcp/package.json index eadc9c2695f068..5669e2f0909fbd 100644 --- a/packages/design-system-mcp/package.json +++ b/packages/design-system-mcp/package.json @@ -46,8 +46,8 @@ "zod": "^4.3.6" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/design-system-mcp/src/test/data.ts b/packages/design-system-mcp/src/test/data.ts index 76d89a914365e4..36f91e3f87c033 100644 --- a/packages/design-system-mcp/src/test/data.ts +++ b/packages/design-system-mcp/src/test/data.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { getComponents, getComponentDetail, @@ -16,7 +17,7 @@ const originalFetch = globalThis.fetch; function mockFetchResponses( handlers: Record< string, { ok: boolean; body: unknown } > ) { - globalThis.fetch = jest.fn( ( url: RequestInfo | URL ) => { + globalThis.fetch = vi.fn( ( url: RequestInfo | URL ) => { const urlStr = url.toString(); const handler = handlers[ urlStr ]; if ( ! handler ) { @@ -37,7 +38,7 @@ function mockFetchResponses( describe( 'data', () => { beforeEach( () => { resetCache(); - globalThis.fetch = jest.fn(); + globalThis.fetch = vi.fn(); } ); afterEach( () => { diff --git a/packages/design-system-mcp/src/test/format.ts b/packages/design-system-mcp/src/test/format.ts index 3b93eede2e0ef9..622a035cbae92e 100644 --- a/packages/design-system-mcp/src/test/format.ts +++ b/packages/design-system-mcp/src/test/format.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { formatComponents, formatComponentDetail } from '../format'; describe( 'formatComponents', () => { diff --git a/packages/design-system-mcp/src/test/parse-components.ts b/packages/design-system-mcp/src/test/parse-components.ts index 2011e2f8c0f74c..16a41db16a6ea0 100644 --- a/packages/design-system-mcp/src/test/parse-components.ts +++ b/packages/design-system-mcp/src/test/parse-components.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { packageNameFromPath, parseProps, diff --git a/packages/design-system-mcp/src/tools/test/get-component-details.ts b/packages/design-system-mcp/src/tools/test/get-component-details.ts index 41daa77a5d961d..8b1b1df623879f 100644 --- a/packages/design-system-mcp/src/tools/test/get-component-details.ts +++ b/packages/design-system-mcp/src/tools/test/get-component-details.ts @@ -1,13 +1,15 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { handler } from '../get-component-details'; import { getComponentDetail } from '../../data'; import { formatComponentDetail } from '../../format'; import type { ComponentDetail } from '../../types'; -jest.mock( '../../data' ); +vi.mock( import( '../../data' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + getComponentDetail: vi.fn(), +} ) ); -const mockGetComponentDetail = getComponentDetail as jest.MockedFunction< - typeof getComponentDetail ->; +const mockGetComponentDetail = vi.mocked( getComponentDetail ); function fakeDetail( name: string ): ComponentDetail { return { diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 1e7eaa94c64950..8ff4afabfff325 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -43,7 +43,8 @@ "unified": "^7.1.0" }, "devDependencies": { - "@types/babel__core": "^7.20.5" + "@types/babel__core": "^7.20.5", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/docgen/test/engine.js b/packages/docgen/test/engine.js index d7aee303dbfbb1..974837968a0c00 100644 --- a/packages/docgen/test/engine.js +++ b/packages/docgen/test/engine.js @@ -1,4 +1,5 @@ -const engine = require( '../lib/engine' ); +import { describe, expect, it } from 'vitest'; +import engine from '../lib/engine'; describe( 'Engine', () => { it( 'should return a void IR for undefined code', () => { diff --git a/packages/docgen/test/formatter-markdown.js b/packages/docgen/test/formatter-markdown.js index b8461635be42ae..e2524a41da337a 100644 --- a/packages/docgen/test/formatter-markdown.js +++ b/packages/docgen/test/formatter-markdown.js @@ -1,4 +1,5 @@ -const formatter = require( '../lib/markdown/formatter' ); +import { describe, expect, it } from 'vitest'; +import formatter from '../lib/markdown/formatter'; describe( 'Formatter', () => { it( 'returns markdown', () => { diff --git a/packages/docgen/test/get-export-entries.js b/packages/docgen/test/get-export-entries.js index 62de97281ae515..12aaef78bf724a 100644 --- a/packages/docgen/test/get-export-entries.js +++ b/packages/docgen/test/get-export-entries.js @@ -1,5 +1,6 @@ -const engine = require( '../lib/engine' ); -const getExportEntries = require( '../lib/get-export-entries' ); +import { describe, expect, it } from 'vitest'; +import engine from '../lib/engine'; +import getExportEntries from '../lib/get-export-entries'; /** * Parses sample code into testable structure. diff --git a/packages/docgen/test/get-intermediate-representation.js b/packages/docgen/test/get-intermediate-representation.js index 9b10cd45c50d3e..7f31ebc8f7ddf6 100644 --- a/packages/docgen/test/get-intermediate-representation.js +++ b/packages/docgen/test/get-intermediate-representation.js @@ -1,5 +1,6 @@ -const engine = require( '../lib/engine' ); -const getIntermediateRepresentation = require( '../lib/get-intermediate-representation' ); +import { describe, expect, it } from 'vitest'; +import engine from '../lib/engine'; +import getIntermediateRepresentation from '../lib/get-intermediate-representation'; /** * Parses sample code into testable structure. diff --git a/packages/docgen/test/get-jsdoc-from-token.js b/packages/docgen/test/get-jsdoc-from-token.js index 5fc419d5a8da47..b914b43364f5ad 100644 --- a/packages/docgen/test/get-jsdoc-from-token.js +++ b/packages/docgen/test/get-jsdoc-from-token.js @@ -1,5 +1,6 @@ -const getJSDocFromToken = require( '../lib/get-jsdoc-from-token' ); -const engine = require( '../lib/engine' ); +import { describe, expect, it } from 'vitest'; +import getJSDocFromToken from '../lib/get-jsdoc-from-token'; +import engine from '../lib/engine'; /** * Generate the AST necessary to assert inferred types. diff --git a/packages/docgen/test/get-type-annotation.js b/packages/docgen/test/get-type-annotation.js index 9db3c496eec4ec..f90c9942548eac 100644 --- a/packages/docgen/test/get-type-annotation.js +++ b/packages/docgen/test/get-type-annotation.js @@ -1,5 +1,6 @@ -const engine = require( '../lib/engine' ); -const getTypeAnnotation = require( '../lib/get-type-annotation' ); +import { describe, expect, it } from 'vitest'; +import engine from '../lib/engine'; +import getTypeAnnotation from '../lib/get-type-annotation'; /** * Generate the AST necessary to assert inferred types. diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index d9d49f05a54afd..05cf091deafcdb 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -44,7 +44,8 @@ "types": "build-types", "sideEffects": false, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/dom/package.json b/packages/dom/package.json index 65cf1bc20cbb0d..5e203e2fa106aa 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -46,6 +46,9 @@ "dependencies": { "@wordpress/deprecated": "file:../deprecated" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" } diff --git a/packages/dom/src/test/utils.js b/packages/dom/src/test/utils.js index 83585c88fa0b95..840e95b17ca3de 100644 --- a/packages/dom/src/test/utils.js +++ b/packages/dom/src/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { assertIsDefined } from '../utils/assert-is-defined'; describe( 'assertIsDefined', () => { diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index fe3e56f770525a..b5f4d5888b3ec2 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -81,7 +81,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@testing-library/user-event": "^14.6.1" + "@testing-library/user-event": "^14.6.1", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19", diff --git a/packages/edit-post/src/store/test/reducer.js b/packages/edit-post/src/store/test/reducer.js index 9712eb28b16468..c66bd0fb7ed7f2 100644 --- a/packages/edit-post/src/store/test/reducer.js +++ b/packages/edit-post/src/store/test/reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isSavingMetaBoxes, metaBoxLocations } from '../reducer'; describe( 'state', () => { diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 12cd7d89699139..6d973b39aee2aa 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -86,6 +86,9 @@ "change-case": "^4.1.2", "clsx": "^2.1.1" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" diff --git a/packages/edit-site/src/utils/test/get-filtered-template-parts.js b/packages/edit-site/src/utils/test/get-filtered-template-parts.js index f082e0074ff074..9b44e9b97bcd46 100644 --- a/packages/edit-site/src/utils/test/get-filtered-template-parts.js +++ b/packages/edit-site/src/utils/test/get-filtered-template-parts.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import getFilteredTemplatePartBlocks from '../get-filtered-template-parts'; const NESTED_BLOCKS = [ diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index 953fd537e22c6e..0d74b072c5cb0e 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -81,7 +81,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19", diff --git a/packages/edit-widgets/src/store/test/reducer.js b/packages/edit-widgets/src/store/test/reducer.js index 7ffe86b118e681..8354de57fc7b3f 100644 --- a/packages/edit-widgets/src/store/test/reducer.js +++ b/packages/edit-widgets/src/store/test/reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { widgetSavingLock } from '../reducer'; diff --git a/packages/editor/package.json b/packages/editor/package.json index ddc959fea29752..34038ed9390553 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -123,7 +123,8 @@ "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", "@wordpress/block-library": "file:../block-library", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/editor/src/components/media-categories/test/index.js b/packages/editor/src/components/media-categories/test/index.js index 89109baf6bdfee..6311ed1c0eb5aa 100644 --- a/packages/editor/src/components/media-categories/test/index.js +++ b/packages/editor/src/components/media-categories/test/index.js @@ -1,20 +1,21 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { dispatch, resolveSelect, select, subscribe } from '@wordpress/data'; import getInserterMediaCategories from '..'; -jest.mock( '@wordpress/data', () => ( { - dispatch: jest.fn(), - resolveSelect: jest.fn(), - select: jest.fn(), - subscribe: jest.fn(), +vi.mock( import( '@wordpress/data' ), () => ( { + dispatch: vi.fn(), + resolveSelect: vi.fn(), + select: vi.fn(), + subscribe: vi.fn(), } ) ); -jest.mock( '@wordpress/core-data', () => ( { +vi.mock( import( '@wordpress/core-data' ), () => ( { store: 'core', } ) ); describe( 'getInserterMediaCategories', () => { beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'does not include attached images for non-numeric post IDs', () => { @@ -30,7 +31,7 @@ describe( 'getInserterMediaCategories', () => { } ); it( 'fetches images attached to the current post', async () => { - const getEntityRecords = jest.fn().mockResolvedValue( [ + const getEntityRecords = vi.fn().mockResolvedValue( [ { id: 10, source_url: 'https://example.com/image.jpg', @@ -48,8 +49,8 @@ describe( 'getInserterMediaCategories', () => { }, ] ); resolveSelect.mockReturnValue( { getEntityRecords } ); - const getEntityRecordsTotalItems = jest.fn().mockReturnValue( 1 ); - const getEntityRecordsTotalPages = jest.fn().mockReturnValue( 1 ); + const getEntityRecordsTotalItems = vi.fn().mockReturnValue( 1 ); + const getEntityRecordsTotalPages = vi.fn().mockReturnValue( 1 ); select.mockReturnValue( { getEntityRecordsTotalItems, getEntityRecordsTotalPages, @@ -99,7 +100,7 @@ describe( 'getInserterMediaCategories', () => { } ); it( 'attaches and detaches attachment records', async () => { - const saveEntityRecord = jest.fn().mockResolvedValue( {} ); + const saveEntityRecord = vi.fn().mockResolvedValue( {} ); dispatch.mockReturnValue( { saveEntityRecord } ); const [ attachedImagesCategory ] = getInserterMediaCategories( @@ -196,14 +197,14 @@ describe( 'getInserterMediaCategories', () => { it( 'refetches on the resolved -> unresolved edge and unsubscribes', () => { let listener; - const unsubscribe = jest.fn(); + const unsubscribe = vi.fn(); subscribe.mockImplementation( ( cb ) => { listener = cb; return unsubscribe; } ); // Starts resolved: the grid has already fetched this query. let resolved = true; - const hasFinishedResolution = jest + const hasFinishedResolution = vi .fn() .mockImplementation( () => resolved ); select.mockReturnValue( { hasFinishedResolution } ); @@ -212,7 +213,7 @@ describe( 'getInserterMediaCategories', () => { 42, 'Post' ); - const onChange = jest.fn(); + const onChange = vi.fn(); const returnedUnsubscribe = attachedImagesCategory.subscribe( onChange, { per_page: 20 } @@ -261,10 +262,10 @@ describe( 'getInserterMediaCategories', () => { let listener; subscribe.mockImplementation( ( cb ) => { listener = cb; - return jest.fn(); + return vi.fn(); } ); let resolved = true; - const hasFinishedResolution = jest + const hasFinishedResolution = vi .fn() .mockImplementation( () => resolved ); select.mockReturnValue( { hasFinishedResolution } ); @@ -277,7 +278,7 @@ describe( 'getInserterMediaCategories', () => { ( category ) => category.name === 'openverse' ); - const onChange = jest.fn(); + const onChange = vi.fn(); images.subscribe( onChange, { per_page: 20 } ); // The Images source watches its own query (no `parent`) and still diff --git a/packages/editor/src/components/post-revisions-preview/test/preserve-client-ids.js b/packages/editor/src/components/post-revisions-preview/test/preserve-client-ids.js index aedc20f4ad3717..2c37532e6984de 100644 --- a/packages/editor/src/components/post-revisions-preview/test/preserve-client-ids.js +++ b/packages/editor/src/components/post-revisions-preview/test/preserve-client-ids.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { preserveClientIds } from '../preserve-client-ids'; describe( 'preserveClientIds', () => { diff --git a/packages/editor/src/components/post-text-editor/test/utils.js b/packages/editor/src/components/post-text-editor/test/utils.js index 7336b2ba07bb18..5c0d333b81b462 100644 --- a/packages/editor/src/components/post-text-editor/test/utils.js +++ b/packages/editor/src/components/post-text-editor/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getAdjustedCursorPosition } from '../utils'; describe( 'PostTextEditor', () => { diff --git a/packages/editor/src/components/style-book/test/categories.js b/packages/editor/src/components/style-book/test/categories.js index bc6e5ac13789b3..cbc3d881efc635 100644 --- a/packages/editor/src/components/style-book/test/categories.js +++ b/packages/editor/src/components/style-book/test/categories.js @@ -1,10 +1,11 @@ +import { describe, expect, it, vi } from 'vitest'; import { getExamplesByCategory, getTopLevelStyleBookCategories, } from '../categories'; import { STYLE_BOOK_CATEGORIES } from '../constants'; -jest.mock( '@wordpress/blocks', () => { +vi.mock( import( '@wordpress/blocks' ), () => { return { getCategories() { return [ diff --git a/packages/editor/src/hooks/push-changes-to-global-styles/test/format-style-value.js b/packages/editor/src/hooks/push-changes-to-global-styles/test/format-style-value.js index 3684bb1f1ee938..c56257fb72cac3 100644 --- a/packages/editor/src/hooks/push-changes-to-global-styles/test/format-style-value.js +++ b/packages/editor/src/hooks/push-changes-to-global-styles/test/format-style-value.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { formatStyleValue, formatBorderShorthand, diff --git a/packages/editor/src/hooks/push-changes-to-global-styles/test/style-labels.js b/packages/editor/src/hooks/push-changes-to-global-styles/test/style-labels.js index c4f681098c5e8a..69a527eae34130 100644 --- a/packages/editor/src/hooks/push-changes-to-global-styles/test/style-labels.js +++ b/packages/editor/src/hooks/push-changes-to-global-styles/test/style-labels.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { capitalCase } from 'change-case'; import { getStyleLabel, STYLE_LABELS } from '../style-labels'; diff --git a/packages/editor/src/store/utils/test/notice-builder.js b/packages/editor/src/store/utils/test/notice-builder.js index aff09c1b1a49f1..eff0bd4bb9af36 100644 --- a/packages/editor/src/store/utils/test/notice-builder.js +++ b/packages/editor/src/store/utils/test/notice-builder.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getNotificationArgumentsForSaveSuccess, getNotificationArgumentsForSaveFail, @@ -84,7 +85,6 @@ describe( 'getNotificationArgumentsForSaveSuccess()', () => { [ previousPostStatus, postStatus, isViewable ], expectedValue, ] ) => { - // eslint-disable-next-line jest/valid-title it( description, () => { previousPost.status = previousPostStatus; post.status = postStatus; @@ -166,7 +166,6 @@ describe( 'getNotificationArgumentsForSaveFail()', () => { expectedValue, options = {}, ] ) => { - // eslint-disable-next-line jest/valid-title it( description, () => { post.status = postStatus; error.code = errorCode; @@ -201,7 +200,6 @@ describe( 'getNotificationArgumentsForTrashFail()', () => { 'Trashing failed', ], ].forEach( ( [ description, error, message ] ) => { - // eslint-disable-next-line jest/valid-title it( description, () => { const expectedValue = [ message, { id: 'editor-trash-fail' } ]; expect( getNotificationArgumentsForTrashFail( { error } ) ).toEqual( diff --git a/packages/editor/src/utils/media-sideload-from-url/test/index.js b/packages/editor/src/utils/media-sideload-from-url/test/index.js index 0b0d9b38c3ec80..831b26ec68e072 100644 --- a/packages/editor/src/utils/media-sideload-from-url/test/index.js +++ b/packages/editor/src/utils/media-sideload-from-url/test/index.js @@ -1,11 +1,14 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import apiFetch from '@wordpress/api-fetch'; import { select } from '@wordpress/data'; import { transformAttachment } from '@wordpress/media-utils'; import mediaSideloadFromUrl from '..'; -jest.mock( '@wordpress/api-fetch', () => jest.fn() ); -jest.mock( '@wordpress/data', () => ( { - select: jest.fn(), +vi.mock( import( '@wordpress/api-fetch' ), () => ( { + default: vi.fn(), +} ) ); +vi.mock( import( '@wordpress/data' ), () => ( { + select: vi.fn(), } ) ); /* * The store and transformAttachment are mocked so this unit test does not pull @@ -14,9 +17,9 @@ jest.mock( '@wordpress/data', () => ( { * @wordpress/media-utils; here we only verify mediaSideloadFromUrl forwards * whatever it returns. */ -jest.mock( '../../../store', () => ( { store: 'core/editor' } ) ); -jest.mock( '@wordpress/media-utils', () => ( { - transformAttachment: jest.fn( ( attachment ) => ( { +vi.mock( import( '../../../store' ), () => ( { store: 'core/editor' } ) ); +vi.mock( import( '@wordpress/media-utils' ), () => ( { + transformAttachment: vi.fn( ( attachment ) => ( { id: attachment.id, url: attachment.source_url, alt: attachment.alt_text, @@ -44,7 +47,7 @@ function mockCurrentPost( post ) { describe( 'mediaSideloadFromUrl', () => { beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); mockCurrentPost( { id: 42 } ); } ); diff --git a/packages/editor/src/utils/test/device-type.js b/packages/editor/src/utils/test/device-type.js index 41843602f48344..1aab07694004b9 100644 --- a/packages/editor/src/utils/test/device-type.js +++ b/packages/editor/src/utils/test/device-type.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getCanvasWidthByDeviceType, getDeviceTypeByCanvasWidth, diff --git a/packages/editor/src/utils/test/get-collaborator-display-name.ts b/packages/editor/src/utils/test/get-collaborator-display-name.ts index 70220e7d0bfe48..076810b01e9a7a 100644 --- a/packages/editor/src/utils/test/get-collaborator-display-name.ts +++ b/packages/editor/src/utils/test/get-collaborator-display-name.ts @@ -1,9 +1,12 @@ +import { describe, expect, test, vi } from 'vitest'; import { getCollaboratorDisplayName } from '../get-collaborator-display-name'; -jest.mock( '@wordpress/i18n', () => ( { - ...jest.requireActual( '@wordpress/i18n' ), - __: ( text: string ) => - 'Anonymous User' === text ? 'Translated Anonymous User' : text, +vi.mock( import( '@wordpress/i18n' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + __: ( ( text: string ) => + 'Anonymous User' === text + ? 'Translated Anonymous User' + : text ) as typeof import('@wordpress/i18n').__, } ) ); describe( 'getCollaboratorDisplayName', () => { diff --git a/packages/editor/src/utils/test/media-upload.js b/packages/editor/src/utils/test/media-upload.js index 9958fa6cae29a8..4692c0fadaed1e 100644 --- a/packages/editor/src/utils/test/media-upload.js +++ b/packages/editor/src/utils/test/media-upload.js @@ -1,45 +1,46 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import mediaUpload from '../media-upload'; import { getState, reset, } from '../../components/upload-progress-snackbar/tracker'; -jest.mock( '@wordpress/media-utils', () => ( { - uploadMedia: jest.fn(), +vi.mock( import( '@wordpress/media-utils' ), () => ( { + uploadMedia: vi.fn(), } ) ); -jest.mock( '@wordpress/core-data', () => ( { +vi.mock( import( '@wordpress/core-data' ), () => ( { store: { name: 'core' }, } ) ); -jest.mock( '../../store', () => ( { +vi.mock( import( '../../store' ), () => ( { store: { name: 'core/editor' }, } ) ); -const mockLockPostSaving = jest.fn(); -const mockLockPostAutosaving = jest.fn(); +const mockLockPostSaving = vi.fn(); +const mockLockPostAutosaving = vi.fn(); -jest.mock( '@wordpress/data', () => ( { - select: jest.fn( () => ( { +vi.mock( import( '@wordpress/data' ), () => ( { + select: vi.fn( () => ( { getCurrentPost: () => ( { id: 1 } ), getEditorSettings: () => ( { allowedMimeTypes: null, maxUploadFileSize: 10 * 1024 * 1024, } ), } ) ), - dispatch: jest.fn( () => ( { - receiveEntityRecords: jest.fn(), + dispatch: vi.fn( () => ( { + receiveEntityRecords: vi.fn(), lockPostSaving: mockLockPostSaving, - unlockPostSaving: jest.fn(), + unlockPostSaving: vi.fn(), lockPostAutosaving: mockLockPostAutosaving, - unlockPostAutosaving: jest.fn(), + unlockPostAutosaving: vi.fn(), } ) ), } ) ); describe( 'mediaUpload', () => { beforeEach( () => { reset(); - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'registers files with the upload progress tracker and locks saving', () => { diff --git a/packages/editor/src/utils/test/normalize-text-string.js b/packages/editor/src/utils/test/normalize-text-string.js index 857cafabbed0cb..e6e6f00a930bf8 100644 --- a/packages/editor/src/utils/test/normalize-text-string.js +++ b/packages/editor/src/utils/test/normalize-text-string.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { normalizeTextString } from '../normalize-text-string'; describe( 'normalizeTextString', () => { diff --git a/packages/editor/src/utils/test/sync-error-messages.js b/packages/editor/src/utils/test/sync-error-messages.js index f2269de97dff1e..767accab40bcf4 100644 --- a/packages/editor/src/utils/test/sync-error-messages.js +++ b/packages/editor/src/utils/test/sync-error-messages.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getSyncErrorMessages } from '../sync-error-messages'; describe( 'getSyncErrorMessages', () => { diff --git a/packages/editor/src/utils/test/terms.js b/packages/editor/src/utils/test/terms.js index 708a14a9a81c16..aaec1b8ce30225 100644 --- a/packages/editor/src/utils/test/terms.js +++ b/packages/editor/src/utils/test/terms.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { buildTermsTree } from '../terms'; describe( 'buildTermsTree()', () => { diff --git a/packages/element/package.json b/packages/element/package.json index b01bc5d0f3f53a..cfddd426603975 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -52,7 +52,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", - "@wordpress/jest-console": "file:../jest-console" + "@wordpress/jest-console": "file:../jest-console", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/element/src/test/index.jsx b/packages/element/src/test/index.jsx index 64940e9ee8bb71..bf0298f63268d4 100644 --- a/packages/element/src/test/index.jsx +++ b/packages/element/src/test/index.jsx @@ -1,4 +1,4 @@ -/* eslint-disable testing-library/render-result-naming-convention */ +import { describe, expect, it } from 'vitest'; import { concatChildren, createElement, @@ -125,5 +125,3 @@ describe( 'element', () => { } ); } ); } ); - -/* eslint-enable testing-library/render-result-naming-convention */ diff --git a/packages/element/src/test/platform.jsx b/packages/element/src/test/platform.jsx index 1fcd74cf8d8c6b..5b7b04ed4b7000 100644 --- a/packages/element/src/test/platform.jsx +++ b/packages/element/src/test/platform.jsx @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import Platform from '../platform'; describe( 'Platform', () => { diff --git a/packages/element/src/test/serialize.jsx b/packages/element/src/test/serialize.jsx index 2ee4549135cb04..f608db6f4a0e21 100644 --- a/packages/element/src/test/serialize.jsx +++ b/packages/element/src/test/serialize.jsx @@ -1,4 +1,4 @@ -/* eslint-disable testing-library/render-result-naming-convention */ +import { describe, expect, it } from 'vitest'; import { Component, createContext, @@ -756,5 +756,3 @@ describe( 'renderStyle()', () => { } ); } ); } ); - -/* eslint-enable testing-library/render-result-naming-convention */ diff --git a/packages/element/src/test/utils.js b/packages/element/src/test/utils.js index fd64a3112311f9..88e9d9dba033f7 100644 --- a/packages/element/src/test/utils.js +++ b/packages/element/src/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { createElement } from '../react'; import { isEmptyElement } from '../utils'; diff --git a/packages/env/lib/config/test/__snapshots__/config-integration.js.snap b/packages/env/lib/config/test/__snapshots__/config-integration.js.snap index c44a7b3a0a7012..423e9dab482396 100644 --- a/packages/env/lib/config/test/__snapshots__/config-integration.js.snap +++ b/packages/env/lib/config/test/__snapshots__/config-integration.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Config Integration should load local and override configuration files 1`] = ` +exports[`Config Integration > should load local and override configuration files 1`] = ` { "configDirectoryPath": "/test/gutenberg", "customConfigPath": null, @@ -85,7 +85,7 @@ exports[`Config Integration should load local and override configuration files 1 } `; -exports[`Config Integration should load local configuration file 1`] = ` +exports[`Config Integration > should load local configuration file 1`] = ` { "configDirectoryPath": "/test/gutenberg", "customConfigPath": null, @@ -170,7 +170,7 @@ exports[`Config Integration should load local configuration file 1`] = ` } `; -exports[`Config Integration should use default configuration 1`] = ` +exports[`Config Integration > should use default configuration 1`] = ` { "configDirectoryPath": "/test/gutenberg", "customConfigPath": null, @@ -255,7 +255,7 @@ exports[`Config Integration should use default configuration 1`] = ` } `; -exports[`Config Integration should use environment variables over local and override configuration files 1`] = ` +exports[`Config Integration > should use environment variables over local and override configuration files 1`] = ` { "configDirectoryPath": "/test/gutenberg", "customConfigPath": null, diff --git a/packages/env/lib/config/test/add-or-replace-port.js b/packages/env/lib/config/test/add-or-replace-port.js index 9621ebf3035d47..f71e88ef7daf2b 100644 --- a/packages/env/lib/config/test/add-or-replace-port.js +++ b/packages/env/lib/config/test/add-or-replace-port.js @@ -1,11 +1,9 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const addOrReplacePort = require( '../add-or-replace-port.js' ); describe( 'addOrReplacePort', () => { - afterEach( () => { - jest.clearAllMocks(); - } ); - it( 'should add or replace port with various inputs', () => { const testMap = [ // Addition diff --git a/packages/env/lib/config/test/config-integration.js b/packages/env/lib/config/test/config-integration.js index 39c518effa4df1..dc18a6b01961ae 100644 --- a/packages/env/lib/config/test/config-integration.js +++ b/packages/env/lib/config/test/config-integration.js @@ -1,42 +1,60 @@ -'use strict'; -const { readFile } = require( 'fs' ).promises; -const { existsSync } = require( 'fs' ); -const loadConfig = require( '../load-config' ); -const detectDirectoryType = require( '../detect-directory-type' ); -const md5 = require( '../../md5' ); - -jest.mock( 'fs', () => ( { - promises: { - readFile: jest.fn(), - stat: jest.fn().mockResolvedValue( true ), - mkdir: jest.fn(), - writeFile: jest.fn(), +import { createRequire } from 'node:module'; +import { + afterAll, + afterEach, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; +const require = createRequire( import.meta.url ); +const dns = require( 'node:dns' ); +const fs = require( 'node:fs' ); +const readFile = vi.spyOn( fs.promises, 'readFile' ); +const stat = vi.spyOn( fs.promises, 'stat' ); +vi.spyOn( fs.promises, 'mkdir' ).mockResolvedValue(); +vi.spyOn( fs.promises, 'writeFile' ).mockResolvedValue(); +const existsSync = vi.spyOn( fs, 'existsSync' ); +const resolveDns = vi.spyOn( dns.promises, 'resolve' ); +const gotPath = require.resolve( 'got' ); +const originalGot = require( gotPath ); +const got = vi.fn( ( url ) => ( { + json: () => { + if ( url === 'https://api.wordpress.org/core/stable-check/1.0/' ) { + return Promise.resolve( { + '1.0': 'insecure', + '99.1.1': 'outdated', + '100.0.0': 'latest', + '100.0.1': 'fancy', + } ); + } }, - existsSync: jest.fn().mockReturnValue( false ), } ) ); - -// This mocks a small response with a format matching the stable-check API. -// It makes getLatestWordPressVersion resolve to "100.0.0". -jest.mock( 'got', () => - jest.fn( ( url ) => ( { - json: () => { - if ( url === 'https://api.wordpress.org/core/stable-check/1.0/' ) { - return Promise.resolve( { - '1.0': 'insecure', - '99.1.1': 'outdated', - '100.0.0': 'latest', - '100.0.1': 'fancy', - } ); - } - }, - } ) ) -); - -jest.mock( '../detect-directory-type', () => jest.fn() ); +require.cache[ gotPath ].exports = got; +const detectDirectoryTypePath = require.resolve( '../detect-directory-type' ); +const originalDetectDirectoryType = require( detectDirectoryTypePath ); +const detectDirectoryType = vi.fn(); +require.cache[ detectDirectoryTypePath ].exports = detectDirectoryType; +const loadConfig = require( '../load-config' ); +const md5 = require( '../../md5' ); +afterAll( () => { + require.cache[ gotPath ].exports = originalGot; + require.cache[ detectDirectoryTypePath ].exports = + originalDetectDirectoryType; + vi.restoreAllMocks(); + delete require.cache[ require.resolve( '../load-config' ) ]; + delete require.cache[ require.resolve( '../parse-config' ) ]; + delete require.cache[ require.resolve( '../../wordpress' ) ]; +} ); describe( 'Config Integration', () => { beforeEach( () => { + vi.clearAllMocks(); process.env.WP_ENV_HOME = '/cache'; + stat.mockResolvedValue( true ); + existsSync.mockReturnValue( false ); + resolveDns.mockResolvedValue( [ '198.143.164.252' ] ); detectDirectoryType.mockResolvedValue( null ); } ); diff --git a/packages/env/lib/config/test/get-cache-directory.js b/packages/env/lib/config/test/get-cache-directory.js index 1be8b83aa2cdd5..20f10455aa94ef 100644 --- a/packages/env/lib/config/test/get-cache-directory.js +++ b/packages/env/lib/config/test/get-cache-directory.js @@ -1,18 +1,29 @@ -'use strict'; -const { stat } = require( 'fs' ).promises; -const { homedir } = require( 'os' ); +import { createRequire } from 'node:module'; +import { + afterAll, + afterEach, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; +const require = createRequire( import.meta.url ); +const fs = require( 'node:fs' ); +const os = require( 'node:os' ); +const stat = vi.spyOn( fs.promises, 'stat' ); +const homedir = vi.spyOn( os, 'homedir' ); const getCacheDirectory = require( '../get-cache-directory' ); -jest.mock( 'fs', () => ( { - promises: { - stat: jest.fn(), - }, -} ) ); -jest.mock( 'os', () => ( { - homedir: jest.fn(), -} ) ); +afterAll( () => { + vi.restoreAllMocks(); +} ); describe( 'getCacheDirectory', () => { + beforeEach( () => { + vi.clearAllMocks(); + } ); + afterEach( () => { delete process.env.WP_ENV_HOME; } ); diff --git a/packages/env/lib/config/test/merge-configs.js b/packages/env/lib/config/test/merge-configs.js index ae47253f4feb4a..119064514ebdf6 100644 --- a/packages/env/lib/config/test/merge-configs.js +++ b/packages/env/lib/config/test/merge-configs.js @@ -1,4 +1,6 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const mergeConfigs = require( '../merge-configs' ); describe( 'mergeConfigs', () => { diff --git a/packages/env/lib/config/test/parse-config.js b/packages/env/lib/config/test/parse-config.js index 27bcee38609f39..65ea7e5016c268 100644 --- a/packages/env/lib/config/test/parse-config.js +++ b/packages/env/lib/config/test/parse-config.js @@ -1,16 +1,41 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { + afterAll, + afterEach, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; +const require = createRequire( import.meta.url ); +const gotPath = require.resolve( 'got' ); +const originalGot = require( gotPath ); +require.cache[ gotPath ].exports = vi.fn(); +const readRawConfigFilePath = require.resolve( '../read-raw-config-file' ); +const originalReadRawConfigFile = require( readRawConfigFilePath ); +const readRawConfigFile = vi.fn(); +require.cache[ readRawConfigFilePath ].exports = readRawConfigFile; +const detectDirectoryTypePath = require.resolve( '../detect-directory-type' ); +const originalDetectDirectoryType = require( detectDirectoryTypePath ); +const detectDirectoryType = vi.fn(); +require.cache[ detectDirectoryTypePath ].exports = detectDirectoryType; +const wordpressModule = require( '../../wordpress' ); +const originalGetLatestWordPressVersion = + wordpressModule.getLatestWordPressVersion; +const getLatestWordPressVersion = vi.fn(); +wordpressModule.getLatestWordPressVersion = getLatestWordPressVersion; const { parseConfig } = require( '../parse-config' ); -const readRawConfigFile = require( '../read-raw-config-file' ); -const { getLatestWordPressVersion } = require( '../../wordpress' ); const { ValidationError } = require( '../validate-config' ); -const detectDirectoryType = require( '../detect-directory-type' ); - -jest.mock( 'got', () => jest.fn() ); -jest.mock( '../read-raw-config-file', () => jest.fn() ); -jest.mock( '../detect-directory-type', () => jest.fn() ); -jest.mock( '../../wordpress', () => ( { - getLatestWordPressVersion: jest.fn(), -} ) ); +afterAll( () => { + require.cache[ gotPath ].exports = originalGot; + require.cache[ readRawConfigFilePath ].exports = originalReadRawConfigFile; + require.cache[ detectDirectoryTypePath ].exports = + originalDetectDirectoryType; + wordpressModule.getLatestWordPressVersion = + originalGetLatestWordPressVersion; + delete require.cache[ require.resolve( '../parse-config' ) ]; +} ); /** * Since our configurations are merged, we will want to refer to the parsed default config frequently. @@ -74,7 +99,7 @@ describe( 'parseConfig', () => { } ); afterEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); delete process.env.WP_ENV_PORT; delete process.env.WP_ENV_TESTS_PORT; delete process.env.WP_ENV_CORE; diff --git a/packages/env/lib/config/test/parse-source-string.js b/packages/env/lib/config/test/parse-source-string.js index b482cb367a42eb..1de8eed8dcd394 100644 --- a/packages/env/lib/config/test/parse-source-string.js +++ b/packages/env/lib/config/test/parse-source-string.js @@ -1,13 +1,15 @@ -'use strict'; -/* eslint-disable jest/no-conditional-expect */ -const path = require( 'path' ); -const { homedir } = require( 'os' ); +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const os = require( 'node:os' ); +const homedir = vi.spyOn( os, 'homedir' ); const { ValidationError } = require( '../validate-config' ); const { parseSourceString } = require( '../parse-source-string' ); -jest.mock( 'os', () => ( { - homedir: jest.fn(), -} ) ); +afterAll( () => { + vi.restoreAllMocks(); +} ); describe( 'parseSourceString', () => { const options = { @@ -144,4 +146,3 @@ describe( 'parseSourceString', () => { } ); } ); } ); -/* eslint-enable jest/no-conditional-expect */ diff --git a/packages/env/lib/config/test/post-process-config.js b/packages/env/lib/config/test/post-process-config.js index d50e16e29824d8..87cbf3ce58974c 100644 --- a/packages/env/lib/config/test/post-process-config.js +++ b/packages/env/lib/config/test/post-process-config.js @@ -1,12 +1,10 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const { ValidationError } = require( '..' ); const postProcessConfig = require( '../post-process-config' ); describe( 'postProcessConfig', () => { - afterEach( () => { - jest.clearAllMocks(); - } ); - it( 'should merge relevant root options into environment options', async () => { const processed = await postProcessConfig( { port: 123, diff --git a/packages/env/lib/config/test/read-raw-config-file.js b/packages/env/lib/config/test/read-raw-config-file.js index d59bc14a12326b..4e52bb9f1a8bf3 100644 --- a/packages/env/lib/config/test/read-raw-config-file.js +++ b/packages/env/lib/config/test/read-raw-config-file.js @@ -1,18 +1,18 @@ -'use strict'; -/* eslint-disable jest/no-conditional-expect */ -const { readFile } = require( 'fs' ).promises; +import { createRequire } from 'node:module'; +import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const fs = require( 'node:fs' ); +const readFile = vi.spyOn( fs.promises, 'readFile' ); const readRawConfigFile = require( '../read-raw-config-file' ); const { ValidationError } = require( '../validate-config' ); -jest.mock( 'fs', () => ( { - promises: { - readFile: jest.fn(), - }, -} ) ); +afterAll( () => { + vi.restoreAllMocks(); +} ); describe( 'readRawConfigFile', () => { afterEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'returns null if it cannot find a file', async () => { @@ -36,4 +36,3 @@ describe( 'readRawConfigFile', () => { } } ); } ); -/* eslint-enable jest/no-conditional-expect */ diff --git a/packages/env/lib/config/test/validate-config.js b/packages/env/lib/config/test/validate-config.js index 34ed602243c2f2..8d531084dfc352 100644 --- a/packages/env/lib/config/test/validate-config.js +++ b/packages/env/lib/config/test/validate-config.js @@ -1,4 +1,6 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const { ValidationError, checkString, diff --git a/packages/env/lib/test/__snapshots__/md5.js.snap b/packages/env/lib/test/__snapshots__/md5.js.snap index 02a7150d550f42..bc2651824e3d34 100644 --- a/packages/env/lib/test/__snapshots__/md5.js.snap +++ b/packages/env/lib/test/__snapshots__/md5.js.snap @@ -1,9 +1,9 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`md5 creates a hash of a number 1`] = `"55587a910882016321201e6ebbc9f595"`; +exports[`md5 > creates a hash of a number 1`] = `"55587a910882016321201e6ebbc9f595"`; -exports[`md5 creates a hash of a object 1`] = `"a0294b0e88367a0ff800d78413356538"`; +exports[`md5 > creates a hash of a object 1`] = `"a0294b0e88367a0ff800d78413356538"`; -exports[`md5 creates a hash of a string 1`] = `"5d41402abc4b2a76b9719d911017c592"`; +exports[`md5 > creates a hash of a string 1`] = `"5d41402abc4b2a76b9719d911017c592"`; -exports[`md5 creates a hash of null 1`] = `"37a6259cc0c1dae299a7866489dff0bd"`; +exports[`md5 > creates a hash of null 1`] = `"37a6259cc0c1dae299a7866489dff0bd"`; diff --git a/packages/env/lib/test/build-docker-compose-config.js b/packages/env/lib/test/build-docker-compose-config.js index 9b5e08180b0673..eedd5c9434af6d 100644 --- a/packages/env/lib/test/build-docker-compose-config.js +++ b/packages/env/lib/test/build-docker-compose-config.js @@ -1,10 +1,27 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { afterAll, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const getHostUserPath = require.resolve( '../runtime/docker/get-host-user' ); +const originalGetHostUser = require( getHostUserPath ); +const getHostUser = vi.fn( () => ( { + name: 'test', + uid: 1, + gid: 2, + fullUser: '1:2', +} ) ); +require.cache[ getHostUserPath ].exports = getHostUser; const buildDockerComposeConfig = require( '../runtime/docker/build-docker-compose-config' ); const { wordpressDockerFileContents, getLoopbackPortConfig, } = require( '../runtime/docker/docker-config' ); -const getHostUser = require( '../runtime/docker/get-host-user' ); + +afterAll( () => { + require.cache[ getHostUserPath ].exports = originalGetHostUser; + delete require.cache[ + require.resolve( '../runtime/docker/build-docker-compose-config' ) + ]; +} ); // The basic config keys which build docker compose config requires. const CONFIG = { @@ -15,16 +32,6 @@ const CONFIG = { configDirectoryPath: '/path/to/config', }; -jest.mock( '../runtime/docker/get-host-user', () => jest.fn() ); -getHostUser.mockImplementation( () => { - return { - name: 'test', - uid: 1, - gid: 2, - fullUser: '1:2', - }; -} ); - describe( 'buildDockerComposeConfig', () => { it( 'should map directories before individual sources', () => { const envConfig = { diff --git a/packages/env/lib/test/cache.js b/packages/env/lib/test/cache.js index 53cbb3bff2c515..940e837ef0cefd 100644 --- a/packages/env/lib/test/cache.js +++ b/packages/env/lib/test/cache.js @@ -1,5 +1,10 @@ -'use strict'; -const { readFile, writeFile } = require( 'fs' ).promises; +import { createRequire } from 'node:module'; +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const fs = require( 'node:fs' ); +const readFile = vi.spyOn( fs.promises, 'readFile' ); +const writeFile = vi.spyOn( fs.promises, 'writeFile' ); +vi.spyOn( fs.promises, 'mkdir' ).mockResolvedValue(); const { didCacheChange, setCache, @@ -7,13 +12,9 @@ const { getCacheFile, } = require( '../cache' ); -jest.mock( 'fs', () => ( { - promises: { - readFile: jest.fn(), - writeFile: jest.fn(), - mkdir: jest.fn(), - }, -} ) ); +afterAll( () => { + vi.restoreAllMocks(); +} ); const cacheOptions = { workDirectoryPath: '/a/b/c', @@ -38,7 +39,7 @@ function setupWriteFile() { describe( 'cache file', () => { beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); describe( 'didCacheChange', () => { diff --git a/packages/env/lib/test/cli.js b/packages/env/lib/test/cli.js index b8f400bb7da2bb..a36cce83e82d54 100644 --- a/packages/env/lib/test/cli.js +++ b/packages/env/lib/test/cli.js @@ -1,33 +1,39 @@ -'use strict'; -const cli = require( '../cli' ); -const env = require( '../env' ); - -/** - * Mocked dependencies - */ -jest.spyOn( process, 'exit' ).mockImplementation( () => {} ); -jest.mock( 'ora', () => () => { - const spinner = { text: '', succeed: jest.fn(), fail: jest.fn() }; +import { createRequire } from 'node:module'; +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const oraPath = require.resolve( 'ora' ); +const originalOra = require( oraPath ); +const createSpinner = vi.fn( () => { + const spinner = { text: '', succeed: vi.fn(), fail: vi.fn() }; spinner.start = () => spinner; return spinner; } ); -jest.mock( '../env', () => { - const actual = jest.requireActual( '../env' ); - return { - start: jest.fn( Promise.resolve.bind( Promise ) ), - stop: jest.fn( Promise.resolve.bind( Promise ) ), - reset: jest.fn( Promise.resolve.bind( Promise ) ), - clean: jest.fn( Promise.resolve.bind( Promise ) ), - run: jest.fn( Promise.resolve.bind( Promise ) ), - destroy: jest.fn( Promise.resolve.bind( Promise ) ), - cleanup: jest.fn( Promise.resolve.bind( Promise ) ), - ValidationError: actual.ValidationError, - LifecycleScriptError: actual.LifecycleScriptError, - }; +require.cache[ oraPath ].exports = createSpinner; +const envPath = require.resolve( '../env' ); +const originalEnv = require( envPath ); +const env = { + start: vi.fn( Promise.resolve.bind( Promise ) ), + stop: vi.fn( Promise.resolve.bind( Promise ) ), + reset: vi.fn( Promise.resolve.bind( Promise ) ), + clean: vi.fn( Promise.resolve.bind( Promise ) ), + run: vi.fn( Promise.resolve.bind( Promise ) ), + destroy: vi.fn( Promise.resolve.bind( Promise ) ), + cleanup: vi.fn( Promise.resolve.bind( Promise ) ), + ValidationError: originalEnv.ValidationError, + LifecycleScriptError: originalEnv.LifecycleScriptError, +}; +require.cache[ envPath ].exports = env; +const processExit = vi.spyOn( process, 'exit' ).mockImplementation( () => {} ); +const cli = require( '../cli' ); +afterAll( () => { + require.cache[ oraPath ].exports = originalOra; + require.cache[ envPath ].exports = originalEnv; + processExit.mockRestore(); + delete require.cache[ require.resolve( '../cli' ) ]; } ); describe( 'env cli', () => { - beforeEach( jest.clearAllMocks ); + beforeEach( vi.clearAllMocks ); it( 'parses start commands.', () => { cli().parse( [ 'start' ] ); @@ -151,20 +157,18 @@ describe( 'env cli', () => { env.start.mockRejectedValueOnce( { message: 'failure message', } ); - const consoleError = console.error; - console.error = jest.fn(); - const processExit = process.exit; - process.exit = jest.fn(); + const consoleError = vi + .spyOn( console, 'error' ) + .mockImplementation( () => {} ); cli().parse( [ 'start' ] ); const { spinner } = env.start.mock.calls[ 0 ][ 0 ]; await env.start.mock.results[ 0 ].value.catch( () => {} ); expect( spinner.fail ).toHaveBeenCalledWith( 'failure message' ); - expect( console.error ).toHaveBeenCalled(); - expect( process.exit ).toHaveBeenCalledWith( 1 ); - console.error = consoleError; - process.exit = processExit; + expect( consoleError ).toHaveBeenCalled(); + expect( processExit ).toHaveBeenCalledWith( 1 ); + consoleError.mockRestore(); } ); it( 'handles failed docker commands with errors.', async () => { env.start.mockRejectedValueOnce( { @@ -172,12 +176,12 @@ describe( 'env cli', () => { out: 'message', exitCode: 1, } ); - const consoleError = console.error; - console.error = jest.fn(); - const processExit = process.exit; - process.exit = jest.fn(); - const stderr = process.stderr.write; - process.stderr.write = jest.fn(); + const stdout = vi + .spyOn( process.stdout, 'write' ) + .mockImplementation( () => true ); + const stderr = vi + .spyOn( process.stderr, 'write' ) + .mockImplementation( () => true ); cli().parse( [ 'start' ] ); const { spinner } = env.start.mock.calls[ 0 ][ 0 ]; @@ -186,10 +190,10 @@ describe( 'env cli', () => { expect( spinner.fail ).toHaveBeenCalledWith( 'Error while running docker compose command.' ); - expect( process.stderr.write ).toHaveBeenCalledWith( 'failure error' ); - expect( process.exit ).toHaveBeenCalledWith( 1 ); - console.error = consoleError; - process.exit = processExit; - process.stderr.write = stderr; + expect( stdout ).toHaveBeenCalledWith( 'message' ); + expect( stderr ).toHaveBeenCalledWith( 'failure error' ); + expect( processExit ).toHaveBeenCalledWith( 1 ); + stdout.mockRestore(); + stderr.mockRestore(); } ); } ); diff --git a/packages/env/lib/test/execute-lifecycle-script.js b/packages/env/lib/test/execute-lifecycle-script.js index c2d147057ff348..4c7f6996da35be 100644 --- a/packages/env/lib/test/execute-lifecycle-script.js +++ b/packages/env/lib/test/execute-lifecycle-script.js @@ -1,5 +1,6 @@ -'use strict'; -/* eslint-disable jest/no-conditional-expect */ +import { createRequire } from 'node:module'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); const { LifecycleScriptError, executeLifecycleScript, @@ -7,11 +8,11 @@ const { describe( 'executeLifecycleScript', () => { const spinner = { - info: jest.fn(), + info: vi.fn(), }; afterEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'should do nothing without event option when debugging', async () => { @@ -53,4 +54,3 @@ describe( 'executeLifecycleScript', () => { } } ); } ); -/* eslint-enable jest/no-conditional-expect */ diff --git a/packages/env/lib/test/md5.js b/packages/env/lib/test/md5.js index 5187ddf2879eff..fe64d765b21c9a 100644 --- a/packages/env/lib/test/md5.js +++ b/packages/env/lib/test/md5.js @@ -1,4 +1,6 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const md5 = require( '../md5' ); describe( 'md5', () => { diff --git a/packages/env/lib/test/parse-spx-mode.js b/packages/env/lib/test/parse-spx-mode.js index 50ca3bad303d5c..32e79e7622ee27 100644 --- a/packages/env/lib/test/parse-spx-mode.js +++ b/packages/env/lib/test/parse-spx-mode.js @@ -1,4 +1,6 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const parseSpxMode = require( '../parse-spx-mode' ); describe( 'parseSpxMode', () => { diff --git a/packages/env/lib/test/parse-xdebug-mode.js b/packages/env/lib/test/parse-xdebug-mode.js index 22a0f2cd22f441..4b4593a75f3dbc 100644 --- a/packages/env/lib/test/parse-xdebug-mode.js +++ b/packages/env/lib/test/parse-xdebug-mode.js @@ -1,4 +1,6 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const parseXdebugMode = require( '../parse-xdebug-mode' ); describe( 'parseXdebugMode', () => { diff --git a/packages/env/lib/test/port-utils.js b/packages/env/lib/test/port-utils.js index f696cd2e008a18..86216e5aac02e1 100644 --- a/packages/env/lib/test/port-utils.js +++ b/packages/env/lib/test/port-utils.js @@ -1,5 +1,8 @@ -'use strict'; -const net = require( 'net' ); +import { createRequire } from 'node:module'; +import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const net = require( 'node:net' ); +vi.spyOn( net, 'createServer' ); const { isPortAvailable, findAvailablePort, @@ -7,11 +10,13 @@ const { DEFAULT_MAX_PORT, } = require( '../port-utils' ); -jest.mock( 'net' ); +afterAll( () => { + vi.restoreAllMocks(); +} ); describe( 'port-utils', () => { afterEach( () => { - jest.restoreAllMocks(); + vi.clearAllMocks(); } ); /** @@ -25,7 +30,7 @@ describe( 'port-utils', () => { let errorCb, listenCb; const server = { - once: jest.fn( ( event, cb ) => { + once: vi.fn( ( event, cb ) => { if ( event === 'error' ) { errorCb = cb; } @@ -33,7 +38,7 @@ describe( 'port-utils', () => { listenCb = cb; } } ), - listen: jest.fn( ( port ) => { + listen: vi.fn( ( port ) => { if ( isAvailable( port ) ) { server.close.mockImplementation( ( cb ) => cb() ); listenCb(); @@ -41,7 +46,7 @@ describe( 'port-utils', () => { errorCb( { code: 'EADDRINUSE' } ); } } ), - close: jest.fn(), + close: vi.fn(), }; return server; @@ -65,15 +70,15 @@ describe( 'port-utils', () => { net.createServer.mockImplementation( () => { let errorCb; const server = { - once: jest.fn( ( event, cb ) => { + once: vi.fn( ( event, cb ) => { if ( event === 'error' ) { errorCb = cb; } } ), - listen: jest.fn( () => { + listen: vi.fn( () => { errorCb( { code: 'EACCES' } ); } ), - close: jest.fn(), + close: vi.fn(), }; return server; } ); diff --git a/packages/env/lib/test/resolve-available-ports.js b/packages/env/lib/test/resolve-available-ports.js index cb752ff20d5b76..753ebddecc913a 100644 --- a/packages/env/lib/test/resolve-available-ports.js +++ b/packages/env/lib/test/resolve-available-ports.js @@ -1,15 +1,21 @@ -'use strict'; +import { createRequire } from 'node:module'; +import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const portUtils = require( '../port-utils' ); +const findAvailablePort = vi.spyOn( portUtils, 'findAvailablePort' ); +const isPortAvailable = vi.spyOn( portUtils, 'isPortAvailable' ); const { createPortResolver, resolveConfigPorts, } = require( '../resolve-available-ports' ); -const { findAvailablePort, isPortAvailable } = require( '../port-utils' ); -jest.mock( '../port-utils' ); +afterAll( () => { + vi.restoreAllMocks(); +} ); describe( 'resolve-available-ports', () => { afterEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); describe( 'createPortResolver', () => { diff --git a/packages/env/package.json b/packages/env/package.json index 8623ce534db44e..a680527048c089 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -57,6 +57,9 @@ "simple-git": "^3.32.3", "yargs": "^17.3.0" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" }, diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index 64c3cf436bb07e..e25c4447da27e3 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -43,7 +43,8 @@ "types": "build-types", "sideEffects": false, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/escape-html/src/test/index.ts b/packages/escape-html/src/test/index.ts index 167b1f55e968ef..be5c783972c4d7 100644 --- a/packages/escape-html/src/test/index.ts +++ b/packages/escape-html/src/test/index.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { escapeAmpersand, escapeQuotationMark, diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index d92951cf075424..56625eea7c98f9 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -67,7 +67,8 @@ "@types/estree": "^1.0.8", "@typescript-eslint/parser": "^8.68.0", "eslint": "^10.0.0", - "typescript": "npm:@typescript/typescript6@^6.0.2" + "typescript": "npm:@typescript/typescript6@^6.0.2", + "vitest": "^4.1.10" }, "peerDependencies": { "@babel/core": ">=7", diff --git a/packages/eslint-plugin/rules/__tests__/components-no-missing-40px-size-prop.js b/packages/eslint-plugin/rules/__tests__/components-no-missing-40px-size-prop.js index 688a9864bcc8e3..0a4d1235e8beb9 100644 --- a/packages/eslint-plugin/rules/__tests__/components-no-missing-40px-size-prop.js +++ b/packages/eslint-plugin/rules/__tests__/components-no-missing-40px-size-prop.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../components-no-missing-40px-size-prop'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/components-no-unsafe-button-disabled.js b/packages/eslint-plugin/rules/__tests__/components-no-unsafe-button-disabled.js index cf6d7152dcef5a..311ef073b2c561 100644 --- a/packages/eslint-plugin/rules/__tests__/components-no-unsafe-button-disabled.js +++ b/packages/eslint-plugin/rules/__tests__/components-no-unsafe-button-disabled.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../components-no-unsafe-button-disabled'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js b/packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js index 2b495645e768cb..2c5e01631ef043 100644 --- a/packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js +++ b/packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../data-no-store-string-literals'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/dependency-group.js b/packages/eslint-plugin/rules/__tests__/dependency-group.js index 8505cb5d20a971..1adb720d2b63b8 100644 --- a/packages/eslint-plugin/rules/__tests__/dependency-group.js +++ b/packages/eslint-plugin/rules/__tests__/dependency-group.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../dependency-group'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/i18n-ellipsis.js b/packages/eslint-plugin/rules/__tests__/i18n-ellipsis.js index d2ac4ea4651099..f4ec4d84ee7bb3 100644 --- a/packages/eslint-plugin/rules/__tests__/i18n-ellipsis.js +++ b/packages/eslint-plugin/rules/__tests__/i18n-ellipsis.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../i18n-ellipsis'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/i18n-hyphenated-range.js b/packages/eslint-plugin/rules/__tests__/i18n-hyphenated-range.js index 4bba2535c9ed44..2cda0bda2ff727 100644 --- a/packages/eslint-plugin/rules/__tests__/i18n-hyphenated-range.js +++ b/packages/eslint-plugin/rules/__tests__/i18n-hyphenated-range.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../i18n-hyphenated-range'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/i18n-no-collapsible-whitespace.js b/packages/eslint-plugin/rules/__tests__/i18n-no-collapsible-whitespace.js index 478e5d1e50c902..0ca4fc5e168ce4 100644 --- a/packages/eslint-plugin/rules/__tests__/i18n-no-collapsible-whitespace.js +++ b/packages/eslint-plugin/rules/__tests__/i18n-no-collapsible-whitespace.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../i18n-no-collapsible-whitespace'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/i18n-no-flanking-whitespace.js b/packages/eslint-plugin/rules/__tests__/i18n-no-flanking-whitespace.js index 39bd2d274ab0ab..7df0607d46ef8b 100644 --- a/packages/eslint-plugin/rules/__tests__/i18n-no-flanking-whitespace.js +++ b/packages/eslint-plugin/rules/__tests__/i18n-no-flanking-whitespace.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../i18n-no-flanking-whitespace'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/i18n-no-placeholders-only.js b/packages/eslint-plugin/rules/__tests__/i18n-no-placeholders-only.js index 682fed9b35cff0..b06aae3c6dc444 100644 --- a/packages/eslint-plugin/rules/__tests__/i18n-no-placeholders-only.js +++ b/packages/eslint-plugin/rules/__tests__/i18n-no-placeholders-only.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../i18n-no-placeholders-only'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/i18n-no-variables.js b/packages/eslint-plugin/rules/__tests__/i18n-no-variables.js index 9c0f1960c073e7..1572f2639f570b 100644 --- a/packages/eslint-plugin/rules/__tests__/i18n-no-variables.js +++ b/packages/eslint-plugin/rules/__tests__/i18n-no-variables.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../i18n-no-variables'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/i18n-text-domain.js b/packages/eslint-plugin/rules/__tests__/i18n-text-domain.js index 34c5ba33b25975..ae723b83d14116 100644 --- a/packages/eslint-plugin/rules/__tests__/i18n-text-domain.js +++ b/packages/eslint-plugin/rules/__tests__/i18n-text-domain.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../i18n-text-domain'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/i18n-translator-comments.js b/packages/eslint-plugin/rules/__tests__/i18n-translator-comments.js index d1282d78be0a5a..8d6a583d28e3b9 100644 --- a/packages/eslint-plugin/rules/__tests__/i18n-translator-comments.js +++ b/packages/eslint-plugin/rules/__tests__/i18n-translator-comments.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../i18n-translator-comments'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-base-control-with-label-without-id.js b/packages/eslint-plugin/rules/__tests__/no-base-control-with-label-without-id.js index abe2852e7507b5..c4f95bfc3f4cac 100644 --- a/packages/eslint-plugin/rules/__tests__/no-base-control-with-label-without-id.js +++ b/packages/eslint-plugin/rules/__tests__/no-base-control-with-label-without-id.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-base-control-with-label-without-id'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-constructor.js b/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-constructor.js index 589d787600518c..6da2cab308d39b 100644 --- a/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-constructor.js +++ b/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-constructor.js @@ -1,6 +1,10 @@ -import { RuleTester } from 'eslint'; +import typescriptParser from '@typescript-eslint/parser'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-dom-globals-in-constructor'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 2020, @@ -53,7 +57,7 @@ ruleTester.run( 'no-dom-globals-in-constructor', rule, { // TypeScript-specific tests for shouldSkipReference. const tsRuleTester = new RuleTester( { languageOptions: { - parser: require( '@typescript-eslint/parser' ), + parser: typescriptParser, ecmaVersion: 2020, sourceType: 'module', parserOptions: { diff --git a/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-module-scope.js b/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-module-scope.js index c045516e49117b..f0e3505627cfa8 100644 --- a/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-module-scope.js +++ b/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-module-scope.js @@ -1,6 +1,10 @@ -import { RuleTester } from 'eslint'; +import typescriptParser from '@typescript-eslint/parser'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-dom-globals-in-module-scope'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 2020, @@ -87,7 +91,7 @@ ruleTester.run( 'no-dom-globals-in-module-scope', rule, { // TypeScript-specific tests for shouldSkipReference. const tsRuleTester = new RuleTester( { languageOptions: { - parser: require( '@typescript-eslint/parser' ), + parser: typescriptParser, ecmaVersion: 2020, sourceType: 'module', parserOptions: { diff --git a/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-cc-render.js b/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-cc-render.js index a2950561415f45..ec0e4e74c11487 100644 --- a/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-cc-render.js +++ b/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-cc-render.js @@ -1,6 +1,10 @@ -import { RuleTester } from 'eslint'; +import typescriptParser from '@typescript-eslint/parser'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-dom-globals-in-react-cc-render'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 2020, @@ -48,7 +52,7 @@ ruleTester.run( 'no-dom-globals-in-react-cc-render', rule, { // TypeScript-specific tests for shouldSkipReference. const tsRuleTester = new RuleTester( { languageOptions: { - parser: require( '@typescript-eslint/parser' ), + parser: typescriptParser, ecmaVersion: 2020, sourceType: 'module', parserOptions: { diff --git a/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-fc.js b/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-fc.js index 400b120908bc20..650cf353796c23 100644 --- a/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-fc.js +++ b/packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-fc.js @@ -1,6 +1,10 @@ -import { RuleTester } from 'eslint'; +import typescriptParser from '@typescript-eslint/parser'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-dom-globals-in-react-fc'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 2020, @@ -74,7 +78,7 @@ ruleTester.run( 'no-dom-globals-in-react-fc', rule, { // TypeScript-specific tests for shouldSkipReference. const tsRuleTester = new RuleTester( { languageOptions: { - parser: require( '@typescript-eslint/parser' ), + parser: typescriptParser, ecmaVersion: 2020, sourceType: 'module', parserOptions: { diff --git a/packages/eslint-plugin/rules/__tests__/no-ds-tokens.js b/packages/eslint-plugin/rules/__tests__/no-ds-tokens.js index 12ce1e840e8fdd..fd4e60ed713f2f 100644 --- a/packages/eslint-plugin/rules/__tests__/no-ds-tokens.js +++ b/packages/eslint-plugin/rules/__tests__/no-ds-tokens.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-ds-tokens'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-global-active-element.js b/packages/eslint-plugin/rules/__tests__/no-global-active-element.js index 916a98ddf2df8b..10025c183dd184 100644 --- a/packages/eslint-plugin/rules/__tests__/no-global-active-element.js +++ b/packages/eslint-plugin/rules/__tests__/no-global-active-element.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-global-active-element'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-global-get-selection.js b/packages/eslint-plugin/rules/__tests__/no-global-get-selection.js index c1720986e392d3..69164134dbe335 100644 --- a/packages/eslint-plugin/rules/__tests__/no-global-get-selection.js +++ b/packages/eslint-plugin/rules/__tests__/no-global-get-selection.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-global-get-selection'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-i18n-in-save.js b/packages/eslint-plugin/rules/__tests__/no-i18n-in-save.js index f891348bc85aad..bf1492a92780f9 100644 --- a/packages/eslint-plugin/rules/__tests__/no-i18n-in-save.js +++ b/packages/eslint-plugin/rules/__tests__/no-i18n-in-save.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-i18n-in-save'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-non-module-stylesheet-imports.js b/packages/eslint-plugin/rules/__tests__/no-non-module-stylesheet-imports.js index 7acf99d732f355..3830fdffd84b64 100644 --- a/packages/eslint-plugin/rules/__tests__/no-non-module-stylesheet-imports.js +++ b/packages/eslint-plugin/rules/__tests__/no-non-module-stylesheet-imports.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-non-module-stylesheet-imports'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/no-setting-ds-tokens.js b/packages/eslint-plugin/rules/__tests__/no-setting-ds-tokens.js index 49b253fa82465b..ec370cf743ad79 100644 --- a/packages/eslint-plugin/rules/__tests__/no-setting-ds-tokens.js +++ b/packages/eslint-plugin/rules/__tests__/no-setting-ds-tokens.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-setting-ds-tokens'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-unguarded-get-range-at.js b/packages/eslint-plugin/rules/__tests__/no-unguarded-get-range-at.js index 3b69e54375b087..20893c588b40c3 100644 --- a/packages/eslint-plugin/rules/__tests__/no-unguarded-get-range-at.js +++ b/packages/eslint-plugin/rules/__tests__/no-unguarded-get-range-at.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-unguarded-get-range-at'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js b/packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js index 916104600e7e26..9759e7c40987f0 100644 --- a/packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js +++ b/packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-unknown-ds-tokens'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-unmerged-classname.js b/packages/eslint-plugin/rules/__tests__/no-unmerged-classname.js index 3c04d9d3452fb8..3173bc2914e69e 100644 --- a/packages/eslint-plugin/rules/__tests__/no-unmerged-classname.js +++ b/packages/eslint-plugin/rules/__tests__/no-unmerged-classname.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-unmerged-classname'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/no-unsafe-render-order.js b/packages/eslint-plugin/rules/__tests__/no-unsafe-render-order.js index 9751eaa32cffb2..d3f71c5efb712d 100644 --- a/packages/eslint-plugin/rules/__tests__/no-unsafe-render-order.js +++ b/packages/eslint-plugin/rules/__tests__/no-unsafe-render-order.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-unsafe-render-order'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js b/packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js index 68ec4d242f6655..affb9f797934e8 100644 --- a/packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js +++ b/packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-unsafe-wp-apis'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/no-unused-vars-before-return.js b/packages/eslint-plugin/rules/__tests__/no-unused-vars-before-return.js index 4056ec5cedacb0..5a26ec261225c8 100644 --- a/packages/eslint-plugin/rules/__tests__/no-unused-vars-before-return.js +++ b/packages/eslint-plugin/rules/__tests__/no-unused-vars-before-return.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-unused-vars-before-return'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/no-wp-process-env.js b/packages/eslint-plugin/rules/__tests__/no-wp-process-env.js index 2bfd1f3c0b6b24..4f54b67c1e1590 100644 --- a/packages/eslint-plugin/rules/__tests__/no-wp-process-env.js +++ b/packages/eslint-plugin/rules/__tests__/no-wp-process-env.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../no-wp-process-env'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/react-no-unsafe-timeout.js b/packages/eslint-plugin/rules/__tests__/react-no-unsafe-timeout.js index 52bdd429f238d3..ac68f3c7f52e35 100644 --- a/packages/eslint-plugin/rules/__tests__/react-no-unsafe-timeout.js +++ b/packages/eslint-plugin/rules/__tests__/react-no-unsafe-timeout.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../react-no-unsafe-timeout'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/use-import-as.js b/packages/eslint-plugin/rules/__tests__/use-import-as.js index 160e25a4df8d50..16afecf4859247 100644 --- a/packages/eslint-plugin/rules/__tests__/use-import-as.js +++ b/packages/eslint-plugin/rules/__tests__/use-import-as.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../use-import-as'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/use-recommended-components.js b/packages/eslint-plugin/rules/__tests__/use-recommended-components.js index baeb7a4159c661..5f9692c6cde23c 100644 --- a/packages/eslint-plugin/rules/__tests__/use-recommended-components.js +++ b/packages/eslint-plugin/rules/__tests__/use-recommended-components.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, expect, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule, { ALLOWLIST, DENYLIST } from '../use-recommended-components'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { sourceType: 'module', diff --git a/packages/eslint-plugin/rules/__tests__/valid-sprintf.js b/packages/eslint-plugin/rules/__tests__/valid-sprintf.js index 7bfabec098e8f7..7b497141daa295 100644 --- a/packages/eslint-plugin/rules/__tests__/valid-sprintf.js +++ b/packages/eslint-plugin/rules/__tests__/valid-sprintf.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../valid-sprintf'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/rules/__tests__/wp-global-usage.js b/packages/eslint-plugin/rules/__tests__/wp-global-usage.js index c1c15f5e093200..2e49003d240ae1 100644 --- a/packages/eslint-plugin/rules/__tests__/wp-global-usage.js +++ b/packages/eslint-plugin/rules/__tests__/wp-global-usage.js @@ -1,6 +1,9 @@ -import { RuleTester } from 'eslint'; +import { describe, it } from 'vitest'; +import configureRuleTester from '../../test-utils/configure-rule-tester'; import rule from '../wp-global-usage'; +const RuleTester = configureRuleTester( { describe, it } ); + const ruleTester = new RuleTester( { languageOptions: { ecmaVersion: 6, diff --git a/packages/eslint-plugin/test-utils/configure-rule-tester.js b/packages/eslint-plugin/test-utils/configure-rule-tester.js new file mode 100644 index 00000000000000..52d21ae4e91daf --- /dev/null +++ b/packages/eslint-plugin/test-utils/configure-rule-tester.js @@ -0,0 +1,9 @@ +import { RuleTester } from 'eslint'; + +export default function configureRuleTester( { describe, it } ) { + RuleTester.describe = describe; + RuleTester.it = it; + RuleTester.itOnly = it.only; + + return RuleTester; +} diff --git a/packages/eslint-plugin/utils/test/is-package-installed.js b/packages/eslint-plugin/utils/test/is-package-installed.js index 930cfb36d70e60..d47e34e9b12614 100644 --- a/packages/eslint-plugin/utils/test/is-package-installed.js +++ b/packages/eslint-plugin/utils/test/is-package-installed.js @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import isPackageInstalled from '../is-package-installed'; describe( 'isPackageInstalled', () => { diff --git a/packages/fields/package.json b/packages/fields/package.json index 5c0ed1f0554225..ec43ecc1a561d7 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -76,7 +76,8 @@ "remove-accents": "^0.5.0" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 0fcbe690028d06..071a7b0894a8ad 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -60,7 +60,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@testing-library/user-event": "^14.6.1" + "@testing-library/user-event": "^14.6.1", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19", diff --git a/packages/format-library/src/link/test/utils.js b/packages/format-library/src/link/test/utils.js index 9f3ca24b15a19a..9d78ba68d7ef33 100644 --- a/packages/format-library/src/link/test/utils.js +++ b/packages/format-library/src/link/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isValidHref, getFormatBoundary } from '../utils'; describe( 'isValidHref', () => { diff --git a/packages/global-styles-engine/package.json b/packages/global-styles-engine/package.json index f714175f364176..6f4bd53134f486 100644 --- a/packages/global-styles-engine/package.json +++ b/packages/global-styles-engine/package.json @@ -54,7 +54,7 @@ "memize": "^2.1.1" }, "devDependencies": { - "@types/jest": "^30.0.0" + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/global-styles-engine/src/test/get-global-styles-changes.test.ts b/packages/global-styles-engine/src/test/get-global-styles-changes.test.ts index 46341a603d2eca..4212c300007946 100644 --- a/packages/global-styles-engine/src/test/get-global-styles-changes.test.ts +++ b/packages/global-styles-engine/src/test/get-global-styles-changes.test.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { registerBlockType, unregisterBlockType, @@ -201,6 +202,7 @@ describe( 'getGlobalStylesChanges and utils', () => { beforeEach( () => { registerBlockType( 'core/test-fiori-di-zucca', { apiVersion: 3, + attributes: {}, save: () => {}, category: 'text', title: 'Test pumpkin flowers', diff --git a/packages/global-styles-engine/src/test/render.test.ts b/packages/global-styles-engine/src/test/render.test.ts index 7b39315913f7e5..45316d80e038f7 100644 --- a/packages/global-styles-engine/src/test/render.test.ts +++ b/packages/global-styles-engine/src/test/render.test.ts @@ -1,3 +1,6 @@ +import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; +import { getBlockSupport } from '@wordpress/blocks'; +import { select } from '@wordpress/data'; import { getNodesWithStyles, getNodesWithSettings, @@ -13,35 +16,21 @@ import { } from '../utils/common'; // Mock WordPress data store -jest.mock( '@wordpress/data', () => ( { - select: jest.fn(), +vi.mock( import( '@wordpress/data' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + select: vi.fn(), } ) ); // Mock WordPress blocks store -jest.mock( '@wordpress/blocks', () => ( { - __EXPERIMENTAL_STYLE_PROPERTY: { - filter: { - value: [ 'filter', 'duotone' ], - support: [ 'filter', 'duotone' ], - }, - }, - __EXPERIMENTAL_ELEMENTS: { - link: 'a:where(:not(.wp-element-button))', - heading: 'h1, h2, h3, h4, h5, h6', - h1: 'h1', - h2: 'h2', - h3: 'h3', - h4: 'h4', - h5: 'h5', - h6: 'h6', - button: '.wp-element-button', - caption: '.wp-element-caption', - }, - getBlockSupport: jest.fn(), - getBlockTypes: jest.fn(), - store: 'core/blocks', +vi.mock( import( '@wordpress/blocks' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + getBlockSupport: vi.fn(), + getBlockTypes: vi.fn(), } ) ); +const mockedSelect = vi.mocked( select ) as Mock; +const mockedGetBlockSupport = vi.mocked( getBlockSupport ) as Mock; + // Mock WordPress elements (minimal, no mocking of complex APIs) const ELEMENTS = { link: 'a:where(:not(.wp-element-button))', @@ -1658,9 +1647,8 @@ describe( 'global styles renderer', () => { describe( 'generateGlobalStyles', () => { beforeEach( () => { - jest.clearAllMocks(); - const mockSelect = require( '@wordpress/data' ).select as jest.Mock; - mockSelect.mockReturnValue( { + vi.clearAllMocks(); + mockedSelect.mockReturnValue( { getBlockStyles: () => [], } ); } ); @@ -1770,8 +1758,7 @@ describe( 'global styles renderer', () => { } ); it( 'should output duotone SVG filters with __unstableType of svgs', () => { - const mockSelect = require( '@wordpress/data' ).select as jest.Mock; - mockSelect.mockReturnValue( { + mockedSelect.mockReturnValue( { getBlockStyles: () => [], } ); @@ -1839,13 +1826,12 @@ describe( 'global styles renderer', () => { describe( 'getBlockSelectors', () => { beforeEach( () => { // Reset mocks before each test - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'should return block selectors data', () => { // Mock the select function to return getBlockStyles - const mockSelect = require( '@wordpress/data' ).select as jest.Mock; - mockSelect.mockReturnValue( { + mockedSelect.mockReturnValue( { getBlockStyles: () => [ { name: 'foo', label: 'foo' } ], } ); @@ -1861,7 +1847,13 @@ describe( 'global styles renderer', () => { category: 'media', }; const blockTypes = [ imageBlock ]; - expect( getBlockSelectors( blockTypes ) ).toEqual( { + expect( + getBlockSelectors( + blockTypes as unknown as Parameters< + typeof getBlockSelectors + >[ 0 ] + ) + ).toEqual( { 'core/image': { name: imageBlock.name, selector: imageSelectors.root, @@ -1882,15 +1874,12 @@ describe( 'global styles renderer', () => { it( 'should return block selectors data with old experimental selectors', () => { // Mock the select function to return getBlockStyles with empty array - const mockSelect = require( '@wordpress/data' ).select as jest.Mock; - mockSelect.mockReturnValue( { + mockedSelect.mockReturnValue( { getBlockStyles: () => [], } ); // Mock getBlockSupport to handle experimental duotone support - const mockGetBlockSupport = require( '@wordpress/blocks' ) - .getBlockSupport as jest.Mock; - mockGetBlockSupport.mockImplementation( + mockedGetBlockSupport.mockImplementation( ( blockType, path, defaultValue ) => { if ( path === 'color.__experimentalDuotone' ) { return 'img'; @@ -1917,7 +1906,13 @@ describe( 'global styles renderer', () => { }; const blockTypes = [ imageBlock ]; - expect( getBlockSelectors( blockTypes ) ).toEqual( { + expect( + getBlockSelectors( + blockTypes as unknown as Parameters< + typeof getBlockSelectors + >[ 0 ] + ) + ).toEqual( { 'core/image': { name: imageBlock.name, selector: imageSupports.__experimentalSelector, diff --git a/packages/global-styles-engine/src/test/resolve-style.test.ts b/packages/global-styles-engine/src/test/resolve-style.test.ts index 66f40c5edc8e66..6f9d487973d385 100644 --- a/packages/global-styles-engine/src/test/resolve-style.test.ts +++ b/packages/global-styles-engine/src/test/resolve-style.test.ts @@ -1,4 +1,6 @@ +import { describe, expect, test } from 'vitest'; import { resolveStyle, privateHelpers } from '../resolve-style'; +import type { GlobalStylesConfig } from '../types'; const { isExplicitEmpty, @@ -38,8 +40,8 @@ describe( 'resolveStyle – merged output', () => { }, }; const out = pickLayerRootContribution( layer ); - expect( out.typography ).toEqual( { lineHeight: '1.2' } ); - expect( out.elements ).toBe( layer.elements ); + expect( out?.typography ).toEqual( { lineHeight: '1.2' } ); + expect( out?.elements ).toBe( layer.elements ); } ); test( 'pickLayerRootContribution returns null for empty layers', () => { @@ -135,7 +137,7 @@ describe( 'resolveStyle – merged output', () => { } ); test( 'deepMergeDroppingEmpties records a single source entry for backgroundImage', () => { - const sources = {}; + const sources: Record< string, { layer: string } > = {}; deepMergeDroppingEmpties( {}, { background: { backgroundImage: { id: 1, url: 'a.jpg' } } }, @@ -732,14 +734,14 @@ describe( 'resolveStyle – memoization', () => { }, }, }; - const buildWithLinks = ( href ) => + const buildWithLinks = ( href: string ) => resolveStyle( { ...gs, _links: { 'wp:theme-file': [ { name: 'file:./img.jpg', href } ], }, - }, + } as GlobalStylesConfig, { blockName: 'core/paragraph' } ).value.background.backgroundImage.url; @@ -794,14 +796,14 @@ describe( 'resolveStyle – memoization', () => { describe( 'resolveStyle – non-cascading root drop', () => { // Each call builds against a fresh `globalStyles` object so the // identity-keyed memo never returns a cross-test cache hit. - const build = ( styles, extra = {} ) => - resolveStyle( - { styles }, - { - blockName: 'core/paragraph', - ...extra, - } - ); + const build = ( + styles: Record< string, unknown >, + extra: Parameters< typeof resolveStyle >[ 1 ] = {} + ) => + resolveStyle( { styles } as GlobalStylesConfig, { + blockName: 'core/paragraph', + ...extra, + } ); test( 'drops a root-sourced background color and its source', () => { const { value, sources } = build( { @@ -888,7 +890,7 @@ describe( 'resolveStyle – non-cascading root drop', () => { }, ], }, - }, + } as GlobalStylesConfig, { blockName: 'core/paragraph' } ); expect( value.background.backgroundImage.url ).toBe( diff --git a/packages/global-styles-engine/src/test/style-state-back-compat.test.ts b/packages/global-styles-engine/src/test/style-state-back-compat.test.ts index e20cd16ee8141d..cee9f2fbb9593d 100644 --- a/packages/global-styles-engine/src/test/style-state-back-compat.test.ts +++ b/packages/global-styles-engine/src/test/style-state-back-compat.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { normalizeStyleStateAliases } from '../style-state-back-compat'; import type { GlobalStylesConfig } from '../types'; diff --git a/packages/global-styles-engine/src/test/typography-utils.test.ts b/packages/global-styles-engine/src/test/typography-utils.test.ts index 5ce067cc5f64b5..2c402ec244e2ad 100644 --- a/packages/global-styles-engine/src/test/typography-utils.test.ts +++ b/packages/global-styles-engine/src/test/typography-utils.test.ts @@ -2,6 +2,8 @@ * Tests for typography utility functions * Ported from Gutenberg's typography-utils.js tests */ + +import { describe, expect, it } from 'vitest'; import { getTypographyFontSizeValue, getFluidTypographyOptionsFromSettings, @@ -250,14 +252,14 @@ describe( 'typography utils', () => { }, ]; - testCases.forEach( ( { message, preset, settings, expected } ) => { - // eslint-disable-next-line jest/valid-title - it( message, () => { + it.each( testCases )( + '$message', + ( { preset, settings, expected } ) => { expect( getTypographyFontSizeValue( preset, settings || {} ) ).toEqual( expected ); - } ); - } ); + } + ); } ); describe( 'getFluidTypographyOptionsFromSettings', () => { @@ -338,13 +340,10 @@ describe( 'typography utils', () => { }, ]; - testCases.forEach( ( { message, settings, expected } ) => { - // eslint-disable-next-line jest/valid-title - it( message, () => { - expect( - getFluidTypographyOptionsFromSettings( settings || {} ) - ).toEqual( expected ); - } ); + it.each( testCases )( '$message', ( { settings, expected } ) => { + expect( + getFluidTypographyOptionsFromSettings( settings || {} ) + ).toEqual( expected ); } ); } ); } ); diff --git a/packages/global-styles-engine/src/test/utils.test.ts b/packages/global-styles-engine/src/test/utils.test.ts index 4e3a60ba1d4cfe..36614b272edcf5 100644 --- a/packages/global-styles-engine/src/test/utils.test.ts +++ b/packages/global-styles-engine/src/test/utils.test.ts @@ -2,6 +2,8 @@ * Tests for utility functions * Ported from Gutenberg's utils.js tests */ + +import { describe, expect, it, test } from 'vitest'; import type { GlobalStylesConfig } from '../types'; import { getBlockStyleVariationFeatureSelector, diff --git a/packages/global-styles-engine/src/test/variation.test.ts b/packages/global-styles-engine/src/test/variation.test.ts index 0960919d4bd4f7..9adcee15bc77ef 100644 --- a/packages/global-styles-engine/src/test/variation.test.ts +++ b/packages/global-styles-engine/src/test/variation.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getVariationStyle } from '../variation'; describe( 'getVariationStyle', () => { diff --git a/packages/global-styles-engine/src/test/viewport-utils.test.ts b/packages/global-styles-engine/src/test/viewport-utils.test.ts index 161b0faeada497..0978f7ae8e3cfb 100644 --- a/packages/global-styles-engine/src/test/viewport-utils.test.ts +++ b/packages/global-styles-engine/src/test/viewport-utils.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getResponsiveMediaQueries, getViewportBreakpoints, diff --git a/packages/global-styles-ui/package.json b/packages/global-styles-ui/package.json index 98ae5c52376848..7def1f5f007df0 100644 --- a/packages/global-styles-ui/package.json +++ b/packages/global-styles-ui/package.json @@ -66,7 +66,8 @@ }, "devDependencies": { "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/global-styles-ui/src/font-library/utils/test/preview-styles.js b/packages/global-styles-ui/src/font-library/utils/test/preview-styles.js index af539131fb3d90..d5f320515e4eda 100644 --- a/packages/global-styles-ui/src/font-library/utils/test/preview-styles.js +++ b/packages/global-styles-ui/src/font-library/utils/test/preview-styles.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { formatFontFamily, getFacePreviewStyle, diff --git a/packages/global-styles-ui/src/test/shadow-utils.js b/packages/global-styles-ui/src/test/shadow-utils.js index d09c44cba85d6c..89ba4e2383c626 100644 --- a/packages/global-styles-ui/src/test/shadow-utils.js +++ b/packages/global-styles-ui/src/test/shadow-utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getShadowParts, shadowStringToObject } from '../shadow-utils'; const colorFormats = { diff --git a/packages/global-styles-ui/src/test/utils.js b/packages/global-styles-ui/src/test/utils.js index d5f97856bbe468..be9b7383921edc 100644 --- a/packages/global-styles-ui/src/test/utils.js +++ b/packages/global-styles-ui/src/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getNewIndexFromPresets } from '../utils'; const validPresets = { diff --git a/packages/grid/package.json b/packages/grid/package.json index 379cf3a7196193..96390ab6982031 100644 --- a/packages/grid/package.json +++ b/packages/grid/package.json @@ -58,10 +58,10 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0", "@wordpress/icons": "file:../icons", "@wordpress/ui": "file:../ui", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/grid/src/dashboard-grid/test/resolve-fill-widths.test.ts b/packages/grid/src/dashboard-grid/test/resolve-fill-widths.test.ts index 3eb504f8e59998..9153dd5fb49be9 100644 --- a/packages/grid/src/dashboard-grid/test/resolve-fill-widths.test.ts +++ b/packages/grid/src/dashboard-grid/test/resolve-fill-widths.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { resolveFillWidths } from '../resolve-fill-widths'; import type { DashboardGridLayoutItem } from '../types'; diff --git a/packages/grid/src/dashboard-lanes/test/lane-placement.test.ts b/packages/grid/src/dashboard-lanes/test/lane-placement.test.ts index ac3f19891d48f0..1b0f96e1702120 100644 --- a/packages/grid/src/dashboard-lanes/test/lane-placement.test.ts +++ b/packages/grid/src/dashboard-lanes/test/lane-placement.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { computeLanePlacements } from '../lane-placement'; import type { LanePlacementItem } from '../lane-placement'; diff --git a/packages/grid/src/shared/test/array-move-with-pinned.test.ts b/packages/grid/src/shared/test/array-move-with-pinned.test.ts index f8b5ec0305b7bd..514ac7eaf57314 100644 --- a/packages/grid/src/shared/test/array-move-with-pinned.test.ts +++ b/packages/grid/src/shared/test/array-move-with-pinned.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { arrayMoveWithPinned } from '../array-move-with-pinned'; const pinned = new Set( [ 'p' ] ); diff --git a/packages/grid/src/shared/test/resize-snap.test.ts b/packages/grid/src/shared/test/resize-snap.test.ts index 219076df1b45b1..d4e8738e14c386 100644 --- a/packages/grid/src/shared/test/resize-snap.test.ts +++ b/packages/grid/src/shared/test/resize-snap.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { clampResizeDelta, clampSpan, diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 6214acdc3e61f0..64817347580d4e 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -42,7 +42,8 @@ "wpScript": true, "types": "build-types", "devDependencies": { - "benchmark": "^2.1.4" + "benchmark": "^2.1.4", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 5d6298b7101dec..f20f47cae1f63d 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -52,7 +52,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "benchmark": "^2.1.4" + "benchmark": "^2.1.4", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/i18n/src/test/create-i18n.ts b/packages/i18n/src/test/create-i18n.ts index d48a755b0b3057..fe1055bf642462 100644 --- a/packages/i18n/src/test/create-i18n.ts +++ b/packages/i18n/src/test/create-i18n.ts @@ -1,4 +1,6 @@ /* eslint-disable @wordpress/i18n-text-domain, @wordpress/i18n-translator-comments */ + +import { describe, expect, it, test } from 'vitest'; import { createHooks } from '@wordpress/hooks'; import { createI18n } from '..'; import type { I18nDomainMetadata, LocaleData } from '../types'; diff --git a/packages/i18n/src/test/default-i18n.ts b/packages/i18n/src/test/default-i18n.ts index 9d7b4dd8fe4805..66f70f91476c25 100644 --- a/packages/i18n/src/test/default-i18n.ts +++ b/packages/i18n/src/test/default-i18n.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { addFilter } from '@wordpress/hooks'; import { __, _x, _n, _nx } from '@wordpress/i18n'; diff --git a/packages/i18n/src/test/sprintf.ts b/packages/i18n/src/test/sprintf.ts index c7edcb25474231..9604eff2260e6a 100644 --- a/packages/i18n/src/test/sprintf.ts +++ b/packages/i18n/src/test/sprintf.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { sprintf } from '../sprintf'; import type { TransformedText } from '../types'; diff --git a/packages/i18n/src/test/subscribe-i18n.ts b/packages/i18n/src/test/subscribe-i18n.ts index 10d1c7d88b57e4..d3fb5da12df571 100644 --- a/packages/i18n/src/test/subscribe-i18n.ts +++ b/packages/i18n/src/test/subscribe-i18n.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { createHooks } from '@wordpress/hooks'; import { createI18n } from '..'; diff --git a/packages/icons/lib/test/generate-library.js b/packages/icons/lib/test/generate-library.js index f092add98f93e1..11ee04d04c2f37 100644 --- a/packages/icons/lib/test/generate-library.js +++ b/packages/icons/lib/test/generate-library.js @@ -1,3 +1,6 @@ +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const { svgToTsx } = require( '../generate-library.cjs' ); describe( 'svgToTsx style attribute conversion', () => { diff --git a/packages/icons/package.json b/packages/icons/package.json index eb89b1b60150d0..e6f4f695948076 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -51,7 +51,8 @@ "@storybook/react-vite": "^10.5.3", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/interactivity-router/package.json b/packages/interactivity-router/package.json index 6c6fd45daec1b2..65aafabbe29781 100644 --- a/packages/interactivity-router/package.json +++ b/packages/interactivity-router/package.json @@ -51,7 +51,8 @@ }, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/interactivity-router/src/assets/test/scs.test.ts b/packages/interactivity-router/src/assets/test/scs.test.ts index b8dd7ac41d4abb..3cd47a0c13cf2d 100644 --- a/packages/interactivity-router/src/assets/test/scs.test.ts +++ b/packages/interactivity-router/src/assets/test/scs.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { shortestCommonSupersequence } from '../scs'; /** diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json index 1e4b88daf3a6b7..49485cb417b1d1 100644 --- a/packages/interactivity/package.json +++ b/packages/interactivity/package.json @@ -38,7 +38,7 @@ "preact": "^10.29.1" }, "devDependencies": { - "@types/jest": "^30.0.0" + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/interface/package.json b/packages/interface/package.json index 515454b17522fb..4a9e0ddcbe3bbb 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -68,7 +68,8 @@ }, "devDependencies": { "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19", diff --git a/packages/interface/src/store/test/actions.js b/packages/interface/src/store/test/actions.js index 000464a96f4f10..f0965d5358b3cb 100644 --- a/packages/interface/src/store/test/actions.js +++ b/packages/interface/src/store/test/actions.js @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from 'vitest'; import { createRegistry } from '@wordpress/data'; import { store as preferencesStore } from '@wordpress/preferences'; import { store as interfaceStore } from '../'; diff --git a/packages/interface/src/store/test/reducer.js b/packages/interface/src/store/test/reducer.js index 818bd9df976378..dc2234f018c1b6 100644 --- a/packages/interface/src/store/test/reducer.js +++ b/packages/interface/src/store/test/reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { activeModal } from '../reducer'; describe( 'state', () => { diff --git a/packages/interface/src/store/test/selectors.js b/packages/interface/src/store/test/selectors.js index f609083cbc7da8..e0ab922e5b43ec 100644 --- a/packages/interface/src/store/test/selectors.js +++ b/packages/interface/src/store/test/selectors.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isModalActive } from '../selectors'; describe( 'selectors', () => { diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index 0e875e196f37e2..77d6ebbeccba27 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -47,7 +47,8 @@ "devDependencies": { "@types/jest": "^30.0.0", "@wordpress/lazy-import": "file:../lazy-import", - "benchmark": "^2.1.4" + "benchmark": "^2.1.4", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/is-shallow-equal/test/index.ts b/packages/is-shallow-equal/test/index.ts index cd5c12bdf920bf..aabab5c3adc173 100644 --- a/packages/is-shallow-equal/test/index.ts +++ b/packages/is-shallow-equal/test/index.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isShallowEqual, isShallowEqualArrays, diff --git a/packages/kebab-case/package.json b/packages/kebab-case/package.json index a2d583697fba90..aa9cdbe3c076dc 100644 --- a/packages/kebab-case/package.json +++ b/packages/kebab-case/package.json @@ -45,6 +45,9 @@ "dependencies": { "change-case": "^4.1.2" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" } diff --git a/packages/kebab-case/src/test/index.js b/packages/kebab-case/src/test/index.js index 2bcdb9ea812134..662c548476ed1d 100644 --- a/packages/kebab-case/src/test/index.js +++ b/packages/kebab-case/src/test/index.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { kebabCase } from '..'; describe( 'kebabCase', () => { diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index 9ab828cbfc95d5..efd2329aae782e 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -46,7 +46,8 @@ "@wordpress/i18n": "file:../i18n" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19" diff --git a/packages/keycodes/src/test/platform.ts b/packages/keycodes/src/test/platform.ts index 5942b59b6d961c..26581554e42aaf 100644 --- a/packages/keycodes/src/test/platform.ts +++ b/packages/keycodes/src/test/platform.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isAppleOS } from '../platform'; describe( 'isAppleOS helper', () => { diff --git a/packages/keycodes/src/test/with-ignore-ime-events.ts b/packages/keycodes/src/test/with-ignore-ime-events.ts index 1faaf924dc6fe0..cc49df7a38bffb 100644 --- a/packages/keycodes/src/test/with-ignore-ime-events.ts +++ b/packages/keycodes/src/test/with-ignore-ime-events.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { withIgnoreIMEEvents } from '../with-ignore-ime-events'; // Builds a native-shaped keyboard event, i.e. one that carries `isComposing` @@ -23,33 +24,33 @@ function reactEvent( { describe( 'withIgnoreIMEEvents', () => { it( 'calls the handler for a regular key press', () => { - const handler = jest.fn(); + const handler = vi.fn(); withIgnoreIMEEvents( handler )( nativeEvent() ); expect( handler ).toHaveBeenCalledTimes( 1 ); } ); it( 'passes the event through to the handler untouched', () => { - const handler = jest.fn(); + const handler = vi.fn(); const event = nativeEvent(); withIgnoreIMEEvents( handler )( event ); expect( handler ).toHaveBeenCalledWith( event ); } ); it( 'ignores an event fired mid-composition', () => { - const handler = jest.fn(); + const handler = vi.fn(); withIgnoreIMEEvents( handler )( nativeEvent( { isComposing: true } ) ); expect( handler ).not.toHaveBeenCalled(); } ); it( 'ignores keyCode 229, which ends a composition in Mac Safari without setting isComposing', () => { - const handler = jest.fn(); + const handler = vi.fn(); withIgnoreIMEEvents( handler )( nativeEvent( { keyCode: 229 } ) ); expect( handler ).not.toHaveBeenCalled(); } ); describe( 'React synthetic events', () => { it( 'reads isComposing from nativeEvent', () => { - const handler = jest.fn(); + const handler = vi.fn(); withIgnoreIMEEvents( handler )( reactEvent( { isComposing: true } ) ); @@ -57,7 +58,7 @@ describe( 'withIgnoreIMEEvents', () => { } ); it( 'calls the handler when the composition has finished', () => { - const handler = jest.fn(); + const handler = vi.fn(); withIgnoreIMEEvents( handler )( reactEvent() ); expect( handler ).toHaveBeenCalledTimes( 1 ); } ); diff --git a/packages/media-editor/package.json b/packages/media-editor/package.json index 9db40464f83b6a..eb00b1f34671b0 100644 --- a/packages/media-editor/package.json +++ b/packages/media-editor/package.json @@ -69,7 +69,8 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts b/packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts index 60c2da05f32dd6..4be7bbbbdbf80f 100644 --- a/packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts +++ b/packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts @@ -52,6 +52,8 @@ * `serverSourcePixel` to undo it, and add values to the grid in * `buildRows`. The probes don't need to change. */ + +import { describe, expect, it } from 'vitest'; import { mat2d, vec2 } from 'gl-matrix'; import { buildModifiers } from '../build-modifiers'; import type { Modifier } from '../build-modifiers'; diff --git a/packages/media-editor/src/image-editor/core/math/test/sanitize.ts b/packages/media-editor/src/image-editor/core/math/test/sanitize.ts index ba29f8ea35caef..b58e3bdebab965 100644 --- a/packages/media-editor/src/image-editor/core/math/test/sanitize.ts +++ b/packages/media-editor/src/image-editor/core/math/test/sanitize.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isValidSize, safeBoundedNumber, diff --git a/packages/media-editor/src/image-editor/core/test/camera.ts b/packages/media-editor/src/image-editor/core/test/camera.ts index bb3f1918a55f88..43763c75b13165 100644 --- a/packages/media-editor/src/image-editor/core/test/camera.ts +++ b/packages/media-editor/src/image-editor/core/test/camera.ts @@ -1,3 +1,5 @@ +import { mat2d as m2d, vec2 } from 'gl-matrix'; +import { describe, expect, it } from 'vitest'; import { createCamera, worldToScreen, @@ -302,7 +304,6 @@ describe( 'createExportCamera', () => { const state = makeState(); const outputSize = { width: 400, height: 225 }; const camera = createExportCamera( state, IMAGE, outputSize ); - const { vec2 } = require( 'gl-matrix' ); const out = vec2.create(); vec2.transformMat2d( out, @@ -317,7 +318,6 @@ describe( 'createExportCamera', () => { const state = makeState(); const outputSize = { width: IMAGE.width, height: IMAGE.height }; const camera = createExportCamera( state, IMAGE, outputSize ); - const { vec2 } = require( 'gl-matrix' ); const topLeft = vec2.create(); vec2.transformMat2d( topLeft, [ 0, 0 ], camera ); expect( topLeft[ 0 ] ).toBeCloseTo( 0, 0 ); @@ -381,7 +381,6 @@ describe( 'createExportCamera', () => { // output canvas. const outputSize = { width: 400, height: 300 }; const exportCamera = createExportCamera( state, IMAGE, outputSize ); - const { mat2d: m2d, vec2 } = require( 'gl-matrix' ); const inv = m2d.create(); m2d.invert( inv, exportCamera ); const exportPxVec = vec2.create(); diff --git a/packages/media-editor/src/image-editor/core/test/interaction-controller.ts b/packages/media-editor/src/image-editor/core/test/interaction-controller.ts index e0a9a8431a1edf..e5783dacf44a23 100644 --- a/packages/media-editor/src/image-editor/core/test/interaction-controller.ts +++ b/packages/media-editor/src/image-editor/core/test/interaction-controller.ts @@ -1,3 +1,13 @@ +import { + afterAll, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, + type Mocked, +} from 'vitest'; import { InteractionController, type CropperInteractionActions, @@ -52,7 +62,7 @@ function createPointerEvent( clientX: 0, clientY: 0, pointerId: 1, - preventDefault: jest.fn(), + preventDefault: vi.fn(), ...overrides, } as unknown as PointerEvent; } @@ -67,16 +77,16 @@ function createMockElement(): HTMLElement & { } { const listeners: Record< string, EventListener[] > = {}; return { - focus: jest.fn(), - setPointerCapture: jest.fn(), - releasePointerCapture: jest.fn(), - addEventListener: jest.fn( ( type: string, fn: EventListener ) => { + focus: vi.fn(), + setPointerCapture: vi.fn(), + releasePointerCapture: vi.fn(), + addEventListener: vi.fn( ( type: string, fn: EventListener ) => { if ( ! listeners[ type ] ) { listeners[ type ] = []; } listeners[ type ].push( fn ); } ), - removeEventListener: jest.fn( ( type: string, fn: EventListener ) => { + removeEventListener: vi.fn( ( type: string, fn: EventListener ) => { if ( listeners[ type ] ) { listeners[ type ] = listeners[ type ].filter( ( l ) => l !== fn @@ -106,7 +116,7 @@ function createWheelEvent( overrides: Partial< WheelEvent > & { currentTarget?: unknown } = {} ): WheelEvent { return { - preventDefault: jest.fn(), + preventDefault: vi.fn(), deltaY: 0, clientX: 0, clientY: 0, @@ -127,7 +137,7 @@ function createKeyboardEvent( ): KeyboardEvent { return { key, - preventDefault: jest.fn(), + preventDefault: vi.fn(), ...overrides, } as unknown as KeyboardEvent; } @@ -144,7 +154,7 @@ function createTouchEvent( overrides: Partial< TouchEvent > = {} ): TouchEvent { return { - preventDefault: jest.fn(), + preventDefault: vi.fn(), touches, ...overrides, } as unknown as TouchEvent; @@ -166,7 +176,7 @@ function createContainerRect( overrides: Partial< DOMRect > = {} ): DOMRect { bottom: 300, x: 0, y: 0, - toJSON: jest.fn(), + toJSON: vi.fn(), ...overrides, } as DOMRect; } @@ -182,13 +192,13 @@ function createMockDocument(): Document & { } { const listeners: Record< string, EventListener[] > = {}; return { - addEventListener: jest.fn( ( type: string, fn: EventListener ) => { + addEventListener: vi.fn( ( type: string, fn: EventListener ) => { if ( ! listeners[ type ] ) { listeners[ type ] = []; } listeners[ type ].push( fn ); } ), - removeEventListener: jest.fn( ( type: string, fn: EventListener ) => { + removeEventListener: vi.fn( ( type: string, fn: EventListener ) => { if ( listeners[ type ] ) { listeners[ type ] = listeners[ type ].filter( ( l ) => l !== fn @@ -208,7 +218,7 @@ function createMockDocument(): Document & { describe( 'InteractionController', () => { const containerSize: Size = { width: 500, height: 300 }; const imageSize: Size = { width: 500, height: 300 }; - let actionMocks: jest.Mocked< CropperInteractionActions >; + let actionMocks: Mocked< CropperInteractionActions >; // Store original requestAnimationFrame so we can restore it. const originalRAF = globalThis.requestAnimationFrame; @@ -220,7 +230,7 @@ describe( 'InteractionController', () => { cb( 0 ); return 0; }; - globalThis.cancelAnimationFrame = jest.fn(); + globalThis.cancelAnimationFrame = vi.fn(); } ); afterAll( () => { @@ -230,11 +240,11 @@ describe( 'InteractionController', () => { beforeEach( () => { actionMocks = { - setPan: jest.fn(), - setZoom: jest.fn(), - setZoomAtPoint: jest.fn(), - snapRotate90: jest.fn(), - toggleFlip: jest.fn(), + setPan: vi.fn(), + setZoom: vi.fn(), + setZoomAtPoint: vi.fn(), + snapRotate90: vi.fn(), + toggleFlip: vi.fn(), }; } ); @@ -290,8 +300,8 @@ describe( 'InteractionController', () => { it( 'ignores touch pointerdown so touch gestures own touch input', () => { const state = makeState( { zoom: 2 } ); - const onGestureStart = jest.fn(); - const onStatusChange = jest.fn(); + const onGestureStart = vi.fn(); + const onStatusChange = vi.fn(); const { controller } = createController( state, { onGestureStart, onStatusChange, @@ -333,7 +343,7 @@ describe( 'InteractionController', () => { // Simulate pointerup. el._fire( 'pointerup', createPointerEvent() ); - jest.clearAllMocks(); + vi.clearAllMocks(); // Another pointermove should not dispatch because the listener // was removed after pointerup. @@ -350,8 +360,8 @@ describe( 'InteractionController', () => { it( 'calls onGestureStart on pointerdown and onGestureEnd on pointerup', () => { const state = makeState( { zoom: 2 } ); - const onGestureStart = jest.fn(); - const onGestureEnd = jest.fn(); + const onGestureStart = vi.fn(); + const onGestureEnd = vi.fn(); const { controller } = createController( state, { onGestureStart, onGestureEnd, @@ -380,7 +390,7 @@ describe( 'InteractionController', () => { it( 'reports isDragging via onStatusChange', () => { const state = makeState( { zoom: 2 } ); - const onStatusChange = jest.fn(); + const onStatusChange = vi.fn(); const { controller } = createController( state, { onStatusChange, } ); @@ -505,7 +515,7 @@ describe( 'InteractionController', () => { el ); - jest.clearAllMocks(); + vi.clearAllMocks(); const wheelEvent = createWheelEvent( { deltaY: -100, @@ -521,12 +531,12 @@ describe( 'InteractionController', () => { } ); it( 'calls onGestureStart on first wheel, onGestureEnd after debounce', () => { - jest.useFakeTimers( { - doNotFake: [ 'requestAnimationFrame', 'cancelAnimationFrame' ], + vi.useFakeTimers( { + toFake: [ 'setTimeout', 'clearTimeout' ], } ); const state = makeState( { zoom: 2 } ); - const onGestureStart = jest.fn(); - const onGestureEnd = jest.fn(); + const onGestureStart = vi.fn(); + const onGestureEnd = vi.fn(); const { controller } = createController( state, { onGestureStart, onGestureEnd, @@ -546,11 +556,11 @@ describe( 'InteractionController', () => { expect( onGestureStart ).toHaveBeenCalledTimes( 1 ); // Advance past the 300ms debounce. - jest.advanceTimersByTime( 350 ); + vi.advanceTimersByTime( 350 ); expect( onGestureEnd ).toHaveBeenCalledTimes( 1 ); - jest.useRealTimers(); + vi.useRealTimers(); } ); } ); @@ -848,8 +858,8 @@ describe( 'InteractionController', () => { it( 'calls onGestureStart/onGestureEnd for single-finger pan', () => { const state = makeState( { zoom: 2 } ); - const onGestureStart = jest.fn(); - const onGestureEnd = jest.fn(); + const onGestureStart = vi.fn(); + const onGestureEnd = vi.fn(); const { controller } = createController( state, { onGestureStart, onGestureEnd, @@ -873,7 +883,7 @@ describe( 'InteractionController', () => { it( 'reports isDragging on first single-finger move', () => { const state = makeState( { zoom: 2 } ); - const onStatusChange = jest.fn(); + const onStatusChange = vi.fn(); const { controller } = createController( state, { onStatusChange, } ); @@ -987,8 +997,8 @@ describe( 'InteractionController', () => { it( 'calls onGestureStart for pinch, onGestureEnd on touchend', () => { const state = makeState( { zoom: 1 } ); - const onGestureStart = jest.fn(); - const onGestureEnd = jest.fn(); + const onGestureStart = vi.fn(); + const onGestureEnd = vi.fn(); const { controller } = createController( state, { onGestureStart, onGestureEnd, @@ -1031,7 +1041,7 @@ describe( 'InteractionController', () => { createTouchEvent( [ { clientX: 210, clientY: 155 } ] ) ); expect( actionMocks.setPan ).toHaveBeenCalled(); - jest.clearAllMocks(); + vi.clearAllMocks(); // Second finger arrives via touchstart. controller.handleTouchStart( @@ -1122,7 +1132,7 @@ describe( 'InteractionController', () => { ] ) ); - jest.clearAllMocks(); + vi.clearAllMocks(); // One finger lifts — move with 1 touch should NOT pan // because didPinch is true. @@ -1225,7 +1235,7 @@ describe( 'InteractionController', () => { createPointerEvent( { clientX: 100, clientY: 100 } ), el ); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.destroy(); diff --git a/packages/media-editor/src/image-editor/core/test/preview-export-parity.ts b/packages/media-editor/src/image-editor/core/test/preview-export-parity.ts index 641789dbc5083b..93a6e3b5c0e770 100644 --- a/packages/media-editor/src/image-editor/core/test/preview-export-parity.ts +++ b/packages/media-editor/src/image-editor/core/test/preview-export-parity.ts @@ -28,6 +28,7 @@ * sideways during fine rotation would have been caught by this test — * the center would agree (symmetric bug) but the corners would drift. */ +import { describe, expect, it } from 'vitest'; import { mat2d, vec2 } from 'gl-matrix'; import { createCamera, diff --git a/packages/media-editor/src/image-editor/core/test/render-camera-bridge.ts b/packages/media-editor/src/image-editor/core/test/render-camera-bridge.ts index 7c84461cf68c4f..b7978dcb03c018 100644 --- a/packages/media-editor/src/image-editor/core/test/render-camera-bridge.ts +++ b/packages/media-editor/src/image-editor/core/test/render-camera-bridge.ts @@ -24,6 +24,7 @@ * * ...run these tests to verify the two paths still agree. */ +import { describe, expect, it } from 'vitest'; import { createCamera, worldToScreen, diff --git a/packages/media-editor/src/image-editor/core/test/state.ts b/packages/media-editor/src/image-editor/core/test/state.ts index 5b97deaf567e7e..8aab4f68649349 100644 --- a/packages/media-editor/src/image-editor/core/test/state.ts +++ b/packages/media-editor/src/image-editor/core/test/state.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { CropperState, Size } from '../types'; import { ABSOLUTE_MIN_ZOOM, DEFAULT_STATE, MAX_ZOOM } from '../constants'; import { cropperReducer, enforceContainment, isStateDirty } from '../state'; diff --git a/packages/media-editor/src/image-editor/core/test/stencil-math.ts b/packages/media-editor/src/image-editor/core/test/stencil-math.ts index 1a4fc27c46f65b..0aaeeb3562122f 100644 --- a/packages/media-editor/src/image-editor/core/test/stencil-math.ts +++ b/packages/media-editor/src/image-editor/core/test/stencil-math.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { computeFreeResizeRect, computeLockedResizeRect, diff --git a/packages/media-editor/src/image-editor/core/transforms/test/pipeline.ts b/packages/media-editor/src/image-editor/core/transforms/test/pipeline.ts index 6495eee855bdd6..1d639cee28415b 100644 --- a/packages/media-editor/src/image-editor/core/transforms/test/pipeline.ts +++ b/packages/media-editor/src/image-editor/core/transforms/test/pipeline.ts @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from 'vitest'; import type { CropperState, TransformOperation } from '../../types'; import { DEFAULT_STATE } from '../../constants'; import { applyOperationToState, stateFromPipeline } from '../pipeline'; diff --git a/packages/media-editor/src/store/test/reducer.ts b/packages/media-editor/src/store/test/reducer.ts index 60eb7591e08f05..bac968a2c1f0f3 100644 --- a/packages/media-editor/src/store/test/reducer.ts +++ b/packages/media-editor/src/store/test/reducer.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import reducer, { DEFAULT_STATE } from '../reducer'; describe( 'core/media-editor reducer', () => { @@ -7,8 +8,8 @@ describe( 'core/media-editor reducer', () => { } ); it( 'opens the modal with id and onUpdate', () => { - const onUpdate = jest.fn(); - const onClose = jest.fn(); + const onUpdate = vi.fn(); + const onClose = vi.fn(); const state = reducer( undefined, { type: 'OPEN_MEDIA_EDITOR_MODAL', id: 42, @@ -24,10 +25,10 @@ describe( 'core/media-editor reducer', () => { } ); it( 'replaces state on a subsequent open (new onUpdate)', () => { - const firstOnUpdate = jest.fn(); - const firstOnClose = jest.fn(); - const secondOnUpdate = jest.fn(); - const secondOnClose = jest.fn(); + const firstOnUpdate = vi.fn(); + const firstOnClose = vi.fn(); + const secondOnUpdate = vi.fn(); + const secondOnClose = vi.fn(); const first = reducer( undefined, { type: 'OPEN_MEDIA_EDITOR_MODAL', id: 42, @@ -52,8 +53,8 @@ describe( 'core/media-editor reducer', () => { const opened = reducer( undefined, { type: 'OPEN_MEDIA_EDITOR_MODAL', id: 42, - onUpdate: jest.fn(), - onClose: jest.fn(), + onUpdate: vi.fn(), + onClose: vi.fn(), } ); const closed = reducer( opened, { type: 'CLOSE_MEDIA_EDITOR_MODAL', diff --git a/packages/media-fields/package.json b/packages/media-fields/package.json index 76638d02b8cf62..77c0c271858d2e 100644 --- a/packages/media-fields/package.json +++ b/packages/media-fields/package.json @@ -65,7 +65,8 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/media-fields/src/filename/test/index.test.ts b/packages/media-fields/src/filename/test/index.test.ts index 1abc188c4682d1..5a96d5bf469197 100644 --- a/packages/media-fields/src/filename/test/index.test.ts +++ b/packages/media-fields/src/filename/test/index.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import filenameField from '../index'; import type { MediaItem } from '../../types'; diff --git a/packages/media-fields/src/filesize/test/index.test.tsx b/packages/media-fields/src/filesize/test/index.test.tsx index 5dc1f808743730..a1756573d666a6 100644 --- a/packages/media-fields/src/filesize/test/index.test.tsx +++ b/packages/media-fields/src/filesize/test/index.test.tsx @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import filesizeField from '../index'; import type { MediaItem } from '../../types'; diff --git a/packages/media-fields/src/media_dimensions/test/index.test.ts b/packages/media-fields/src/media_dimensions/test/index.test.ts index 7f480ed6b687eb..949c6202536da3 100644 --- a/packages/media-fields/src/media_dimensions/test/index.test.ts +++ b/packages/media-fields/src/media_dimensions/test/index.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { Attachment, Updatable } from '@wordpress/core-data'; import mediaDimensionsField from '../index'; diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index 69b34e30125598..0addb7824623fb 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -70,7 +70,8 @@ "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/jest": "^30.0.0", - "@wordpress/preferences": "file:../preferences" + "@wordpress/preferences": "file:../preferences", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/media-utils/src/utils/test/flatten-form-data.ts b/packages/media-utils/src/utils/test/flatten-form-data.ts index 043a68c8b5e02f..b88927afa21561 100644 --- a/packages/media-utils/src/utils/test/flatten-form-data.ts +++ b/packages/media-utils/src/utils/test/flatten-form-data.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { flattenFormData } from '../flatten-form-data'; describe( 'flattenFormData', () => { diff --git a/packages/media-utils/src/utils/test/get-mime-types-array.ts b/packages/media-utils/src/utils/test/get-mime-types-array.ts index d845f5c2f7ced5..e57ac33d130b34 100644 --- a/packages/media-utils/src/utils/test/get-mime-types-array.ts +++ b/packages/media-utils/src/utils/test/get-mime-types-array.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getMimeTypesArray } from '../get-mime-types-array'; describe( 'getMimeTypesArray', () => { diff --git a/packages/media-utils/src/utils/test/get-upload-error-message.ts b/packages/media-utils/src/utils/test/get-upload-error-message.ts index 0518fbf844545d..2f0c12b34ace04 100644 --- a/packages/media-utils/src/utils/test/get-upload-error-message.ts +++ b/packages/media-utils/src/utils/test/get-upload-error-message.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getUploadErrorMessage } from '../get-upload-error-message'; const SERVER_ERROR = 'Failed to upload "kitten.jpeg". Please try again.'; diff --git a/packages/media-utils/src/utils/test/upload-error.ts b/packages/media-utils/src/utils/test/upload-error.ts index 3939233fad57b3..807ae57712cf77 100644 --- a/packages/media-utils/src/utils/test/upload-error.ts +++ b/packages/media-utils/src/utils/test/upload-error.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { UploadError } from '../upload-error'; describe( 'UploadError', () => { diff --git a/packages/notices/package.json b/packages/notices/package.json index e466489c7f54a4..f400c986ed4642 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -58,7 +58,8 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/notices/src/store/test/actions.ts b/packages/notices/src/store/test/actions.ts index 684b44b534ae49..ad37bd56221b98 100644 --- a/packages/notices/src/store/test/actions.ts +++ b/packages/notices/src/store/test/actions.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { createNotice, createSuccessNotice, diff --git a/packages/notices/src/store/test/reducer.ts b/packages/notices/src/store/test/reducer.ts index a0891d232e62d1..f039e47d70acb1 100644 --- a/packages/notices/src/store/test/reducer.ts +++ b/packages/notices/src/store/test/reducer.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import reducer from '../reducer'; import { diff --git a/packages/notices/src/store/test/selectors.ts b/packages/notices/src/store/test/selectors.ts index 7847560d7112ae..3a1336b38fbfba 100644 --- a/packages/notices/src/store/test/selectors.ts +++ b/packages/notices/src/store/test/selectors.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getNotices } from '../selectors'; import { DEFAULT_CONTEXT } from '../constants'; diff --git a/packages/npm-package-json-lint-config/package.json b/packages/npm-package-json-lint-config/package.json index 76f22c3a419b59..a69765614b713e 100644 --- a/packages/npm-package-json-lint-config/package.json +++ b/packages/npm-package-json-lint-config/package.json @@ -31,6 +31,9 @@ ".": "./index.js", "./package.json": "./package.json" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "peerDependencies": { "npm-package-json-lint": ">=6.0.0" }, diff --git a/packages/npm-package-json-lint-config/test/index.test.js b/packages/npm-package-json-lint-config/test/index.test.js index ef68d4e973f00f..d7cb09b34b7309 100644 --- a/packages/npm-package-json-lint-config/test/index.test.js +++ b/packages/npm-package-json-lint-config/test/index.test.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import config from '../'; describe( 'npm-package-json-lint config tests', () => { diff --git a/packages/nux/package.json b/packages/nux/package.json index 5920cfab0b7a20..bdd178dd4f8094 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -57,7 +57,8 @@ }, "devDependencies": { "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19", diff --git a/packages/nux/src/store/test/actions.js b/packages/nux/src/store/test/actions.js index 94913790dc44a2..24cf7f164f47d7 100644 --- a/packages/nux/src/store/test/actions.js +++ b/packages/nux/src/store/test/actions.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { triggerGuide, dismissTip, disableTips, enableTips } from '../actions'; describe( 'actions', () => { diff --git a/packages/nux/src/store/test/reducer.js b/packages/nux/src/store/test/reducer.js index 7062b280ff4b13..9bbb74420d35f4 100644 --- a/packages/nux/src/store/test/reducer.js +++ b/packages/nux/src/store/test/reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import reducer from '../reducer'; describe( 'reducer', () => { diff --git a/packages/nux/src/store/test/selectors.js b/packages/nux/src/store/test/selectors.js index 8d013bd799171e..f68674c22fd0ce 100644 --- a/packages/nux/src/store/test/selectors.js +++ b/packages/nux/src/store/test/selectors.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getAssociatedGuide, isTipVisible, areTipsEnabled } from '../selectors'; describe( 'selectors', () => { diff --git a/packages/plugins/package.json b/packages/plugins/package.json index da9aa37f7a39ee..3ce520d2745e5d 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -55,7 +55,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "@types/react-is": "^18.3.1" + "@types/react-is": "^18.3.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/plugins/src/api/test/index.js b/packages/plugins/src/api/test/index.js index b602314ec73e34..f90eb89932160b 100644 --- a/packages/plugins/src/api/test/index.js +++ b/packages/plugins/src/api/test/index.js @@ -1,3 +1,4 @@ +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; import { memo } from '@wordpress/element'; import { registerPlugin, unregisterPlugin, getPlugin, getPlugins } from '../'; diff --git a/packages/postcss-themes/package.json b/packages/postcss-themes/package.json index 1df757449896f2..72d17255e3b682 100644 --- a/packages/postcss-themes/package.json +++ b/packages/postcss-themes/package.json @@ -35,6 +35,9 @@ ".": "./index.js", "./package.json": "./package.json" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "peerDependencies": { "postcss": "^8.0.0" }, diff --git a/packages/postcss-themes/test/__snapshots__/index.js.snap b/packages/postcss-themes/test/__snapshots__/index.js.snap index c4966e05172b66..23cb07fe4da5f7 100644 --- a/packages/postcss-themes/test/__snapshots__/index.js.snap +++ b/packages/postcss-themes/test/__snapshots__/index.js.snap @@ -1,24 +1,24 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`postcss-themes add the prefix selector to all subselectors 1`] = ` +exports[`postcss-themes > add the prefix selector to all subselectors 1`] = ` "a, span{ color: #ccc } body.light a, body.light span{ color: #FFF } body.dark a, body.dark span{ color: #000 }" `; -exports[`postcss-themes gather several declaration in a unique rule 1`] = ` +exports[`postcss-themes > gather several declaration in a unique rule 1`] = ` "a{ color: #ccc; background: red; } body.light a{ color: #FFF; background: green; } body.dark a{ color: #000; background: blue; }" `; -exports[`postcss-themes replaces multiple rules in the same declaration 1`] = ` +exports[`postcss-themes > replaces multiple rules in the same declaration 1`] = ` "a{ background: linear-gradient( -45deg, #ccc 50%, red 50% ) } body.light a{ background: linear-gradient( -45deg, #FFF 50%, green 50% ) } body.dark a{ background: linear-gradient( -45deg, #000 50%, blue 50% ) }" `; -exports[`postcss-themes replaces the default color and generate theme colors 1`] = ` +exports[`postcss-themes > replaces the default color and generate theme colors 1`] = ` "a{ color: #ccc } body.light a{ color: #FFF } body.dark a{ color: #000 }" diff --git a/packages/postcss-themes/test/index.js b/packages/postcss-themes/test/index.js index 95b9b98e252618..5e2092331e4f03 100644 --- a/packages/postcss-themes/test/index.js +++ b/packages/postcss-themes/test/index.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import postcss from 'postcss'; import plugin from '../'; diff --git a/packages/preferences-persistence/package.json b/packages/preferences-persistence/package.json index bafab0faa40471..3acbaa4ca7a4cb 100644 --- a/packages/preferences-persistence/package.json +++ b/packages/preferences-persistence/package.json @@ -45,6 +45,9 @@ "dependencies": { "@wordpress/api-fetch": "file:../api-fetch" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" } diff --git a/packages/preferences-persistence/src/create/test/debounce-async.js b/packages/preferences-persistence/src/create/test/debounce-async.js index be22800b424359..5a683e2bec2f0f 100644 --- a/packages/preferences-persistence/src/create/test/debounce-async.js +++ b/packages/preferences-persistence/src/create/test/debounce-async.js @@ -1,84 +1,87 @@ +import { setImmediate as setImmediateCallback } from 'node:timers'; +import { describe, expect, it, vi } from 'vitest'; import debounceAsync from '../debounce-async'; -// See https://stackoverflow.com/questions/52177631/jest-timer-and-promise-dont-work-well-settimeout-and-async-function. -// Jest fake timers and async functions don't mix too well, since queued up -// promises can prevent jest from calling timeouts. -// This function flushes promises in the queue. +// Let promise callbacks settle without advancing the faked timeout clock. function flushPromises() { - return new Promise( jest.requireActual( 'timers' ).setImmediate ); + return new Promise( setImmediateCallback ); } -// Promisify a timeout for use with jest.fn. +// Promisify a timeout for use with vi.fn. function timeout( milliseconds ) { return new Promise( ( resolve ) => setTimeout( resolve, milliseconds ) ); } describe( 'debounceAsync', () => { it( 'uses a leading debounce, the first call happens immediately', () => { - const fn = jest.fn( async () => {} ); + const fn = vi.fn( async () => {} ); const debounced = debounceAsync( fn, 20 ); debounced(); expect( fn ).toHaveBeenCalledTimes( 1 ); } ); it( 'calls the function on the leading edge and then once on the trailing edge when there are multiple calls', async () => { - jest.useFakeTimers(); - const fn = jest.fn( async () => {} ); - const debounced = debounceAsync( fn, 20 ); - - debounced( 'A' ); + vi.useFakeTimers( { toFake: [ 'setTimeout', 'clearTimeout' ] } ); + try { + const fn = vi.fn( async () => {} ); + const debounced = debounceAsync( fn, 20 ); - expect( fn ).toHaveBeenCalledTimes( 1 ); + debounced( 'A' ); - debounced( 'B' ); - debounced( 'C' ); - debounced( 'D' ); + expect( fn ).toHaveBeenCalledTimes( 1 ); - await flushPromises(); - jest.runAllTimers(); + debounced( 'B' ); + debounced( 'C' ); + debounced( 'D' ); - expect( fn ).toHaveBeenCalledTimes( 2 ); - expect( fn ).toHaveBeenCalledWith( 'A' ); - expect( fn ).toHaveBeenCalledWith( 'D' ); + await flushPromises(); + vi.runAllTimers(); - jest.runOnlyPendingTimers(); - jest.useRealTimers(); + expect( fn ).toHaveBeenCalledTimes( 2 ); + expect( fn ).toHaveBeenCalledWith( 'A' ); + expect( fn ).toHaveBeenCalledWith( 'D' ); + } finally { + vi.clearAllTimers(); + vi.useRealTimers(); + } } ); it( 'ensures the delay has elapsed between calls', async () => { - jest.useFakeTimers(); - const fn = jest.fn( async () => timeout( 10 ) ); - const debounced = debounceAsync( fn, 20 ); - - // The first call has been triggered, but will take 10ms to resolve. - debounced(); - debounced(); - debounced(); - debounced(); - expect( fn ).toHaveBeenCalledTimes( 1 ); - - // The first call has resolved. The delay period has started but has yet to finish. - await flushPromises(); - jest.advanceTimersByTime( 11 ); - expect( fn ).toHaveBeenCalledTimes( 1 ); - - // The second call is about to commence, but hasn't yet. - await flushPromises(); - jest.advanceTimersByTime( 18 ); - expect( fn ).toHaveBeenCalledTimes( 1 ); - - // The second call has now commenced. - await flushPromises(); - jest.advanceTimersByTime( 2 ); - expect( fn ).toHaveBeenCalledTimes( 2 ); - - // No more calls happen. - await flushPromises(); - jest.runAllTimers(); - expect( fn ).toHaveBeenCalledTimes( 2 ); - - jest.runOnlyPendingTimers(); - jest.useRealTimers(); + vi.useFakeTimers( { toFake: [ 'setTimeout', 'clearTimeout' ] } ); + try { + const fn = vi.fn( async () => timeout( 10 ) ); + const debounced = debounceAsync( fn, 20 ); + + // The first call has been triggered, but will take 10ms to resolve. + debounced(); + debounced(); + debounced(); + debounced(); + expect( fn ).toHaveBeenCalledTimes( 1 ); + + // The first call has resolved. The delay period has started but has yet to finish. + await flushPromises(); + vi.advanceTimersByTime( 11 ); + expect( fn ).toHaveBeenCalledTimes( 1 ); + + // The second call is about to commence, but hasn't yet. + await flushPromises(); + vi.advanceTimersByTime( 18 ); + expect( fn ).toHaveBeenCalledTimes( 1 ); + + // The second call has now commenced. + await flushPromises(); + vi.advanceTimersByTime( 2 ); + expect( fn ).toHaveBeenCalledTimes( 2 ); + + // No more calls happen. + await flushPromises(); + vi.runAllTimers(); + expect( fn ).toHaveBeenCalledTimes( 2 ); + } finally { + vi.clearAllTimers(); + vi.useRealTimers(); + } } ); it( 'is thenable, returning any data from promise resolution of the debounced function', async () => { @@ -105,21 +108,11 @@ describe( 'debounceAsync', () => { const debounced = debounceAsync( fn, 20 ); - // Test traditional try/catch. - try { - await debounced(); - } catch ( error ) { - // Disable reason - the test uses `expect.assertions` to ensure - // conditional assertions are called. - // eslint-disable-next-line jest/no-conditional-expect - expect( error ).toBe( expectedError ); - } + // Test awaiting a rejection. + await expect( debounced() ).rejects.toBe( expectedError ); // Test chained .catch(). await debounced().catch( ( error ) => { - // Disable reason - the test uses `expect.assertions` to ensure - // conditional assertions are called. - // eslint-disable-next-line jest/no-conditional-expect expect( error ).toBe( expectedError ); } ); } ); diff --git a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/convert-edit-post-panels.js b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/convert-edit-post-panels.js index 25efe9a56f71d2..3b12ef102862f9 100644 --- a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/convert-edit-post-panels.js +++ b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/convert-edit-post-panels.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import convertEditPostPanels from '../convert-edit-post-panels'; describe( 'convertEditPostPanels', () => { diff --git a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/index.js b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/index.js index b4cfa2bffd02ce..f0d92307fab879 100644 --- a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/index.js +++ b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/index.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { convertLegacyData } from '..'; const legacyData = { diff --git a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-feature-preferences.js b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-feature-preferences.js index a8bb008b9381e4..eb6fb27c89cea2 100644 --- a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-feature-preferences.js +++ b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-feature-preferences.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import moveFeaturePreferences from '../move-feature-preferences'; describe( 'moveFeaturePreferences', () => { diff --git a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-individual-preference.js b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-individual-preference.js index 69862558c41540..960cf5037400e2 100644 --- a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-individual-preference.js +++ b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-individual-preference.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import moveIndividualPreference from '../move-individual-preference'; describe( 'moveIndividualPreference', () => { diff --git a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-interface-enable-items.js b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-interface-enable-items.js index 37dfcc80935fff..5d4d071147a9f5 100644 --- a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-interface-enable-items.js +++ b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-interface-enable-items.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import moveInterfaceEnableItems from '../move-interface-enable-items'; describe( 'moveInterfaceEnableItems', () => { diff --git a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-third-party-feature-preferences.js b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-third-party-feature-preferences.js index ecd87ea43ac557..a81c7504c10887 100644 --- a/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-third-party-feature-preferences.js +++ b/packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-third-party-feature-preferences.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import moveThirdPartyFeaturePreferences from '../move-third-party-feature-preferences'; describe( 'moveThirdPartyFeaturePreferences', () => { diff --git a/packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-complementary-areas.js b/packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-complementary-areas.js index 0491053f291388..92158b0969ec8a 100644 --- a/packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-complementary-areas.js +++ b/packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-complementary-areas.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import convertComplementaryAreas from '../convert-complementary-areas'; describe( 'convertComplementaryAreas', () => { diff --git a/packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-editor-settings.js b/packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-editor-settings.js index 051b2502b80d50..5da6f59cdd37bd 100644 --- a/packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-editor-settings.js +++ b/packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-editor-settings.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import convertEditorSettings from '../convert-editor-settings'; describe( 'convertEditorSettings', () => { diff --git a/packages/preferences-persistence/src/migrations/preferences-package-data/test/index.js b/packages/preferences-persistence/src/migrations/preferences-package-data/test/index.js index c65194ac6bbaf4..588f962c745c3f 100644 --- a/packages/preferences-persistence/src/migrations/preferences-package-data/test/index.js +++ b/packages/preferences-persistence/src/migrations/preferences-package-data/test/index.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import convertPreferencesPackageData from '../'; const input = { diff --git a/packages/preferences/package.json b/packages/preferences/package.json index 39367295f3983b..785f833256e9bd 100644 --- a/packages/preferences/package.json +++ b/packages/preferences/package.json @@ -62,7 +62,8 @@ "@wordpress/private-apis": "file:../private-apis" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/preferences/src/store/test/actions.ts b/packages/preferences/src/store/test/actions.ts index 3a6613311d242f..1a45c4bdb1677c 100644 --- a/packages/preferences/src/store/test/actions.ts +++ b/packages/preferences/src/store/test/actions.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { setPersistenceLayer } from '../actions'; describe( 'setPersistenceLayer', () => { diff --git a/packages/preferences/src/store/test/reducer.ts b/packages/preferences/src/store/test/reducer.ts index 2c53f2efbd121d..af0e1ceee0078f 100644 --- a/packages/preferences/src/store/test/reducer.ts +++ b/packages/preferences/src/store/test/reducer.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { preferences } from '../reducer'; describe( 'withPersistenceLayer( preferences )', () => { @@ -9,15 +10,20 @@ describe( 'withPersistenceLayer( preferences )', () => { const action = { type: 'SET_PERSISTENCE_LAYER', + persistenceLayer: { + get: async () => persistedData, + set() {}, + }, persistedData, - }; + } as const; expect( preferences( {}, action ) ).toEqual( persistedData ); } ); it( 'calls the persistence layer `set` function with the updated store state whenever the `SET_PREFERENCE_VALUE` action is dispatched', () => { - const set = jest.fn(); + const set = vi.fn(); const persistenceLayer = { + get: async () => ( {} ), set, }; @@ -25,7 +31,7 @@ describe( 'withPersistenceLayer( preferences )', () => { type: 'SET_PERSISTENCE_LAYER', persistenceLayer, persistedData: {}, - }; + } as const; // Set the persistence layer. preferences( {}, setPersistenceLayerAction ); @@ -33,9 +39,10 @@ describe( 'withPersistenceLayer( preferences )', () => { // Update a value. const setPreferenceValueAction = { type: 'SET_PREFERENCE_VALUE', + scope: 'test-scope', name: 'myPreference', value: 'myValue', - }; + } as const; const state = preferences( {}, setPreferenceValueAction ); diff --git a/packages/preferences/src/store/test/selectors.ts b/packages/preferences/src/store/test/selectors.ts index 079c2516d4368e..28188b5fb2b6ef 100644 --- a/packages/preferences/src/store/test/selectors.ts +++ b/packages/preferences/src/store/test/selectors.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { get } from '../selectors'; describe( 'selectors', () => { diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json index 7f358df627e07a..66dac9e46ba5fc 100644 --- a/packages/prettier-config/package.json +++ b/packages/prettier-config/package.json @@ -35,7 +35,8 @@ "devDependencies": { "@types/node": "^20.19.39", "@types/prettier": "^2.4.4", - "prettier": "npm:wp-prettier@^3.0.3" + "prettier": "npm:wp-prettier@^3.0.3", + "vitest": "^4.1.10" }, "peerDependencies": { "prettier": ">=3" diff --git a/packages/prettier-config/test/index.js b/packages/prettier-config/test/index.js index fae7745b6c334c..d6a700ed10f6d6 100644 --- a/packages/prettier-config/test/index.js +++ b/packages/prettier-config/test/index.js @@ -1,9 +1,12 @@ import { execFile } from 'child_process'; import path from 'path'; +import { fileURLToPath } from 'url'; import { promisify } from 'util'; +import { describe, expect, it } from 'vitest'; import config from '../lib/'; const execFileAsync = promisify( execFile ); +const testDirectory = path.dirname( fileURLToPath( import.meta.url ) ); describe( 'prettier config tests', () => { it( 'should be an object', () => { @@ -12,7 +15,7 @@ describe( 'prettier config tests', () => { } ); it( 'should resolve file-specific options from the root config', async () => { - const repositoryRoot = path.resolve( __dirname, '../../..' ); + const repositoryRoot = path.resolve( testDirectory, '../../..' ); // Resolve prettier from this file const prettierPath = require.resolve( 'prettier' ); const resolveConfigScript = ` diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index a4897dfdabf951..90199060067b2d 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -47,8 +47,8 @@ "requestidlecallback": "^0.3.0" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/requestidlecallback": "^0.3.4" + "@types/requestidlecallback": "^0.3.4", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/priority-queue/src/test/index.ts b/packages/priority-queue/src/test/index.ts index 51912977e4a194..7884958ce7f9de 100644 --- a/packages/priority-queue/src/test/index.ts +++ b/packages/priority-queue/src/test/index.ts @@ -1,23 +1,33 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createQueue } from '../'; import _requestIdleCallback from '../request-idle-callback'; +import type { RequestIdleCallbackCallback } from '../types'; const requestIdleCallback = _requestIdleCallback as typeof _requestIdleCallback & { tick: ( deadline?: Partial< IdleDeadline > | number ) => void; }; -jest.mock( '../request-idle-callback', () => { - const emitter = new ( jest.requireActual( 'events' ).EventEmitter )(); - - return Object.assign( - ( callback: ( deadline: number ) => void ) => - emitter.once( 'tick', ( deadline = Date.now() ) => - callback( deadline ) - ), - { - tick: ( deadline?: number ) => emitter.emit( 'tick', deadline ), - } - ); +vi.mock( import( '../request-idle-callback' ), () => { + const callbacks = new Set< RequestIdleCallbackCallback >(); + + return { + default: Object.assign( + ( callback: RequestIdleCallbackCallback ) => + callbacks.add( callback ), + { + tick: ( + deadline: Partial< IdleDeadline > | number = Date.now() + ) => { + const pendingCallbacks = [ ...callbacks ]; + callbacks.clear(); + for ( const callback of pendingCallbacks ) { + callback( deadline as IdleDeadline | number ); + } + }, + } + ), + }; } ); describe( 'createQueue', () => { @@ -28,7 +38,7 @@ describe( 'createQueue', () => { describe( 'add', () => { it( 'runs callback after processing waiting queue', () => { - const callback = jest.fn(); + const callback = vi.fn(); queue.add( {}, callback ); @@ -40,8 +50,8 @@ describe( 'createQueue', () => { it( 'runs callbacks in order by distinct added element', () => { const elementA = {}; const elementB = {}; - const callbackElementA = jest.fn(); - const callbackElementB = jest.fn(); + const callbackElementA = vi.fn(); + const callbackElementB = vi.fn(); queue.add( elementA, callbackElementA ); queue.add( elementB, callbackElementB ); @@ -61,8 +71,8 @@ describe( 'createQueue', () => { it( 'calls most recently added callback if added for same element', () => { const element = {}; - const callbackOne = jest.fn(); - const callbackTwo = jest.fn(); + const callbackOne = vi.fn(); + const callbackTwo = vi.fn(); queue.add( element, callbackOne ); queue.add( element, callbackTwo ); @@ -78,9 +88,9 @@ describe( 'createQueue', () => { const elementA = {}; const elementB = {}; const elementC = {}; - const callbackElementA = jest.fn(); - const callbackElementB = jest.fn(); - const callbackElementC = jest.fn(); + const callbackElementA = vi.fn(); + const callbackElementB = vi.fn(); + const callbackElementC = vi.fn(); queue.add( elementA, callbackElementA ); queue.add( elementB, callbackElementB ); queue.add( elementC, callbackElementC ); @@ -91,7 +101,7 @@ describe( 'createQueue', () => { // Mock implementation such that with the first call, it reports as // having some time remaining, but no time remaining on the second. - const timeRemaining = jest + const timeRemaining = vi .fn() .mockImplementationOnce( () => 100 ) .mockImplementationOnce( () => 0 ); @@ -111,8 +121,8 @@ describe( 'createQueue', () => { it( 'invokes all callbacks associated with element', () => { const elementA = {}; const elementB = {}; - const callbackElementA = jest.fn(); - const callbackElementB = jest.fn(); + const callbackElementA = vi.fn(); + const callbackElementB = vi.fn(); queue.add( elementA, callbackElementA ); queue.add( elementB, callbackElementB ); @@ -137,8 +147,8 @@ describe( 'createQueue', () => { it( 'removes all callbacks associated with element without executing', () => { const elementA = {}; const elementB = {}; - const callbackElementA = jest.fn(); - const callbackElementB = jest.fn(); + const callbackElementA = vi.fn(); + const callbackElementB = vi.fn(); queue.add( elementA, callbackElementA ); queue.add( elementB, callbackElementB ); diff --git a/packages/private-apis/package.json b/packages/private-apis/package.json index 9e90af6a97d3f0..4eb3f54191d8c6 100644 --- a/packages/private-apis/package.json +++ b/packages/private-apis/package.json @@ -44,7 +44,8 @@ "types": "build-types", "sideEffects": false, "devDependencies": { - "react": "^18.3.1" + "react": "^18.3.1", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/private-apis/src/test/index.jsx b/packages/private-apis/src/test/index.jsx index 3cf54c211398a6..ceee24e291f8c8 100644 --- a/packages/private-apis/src/test/index.jsx +++ b/packages/private-apis/src/test/index.jsx @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from 'vitest'; import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '../'; import { resetAllowedCoreModules, allowCoreModule } from '../implementation'; diff --git a/packages/project-management-automation/lib/tasks/add-milestone/test/index.js b/packages/project-management-automation/lib/tasks/add-milestone/test/index.js index 0661bbee673a3e..fa9a8d04ad6c4b 100644 --- a/packages/project-management-automation/lib/tasks/add-milestone/test/index.js +++ b/packages/project-management-automation/lib/tasks/add-milestone/test/index.js @@ -1,4 +1,7 @@ -import addMilestone from '../'; +import { createRequire } from 'node:module'; +import { describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const addMilestone = require( '../' ); describe( 'addMilestone', () => { it( 'does nothing if base is not trunk', async () => { @@ -7,21 +10,21 @@ describe( 'addMilestone', () => { }; const octokit = { paginate: { - iterator: jest.fn(), + iterator: vi.fn(), }, rest: { issues: { - get: jest.fn(), - createMilestone: jest.fn(), + get: vi.fn(), + createMilestone: vi.fn(), listMilestones: { endpoint: { - merge: jest.fn(), + merge: vi.fn(), }, }, - update: jest.fn(), + update: vi.fn(), }, repos: { - getContent: jest.fn(), + getContent: vi.fn(), }, }, }; @@ -50,23 +53,23 @@ describe( 'addMilestone', () => { }; const octokit = { paginate: { - iterator: jest.fn(), + iterator: vi.fn(), }, rest: { issues: { - get: jest.fn( () => + get: vi.fn( () => Promise.resolve( { data: { milestone: 'Gutenberg 6.4', }, } ) ), - createMilestone: jest.fn(), - listMilestones: jest.fn(), - update: jest.fn(), + createMilestone: vi.fn(), + listMilestones: vi.fn(), + update: vi.fn(), }, repos: { - getContent: jest.fn(), + getContent: vi.fn(), }, }, }; @@ -97,7 +100,7 @@ describe( 'addMilestone', () => { }; const octokit = { paginate: { - iterator: jest.fn().mockReturnValue( [ + iterator: vi.fn().mockReturnValue( [ Promise.resolve( { data: [ { @@ -121,23 +124,23 @@ describe( 'addMilestone', () => { }, rest: { issues: { - get: jest.fn( () => + get: vi.fn( () => Promise.resolve( { data: { milestone: null, }, } ) ), - createMilestone: jest.fn(), + createMilestone: vi.fn(), listMilestones: { endpoint: { - merge: jest.fn(), + merge: vi.fn(), }, }, - update: jest.fn(), + update: vi.fn(), }, repos: { - getContent: jest.fn( () => + getContent: vi.fn( () => Promise.resolve( { data: { content: Buffer.from( @@ -192,7 +195,7 @@ describe( 'addMilestone', () => { }; const octokit = { paginate: { - iterator: jest.fn().mockReturnValue( [ + iterator: vi.fn().mockReturnValue( [ Promise.resolve( { data: [ { @@ -216,23 +219,23 @@ describe( 'addMilestone', () => { }, rest: { issues: { - get: jest.fn( () => + get: vi.fn( () => Promise.resolve( { data: { milestone: null, }, } ) ), - createMilestone: jest.fn(), + createMilestone: vi.fn(), listMilestones: { endpoint: { - merge: jest.fn(), + merge: vi.fn(), }, }, - update: jest.fn(), + update: vi.fn(), }, repos: { - getContent: jest.fn( () => + getContent: vi.fn( () => Promise.resolve( { data: { content: Buffer.from( diff --git a/packages/project-management-automation/lib/tasks/assign-fixed-issues/test/index.js b/packages/project-management-automation/lib/tasks/assign-fixed-issues/test/index.js index f49d82181056ea..3bb72d20990e5a 100644 --- a/packages/project-management-automation/lib/tasks/assign-fixed-issues/test/index.js +++ b/packages/project-management-automation/lib/tasks/assign-fixed-issues/test/index.js @@ -1,4 +1,7 @@ -import assignFixedIssues from '../'; +import { createRequire } from 'node:module'; +import { describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const assignFixedIssues = require( '../' ); describe( 'assignFixedIssues', () => { it( 'does nothing if there are no fixed issues', async () => { @@ -10,8 +13,8 @@ describe( 'assignFixedIssues', () => { const octokit = { rest: { issues: { - addAssignees: jest.fn(), - addLabels: jest.fn(), + addAssignees: vi.fn(), + addLabels: vi.fn(), }, }, }; @@ -40,8 +43,8 @@ describe( 'assignFixedIssues', () => { const octokit = { rest: { issues: { - addAssignees: jest.fn( () => Promise.resolve( {} ) ), - addLabels: jest.fn( () => Promise.resolve( {} ) ), + addAssignees: vi.fn( () => Promise.resolve( {} ) ), + addLabels: vi.fn( () => Promise.resolve( {} ) ), }, }, }; diff --git a/packages/project-management-automation/lib/tasks/first-time-contributor-account-link/test/index.js b/packages/project-management-automation/lib/tasks/first-time-contributor-account-link/test/index.js index f338e9cb7706e2..5f2623bfd446ef 100644 --- a/packages/project-management-automation/lib/tasks/first-time-contributor-account-link/test/index.js +++ b/packages/project-management-automation/lib/tasks/first-time-contributor-account-link/test/index.js @@ -1,10 +1,29 @@ -import { setOutput } from '@actions/core'; -import firstTimeContributorAccountLink from '../'; -import hasWordPressProfile from '../../../has-wordpress-profile'; - -jest.mock( '../../../has-wordpress-profile', () => jest.fn() ); -jest.mock( '@actions/core', () => ( { setOutput: jest.fn() } ) ); - +import { createRequire } from 'node:module'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const actionsCorePath = require.resolve( '@actions/core' ); +const originalActionsCore = require( actionsCorePath ); +const setOutput = vi.fn(); +const hasWordPressProfile = vi.fn(); +const hasWordPressProfilePath = require.resolve( + '../../../has-wordpress-profile' +); +const originalHasWordPressProfile = require( hasWordPressProfilePath ); +const taskPath = require.resolve( '../' ); +let firstTimeContributorAccountLink; +try { + require.cache[ actionsCorePath ].exports = { + ...originalActionsCore, + setOutput, + }; + require.cache[ hasWordPressProfilePath ].exports = hasWordPressProfile; + firstTimeContributorAccountLink = require( taskPath ); +} finally { + require.cache[ actionsCorePath ].exports = originalActionsCore; + require.cache[ hasWordPressProfilePath ].exports = + originalHasWordPressProfile; + delete require.cache[ taskPath ]; +} const botUser = { data: { name: 'Ghost', @@ -58,10 +77,10 @@ describe( 'firstTimeContributorAccountLink', () => { const octokit = { rest: { repos: { - listCommits: jest.fn(), + listCommits: vi.fn(), }, users: { - getByUsername: jest.fn( () => humanUser ), + getByUsername: vi.fn( () => humanUser ), }, }, }; @@ -90,10 +109,10 @@ describe( 'firstTimeContributorAccountLink', () => { const octokit = { rest: { repos: { - listCommits: jest.fn(), + listCommits: vi.fn(), }, users: { - getByUsername: jest.fn( () => humanUser ), + getByUsername: vi.fn( () => humanUser ), }, }, }; @@ -108,11 +127,11 @@ describe( 'firstTimeContributorAccountLink', () => { const octokit = { rest: { repos: { - listCommits: jest.fn(), + listCommits: vi.fn(), }, users: { // Return a bot when `getByUsername` is called. - getByUsername: jest.fn( () => botUser ), + getByUsername: vi.fn( () => botUser ), }, }, }; @@ -129,7 +148,7 @@ describe( 'firstTimeContributorAccountLink', () => { const octokit = { rest: { repos: { - listCommits: jest.fn( () => + listCommits: vi.fn( () => Promise.resolve( { data: [ { @@ -143,7 +162,7 @@ describe( 'firstTimeContributorAccountLink', () => { ), }, users: { - getByUsername: jest.fn( () => humanUser ), + getByUsername: vi.fn( () => humanUser ), }, }, }; @@ -165,7 +184,7 @@ describe( 'firstTimeContributorAccountLink', () => { const octokit = { rest: { repos: { - listCommits: jest.fn( () => + listCommits: vi.fn( () => Promise.resolve( { data: [ { @@ -176,7 +195,7 @@ describe( 'firstTimeContributorAccountLink', () => { ), }, users: { - getByUsername: jest.fn( () => humanUser ), + getByUsername: vi.fn( () => humanUser ), }, }, }; @@ -202,7 +221,7 @@ describe( 'firstTimeContributorAccountLink', () => { const octokit = { rest: { repos: { - listCommits: jest.fn( () => + listCommits: vi.fn( () => Promise.resolve( { data: [ { @@ -213,7 +232,7 @@ describe( 'firstTimeContributorAccountLink', () => { ), }, users: { - getByUsername: jest.fn( () => humanUser ), + getByUsername: vi.fn( () => humanUser ), }, }, }; diff --git a/packages/project-management-automation/lib/tasks/first-time-contributor-label/test/index.js b/packages/project-management-automation/lib/tasks/first-time-contributor-label/test/index.js index b97881803bf67a..1badc5311755f5 100644 --- a/packages/project-management-automation/lib/tasks/first-time-contributor-label/test/index.js +++ b/packages/project-management-automation/lib/tasks/first-time-contributor-label/test/index.js @@ -1,7 +1,21 @@ -import { setOutput } from '@actions/core'; -import firstTimeContributorLabel from '../'; - -jest.mock( '@actions/core', () => ( { setOutput: jest.fn() } ) ); +import { createRequire } from 'node:module'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const actionsCorePath = require.resolve( '@actions/core' ); +const originalActionsCore = require( actionsCorePath ); +const setOutput = vi.fn(); +const taskPath = require.resolve( '../' ); +let firstTimeContributorLabel; +try { + require.cache[ actionsCorePath ].exports = { + ...originalActionsCore, + setOutput, + }; + firstTimeContributorLabel = require( taskPath ); +} finally { + require.cache[ actionsCorePath ].exports = originalActionsCore; + delete require.cache[ taskPath ]; +} describe( 'firstTimeContributorLabel', () => { beforeEach( () => { @@ -38,10 +52,10 @@ describe( 'firstTimeContributorLabel', () => { const octokit = { rest: { repos: { - listCommits: jest.fn(), + listCommits: vi.fn(), }, search: { - commits: jest.fn(), + commits: vi.fn(), }, }, }; @@ -56,7 +70,7 @@ describe( 'firstTimeContributorLabel', () => { const octokit = { rest: { repos: { - listCommits: jest.fn( () => + listCommits: vi.fn( () => Promise.resolve( { data: [ { @@ -67,10 +81,10 @@ describe( 'firstTimeContributorLabel', () => { ), }, search: { - commits: jest.fn(), + commits: vi.fn(), }, issues: { - addLabels: jest.fn(), + addLabels: vi.fn(), }, }, }; @@ -91,12 +105,10 @@ describe( 'firstTimeContributorLabel', () => { const octokit = { rest: { repos: { - listCommits: jest.fn( () => - Promise.resolve( { data: [] } ) - ), + listCommits: vi.fn( () => Promise.resolve( { data: [] } ) ), }, search: { - commits: jest.fn( () => + commits: vi.fn( () => Promise.resolve( { data: { total_count: 1, @@ -110,7 +122,7 @@ describe( 'firstTimeContributorLabel', () => { ), }, issues: { - addLabels: jest.fn(), + addLabels: vi.fn(), }, }, }; @@ -129,12 +141,10 @@ describe( 'firstTimeContributorLabel', () => { const octokit = { rest: { repos: { - listCommits: jest.fn( () => - Promise.resolve( { data: [] } ) - ), + listCommits: vi.fn( () => Promise.resolve( { data: [] } ) ), }, search: { - commits: jest.fn( () => + commits: vi.fn( () => Promise.resolve( { data: { total_count: 0, @@ -144,7 +154,7 @@ describe( 'firstTimeContributorLabel', () => { ), }, issues: { - addLabels: jest.fn(), + addLabels: vi.fn(), }, }, }; diff --git a/packages/project-management-automation/lib/test/get-associated-pull-request.js b/packages/project-management-automation/lib/test/get-associated-pull-request.js index 1dded3a8447329..f9712e465660db 100644 --- a/packages/project-management-automation/lib/test/get-associated-pull-request.js +++ b/packages/project-management-automation/lib/test/get-associated-pull-request.js @@ -1,4 +1,7 @@ -import getAssociatedPullRequest from '../get-associated-pull-request'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); +const getAssociatedPullRequest = require( '../get-associated-pull-request' ); /** @typedef {import('../get-associated-pull-request').WebhookPayloadPushCommit} WebhookPayloadPushCommit */ diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json index d7cc59c9d66a87..82e9ae82636410 100644 --- a/packages/project-management-automation/package.json +++ b/packages/project-management-automation/package.json @@ -37,7 +37,8 @@ "@octokit/webhooks-types": "^5.8.0" }, "devDependencies": { - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/readable-js-assets-webpack-plugin/package.json b/packages/readable-js-assets-webpack-plugin/package.json index 4034b18be429d8..d97f63857caa42 100644 --- a/packages/readable-js-assets-webpack-plugin/package.json +++ b/packages/readable-js-assets-webpack-plugin/package.json @@ -37,6 +37,7 @@ "mkdirp": "^3.0.1", "rimraf": "^5.0.10", "terser-webpack-plugin": "^5.3.10", + "vitest": "^4.1.10", "webpack": "^5.108.1" }, "peerDependencies": { diff --git a/packages/readable-js-assets-webpack-plugin/test/__snapshots__/build.js.snap b/packages/readable-js-assets-webpack-plugin/test/__snapshots__/build.js.snap index 03aff90e5322dd..c817df27f510b7 100644 --- a/packages/readable-js-assets-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/readable-js-assets-webpack-plugin/test/__snapshots__/build.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`ReadableJsAssetsWebpackPlugin should produce the expected output: Asset file index.js should match snapshot 1`] = ` +exports[`ReadableJsAssetsWebpackPlugin > should produce the expected output > Asset file index.js should match snapshot 1`] = ` "/******/ (() => { // webpackBootstrap function notMinified() { // eslint-disable-next-line no-console @@ -12,9 +12,9 @@ notMinified(); ;" `; -exports[`ReadableJsAssetsWebpackPlugin should produce the expected output: Asset file index.min.js should match snapshot 1`] = `"console.log("hello");"`; +exports[`ReadableJsAssetsWebpackPlugin > should produce the expected output > Asset file index.min.js should match snapshot 1`] = `"console.log("hello");"`; -exports[`ReadableJsAssetsWebpackPlugin should produce the expected output: Asset file view.js should match snapshot 1`] = ` +exports[`ReadableJsAssetsWebpackPlugin > should produce the expected output > Asset file view.js should match snapshot 1`] = ` "/******/ (() => { // webpackBootstrap function notMinified() { // eslint-disable-next-line no-console @@ -26,4 +26,4 @@ notMinified(); ;" `; -exports[`ReadableJsAssetsWebpackPlugin should produce the expected output: Asset file view.min.js should match snapshot 1`] = `"console.log("hello");"`; +exports[`ReadableJsAssetsWebpackPlugin > should produce the expected output > Asset file view.min.js should match snapshot 1`] = `"console.log("hello");"`; diff --git a/packages/readable-js-assets-webpack-plugin/test/build.js b/packages/readable-js-assets-webpack-plugin/test/build.js index 7e122621b26505..4013a55fee34f2 100644 --- a/packages/readable-js-assets-webpack-plugin/test/build.js +++ b/packages/readable-js-assets-webpack-plugin/test/build.js @@ -1,21 +1,27 @@ -const fs = require( 'fs' ); -const path = require( 'path' ); -const { globSync } = require( 'glob' ); -const mkdirp = require( 'mkdirp' ).mkdirp.sync; -const rimraf = require( 'rimraf' ).sync; -const webpack = require( 'webpack' ); +import fs from 'node:fs'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { globSync } from 'glob'; +import { mkdirpSync } from 'mkdirp'; +import { rimrafSync } from 'rimraf'; +import { afterEach, beforeEach, describe, expect, test } from 'vitest'; +import webpack from 'webpack'; + +const require = createRequire( import.meta.url ); +const currentDirectory = path.dirname( fileURLToPath( import.meta.url ) ); describe( 'ReadableJsAssetsWebpackPlugin', () => { - const outputDirectory = path.join( __dirname, 'build' ); - const testDirectory = path.join( __dirname, 'fixtures' ); + const outputDirectory = path.join( currentDirectory, 'build' ); + const testDirectory = path.join( currentDirectory, 'fixtures' ); beforeEach( () => { - rimraf( outputDirectory ); - mkdirp( outputDirectory ); + rimrafSync( outputDirectory ); + mkdirpSync( outputDirectory ); } ); // This afterEach is necessary to prevent watched tests from retriggering on every run. - afterEach( () => rimraf( outputDirectory ) ); + afterEach( () => rimrafSync( outputDirectory ) ); test( 'should produce the expected output', async () => { await new Promise( ( resolve ) => { diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index 80d683353abfaa..d1616ea6f36f80 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -51,7 +51,8 @@ "rungen": "^0.3.2" }, "devDependencies": { - "redux": "^5.0.1" + "redux": "^5.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "redux": ">=4" diff --git a/packages/redux-routine/src/test/index.js b/packages/redux-routine/src/test/index.js index bdfc28aa6e75bf..e109022e60d865 100644 --- a/packages/redux-routine/src/test/index.js +++ b/packages/redux-routine/src/test/index.js @@ -1,4 +1,5 @@ import { createStore, applyMiddleware } from 'redux'; +import { describe, expect, it } from 'vitest'; import createMiddleware from '../'; describe( 'createMiddleware', () => { @@ -43,7 +44,7 @@ describe( 'createMiddleware', () => { } ); it( 'should throw if promise rejects', async () => { - expect.hasAssertions(); + let caughtError; const middleware = createMiddleware( { WAIT_FAIL: () => new Promise( ( resolve, reject ) => reject( 'Message' ) ), @@ -53,16 +54,16 @@ describe( 'createMiddleware', () => { try { yield { type: 'WAIT_FAIL' }; } catch ( error ) { - // eslint-disable-next-line jest/no-conditional-expect - expect( error ).toBe( 'Message' ); + caughtError = error; } } await store.dispatch( createAction() ); + expect( caughtError ).toBe( 'Message' ); } ); - it( 'should throw if promise throws', () => { - expect.hasAssertions(); + it( 'should throw if promise throws', async () => { + let caughtError; const middleware = createMiddleware( { WAIT_FAIL: () => new Promise( () => { @@ -74,22 +75,16 @@ describe( 'createMiddleware', () => { try { yield { type: 'WAIT_FAIL' }; } catch ( error ) { - // eslint-disable-next-line jest/no-conditional-expect - expect( error.message ).toBe( 'Message' ); + caughtError = error; } } - return store.dispatch( createAction() ); + await store.dispatch( createAction() ); + expect( caughtError ).toHaveProperty( 'message', 'Message' ); } ); - // Currently this test will not error even under conditions producing it but - // instead will have an uncaught error/warning printed in the cli console: - // - (node:37109) UnhandledPromiseRejectionWarning: TypeError: Cannot read - // property 'type' of null (and others) - // See this github thread for context: - // https://github.com/facebook/jest/issues/3251 - it( 'should handle a null returned from a caught promise error', () => { - expect.hasAssertions(); + it( 'should handle a null returned from a caught promise error', async () => { + let caughtError; const middleware = createMiddleware( { WAIT_FAIL: () => new Promise( () => { @@ -101,12 +96,12 @@ describe( 'createMiddleware', () => { try { yield { type: 'WAIT_FAIL' }; } catch ( error ) { - // eslint-disable-next-line jest/no-conditional-expect - expect( error.message ).toBe( 'Message' ); + caughtError = error; return null; } } - store.dispatch( createAction() ); + await store.dispatch( createAction() ); + expect( caughtError ).toHaveProperty( 'message', 'Message' ); } ); it( 'assigns sync controlled return value into yield assignment', () => { diff --git a/packages/redux-routine/src/test/is-action.js b/packages/redux-routine/src/test/is-action.js index a1a051f381b622..42ae4c776f42bd 100644 --- a/packages/redux-routine/src/test/is-action.js +++ b/packages/redux-routine/src/test/is-action.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isAction, isActionOfType } from '../is-action'; const nonActions = [ null, [], 42, 'foo', () => {}, { foo: 'bar' } ]; diff --git a/packages/redux-routine/src/test/is-generator.js b/packages/redux-routine/src/test/is-generator.js index ec906f0671142b..4bfe0d4c703b37 100644 --- a/packages/redux-routine/src/test/is-generator.js +++ b/packages/redux-routine/src/test/is-generator.js @@ -1,3 +1,4 @@ +import { describe, expect, it, test } from 'vitest'; import isGenerator from '../is-generator'; describe( 'isGenerator', () => { diff --git a/packages/report-flaky-tests/package.json b/packages/report-flaky-tests/package.json index 3adad40abc1911..20144c516b8dca 100644 --- a/packages/report-flaky-tests/package.json +++ b/packages/report-flaky-tests/package.json @@ -40,8 +40,8 @@ "jest-message-util": "^30.5.0" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/report-flaky-tests/src/__tests__/__snapshots__/markdown.test.ts.snap b/packages/report-flaky-tests/src/__tests__/__snapshots__/markdown.test.ts.snap index eb3c6a5932a1ec..ff9026b296d515 100644 --- a/packages/report-flaky-tests/src/__tests__/__snapshots__/markdown.test.ts.snap +++ b/packages/report-flaky-tests/src/__tests__/__snapshots__/markdown.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`formatTestErrorMessage should format test error message for @playwright/test 1`] = ` +exports[`formatTestErrorMessage > should format test error message for @playwright/test 1`] = ` "Error: Snapshot comparison failed: @@ -28,7 +28,7 @@ Received: /home/runner/work/gutenberg/gutenberg/artifacts/test-results/editor-va at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/various/copy-cut-paste.spec.js:253:52" `; -exports[`formatTestErrorMessage should format test error message for jest-circus 1`] = ` +exports[`formatTestErrorMessage > should format test error message for jest-circus 1`] = ` " ● Template Part › Template part block › Template part placeholder › Should insert new template part on creation expect(jest.fn()).not.toHaveErrored(expected) diff --git a/packages/report-flaky-tests/src/__tests__/__snapshots__/run.test.ts.snap b/packages/report-flaky-tests/src/__tests__/__snapshots__/run.test.ts.snap index 02a586483eca7f..4c014a490b7015 100644 --- a/packages/report-flaky-tests/src/__tests__/__snapshots__/run.test.ts.snap +++ b/packages/report-flaky-tests/src/__tests__/__snapshots__/run.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Report flaky tests should write the report 1`] = ` +exports[`Report flaky tests > should write the report 1`] = ` "Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See [the documentation](https://github.com/WordPress/gutenberg/blob/HEAD/docs/contributors/code/testing-overview.md#flaky-tests) for more information.
diff --git a/packages/report-flaky-tests/src/__tests__/markdown.test.ts b/packages/report-flaky-tests/src/__tests__/markdown.test.ts index 78f308f7f3cb04..66915361d72a37 100644 --- a/packages/report-flaky-tests/src/__tests__/markdown.test.ts +++ b/packages/report-flaky-tests/src/__tests__/markdown.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { formatTestErrorMessage, renderReport } from '../markdown'; import type { ReportedFlakyTest } from '../types'; diff --git a/packages/report-flaky-tests/src/__tests__/run.test.ts b/packages/report-flaky-tests/src/__tests__/run.test.ts index 1426c174301bc4..4ebcf717be1e57 100644 --- a/packages/report-flaky-tests/src/__tests__/run.test.ts +++ b/packages/report-flaky-tests/src/__tests__/run.test.ts @@ -1,21 +1,40 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import * as core from '@actions/core'; import { run } from '../run'; -jest.mock( '@actions/core', () => ( { - error: jest.fn(), - info: jest.fn(), - getInput: jest.fn(), +const { mockMkdir, mockReadFile, mockReaddir, mockWriteFile } = vi.hoisted( + () => ( { + mockMkdir: vi.fn(), + mockReadFile: + vi.fn< ( path: string, encoding: string ) => Promise< string > >(), + mockReaddir: vi.fn< ( path: string ) => Promise< string[] > >(), + mockWriteFile: vi.fn(), + } ) +); + +vi.mock( import( '@actions/core' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + error: vi.fn(), + info: vi.fn(), + getInput: vi.fn(), } ) ); -jest.mock( 'fs/promises', () => ( { - readdir: jest.fn(), - readFile: jest.fn(), - writeFile: jest.fn(), - mkdir: jest.fn(), -} ) ); +vi.mock( import( 'fs/promises' ), async ( importOriginal ) => { + const original = await importOriginal(); + + return { + ...original, + mkdir: mockMkdir as unknown as typeof original.mkdir, + readFile: mockReadFile as unknown as typeof original.readFile, + readdir: mockReaddir as unknown as typeof original.readdir, + writeFile: mockWriteFile as unknown as typeof original.writeFile, + }; +} ); + +const mockedGetInput = vi.mocked( core.getInput ); function mockInputs() { - ( core.getInput as jest.Mock ) + mockedGetInput // artifact-path .mockReturnValueOnce( 'flaky-tests' ) // output-path @@ -38,14 +57,13 @@ async function mockFlakyTestsArtifact() { process.cwd() ); - const mockedFs = require( 'fs/promises' ); - mockedFs.readdir.mockImplementationOnce( () => + mockReaddir.mockImplementationOnce( () => Promise.resolve( [ `${ playwrightFlakyTest.title }.json`, `${ jestFlakyTest.title }.json`, ] ) ); - mockedFs.readFile + mockReadFile .mockImplementationOnce( () => Promise.resolve( JSON.stringify( playwrightFlakyTest ) ) ) @@ -55,8 +73,8 @@ async function mockFlakyTestsArtifact() { } describe( 'Report flaky tests', () => { - afterEach( () => { - jest.clearAllMocks(); + beforeEach( () => { + vi.resetAllMocks(); } ); it( 'should write the report', async () => { @@ -64,23 +82,18 @@ describe( 'Report flaky tests', () => { await run(); - const mockedFs = require( 'fs/promises' ); - expect( mockedFs.writeFile ).toHaveBeenCalledTimes( 1 ); - expect( mockedFs.writeFile.mock.calls[ 0 ][ 0 ] ).toBe( - 'pr-meta/body.md' - ); - expect( mockedFs.writeFile.mock.calls[ 0 ][ 1 ] ).toMatchSnapshot(); + expect( mockWriteFile ).toHaveBeenCalledTimes( 1 ); + expect( mockWriteFile.mock.calls[ 0 ][ 0 ] ).toBe( 'pr-meta/body.md' ); + expect( mockWriteFile.mock.calls[ 0 ][ 1 ] ).toMatchSnapshot(); } ); it( 'should write nothing when there are no flaky tests', async () => { mockInputs(); - - const mockedFs = require( 'fs/promises' ); - mockedFs.readdir.mockImplementationOnce( () => Promise.resolve( [] ) ); + mockReaddir.mockImplementationOnce( () => Promise.resolve( [] ) ); await run(); - expect( mockedFs.writeFile ).not.toHaveBeenCalled(); + expect( mockWriteFile ).not.toHaveBeenCalled(); } ); /* @@ -93,14 +106,11 @@ describe( 'Report flaky tests', () => { const missing = Object.assign( new Error( 'ENOENT' ), { code: 'ENOENT', } ); - const mockedFs = require( 'fs/promises' ); - mockedFs.readdir.mockImplementationOnce( () => - Promise.reject( missing ) - ); + mockReaddir.mockImplementationOnce( () => Promise.reject( missing ) ); await run(); - expect( mockedFs.writeFile ).not.toHaveBeenCalled(); + expect( mockWriteFile ).not.toHaveBeenCalled(); } ); /* Anything else would clear a report that nothing had disproved. */ @@ -110,12 +120,9 @@ describe( 'Report flaky tests', () => { const denied = Object.assign( new Error( 'EACCES' ), { code: 'EACCES', } ); - const mockedFs = require( 'fs/promises' ); - mockedFs.readdir.mockImplementationOnce( () => - Promise.reject( denied ) - ); + mockReaddir.mockImplementationOnce( () => Promise.reject( denied ) ); await expect( run() ).rejects.toThrow( 'EACCES' ); - expect( mockedFs.writeFile ).not.toHaveBeenCalled(); + expect( mockWriteFile ).not.toHaveBeenCalled(); } ); } ); diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 752f08f3740569..105ab69b7f9073 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -64,7 +64,8 @@ "colord": "^2.9.3" }, "devDependencies": { - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/rich-text/src/store/test/actions.js b/packages/rich-text/src/store/test/actions.js index b33d6c0c5d7fb2..eb62e4c3bb03d1 100644 --- a/packages/rich-text/src/store/test/actions.js +++ b/packages/rich-text/src/store/test/actions.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { addFormatTypes, removeFormatTypes } from '../actions'; describe( 'actions', () => { diff --git a/packages/rich-text/src/store/test/reducer.js b/packages/rich-text/src/store/test/reducer.js index bac7209e2bbae4..9c1b3b65ed35b0 100644 --- a/packages/rich-text/src/store/test/reducer.js +++ b/packages/rich-text/src/store/test/reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { formatTypes } from '../reducer'; diff --git a/packages/rich-text/src/store/test/selectors.js b/packages/rich-text/src/store/test/selectors.js index 5a844c599b5c6a..8ddd18721740e0 100644 --- a/packages/rich-text/src/store/test/selectors.js +++ b/packages/rich-text/src/store/test/selectors.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { getFormatTypes, diff --git a/packages/rich-text/src/test/apply-format.js b/packages/rich-text/src/test/apply-format.js index 60e50c7b9b8e87..349c65f8dcb65b 100644 --- a/packages/rich-text/src/test/apply-format.js +++ b/packages/rich-text/src/test/apply-format.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { applyFormat } from '../apply-format'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/concat.js b/packages/rich-text/src/test/concat.js index 0432856b314b17..4d4cc71ecdf5dd 100644 --- a/packages/rich-text/src/test/concat.js +++ b/packages/rich-text/src/test/concat.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { concat } from '../concat'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/get-active-format.js b/packages/rich-text/src/test/get-active-format.js index 12324c34bb7164..abf9848ec2eecb 100644 --- a/packages/rich-text/src/test/get-active-format.js +++ b/packages/rich-text/src/test/get-active-format.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getActiveFormat } from '../get-active-format'; describe( 'getActiveFormat', () => { diff --git a/packages/rich-text/src/test/get-active-object.js b/packages/rich-text/src/test/get-active-object.js index 4e75f5459e3124..2ab56f1cc045e5 100644 --- a/packages/rich-text/src/test/get-active-object.js +++ b/packages/rich-text/src/test/get-active-object.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getActiveObject } from '../get-active-object'; import { OBJECT_REPLACEMENT_CHARACTER } from '../special-characters'; diff --git a/packages/rich-text/src/test/get-format-type.js b/packages/rich-text/src/test/get-format-type.js index 5a0e6d15499000..99b4408a615207 100644 --- a/packages/rich-text/src/test/get-format-type.js +++ b/packages/rich-text/src/test/get-format-type.js @@ -1,3 +1,5 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import '../store'; import { getFormatType } from '../get-format-type'; import { unregisterFormatType } from '../unregister-format-type'; import { registerFormatType } from '../register-format-type'; @@ -6,11 +8,6 @@ import { getFormatTypes } from '../get-format-types'; const noop = () => {}; describe( 'getFormatType', () => { - beforeAll( () => { - // Initialize the rich-text store. - require( '../store' ); - } ); - afterEach( () => { getFormatTypes().forEach( ( format ) => { unregisterFormatType( format.name ); diff --git a/packages/rich-text/src/test/get-format-types.js b/packages/rich-text/src/test/get-format-types.js index 1fd571bef2b683..28ef9cff64857a 100644 --- a/packages/rich-text/src/test/get-format-types.js +++ b/packages/rich-text/src/test/get-format-types.js @@ -1,3 +1,5 @@ +import { afterEach, describe, expect, it } from 'vitest'; +import '../store'; import { getFormatTypes } from '../get-format-types'; import { unregisterFormatType } from '../unregister-format-type'; import { registerFormatType } from '../register-format-type'; @@ -5,11 +7,6 @@ import { registerFormatType } from '../register-format-type'; const noop = () => {}; describe( 'getFormatTypes', () => { - beforeAll( () => { - // Initialize the rich-text store. - require( '../store' ); - } ); - afterEach( () => { getFormatTypes().forEach( ( format ) => { unregisterFormatType( format.name ); diff --git a/packages/rich-text/src/test/helpers/index.js b/packages/rich-text/src/test/helpers/index.js index d92d09dc3b974c..2341b347853298 100644 --- a/packages/rich-text/src/test/helpers/index.js +++ b/packages/rich-text/src/test/helpers/index.js @@ -839,7 +839,7 @@ export const specWithRegistration = [ endPath: [ 0, 0, 1 ], value: { formats: [ [ math, mi ] ], - replacements: [], + replacements: [ , ], text: 'x', }, }, diff --git a/packages/rich-text/src/test/insert-object.js b/packages/rich-text/src/test/insert-object.js index 87db25ab2e759c..456bc9090dad6a 100644 --- a/packages/rich-text/src/test/insert-object.js +++ b/packages/rich-text/src/test/insert-object.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { insertObject } from '../insert-object'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/insert.js b/packages/rich-text/src/test/insert.js index 0d9216d9a0186f..3520f0a2feba13 100644 --- a/packages/rich-text/src/test/insert.js +++ b/packages/rich-text/src/test/insert.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { insert } from '../insert'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/is-collapsed.js b/packages/rich-text/src/test/is-collapsed.js index d6943504b53875..d5ab7bb3e56081 100644 --- a/packages/rich-text/src/test/is-collapsed.js +++ b/packages/rich-text/src/test/is-collapsed.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isCollapsed } from '../is-collapsed'; describe( 'isCollapsed', () => { diff --git a/packages/rich-text/src/test/is-empty.js b/packages/rich-text/src/test/is-empty.js index eb066624b32c5b..e68cea96410f5c 100644 --- a/packages/rich-text/src/test/is-empty.js +++ b/packages/rich-text/src/test/is-empty.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isEmpty } from '../is-empty'; describe( 'isEmpty', () => { diff --git a/packages/rich-text/src/test/is-format-equal.js b/packages/rich-text/src/test/is-format-equal.js index 7224f4f2da80c1..48268929a357ad 100644 --- a/packages/rich-text/src/test/is-format-equal.js +++ b/packages/rich-text/src/test/is-format-equal.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isFormatEqual } from '../is-format-equal'; describe( 'isFormatEqual', () => { @@ -102,7 +103,6 @@ describe( 'isFormatEqual', () => { ]; spec.forEach( ( { format1, format2, isEqual, description } ) => { - // eslint-disable-next-line jest/valid-title it( description, () => { expect( isFormatEqual( format1, format2 ) ).toBe( isEqual ); } ); diff --git a/packages/rich-text/src/test/join.js b/packages/rich-text/src/test/join.js index 1847eea50dcb12..9e7166c909dcd6 100644 --- a/packages/rich-text/src/test/join.js +++ b/packages/rich-text/src/test/join.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { join } from '../join'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/normalise-formats.js b/packages/rich-text/src/test/normalise-formats.js index eded66334075f0..30687e67e87383 100644 --- a/packages/rich-text/src/test/normalise-formats.js +++ b/packages/rich-text/src/test/normalise-formats.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { normaliseFormats } from '../normalise-formats'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/remove-format.js b/packages/rich-text/src/test/remove-format.js index bd87b78ee5f9ed..d9dec32b943d2e 100644 --- a/packages/rich-text/src/test/remove-format.js +++ b/packages/rich-text/src/test/remove-format.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { removeFormat } from '../remove-format'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/replace.js b/packages/rich-text/src/test/replace.js index 2795e9508019ea..ba38c7f2e49d06 100644 --- a/packages/rich-text/src/test/replace.js +++ b/packages/rich-text/src/test/replace.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { replace } from '../replace'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/slice.js b/packages/rich-text/src/test/slice.js index 9be2850731e51b..c4ea865d20166f 100644 --- a/packages/rich-text/src/test/slice.js +++ b/packages/rich-text/src/test/slice.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { slice } from '../slice'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/split.js b/packages/rich-text/src/test/split.js index 8c73271fbf34a8..126def047638bd 100644 --- a/packages/rich-text/src/test/split.js +++ b/packages/rich-text/src/test/split.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { split } from '../split'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/rich-text/src/test/toggle-format.js b/packages/rich-text/src/test/toggle-format.js index ccd2dc9016f7fb..d568d4409b57f7 100644 --- a/packages/rich-text/src/test/toggle-format.js +++ b/packages/rich-text/src/test/toggle-format.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import { toggleFormat } from '../toggle-format'; import { getSparseArrayLength } from './helpers'; diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 79352c9d64f057..34d09d6da1ace4 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -88,7 +88,8 @@ "webpack-dev-server": "^5.2.1" }, "devDependencies": { - "rimraf": "^5.0.10" + "rimraf": "^5.0.10", + "vitest": "^4.1.10" }, "peerDependencies": { "@playwright/test": ">=1", diff --git a/packages/scripts/scripts/test/__snapshots__/build-blocks-manifest.js.snap b/packages/scripts/scripts/test/__snapshots__/build-blocks-manifest.js.snap index 0a7e7b0c329d3b..671fdd4c972ce3 100644 --- a/packages/scripts/scripts/test/__snapshots__/build-blocks-manifest.js.snap +++ b/packages/scripts/scripts/test/__snapshots__/build-blocks-manifest.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`build-blocks-manifest script should generate expected blocks manifest 1`] = ` +exports[`build-blocks-manifest script > should generate expected blocks manifest 1`] = ` " { beforeAll( () => { - if ( ! fs.existsSync( outputPath ) ) { - fs.mkdirSync( outputPath, { recursive: true } ); - } - rimraf( outputPath ); + rimrafSync( outputPath ); } ); afterAll( () => { - rimraf( outputPath ); + rimrafSync( outputPath ); } ); it( 'should generate expected blocks manifest', () => { @@ -28,13 +32,15 @@ describe( 'build-blocks-manifest script', () => { // Run the build-blocks-manifest script const scriptPath = path.resolve( - __dirname, + currentDirectory, '..', 'build-blocks-manifest.js' ); - execSync( - `node ${ scriptPath } --input=${ inputDir } --output=${ outputFile }` - ); + execFileSync( process.execPath, [ + scriptPath, + `--input=${ inputDir }`, + `--output=${ outputFile }`, + ] ); const generatedContent = fs.readFileSync( outputFile, 'utf8' ); expect( generatedContent ).toMatchSnapshot(); @@ -45,14 +51,19 @@ describe( 'build-blocks-manifest script', () => { const outputFile = path.join( outputPath, 'empty-blocks-manifest.php' ); const scriptPath = path.resolve( - __dirname, + currentDirectory, '..', 'build-blocks-manifest.js' ); let error; try { - execSync( - `node ${ scriptPath } --input=${ emptyInputDir } --output=${ outputFile }`, + execFileSync( + process.execPath, + [ + scriptPath, + `--input=${ emptyInputDir }`, + `--output=${ outputFile }`, + ], { encoding: 'utf8' } ); } catch ( e ) { @@ -74,14 +85,19 @@ describe( 'build-blocks-manifest script', () => { const outputFile = path.join( outputPath, 'empty-blocks-manifest.php' ); const scriptPath = path.resolve( - __dirname, + currentDirectory, '..', 'build-blocks-manifest.js' ); let error; try { - execSync( - `node ${ scriptPath } --input=${ nonExistentInputDir } --output=${ outputFile }`, + execFileSync( + process.execPath, + [ + scriptPath, + `--input=${ nonExistentInputDir }`, + `--output=${ outputFile }`, + ], { encoding: 'utf8' } ); } catch ( e ) { diff --git a/packages/scripts/utils/test/index.js b/packages/scripts/utils/test/index.js index 2a78dcec8059da..1409751e18c70a 100644 --- a/packages/scripts/utils/test/index.js +++ b/packages/scripts/utils/test/index.js @@ -1,54 +1,44 @@ -import crossSpawn from 'cross-spawn'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + test, + vi, +} from 'vitest'; +const require = createRequire( import.meta.url ); +const currentDirectory = path.dirname( fileURLToPath( import.meta.url ) ); +const crossSpawn = require( 'cross-spawn' ); +const processUtils = require( '../process' ); +const getArgsFromCLIMock = vi.spyOn( processUtils, 'getArgsFromCLI' ); +const exitMock = vi.spyOn( processUtils, 'exit' ); +const packageUtils = require( '../package' ); +const getPackagePathMock = vi.spyOn( packageUtils, 'getPackagePath' ); +const hasPackagePropMock = vi.spyOn( packageUtils, 'hasPackageProp' ); +const fileUtils = require( '../file' ); +const hasProjectFileMock = vi.spyOn( fileUtils, 'hasProjectFile' ); +const fromProjectRootMock = vi.spyOn( fileUtils, 'fromProjectRoot' ); +const fromConfigRootMock = vi.spyOn( fileUtils, 'fromConfigRoot' ); +const crossSpawnMock = vi.spyOn( crossSpawn, 'sync' ); +const { hasArgInCLI, hasProjectFile, getJestOverrideConfigFile, getWebpackArgs, spawnScript, -} from '../'; -import { - getPackagePath as getPackagePathMock, - hasPackageProp as hasPackagePropMock, -} from '../package'; -import { - exit as exitMock, - getArgsFromCLI as getArgsFromCLIMock, -} from '../process'; -import { - hasProjectFile as hasProjectFileMock, - fromProjectRoot as fromProjectRootMock, - fromConfigRoot as fromConfigRootMock, -} from '../file'; - -jest.mock( '../package', () => { - const module = jest.requireActual( '../package' ); - - jest.spyOn( module, 'getPackagePath' ); - jest.spyOn( module, 'hasPackageProp' ); - - return module; -} ); -jest.mock( '../process', () => { - const module = jest.requireActual( '../process' ); - - jest.spyOn( module, 'exit' ); - jest.spyOn( module, 'getArgsFromCLI' ); +} = require( '../' ); - return module; -} ); -jest.mock( '../file', () => { - const module = jest.requireActual( '../file' ); - - jest.spyOn( module, 'hasProjectFile' ); - jest.spyOn( module, 'fromProjectRoot' ); - jest.spyOn( module, 'fromConfigRoot' ); - - return module; +afterAll( () => { + vi.restoreAllMocks(); } ); describe( 'utils', () => { - const crossSpawnMock = jest.spyOn( crossSpawn, 'sync' ); - describe( 'hasArgInCLI', () => { beforeAll( () => { getArgsFromCLIMock.mockReturnValue( [ @@ -81,13 +71,13 @@ describe( 'utils', () => { describe( 'hasProjectFile', () => { test( 'should return false for the current directory and unknown file', () => { - getPackagePathMock.mockReturnValueOnce( __dirname ); + getPackagePathMock.mockReturnValueOnce( currentDirectory ); expect( hasProjectFile( 'unknown-file.name' ) ).toBe( false ); } ); test( 'should return true for the current directory and this file', () => { - getPackagePathMock.mockReturnValueOnce( __dirname ); + getPackagePathMock.mockReturnValueOnce( currentDirectory ); expect( hasProjectFile( 'index.js' ) ).toBe( true ); } ); @@ -98,8 +88,12 @@ describe( 'utils', () => { getArgsFromCLIMock.mockReturnValue( [] ); hasPackagePropMock.mockReturnValue( false ); hasProjectFileMock.mockReturnValue( false ); - fromProjectRootMock.mockImplementation( ( path ) => '/p/' + path ); - fromConfigRootMock.mockImplementation( ( path ) => '/c/' + path ); + fromProjectRootMock.mockImplementation( + ( filePath ) => '/p/' + filePath + ); + fromConfigRootMock.mockImplementation( + ( filePath ) => '/c/' + filePath + ); } ); afterEach( () => { @@ -170,8 +164,12 @@ describe( 'utils', () => { describe( 'getWebpackArgs', () => { beforeEach( () => { hasProjectFileMock.mockReturnValue( false ); - fromProjectRootMock.mockImplementation( ( path ) => '/p/' + path ); - fromConfigRootMock.mockImplementation( ( path ) => '/c/' + path ); + fromProjectRootMock.mockImplementation( + ( filePath ) => '/p/' + filePath + ); + fromConfigRootMock.mockImplementation( + ( filePath ) => '/c/' + filePath + ); } ); afterEach( () => { diff --git a/packages/scripts/utils/test/license.js b/packages/scripts/utils/test/license.js index 7b586274f72085..93024632539ba3 100644 --- a/packages/scripts/utils/test/license.js +++ b/packages/scripts/utils/test/license.js @@ -1,13 +1,22 @@ -import { detectTypeFromLicenseText, checkAllCompatible } from '../license'; -const fs = require( 'fs' ); -const path = require( 'path' ); +import fs from 'node:fs'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; +const { detectTypeFromLicenseText, checkAllCompatible } = createRequire( + import.meta.url +)( '../license' ); + +const currentDirectory = path.dirname( fileURLToPath( import.meta.url ) ); describe( 'detectTypeFromLicenseText', () => { let licenseText; it( "should return 'Apache 2.0' when the license text is the Apache 2.0 license", () => { licenseText = fs - .readFileSync( path.resolve( __dirname, 'licenses/apache2.txt' ) ) + .readFileSync( + path.resolve( currentDirectory, 'licenses/apache2.txt' ) + ) .toString(); expect( detectTypeFromLicenseText( licenseText ) ).toBe( 'Apache-2.0' ); @@ -16,7 +25,7 @@ describe( 'detectTypeFromLicenseText', () => { it( "should return 'BSD' when the license text is the BSD 3-clause license", () => { licenseText = fs .readFileSync( - path.resolve( __dirname, 'licenses/bsd3clause.txt' ) + path.resolve( currentDirectory, 'licenses/bsd3clause.txt' ) ) .toString(); @@ -25,7 +34,9 @@ describe( 'detectTypeFromLicenseText', () => { it( "should return 'BSD-3-Clause-W3C' when the license text is the W3C variation of the BSD 3-clause license", () => { licenseText = fs - .readFileSync( path.resolve( __dirname, 'licenses/w3cbsd.txt' ) ) + .readFileSync( + path.resolve( currentDirectory, 'licenses/w3cbsd.txt' ) + ) .toString(); expect( detectTypeFromLicenseText( licenseText ) ).toBe( @@ -35,7 +46,9 @@ describe( 'detectTypeFromLicenseText', () => { it( "should return 'MIT' when the license text is the MIT license", () => { licenseText = fs - .readFileSync( path.resolve( __dirname, 'licenses/mit.txt' ) ) + .readFileSync( + path.resolve( currentDirectory, 'licenses/mit.txt' ) + ) .toString(); expect( detectTypeFromLicenseText( licenseText ) ).toBe( 'MIT' ); @@ -44,7 +57,7 @@ describe( 'detectTypeFromLicenseText', () => { it( "should return 'Apache2 AND MIT' when the license text is Apache2 followed by MIT license", () => { licenseText = fs .readFileSync( - path.resolve( __dirname, 'licenses/apache2-mit.txt' ) + path.resolve( currentDirectory, 'licenses/apache2-mit.txt' ) ) .toString(); diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index 0d9fa8df47a962..379e5ac490231d 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -54,7 +54,7 @@ "@wordpress/url": "file:../url" }, "devDependencies": { - "@types/jest": "^30.0.0" + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/server-side-render/src/test/index.ts b/packages/server-side-render/src/test/index.ts index fa06d4d78c34da..a5ad15b07f5203 100644 --- a/packages/server-side-render/src/test/index.ts +++ b/packages/server-side-render/src/test/index.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { rendererPath, removeBlockSupportAttributes } from '../hook'; describe( 'rendererPath', () => { diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index 150e04a8ec77c2..7d7a8c5e8655b3 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -46,7 +46,8 @@ "memize": "^2.1.1" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/shortcode/src/test/index.ts b/packages/shortcode/src/test/index.ts index 25151791b3a57d..9f368f6addd20f 100644 --- a/packages/shortcode/src/test/index.ts +++ b/packages/shortcode/src/test/index.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { next, replace, attrs, string } from '../'; describe( 'shortcode', () => { diff --git a/packages/style-engine/package.json b/packages/style-engine/package.json index 1eb17bce78181a..299e8de6491032 100644 --- a/packages/style-engine/package.json +++ b/packages/style-engine/package.json @@ -66,6 +66,9 @@ "dependencies": { "change-case": "^4.1.2" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "peerDependencies": { "@types/react": "^18 || ^19" }, diff --git a/packages/style-engine/src/styles/test/utils.js b/packages/style-engine/src/styles/test/utils.js index 5655e3d79034b6..a1e1a45769145d 100644 --- a/packages/style-engine/src/styles/test/utils.js +++ b/packages/style-engine/src/styles/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { camelCaseJoin, getCSSValueFromRawStyle, upperFirst } from '../utils'; describe( 'utils', () => { diff --git a/packages/style-engine/src/test/index.js b/packages/style-engine/src/test/index.js index 78f67581a0535b..c2a37bb0ed9271 100644 --- a/packages/style-engine/src/test/index.js +++ b/packages/style-engine/src/test/index.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getCSSRules, compileCSS } from '../index'; describe( 'generate', () => { diff --git a/packages/style-runtime/package.json b/packages/style-runtime/package.json index 5edf8a5d0f213c..42c222b83d6416 100644 --- a/packages/style-runtime/package.json +++ b/packages/style-runtime/package.json @@ -43,7 +43,8 @@ "sideEffects": false, "devDependencies": { "@testing-library/jest-dom": "^6.9.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json index 4da8bf673a90a5..9151e26819f724 100644 --- a/packages/stylelint-config/package.json +++ b/packages/stylelint-config/package.json @@ -50,7 +50,8 @@ "devDependencies": { "@wordpress/npm-package-json-lint-config": "file:../npm-package-json-lint-config", "stylelint": "^16.26.1", - "stylelint-scss": "^6.14.0" + "stylelint-scss": "^6.14.0", + "vitest": "^4.1.10" }, "peerDependencies": { "stylelint": "^16.8.2", diff --git a/packages/stylelint-config/test/__snapshots__/commenting.js.snap b/packages/stylelint-config/test/__snapshots__/commenting.js.snap index 7f6cff3a1c7d84..f8e2000247335c 100644 --- a/packages/stylelint-config/test/__snapshots__/commenting.js.snap +++ b/packages/stylelint-config/test/__snapshots__/commenting.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid commenting css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid commenting css > snapshot matches warnings 1`] = ` [ { "column": 131, diff --git a/packages/stylelint-config/test/__snapshots__/functions.js.snap b/packages/stylelint-config/test/__snapshots__/functions.js.snap index 5e861ea3024797..ee9a0d36213d30 100644 --- a/packages/stylelint-config/test/__snapshots__/functions.js.snap +++ b/packages/stylelint-config/test/__snapshots__/functions.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid functions css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid functions css > snapshot matches warnings 1`] = ` [ { "column": 9, diff --git a/packages/stylelint-config/test/__snapshots__/index.js.snap b/packages/stylelint-config/test/__snapshots__/index.js.snap index f870c6b67b3940..6c28d5494c4b15 100644 --- a/packages/stylelint-config/test/__snapshots__/index.js.snap +++ b/packages/stylelint-config/test/__snapshots__/index.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid css > snapshot matches warnings 1`] = ` [ { "column": 7, diff --git a/packages/stylelint-config/test/__snapshots__/media-queries.js.snap b/packages/stylelint-config/test/__snapshots__/media-queries.js.snap index 5b9a6a7104d680..1f54c5fcad9535 100644 --- a/packages/stylelint-config/test/__snapshots__/media-queries.js.snap +++ b/packages/stylelint-config/test/__snapshots__/media-queries.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid media queries css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid media queries css > snapshot matches warnings 1`] = ` [ { "column": 26, diff --git a/packages/stylelint-config/test/__snapshots__/properties.js.snap b/packages/stylelint-config/test/__snapshots__/properties.js.snap index 6789d5bc488997..e2c75feea0a9bb 100644 --- a/packages/stylelint-config/test/__snapshots__/properties.js.snap +++ b/packages/stylelint-config/test/__snapshots__/properties.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid properties css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid properties css > snapshot matches warnings 1`] = ` [ { "column": 13, diff --git a/packages/stylelint-config/test/__snapshots__/scss.js.snap b/packages/stylelint-config/test/__snapshots__/scss.js.snap index e0c6b5a52a68ea..83298119a85a19 100644 --- a/packages/stylelint-config/test/__snapshots__/scss.js.snap +++ b/packages/stylelint-config/test/__snapshots__/scss.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid scss snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid scss > snapshot matches warnings 1`] = ` [ { "column": 5, diff --git a/packages/stylelint-config/test/__snapshots__/selectors-scss.js.snap b/packages/stylelint-config/test/__snapshots__/selectors-scss.js.snap index 7f80aa8aa4e748..236308c8f15772 100644 --- a/packages/stylelint-config/test/__snapshots__/selectors-scss.js.snap +++ b/packages/stylelint-config/test/__snapshots__/selectors-scss.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid selectors scss snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid selectors scss > snapshot matches warnings 1`] = ` [ { "column": 2, diff --git a/packages/stylelint-config/test/__snapshots__/selectors.js.snap b/packages/stylelint-config/test/__snapshots__/selectors.js.snap index 0f3fea5d90d1c7..b4a37fa2748d81 100644 --- a/packages/stylelint-config/test/__snapshots__/selectors.js.snap +++ b/packages/stylelint-config/test/__snapshots__/selectors.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid selectors css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid selectors css > snapshot matches warnings 1`] = ` [ { "column": 12, diff --git a/packages/stylelint-config/test/__snapshots__/structure.js.snap b/packages/stylelint-config/test/__snapshots__/structure.js.snap index 974f4e5f0b7459..272ff9b1542b12 100644 --- a/packages/stylelint-config/test/__snapshots__/structure.js.snap +++ b/packages/stylelint-config/test/__snapshots__/structure.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid structure css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid structure css > snapshot matches warnings 1`] = ` [ { "column": 45, diff --git a/packages/stylelint-config/test/__snapshots__/values.js.snap b/packages/stylelint-config/test/__snapshots__/values.js.snap index 9e4df889d99620..758c978bbaa875 100644 --- a/packages/stylelint-config/test/__snapshots__/values.js.snap +++ b/packages/stylelint-config/test/__snapshots__/values.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid values css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid values css > snapshot matches warnings 1`] = ` [ { "column": 16, diff --git a/packages/stylelint-config/test/commenting.js b/packages/stylelint-config/test/commenting.js index 333cccb6fe7f3f..90c289d7e3e510 100644 --- a/packages/stylelint-config/test/commenting.js +++ b/packages/stylelint-config/test/commenting.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid commenting css', () => { let result; diff --git a/packages/stylelint-config/test/functions.js b/packages/stylelint-config/test/functions.js index 9d243c1e7d381f..8478e2b2c505dd 100644 --- a/packages/stylelint-config/test/functions.js +++ b/packages/stylelint-config/test/functions.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid functions css', () => { let result; diff --git a/packages/stylelint-config/test/index.js b/packages/stylelint-config/test/index.js index 243d4b231c7372..41a7e53f776512 100644 --- a/packages/stylelint-config/test/index.js +++ b/packages/stylelint-config/test/index.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid css', () => { let result; diff --git a/packages/stylelint-config/test/media-queries.js b/packages/stylelint-config/test/media-queries.js index 14bd116e1e5bb8..ae1a7873a4f5e3 100644 --- a/packages/stylelint-config/test/media-queries.js +++ b/packages/stylelint-config/test/media-queries.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid media queries css', () => { let result; diff --git a/packages/stylelint-config/test/properties.js b/packages/stylelint-config/test/properties.js index ebed7d211470cf..6547f416e6dc4d 100644 --- a/packages/stylelint-config/test/properties.js +++ b/packages/stylelint-config/test/properties.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid properties css', () => { let result; diff --git a/packages/stylelint-config/test/scss.js b/packages/stylelint-config/test/scss.js index e89a48478601bf..1a4fc09447eae2 100644 --- a/packages/stylelint-config/test/scss.js +++ b/packages/stylelint-config/test/scss.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid scss', () => { let result; diff --git a/packages/stylelint-config/test/selectors-scss.js b/packages/stylelint-config/test/selectors-scss.js index e2cc250dd89e9c..9b6bec4c03ad62 100644 --- a/packages/stylelint-config/test/selectors-scss.js +++ b/packages/stylelint-config/test/selectors-scss.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid selectors scss', () => { let result; diff --git a/packages/stylelint-config/test/selectors.js b/packages/stylelint-config/test/selectors.js index 9b461c5c806acb..f2d09e1b2fe463 100644 --- a/packages/stylelint-config/test/selectors.js +++ b/packages/stylelint-config/test/selectors.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid selectors css', () => { let result; diff --git a/packages/stylelint-config/test/structure.js b/packages/stylelint-config/test/structure.js index 5ea0adf06a821e..8da82c8159c17c 100644 --- a/packages/stylelint-config/test/structure.js +++ b/packages/stylelint-config/test/structure.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid structure css', () => { let result; diff --git a/packages/stylelint-config/test/themes.js b/packages/stylelint-config/test/themes.js index f1e4a0d5f397ec..1e4c5ee7e1c8c9 100644 --- a/packages/stylelint-config/test/themes.js +++ b/packages/stylelint-config/test/themes.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid css', () => { let result; diff --git a/packages/stylelint-config/test/utils/index.js b/packages/stylelint-config/test/utils/index.js index 7bcd1e8cf9d481..31533ed66e4d88 100644 --- a/packages/stylelint-config/test/utils/index.js +++ b/packages/stylelint-config/test/utils/index.js @@ -1,43 +1,45 @@ -const util = require( 'node:util' ), - path = require( 'node:path' ), - childProcess = require( 'node:child_process' ); +import { exec } from 'node:child_process'; +import path from 'node:path'; +import { promisify } from 'node:util'; +import { fileURLToPath } from 'node:url'; -const execute = util.promisify( childProcess.exec ); +const execute = promisify( exec ); /* * Resolve Stylelint's binary through Node's module resolution rather than a * fixed `node_modules/.bin` path, so the test works regardless of whether the * dependency is hoisted to the repo root or isolated in this package. */ -const stylelintBin = path.join( - path.dirname( require.resolve( 'stylelint/package.json' ) ), - 'bin/stylelint.mjs' +const stylelintBin = fileURLToPath( + new URL( + './bin/stylelint.mjs', + import.meta.resolve( 'stylelint/package.json' ) + ) ); +const testDirectory = fileURLToPath( new URL( '../', import.meta.url ) ); const generateStylelintCommand = ( filename ) => 'node ' + stylelintBin + ' ' + - path.resolve( __dirname, '../', filename ) + + path.resolve( testDirectory, filename ) + ' -c' + - path.resolve( __dirname, '../', './.stylelintrc.tests.json' ) + + path.resolve( testDirectory, './.stylelintrc.tests.json' ) + ' --formatter json' + ' --ignore-path ' + - path.resolve( __dirname, '../', './.stylelintignore' ); + path.resolve( testDirectory, './.stylelintignore' ); -module.exports = { - getStylelintResult: ( filename ) => - execute( generateStylelintCommand( filename ) ) - .then( ( { stderr } ) => { - return { - errored: false, - results: JSON.parse( stderr ), - }; - } ) - .catch( ( { stderr } ) => { - return { - errored: true, - results: JSON.parse( stderr ), - }; - } ), -}; +export const getStylelintResult = ( filename ) => + execute( generateStylelintCommand( filename ) ) + .then( ( { stderr } ) => { + return { + errored: false, + results: JSON.parse( stderr ), + }; + } ) + .catch( ( { stderr } ) => { + return { + errored: true, + results: JSON.parse( stderr ), + }; + } ); diff --git a/packages/stylelint-config/test/values.js b/packages/stylelint-config/test/values.js index 1e802048834a71..29f958d40c04c7 100644 --- a/packages/stylelint-config/test/values.js +++ b/packages/stylelint-config/test/values.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid values css', () => { let result; diff --git a/packages/stylelint-config/test/vendor-prefixes.js b/packages/stylelint-config/test/vendor-prefixes.js index d10440933fbb23..d4f2e6650e3384 100644 --- a/packages/stylelint-config/test/vendor-prefixes.js +++ b/packages/stylelint-config/test/vendor-prefixes.js @@ -1,5 +1,5 @@ -const utils = require( './utils' ); -const getStylelintResult = utils.getStylelintResult; +import { beforeEach, describe, expect, it } from 'vitest'; +import { getStylelintResult } from './utils'; describe( 'flags no warnings with valid vendor prefixes css', () => { let result; diff --git a/packages/sync/package.json b/packages/sync/package.json index 93566ed682b13c..0a9f62a2584e34 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -54,9 +54,8 @@ "yjs": "^13.6.29" }, "devDependencies": { - "@jest/globals": "^30.5.0", - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/sync/src/providers/http-polling/test/config.test.ts b/packages/sync/src/providers/http-polling/test/config.test.ts index 7467378676ad87..7fe7fe5d6ad931 100644 --- a/packages/sync/src/providers/http-polling/test/config.test.ts +++ b/packages/sync/src/providers/http-polling/test/config.test.ts @@ -1,13 +1,14 @@ -import { describe, expect, it, jest } from '@jest/globals'; +import { describe, expect, it, vi } from 'vitest'; type SyncConfig = typeof import('../config'); function loadConfigWithFilteredIntervals( filteredIntervals: Record< string, unknown > -): SyncConfig { - jest.resetModules(); - jest.doMock( '@wordpress/hooks', () => ( { - applyFilters: jest.fn( ( hookName: string, defaultValue: unknown ) => { +): Promise< SyncConfig > { + vi.resetModules(); + vi.doMock( import( '@wordpress/hooks' ), async ( importOriginal ) => ( { + ...( await importOriginal() ), + applyFilters: vi.fn( ( hookName: string, defaultValue: unknown ) => { if ( Object.prototype.hasOwnProperty.call( filteredIntervals, @@ -21,19 +22,19 @@ function loadConfigWithFilteredIntervals( } ), } ) ); - return require( '../config' ) as SyncConfig; + return import( '../config' ); } describe( 'http-polling config', () => { - it( 'uses default polling intervals when filters do not change them', () => { - const config = loadConfigWithFilteredIntervals( {} ); + it( 'uses default polling intervals when filters do not change them', async () => { + const config = await loadConfigWithFilteredIntervals( {} ); expect( config.POLLING_INTERVAL_IN_MS ).toBe( 4000 ); expect( config.POLLING_INTERVAL_WITH_COLLABORATORS_IN_MS ).toBe( 1000 ); } ); - it( 'allows filters to make active polling intervals faster', () => { - const config = loadConfigWithFilteredIntervals( { + it( 'allows filters to make active polling intervals faster', async () => { + const config = await loadConfigWithFilteredIntervals( { 'sync.pollingManager.pollingInterval': 1000, 'sync.pollingManager.pollingIntervalWithCollaborators': 250, } ); @@ -42,8 +43,8 @@ describe( 'http-polling config', () => { expect( config.POLLING_INTERVAL_WITH_COLLABORATORS_IN_MS ).toBe( 250 ); } ); - it( 'caps filters that would make active polling intervals slower', () => { - const config = loadConfigWithFilteredIntervals( { + it( 'caps filters that would make active polling intervals slower', async () => { + const config = await loadConfigWithFilteredIntervals( { 'sync.pollingManager.pollingInterval': 10000, 'sync.pollingManager.pollingIntervalWithCollaborators': 2500, } ); @@ -59,8 +60,8 @@ describe( 'http-polling config', () => { [ 'non-number', '100' ], ] )( 'uses default intervals when filters return %s values', - ( _label, filteredValue ) => { - const config = loadConfigWithFilteredIntervals( { + async ( _label, filteredValue ) => { + const config = await loadConfigWithFilteredIntervals( { 'sync.pollingManager.pollingInterval': filteredValue, 'sync.pollingManager.pollingIntervalWithCollaborators': filteredValue, diff --git a/packages/sync/src/providers/http-polling/test/utils.test.ts b/packages/sync/src/providers/http-polling/test/utils.test.ts index a4c021e9031fe1..e87f59d37401b9 100644 --- a/packages/sync/src/providers/http-polling/test/utils.test.ts +++ b/packages/sync/src/providers/http-polling/test/utils.test.ts @@ -1,9 +1,4 @@ -import { describe, expect, it, jest, beforeEach } from '@jest/globals'; -// Mock @wordpress/api-fetch -jest.mock( '@wordpress/api-fetch', () => ( { - __esModule: true, - default: jest.fn(), -} ) ); +import { beforeEach, describe, expect, it, vi } from 'vitest'; import apiFetch from '@wordpress/api-fetch'; import { SyncUpdateType } from '../types'; import { MAX_UPDATE_SIZE_IN_BYTES } from '../config'; @@ -16,8 +11,17 @@ import { rotateWindow, uint8ArrayToBase64, } from '../utils'; +// Mock @wordpress/api-fetch +vi.mock( import( '@wordpress/api-fetch' ), async ( importOriginal ) => { + const original = await importOriginal(); + + return { + ...original, + default: vi.fn(), + } as unknown as typeof original; +} ); -const mockApiFetch = jest.mocked( apiFetch ); +const mockApiFetch = vi.mocked( apiFetch ); const SERVER_MAX_UPDATE_DATA_SIZE_IN_BYTES = 1024 * 1024; describe( 'http-polling utils', () => { @@ -569,7 +573,7 @@ describe( 'http-polling utils', () => { describe( 'postSyncUpdate', () => { beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'sends a POST request to the sync endpoint', async () => { diff --git a/packages/sync/src/quill-delta/test/Delta.ts b/packages/sync/src/quill-delta/test/Delta.ts index c7b1c817b5fb39..89dc7db7ec0728 100644 --- a/packages/sync/src/quill-delta/test/Delta.ts +++ b/packages/sync/src/quill-delta/test/Delta.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from '@jest/globals'; +import { describe, expect, it } from 'vitest'; import Delta from '../Delta'; describe( 'Delta.diffWithCursor', () => { diff --git a/packages/sync/src/test/crdt-snapshot.ts b/packages/sync/src/test/crdt-snapshot.ts index f08f0b7e6b2081..d44863956923a9 100644 --- a/packages/sync/src/test/crdt-snapshot.ts +++ b/packages/sync/src/test/crdt-snapshot.ts @@ -1,6 +1,6 @@ import * as Y from 'yjs'; import * as buffer from 'lib0/buffer'; -import { describe, expect, it } from '@jest/globals'; +import { describe, expect, it } from 'vitest'; import { docContainsSnapshot, encodeDocSnapshot } from '../crdt-snapshot'; import { createYjsDoc } from '../utils'; diff --git a/packages/sync/src/test/performance.test.ts b/packages/sync/src/test/performance.test.ts index aecf8febd90edf..8cf706d4a0a88f 100644 --- a/packages/sync/src/test/performance.test.ts +++ b/packages/sync/src/test/performance.test.ts @@ -1,19 +1,20 @@ import { + afterEach, + beforeEach, describe, expect, it, - jest, - beforeEach, - afterEach, -} from '@jest/globals'; + vi, + type MockInstance, +} from 'vitest'; import { logPerformanceTiming, passThru } from '../performance'; describe( 'performance utilities', () => { describe( 'logPerformanceTiming', () => { - let consoleSpy: jest.SpiedFunction< typeof console.log >; + let consoleSpy: MockInstance< typeof console.log >; beforeEach( () => { - consoleSpy = jest + consoleSpy = vi .spyOn( console, 'log' ) .mockImplementation( () => {} ); } ); @@ -46,7 +47,7 @@ describe( 'performance utilities', () => { } ); it( 'passes all arguments to the wrapped function', () => { - const fn = jest.fn( ( a: number, b: string, c: boolean ) => { + const fn = vi.fn( ( a: number, b: string, c: boolean ) => { return `${ a }-${ b }-${ c }`; } ); @@ -105,7 +106,7 @@ describe( 'performance utilities', () => { describe( 'passThru', () => { it( 'returns a function that calls the original function', () => { - const fn = jest.fn( () => 'result' ); + const fn = vi.fn( () => 'result' ); const wrapped = passThru( fn ); const result = wrapped(); @@ -115,7 +116,7 @@ describe( 'performance utilities', () => { } ); it( 'passes all arguments to the original function', () => { - const fn = jest.fn( ( a: number, b: string ) => `${ a }-${ b }` ); + const fn = vi.fn( ( a: number, b: string ) => `${ a }-${ b }` ); const wrapped = passThru( fn ); const result = wrapped( 42, 'test' ); diff --git a/packages/sync/src/test/undo-manager.test.ts b/packages/sync/src/test/undo-manager.test.ts index d899adcf03891f..752074de25a565 100644 --- a/packages/sync/src/test/undo-manager.test.ts +++ b/packages/sync/src/test/undo-manager.test.ts @@ -1,5 +1,5 @@ import * as Y from 'yjs'; -import { afterEach, describe, expect, it, jest } from '@jest/globals'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { LOCAL_EDITOR_ORIGIN } from '../config'; import { createUndoManager } from '../undo-manager'; @@ -17,9 +17,9 @@ describe( 'SyncUndoManager', () => { doc, map: doc.getMap( 'record' ), handlers: { - addUndoMeta: jest.fn(), - onUndoStackChange: jest.fn(), - restoreUndoMeta: jest.fn(), + addUndoMeta: vi.fn(), + onUndoStackChange: vi.fn(), + restoreUndoMeta: vi.fn(), }, }; } diff --git a/packages/sync/src/test/utils.ts b/packages/sync/src/test/utils.ts index 7dc10c29cf7d2e..35939cbef0cc79 100644 --- a/packages/sync/src/test/utils.ts +++ b/packages/sync/src/test/utils.ts @@ -1,6 +1,6 @@ import * as Y from 'yjs'; import * as buffer from 'lib0/buffer'; -import { describe, expect, it, beforeEach } from '@jest/globals'; +import { beforeEach, describe, expect, it } from 'vitest'; import { createYjsDoc, initializeYjsDoc, diff --git a/packages/theme/bin/terrazzo-plugin-ds-token-fallbacks/test/index.test.ts b/packages/theme/bin/terrazzo-plugin-ds-token-fallbacks/test/index.test.ts index 1ab7dee9a6f12c..9dbcbe62593251 100644 --- a/packages/theme/bin/terrazzo-plugin-ds-token-fallbacks/test/index.test.ts +++ b/packages/theme/bin/terrazzo-plugin-ds-token-fallbacks/test/index.test.ts @@ -1,7 +1,6 @@ +import { describe, expect, it } from 'vitest'; import { computeBrandFallback, formatDesignTokenFallbacksScss } from '../index'; -jest.mock( '@terrazzo/plugin-css', () => ( { FORMAT_ID: 'css/value' } ) ); - describe( 'computeBrandFallback', () => { it( 'throws on colors with alpha (8-digit hex)', () => { expect( () => computeBrandFallback( '#3858e980' ) ).toThrow( diff --git a/packages/theme/bin/terrazzo-plugin-mode-overrides/test/index.test.ts b/packages/theme/bin/terrazzo-plugin-mode-overrides/test/index.test.ts index 5d72a08afe4732..9e987eb4c5a7e6 100644 --- a/packages/theme/bin/terrazzo-plugin-mode-overrides/test/index.test.ts +++ b/packages/theme/bin/terrazzo-plugin-mode-overrides/test/index.test.ts @@ -1,4 +1,5 @@ import { fileURLToPath } from 'node:url'; +import { describe, expect, it, vi } from 'vitest'; import pluginModeOverrides from '../index'; const cwd = new URL( 'file:///virtual/' ); @@ -26,7 +27,7 @@ async function buildModeOverrides( { { [ modifierName ]: '.' }, { [ modifierName ]: 'compact' }, ], - apply: jest.fn( + apply: vi.fn( ( input: Record< string, string >, options?: { resolveAliases?: boolean } diff --git a/packages/theme/package.json b/packages/theme/package.json index 028b6acce67e6b..3d5f33a122dcf3 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -99,7 +99,6 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "@wordpress/validation-tools": "file:../../tools/validation", "esbuild": "^0.27.2", @@ -107,7 +106,8 @@ "lightningcss": "^1.33.0", "prettier": "npm:wp-prettier@^3.0.3", "storybook": "^10.5.3", - "stylelint": "^16.26.1" + "stylelint": "^16.26.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/theme/postcss-plugins/test/add-fallback-to-var.test.ts b/packages/theme/postcss-plugins/test/add-fallback-to-var.test.ts index 5f104b9f3a3610..fa4127b8e8ab1f 100644 --- a/packages/theme/postcss-plugins/test/add-fallback-to-var.test.ts +++ b/packages/theme/postcss-plugins/test/add-fallback-to-var.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { addFallbackToVar } from '../add-fallback-to-var.mjs'; const mockFallbacks: Record< string, string > = { diff --git a/packages/theme/postcss-plugins/test/ds-token-fallbacks.test.ts b/packages/theme/postcss-plugins/test/ds-token-fallbacks.test.ts index 182ef3b33bee7b..85667b4b05a972 100644 --- a/packages/theme/postcss-plugins/test/ds-token-fallbacks.test.ts +++ b/packages/theme/postcss-plugins/test/ds-token-fallbacks.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { addFallbackToVar } from '../ds-token-fallbacks.mjs'; describe( 'addFallbackToVar', () => { diff --git a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap index a468d9f614641b..958cbe88ca6dab 100644 --- a/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap +++ b/packages/theme/src/color-ramps/test/__snapshots__/index.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`buildRamps accent ramp snapshots 1`] = ` +exports[`buildRamps > accent ramp snapshots 1`] = ` [ [ { @@ -4185,7 +4185,7 @@ exports[`buildRamps accent ramp snapshots 1`] = ` ] `; -exports[`buildRamps background ramp snapshots 1`] = ` +exports[`buildRamps > background ramp snapshots 1`] = ` [ { "input": { diff --git a/packages/theme/src/color-ramps/test/index.test.ts b/packages/theme/src/color-ramps/test/index.test.ts index f45335a4ad7c1f..24334173dbe9dc 100644 --- a/packages/theme/src/color-ramps/test/index.test.ts +++ b/packages/theme/src/color-ramps/test/index.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { serialize, to, HSL, sRGB } from 'colorjs.io/fn'; import { buildAccentRamp, buildBgRamp, checkAccessibleCombinations } from '..'; import { buildRamp } from '../lib'; diff --git a/packages/theme/src/color-ramps/test/seed-input.test.ts b/packages/theme/src/color-ramps/test/seed-input.test.ts index 92beb5f32f2cae..9c3e04ce867d28 100644 --- a/packages/theme/src/color-ramps/test/seed-input.test.ts +++ b/packages/theme/src/color-ramps/test/seed-input.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { ColorSpace, OKLCH } from 'colorjs.io/fn'; import { buildBgRamp, buildAccentRamp } from '../index'; import { buildRamp } from '../lib'; diff --git a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap index 010f37a0b5f634..e618d7d5c5ce37 100644 --- a/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap +++ b/packages/theme/src/test/__snapshots__/build-plugin-parity.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`design token fallback build plugin parity keeps esbuild and Vite source-text transforms aligned 1`] = ` +exports[`design token fallback build plugin parity > keeps esbuild and Vite source-text transforms aligned 1`] = ` "// Source text is transformed consistently: var(--wpds-border-radius-sm, 2px). export const templateStyles = \`color: var(--wpds-color-foreground-content-neutral, #1e1e1e);\`; export const doubleQuotedStyles = @@ -10,7 +10,7 @@ export const manualFallback = 'radius: var(--wpds-border-radius-sm, 99px);'; " `; -exports[`design token fallback build plugin parity transforms supported CSS in styles.css 1`] = ` +exports[`design token fallback build plugin parity > transforms supported CSS in styles.css 1`] = ` "/* Token references in CSS comments are not transformed: var(--wpds-border-radius-sm). */ .fixture { color: var(--wpds-color-foreground-content-neutral, #1e1e1e); @@ -19,14 +19,14 @@ exports[`design token fallback build plugin parity transforms supported CSS in s " `; -exports[`design token fallback build plugin parity transforms supported CSS in styles.module.css 1`] = ` +exports[`design token fallback build plugin parity > transforms supported CSS in styles.module.css 1`] = ` ".fixture { gap: var(--wpds-dimension-gap-sm, 8px); } " `; -exports[`design token fallback build plugin parity transforms supported CSS with Lightning CSS in styles.css 1`] = ` +exports[`design token fallback build plugin parity > transforms supported CSS with Lightning CSS in styles.css 1`] = ` ".fixture { color: var(--wpds-color-foreground-content-neutral, #1e1e1e); content: "var(--wpds-border-radius-sm)"; @@ -34,7 +34,7 @@ exports[`design token fallback build plugin parity transforms supported CSS with " `; -exports[`design token fallback build plugin parity transforms supported CSS with Lightning CSS in styles.module.css 1`] = ` +exports[`design token fallback build plugin parity > transforms supported CSS with Lightning CSS in styles.module.css 1`] = ` ".fixture { gap: var(--wpds-dimension-gap-sm, 8px); } diff --git a/packages/theme/src/test/build-plugin-parity.test.ts b/packages/theme/src/test/build-plugin-parity.test.ts index ed7d6621bb111c..42fead4dc2e53f 100644 --- a/packages/theme/src/test/build-plugin-parity.test.ts +++ b/packages/theme/src/test/build-plugin-parity.test.ts @@ -1,5 +1,6 @@ import { readFile } from 'fs/promises'; import { join } from 'path'; +import { describe, expect, it } from 'vitest'; import type { OnLoadArgs, OnLoadOptions, diff --git a/packages/theme/src/test/private-apis.test.ts b/packages/theme/src/test/private-apis.test.ts index 7d943fc1de6cf5..78b3546b5026e0 100644 --- a/packages/theme/src/test/private-apis.test.ts +++ b/packages/theme/src/test/private-apis.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { unlock } from '../lock-unlock'; import { privateApis } from '../private-apis'; import { ThemeProvider } from '../theme-provider'; @@ -5,9 +6,7 @@ import { useThemeProviderStyles } from '../use-theme-provider-styles'; describe( 'privateApis', () => { it( 'warns when accessing useThemeProviderStyles through private APIs', () => { - const warn = jest - .spyOn( console, 'warn' ) - .mockImplementation( () => {} ); + const warn = vi.spyOn( console, 'warn' ).mockImplementation( () => {} ); const unlockedPrivateApis = unlock< { useThemeProviderStyles: typeof useThemeProviderStyles; @@ -25,9 +24,7 @@ describe( 'privateApis', () => { } ); it( 'warns when accessing ThemeProvider through private APIs', () => { - const warn = jest - .spyOn( console, 'warn' ) - .mockImplementation( () => {} ); + const warn = vi.spyOn( console, 'warn' ).mockImplementation( () => {} ); const unlockedPrivateApis = unlock< { ThemeProvider: typeof ThemeProvider; diff --git a/packages/theme/src/test/theme-provider-color-warnings.test.ts b/packages/theme/src/test/theme-provider-color-warnings.test.ts index 06729da1775850..4450399f8a2d7d 100644 --- a/packages/theme/src/test/theme-provider-color-warnings.test.ts +++ b/packages/theme/src/test/theme-provider-color-warnings.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { buildBgRamp, type RampResult } from '../color-ramps'; import { collectThemeProviderColorWarnings, diff --git a/packages/theme/stylelint-plugins/test/__snapshots__/no-setting-wpds-custom-properties.test.ts.snap b/packages/theme/stylelint-plugins/test/__snapshots__/no-setting-wpds-custom-properties.test.ts.snap index b1e980b4958b15..fbcc7c1c2fbd62 100644 --- a/packages/theme/stylelint-plugins/test/__snapshots__/no-setting-wpds-custom-properties.test.ts.snap +++ b/packages/theme/stylelint-plugins/test/__snapshots__/no-setting-wpds-custom-properties.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid wpds custom properties css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid wpds custom properties css > snapshot matches warnings 1`] = ` [ { "column": 2, diff --git a/packages/theme/stylelint-plugins/test/__snapshots__/no-token-fallback-values.test.ts.snap b/packages/theme/stylelint-plugins/test/__snapshots__/no-token-fallback-values.test.ts.snap index d4e0314ea68caf..67f4f3a48edde1 100644 --- a/packages/theme/stylelint-plugins/test/__snapshots__/no-token-fallback-values.test.ts.snap +++ b/packages/theme/stylelint-plugins/test/__snapshots__/no-token-fallback-values.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid css (wpds fallbacks) snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid css (wpds fallbacks) > snapshot matches warnings 1`] = ` [ { "column": 9, diff --git a/packages/theme/stylelint-plugins/test/__snapshots__/no-unknown-ds-tokens.test.ts.snap b/packages/theme/stylelint-plugins/test/__snapshots__/no-unknown-ds-tokens.test.ts.snap index 8e13c2ebae5d42..f15cd8efaf6062 100644 --- a/packages/theme/stylelint-plugins/test/__snapshots__/no-unknown-ds-tokens.test.ts.snap +++ b/packages/theme/stylelint-plugins/test/__snapshots__/no-unknown-ds-tokens.test.ts.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`flags warnings with invalid wpds tokens css snapshot matches warnings 1`] = ` +exports[`flags warnings with invalid wpds tokens css > snapshot matches warnings 1`] = ` [ { "column": 2, diff --git a/packages/theme/stylelint-plugins/test/no-setting-wpds-custom-properties.test.ts b/packages/theme/stylelint-plugins/test/no-setting-wpds-custom-properties.test.ts index 02af89da93b81f..77cd235ceca0f7 100644 --- a/packages/theme/stylelint-plugins/test/no-setting-wpds-custom-properties.test.ts +++ b/packages/theme/stylelint-plugins/test/no-setting-wpds-custom-properties.test.ts @@ -1,3 +1,4 @@ +import { beforeAll, describe, expect, it } from 'vitest'; import plugin from '../no-setting-wpds-custom-properties.mjs'; import { getStylelintResult } from './utils'; diff --git a/packages/theme/stylelint-plugins/test/no-token-fallback-values.test.ts b/packages/theme/stylelint-plugins/test/no-token-fallback-values.test.ts index 3523db3238e90b..af1d6c729fdab8 100644 --- a/packages/theme/stylelint-plugins/test/no-token-fallback-values.test.ts +++ b/packages/theme/stylelint-plugins/test/no-token-fallback-values.test.ts @@ -1,3 +1,4 @@ +import { beforeAll, describe, expect, it } from 'vitest'; import plugin from '../no-token-fallback-values.mjs'; import { getStylelintResult } from './utils'; diff --git a/packages/theme/stylelint-plugins/test/no-unknown-ds-tokens.test.ts b/packages/theme/stylelint-plugins/test/no-unknown-ds-tokens.test.ts index 766e2d5ded9d42..e7d512579c7f9d 100644 --- a/packages/theme/stylelint-plugins/test/no-unknown-ds-tokens.test.ts +++ b/packages/theme/stylelint-plugins/test/no-unknown-ds-tokens.test.ts @@ -1,3 +1,4 @@ +import { beforeAll, describe, expect, it } from 'vitest'; import plugin from '../no-unknown-ds-tokens.mjs'; import { getStylelintResult } from './utils'; diff --git a/packages/token-list/package.json b/packages/token-list/package.json index ac95975014a9ec..21a31c076fc9a4 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -43,7 +43,8 @@ "wpScriptDefaultExport": true, "types": "build-types", "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/token-list/src/test/index.ts b/packages/token-list/src/test/index.ts index 190e0f6de9b088..9e5103734b9ae0 100644 --- a/packages/token-list/src/test/index.ts +++ b/packages/token-list/src/test/index.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import TokenList from '../'; describe( 'token-list', () => { diff --git a/packages/ui/package.json b/packages/ui/package.json index 2c8fa22c86aa9d..0cee725287bb5d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -67,12 +67,12 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "@wordpress/base-styles": "file:../base-styles", "@wordpress/jest-console": "file:../jest-console", "fast-glob": "^3.2.7", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/ui/src/test/index.test.ts b/packages/ui/src/test/index.test.ts index ec6c70113c79df..c6f20f1ee82860 100644 --- a/packages/ui/src/test/index.test.ts +++ b/packages/ui/src/test/index.test.ts @@ -1,5 +1,6 @@ import { join, dirname, basename } from 'node:path'; import { readFile } from 'node:fs/promises'; +import { describe, expect, it } from 'vitest'; import glob from 'fast-glob'; describe( 'index', () => { diff --git a/packages/ui/src/utils/test/theme-provider.test.ts b/packages/ui/src/utils/test/theme-provider.test.ts index 6259af42889887..e3aae968a5d8a5 100644 --- a/packages/ui/src/utils/test/theme-provider.test.ts +++ b/packages/ui/src/utils/test/theme-provider.test.ts @@ -1,41 +1,54 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + const consent = 'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.'; -function loadThemeProvider( themeModule: object, privateApis = {} ) { - const unlock = jest.fn( () => privateApis ); - jest.doMock( '@wordpress/theme', () => themeModule ); - jest.doMock( '@wordpress/private-apis', () => ( { - __dangerousOptInToUnstableAPIsOnlyForCoreModules: jest.fn( - ( optInConsent, moduleName ) => { - expect( optInConsent ).toBe( consent ); - expect( moduleName ).toBe( '@wordpress/ui' ); +async function loadThemeProvider( + themeModule: Partial< typeof import('@wordpress/theme') >, + privateApis: object = {} +) { + const unlockSpy = vi.fn(); + const unlock = < T = unknown >( object: unknown ): T => { + unlockSpy( object ); + return privateApis as T; + }; + vi.doMock( + import( '@wordpress/theme' ), + () => themeModule as typeof import('@wordpress/theme') + ); + vi.doMock( + import( '@wordpress/private-apis' ), + async ( importOriginal ) => ( { + ...( await importOriginal() ), + __dangerousOptInToUnstableAPIsOnlyForCoreModules: vi.fn( + ( optInConsent, moduleName ) => { + expect( optInConsent ).toBe( consent ); + expect( moduleName ).toBe( '@wordpress/ui' ); - return { unlock }; - } - ), - } ) ); + return { lock: vi.fn(), unlock }; + } + ), + } ) + ); - let ThemeProvider: unknown; - jest.isolateModules( () => { - ( { ThemeProvider } = require( '../theme-provider' ) ); - } ); + const { ThemeProvider } = await import( '../theme-provider' ); - return { ThemeProvider, unlock }; + return { ThemeProvider, unlock: unlockSpy }; } describe( 'ThemeProvider compatibility', () => { afterEach( () => { - jest.resetModules(); - jest.dontMock( '@wordpress/theme' ); - jest.dontMock( '@wordpress/private-apis' ); + vi.resetModules(); + vi.doUnmock( import( '@wordpress/theme' ) ); + vi.doUnmock( import( '@wordpress/private-apis' ) ); } ); - it( 'uses the public ThemeProvider when it is available', () => { - const PublicThemeProvider = jest.fn(); - const PrivateThemeProvider = jest.fn(); + it( 'uses the public ThemeProvider when it is available', async () => { + const PublicThemeProvider = vi.fn(); + const PrivateThemeProvider = vi.fn(); const themePrivateApis = {}; - const { ThemeProvider, unlock } = loadThemeProvider( { + const { ThemeProvider, unlock } = await loadThemeProvider( { ThemeProvider: PublicThemeProvider, privateApis: themePrivateApis, } ); @@ -45,12 +58,13 @@ describe( 'ThemeProvider compatibility', () => { expect( PrivateThemeProvider ).not.toHaveBeenCalled(); } ); - it( 'falls back to privateApis.ThemeProvider for older @wordpress/theme runtimes', () => { - const PrivateThemeProvider = jest.fn(); + it( 'falls back to privateApis.ThemeProvider for older @wordpress/theme runtimes', async () => { + const PrivateThemeProvider = vi.fn(); const themePrivateApis = {}; - const { ThemeProvider, unlock } = loadThemeProvider( + const { ThemeProvider, unlock } = await loadThemeProvider( { + ThemeProvider: undefined, privateApis: themePrivateApis, }, { diff --git a/packages/undo-manager/package.json b/packages/undo-manager/package.json index cce7c57210a454..557c4ba10c5811 100644 --- a/packages/undo-manager/package.json +++ b/packages/undo-manager/package.json @@ -47,7 +47,8 @@ "@wordpress/is-shallow-equal": "file:../is-shallow-equal" }, "devDependencies": { - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/undo-manager/src/test/index.js b/packages/undo-manager/src/test/index.js index 63ef07579a4ef1..bb3660ad347c65 100644 --- a/packages/undo-manager/src/test/index.js +++ b/packages/undo-manager/src/test/index.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { createUndoManager } from '../'; describe( 'Undo Manager', () => { diff --git a/packages/upload-media/package.json b/packages/upload-media/package.json index ae68adb05ab925..4bb83e59db19bf 100644 --- a/packages/upload-media/package.json +++ b/packages/upload-media/package.json @@ -62,7 +62,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/upload-media/src/store/test/actions.ts b/packages/upload-media/src/store/test/actions.ts index 15baef54746249..f60f083b83e759 100644 --- a/packages/upload-media/src/store/test/actions.ts +++ b/packages/upload-media/src/store/test/actions.ts @@ -1,50 +1,71 @@ +import { + afterEach, + beforeEach, + describe, + expect, + it, + vi, + type Mock, +} from 'vitest'; import { createRegistry } from '@wordpress/data'; -type WPDataRegistry = ReturnType< typeof createRegistry >; import { store as uploadStore } from '..'; -import { ItemStatus, OperationType } from '../types'; +import { ItemStatus, OperationType, type QueueItem } from '../types'; import { unlock } from '../../lock-unlock'; -jest.mock( '@wordpress/blob', () => ( { - __esModule: true, - createBlobURL: jest.fn( () => 'blob:foo' ), - isBlobURL: jest.fn( ( str: string ) => str.startsWith( 'blob:' ) ), - revokeBlobURL: jest.fn(), -} ) ); -jest.mock( '../utils', () => ( { - vipsCancelOperations: jest.fn( () => Promise.resolve( true ) ), - vipsResizeImage: jest.fn( () => - Promise.resolve( - new File( [ 'resized' ], 'example-100x100.jpg', { - type: 'image/jpeg', - } ) - ) - ), - vipsRotateImage: jest.fn(), - vipsHasTransparency: jest.fn( () => Promise.resolve( false ) ), - vipsConvertImageFormat: jest.fn(), - terminateVipsWorker: jest.fn(), -} ) ); +import { UploadError } from '../../upload-error'; +import { vipsCancelOperations, vipsResizeImage } from '../utils'; +import { cancelGifToVideoOperations } from '../utils/video-conversion'; +type WPDataRegistry = ReturnType< typeof createRegistry >; + +vi.mock( + import( '@wordpress/blob' ), + () => + ( { + createBlobURL: vi.fn( () => 'blob:foo' ), + isBlobURL: vi.fn( ( str: string ) => str.startsWith( 'blob:' ) ), + revokeBlobURL: vi.fn(), + } ) as unknown as typeof import('@wordpress/blob') +); + +vi.mock( + import( '../utils' ), + () => + ( { + vipsCancelOperations: vi.fn( () => Promise.resolve( true ) ), + vipsResizeImage: vi.fn( () => + Promise.resolve( + new File( [ 'resized' ], 'example-100x100.jpg', { + type: 'image/jpeg', + } ) + ) + ), + vipsRotateImage: vi.fn(), + vipsHasTransparency: vi.fn( () => Promise.resolve( false ) ), + vipsConvertImageFormat: vi.fn(), + terminateVipsWorker: vi.fn(), + } ) as unknown as typeof import('../utils') +); + /* * actions.ts transitively imports private-actions, which also pulls in * convertGifToVideo / isUnsupportedConversionError, so the mock must cover the * whole module surface. isUnsupportedConversionError is kept real. */ -jest.mock( '../utils/video-conversion', () => { - const actual = jest.requireActual( '../utils/video-conversion' ); +vi.mock( import( '../utils/video-conversion' ), async ( importOriginal ) => { + const actual = await importOriginal(); return { - convertGifToVideo: jest.fn(), - cancelGifToVideoOperations: jest.fn( () => Promise.resolve( true ) ), - terminateVideoConversionWorker: jest.fn(), - isUnsupportedConversionError: actual.isUnsupportedConversionError, + ...actual, + convertGifToVideo: vi.fn(), + cancelGifToVideoOperations: vi.fn( () => Promise.resolve( true ) ), + terminateVideoConversionWorker: vi.fn(), }; } ); + // Import the mocked modules to access the mock functions. -import { vipsCancelOperations } from '../utils'; -import { cancelGifToVideoOperations } from '../utils/video-conversion'; function createRegistryWithStores() { // Create a registry and register used stores. const registry = createRegistry(); - [ uploadStore ].forEach( registry.register ); + registry.register( uploadStore ); return registry; } @@ -92,7 +113,7 @@ describe( 'actions', () => { describe( 'addItems', () => { it( 'adds multiple items to the queue', () => { - const onError = jest.fn(); + const onError = vi.fn(); registry.dispatch( uploadStore ).addItems( { files: [ jpegFile, mp4File ], onError, @@ -399,7 +420,7 @@ describe( 'actions', () => { it( 'does not pause sideload items targeting the same post', async () => { // Configure mediaSideload so sideload uploads can proceed. unlock( registry.dispatch( uploadStore ) ).updateSettings( { - mediaSideload: jest.fn(), + mediaSideload: vi.fn(), } ); // Use a fake parentId so we only test sideload scheduling. @@ -426,7 +447,7 @@ describe( 'actions', () => { registry.select( uploadStore ) ).getAllItems(); const sideloadItems = items.filter( - ( item ) => item.parentId === fakeParentId + ( item: QueueItem ) => item.parentId === fakeParentId ); // Neither sideload item should be paused. @@ -436,7 +457,7 @@ describe( 'actions', () => { } ); it( 'allows multiple sideloads to the same attachment to upload concurrently', async () => { - const mediaSideload = jest.fn(); + const mediaSideload = vi.fn(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { mediaSideload, @@ -469,7 +490,7 @@ describe( 'actions', () => { } ); it( 'respects maxConcurrentUploads for sideloads', async () => { - const mediaSideload = jest.fn(); + const mediaSideload = vi.fn(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { mediaSideload, @@ -506,7 +527,7 @@ describe( 'actions', () => { let onSuccessCallback: | ( ( subSize: Record< string, unknown > ) => void ) | undefined; - const mediaSideload = jest.fn( ( { onSuccess } ) => { + const mediaSideload = vi.fn( ( { onSuccess } ) => { // Capture the first callback to simulate completion later. if ( ! onSuccessCallback ) { onSuccessCallback = onSuccess; @@ -570,13 +591,13 @@ describe( 'actions', () => { describe( 'cancelItem', () => { beforeEach( () => { - ( vipsCancelOperations as jest.Mock ).mockClear(); - ( cancelGifToVideoOperations as jest.Mock ).mockClear(); + ( vipsCancelOperations as Mock ).mockClear(); + ( cancelGifToVideoOperations as Mock ).mockClear(); } ); it( 'calls vipsCancelOperations when cancelling', async () => { // Suppress console.error that fires when there's no onError callback. - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) .mockImplementation( () => {} ); @@ -599,7 +620,7 @@ describe( 'actions', () => { it( 'cancels any in-flight GIF-to-video conversion when cancelling', async () => { // Suppress console.error that fires when there's no onError callback. - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) .mockImplementation( () => {} ); @@ -623,7 +644,7 @@ describe( 'actions', () => { it( 'removes item from queue after cancelling', async () => { // Suppress console.error that fires when there's no onError callback. - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) .mockImplementation( () => {} ); @@ -646,7 +667,7 @@ describe( 'actions', () => { } ); it( 'calls onError callback when not silent', async () => { - const onError = jest.fn(); + const onError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).addItem( { file: jpegFile, onError, @@ -665,7 +686,7 @@ describe( 'actions', () => { } ); it( 'does not call onError when silent', async () => { - const onError = jest.fn(); + const onError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).addItem( { file: jpegFile, onError, @@ -688,11 +709,11 @@ describe( 'actions', () => { * must not abort cancelItem, or the item would be stuck in the * queue forever with no error surfaced. */ - ( vipsCancelOperations as jest.Mock ).mockImplementationOnce( () => + ( vipsCancelOperations as Mock ).mockImplementationOnce( () => Promise.reject( new Error( 'Worker error: crashed' ) ) ); - const onError = jest.fn(); + const onError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).addItem( { file: jpegFile, onError, @@ -714,11 +735,11 @@ describe( 'actions', () => { } ); it( 'still calls onError and removes the item when GIF-to-video cancellation rejects', async () => { - ( cancelGifToVideoOperations as jest.Mock ).mockImplementationOnce( - () => Promise.reject( new Error( 'Worker error: crashed' ) ) + ( cancelGifToVideoOperations as Mock ).mockImplementationOnce( () => + Promise.reject( new Error( 'Worker error: crashed' ) ) ); - const onError = jest.fn(); + const onError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).addItem( { file: jpegFile, onError, @@ -749,14 +770,14 @@ describe( 'actions', () => { * block on them, or the cancelled item lingers in the queue and * gates the parent's finalization the whole time. */ - ( vipsCancelOperations as jest.Mock ).mockImplementationOnce( + ( vipsCancelOperations as Mock ).mockImplementationOnce( () => new Promise( () => {} ) ); - ( cancelGifToVideoOperations as jest.Mock ).mockImplementationOnce( + ( cancelGifToVideoOperations as Mock ).mockImplementationOnce( () => new Promise( () => {} ) ); - const onError = jest.fn(); + const onError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).addItem( { file: jpegFile, onError, @@ -789,7 +810,7 @@ describe( 'actions', () => { imageSize = 'medium', }: { parentSubSizes?: { name: string; id: number }[]; - parentOnError?: jest.Mock; + parentOnError?: Mock; imageSize?: string; } = {} ) => { unlock( registry.dispatch( uploadStore ) ).addItem( { @@ -822,17 +843,17 @@ describe( 'actions', () => { const child = unlock( registry.select( uploadStore ) ) .getAllItems() - .find( ( i ) => i.parentId === parent.id ); + .find( ( i: QueueItem ) => i.parentId === parent.id ); return { parent, child }; }; it( 'deletes parent attachment and cancels parent for vips processing failures with no successful siblings', async () => { - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) .mockImplementation( () => {} ); - const mediaDelete = jest.fn().mockResolvedValue( undefined ); - const parentOnError = jest.fn(); + const mediaDelete = vi.fn().mockResolvedValue( undefined ); + const parentOnError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { mediaDelete, } ); @@ -844,9 +865,7 @@ describe( 'actions', () => { // resizeCropItem and rotateItem already wrap vips // failures in an UploadError that carries the // actionable user-facing message at the source. - const vipsError = new ( jest.requireActual( - '../../upload-error' - ).UploadError )( { + const vipsError = new UploadError( { code: 'IMAGE_TRANSCODING_ERROR', message: 'The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading.', @@ -876,17 +895,15 @@ describe( 'actions', () => { } ); it( 'propagates the underlying error message for non-vips sideload failures', async () => { - const mediaDelete = jest.fn().mockResolvedValue( undefined ); - const parentOnError = jest.fn(); + const mediaDelete = vi.fn().mockResolvedValue( undefined ); + const parentOnError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { mediaDelete, } ); const { child } = setUpParentAndChild( { parentOnError } ); - const networkError = new ( jest.requireActual( - '../../upload-error' - ).UploadError )( { + const networkError = new UploadError( { code: 'GENERAL', message: 'Network request failed: 503', file: jpegFile, @@ -906,8 +923,8 @@ describe( 'actions', () => { } ); it( 'preserves the parent attachment when at least one sibling sub-size succeeded', async () => { - const mediaDelete = jest.fn().mockResolvedValue( undefined ); - const parentOnError = jest.fn(); + const mediaDelete = vi.fn().mockResolvedValue( undefined ); + const parentOnError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { mediaDelete, } ); @@ -917,9 +934,7 @@ describe( 'actions', () => { parentSubSizes: [ { name: 'medium', id: 99 } ], } ); - const networkError = new ( jest.requireActual( - '../../upload-error' - ).UploadError )( { + const networkError = new UploadError( { code: 'GENERAL', message: 'sideload of large size failed', file: jpegFile, @@ -942,8 +957,8 @@ describe( 'actions', () => { } ); it( 'falls back to a generic message when the underlying error has no message', async () => { - const mediaDelete = jest.fn().mockResolvedValue( undefined ); - const parentOnError = jest.fn(); + const mediaDelete = vi.fn().mockResolvedValue( undefined ); + const parentOnError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { mediaDelete, } ); @@ -962,8 +977,8 @@ describe( 'actions', () => { } ); it( 'keeps the parent GIF when its only child is a failed animated_video companion', async () => { - const mediaDelete = jest.fn().mockResolvedValue( undefined ); - const parentOnError = jest.fn(); + const mediaDelete = vi.fn().mockResolvedValue( undefined ); + const parentOnError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { mediaDelete, } ); @@ -1085,8 +1100,8 @@ describe( 'actions', () => { describe( 'cancelItem retry integration', () => { beforeEach( () => { - jest.useFakeTimers(); - ( vipsCancelOperations as jest.Mock ).mockClear(); + vi.useFakeTimers(); + ( vipsCancelOperations as Mock ).mockClear(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { retry: { maxRetryAttempts: 3, @@ -1099,7 +1114,7 @@ describe( 'actions', () => { } ); afterEach( () => { - jest.useRealTimers(); + vi.useRealTimers(); } ); it( 'schedules retry for retryable errors', async () => { @@ -1143,7 +1158,7 @@ describe( 'actions', () => { } ); it( 'does NOT schedule retry for non-retryable errors', async () => { - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) .mockImplementation( () => {} ); @@ -1168,7 +1183,7 @@ describe( 'actions', () => { } ); it( 'does NOT schedule retry when retry settings are undefined', async () => { - const consoleErrorSpy = jest + const consoleErrorSpy = vi .spyOn( console, 'error' ) .mockImplementation( () => {} ); @@ -1198,7 +1213,7 @@ describe( 'actions', () => { } ); it( 'clears pending retry timer on manual cancel', async () => { - const onError = jest.fn(); + const onError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).addItem( { file: jpegFile, onError, @@ -1228,7 +1243,7 @@ describe( 'actions', () => { ).toHaveLength( 0 ); // Advance timers — the old retry timer should NOT fire. - await jest.runAllTimersAsync(); + vi.runOnlyPendingTimers(); // Queue should still be empty (timer was cleared). expect( @@ -1239,7 +1254,7 @@ describe( 'actions', () => { describe( 'scheduleRetry', () => { beforeEach( () => { - jest.useFakeTimers(); + vi.useFakeTimers(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { retry: { maxRetryAttempts: 3, @@ -1252,7 +1267,7 @@ describe( 'actions', () => { } ); afterEach( () => { - jest.useRealTimers(); + vi.useRealTimers(); } ); it( 'sets item status to PendingRetry', async () => { @@ -1350,7 +1365,7 @@ describe( 'actions', () => { expect( updatedItem.status ).toBe( ItemStatus.PendingRetry ); // Fire all timers to trigger executeRetry. - await jest.runAllTimersAsync(); + vi.runOnlyPendingTimers(); // Item should now be back in Processing status with incremented retryCount. updatedItem = unlock( @@ -1363,7 +1378,7 @@ describe( 'actions', () => { describe( 'executeRetry', () => { beforeEach( async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); unlock( registry.dispatch( uploadStore ) ).updateSettings( { retry: { maxRetryAttempts: 3, @@ -1379,7 +1394,7 @@ describe( 'actions', () => { } ); afterEach( () => { - jest.useRealTimers(); + vi.useRealTimers(); } ); it( 'resets item to Processing status', async () => { @@ -1527,7 +1542,7 @@ describe( 'actions', () => { // Advance timers — the old retry timer must NOT re-add or // touch the item. - await jest.runAllTimersAsync(); + vi.runOnlyPendingTimers(); expect( unlock( registry.select( uploadStore ) ).getAllItems() @@ -1535,7 +1550,7 @@ describe( 'actions', () => { } ); it( 'falls through to cancellation after exhausting max retries', async () => { - const onError = jest.fn(); + const onError = vi.fn(); unlock( registry.dispatch( uploadStore ) ).addItem( { file: jpegFile, onError, @@ -1558,7 +1573,7 @@ describe( 'actions', () => { // executes the retry (incrementing retryCount), then we simulate // another failure. for ( let attempt = 1; attempt <= 3; attempt++ ) { - await jest.runAllTimersAsync(); + vi.runOnlyPendingTimers(); const inProgress = unlock( registry.select( uploadStore ) @@ -1621,7 +1636,7 @@ describe( 'actions', () => { // Fire the retry timer while paused — executeRetry should bail // without mutating state. - await jest.runAllTimersAsync(); + vi.runOnlyPendingTimers(); const pausedItem = unlock( registry.select( uploadStore ) @@ -1655,8 +1670,7 @@ describe( 'actions', () => { registry.select( uploadStore ) ).getAllItems()[ 0 ]; - const { vipsResizeImage } = require( '../utils' ); - ( vipsResizeImage as jest.Mock ).mockClear(); + ( vipsResizeImage as Mock ).mockClear(); await unlock( registry.dispatch( uploadStore ) ).resizeCropItem( item.id, @@ -1676,8 +1690,7 @@ describe( 'actions', () => { registry.select( uploadStore ) ).getAllItems()[ 0 ]; - const { vipsResizeImage } = require( '../utils' ); - ( vipsResizeImage as jest.Mock ).mockClear(); + ( vipsResizeImage as Mock ).mockClear(); await unlock( registry.dispatch( uploadStore ) ).resizeCropItem( item.id, @@ -1709,10 +1722,10 @@ describe( 'actions', () => { } ); describe( 'generateThumbnails', () => { - const mockBitmapClose = jest.fn(); + const mockBitmapClose = vi.fn(); function mockCreateImageBitmap( width: number, height: number ) { - global.createImageBitmap = jest.fn( () => + global.createImageBitmap = vi.fn( () => Promise.resolve( { width, height, @@ -1766,7 +1779,7 @@ describe( 'actions', () => { afterEach( () => { // Clean up global mock. - // @ts-expect-error The operand of `delete` must be optional. + // @ts-expect-error The test removes the browser API to verify the fallback. delete global.createImageBitmap; } ); @@ -1793,7 +1806,7 @@ describe( 'actions', () => { // Should have sideload items for thumbnails, but NOT for 'scaled'. const scaledItems = allItems.filter( - ( i ) => i.additionalData?.image_size === 'scaled' + ( i: QueueItem ) => i.additionalData?.image_size === 'scaled' ); expect( scaledItems ).toHaveLength( 0 ); expect( mockBitmapClose ).toHaveBeenCalled(); @@ -1821,7 +1834,7 @@ describe( 'actions', () => { ).getAllItems(); const scaledItems = allItems.filter( - ( i ) => i.additionalData?.image_size === 'scaled' + ( i: QueueItem ) => i.additionalData?.image_size === 'scaled' ); expect( scaledItems ).toHaveLength( 1 ); expect( scaledItems[ 0 ].additionalData.post ).toBe( 123 ); @@ -1850,7 +1863,7 @@ describe( 'actions', () => { ).getAllItems(); const scaledItems = allItems.filter( - ( i ) => i.additionalData?.image_size === 'scaled' + ( i: QueueItem ) => i.additionalData?.image_size === 'scaled' ); expect( scaledItems ).toHaveLength( 1 ); } ); @@ -1874,7 +1887,7 @@ describe( 'actions', () => { ).getAllItems(); const scaledItems = allItems.filter( - ( i ) => i.additionalData?.image_size === 'scaled' + ( i: QueueItem ) => i.additionalData?.image_size === 'scaled' ); expect( scaledItems ).toHaveLength( 0 ); // createImageBitmap should not have been called since threshold is not set. @@ -1905,7 +1918,7 @@ describe( 'actions', () => { ).getAllItems(); const scaledItems = allItems.filter( - ( i ) => i.additionalData?.image_size === 'scaled' + ( i: QueueItem ) => i.additionalData?.image_size === 'scaled' ); expect( scaledItems ).toHaveLength( 0 ); } ); @@ -1932,10 +1945,10 @@ describe( 'actions', () => { // Should have the original item plus 2 sideload items for thumbnail and medium. const thumbnailItems = allItems.filter( - ( i ) => i.additionalData?.image_size === 'thumbnail' + ( i: QueueItem ) => i.additionalData?.image_size === 'thumbnail' ); const mediumItems = allItems.filter( - ( i ) => i.additionalData?.image_size === 'medium' + ( i: QueueItem ) => i.additionalData?.image_size === 'medium' ); expect( thumbnailItems ).toHaveLength( 1 ); expect( mediumItems ).toHaveLength( 1 ); @@ -1970,12 +1983,12 @@ describe( 'actions', () => { // Should have the original item plus 2 sideload items (not 3), // because medium and custom share the same dimensions. const sideloadItems = allItems.filter( - ( i ) => i.parentId === item.id + ( i: QueueItem ) => i.parentId === item.id ); expect( sideloadItems ).toHaveLength( 2 ); // The deduplicated group should pass both size names. - const mediumCustomItem = sideloadItems.find( ( i ) => + const mediumCustomItem = sideloadItems.find( ( i: QueueItem ) => Array.isArray( i.additionalData?.image_size ) ); expect( mediumCustomItem ).toBeDefined(); @@ -2011,7 +2024,7 @@ describe( 'actions', () => { ).getAllItems(); const sideloadItems = allItems.filter( - ( i ) => i.parentId === item.id + ( i: QueueItem ) => i.parentId === item.id ); // Two separate sideloads because crop differs. expect( sideloadItems ).toHaveLength( 2 ); @@ -2023,7 +2036,7 @@ describe( 'actions', () => { ).toBe( true ); } const imageSizes = sideloadItems.map( - ( i ) => i.additionalData?.image_size + ( i: QueueItem ) => i.additionalData?.image_size ); expect( imageSizes ).toEqual( expect.arrayContaining( [ 'soft', 'hard' ] ) @@ -2056,7 +2069,7 @@ describe( 'actions', () => { ).getAllItems(); const sideloadItems = allItems.filter( - ( i ) => i.parentId === item.id + ( i: QueueItem ) => i.parentId === item.id ); // One sideload, all three names grouped together. expect( sideloadItems ).toHaveLength( 1 ); @@ -2123,7 +2136,7 @@ describe( 'actions', () => { ).getAllItems(); const scaledItems = allItems.filter( - ( i ) => i.additionalData?.image_size === 'scaled' + ( i: QueueItem ) => i.additionalData?.image_size === 'scaled' ); // Exactly at threshold means no scaling (condition is > not >=). expect( scaledItems ).toHaveLength( 0 ); @@ -2168,7 +2181,7 @@ describe( 'actions', () => { const thumbnailItems = unlock( registry.select( uploadStore ) ) .getAllItems() .filter( - ( i ) => + ( i: QueueItem ) => i.additionalData?.image_size === 'thumbnail' || i.additionalData?.image_size === 'medium' ); @@ -2210,7 +2223,8 @@ describe( 'actions', () => { const scaledItems = unlock( registry.select( uploadStore ) ) .getAllItems() .filter( - ( i ) => i.additionalData?.image_size === 'scaled' + ( i: QueueItem ) => + i.additionalData?.image_size === 'scaled' ); expect( scaledItems ).toHaveLength( 1 ); // vipsResizeImage adds the `-scaled` suffix during the diff --git a/packages/upload-media/src/store/test/private-actions.js b/packages/upload-media/src/store/test/private-actions.js index 8693c4bb42f813..d15da9f6edce86 100644 --- a/packages/upload-media/src/store/test/private-actions.js +++ b/packages/upload-media/src/store/test/private-actions.js @@ -1,5 +1,6 @@ import { readFileSync } from 'node:fs'; import { join } from 'node:path'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createBlobURL, revokeBlobURL } from '@wordpress/blob'; import { generateThumbnails, @@ -25,47 +26,42 @@ import { } from '../utils/video-conversion'; // Mock @wordpress/blob -jest.mock( '@wordpress/blob', () => ( { - createBlobURL: jest.fn( () => 'blob:mock-url' ), - revokeBlobURL: jest.fn(), +vi.mock( import( '@wordpress/blob' ), () => ( { + createBlobURL: vi.fn( () => 'blob:mock-url' ), + revokeBlobURL: vi.fn(), } ) ); // Mock vips utilities. The real isAnimatedGif() is needed by prepareItem // so it is required from the actual module rather than stubbed out. -jest.mock( '../utils', () => { - const actual = jest.requireActual( '../utils' ); +vi.mock( import( '../utils' ), async ( importOriginal ) => { + const actual = await importOriginal(); return { - vipsHasTransparency: jest.fn(), - vipsGetUltraHdrInfo: jest.fn(), - vipsRotateImage: jest.fn(), - terminateVipsWorker: jest.fn(), - maybeRecycleVipsWorker: jest.fn(), - isAnimatedGif: actual.isAnimatedGif, - cloneFile: actual.cloneFile, - convertBlobToFile: actual.convertBlobToFile, - renameFile: actual.renameFile, + ...actual, + vipsHasTransparency: vi.fn(), + vipsGetUltraHdrInfo: vi.fn(), + vipsRotateImage: vi.fn(), + terminateVipsWorker: vi.fn(), + maybeRecycleVipsWorker: vi.fn(), }; } ); // Mock the video-conversion wrapper so the dynamic worker import is never // executed. isUnsupportedConversionError is kept real so transcodeGifItem's // graceful-skip vs. hard-failure branching is genuinely exercised. -jest.mock( '../utils/video-conversion', () => { - const actual = jest.requireActual( '../utils/video-conversion' ); +vi.mock( import( '../utils/video-conversion' ), async ( importOriginal ) => { + const actual = await importOriginal(); return { - convertGifToVideo: jest.fn(), - cancelGifToVideoOperations: jest.fn(), - terminateVideoConversionWorker: jest.fn(), - isUnsupportedConversionError: actual.isUnsupportedConversionError, - isSizeLimitConversionError: actual.isSizeLimitConversionError, - isConversionTimeoutError: actual.isConversionTimeoutError, + ...actual, + convertGifToVideo: vi.fn(), + cancelGifToVideoOperations: vi.fn(), + terminateVideoConversionWorker: vi.fn(), }; } ); describe( 'private actions', () => { describe( 'getTranscodeImageOperation', () => { beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'should return transcode operation for valid format conversion', async () => { @@ -330,8 +326,8 @@ describe( 'private actions', () => { ]; it( 'should call mediaFinalize with the attachment ID and sub-sizes', async () => { - const mediaFinalize = jest.fn().mockResolvedValue( undefined ); - const finishOperation = jest.fn(); + const mediaFinalize = vi.fn().mockResolvedValue( undefined ); + const finishOperation = vi.fn(); const select = { getItem: () => ( { attachment: { id: 42 }, @@ -349,8 +345,8 @@ describe( 'private actions', () => { } ); it( 'should pass empty array when no sub-sizes accumulated', async () => { - const mediaFinalize = jest.fn().mockResolvedValue( undefined ); - const finishOperation = jest.fn(); + const mediaFinalize = vi.fn().mockResolvedValue( undefined ); + const finishOperation = vi.fn(); const select = { getItem: () => ( { attachment: { id: 42 }, @@ -367,7 +363,7 @@ describe( 'private actions', () => { } ); it( 'should not call mediaFinalize when no callback is provided', async () => { - const finishOperation = jest.fn(); + const finishOperation = vi.fn(); const select = { getItem: () => ( { attachment: { id: 42 }, @@ -383,8 +379,8 @@ describe( 'private actions', () => { } ); it( 'should not call mediaFinalize when there is no attachment ID', async () => { - const mediaFinalize = jest.fn(); - const finishOperation = jest.fn(); + const mediaFinalize = vi.fn(); + const finishOperation = vi.fn(); const select = { getItem: () => ( { attachment: {}, @@ -413,10 +409,10 @@ describe( 'private actions', () => { id: 42, url: 'https://example.com/wp-content/uploads/image-scaled.jpg', }; - const mediaFinalize = jest + const mediaFinalize = vi .fn() .mockResolvedValue( updatedAttachment ); - const finishOperation = jest.fn(); + const finishOperation = vi.fn(); const select = { getItem: () => ( { attachment: { @@ -446,10 +442,10 @@ describe( 'private actions', () => { // apiFetch rejects a failed REST request with a plain object, not // an Error instance, so reject with one here to mirror that. const restError = { code: 'rest_error', message: 'Server error' }; - const mediaFinalize = jest.fn().mockRejectedValue( restError ); - const finishOperation = jest.fn(); - const cancelItem = jest.fn(); - const warnSpy = jest + const mediaFinalize = vi.fn().mockRejectedValue( restError ); + const finishOperation = vi.fn(); + const cancelItem = vi.fn(); + const warnSpy = vi .spyOn( console, 'warn' ) .mockImplementation( () => {} ); const file = new File( [ 'foo' ], 'foo.jpg', { @@ -485,7 +481,7 @@ describe( 'private actions', () => { } ); it( 'should return early when item is not found', async () => { - const finishOperation = jest.fn(); + const finishOperation = vi.fn(); const select = { getItem: () => undefined, }; @@ -533,8 +529,8 @@ describe( 'private actions', () => { dispatchedOperations = action.operations; } }; - dispatch.cancelItem = jest.fn(); - dispatch.finishOperation = jest.fn(); + dispatch.cancelItem = vi.fn(); + dispatch.finishOperation = vi.fn(); const select = { getItem: () => item, @@ -652,8 +648,8 @@ describe( 'private actions', () => { dispatchedOperations = action.operations; } }; - dispatch.cancelItem = jest.fn(); - dispatch.finishOperation = jest.fn(); + dispatch.cancelItem = vi.fn(); + dispatch.finishOperation = vi.fn(); const select = { getItem: () => item, @@ -691,8 +687,8 @@ describe( 'private actions', () => { dispatchedOperations = action.operations; } }; - dispatch.cancelItem = jest.fn(); - dispatch.finishOperation = jest.fn(); + dispatch.cancelItem = vi.fn(); + dispatch.finishOperation = vi.fn(); const select = { getItem: () => item, @@ -716,13 +712,13 @@ describe( 'private actions', () => { } ); function buildArgs() { - const dispatch = Object.assign( jest.fn(), { - finishOperation: jest.fn(), - cancelItem: jest.fn(), - addSideloadItem: jest.fn(), + const dispatch = Object.assign( vi.fn(), { + finishOperation: vi.fn(), + cancelItem: vi.fn(), + addSideloadItem: vi.fn(), } ); const select = { - getItem: jest.fn( () => ( { + getItem: vi.fn( () => ( { id: 'gif-1', file: gifFile, parentId: 'parent-1', @@ -738,10 +734,10 @@ describe( 'private actions', () => { beforeEach( () => { convertGifToVideo.mockReset(); createBlobURL.mockClear(); - consoleError = jest + consoleError = vi .spyOn( console, 'error' ) .mockImplementation( () => {} ); - consoleDebug = jest + consoleDebug = vi .spyOn( console, 'debug' ) .mockImplementation( () => {} ); } ); @@ -954,7 +950,7 @@ describe( 'private actions', () => { it( 'does nothing when the item is not in the queue', async () => { const { dispatch } = buildArgs(); - const select = { getItem: jest.fn( () => undefined ) }; + const select = { getItem: vi.fn( () => undefined ) }; await transcodeGifItem( 'missing' )( { select, dispatch } ); @@ -966,9 +962,9 @@ describe( 'private actions', () => { describe( 'generateThumbnails (animated GIF video sideload)', () => { function runGenerate( { item, settings } ) { - const dispatchFn = jest.fn(); - dispatchFn.finishOperation = jest.fn(); - dispatchFn.addSideloadItem = jest.fn(); + const dispatchFn = vi.fn(); + dispatchFn.finishOperation = vi.fn(); + dispatchFn.addSideloadItem = vi.fn(); const select = { getItem: () => item, getSettings: () => settings, @@ -1071,7 +1067,7 @@ describe( 'private actions', () => { } ); beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'probes the file and finishes the operation when UltraHDR', async () => { @@ -1080,7 +1076,7 @@ describe( 'private actions', () => { height: 768, hdrCapacity: 3, } ); - const finishOperation = jest.fn(); + const finishOperation = vi.fn(); const item = makeItem(); const select = { getItem: () => item }; const dispatch = { finishOperation }; @@ -1097,7 +1093,7 @@ describe( 'private actions', () => { it( 'finishes cleanly when buffer is not UltraHDR', async () => { vipsGetUltraHdrInfo.mockResolvedValue( null ); - const finishOperation = jest.fn(); + const finishOperation = vi.fn(); const select = { getItem: () => makeItem() }; const dispatch = { finishOperation }; @@ -1111,7 +1107,7 @@ describe( 'private actions', () => { vipsGetUltraHdrInfo.mockRejectedValue( new Error( 'wasm module unavailable' ) ); - const finishOperation = jest.fn(); + const finishOperation = vi.fn(); const select = { getItem: () => makeItem() }; const dispatch = { finishOperation }; @@ -1123,7 +1119,7 @@ describe( 'private actions', () => { } ); it( 'returns early when item is not found', async () => { - const finishOperation = jest.fn(); + const finishOperation = vi.fn(); const select = { getItem: () => undefined }; const dispatch = { finishOperation }; @@ -1162,12 +1158,12 @@ describe( 'private actions', () => { } ); const makeHarness = ( item, settings = {} ) => { - const addSideloadItem = jest.fn(); - const finishOperation = jest.fn(); + const addSideloadItem = vi.fn(); + const finishOperation = vi.fn(); const dispatch = { addSideloadItem, finishOperation, - addItem: jest.fn(), + addItem: vi.fn(), }; const select = { getItem: () => item, @@ -1193,7 +1189,7 @@ describe( 'private actions', () => { vipsGetUltraHdrInfo.mockResolvedValue( ULTRAHDR_INFO ); await detectUltraHdr( item.id )( { select: { getItem: () => item }, - dispatch: { finishOperation: jest.fn() }, + dispatch: { finishOperation: vi.fn() }, } ); }; @@ -1202,12 +1198,12 @@ describe( 'private actions', () => { const clearTracking = async ( id ) => { await removeItem( id )( { select: { getItem: () => ( {} ), getAllItems: () => [] }, - dispatch: jest.fn(), + dispatch: vi.fn(), } ); }; beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); afterEach( async () => { @@ -1245,10 +1241,10 @@ describe( 'private actions', () => { it( 'keeps the over-threshold -scaled full-size copy as UltraHDR (no transcode)', async () => { const originalCreateImageBitmap = global.createImageBitmap; - global.createImageBitmap = jest.fn( async () => ( { + global.createImageBitmap = vi.fn( async () => ( { width: 5000, height: 4000, - close: jest.fn(), + close: vi.fn(), } ) ); try { @@ -1339,11 +1335,11 @@ describe( 'private actions', () => { } ); const makeHarness = ( item ) => { - const addSideloadItem = jest.fn(); + const addSideloadItem = vi.fn(); const dispatch = { addSideloadItem, - finishOperation: jest.fn(), - addItem: jest.fn(), + finishOperation: vi.fn(), + addItem: vi.fn(), }; const select = { getItem: () => item, @@ -1362,7 +1358,7 @@ describe( 'private actions', () => { ); beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); vipsRotateImage.mockResolvedValue( new File( [ 'rotated' ], 'photo-rotated.jpg', { type: 'image/jpeg', @@ -1475,7 +1471,7 @@ describe( 'private actions', () => { } ); it( 'continues thumbnail generation when rotation fails', async () => { - const warnSpy = jest + const warnSpy = vi .spyOn( console, 'warn' ) .mockImplementation( () => {} ); vipsRotateImage.mockRejectedValue( new Error( 'decode failed' ) ); @@ -1509,15 +1505,15 @@ describe( 'private actions', () => { // The queue already counts its items for progress UI; the // `mediaUpload` callback it delegates the server upload to must // not count the same file again (see gutenberg#80369). - const mediaUpload = jest.fn(); + const mediaUpload = vi.fn(); const file = new File( [ 'content' ], 'photo.jpg', { type: 'image/jpeg', } ); const item = { id: 'item-1', file, additionalData: {} }; - const dispatch = jest.fn(); - dispatch.finishOperation = jest.fn(); - dispatch.cancelItem = jest.fn(); + const dispatch = vi.fn(); + dispatch.finishOperation = vi.fn(); + dispatch.cancelItem = vi.fn(); await uploadItem( 'item-1' )( { select: { @@ -1538,7 +1534,7 @@ describe( 'private actions', () => { describe( 'removeItem worker teardown', () => { beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); const runRemove = async ( remaining ) => @@ -1547,7 +1543,7 @@ describe( 'private actions', () => { getItem: () => ( {} ), getAllItems: () => remaining, }, - dispatch: jest.fn(), + dispatch: vi.fn(), } ); it( 'terminates both background workers once the queue empties', async () => { diff --git a/packages/upload-media/src/store/test/reducer.ts b/packages/upload-media/src/store/test/reducer.ts index 21a916173e2581..d581a4f8b4f55d 100644 --- a/packages/upload-media/src/store/test/reducer.ts +++ b/packages/upload-media/src/store/test/reducer.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import reducer from '../reducer'; import { ItemStatus, @@ -15,7 +16,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -38,6 +41,8 @@ describe( 'reducer', () => { blobUrls: {}, settings: { mediaUpload: expect.any( Function ), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -60,7 +65,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -85,6 +92,8 @@ describe( 'reducer', () => { blobUrls: {}, settings: { mediaUpload: expect.any( Function ), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -156,7 +165,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -180,6 +191,8 @@ describe( 'reducer', () => { blobUrls: {}, settings: { mediaUpload: expect.any( Function ), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -198,7 +211,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -220,6 +235,8 @@ describe( 'reducer', () => { blobUrls: {}, settings: { mediaUpload: expect.any( Function ), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -242,7 +259,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -269,6 +288,8 @@ describe( 'reducer', () => { blobUrls: {}, settings: { mediaUpload: expect.any( Function ), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -294,7 +315,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -319,6 +342,8 @@ describe( 'reducer', () => { blobUrls: {}, settings: { mediaUpload: expect.any( Function ), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -341,7 +366,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -371,7 +398,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -396,7 +425,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -423,7 +454,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -450,7 +483,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -486,7 +521,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -512,7 +549,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -539,7 +578,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -573,7 +614,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [], }; @@ -590,7 +633,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [], }; @@ -607,7 +652,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: { '1': [ 'blob:foo' ] }, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -632,7 +679,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [], }; @@ -649,7 +698,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [], }; @@ -666,7 +717,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -695,7 +748,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { diff --git a/packages/upload-media/src/store/test/retry.ts b/packages/upload-media/src/store/test/retry.ts index e65a5fa65fb629..06633eebecd16b 100644 --- a/packages/upload-media/src/store/test/retry.ts +++ b/packages/upload-media/src/store/test/retry.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { calculateRetryDelay, shouldRetryError } from '../utils/retry'; import { UploadError } from '../../upload-error'; @@ -6,7 +7,7 @@ describe( 'calculateRetryDelay', () => { beforeEach( () => { // Force the jitter factor to 1 (no offset) for deterministic results. - Math.random = jest.fn( () => 0.5 ); + Math.random = vi.fn( () => 0.5 ); } ); afterEach( () => { @@ -79,7 +80,7 @@ describe( 'calculateRetryDelay', () => { expect( delayWithMiddleJitter ).toBe( 1000 ); // Math.random = 0 → jitter factor = 1 + (0 * 2 - 1) * 0.1 = 0.9. - Math.random = jest.fn( () => 0 ); + Math.random = vi.fn( () => 0 ); const delayWithMinJitter = calculateRetryDelay( { attempt: 1, initialDelay: 1000, @@ -90,7 +91,7 @@ describe( 'calculateRetryDelay', () => { expect( delayWithMinJitter ).toBe( 900 ); // Math.random = 1 → jitter factor = 1 + (1 * 2 - 1) * 0.1 = 1.1. - Math.random = jest.fn( () => 1 ); + Math.random = vi.fn( () => 1 ); const delayWithMaxJitter = calculateRetryDelay( { attempt: 1, initialDelay: 1000, diff --git a/packages/upload-media/src/store/test/selectors.ts b/packages/upload-media/src/store/test/selectors.ts index b59f88c742fe43..35b6c0271c63d9 100644 --- a/packages/upload-media/src/store/test/selectors.ts +++ b/packages/upload-media/src/store/test/selectors.ts @@ -1,3 +1,5 @@ +import { describe, expect, it, vi } from 'vitest'; +import * as privateSelectors from '../private-selectors'; import { getItems, isUploading, @@ -48,7 +50,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -74,7 +78,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -100,7 +106,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -128,7 +136,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -161,7 +171,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -198,7 +210,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -218,7 +232,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -247,7 +263,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -304,7 +322,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -329,7 +349,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -362,7 +384,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -392,7 +416,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -404,13 +430,15 @@ describe( 'selectors', () => { describe( 'removed selectors', () => { it( 'isUploadingToPost is no longer exported', () => { - const privateSelectors = require( '../private-selectors' ); - expect( privateSelectors.isUploadingToPost ).toBeUndefined(); + expect( privateSelectors ).not.toHaveProperty( + 'isUploadingToPost' + ); } ); it( 'getPausedUploadForPost is no longer exported', () => { - const privateSelectors = require( '../private-selectors' ); - expect( privateSelectors.getPausedUploadForPost ).toBeUndefined(); + expect( privateSelectors ).not.toHaveProperty( + 'getPausedUploadForPost' + ); } ); } ); @@ -461,7 +489,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; @@ -485,7 +515,9 @@ describe( 'selectors', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; diff --git a/packages/upload-media/src/store/utils/test/video-conversion.ts b/packages/upload-media/src/store/utils/test/video-conversion.ts index 449d77bed3710a..c0b6eb0dc1f426 100644 --- a/packages/upload-media/src/store/utils/test/video-conversion.ts +++ b/packages/upload-media/src/store/utils/test/video-conversion.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { isUnsupportedConversionError, isSizeLimitConversionError, @@ -85,15 +86,15 @@ describe( 'isConversionTimeoutError', () => { */ describe( 'convertGifToVideo', () => { beforeEach( () => { - jest.resetModules(); + vi.resetModules(); } ); it( 'delegates to the worker and wraps mp4 output in a named File', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); + const worker = await import( '@wordpress/video-conversion/worker' ); const buffer = new ArrayBuffer( 8 ); - worker.convertGifToVideo.mockResolvedValue( buffer ); + vi.mocked( worker.convertGifToVideo ).mockResolvedValue( buffer ); - const { convertGifToVideo } = require( '../video-conversion' ); + const { convertGifToVideo } = await import( '../video-conversion' ); const gif = new File( [ new Uint8Array( [ 1, 2, 3 ] ) ], 'my-anim.gif', @@ -123,10 +124,12 @@ describe( 'convertGifToVideo', () => { } ); it( 'uses a .webm extension for webm output', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); - worker.convertGifToVideo.mockResolvedValue( new ArrayBuffer( 4 ) ); + const worker = await import( '@wordpress/video-conversion/worker' ); + vi.mocked( worker.convertGifToVideo ).mockResolvedValue( + new ArrayBuffer( 4 ) + ); - const { convertGifToVideo } = require( '../video-conversion' ); + const { convertGifToVideo } = await import( '../video-conversion' ); const gif = new File( [ new Uint8Array( [ 0 ] ) ], 'clip.gif', { type: 'image/gif', } ); @@ -145,10 +148,12 @@ describe( 'convertGifToVideo', () => { } ); it( 'defaults to a .mp4 extension for any non-webm output type', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); - worker.convertGifToVideo.mockResolvedValue( new ArrayBuffer( 4 ) ); + const worker = await import( '@wordpress/video-conversion/worker' ); + vi.mocked( worker.convertGifToVideo ).mockResolvedValue( + new ArrayBuffer( 4 ) + ); - const { convertGifToVideo } = require( '../video-conversion' ); + const { convertGifToVideo } = await import( '../video-conversion' ); const gif = new File( [ new Uint8Array( [ 0 ] ) ], 'clip.gif', { type: 'image/gif', } ); @@ -164,13 +169,34 @@ describe( 'convertGifToVideo', () => { } ); describe( 'convertGifToVideo timeout', () => { - beforeEach( () => { - jest.resetModules(); - jest.useFakeTimers(); + let worker: typeof import('@wordpress/video-conversion/worker'); + let convertGifToVideo: typeof import('../video-conversion').convertGifToVideo; + + beforeEach( async () => { + vi.resetModules(); + + /* + * Resolve the production module's lazy worker import before switching + * to fake timers. Vitest's module loader schedules work through the + * real event loop, so awaiting the first import while timers are faked + * would make the test race the loader rather than the conversion timer + * under test. + */ + worker = await import( '@wordpress/video-conversion/worker' ); + vi.mocked( worker.convertGifToVideo ).mockResolvedValue( + new ArrayBuffer( 0 ) + ); + ( { convertGifToVideo } = await import( '../video-conversion' ) ); + await convertGifToVideo( 'warmup', makeGif(), 'video/mp4', { + timeout: 0, + } ); + + vi.clearAllMocks(); + vi.useFakeTimers(); } ); afterEach( () => { - jest.useRealTimers(); + vi.useRealTimers(); } ); function makeGif() { @@ -179,20 +205,36 @@ describe( 'convertGifToVideo timeout', () => { } ); } + async function waitForWorkerCall( + workerModule: typeof import('@wordpress/video-conversion/worker') + ) { + for ( + let attempts = 0; + attempts < 10 && + ! vi.mocked( workerModule.convertGifToVideo ).mock.calls.length; + attempts++ + ) { + await Promise.resolve(); + } + expect( workerModule.convertGifToVideo ).toHaveBeenCalled(); + } + it( 'abandons a conversion that exceeds the default 30s timeout and cancels the worker', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); // A conversion that never settles, like a huge GIF churning away. - worker.convertGifToVideo.mockReturnValue( new Promise( () => {} ) ); - worker.cancelGifToVideoOperations.mockResolvedValue( true ); - - const { convertGifToVideo } = require( '../video-conversion' ); + vi.mocked( worker.convertGifToVideo ).mockReturnValue( + new Promise( () => {} ) + ); + vi.mocked( worker.cancelGifToVideoOperations ).mockResolvedValue( + true + ); const promise = convertGifToVideo( 'item-1', makeGif(), 'video/mp4' ); // Attach a handler before advancing time so the rejection is never // reported as unhandled. promise.catch( () => {} ); + await waitForWorkerCall( worker ); - await jest.advanceTimersByTimeAsync( 30_000 ); + vi.advanceTimersByTime( 30_000 ); // The exact message is the isConversionTimeoutError contract. await expect( promise ).rejects.toThrow( @@ -206,57 +248,57 @@ describe( 'convertGifToVideo timeout', () => { } ); it( 'does not time out a conversion that finishes in time', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); - worker.convertGifToVideo.mockResolvedValue( new ArrayBuffer( 4 ) ); - - const { convertGifToVideo } = require( '../video-conversion' ); + vi.mocked( worker.convertGifToVideo ).mockResolvedValue( + new ArrayBuffer( 4 ) + ); const promise = convertGifToVideo( 'item-2', makeGif(), 'video/mp4' ); - await jest.advanceTimersByTimeAsync( 0 ); + await waitForWorkerCall( worker ); + vi.advanceTimersByTime( 0 ); const result = await promise; expect( result ).toBeInstanceOf( File ); expect( worker.cancelGifToVideoOperations ).not.toHaveBeenCalled(); // The timeout timer was cleared; nothing left pending. - expect( jest.getTimerCount() ).toBe( 0 ); + expect( vi.getTimerCount() ).toBe( 0 ); } ); it( 'honors a custom timeout', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); - worker.convertGifToVideo.mockReturnValue( new Promise( () => {} ) ); - worker.cancelGifToVideoOperations.mockResolvedValue( true ); - - const { convertGifToVideo } = require( '../video-conversion' ); + vi.mocked( worker.convertGifToVideo ).mockReturnValue( + new Promise( () => {} ) + ); + vi.mocked( worker.cancelGifToVideoOperations ).mockResolvedValue( + true + ); const promise = convertGifToVideo( 'item-3', makeGif(), 'video/mp4', { timeout: 5_000, } ); promise.catch( () => {} ); + await waitForWorkerCall( worker ); - await jest.advanceTimersByTimeAsync( 5_000 ); + vi.advanceTimersByTime( 5_000 ); await expect( promise ).rejects.toThrow( /timed out/i ); } ); it( 'treats a timeout of 0 as disabled', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); let resolveConversion: ( buffer: ArrayBuffer ) => void = () => {}; - worker.convertGifToVideo.mockReturnValue( + vi.mocked( worker.convertGifToVideo ).mockReturnValue( new Promise( ( resolve ) => { resolveConversion = resolve; } ) ); - const { convertGifToVideo } = require( '../video-conversion' ); - const promise = convertGifToVideo( 'item-4', makeGif(), 'video/mp4', { timeout: 0, } ); + await waitForWorkerCall( worker ); // Way past the default timeout: nothing should reject because no // timer was ever set. - await jest.advanceTimersByTimeAsync( 120_000 ); - expect( jest.getTimerCount() ).toBe( 0 ); + vi.advanceTimersByTime( 120_000 ); + expect( vi.getTimerCount() ).toBe( 0 ); resolveConversion( new ArrayBuffer( 4 ) ); const result = await promise; @@ -267,12 +309,14 @@ describe( 'convertGifToVideo timeout', () => { describe( 'cancelGifToVideoOperations', () => { beforeEach( () => { - jest.resetModules(); + vi.resetModules(); } ); it( 'returns false when the worker module has not been loaded yet', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); - const { cancelGifToVideoOperations } = require( '../video-conversion' ); + const worker = await import( '@wordpress/video-conversion/worker' ); + const { cancelGifToVideoOperations } = await import( + '../video-conversion' + ); await expect( cancelGifToVideoOperations( 'item-1' ) ).resolves.toBe( false @@ -281,14 +325,17 @@ describe( 'cancelGifToVideoOperations', () => { } ); it( 'delegates to the worker once the module is loaded', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); - worker.convertGifToVideo.mockResolvedValue( new ArrayBuffer( 4 ) ); - worker.cancelGifToVideoOperations.mockResolvedValue( true ); + const worker = await import( '@wordpress/video-conversion/worker' ); + vi.mocked( worker.convertGifToVideo ).mockResolvedValue( + new ArrayBuffer( 4 ) + ); + vi.mocked( worker.cancelGifToVideoOperations ).mockResolvedValue( + true + ); - const { - convertGifToVideo, - cancelGifToVideoOperations, - } = require( '../video-conversion' ); + const { convertGifToVideo, cancelGifToVideoOperations } = await import( + '../video-conversion' + ); // Trigger a conversion to lazily load (and cache) the worker module. await convertGifToVideo( @@ -308,14 +355,17 @@ describe( 'cancelGifToVideoOperations', () => { } ); it( 'delegates a cancel issued while the worker is still loading', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); - worker.convertGifToVideo.mockResolvedValue( new ArrayBuffer( 4 ) ); - worker.cancelGifToVideoOperations.mockResolvedValue( true ); + const worker = await import( '@wordpress/video-conversion/worker' ); + vi.mocked( worker.convertGifToVideo ).mockResolvedValue( + new ArrayBuffer( 4 ) + ); + vi.mocked( worker.cancelGifToVideoOperations ).mockResolvedValue( + true + ); - const { - convertGifToVideo, - cancelGifToVideoOperations, - } = require( '../video-conversion' ); + const { convertGifToVideo, cancelGifToVideoOperations } = await import( + '../video-conversion' + ); /* * Start a conversion but do NOT await it: the worker module is now @@ -343,27 +393,27 @@ describe( 'cancelGifToVideoOperations', () => { describe( 'terminateVideoConversionWorker', () => { beforeEach( () => { - jest.resetModules(); + vi.resetModules(); } ); - it( 'is a no-op when the worker module has not been loaded yet', () => { - const worker = require( '@wordpress/video-conversion/worker' ); - const { - terminateVideoConversionWorker, - } = require( '../video-conversion' ); + it( 'is a no-op when the worker module has not been loaded yet', async () => { + const worker = await import( '@wordpress/video-conversion/worker' ); + const { terminateVideoConversionWorker } = await import( + '../video-conversion' + ); expect( () => terminateVideoConversionWorker() ).not.toThrow(); expect( worker.terminateVideoConversionWorker ).not.toHaveBeenCalled(); } ); it( 'terminates the worker once the module is loaded', async () => { - const worker = require( '@wordpress/video-conversion/worker' ); - worker.convertGifToVideo.mockResolvedValue( new ArrayBuffer( 4 ) ); + const worker = await import( '@wordpress/video-conversion/worker' ); + vi.mocked( worker.convertGifToVideo ).mockResolvedValue( + new ArrayBuffer( 4 ) + ); - const { - convertGifToVideo, - terminateVideoConversionWorker, - } = require( '../video-conversion' ); + const { convertGifToVideo, terminateVideoConversionWorker } = + await import( '../video-conversion' ); await convertGifToVideo( 'item-1', diff --git a/packages/upload-media/src/test/canvas-utils.ts b/packages/upload-media/src/test/canvas-utils.ts index 4484d1487659bd..5c0f9729ba0ed0 100644 --- a/packages/upload-media/src/test/canvas-utils.ts +++ b/packages/upload-media/src/test/canvas-utils.ts @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; import { canvasConvertToJpeg } from '../canvas-utils'; import { getHeicUnsupportedMessage } from '../heic-support'; @@ -42,20 +43,22 @@ describe( 'canvasConvertToJpeg', () => { const mockBitmap = { width: 200, height: 150, - close: jest.fn(), + close: vi.fn(), }; const mockCtx = { - drawImage: jest.fn(), + drawImage: vi.fn(), }; - global.createImageBitmap = jest + global.createImageBitmap = vi.fn().mockResolvedValue( mockBitmap ); + global.OffscreenCanvas = vi .fn() - .mockResolvedValue( mockBitmap ); - global.OffscreenCanvas = jest.fn().mockImplementation( () => ( { - getContext: jest.fn().mockReturnValue( mockCtx ), - convertToBlob: jest.fn().mockResolvedValue( jpegBlob ), - } ) ); + .mockImplementation( function OffscreenCanvas() { + return { + getContext: vi.fn().mockReturnValue( mockCtx ), + convertToBlob: vi.fn().mockResolvedValue( jpegBlob ), + }; + } ); const file = new File( [ 'heic-data' ], 'photo.heic', { type: 'image/heic', @@ -74,18 +77,20 @@ describe( 'canvasConvertToJpeg', () => { type: 'image/jpeg', } ); - const mockConvertToBlob = jest.fn().mockResolvedValue( jpegBlob ); - const mockBitmap = { width: 100, height: 100, close: jest.fn() }; + const mockConvertToBlob = vi.fn().mockResolvedValue( jpegBlob ); + const mockBitmap = { width: 100, height: 100, close: vi.fn() }; - global.createImageBitmap = jest + global.createImageBitmap = vi.fn().mockResolvedValue( mockBitmap ); + global.OffscreenCanvas = vi .fn() - .mockResolvedValue( mockBitmap ); - global.OffscreenCanvas = jest.fn().mockImplementation( () => ( { - getContext: jest - .fn() - .mockReturnValue( { drawImage: jest.fn() } ), - convertToBlob: mockConvertToBlob, - } ) ); + .mockImplementation( function OffscreenCanvas() { + return { + getContext: vi + .fn() + .mockReturnValue( { drawImage: vi.fn() } ), + convertToBlob: mockConvertToBlob, + }; + } ); const file = new File( [ 'data' ], 'photo.heic', { type: 'image/heic', @@ -102,17 +107,19 @@ describe( 'canvasConvertToJpeg', () => { const jpegBlob = new Blob( [ 'jpeg-data' ], { type: 'image/jpeg', } ); - const mockBitmap = { width: 10, height: 10, close: jest.fn() }; + const mockBitmap = { width: 10, height: 10, close: vi.fn() }; - global.createImageBitmap = jest + global.createImageBitmap = vi.fn().mockResolvedValue( mockBitmap ); + global.OffscreenCanvas = vi .fn() - .mockResolvedValue( mockBitmap ); - global.OffscreenCanvas = jest.fn().mockImplementation( () => ( { - getContext: jest - .fn() - .mockReturnValue( { drawImage: jest.fn() } ), - convertToBlob: jest.fn().mockResolvedValue( jpegBlob ), - } ) ); + .mockImplementation( function OffscreenCanvas() { + return { + getContext: vi + .fn() + .mockReturnValue( { drawImage: vi.fn() } ), + convertToBlob: vi.fn().mockResolvedValue( jpegBlob ), + }; + } ); const file = new File( [ 'data' ], 'my-photo.HEIC', { type: 'image/heic', @@ -122,15 +129,17 @@ describe( 'canvasConvertToJpeg', () => { } ); it( 'should close the bitmap even if canvas context fails', async () => { - const mockBitmap = { width: 10, height: 10, close: jest.fn() }; + const mockBitmap = { width: 10, height: 10, close: vi.fn() }; - global.createImageBitmap = jest + global.createImageBitmap = vi.fn().mockResolvedValue( mockBitmap ); + global.OffscreenCanvas = vi .fn() - .mockResolvedValue( mockBitmap ); - global.OffscreenCanvas = jest.fn().mockImplementation( () => ( { - getContext: jest.fn().mockReturnValue( null ), - convertToBlob: jest.fn(), - } ) ); + .mockImplementation( function OffscreenCanvas() { + return { + getContext: vi.fn().mockReturnValue( null ), + convertToBlob: vi.fn(), + }; + } ); // Remove other decoders so it falls through to the final error. delete ( global as any ).ImageDecoder; @@ -150,7 +159,7 @@ describe( 'canvasConvertToJpeg', () => { describe( 'fallback behavior', () => { it( 'should throw when no strategy is available', async () => { // createImageBitmap throws (doesn't support HEIC). - global.createImageBitmap = jest + global.createImageBitmap = vi .fn() .mockRejectedValue( new Error( 'Unsupported format' ) ); // No ImageDecoder or VideoDecoder. @@ -168,13 +177,13 @@ describe( 'canvasConvertToJpeg', () => { it( 'should fall through Strategy 1 failure to subsequent strategies', async () => { // Strategy 1 fails. - global.createImageBitmap = jest + global.createImageBitmap = vi .fn() .mockRejectedValue( new Error( 'Unsupported' ) ); // Strategy 2: ImageDecoder not supported for this type. ( global as any ).ImageDecoder = { - isTypeSupported: jest.fn().mockResolvedValue( false ), + isTypeSupported: vi.fn().mockResolvedValue( false ), }; // No VideoDecoder. diff --git a/packages/upload-media/src/test/convert-blob-to-file.ts b/packages/upload-media/src/test/convert-blob-to-file.ts index b87d7395565999..b57003233bb57a 100644 --- a/packages/upload-media/src/test/convert-blob-to-file.ts +++ b/packages/upload-media/src/test/convert-blob-to-file.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { convertBlobToFile } from '../utils'; describe( 'convertBlobToFile', () => { diff --git a/packages/upload-media/src/test/error-messages.ts b/packages/upload-media/src/test/error-messages.ts index 260176767adede..a45e576c10f464 100644 --- a/packages/upload-media/src/test/error-messages.ts +++ b/packages/upload-media/src/test/error-messages.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getErrorMessage } from '../error-messages'; import { ErrorCode } from '../upload-error'; diff --git a/packages/upload-media/src/test/get-file-basename.ts b/packages/upload-media/src/test/get-file-basename.ts index bc3f6800341945..ce8957491b948e 100644 --- a/packages/upload-media/src/test/get-file-basename.ts +++ b/packages/upload-media/src/test/get-file-basename.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getFileBasename } from '../utils'; describe( 'getFileBasename', () => { diff --git a/packages/upload-media/src/test/get-file-extension.ts b/packages/upload-media/src/test/get-file-extension.ts index 12b403914a0fc4..95aa0d90a3f84f 100644 --- a/packages/upload-media/src/test/get-file-extension.ts +++ b/packages/upload-media/src/test/get-file-extension.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getFileExtension } from '../utils'; describe( 'getFileExtension', () => { diff --git a/packages/upload-media/src/test/get-file-name-from-url.ts b/packages/upload-media/src/test/get-file-name-from-url.ts index e5e0d5a43a4ccc..8ae1be5b091ed1 100644 --- a/packages/upload-media/src/test/get-file-name-from-url.ts +++ b/packages/upload-media/src/test/get-file-name-from-url.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getFileNameFromUrl } from '../utils'; describe( 'getFileNameFromUrl', () => { diff --git a/packages/upload-media/src/test/get-image-dimensions.ts b/packages/upload-media/src/test/get-image-dimensions.ts index aaf68a61793050..e0fcf58331fff9 100644 --- a/packages/upload-media/src/test/get-image-dimensions.ts +++ b/packages/upload-media/src/test/get-image-dimensions.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getImageDimensions } from '../get-image-dimensions'; /** diff --git a/packages/upload-media/src/test/get-mime-types-array.ts b/packages/upload-media/src/test/get-mime-types-array.ts index d845f5c2f7ced5..e57ac33d130b34 100644 --- a/packages/upload-media/src/test/get-mime-types-array.ts +++ b/packages/upload-media/src/test/get-mime-types-array.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getMimeTypesArray } from '../get-mime-types-array'; describe( 'getMimeTypesArray', () => { diff --git a/packages/upload-media/src/test/heic-parser.ts b/packages/upload-media/src/test/heic-parser.ts index 3e0f0d8f5c3cfe..449d67a039f31c 100644 --- a/packages/upload-media/src/test/heic-parser.ts +++ b/packages/upload-media/src/test/heic-parser.ts @@ -1,6 +1,8 @@ /* eslint-disable no-bitwise, jsdoc/require-param */ + import { readFileSync } from 'node:fs'; import { join } from 'node:path'; +import { describe, expect, it } from 'vitest'; import { parseHeic, reverseBits32, @@ -63,6 +65,13 @@ function concat( ...arrays: Uint8Array[] ): Uint8Array { return result; } +/** Copy bytes into an ArrayBuffer with an exact, non-shared backing store. */ +function toArrayBuffer( bytes: Uint8Array ): ArrayBuffer { + const buffer = new ArrayBuffer( bytes.byteLength ); + new Uint8Array( buffer ).set( bytes ); + return buffer; +} + /** Build a pitm box (Primary Item). */ function buildPitm( primaryItemId: number ): Uint8Array { const data = new Uint8Array( 2 ); @@ -279,7 +288,7 @@ function buildSingleImageHeic( { // Assemble full file const file = concat( ftyp, meta, mdat ); - return file.buffer; + return toArrayBuffer( file ); } // --------------------------------------------------------------------------- @@ -512,7 +521,7 @@ describe( 'heic-parser', () => { ); const mdat = buildBox( 'mdat', mdatPayload ); - return concat( ftyp, meta, mdat ).buffer; + return toArrayBuffer( concat( ftyp, meta, mdat ) ); } /** Build an iinf box with infe entries. */ @@ -658,7 +667,7 @@ describe( 'heic-parser', () => { 'ftyp', new Uint8Array( [ 0x68, 0x65, 0x69, 0x63 ] ) ); - expect( () => parseHeic( ftyp.buffer ) ).toThrow( + expect( () => parseHeic( toArrayBuffer( ftyp ) ) ).toThrow( 'No meta box found' ); } ); @@ -667,7 +676,7 @@ describe( 'heic-parser', () => { // meta box with only hdlr (no pitm, iloc, iprp) const hdlr = buildHdlr(); const meta = buildFullBox( 'meta', 0, 0, hdlr ); - expect( () => parseHeic( meta.buffer ) ).toThrow( + expect( () => parseHeic( toArrayBuffer( meta ) ) ).toThrow( 'Missing required boxes' ); } ); @@ -684,7 +693,7 @@ describe( 'heic-parser', () => { 0, concat( hdlr, pitm, iloc, emptyIprp ) ); - expect( () => parseHeic( meta.buffer ) ).toThrow( + expect( () => parseHeic( toArrayBuffer( meta ) ) ).toThrow( 'Missing ipco or ipma' ); } ); @@ -705,7 +714,7 @@ describe( 'heic-parser', () => { 0, concat( hdlr, pitm, iloc, iprp ) ); - expect( () => parseHeic( meta.buffer ) ).toThrow( + expect( () => parseHeic( toArrayBuffer( meta ) ) ).toThrow( 'No location data for primary item' ); } ); @@ -726,7 +735,7 @@ describe( 'heic-parser', () => { 0, concat( hdlr, pitm, iloc, iprp ) ); - expect( () => parseHeic( meta.buffer ) ).toThrow( + expect( () => parseHeic( toArrayBuffer( meta ) ) ).toThrow( 'No property associations' ); } ); @@ -819,7 +828,7 @@ describe( 'parseExifOrientation / getUnappliedExifOrientation', () => { ] ); // Optional native transform property associated with the primary item. - let iprp = new Uint8Array( 0 ); + let iprp: Uint8Array< ArrayBufferLike > = new Uint8Array( 0 ); if ( withIrot ) { const ipco = buildIpco( buildIspe( 10, 10 ), buildIrot( 1 ) ); const ipma = buildIpma( [ [ primaryItemId, [ 1, 2 ] ] ] ); @@ -845,7 +854,7 @@ describe( 'parseExifOrientation / getUnappliedExifOrientation', () => { const meta = buildMeta( exifOffset ); const mdat = buildBox( 'mdat', exifPayload ); - return concat( ftyp, meta, mdat ).buffer; + return toArrayBuffer( concat( ftyp, meta, mdat ) ); } describe( 'parseExifOrientation', () => { diff --git a/packages/upload-media/src/test/is-animated-gif.ts b/packages/upload-media/src/test/is-animated-gif.ts index 34483b99eaecc7..db2b620f3fdcba 100644 --- a/packages/upload-media/src/test/is-animated-gif.ts +++ b/packages/upload-media/src/test/is-animated-gif.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isAnimatedGif } from '../utils'; /** diff --git a/packages/upload-media/src/test/rename-and-clone-file.ts b/packages/upload-media/src/test/rename-and-clone-file.ts index 154b999fa2b9ef..3149cd58b31d72 100644 --- a/packages/upload-media/src/test/rename-and-clone-file.ts +++ b/packages/upload-media/src/test/rename-and-clone-file.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { renameFile, cloneFile } from '../utils'; describe( 'renameFile', () => { diff --git a/packages/upload-media/src/test/upload-error.ts b/packages/upload-media/src/test/upload-error.ts index 3939233fad57b3..807ae57712cf77 100644 --- a/packages/upload-media/src/test/upload-error.ts +++ b/packages/upload-media/src/test/upload-error.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { UploadError } from '../upload-error'; describe( 'UploadError', () => { diff --git a/packages/url/package.json b/packages/url/package.json index 3dcddbd4f800c2..1c26e5345c260b 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -45,6 +45,9 @@ "dependencies": { "remove-accents": "^0.5.0" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" } diff --git a/packages/video-conversion/package.json b/packages/video-conversion/package.json index e043a4e41b54dd..4920ad8bfdc6c2 100644 --- a/packages/video-conversion/package.json +++ b/packages/video-conversion/package.json @@ -58,7 +58,8 @@ "mediabunny": "^1.45.2" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/video-conversion/src/test/index.ts b/packages/video-conversion/src/test/index.ts index c3a43ff2fd9fef..84b96dd4c0cb31 100644 --- a/packages/video-conversion/src/test/index.ts +++ b/packages/video-conversion/src/test/index.ts @@ -1,10 +1,4 @@ -import { - convertGifToVideo, - cancelOperations, - UNSUPPORTED_ERROR_PREFIX, - SIZE_LIMIT_ERROR_PREFIX, - DEFAULT_MAX_TOTAL_PIXELS, -} from '../index'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; // Configurable decoded-frame dimensions; the default (10x10) is even so the // canvas/resize path is skipped. Tests set odd values to exercise it. @@ -87,10 +81,12 @@ class FakeReplacementVideoFrame { const mockAddedSamples: Array< { init: { timestamp: number; duration: number }; } > = []; -const mockCanEncodeVideo = jest.fn().mockResolvedValue( true ); +const mockCanEncodeVideo = vi + .fn< ( ...args: unknown[] ) => Promise< boolean > >() + .mockResolvedValue( true ); // Default VideoSampleSource.add implementation; tests can override it, // e.g. to make it reject and exercise frame-cleanup error paths. -const mockSourceAdd = jest.fn( +const mockSourceAdd = vi.fn( async ( sample: { init: { timestamp: number; duration: number } } ) => { mockAddedSamples.push( sample ); } @@ -108,50 +104,65 @@ let mockVideoSamples: Array< { closed: boolean } > = []; // encoding configuration. let mockVideoSampleSourceOpts: Array< Record< string, unknown > > = []; -jest.mock( 'mediabunny', () => ( { - Output: class { - opts: { target: { buffer: ArrayBuffer | null } }; - constructor( opts: { target: { buffer: ArrayBuffer | null } } ) { - this.opts = opts; - } - addVideoTrack() {} - async start() {} - async finalize() { - this.opts.target.buffer = new Uint8Array( [ 1, 2, 3 ] ).buffer; - } - }, - BufferTarget: class { - buffer: ArrayBuffer | null = null; - }, - Mp4OutputFormat: class {}, - WebMOutputFormat: class {}, - VideoSampleSource: class { - constructor( opts: Record< string, unknown > ) { - mockVideoSampleSourceOpts.push( opts ); - } - add( sample: { init: { timestamp: number; duration: number } } ) { - return mockSourceAdd( sample ); - } - }, - VideoSample: class { - frame: unknown; - init: { timestamp: number; duration: number }; - closed = false; - constructor( - frame: unknown, - init: { timestamp: number; duration: number } - ) { - this.frame = frame; - this.init = init; - mockVideoSamples.push( this ); - } - close() { - this.closed = true; - } - }, - QUALITY_HIGH: 'quality-high', - canEncodeVideo: ( ...args: unknown[] ) => mockCanEncodeVideo( ...args ), -} ) ); +vi.doMock( import( 'mediabunny' ), async ( importOriginal ) => { + const original = await importOriginal(); + + return { + ...original, + Output: class { + opts: { target: { buffer: ArrayBuffer | null } }; + constructor( opts: { target: { buffer: ArrayBuffer | null } } ) { + this.opts = opts; + } + addVideoTrack() {} + async start() {} + async finalize() { + this.opts.target.buffer = new Uint8Array( [ 1, 2, 3 ] ).buffer; + } + } as unknown as typeof original.Output, + BufferTarget: class { + buffer: ArrayBuffer | null = null; + } as unknown as typeof original.BufferTarget, + Mp4OutputFormat: class {} as unknown as typeof original.Mp4OutputFormat, + WebMOutputFormat: + class {} as unknown as typeof original.WebMOutputFormat, + VideoSampleSource: class { + constructor( opts: Record< string, unknown > ) { + mockVideoSampleSourceOpts.push( opts ); + } + add( sample: { init: { timestamp: number; duration: number } } ) { + return mockSourceAdd( sample ); + } + } as unknown as typeof original.VideoSampleSource, + VideoSample: class { + frame: unknown; + init: { timestamp: number; duration: number }; + closed = false; + constructor( + frame: unknown, + init: { timestamp: number; duration: number } + ) { + this.frame = frame; + this.init = init; + mockVideoSamples.push( this ); + } + close() { + this.closed = true; + } + } as unknown as typeof original.VideoSample, + QUALITY_HIGH: 'quality-high' as unknown as typeof original.QUALITY_HIGH, + canEncodeVideo: + mockCanEncodeVideo as unknown as typeof original.canEncodeVideo, + }; +} ); + +const { + convertGifToVideo, + cancelOperations, + UNSUPPORTED_ERROR_PREFIX, + SIZE_LIMIT_ERROR_PREFIX, + DEFAULT_MAX_TOTAL_PIXELS, +} = await import( '../index' ); beforeEach( () => { mockAddedSamples.length = 0; diff --git a/packages/viewport/package.json b/packages/viewport/package.json index 8a8a86508b192d..4f331927b7fa64 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -49,7 +49,8 @@ "devDependencies": { "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", - "deep-freeze": "^0.0.1" + "deep-freeze": "^0.0.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/viewport/src/store/test/reducer.js b/packages/viewport/src/store/test/reducer.js index b716bd74251d64..41418cafea40e5 100644 --- a/packages/viewport/src/store/test/reducer.js +++ b/packages/viewport/src/store/test/reducer.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import deepFreeze from 'deep-freeze'; import reducer from '../reducer'; diff --git a/packages/viewport/src/store/test/selectors.js b/packages/viewport/src/store/test/selectors.js index c25800f5ebd78e..891208edf1583f 100644 --- a/packages/viewport/src/store/test/selectors.js +++ b/packages/viewport/src/store/test/selectors.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { isViewportMatch } from '../selectors'; describe( 'selectors', () => { diff --git a/packages/views/package.json b/packages/views/package.json index dea6dec1cada7b..52c2d68346488d 100644 --- a/packages/views/package.json +++ b/packages/views/package.json @@ -53,7 +53,8 @@ "@testing-library/react": "^16.3.2", "@types/jest": "^30.0.0", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/views/src/test/resolve-view.ts b/packages/views/src/test/resolve-view.ts index 9d1cdc8826fda2..f89155fcd1ddbe 100644 --- a/packages/views/src/test/resolve-view.ts +++ b/packages/views/src/test/resolve-view.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { View } from '@wordpress/dataviews'; import { getUserModifications, resolveView } from '../resolve-view'; diff --git a/packages/vips/package.json b/packages/vips/package.json index b4f637daad1eb1..d6c95572a7d2da 100644 --- a/packages/vips/package.json +++ b/packages/vips/package.json @@ -60,7 +60,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/vips/src/test/convert-image-format.ts b/packages/vips/src/test/convert-image-format.ts index ab785ad0d3e91d..8beb0dfc63f99c 100644 --- a/packages/vips/src/test/convert-image-format.ts +++ b/packages/vips/src/test/convert-image-format.ts @@ -1,34 +1,44 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import type VipsFactory from 'wasm-vips'; import { convertImageFormat, compressImage } from '../'; -const mockNewFromBuffer = jest.fn( () => new MockImage() ); -const mockWriteToBuffer = jest.fn( () => ( { - buffer: '', -} ) ); - -class MockImage { - width = 100; - height = 100; - pageHeight = 100; - writeToBuffer = mockWriteToBuffer; - getInt = jest.fn( () => 0 ); -} - -class MockVipsImage { - static newFromBuffer = mockNewFromBuffer; -} +const { MockVipsImage, mockNewFromBuffer } = vi.hoisted( () => { + const writeToBufferMock = vi.fn( () => ( { + buffer: '', + } ) ); + + class ImageMock { + width = 100; + height = 100; + pageHeight = 100; + writeToBuffer = writeToBufferMock; + getInt = vi.fn( () => 0 ); + } + + const newFromBufferMock = vi.fn( () => new ImageMock() ); + + class VipsImageMock { + static newFromBuffer = newFromBufferMock; + } + + return { + MockVipsImage: VipsImageMock, + mockNewFromBuffer: newFromBufferMock, + }; +} ); -jest.mock( 'wasm-vips', () => - jest.fn( () => ( { +vi.mock( import( 'wasm-vips' ), () => ( { + default: vi.fn( () => ( { Image: MockVipsImage, Cache: { - max: jest.fn(), + max: vi.fn(), }, - } ) ) -); + } ) ) as unknown as typeof VipsFactory, +} ) ); describe( 'convertImageFormat', () => { afterEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'loads only the first frame when converting a GIF to JPEG', async () => { diff --git a/packages/vips/src/test/has-transparency.ts b/packages/vips/src/test/has-transparency.ts index 89558e9f6206e5..94e9d9a74230d1 100644 --- a/packages/vips/src/test/has-transparency.ts +++ b/packages/vips/src/test/has-transparency.ts @@ -1,32 +1,52 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type VipsFactory from 'wasm-vips'; import { hasTransparency } from '../'; -const mockHasAlpha = jest.fn(); -const mockExtractBand = jest.fn(); -const mockMin = jest.fn(); - -class MockAlphaBand { - min = mockMin; -} - -class MockImage { - hasAlpha = mockHasAlpha; - extractBand = mockExtractBand; - bands = 4; - format = 'uchar'; -} - -const mockNewFromBuffer = jest.fn( () => new MockImage() ); +const { + MockAlphaBand, + MockImage, + mockExtractBand, + mockHasAlpha, + mockMin, + mockNewFromBuffer, +} = vi.hoisted( () => { + const hasAlphaMock = vi.fn(); + const extractBandMock = vi.fn(); + const minMock = vi.fn(); + + class AlphaBandMock { + min = minMock; + } + + class ImageMock { + hasAlpha = hasAlphaMock; + extractBand = extractBandMock; + bands = 4; + format = 'uchar'; + } + + const newFromBufferMock = vi.fn( () => new ImageMock() ); + + return { + MockAlphaBand: AlphaBandMock, + MockImage: ImageMock, + mockExtractBand: extractBandMock, + mockHasAlpha: hasAlphaMock, + mockMin: minMock, + mockNewFromBuffer: newFromBufferMock, + }; +} ); -jest.mock( 'wasm-vips', () => - jest.fn( () => ( { +vi.mock( import( 'wasm-vips' ), () => ( { + default: vi.fn( () => ( { Image: { newFromBuffer: mockNewFromBuffer, }, Cache: { - max: jest.fn(), + max: vi.fn(), }, - } ) ) -); + } ) ) as unknown as typeof VipsFactory, +} ) ); describe( 'hasTransparency', () => { beforeEach( () => { @@ -34,7 +54,7 @@ describe( 'hasTransparency', () => { } ); afterEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); it( 'returns false when the image has no alpha channel', async () => { diff --git a/packages/vips/src/test/highbitdepth-avif.ts b/packages/vips/src/test/highbitdepth-avif.ts index 7df5522814e25a..600d41b6d01b25 100644 --- a/packages/vips/src/test/highbitdepth-avif.ts +++ b/packages/vips/src/test/highbitdepth-avif.ts @@ -1,6 +1,8 @@ import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; -/** +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import Vips from 'wasm-vips'; + +/* * Loads the real `wasm-vips` build to exercise the actual AVIF decoder. * * Unlike the other tests in this package, this is an integration test: it does @@ -13,9 +15,16 @@ import { join } from 'node:path'; * AVIF images (kleisauke/wasm-vips#118). Earlier builds threw "error in tile" * decode failures on the same files. */ -const Vips = require( 'wasm-vips' ); -const FIXTURES = join( __dirname, 'fixtures' ); +/** + * Returns a fixture URL relative to this ESM test module. + * + * @param file Fixture filename. + * + * @return Fixture URL. + */ +const getFixtureUrl = ( file: string ) => + new URL( `./fixtures/${ file }`, import.meta.url ); describe( 'wasm-vips high-bit-depth AVIF decoding', () => { let vips: Awaited< ReturnType< typeof Vips > >; @@ -32,7 +41,7 @@ describe( 'wasm-vips high-bit-depth AVIF decoding', () => { [ '10-bit', 'highbitdepth-10bit.avif' ], [ '12-bit', 'highbitdepth-12bit.avif' ], ] )( 'decodes a %s AVIF image into a 16-bit image', ( _label, file ) => { - const buffer = readFileSync( join( FIXTURES, file ) ); + const buffer = readFileSync( getFixtureUrl( file ) ); const image = vips.Image.newFromBuffer( buffer ); @@ -47,7 +56,7 @@ describe( 'wasm-vips high-bit-depth AVIF decoding', () => { } ); it( 'decodes a standard 8-bit AVIF image into an 8-bit image', () => { - const buffer = readFileSync( join( FIXTURES, 'standard-8bit.avif' ) ); + const buffer = readFileSync( getFixtureUrl( 'standard-8bit.avif' ) ); const image = vips.Image.newFromBuffer( buffer ); @@ -77,7 +86,7 @@ describe( 'wasm-vips high-bit-depth AVIF decoding', () => { ] )( 'keeps a %s AVIF high-bit-depth through resize and re-encode', ( _label, file, depth ) => { - const buffer = readFileSync( join( FIXTURES, file ) ); + const buffer = readFileSync( getFixtureUrl( file ) ); // `thumbnail` flattens samples to 8-bit sRGB... const flattened = vips.Image.thumbnailBuffer( buffer, 32, { @@ -118,7 +127,7 @@ describe( 'wasm-vips high-bit-depth AVIF decoding', () => { // would be inflated to 12-bit (larger, not "similar") unless the // original depth is written explicitly. const buffer = readFileSync( - join( FIXTURES, 'highbitdepth-10bit.avif' ) + getFixtureUrl( 'highbitdepth-10bit.avif' ) ); const resized = vips.Image.newFromBuffer( buffer ).resize( 0.5 ); diff --git a/packages/vips/src/test/resize-image.ts b/packages/vips/src/test/resize-image.ts index 77d7f300ca9274..8fcd444850cd4f 100644 --- a/packages/vips/src/test/resize-image.ts +++ b/packages/vips/src/test/resize-image.ts @@ -1,48 +1,66 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import type VipsFactory from 'wasm-vips'; import { resizeImage } from '../'; import type { ImageSizeCrop } from '../types'; -const mockThumbnailBuffer = jest.fn( () => new MockImage() ); -const mockCrop = jest.fn( () => new MockImage() ); -const mockResize = jest.fn( () => new MockImage() ); -const mockNewFromBuffer = jest.fn( () => new MockImage() ); -const mockWriteToBuffer = jest.fn( () => ( { - buffer: '', -} ) ); +const { + MockVipsImage, + mockCrop, + mockResize, + mockState, + mockThumbnailBuffer, + mockWriteToBuffer, +} = vi.hoisted( () => { + const state = { bitdepth: 8 }; + const writeToBufferMock = vi.fn( () => ( { + buffer: '', + } ) ); + + class ImageMock { + width = 100; + height = 100; + pageHeight = 100; + crop = cropMock; + resize = resizeMock; + writeToBuffer = writeToBufferMock; + getInt = vi.fn( ( name: string ) => + 'heif-bitdepth' === name ? state.bitdepth : 0 + ); + } + + const thumbnailBufferMock = vi.fn( () => new ImageMock() ); + const cropMock = vi.fn( () => new ImageMock() ); + const resizeMock = vi.fn( () => new ImageMock() ); + const newFromBufferMock = vi.fn( () => new ImageMock() ); + + class VipsImageMock { + static thumbnailBuffer = thumbnailBufferMock; + static newFromBuffer = newFromBufferMock; + } + + return { + MockVipsImage: VipsImageMock, + mockCrop: cropMock, + mockResize: resizeMock, + mockState: state, + mockThumbnailBuffer: thumbnailBufferMock, + mockWriteToBuffer: writeToBufferMock, + }; +} ); -// Controls the `heif-bitdepth` value reported by the mocked source image so -// tests can exercise both the standard (8-bit) and high-bit-depth code paths. -let mockBitdepth = 8; - -class MockImage { - width = 100; - height = 100; - pageHeight = 100; - crop = mockCrop; - resize = mockResize; - writeToBuffer = mockWriteToBuffer; - getInt = jest.fn( ( name: string ) => - 'heif-bitdepth' === name ? mockBitdepth : 0 - ); -} - -class MockVipsImage { - static thumbnailBuffer = mockThumbnailBuffer; - static newFromBuffer = mockNewFromBuffer; -} - -jest.mock( 'wasm-vips', () => - jest.fn( () => ( { +vi.mock( import( 'wasm-vips' ), () => ( { + default: vi.fn( () => ( { Image: MockVipsImage, Cache: { - max: jest.fn(), + max: vi.fn(), }, - } ) ) -); + } ) ) as unknown as typeof VipsFactory, +} ) ); describe( 'resizeImage', () => { afterEach( () => { - jest.clearAllMocks(); - mockBitdepth = 8; + vi.clearAllMocks(); + mockState.bitdepth = 8; } ); it( 'resizes without crop', async () => { @@ -259,7 +277,7 @@ describe( 'resizeImage', () => { describe( 'high-bit-depth AVIF', () => { it( 'preserves bit depth when resizing a 10-bit AVIF without crop', async () => { - mockBitdepth = 10; + mockState.bitdepth = 10; const avifFile = new File( [ '' ], 'example.avif', { type: 'image/avif', } ); @@ -282,7 +300,7 @@ describe( 'resizeImage', () => { } ); it( 'centre-crops a 12-bit AVIF while preserving bit depth', async () => { - mockBitdepth = 12; + mockState.bitdepth = 12; const avifFile = new File( [ '' ], 'example.avif', { type: 'image/avif', } ); @@ -304,7 +322,7 @@ describe( 'resizeImage', () => { } ); it( 'crops a 10-bit AVIF to a position while preserving bit depth', async () => { - mockBitdepth = 10; + mockState.bitdepth = 10; const avifFile = new File( [ '' ], 'example.avif', { type: 'image/avif', } ); @@ -328,7 +346,7 @@ describe( 'resizeImage', () => { } ); it( 'uses the standard thumbnail path for an 8-bit AVIF', async () => { - mockBitdepth = 8; + mockState.bitdepth = 8; const avifFile = new File( [ '' ], 'example.avif', { type: 'image/avif', } ); @@ -392,7 +410,7 @@ describe( 'resizeImage', () => { describe( 'image_max_bit_depth', () => { it( 'caps a 12-bit AVIF at 10-bit', async () => { - mockBitdepth = 12; + mockState.bitdepth = 12; const avifFile = new File( [ '' ], 'example.avif', { type: 'image/avif', } ); @@ -419,7 +437,7 @@ describe( 'resizeImage', () => { } ); it( 'snaps an unsupported cap down to the nearest valid depth', async () => { - mockBitdepth = 12; + mockState.bitdepth = 12; const avifFile = new File( [ '' ], 'example.avif', { type: 'image/avif', } ); @@ -443,7 +461,7 @@ describe( 'resizeImage', () => { } ); it( 'flattens a 10-bit AVIF via the thumbnail path when capped at 8-bit', async () => { - mockBitdepth = 10; + mockState.bitdepth = 10; const avifFile = new File( [ '' ], 'example.avif', { type: 'image/avif', } ); @@ -471,7 +489,7 @@ describe( 'resizeImage', () => { } ); it( 'ignores the cap for sources at or below it', async () => { - mockBitdepth = 10; + mockState.bitdepth = 10; const avifFile = new File( [ '' ], 'example.avif', { type: 'image/avif', } ); diff --git a/packages/vips/src/test/rotate-image-integration.ts b/packages/vips/src/test/rotate-image-integration.ts index 451a25646f23fe..d60da0f8292c26 100644 --- a/packages/vips/src/test/rotate-image-integration.ts +++ b/packages/vips/src/test/rotate-image-integration.ts @@ -1,5 +1,6 @@ import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; +import type VipsFactory from 'wasm-vips'; import { rotateImage } from '../'; /** @@ -28,17 +29,31 @@ import { rotateImage } from '../'; * See https://github.com/WordPress/gutenberg/issues/79383. */ -jest.mock( 'wasm-vips', () => { - const RealVips = jest.requireActual( 'wasm-vips' ); - return jest.fn( ( options: Record< string, unknown > = {} ) => - RealVips( { dynamicLibraries: options.dynamicLibraries } ) - ); +vi.mock( import( 'wasm-vips' ), async ( importOriginal ) => { + const original = await importOriginal(); + + return { + ...original, + default: vi.fn( ( options: { dynamicLibraries?: string[] } = {} ) => + original.default( { + dynamicLibraries: options.dynamicLibraries, + } ) + ) as unknown as typeof original.default, + }; } ); -const FIXTURES = join( __dirname, 'fixtures' ); +/** + * Returns a fixture URL relative to this ESM test module. + * + * @param file Fixture filename. + * + * @return Fixture URL. + */ +const getFixtureUrl = ( file: string ) => + new URL( `./fixtures/${ file }`, import.meta.url ); const loadFixture = ( file: string ): ArrayBuffer => { - const contents = readFileSync( join( FIXTURES, file ) ); + const contents = readFileSync( getFixtureUrl( file ) ); return contents.buffer.slice( contents.byteOffset, contents.byteOffset + contents.byteLength @@ -50,10 +65,12 @@ const isRed = ( [ r, , b ]: number[] ) => r > 128 && r > b; const isBlue = ( [ r, , b ]: number[] ) => b > 128 && b > r; describe( 'rotateImage EXIF orientation fixtures', () => { - let vips: any; + let vips: Awaited< ReturnType< typeof VipsFactory > >; beforeAll( async () => { - const Vips = jest.requireActual( 'wasm-vips' ); + const { default: Vips } = await vi.importActual< { + default: typeof VipsFactory; + } >( 'wasm-vips' ); vips = await Vips( { dynamicLibraries: [ 'vips-heif.wasm' ] } ); } ); @@ -116,7 +133,9 @@ describe( 'rotateImage EXIF orientation fixtures', () => { expect( result.width ).toBe( width ); expect( result.height ).toBe( height ); - const rotated = vips.Image.newFromBuffer( result.buffer ); + const rotated = vips.Image.newFromBuffer( + result.buffer as ArrayBuffer + ); expect( rotated.width ).toBe( width ); expect( rotated.height ).toBe( height ); @@ -176,7 +195,9 @@ describe( 'rotateImage EXIF orientation fixtures', () => { 6 ); - const rotated = vips.Image.newFromBuffer( result.buffer ); + const rotated = vips.Image.newFromBuffer( + result.buffer as ArrayBuffer + ); expect( reportedOrientation( rotated ) ).toBe( 1 ); rotated.delete(); } diff --git a/packages/vips/src/test/rotate-image.ts b/packages/vips/src/test/rotate-image.ts index 7a9c426ac07aa7..6477ddc63f3632 100644 --- a/packages/vips/src/test/rotate-image.ts +++ b/packages/vips/src/test/rotate-image.ts @@ -1,3 +1,5 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import type VipsFactory from 'wasm-vips'; import { rotateImage } from '../'; /** @@ -7,66 +9,72 @@ import { rotateImage } from '../'; * returns the same image instance so chained calls (e.g. `flipHor().rot90()`) * are captured in order. */ -let calls: string[]; -let removed: string[]; +const { MockVipsImage, mockState } = vi.hoisted( () => { + const state = { + calls: [] as string[], + removed: [] as string[], + }; -class MockImage { - width = 100; - height = 100; - pageHeight = 100; - onProgress = () => {}; - kill = false; + class ImageMock { + width = 100; + height = 100; + pageHeight = 100; + onProgress = () => {}; + kill = false; - flipHor = jest.fn( () => { - calls.push( 'flipHor' ); - return this; - } ); - flipVer = jest.fn( () => { - calls.push( 'flipVer' ); - return this; - } ); - rot90 = jest.fn( () => { - calls.push( 'rot90' ); - return this; - } ); - rot180 = jest.fn( () => { - calls.push( 'rot180' ); - return this; - } ); - rot270 = jest.fn( () => { - calls.push( 'rot270' ); - return this; - } ); - remove = jest.fn( ( field: string ) => { - removed.push( field ); - return true; - } ); - writeToBuffer = jest.fn( () => ( { - buffer: new ArrayBuffer( 0 ), - } ) ); -} + flipHor = vi.fn( () => { + state.calls.push( 'flipHor' ); + return this; + } ); + flipVer = vi.fn( () => { + state.calls.push( 'flipVer' ); + return this; + } ); + rot90 = vi.fn( () => { + state.calls.push( 'rot90' ); + return this; + } ); + rot180 = vi.fn( () => { + state.calls.push( 'rot180' ); + return this; + } ); + rot270 = vi.fn( () => { + state.calls.push( 'rot270' ); + return this; + } ); + remove = vi.fn( ( field: string ) => { + state.removed.push( field ); + return true; + } ); + writeToBuffer = vi.fn( () => ( { + buffer: new ArrayBuffer( 0 ), + } ) ); + } + + class VipsImageMock { + static newFromBuffer = vi.fn( () => new ImageMock() ); + } -class MockVipsImage { - static newFromBuffer = jest.fn( () => new MockImage() ); -} + return { MockVipsImage: VipsImageMock, mockState: state }; +} ); -jest.mock( 'wasm-vips', () => - jest.fn( () => ( { +vi.mock( import( 'wasm-vips' ), () => ( { + default: vi.fn( () => ( { Image: MockVipsImage, Cache: { - max: jest.fn(), + max: vi.fn(), }, - } ) ) -); + } ) ) as unknown as typeof VipsFactory, +} ) ); describe( 'rotateImage', () => { beforeEach( () => { - calls = []; - removed = []; + mockState.calls = []; + mockState.removed = []; } ); afterEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); async function rotate( orientation: number ) { @@ -95,13 +103,13 @@ describe( 'rotateImage', () => { async ( orientation, expected ) => { await rotate( orientation ); - expect( calls ).toEqual( expected ); + expect( mockState.calls ).toEqual( expected ); } ); it( 'strips the EXIF orientation tag after rotating', async () => { await rotate( 6 ); - expect( removed ).toContain( 'orientation' ); + expect( mockState.removed ).toContain( 'orientation' ); } ); } ); diff --git a/packages/vips/src/test/ultrahdr.ts b/packages/vips/src/test/ultrahdr.ts index c8540900c07924..7ea5eb3e5a74e3 100644 --- a/packages/vips/src/test/ultrahdr.ts +++ b/packages/vips/src/test/ultrahdr.ts @@ -1,48 +1,74 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import type VipsFactory from 'wasm-vips'; import { getUltraHdrInfo, resizeImage } from '../'; -const mockThumbnailImage = jest.fn(); -const mockThumbnailBuffer = jest.fn(); -const mockNewFromBuffer = jest.fn(); -const mockUhdrLoadBuffer = jest.fn(); -const mockWriteToBuffer = jest.fn( () => ( { buffer: new ArrayBuffer( 0 ) } ) ); -const mockGetDouble = jest.fn(); -const mockSetImage = jest.fn(); -const mockCrop = jest.fn(); - -class MockImage { - width = 100; - height = 100; - pageHeight = 100; - gainmap: MockImage | undefined; - thumbnailImage = mockThumbnailImage.mockImplementation( () => this ); - writeToBuffer = mockWriteToBuffer; - getDouble = mockGetDouble; - crop = mockCrop.mockImplementation( () => this ); - copy = jest.fn( () => this ); - setImage = mockSetImage; -} - -class MockVipsImage { - static thumbnailBuffer = mockThumbnailBuffer.mockImplementation( - () => new MockImage() - ); - static newFromBuffer = mockNewFromBuffer.mockImplementation( - () => new MockImage() - ); - static uhdrloadBuffer = mockUhdrLoadBuffer; -} - -jest.mock( 'wasm-vips', () => - jest.fn( () => ( { +const { + MockImage, + MockVipsImage, + mockGetDouble, + mockNewFromBuffer, + mockSetImage, + mockThumbnailBuffer, + mockUhdrLoadBuffer, + mockWriteToBuffer, +} = vi.hoisted( () => { + const thumbnailImageMock = vi.fn(); + const thumbnailBufferMock = vi.fn(); + const newFromBufferMock = vi.fn(); + const uhdrLoadBufferMock = vi.fn(); + const writeToBufferMock = vi.fn( () => ( { + buffer: new ArrayBuffer( 0 ), + } ) ); + const getDoubleMock = vi.fn(); + const setImageMock = vi.fn(); + const cropMock = vi.fn(); + + class ImageMock { + width = 100; + height = 100; + pageHeight = 100; + gainmap: ImageMock | undefined; + thumbnailImage = thumbnailImageMock.mockImplementation( () => this ); + writeToBuffer = writeToBufferMock; + getDouble = getDoubleMock; + crop = cropMock.mockImplementation( () => this ); + copy = vi.fn( () => this ); + setImage = setImageMock; + } + + class VipsImageMock { + static thumbnailBuffer = thumbnailBufferMock.mockImplementation( + () => new ImageMock() + ); + static newFromBuffer = newFromBufferMock.mockImplementation( + () => new ImageMock() + ); + static uhdrloadBuffer = uhdrLoadBufferMock; + } + + return { + MockImage: ImageMock, + MockVipsImage: VipsImageMock, + mockGetDouble: getDoubleMock, + mockNewFromBuffer: newFromBufferMock, + mockSetImage: setImageMock, + mockThumbnailBuffer: thumbnailBufferMock, + mockUhdrLoadBuffer: uhdrLoadBufferMock, + mockWriteToBuffer: writeToBufferMock, + }; +} ); + +vi.mock( import( 'wasm-vips' ), () => ( { + default: vi.fn( () => ( { Image: MockVipsImage, // getVips() calls Cache.max(0) to disable libvips's operation cache. - Cache: { max: jest.fn() }, - } ) ) -); + Cache: { max: vi.fn() }, + } ) ) as unknown as typeof VipsFactory, +} ) ); describe( 'UltraHDR helpers', () => { afterEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); } ); describe( 'getUltraHdrInfo', () => { diff --git a/packages/warning/package.json b/packages/warning/package.json index dbaed9554ece59..530c76ce4d9eeb 100644 --- a/packages/warning/package.json +++ b/packages/warning/package.json @@ -46,7 +46,8 @@ "types": "build-types", "sideEffects": false, "devDependencies": { - "@babel/core": "^7.25.7" + "@babel/core": "^7.25.7", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/warning/src/test/index.js b/packages/warning/src/test/index.js index f53e92957c1c07..76c99cb1384c98 100644 --- a/packages/warning/src/test/index.js +++ b/packages/warning/src/test/index.js @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it } from 'vitest'; import warning from '..'; import { logged } from '../utils'; diff --git a/packages/warning/test/babel-plugin.js b/packages/warning/test/babel-plugin.js index d09a7f8b1ec078..a7ccea57821d75 100644 --- a/packages/warning/test/babel-plugin.js +++ b/packages/warning/test/babel-plugin.js @@ -1,5 +1,6 @@ import { transform } from '@babel/core'; -import babelPlugin from '../babel-plugin'; +import { describe, expect, it } from 'vitest'; +import babelPlugin from '../babel-plugin.cjs'; function join( ...strings ) { return strings.join( '\n' ); diff --git a/packages/widget-dashboard/package.json b/packages/widget-dashboard/package.json index 611d5291e637f2..d0ac1cc1154809 100644 --- a/packages/widget-dashboard/package.json +++ b/packages/widget-dashboard/package.json @@ -71,9 +71,9 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", "@wordpress/admin-ui": "file:../admin-ui", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/widget-dashboard/src/test/create-dashboard-widget.test.ts b/packages/widget-dashboard/src/test/create-dashboard-widget.test.ts index 9dabcf1b664918..c1cb435ba59caa 100644 --- a/packages/widget-dashboard/src/test/create-dashboard-widget.test.ts +++ b/packages/widget-dashboard/src/test/create-dashboard-widget.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { WidgetType } from '@wordpress/widget-primitives'; import { createDashboardWidget } from '../utils/create-dashboard-widget'; diff --git a/packages/widget-dashboard/src/utils/resolve-dashboard-column-count/test/resolve-dashboard-column-count.test.ts b/packages/widget-dashboard/src/utils/resolve-dashboard-column-count/test/resolve-dashboard-column-count.test.ts index 670ebe18789be3..021164fcf30b08 100644 --- a/packages/widget-dashboard/src/utils/resolve-dashboard-column-count/test/resolve-dashboard-column-count.test.ts +++ b/packages/widget-dashboard/src/utils/resolve-dashboard-column-count/test/resolve-dashboard-column-count.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { resolveDashboardColumnCap, resolveDashboardColumnCount, diff --git a/packages/widget-primitives/package.json b/packages/widget-primitives/package.json index fab26298eb1e70..e0dfb6fa444d9a 100644 --- a/packages/widget-primitives/package.json +++ b/packages/widget-primitives/package.json @@ -56,7 +56,8 @@ "@wordpress/components": "file:../components", "@wordpress/icons": "file:../icons", "@wordpress/ui": "file:../ui", - "storybook": "^10.5.3" + "storybook": "^10.5.3", + "vitest": "^4.1.10" }, "peerDependencies": { "@types/react": "^18 || ^19", diff --git a/packages/widget-primitives/src/field-types/test/field-types.ts b/packages/widget-primitives/src/field-types/test/field-types.ts index 4767ae72927e02..6677b9e81aed88 100644 --- a/packages/widget-primitives/src/field-types/test/field-types.ts +++ b/packages/widget-primitives/src/field-types/test/field-types.ts @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it } from 'vitest'; import { getFieldType, registerFieldType, diff --git a/packages/widget-primitives/src/hooks/test/fixtures/string-icon-module.mjs b/packages/widget-primitives/src/hooks/test/fixtures/string-icon-module.mjs new file mode 100644 index 00000000000000..481df5fc22e00c --- /dev/null +++ b/packages/widget-primitives/src/hooks/test/fixtures/string-icon-module.mjs @@ -0,0 +1,12 @@ +export default { + title: 'String icon', + icon: 'wordpress', + actions: [ + { + id: 'module-docs', + label: 'Docs', + href: 'https://example.com/docs', + icon: 'wordpress', + }, + ], +}; diff --git a/packages/widget-primitives/src/hooks/test/fixtures/widget-module.mjs b/packages/widget-primitives/src/hooks/test/fixtures/widget-module.mjs new file mode 100644 index 00000000000000..cbb30ff22d1881 --- /dev/null +++ b/packages/widget-primitives/src/hooks/test/fixtures/widget-module.mjs @@ -0,0 +1,27 @@ +import { createElement } from '@wordpress/element'; + +export const moduleIcon = createElement( 'svg', { + viewBox: '0 0 24 24', +} ); + +export default { + apiVersion: 1, + title: 'Store', + icon: moduleIcon, + attributes: [ + { + id: 'location', + label: 'Location', + type: 'test/location', + }, + { id: 'label', label: 'Label', type: 'text' }, + ], + actions: [ + { + id: 'module-action', + label: 'Module action', + href: 'https://example.com/module', + icon: moduleIcon, + }, + ], +}; diff --git a/packages/widget-primitives/src/icon-resolver/test/icon-resolver.ts b/packages/widget-primitives/src/icon-resolver/test/icon-resolver.ts index 267b2d35a02674..415a2e8d4885ca 100644 --- a/packages/widget-primitives/src/icon-resolver/test/icon-resolver.ts +++ b/packages/widget-primitives/src/icon-resolver/test/icon-resolver.ts @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; import { createElement } from '@wordpress/element'; import { registerIconResolver, @@ -18,7 +19,7 @@ describe( 'icon resolver registry', () => { } ); it( 'resolves references through the registered resolver', async () => { - const resolver = jest.fn( async () => icon ); + const resolver = vi.fn( async () => icon ); expect( registerIconResolver( resolver ) ).toBe( resolver ); await expect( resolveIcon( 'core/calendar' ) ).resolves.toBe( icon ); @@ -26,8 +27,8 @@ describe( 'icon resolver registry', () => { } ); it( 'keeps the first registered resolver', async () => { - const first = jest.fn( async () => icon ); - const second = jest.fn( async () => null ); + const first = vi.fn( async () => icon ); + const second = vi.fn( async () => null ); registerIconResolver( first ); expect( registerIconResolver( second ) ).toBeUndefined(); @@ -43,7 +44,7 @@ describe( 'icon resolver registry', () => { } ); it( 'unregisters the resolver', async () => { - const resolver = jest.fn( async () => icon ); + const resolver = vi.fn( async () => icon ); registerIconResolver( resolver ); expect( unregisterIconResolver() ).toBe( resolver ); diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index c5ddc3f21f90d7..d35151ddeaeba5 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -43,7 +43,8 @@ "types": "build-types", "sideEffects": false, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/wordcount/src/test/index.test.ts b/packages/wordcount/src/test/index.test.ts index b255898389176b..4dd50254afb055 100644 --- a/packages/wordcount/src/test/index.test.ts +++ b/packages/wordcount/src/test/index.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { count } from '../'; import type { UserSettings, Strategy } from '../types'; diff --git a/packages/worker-threads/package.json b/packages/worker-threads/package.json index c1fc4da30490c0..e9c891f53e3caf 100644 --- a/packages/worker-threads/package.json +++ b/packages/worker-threads/package.json @@ -52,7 +52,8 @@ "comctx": "^1.4.3" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/packages/worker-threads/src/test/error-propagation.test.ts b/packages/worker-threads/src/test/error-propagation.test.ts index dc3f198d32d59a..f8399c16fa8bf1 100644 --- a/packages/worker-threads/src/test/error-propagation.test.ts +++ b/packages/worker-threads/src/test/error-propagation.test.ts @@ -8,22 +8,25 @@ * a rejection. See https://github.com/WordPress/gutenberg/issues/80259. */ -/* - * Scope the mock globals to this file; test files without imports would - * otherwise share one global scope and collide. - */ -// eslint-disable-next-line jest/no-export -export {}; +import { + afterEach, + beforeEach, + describe, + expect, + it, + vi, + type Mock, +} from 'vitest'; // Store the original self. const originalSelf = globalThis.self; // Mock self for worker context. -let mockPostMessage: jest.Mock; +let mockPostMessage: Mock; let messageListeners: Array< ( event: MessageEvent ) => void > = []; function setupMockSelf() { - mockPostMessage = jest.fn(); + mockPostMessage = vi.fn(); messageListeners = []; // Override self with addEventListener pattern (matching comctx usage). @@ -111,7 +114,7 @@ async function callExposedMethod( describe( 'worker-thread error propagation', () => { beforeEach( () => { setupMockSelf(); - jest.resetModules(); + vi.resetModules(); } ); afterEach( () => { diff --git a/packages/worker-threads/src/test/main-thread.test.ts b/packages/worker-threads/src/test/main-thread.test.ts index b9ca8620eec3db..cb1ac5e6d8a425 100644 --- a/packages/worker-threads/src/test/main-thread.test.ts +++ b/packages/worker-threads/src/test/main-thread.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { wrap, terminate } from '../main-thread'; import { WORKER_SYMBOL } from '../types'; @@ -7,8 +8,8 @@ import { WORKER_SYMBOL } from '../types'; * error/messageerror events used for worker failure detection. */ class MockWorker { - postMessage = jest.fn(); - terminate = jest.fn(); + postMessage = vi.fn(); + terminate = vi.fn(); private listeners: Map< string, Array< ( event: Event ) => void > > = new Map(); diff --git a/packages/worker-threads/src/test/worker-thread.test.ts b/packages/worker-threads/src/test/worker-thread.test.ts index dfea667a6e8302..5634fe04ab830b 100644 --- a/packages/worker-threads/src/test/worker-thread.test.ts +++ b/packages/worker-threads/src/test/worker-thread.test.ts @@ -1,19 +1,22 @@ -/* - * Scope the mock globals to this file; test files without imports would - * otherwise share one global scope and collide. - */ -// eslint-disable-next-line jest/no-export -export {}; +import { + afterEach, + beforeEach, + describe, + expect, + it, + vi, + type Mock, +} from 'vitest'; // Store the original self. const originalSelf = globalThis.self; // Mock self for worker context. -let mockPostMessage: jest.Mock; +let mockPostMessage: Mock; let messageListeners: Array< ( event: MessageEvent ) => void > = []; function setupMockSelf() { - mockPostMessage = jest.fn(); + mockPostMessage = vi.fn(); messageListeners = []; // Override self with addEventListener pattern (matching comctx usage). @@ -58,7 +61,7 @@ function restoreSelf() { describe( 'worker-thread', () => { beforeEach( () => { setupMockSelf(); - jest.resetModules(); + vi.resetModules(); } ); afterEach( () => { @@ -68,7 +71,7 @@ describe( 'worker-thread', () => { describe( 'expose', () => { it( 'should set up message handler', async () => { const { expose } = await import( '../worker-thread' ); - const api = { method: jest.fn() }; + const api = { method: vi.fn() }; expose( api ); @@ -77,8 +80,8 @@ describe( 'worker-thread', () => { it( 'should register handlers for all methods on target', async () => { const { expose } = await import( '../worker-thread' ); - const method1 = jest.fn(); - const method2 = jest.fn(); + const method1 = vi.fn(); + const method2 = vi.fn(); const api = { method1, method2, @@ -92,7 +95,7 @@ describe( 'worker-thread', () => { it( 'should only expose functions, not other properties', async () => { const { expose } = await import( '../worker-thread' ); const api = { - validMethod: jest.fn(), + validMethod: vi.fn(), stringProp: 'not a function', numberProp: 42, objectProp: { nested: true }, diff --git a/packages/wp-build/lib/test/browserslist.js b/packages/wp-build/lib/test/browserslist.js index 2e3e1c5ba2b101..2b88970fdd219a 100644 --- a/packages/wp-build/lib/test/browserslist.js +++ b/packages/wp-build/lib/test/browserslist.js @@ -1,14 +1,15 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; import browserslist from 'browserslist'; import wordpressBrowserslistConfig from '@wordpress/browserslist-config'; import { getBrowserslistQueries } from '../browserslist.mjs'; describe( 'browserslist targeting', () => { afterEach( () => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); } ); it( 'falls back to @wordpress/browserslist-config when the project has no config', () => { - jest.spyOn( browserslist, 'findConfig' ).mockReturnValue( undefined ); + vi.spyOn( browserslist, 'findConfig' ).mockReturnValue( undefined ); expect( getBrowserslistQueries() ).toEqual( wordpressBrowserslistConfig @@ -16,7 +17,7 @@ describe( 'browserslist targeting', () => { } ); it( 'uses the project browserslist config when one is present', () => { - jest.spyOn( browserslist, 'findConfig' ).mockReturnValue( { + vi.spyOn( browserslist, 'findConfig' ).mockReturnValue( { defaults: [ 'last 1 chrome version' ], } ); diff --git a/packages/wp-build/lib/test/package-utils.js b/packages/wp-build/lib/test/package-utils.js index dbda64cdabd430..813b5135619fc2 100644 --- a/packages/wp-build/lib/test/package-utils.js +++ b/packages/wp-build/lib/test/package-utils.js @@ -1,26 +1,31 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; import { getPackageInfo } from '../package-utils.mjs'; +const testDirectory = path.dirname( fileURLToPath( import.meta.url ) ); + describe( 'getPackageInfo() resolve-miss contract', () => { it( 'returns null (does not throw) for an unresolvable scoped package', () => { expect( () => - getPackageInfo( '@does-not-exist/nope', __dirname ) + getPackageInfo( '@does-not-exist/nope', testDirectory ) ).not.toThrow(); expect( - getPackageInfo( '@does-not-exist/nope', __dirname ) + getPackageInfo( '@does-not-exist/nope', testDirectory ) ).toBeNull(); } ); it( 'returns the cached null on a repeated miss', () => { - const first = getPackageInfo( '@still-missing/pkg', __dirname ); - const second = getPackageInfo( '@still-missing/pkg', __dirname ); + const first = getPackageInfo( '@still-missing/pkg', testDirectory ); + const second = getPackageInfo( '@still-missing/pkg', testDirectory ); expect( first ).toBeNull(); expect( second ).toBeNull(); } ); it( 'still resolves an installed package that exposes its package.json', () => { - const info = getPackageInfo( '@wordpress/build', __dirname ); + const info = getPackageInfo( '@wordpress/build', testDirectory ); expect( info ).toMatchObject( { name: '@wordpress/build' } ); } ); diff --git a/packages/wp-build/lib/test/php-generator.js b/packages/wp-build/lib/test/php-generator.js index b338fa7ad732ea..6dc3a4014eb223 100644 --- a/packages/wp-build/lib/test/php-generator.js +++ b/packages/wp-build/lib/test/php-generator.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { renderTemplateToString as generateTemplate } from '../php-generator.mjs'; describe( 'page-wp-admin.php template', () => { diff --git a/packages/wp-build/lib/test/source-files.js b/packages/wp-build/lib/test/source-files.js index 528bd5220a122f..b6da339284d6cd 100644 --- a/packages/wp-build/lib/test/source-files.js +++ b/packages/wp-build/lib/test/source-files.js @@ -2,6 +2,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import glob from 'fast-glob'; +import { afterEach, describe, expect, it } from 'vitest'; import { getSourceFileGlob, isTestSourceFile } from '../source-files.mjs'; describe( 'source file discovery', () => { diff --git a/packages/wp-build/package.json b/packages/wp-build/package.json index 3361a2af18f28c..12fb4f3320986d 100644 --- a/packages/wp-build/package.json +++ b/packages/wp-build/package.json @@ -55,7 +55,8 @@ "sass-embedded": "^1.97.2" }, "devDependencies": { - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "peerDependencies": { "@wordpress/boot": ">=0.3.0 <1.0.0", diff --git a/routes/connectors-home/package.json b/routes/connectors-home/package.json index a40e5d8e22bb3e..c04ab0c8641677 100644 --- a/routes/connectors-home/package.json +++ b/routes/connectors-home/package.json @@ -28,7 +28,8 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19" diff --git a/routes/dashboard/package.json b/routes/dashboard/package.json index bef06c8887ad81..af15c80f1f304e 100644 --- a/routes/dashboard/package.json +++ b/routes/dashboard/package.json @@ -29,7 +29,8 @@ "@testing-library/dom": "^10.4.1", "@types/jest": "^30.0.0", "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^16.3.2" + "@testing-library/react": "^16.3.2", + "vitest": "^4.1.10" }, "peerDependencies": { "react": "^18 || ^19" diff --git a/routes/dashboard/widget-host/test/match-dashboard-href.test.ts b/routes/dashboard/widget-host/test/match-dashboard-href.test.ts index 8f20fc16b20d80..621a57d91698f6 100644 --- a/routes/dashboard/widget-host/test/match-dashboard-href.test.ts +++ b/routes/dashboard/widget-host/test/match-dashboard-href.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { matchDashboardHref } from '../match-dashboard-href'; const BASE = 'https://example.com/wp-admin/admin.php?page=dashboard&p=/'; diff --git a/storybook/test/load-error-recovery.test.ts b/storybook/test/load-error-recovery.test.ts index e9472586b19e71..46e4bd97f99a7c 100644 --- a/storybook/test/load-error-recovery.test.ts +++ b/storybook/test/load-error-recovery.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { PRELOAD_ERROR_RELOAD_GUARD_INTERVAL, handlePreloadError, @@ -12,8 +13,8 @@ function createStorage( } return { - getItem: jest.fn( ( key: string ) => values.get( key ) ?? null ), - setItem: jest.fn( ( key: string, value: string ) => { + getItem: vi.fn( ( key: string ) => values.get( key ) ?? null ), + setItem: vi.fn( ( key: string, value: string ) => { values.set( key, value ); } ), }; @@ -21,9 +22,9 @@ function createStorage( describe( 'handlePreloadError', () => { it( 'reloads the full Storybook page after the first preload error', () => { - const event = { preventDefault: jest.fn() }; + const event = { preventDefault: vi.fn() }; const storage = createStorage(); - const reload = jest.fn(); + const reload = vi.fn(); handlePreloadError( event, { now: 100_000, @@ -40,9 +41,9 @@ describe( 'handlePreloadError', () => { } ); it( 'lets a repeated preload error surface instead of reloading again', () => { - const event = { preventDefault: jest.fn() }; + const event = { preventDefault: vi.fn() }; const storage = createStorage( '100000' ); - const reload = jest.fn(); + const reload = vi.fn(); handlePreloadError( event, { now: 100_001, @@ -56,9 +57,9 @@ describe( 'handlePreloadError', () => { } ); it( 'allows another recovery attempt after the guard interval', () => { - const event = { preventDefault: jest.fn() }; + const event = { preventDefault: vi.fn() }; const storage = createStorage( '100000' ); - const reload = jest.fn(); + const reload = vi.fn(); handlePreloadError( event, { now: 100_000 + PRELOAD_ERROR_RELOAD_GUARD_INTERVAL, diff --git a/test/integration/blocks-schema.test.js b/test/integration/blocks-schema.test.js index 55dd0d19a8194b..1345919fb3d9f5 100644 --- a/test/integration/blocks-schema.test.js +++ b/test/integration/blocks-schema.test.js @@ -1,5 +1,7 @@ +import { readFileSync } from 'node:fs'; import Ajv from 'ajv'; import glob from 'fast-glob'; +import { describe, expect, test } from 'vitest'; import blockSchema from '../../schemas/json/block.json'; describe( 'block.json schema', () => { @@ -29,7 +31,9 @@ describe( 'block.json schema', () => { test.each( jsonFiles )( 'validates schema for `%s`', ( filepath ) => { // We want to validate the block.json file using the local schema. - const { $schema, ...blockMetadata } = require( filepath ); + const { $schema, ...blockMetadata } = JSON.parse( + readFileSync( filepath, 'utf8' ) + ); expect( $schema ).toBe( 'https://schemas.wp.org/trunk/block.json' ); @@ -41,7 +45,9 @@ describe( 'block.json schema', () => { test.each( invalidFiles )( 'rejects invalid block metadata in `%s`', ( filepath ) => { - const { $schema, ...blockMetadata } = require( filepath ); + const { $schema, ...blockMetadata } = JSON.parse( + readFileSync( filepath, 'utf8' ) + ); const result = ajv.validate( blockSchema, blockMetadata ); diff --git a/test/integration/helpers/wait-for-store-resolvers.js b/test/integration/helpers/wait-for-store-resolvers.js index e81a371508d06a..35a7c31ddb309d 100644 --- a/test/integration/helpers/wait-for-store-resolvers.js +++ b/test/integration/helpers/wait-for-store-resolvers.js @@ -18,7 +18,9 @@ export async function waitForStoreResolvers( fn ) { const result = fn(); // Advance all timers allowing store resolvers to resolve. - act( () => jest.runAllTimers() ); + act( () => + ( globalThis.wpVitest?.timers ?? globalThis.wpJest ).runAllTimers() + ); // The store resolvers perform several API fetches during editor // initialization. The most straightforward approach to ensure all of them diff --git a/test/integration/helpers/with-fake-timers.js b/test/integration/helpers/with-fake-timers.js index 91e3ead0a2bf00..abaf6bfd5e9f1d 100644 --- a/test/integration/helpers/with-fake-timers.js +++ b/test/integration/helpers/with-fake-timers.js @@ -6,22 +6,29 @@ * @return {*} The result of the function call. */ export async function withFakeTimers( fn ) { - const usingFakeTimers = jest.isMockFunction( setTimeout ); + const timers = globalThis.wpVitest?.timers ?? globalThis.wpJest; + const usingFakeTimers = globalThis.wpVitest + ? timers.isFakeTimers() + : timers.isMockFunction( setTimeout ); // Portions of the React Native Animation API rely upon these APIs. However, - // Jest's 'legacy' fake timers mutate these globals, which breaks the Animated - // API. We preserve the original implementations to restore them later. + // fake timers may mutate these globals, which breaks the Animated API. We + // preserve the original implementations to restore them later. const requestAnimationFrameCopy = global.requestAnimationFrame; const cancelAnimationFrameCopy = global.cancelAnimationFrame; if ( ! usingFakeTimers ) { - jest.useFakeTimers( { legacyFakeTimers: true } ); + if ( globalThis.wpVitest ) { + timers.useFakeTimers(); + } else { + timers.useFakeTimers( { legacyFakeTimers: true } ); + } } const result = await fn(); if ( ! usingFakeTimers ) { - jest.useRealTimers(); + timers.useRealTimers(); global.requestAnimationFrame = requestAnimationFrameCopy; global.cancelAnimationFrame = cancelAnimationFrameCopy; diff --git a/test/integration/package.json b/test/integration/package.json index a48b797913d09a..3a3e29f9f0fd9f 100644 --- a/test/integration/package.json +++ b/test/integration/package.json @@ -37,7 +37,8 @@ "ajv": "^8.20.0", "fast-glob": "^3.2.7", "react": "^18.3.1", - "rimraf": "^5.0.10" + "rimraf": "^5.0.10", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/test/integration/theme-schema.test.js b/test/integration/theme-schema.test.js index c972975cba4c47..89bb1099ad127e 100644 --- a/test/integration/theme-schema.test.js +++ b/test/integration/theme-schema.test.js @@ -1,5 +1,7 @@ +import { readFileSync } from 'node:fs'; import Ajv from 'ajv'; import glob from 'fast-glob'; +import { describe, expect, it, test } from 'vitest'; import themeSchema from '../../schemas/json/theme.json'; describe( 'theme.json schema', () => { @@ -33,7 +35,9 @@ describe( 'theme.json schema', () => { test.each( jsonFiles )( 'validates schema for `%s`', ( filepath ) => { // We want to validate the theme.json file using the local schema. - const { $schema, ...metadata } = require( filepath ); + const { $schema, ...metadata } = JSON.parse( + readFileSync( filepath, 'utf8' ) + ); // we expect the $schema property to be present in the theme.json file expect( $schema ).toBeTruthy(); @@ -45,7 +49,9 @@ describe( 'theme.json schema', () => { test.each( invalidFiles )( 'invalidates schema for `%s`', ( filepath ) => { // We want to validate the theme.json file using the local schema. - const { $schema, ...metadata } = require( filepath ); + const { $schema, ...metadata } = JSON.parse( + readFileSync( filepath, 'utf8' ) + ); const result = ajv.validate( themeSchema, metadata ); diff --git a/test/integration/wp-env-schema.test.js b/test/integration/wp-env-schema.test.js index fb6074b89571dd..26071fe2e7eded 100644 --- a/test/integration/wp-env-schema.test.js +++ b/test/integration/wp-env-schema.test.js @@ -1,4 +1,5 @@ import Ajv from 'ajv'; +import { describe, expect, test } from 'vitest'; import wpEnvSchema from '../../schemas/json/wp-env.json'; import wpEnvJsonFile from '../../.wp-env.json'; diff --git a/test/unit/config/global-mocks.js b/test/unit/config/global-mocks.js index a581b368d8d98c..d9b8ca524d2a92 100644 --- a/test/unit/config/global-mocks.js +++ b/test/unit/config/global-mocks.js @@ -2,6 +2,8 @@ import { TextDecoder, TextEncoder } from 'node:util'; import { Blob as BlobPolyfill, File as FilePolyfill } from 'node:buffer'; import timezoneMock from 'timezone-mock'; +globalThis.wpJest = jest; + /** * Configure timezone-mock to handle Date subclasses (like UTCDateMini) correctly. * MockDate constructor normally rejects object arguments, but date-fns v4 diff --git a/test/unit/config/testing-library.vitest.js b/test/unit/config/testing-library.vitest.js index 026e27543db3ac..de670df90c9cd9 100644 --- a/test/unit/config/testing-library.vitest.js +++ b/test/unit/config/testing-library.vitest.js @@ -1,4 +1,4 @@ -import * as matchers from '@testing-library/jest-dom/matchers'; +import '@testing-library/jest-dom/vitest'; // eslint-disable-next-line testing-library/no-manual-cleanup -- Vitest globals are disabled, so Testing Library cannot register cleanup automatically. import { cleanup } from '@testing-library/react'; import { afterAll, afterEach, beforeAll, expect } from 'vitest'; @@ -7,8 +7,6 @@ import './matchers/to-be-positioned-popover.vitest'; let previousIsReactActEnvironment; -expect.extend( matchers ); - beforeAll( () => { previousIsReactActEnvironment = globalThis.IS_REACT_ACT_ENVIRONMENT; globalThis.IS_REACT_ACT_ENVIRONMENT = true; diff --git a/test/unit/package.json b/test/unit/package.json index c8384e02780dcc..47e4d388669687 100644 --- a/test/unit/package.json +++ b/test/unit/package.json @@ -53,7 +53,6 @@ "snapshot-diff": "^0.10.0", "timezone-mock": "^1.3.6", "typescript": "npm:@typescript/typescript6@^6.0.2", - "vite-plugin-commonjs": "^0.10.4", "vitest": "^4.1.10", "yargs": "^17.3.0" }, diff --git a/test/unit/scripts/test/resolver.js b/test/unit/scripts/test/resolver.js index a110b17e843ebc..9864f1a3ddf2cf 100644 --- a/test/unit/scripts/test/resolver.js +++ b/test/unit/scripts/test/resolver.js @@ -1,9 +1,12 @@ -const nodePath = require( 'node:path' ); +import { createRequire } from 'node:module'; +import nodePath from 'node:path'; +import { describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); const resolver = require( '../resolver' ); describe( 'unit test resolver', () => { it( 'resolves WordPress source imports without package exports', () => { - const defaultResolver = jest.fn( ( request ) => request ); + const defaultResolver = vi.fn( ( request ) => request ); resolver( '@wordpress/block-editor/src/hooks/list-view', { defaultResolver, diff --git a/test/unit/scripts/validate-vitest-conventions.mjs b/test/unit/scripts/validate-vitest-conventions.mjs index dd22b11a8ba40a..6f14ff604efc0b 100644 --- a/test/unit/scripts/validate-vitest-conventions.mjs +++ b/test/unit/scripts/validate-vitest-conventions.mjs @@ -1,8 +1,16 @@ -import { execFileSync } from 'node:child_process'; -import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { spawnSync } from 'node:child_process'; +import { + existsSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; +import { isBuiltin } from 'node:module'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { globSync } from 'glob'; +import typescript from 'typescript'; import { discoverTestFiles, getVitestTestsByProject, @@ -161,70 +169,189 @@ if ( violations.length ) { const commonTypes = [ 'gutenberg-env', + 'react', 'react-css-custom-properties', 'style-imports', ]; + +function importsNodeBuiltin( file ) { + const source = + sourcesByFile.get( file ) ?? + readFileSync( path.join( ROOT_DIR, file ), 'utf8' ); + return [ + ...source.matchAll( /(?:from\s+|import\s*\(\s*)['"]([^'"]+)['"]/g ), + ].some( ( match ) => isBuiltin( match[ 1 ] ) ); +} + +function getTypecheckConfigPath( testFile ) { + let directory = path.dirname( path.join( ROOT_DIR, testFile ) ); + + while ( directory.startsWith( ROOT_DIR ) && directory !== ROOT_DIR ) { + for ( const configName of [ 'tsconfig.test.json', 'tsconfig.json' ] ) { + const configPath = path.join( directory, configName ); + if ( existsSync( configPath ) ) { + return configPath; + } + } + directory = path.dirname( directory ); + } + + return path.join( ROOT_DIR, 'tsconfig.base.json' ); +} + let typescriptTestCount = 0; for ( const projectName of VITEST_PROJECT_NAMES ) { - const typescriptTests = vitestTestsByProject[ projectName ].filter( + const projectTypescriptTests = vitestTestsByProject[ projectName ].filter( ( file ) => /\.tsx?$/.test( file ) ); - if ( ! typescriptTests.length ) { + if ( ! projectTypescriptTests.length ) { continue; } - typescriptTestCount += typescriptTests.length; - const configPath = path.join( - ROOT_DIR, - 'test/unit', - `.vitest-${ projectName }-typecheck.json` - ); - const needsNodeTypes = - projectName === 'node' || - typescriptTests.some( ( file ) => - /[('"]node:/.test( sourcesByFile.get( file ) ) + const testsByConfig = new Map(); + for ( const testFile of projectTypescriptTests ) { + const configPath = getTypecheckConfigPath( testFile ); + const configTests = testsByConfig.get( configPath ) ?? []; + configTests.push( testFile ); + testsByConfig.set( configPath, configTests ); + } + + for ( const [ baseConfigPath, typescriptTests ] of testsByConfig ) { + const { config: baseConfig, error: baseConfigError } = + typescript.readConfigFile( + baseConfigPath, + typescript.sys.readFile + ); + if ( baseConfigError ) { + throw new Error( + typescript.formatDiagnostics( [ baseConfigError ], { + getCanonicalFileName: ( fileName ) => fileName, + getCurrentDirectory: () => ROOT_DIR, + getNewLine: () => '\n', + } ) + ); + } + + typescriptTestCount += typescriptTests.length; + const needsNodeTypes = + projectName === 'node' || + typescriptTests.some( importsNodeBuiltin ); + const temporaryDirectory = mkdtempSync( + path.join( + path.dirname( baseConfigPath ), + `.vitest-${ projectName }-typecheck-` + ) + ); + const configPath = path.join( temporaryDirectory, 'tsconfig.json' ); + const compatibilityTypesPath = path.join( + temporaryDirectory, + 'compatibility.d.ts' ); - const setupTypeFiles = - projectName === 'jsdom' - ? [ - path.join( - ROOT_DIR, - 'test/unit/config/testing-library.vitest.js' - ), - ] - : []; - const typecheckConfig = { - extends: path.join( ROOT_DIR, 'tsconfig.base.json' ), - compilerOptions: { - checkJs: false, - composite: false, - emitDeclarationOnly: false, - noEmit: true, - rootDir: ROOT_DIR, - types: [ ...commonTypes, ...( needsNodeTypes ? [ 'node' ] : [] ) ], - }, - files: [ - ...setupTypeFiles, - ...typescriptTests.map( ( file ) => path.join( ROOT_DIR, file ) ), - ], - }; - - try { - writeFileSync( configPath, JSON.stringify( typecheckConfig ) ); - execFileSync( - process.execPath, - [ + const typecheckConfig = { + extends: baseConfigPath, + compilerOptions: { + allowJs: true, + allowImportingTsExtensions: true, + checkJs: false, + composite: false, + declaration: true, + declarationMap: false, + emitDeclarationOnly: false, + noEmit: true, + rootDir: ROOT_DIR, + typeRoots: [ + path.join( ROOT_DIR, 'typings' ), + path.join( ROOT_DIR, 'node_modules/@types' ), + ], + types: + projectName === 'jsdom' + ? [ + ...commonTypes, + ...( needsNodeTypes ? [ 'node' ] : [] ), + 'gutenberg-vitest-test-env', + ] + : [ + ...commonTypes, + 'node', + 'gutenberg-vitest-test-env', + ], + }, + // Package configs often include every source, story, and test file. + // This validator owns an exact routed-test set, so do not inherit + // those broader globs into another Vitest project's typecheck. + include: [], + exclude: [], + references: baseConfig.references?.map( ( reference ) => ( { + ...reference, + path: path.resolve( + path.dirname( baseConfigPath ), + reference.path + ), + } ) ), + files: [ + compatibilityTypesPath, + ...( projectName === 'jsdom' + ? [ + path.join( + ROOT_DIR, + 'test/unit/config/testing-library.vitest.js' + ), + ] + : [] ), + ...typescriptTests.map( ( file ) => + path.join( ROOT_DIR, file ) + ), + ], + }; + + try { + writeFileSync( + compatibilityTypesPath, + [ + 'declare const global: typeof globalThis;', + "declare module '@wordpress/commands';", + "declare module '@wordpress/interface';", + "declare module 'deep-freeze' { export default function deepFreeze(value: T): T; }", + ].join( '\n' ) + ); + const typecheckArguments = [ resolvePackageBin( 'typescript', 'tsc6' ), '--project', configPath, '--pretty', 'false', - ], - { cwd: ROOT_DIR, stdio: 'inherit' } - ); - } finally { - rmSync( configPath, { force: true } ); + ]; + const runTypecheck = () => + spawnSync( process.execPath, typecheckArguments, { + cwd: ROOT_DIR, + encoding: 'utf8', + } ); + writeFileSync( configPath, JSON.stringify( typecheckConfig ) ); + let result = runTypecheck(); + + const output = `${ result.stdout ?? '' }${ result.stderr ?? '' }`; + if ( result.status !== 0 && /TS63(?:05|10)/.test( output ) ) { + // A dependency cycle can make TypeScript treat routed tests as + // source files owned by a referenced package project. The package + // declarations were built before this check, so fall back to normal + // module resolution when that project-ownership check fails. + typecheckConfig.references = []; + writeFileSync( configPath, JSON.stringify( typecheckConfig ) ); + result = runTypecheck(); + } + + if ( result.error ) { + throw result.error; + } + if ( result.status !== 0 ) { + process.stderr.write( result.stdout ?? '' ); + process.stderr.write( result.stderr ?? '' ); + throw new Error( 'TypeScript test graph validation failed.' ); + } + } finally { + rmSync( temporaryDirectory, { force: true, recursive: true } ); + } } } diff --git a/test/unit/test-migration.json b/test/unit/test-migration.json index 683cbe586361da..212ab9994949e5 100644 --- a/test/unit/test-migration.json +++ b/test/unit/test-migration.json @@ -1,14 +1,484 @@ { "vitest": { "files": [ + "packages/a11y/src/shared/test/filter-message.test.ts", + "packages/abilities/src/store/tests/actions.test.ts", + "packages/abilities/src/store/tests/reducer.test.ts", + "packages/abilities/src/store/tests/selectors.test.ts", + "packages/abilities/src/tests/api.test.ts", + "packages/abilities/src/tests/validation.test.ts", + "packages/annotations/src/store/test/reducer.ts", + "packages/api-fetch/src/middlewares/test/fetch-all-middleware.ts", + "packages/api-fetch/src/middlewares/test/http-v1.ts", + "packages/api-fetch/src/middlewares/test/media-upload.ts", + "packages/api-fetch/src/middlewares/test/namespace-endpoint.ts", + "packages/api-fetch/src/middlewares/test/nonce.ts", + "packages/api-fetch/src/middlewares/test/root-url.ts", + "packages/api-fetch/src/middlewares/test/user-locale.ts", + "packages/api-fetch/src/test/exports.test.ts", + "packages/autop/src/test/index.test.ts", + "packages/babel-plugin-import-jsx-pragma/test/index.js", + "packages/babel-plugin-makepot/test/index.js", + "packages/babel-preset-default/test/index.js", + "packages/block-directory/src/store/test/reducer.js", + "packages/block-editor/src/autocompleters/test/block.js", + "packages/block-editor/src/components/block-mover/test/mover-description.js", + "packages/block-editor/src/components/block-styles/test/utils.js", + "packages/block-editor/src/components/block-switcher/test/use-transformed.patterns.js", + "packages/block-editor/src/components/block-switcher/test/utils.js", + "packages/block-editor/src/components/block-visibility/test/utils.js", + "packages/block-editor/src/components/border-radius-control/test/utils.js", + "packages/block-editor/src/components/colors/test/utils.js", + "packages/block-editor/src/components/font-sizes/test/fluid-utils.js", + "packages/block-editor/src/components/font-sizes/test/utils.js", + "packages/block-editor/src/components/global-styles/test/typography-utils.js", + "packages/block-editor/src/components/global-styles/test/utils.js", + "packages/block-editor/src/components/image-editor/test/index.js", + "packages/block-editor/src/components/inserter/test/get-appender-label.js", + "packages/block-editor/src/components/inserter/test/search-items.js", + "packages/block-editor/src/components/link-control/test/is-url-like.js", + "packages/block-editor/src/components/list-view/test/use-list-view-drop-zone.js", + "packages/block-editor/src/components/list-view/test/utils.js", + "packages/block-editor/src/components/media-placeholder/test/get-computed-accept-attribute.js", + "packages/block-editor/src/components/preset-input-control/test/utils.js", + "packages/block-editor/src/components/spacing-sizes-control/test/utils.js", + "packages/block-editor/src/components/use-block-drop-zone/test/index.js", + "packages/block-editor/src/components/use-on-block-drop/test/index.js", + "packages/block-editor/src/hooks/test/allowed-blocks.js", + "packages/block-editor/src/hooks/test/gap.js", + "packages/block-editor/src/hooks/test/generate-fields-from-attributes.js", + "packages/block-editor/src/hooks/test/generated-class-name.js", + "packages/block-editor/src/hooks/test/metadata.js", + "packages/block-editor/src/hooks/test/preset-round-trip.js", + "packages/block-editor/src/hooks/test/settings.jsx", + "packages/block-editor/src/hooks/test/state-utils.js", + "packages/block-editor/src/layouts/test/utils.js", + "packages/block-editor/src/store/test/array.js", + "packages/block-editor/src/store/test/get-block-settings.js", + "packages/block-editor/src/store/test/performance.js", + "packages/block-editor/src/store/test/private-actions.js", + "packages/block-editor/src/store/test/private-selectors.js", + "packages/block-editor/src/store/test/reducer.js", + "packages/block-editor/src/utils/test/block-bindings.js", + "packages/block-editor/src/utils/test/color-values.js", + "packages/block-editor/src/utils/test/format-font-style.js", + "packages/block-editor/src/utils/test/format-font-weight.js", + "packages/block-editor/src/utils/test/get-font-styles-and-weights.js", + "packages/block-editor/src/utils/test/math.js", + "packages/block-editor/src/utils/test/object.js", + "packages/block-editor/src/utils/test/pasting.js", + "packages/block-editor/src/utils/test/selection.js", + "packages/block-editor/src/utils/test/sorting.js", + "packages/block-editor/src/utils/test/transform-styles.js", + "packages/block-library/src/button/test/get-updated-link-attributes.js", + "packages/block-library/src/button/test/utils.js", + "packages/block-library/src/code/test/utils.js", + "packages/block-library/src/columns/test/utils.js", + "packages/block-library/src/comment-template/test/index.js", + "packages/block-library/src/gallery/test/dynamic-source.js", + "packages/block-library/src/gallery/test/responsive-styles.js", + "packages/block-library/src/gallery/test/shared.js", + "packages/block-library/src/gallery/test/variations.js", + "packages/block-library/src/heading/test/shared.js", + "packages/block-library/src/media-text/test/image-fill.js", + "packages/block-library/src/navigation-link/shared/test/update-attributes.test.js", + "packages/block-library/src/navigation-link/test/block-labelling.js", + "packages/block-library/src/navigation/edit/test/are-blocks-dirty.js", + "packages/block-library/src/navigation/edit/test/utils.js", + "packages/block-library/src/pattern/test/index.js", + "packages/block-library/src/table/test/state.js", + "packages/block-library/src/table/test/utils.js", + "packages/block-library/src/utils/test/search-patterns.js", + "packages/block-library/src/video/test/variations.js", + "packages/block-serialization-default-parser/test/index.js", + "packages/block-serialization-spec-parser/test/index.js", + "packages/blocks/src/api/parser/test/convert-legacy-block.js", + "packages/blocks/src/api/parser/test/serialize-raw-block.js", + "packages/blocks/src/api/raw-handling/test/latex-to-math.js", + "packages/blocks/src/api/raw-handling/test/markdown-converter.js", + "packages/blocks/src/api/test/factory.js", + "packages/blocks/src/api/test/registration.jsx", + "packages/blocks/src/api/test/serializer.jsx", + "packages/blocks/src/api/test/templates.js", + "packages/blocks/src/store/test/actions.js", + "packages/blocks/src/store/test/private-selectors.js", + "packages/blocks/src/store/test/reducer.js", + "packages/browserslist-config/test/index.test.js", + "packages/components/src/autocomplete/test/get-autocomplete-match.ts", + "packages/components/src/border-box-control/test/utils.ts", + "packages/components/src/context/test/context-connect.tsx", + "packages/components/src/context/test/wordpress-component.tsx", + "packages/components/src/custom-gradient-picker/test/serializer.ts", + "packages/components/src/date-time/test/utils.test.ts", + "packages/components/src/font-size-picker/test/utils.ts", + "packages/components/src/navigator/test/router.ts", + "packages/components/src/query-controls/test/terms.ts", + "packages/components/src/unit-control/test/utils.ts", + "packages/components/src/utils/test/colors.js", + "packages/components/src/utils/test/get-node-text.tsx", + "packages/components/src/utils/test/math.js", + "packages/components/src/utils/test/rtl.js", + "packages/components/src/utils/test/space.js", + "packages/components/src/utils/test/strings.js", + "packages/components/src/utils/test/unit-values.js", + "packages/compose/src/higher-order/test/compose.ts", + "packages/compose/src/higher-order/test/pipe.ts", + "packages/compose/src/hooks/use-media-query/test/ssr.jsx", + "packages/compose/src/utils/create-higher-order-component/test/index.jsx", + "packages/compose/src/utils/debounce/test/index.ts", + "packages/compose/src/utils/observable-map/test/index.js", + "packages/compose/src/utils/throttle/test/index.ts", + "packages/connectors/src/test/store.ts", + "packages/core-commands/src/utils/test/order-entity-records-by-search.js", + "packages/core-data/src/batch/test/create-batch.js", + "packages/core-data/src/batch/test/default-processor.js", + "packages/core-data/src/entity-types/test/attachment.test.ts", + "packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js", + "packages/core-data/src/fetch/test/__experimental-fetch-url-data.js", + "packages/core-data/src/locks/test/engine.js", + "packages/core-data/src/locks/test/reducer.js", + "packages/core-data/src/locks/test/selectors.js", + "packages/core-data/src/locks/test/utils.js", + "packages/core-data/src/queried-data/test/actions.js", + "packages/core-data/src/test/parsed-blocks-cache.js", + "packages/core-data/src/utils/test/clear-unchanged-edits.js", + "packages/core-data/src/utils/test/conservative-map-item.js", + "packages/core-data/src/utils/test/get-nested-value.js", + "packages/core-data/src/utils/test/get-normalized-comma-separable.js", + "packages/core-data/src/utils/test/get-pagination-meta.js", + "packages/core-data/src/utils/test/get-template-info.js", + "packages/core-data/src/utils/test/if-matching-action.js", + "packages/core-data/src/utils/test/is-numeric-id.js", + "packages/core-data/src/utils/test/log-entity-deprecation.js", + "packages/core-data/src/utils/test/replace-action.js", + "packages/core-data/src/utils/test/save-crdt-doc.js", + "packages/core-data/src/utils/test/set-nested-value.js", + "packages/core-data/src/utils/test/with-weak-map-cache.js", + "packages/data-controls/src/test/index.js", + "packages/data/src/plugins/persistence/storage/test/object.js", + "packages/data/src/plugins/persistence/test/index.js", + "packages/data/src/redux-store/metadata/test/reducer.js", + "packages/data/src/redux-store/metadata/test/selectors.js", + "packages/data/src/redux-store/metadata/test/utils.js", + "packages/data/src/redux-store/test/combine-reducers.js", + "packages/data/src/redux-store/test/index.js", + "packages/data/src/redux-store/test/keyed-reducer.js", + "packages/data/src/test/controls.js", + "packages/data/src/test/privateAPIs.js", + "packages/data/src/test/registry-selectors.js", + "packages/data/src/test/registry.js", + "packages/dataviews/src/components/dataform-controls/utils/test/get-calendar-locale.ts", + "packages/dataviews/src/components/dataform-layouts/test/normalize-form.ts", + "packages/dataviews/src/components/dataviews-layouts/utils/test/use-selection-props.ts", + "packages/dataviews/src/field-types/test/normalize-fields.ts", + "packages/dataviews/src/field-types/test/time.ts", + "packages/dataviews/src/utils/test/filter-sort-and-paginate.js", + "packages/date/src/test/index.js", + "packages/dependency-extraction-webpack-plugin/test/build.js", + "packages/dependency-extraction-webpack-plugin/test/util.js", + "packages/deprecated/src/test/index.ts", + "packages/design-system-mcp/src/test/data.ts", + "packages/design-system-mcp/src/test/format.ts", + "packages/design-system-mcp/src/test/parse-components.ts", + "packages/design-system-mcp/src/tools/test/get-component-details.ts", + "packages/docgen/test/engine.js", + "packages/docgen/test/formatter-markdown.js", + "packages/docgen/test/get-export-entries.js", + "packages/docgen/test/get-intermediate-representation.js", + "packages/docgen/test/get-jsdoc-from-token.js", + "packages/docgen/test/get-type-annotation.js", + "packages/dom/src/test/utils.js", + "packages/edit-post/src/store/test/reducer.js", + "packages/edit-site/src/utils/test/get-filtered-template-parts.js", + "packages/edit-widgets/src/store/test/reducer.js", + "packages/editor/src/components/media-categories/test/index.js", + "packages/editor/src/components/post-revisions-preview/test/preserve-client-ids.js", + "packages/editor/src/components/post-text-editor/test/utils.js", + "packages/editor/src/components/style-book/test/categories.js", + "packages/editor/src/hooks/push-changes-to-global-styles/test/format-style-value.js", + "packages/editor/src/hooks/push-changes-to-global-styles/test/style-labels.js", + "packages/editor/src/store/utils/test/notice-builder.js", + "packages/editor/src/utils/media-sideload-from-url/test/index.js", + "packages/editor/src/utils/test/device-type.js", + "packages/editor/src/utils/test/get-collaborator-display-name.ts", + "packages/editor/src/utils/test/media-upload.js", + "packages/editor/src/utils/test/normalize-text-string.js", + "packages/editor/src/utils/test/sync-error-messages.js", + "packages/editor/src/utils/test/terms.js", + "packages/element/src/test/index.jsx", + "packages/element/src/test/platform.jsx", + "packages/element/src/test/serialize.jsx", + "packages/element/src/test/utils.js", + "packages/env/lib/config/test/add-or-replace-port.js", + "packages/env/lib/config/test/config-integration.js", + "packages/env/lib/config/test/get-cache-directory.js", + "packages/env/lib/config/test/merge-configs.js", + "packages/env/lib/config/test/parse-config.js", + "packages/env/lib/config/test/parse-source-string.js", + "packages/env/lib/config/test/post-process-config.js", + "packages/env/lib/config/test/read-raw-config-file.js", + "packages/env/lib/config/test/validate-config.js", + "packages/env/lib/test/build-docker-compose-config.js", + "packages/env/lib/test/cache.js", + "packages/env/lib/test/cli.js", + "packages/env/lib/test/execute-lifecycle-script.js", + "packages/env/lib/test/md5.js", + "packages/env/lib/test/parse-spx-mode.js", + "packages/env/lib/test/parse-xdebug-mode.js", + "packages/env/lib/test/port-utils.js", + "packages/env/lib/test/resolve-available-ports.js", + "packages/escape-html/src/test/index.ts", + "packages/eslint-plugin/rules/__tests__/components-no-missing-40px-size-prop.js", + "packages/eslint-plugin/rules/__tests__/components-no-unsafe-button-disabled.js", + "packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js", + "packages/eslint-plugin/rules/__tests__/dependency-group.js", + "packages/eslint-plugin/rules/__tests__/i18n-ellipsis.js", + "packages/eslint-plugin/rules/__tests__/i18n-hyphenated-range.js", + "packages/eslint-plugin/rules/__tests__/i18n-no-collapsible-whitespace.js", + "packages/eslint-plugin/rules/__tests__/i18n-no-flanking-whitespace.js", + "packages/eslint-plugin/rules/__tests__/i18n-no-placeholders-only.js", + "packages/eslint-plugin/rules/__tests__/i18n-no-variables.js", + "packages/eslint-plugin/rules/__tests__/i18n-text-domain.js", + "packages/eslint-plugin/rules/__tests__/i18n-translator-comments.js", + "packages/eslint-plugin/rules/__tests__/no-base-control-with-label-without-id.js", + "packages/eslint-plugin/rules/__tests__/no-dom-globals-in-constructor.js", + "packages/eslint-plugin/rules/__tests__/no-dom-globals-in-module-scope.js", + "packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-cc-render.js", + "packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-fc.js", + "packages/eslint-plugin/rules/__tests__/no-ds-tokens.js", + "packages/eslint-plugin/rules/__tests__/no-global-active-element.js", + "packages/eslint-plugin/rules/__tests__/no-global-get-selection.js", + "packages/eslint-plugin/rules/__tests__/no-i18n-in-save.js", + "packages/eslint-plugin/rules/__tests__/no-non-module-stylesheet-imports.js", + "packages/eslint-plugin/rules/__tests__/no-setting-ds-tokens.js", + "packages/eslint-plugin/rules/__tests__/no-unguarded-get-range-at.js", + "packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js", + "packages/eslint-plugin/rules/__tests__/no-unmerged-classname.js", + "packages/eslint-plugin/rules/__tests__/no-unsafe-render-order.js", + "packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js", + "packages/eslint-plugin/rules/__tests__/no-unused-vars-before-return.js", + "packages/eslint-plugin/rules/__tests__/no-wp-process-env.js", + "packages/eslint-plugin/rules/__tests__/react-no-unsafe-timeout.js", + "packages/eslint-plugin/rules/__tests__/use-import-as.js", + "packages/eslint-plugin/rules/__tests__/use-recommended-components.js", + "packages/eslint-plugin/rules/__tests__/valid-sprintf.js", + "packages/eslint-plugin/rules/__tests__/wp-global-usage.js", + "packages/eslint-plugin/utils/test/is-package-installed.js", + "packages/format-library/src/link/test/utils.js", + "packages/global-styles-engine/src/test/get-global-styles-changes.test.ts", + "packages/global-styles-engine/src/test/render.test.ts", + "packages/global-styles-engine/src/test/resolve-style.test.ts", + "packages/global-styles-engine/src/test/style-state-back-compat.test.ts", + "packages/global-styles-engine/src/test/typography-utils.test.ts", + "packages/global-styles-engine/src/test/utils.test.ts", + "packages/global-styles-engine/src/test/variation.test.ts", + "packages/global-styles-engine/src/test/viewport-utils.test.ts", + "packages/global-styles-ui/src/font-library/utils/test/preview-styles.js", + "packages/global-styles-ui/src/test/shadow-utils.js", + "packages/global-styles-ui/src/test/utils.js", + "packages/grid/src/dashboard-grid/test/resolve-fill-widths.test.ts", + "packages/grid/src/dashboard-lanes/test/lane-placement.test.ts", + "packages/grid/src/shared/test/array-move-with-pinned.test.ts", + "packages/grid/src/shared/test/resize-snap.test.ts", "packages/html-entities/src/test/entities.jsdom.test.ts", + "packages/i18n/src/test/create-i18n.ts", + "packages/i18n/src/test/default-i18n.ts", + "packages/i18n/src/test/sprintf.ts", + "packages/i18n/src/test/subscribe-i18n.ts", + "packages/icons/lib/test/generate-library.js", + "packages/interactivity-router/src/assets/test/scs.test.ts", + "packages/interface/src/store/test/actions.js", + "packages/interface/src/store/test/reducer.js", + "packages/interface/src/store/test/selectors.js", + "packages/is-shallow-equal/test/index.ts", + "packages/kebab-case/src/test/index.js", + "packages/keycodes/src/test/platform.ts", + "packages/keycodes/src/test/with-ignore-ime-events.ts", + "packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts", + "packages/media-editor/src/image-editor/core/math/test/sanitize.ts", + "packages/media-editor/src/image-editor/core/test/camera.ts", + "packages/media-editor/src/image-editor/core/test/interaction-controller.ts", + "packages/media-editor/src/image-editor/core/test/preview-export-parity.ts", + "packages/media-editor/src/image-editor/core/test/render-camera-bridge.ts", + "packages/media-editor/src/image-editor/core/test/state.ts", + "packages/media-editor/src/image-editor/core/test/stencil-math.ts", + "packages/media-editor/src/image-editor/core/transforms/test/pipeline.ts", + "packages/media-editor/src/store/test/reducer.ts", + "packages/media-fields/src/filename/test/index.test.ts", + "packages/media-fields/src/filesize/test/index.test.tsx", + "packages/media-fields/src/media_dimensions/test/index.test.ts", + "packages/media-utils/src/utils/test/flatten-form-data.ts", + "packages/media-utils/src/utils/test/get-mime-types-array.ts", + "packages/media-utils/src/utils/test/get-upload-error-message.ts", + "packages/media-utils/src/utils/test/upload-error.ts", + "packages/notices/src/store/test/actions.ts", + "packages/notices/src/store/test/reducer.ts", + "packages/notices/src/store/test/selectors.ts", + "packages/npm-package-json-lint-config/test/index.test.js", + "packages/nux/src/store/test/actions.js", + "packages/nux/src/store/test/reducer.js", + "packages/nux/src/store/test/selectors.js", + "packages/plugins/src/api/test/index.js", + "packages/postcss-themes/test/index.js", + "packages/preferences-persistence/src/create/test/debounce-async.js", + "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/convert-edit-post-panels.js", + "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/index.js", + "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-feature-preferences.js", + "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-individual-preference.js", + "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-interface-enable-items.js", + "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-third-party-feature-preferences.js", + "packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-complementary-areas.js", + "packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-editor-settings.js", + "packages/preferences-persistence/src/migrations/preferences-package-data/test/index.js", + "packages/preferences/src/store/test/actions.ts", + "packages/preferences/src/store/test/reducer.ts", + "packages/preferences/src/store/test/selectors.ts", + "packages/prettier-config/test/index.js", + "packages/priority-queue/src/test/index.ts", + "packages/private-apis/src/test/index.jsx", + "packages/project-management-automation/lib/tasks/add-milestone/test/index.js", + "packages/project-management-automation/lib/tasks/assign-fixed-issues/test/index.js", + "packages/project-management-automation/lib/tasks/first-time-contributor-account-link/test/index.js", + "packages/project-management-automation/lib/tasks/first-time-contributor-label/test/index.js", + "packages/project-management-automation/lib/test/get-associated-pull-request.js", + "packages/readable-js-assets-webpack-plugin/test/build.js", + "packages/redux-routine/src/test/index.js", + "packages/redux-routine/src/test/is-action.js", + "packages/redux-routine/src/test/is-generator.js", + "packages/report-flaky-tests/src/__tests__/markdown.test.ts", + "packages/report-flaky-tests/src/__tests__/run.test.ts", + "packages/rich-text/src/store/test/actions.js", + "packages/rich-text/src/store/test/reducer.js", + "packages/rich-text/src/store/test/selectors.js", + "packages/rich-text/src/test/apply-format.js", + "packages/rich-text/src/test/concat.js", + "packages/rich-text/src/test/get-active-format.js", + "packages/rich-text/src/test/get-active-object.js", + "packages/rich-text/src/test/get-format-type.js", + "packages/rich-text/src/test/get-format-types.js", + "packages/rich-text/src/test/insert-object.js", + "packages/rich-text/src/test/insert.js", + "packages/rich-text/src/test/is-collapsed.js", + "packages/rich-text/src/test/is-empty.js", + "packages/rich-text/src/test/is-format-equal.js", + "packages/rich-text/src/test/join.js", + "packages/rich-text/src/test/normalise-formats.js", + "packages/rich-text/src/test/remove-format.js", + "packages/rich-text/src/test/replace.js", + "packages/rich-text/src/test/slice.js", + "packages/rich-text/src/test/split.js", + "packages/rich-text/src/test/toggle-format.js", + "packages/scripts/scripts/test/build-blocks-manifest.js", + "packages/scripts/utils/test/index.js", + "packages/scripts/utils/test/license.js", + "packages/server-side-render/src/test/index.ts", + "packages/shortcode/src/test/index.ts", + "packages/style-engine/src/styles/test/utils.js", + "packages/style-engine/src/test/index.js", + "packages/stylelint-config/test/commenting.js", + "packages/stylelint-config/test/functions.js", + "packages/stylelint-config/test/index.js", + "packages/stylelint-config/test/media-queries.js", + "packages/stylelint-config/test/properties.js", + "packages/stylelint-config/test/scss.js", + "packages/stylelint-config/test/selectors-scss.js", + "packages/stylelint-config/test/selectors.js", + "packages/stylelint-config/test/structure.js", + "packages/stylelint-config/test/themes.js", + "packages/stylelint-config/test/values.js", + "packages/stylelint-config/test/vendor-prefixes.js", + "packages/sync/src/providers/http-polling/test/config.test.ts", + "packages/sync/src/providers/http-polling/test/utils.test.ts", + "packages/sync/src/quill-delta/test/Delta.ts", + "packages/sync/src/test/crdt-snapshot.ts", + "packages/sync/src/test/performance.test.ts", + "packages/sync/src/test/undo-manager.test.ts", + "packages/sync/src/test/utils.ts", + "packages/theme/bin/terrazzo-plugin-ds-token-fallbacks/test/index.test.ts", + "packages/theme/bin/terrazzo-plugin-mode-overrides/test/index.test.ts", + "packages/theme/postcss-plugins/test/add-fallback-to-var.test.ts", + "packages/theme/postcss-plugins/test/ds-token-fallbacks.test.ts", + "packages/theme/src/color-ramps/test/index.test.ts", + "packages/theme/src/color-ramps/test/seed-input.test.ts", + "packages/theme/src/test/build-plugin-parity.test.ts", + "packages/theme/src/test/private-apis.test.ts", + "packages/theme/src/test/theme-provider-color-warnings.test.ts", + "packages/theme/stylelint-plugins/test/no-setting-wpds-custom-properties.test.ts", + "packages/theme/stylelint-plugins/test/no-token-fallback-values.test.ts", + "packages/theme/stylelint-plugins/test/no-unknown-ds-tokens.test.ts", + "packages/token-list/src/test/index.ts", + "packages/ui/src/test/index.test.ts", + "packages/ui/src/utils/test/theme-provider.test.ts", + "packages/undo-manager/src/test/index.js", + "packages/upload-media/src/store/test/actions.ts", + "packages/upload-media/src/store/test/private-actions.js", + "packages/upload-media/src/store/test/reducer.ts", + "packages/upload-media/src/store/test/retry.ts", + "packages/upload-media/src/store/test/selectors.ts", + "packages/upload-media/src/store/utils/test/video-conversion.ts", + "packages/upload-media/src/test/canvas-utils.ts", + "packages/upload-media/src/test/convert-blob-to-file.ts", + "packages/upload-media/src/test/error-messages.ts", + "packages/upload-media/src/test/get-file-basename.ts", + "packages/upload-media/src/test/get-file-extension.ts", + "packages/upload-media/src/test/get-file-name-from-url.ts", + "packages/upload-media/src/test/get-image-dimensions.ts", + "packages/upload-media/src/test/get-mime-types-array.ts", + "packages/upload-media/src/test/heic-parser.ts", + "packages/upload-media/src/test/is-animated-gif.ts", + "packages/upload-media/src/test/rename-and-clone-file.ts", + "packages/upload-media/src/test/upload-error.ts", + "packages/video-conversion/src/test/index.ts", + "packages/viewport/src/store/test/reducer.js", + "packages/viewport/src/store/test/selectors.js", + "packages/views/src/test/resolve-view.ts", + "packages/vips/src/test/convert-image-format.ts", + "packages/vips/src/test/has-transparency.ts", + "packages/vips/src/test/highbitdepth-avif.ts", + "packages/vips/src/test/resize-image.ts", + "packages/vips/src/test/rotate-image-integration.ts", + "packages/vips/src/test/rotate-image.ts", + "packages/vips/src/test/ultrahdr.ts", + "packages/warning/src/test/index.js", + "packages/warning/test/babel-plugin.js", + "packages/widget-dashboard/src/test/create-dashboard-widget.test.ts", + "packages/widget-dashboard/src/utils/resolve-dashboard-column-count/test/resolve-dashboard-column-count.test.ts", + "packages/widget-primitives/src/field-types/test/field-types.ts", + "packages/widget-primitives/src/icon-resolver/test/icon-resolver.ts", + "packages/wordcount/src/test/index.test.ts", + "packages/worker-threads/src/test/error-propagation.test.ts", + "packages/worker-threads/src/test/main-thread.test.ts", + "packages/worker-threads/src/test/worker-thread.test.ts", + "packages/wp-build/lib/test/browserslist.js", + "packages/wp-build/lib/test/package-utils.js", + "packages/wp-build/lib/test/php-generator.js", + "packages/wp-build/lib/test/source-files.js", + "routes/dashboard/widget-host/test/match-dashboard-href.test.ts", + "storybook/test/load-error-recovery.test.ts", + "test/integration/blocks-schema.test.js", + "test/integration/theme-schema.test.js", + "test/integration/wp-env-schema.test.js", "test/unit/config/console.vitest.test.js", "test/unit/config/emotion-compiler.vitest.jsdom.test.jsx", "test/unit/config/matchers/test/to-match-diff-snapshot.vitest.jsdom.test.js", "test/unit/config/setup.vitest.jsdom.test.jsx", "test/unit/config/setup.vitest.node.test.js", "test/unit/scripts/test/discover-test-files.test.js", - "test/unit/scripts/test/vitest-policy-rules.test.js" + "test/unit/scripts/test/resolver.js", + "test/unit/scripts/test/vitest-policy-rules.test.js", + "tools/agents/test/setup-skills.test.js", + "tools/build-scripts/test/check-esm-package-types.test.js", + "tools/build-scripts/test/process-windows.test.js", + "tools/build-scripts/test/process.test.js", + "tools/release/commands/test/changelog.js", + "tools/release/commands/test/common.js", + "tools/release/commands/test/packages.js", + "tools/release/lib/test/version.js", + "tools/release/test/cherry-pick.js", + "tools/release/test/cli.js", + "tools/release/test/resolve-performance-branches.js", + "tools/validation/validate-package-contents.test.js", + "tools/validation/validate-tsconfig.test.js" ], "directories": [] } diff --git a/test/unit/vitest.config.mjs b/test/unit/vitest.config.mjs index 3016b80a290e32..71091a806ff25c 100644 --- a/test/unit/vitest.config.mjs +++ b/test/unit/vitest.config.mjs @@ -5,7 +5,6 @@ import { fileURLToPath } from 'node:url'; import { playwright } from '@vitest/browser-playwright'; import react from '@vitejs/plugin-react-swc'; import { globSync } from 'glob'; -import commonjs from 'vite-plugin-commonjs'; import { defineConfig } from 'vitest/config'; import { discoverTestFiles, @@ -18,7 +17,6 @@ const ROOT_DIR = path.resolve( ); const nodeRequire = createRequire( import.meta.url ); const emotionPlugin = nodeRequire.resolve( '@swc/plugin-emotion' ); -const NORMALIZED_ROOT_DIR = ROOT_DIR.split( path.sep ).join( '/' ); const gutenbergEnvSetupFile = path.join( ROOT_DIR, 'test/unit/config/gutenberg-env.js' @@ -59,9 +57,10 @@ if ( ] ); } -// Preserve Jest's repository-root configuration discovery and default timezone. +// Preserve repository-root configuration discovery and default to UTC while +// allowing the date-test matrix to supply another timezone. process.chdir( ROOT_DIR ); -process.env.TZ = 'UTC'; +process.env.TZ ||= 'UTC'; const transpiledPackageNames = globSync( 'packages/*/src/index.{js,jsx,ts,tsx}', @@ -92,17 +91,6 @@ export default defineConfig( { ], ], } ), - commonjs( { - filter: ( id ) => - [ - `${ NORMALIZED_ROOT_DIR }/packages/block-serialization-spec-parser/parser.js`, - `${ NORMALIZED_ROOT_DIR }/packages/env/lib/`, - `${ NORMALIZED_ROOT_DIR }/packages/project-management-automation/lib/`, - `${ NORMALIZED_ROOT_DIR }/packages/scripts/utils/`, - `${ NORMALIZED_ROOT_DIR }/tools/release/commands/changelog.js`, - ].some( ( directory ) => id.startsWith( directory ) ) && - ! id.endsWith( '/packages/scripts/utils/license.js' ), - } ), ], resolve: { alias: [ diff --git a/tools/agents/package.json b/tools/agents/package.json index 6b6bd69f253947..5e0b2b4ae33e8c 100644 --- a/tools/agents/package.json +++ b/tools/agents/package.json @@ -20,6 +20,9 @@ "bugs": { "url": "https://github.com/WordPress/gutenberg/issues" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" }, diff --git a/tools/agents/test/setup-skills.test.js b/tools/agents/test/setup-skills.test.js index c5e817f19a96b7..0516680be23e6e 100644 --- a/tools/agents/test/setup-skills.test.js +++ b/tools/agents/test/setup-skills.test.js @@ -10,12 +10,16 @@ import { } from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; -import { pathToFileURL } from 'node:url'; +import { fileURLToPath, pathToFileURL } from 'node:url'; import { promisify } from 'node:util'; +import { afterEach, describe, expect, test, vi } from 'vitest'; import { setupSkills } from '../setup-skills.mjs'; const execFileAsync = promisify( execFile ); -const setupScriptSource = path.join( __dirname, '../setup-skills.mjs' ); +const setupScriptSource = path.join( + path.dirname( fileURLToPath( import.meta.url ) ), + '../setup-skills.mjs' +); const temporaryRoots = []; afterEach( async () => { @@ -122,7 +126,7 @@ describe( 'setupSkills', () => { ); await createSkill( repositoryRoot, 'release' ); - const confirm = jest.fn(); + const confirm = vi.fn(); const generated = await setupSkills( { repositoryRoot, confirm, @@ -149,7 +153,7 @@ describe( 'setupSkills', () => { const repositoryRoot = await createRepository(); await setupSkills( { repositoryRoot } ); await createFloatingSkill( repositoryRoot, 'private' ); - const confirm = jest.fn(); + const confirm = vi.fn(); const generated = await setupSkills( { repositoryRoot, diff --git a/tools/build-scripts/package.json b/tools/build-scripts/package.json index 8dc7ae3b6b799e..21445b5a0ff58d 100644 --- a/tools/build-scripts/package.json +++ b/tools/build-scripts/package.json @@ -40,6 +40,9 @@ "rimraf": "^5.0.10", "source-map": "^0.6.1" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" }, diff --git a/tools/build-scripts/test/check-esm-package-types.test.js b/tools/build-scripts/test/check-esm-package-types.test.js index 2f8e29de88022d..12103c80eaf722 100644 --- a/tools/build-scripts/test/check-esm-package-types.test.js +++ b/tools/build-scripts/test/check-esm-package-types.test.js @@ -1,6 +1,7 @@ import { access, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; +import { afterEach, expect, test, vi } from 'vitest'; import { classifyTypeScriptDiagnostics, getCssEntrypoints, @@ -85,7 +86,7 @@ test( 'continues package packing after a failure', () => { { packageJson: { name: 'second' } }, ]; const failure = new Error( 'Inspection failed' ); - const inspectPackage = jest + const inspectPackage = vi .fn() .mockImplementationOnce( () => { throw failure; diff --git a/tools/build-scripts/test/process-windows.test.js b/tools/build-scripts/test/process-windows.test.js index 80989792205523..8ee99c30d4537c 100644 --- a/tools/build-scripts/test/process-windows.test.js +++ b/tools/build-scripts/test/process-windows.test.js @@ -1,12 +1,13 @@ import { execFileSync } from 'node:child_process'; +import { afterEach, beforeEach, expect, test, vi } from 'vitest'; import spawn from 'cross-spawn'; import { spawnWatchProcess, stopWatchProcess } from '../process.mjs'; -jest.mock( 'node:child_process', () => ( { - ...jest.requireActual( 'node:child_process' ), - execFileSync: jest.fn(), +vi.mock( 'node:child_process', async ( importOriginal ) => ( { + ...( await importOriginal() ), + execFileSync: vi.fn(), } ) ); -jest.mock( 'cross-spawn', () => jest.fn() ); +vi.mock( 'cross-spawn', () => ( { default: vi.fn() } ) ); const platformDescriptor = Object.getOwnPropertyDescriptor( process, @@ -14,7 +15,7 @@ const platformDescriptor = Object.getOwnPropertyDescriptor( ); beforeEach( () => { - jest.clearAllMocks(); + vi.clearAllMocks(); Object.defineProperty( process, 'platform', { value: 'win32' } ); } ); diff --git a/tools/build-scripts/test/process.test.js b/tools/build-scripts/test/process.test.js index 802017f26469a6..c5a762a8e21810 100644 --- a/tools/build-scripts/test/process.test.js +++ b/tools/build-scripts/test/process.test.js @@ -1,5 +1,6 @@ import { once } from 'node:events'; import path from 'node:path'; +import { afterEach, expect, test } from 'vitest'; import { spawnWatchProcess, stopWatchProcess } from '../process.mjs'; let watcher; diff --git a/tools/eslint/config.mjs b/tools/eslint/config.mjs index 39f067eca345c9..42378a476c7682 100644 --- a/tools/eslint/config.mjs +++ b/tools/eslint/config.mjs @@ -13,6 +13,26 @@ import wpBuildConfig from '../../packages/wp-build/eslint-overrides.cjs'; const require = createRequire( import.meta.url ); const rootDir = resolve( import.meta.dirname, '../..' ); const wpPlugin = require( '@wordpress/eslint-plugin' ); +const testMigration = require( + join( rootDir, 'test/unit/test-migration.json' ) +); + +const vitestTestPatterns = [ + ...testMigration.vitest.files, + ...testMigration.vitest.directories.flatMap( ( directory ) => [ + `${ directory }/**/__tests__/**/*.[jt]s?(x)`, + `${ directory }/**/test/*.[jt]s?(x)`, + `${ directory }/**/?(*.)test.[jt]s?(x)`, + ] ), +]; +const vitestJsdomTestPatterns = [ + ...testMigration.vitest.files.filter( ( file ) => + /\.jsdom\.test\.[cm]?[jt]sx?$/.test( file ) + ), + ...testMigration.vitest.directories.map( + ( directory ) => `${ directory }/**/*.jsdom.test.[cm]?[jt]s?(x)` + ), +]; // Prefer the installed React version for linting, but fall back to the detected version. let reactVersion = 'detect'; try { @@ -27,24 +47,26 @@ try { * of the same plugin name resolves to a single shared reference. * * @param {Object[]} configs Flat config array. - * @return {Object[]} The same array with plugin references deduplicated. + * @return {Object[]} Configs with plugin references deduplicated. */ function dedupePlugins( configs ) { /** @type {Record} */ const seen = Object.create( null ); - for ( const config of configs ) { + return configs.map( ( config ) => { if ( ! config.plugins ) { - continue; + return config; } - for ( const name of Object.keys( config.plugins ) ) { + const plugins = {}; + for ( const [ name, plugin ] of Object.entries( config.plugins ) ) { if ( name in seen ) { - config.plugins[ name ] = seen[ name ]; + plugins[ name ] = seen[ name ]; } else { - seen[ name ] = config.plugins[ name ]; + seen[ name ] = plugin; + plugins[ name ] = plugin; } } - } - return configs; + return { ...config, plugins }; + } ); } /** @@ -503,6 +525,34 @@ export default dedupePlugins( [ }, }, + // Override: Vitest files — runner-neutral jest-dom and Testing Library rules. + { + ...jestDomPlugin.configs[ 'flat/recommended' ], + files: vitestJsdomTestPatterns, + }, + { + ...testingLibraryPlugin.configs[ 'flat/react' ], + files: vitestJsdomTestPatterns, + }, + { + plugins: jestPlugin.configs[ 'flat/recommended' ].plugins, + files: vitestTestPatterns, + settings: { + jest: { + globalPackage: 'vitest', + }, + }, + rules: { + ...jestPlugin.configs[ 'flat/recommended' ].rules, + // Preserve existing test patterns while changing runners. These rules + // newly flag valid patterns once the globals are imported from Vitest. + 'jest/no-conditional-expect': 'off', + 'jest/valid-describe-callback': 'off', + 'jest/valid-expect-in-promise': 'off', + 'jest/valid-title': 'off', + }, + }, + // Override: Jest test files (unit tests). ...wpPlugin.configs[ 'test-unit' ].map( ( config ) => ( { ...config, @@ -511,7 +561,11 @@ export default dedupePlugins( [ '**/test/**/*.{js,jsx}', '**/__tests__/**/*.{js,jsx}', ], - ignores: [ 'test/e2e/**/*.js', 'test/performance/**/*.js' ], + ignores: [ + 'test/e2e/**/*.js', + 'test/performance/**/*.js', + ...vitestTestPatterns, + ], } ) ), // Override: Test files — jest-dom, testing-library, jest recommended. @@ -522,6 +576,7 @@ export default dedupePlugins( [ 'test/e2e/**/*.[tj]s?(x)', 'test/performance/**/*.[tj]s?(x)', 'test/storybook-playwright/**/*.[tj]s?(x)', + ...vitestTestPatterns, ], }, { @@ -531,6 +586,7 @@ export default dedupePlugins( [ 'test/e2e/**/*.[tj]s?(x)', 'test/performance/**/*.[tj]s?(x)', 'test/storybook-playwright/**/*.[tj]s?(x)', + ...vitestTestPatterns, ], }, { @@ -540,6 +596,7 @@ export default dedupePlugins( [ 'test/e2e/**/*.[tj]s?(x)', 'test/performance/**/*.[tj]s?(x)', 'test/storybook-playwright/**/*.[tj]s?(x)', + ...vitestTestPatterns, ], rules: { ...jestPlugin.configs[ 'flat/recommended' ].rules, diff --git a/tools/release/commands/test/__snapshots__/changelog.js.snap b/tools/release/commands/test/__snapshots__/changelog.js.snap index 9fcb017ecc42ea..931327a938ae08 100644 --- a/tools/release/commands/test/__snapshots__/changelog.js.snap +++ b/tools/release/commands/test/__snapshots__/changelog.js.snap @@ -1,6 +1,6 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`getChangelog verify that the changelog is properly formatted 1`] = ` +exports[`getChangelog > verify that the changelog is properly formatted 1`] = ` "## Changelog ### Features @@ -289,7 +289,7 @@ exports[`getChangelog verify that the changelog is properly formatted 1`] = ` " `; -exports[`getContributorList verify that the contributors list is properly formatted 1`] = ` +exports[`getContributorList > verify that the contributors list is properly formatted 1`] = ` " ## Contributors @@ -299,7 +299,7 @@ The following contributors merged PRs in this release: @aaronrobertshaw @adamsilverstein @alexstine @andrewhayward @andrewserong @annezazu @anton-vlasenko @artemiomorales @aurooba @bangank36 @brookewp @c4rl0sbr4v0 @carolinan @chad1008 @ciampo @dcalhoun @derekblank @draganescu @ellatrix @fluiddot @fullofcaffeine @geriux @getdave @glendaviesnz @gziolo @jameskoster @jeryj @jsnajdr @juhi123 @kevin940726 @leemyongpakvn @madhusudhand @MaggieCabrera @Mamaduka @matiasbenedetto @michalczaplinski @mirka @mtias @mujuonly @ndiego @noahtallen @noisysocks @ntsekouras @oandregal @ockham @pbking @priethor @ramonjd @richtabor @scruffian @SiobhyB @spacedmonkey @stokesman @swissspidy @t-hamano @tellthemachines @tellyworth @them-es @torounit @tyxla @westonruter @WunderBart @youknowriad" `; -exports[`getContributorProps verify that the contributors props are properly formatted 1`] = ` +exports[`getContributorProps > verify that the contributors props are properly formatted 1`] = ` "## First-time contributors The following PRs were merged by first-time contributors: diff --git a/tools/release/commands/test/changelog.js b/tools/release/commands/test/changelog.js index 06297780d48bbf..8a0967b7f9a670 100644 --- a/tools/release/commands/test/changelog.js +++ b/tools/release/commands/test/changelog.js @@ -1,16 +1,31 @@ -jest.mock( '@octokit/rest' ); -jest.mock( '../../lib/milestone' ); -jest.mock( '../../lib/logger', () => ( { - log: jest.fn(), - warn: jest.fn(), - formats: { - title: jest.fn( ( message ) => message ), - error: jest.fn( ( message ) => message ), - warning: jest.fn( ( message ) => message ), - }, -} ) ); - -import { +import { createRequire } from 'node:module'; +import { afterAll, beforeEach, describe, expect, it, test, vi } from 'vitest'; +import _pullRequests from './fixtures/pull-requests.json'; +import botPullRequestFixture from './fixtures/bot-pull-requests.json'; +const require = createRequire( import.meta.url ); +const octokitModule = require( '@octokit/rest' ); +const originalOctokit = octokitModule.Octokit; +const Octokit = vi.fn(); +octokitModule.Octokit = Octokit; +const milestoneModule = require( '../../lib/milestone' ); +const originalGetMilestoneByTitle = milestoneModule.getMilestoneByTitle; +const originalGetIssuesByMilestone = milestoneModule.getIssuesByMilestone; +const getMilestoneByTitle = vi.fn(); +const getIssuesByMilestone = vi.fn(); +milestoneModule.getMilestoneByTitle = getMilestoneByTitle; +milestoneModule.getIssuesByMilestone = getIssuesByMilestone; +const loggerModule = require( '../../lib/logger' ); +const originalLogger = { ...loggerModule }; +const log = vi.fn(); +const warn = vi.fn(); +loggerModule.log = log; +loggerModule.warn = warn; +loggerModule.formats = { + title: vi.fn( ( message ) => message ), + error: vi.fn( ( message ) => message ), + warning: vi.fn( ( message ) => message ), +}; +const { getNormalizedTitle, reword, addTrailingPeriod, @@ -32,15 +47,15 @@ import { createChangelog, fetchAllPullRequests, getManualChangelogInstructions, -} from '../changelog'; -import _pullRequests from './fixtures/pull-requests.json'; -import botPullRequestFixture from './fixtures/bot-pull-requests.json'; -const { Octokit } = require( '@octokit/rest' ); -const { - getMilestoneByTitle, - getIssuesByMilestone, -} = require( '../../lib/milestone' ); -const { log, warn } = require( '../../lib/logger' ); +} = require( '../changelog' ); + +afterAll( () => { + octokitModule.Octokit = originalOctokit; + milestoneModule.getMilestoneByTitle = originalGetMilestoneByTitle; + milestoneModule.getIssuesByMilestone = originalGetIssuesByMilestone; + Object.assign( loggerModule, originalLogger ); + delete require.cache[ require.resolve( '../changelog' ) ]; +} ); /** * pull-requests.json is a static snapshot of real data from the GitHub API. @@ -57,22 +72,24 @@ const pullRequests = _pullRequests.concat( botPullRequestFixture ); * * @return {Object} Octokit stub. */ -const createOctokitWithoutReleases = () => ( { - repos: { - listReleases: { - endpoint: { - merge: jest.fn().mockReturnValue( {} ), +function createOctokitWithoutReleases() { + return { + repos: { + listReleases: { + endpoint: { + merge: vi.fn().mockReturnValue( {} ), + }, }, }, - }, - paginate: { - iterator: jest.fn().mockReturnValue( - ( async function* () { - yield { data: [] }; - } )() - ), - }, -} ); + paginate: { + iterator: vi.fn().mockReturnValue( + ( async function* () { + yield { data: [] }; + } )() + ), + }, + }; +} describe( 'createChangelog', () => { const settings = { @@ -83,8 +100,10 @@ describe( 'createChangelog', () => { }; beforeEach( () => { - jest.clearAllMocks(); - Octokit.mockImplementation( () => ( {} ) ); + vi.clearAllMocks(); + Octokit.mockImplementation( function () { + return {}; + } ); } ); it( 'keeps successful changelog output unchanged', async () => { diff --git a/tools/release/commands/test/common.js b/tools/release/commands/test/common.js index 55395460b4948e..c20e9d684716d3 100644 --- a/tools/release/commands/test/common.js +++ b/tools/release/commands/test/common.js @@ -1,4 +1,7 @@ -import { calculateVersionBumpFromChangelog } from '../common'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); +const { calculateVersionBumpFromChangelog } = require( '../common' ); describe( 'calculateVersionBumpFromChangelog', () => { it( 'should return null when no lines provided', () => { diff --git a/tools/release/commands/test/packages.js b/tools/release/commands/test/packages.js index f61f931021c5fb..b9e896b07e41e2 100644 --- a/tools/release/commands/test/packages.js +++ b/tools/release/commands/test/packages.js @@ -1,4 +1,7 @@ -import { +import { createRequire } from 'node:module'; +import { describe, expect, it, vi } from 'vitest'; +const require = createRequire( import.meta.url ); +const { finalizePreparedNpmRelease, getNpmReleasePackages, getNpmReleaseGitRecoveryCommands, @@ -14,7 +17,7 @@ import { runNpmReleasePhase, runPackagesRelease, verifyRemotePackageTags, -} from '../packages'; +} = require( '../packages' ); describe( 'prepareNpmRelease', () => { it.each( [ @@ -29,12 +32,12 @@ describe( 'prepareNpmRelease', () => { gitWorkingDirectoryPath: '/repo', releaseType, }; - const checkoutNpmReleaseBranchFn = jest.fn(); - const findPluginReleaseBranchNameFn = jest + const checkoutNpmReleaseBranchFn = vi.fn(); + const findPluginReleaseBranchNameFn = vi .fn() .mockResolvedValue( 'release/23.5' ); - const runNpmReleaseBranchSyncStepFn = jest.fn(); - const updatePackagesFn = jest + const runNpmReleaseBranchSyncStepFn = vi.fn(); + const updatePackagesFn = vi .fn() .mockResolvedValue( 'changelog-sha' ); @@ -76,7 +79,7 @@ describe( 'finalizePreparedNpmRelease', () => { 'finalizes a %s release', async ( releaseType, pluginReleaseBranch, expectedBranches ) => { const config = { releaseType }; - const backportCommitsToBranchFn = jest.fn(); + const backportCommitsToBranchFn = vi.fn(); const commits = [ 'changelog-sha', 'publish-sha' ]; await finalizePreparedNpmRelease( @@ -107,11 +110,11 @@ describe( 'runPackagesRelease', () => { interactive: false, }; const releaseState = { changelogCommit: 'changelog-sha' }; - const prepareNpmReleaseFn = jest.fn().mockResolvedValue( releaseState ); - const publishPreparedPackagesToNpmFn = jest + const prepareNpmReleaseFn = vi.fn().mockResolvedValue( releaseState ); + const publishPreparedPackagesToNpmFn = vi .fn() .mockResolvedValue( 'publish-sha' ); - const finalizePreparedNpmReleaseFn = jest.fn(); + const finalizePreparedNpmReleaseFn = vi.fn(); await runPackagesRelease( config, [], { finalizePreparedNpmReleaseFn, @@ -170,7 +173,7 @@ describe( 'getNpmReleasePackages', () => { }, }; const git = { - raw: jest + raw: vi .fn() .mockResolvedValue( [ @@ -184,7 +187,7 @@ describe( 'getNpmReleasePackages', () => { await expect( getNpmReleasePackages( '/repo', { git, - globFn: jest.fn().mockResolvedValue( files ), + globFn: vi.fn().mockResolvedValue( files ), readJSON: ( file ) => packageJsonByPath[ file ], } ) ).resolves.toEqual( [ @@ -255,7 +258,7 @@ describe( 'getNpmReleaseGitRecoveryCommands', () => { describe( 'getRemoteBranchSha', () => { it( 'returns the exact remote branch ref SHA', async () => { const git = { - raw: jest + raw: vi .fn() .mockResolvedValue( [ @@ -280,7 +283,7 @@ describe( 'getRemoteBranchSha', () => { describe( 'getRemoteTagShas', () => { it( 'fetches tag refs in one call and prefers peeled SHAs', async () => { const git = { - raw: jest + raw: vi .fn() .mockResolvedValue( [ @@ -326,7 +329,7 @@ describe( 'verifyRemotePackageTags', () => { publishCommit: 'expected-sha', }, { - getRemoteTagShasFn: jest.fn().mockResolvedValue( + getRemoteTagShasFn: vi.fn().mockResolvedValue( new Map( [ [ '@wordpress/a11y@4.50.0', 'expected-sha' ], [ '@wordpress/blocks@14.20.0', 'other-sha' ], @@ -345,7 +348,7 @@ describe( 'runNpmPublishPreflight', () => { const WHOAMI = { stdout: 'wp-user\n' }; it( 'verifies npm authentication before checking registry state', async () => { - const commandFn = jest + const commandFn = vi .fn() .mockResolvedValueOnce( WHOAMI ) .mockRejectedValueOnce( { @@ -379,7 +382,7 @@ describe( 'runNpmPublishPreflight', () => { } ); it( 'fails without checking registry state when npm authentication fails', async () => { - const commandFn = jest.fn().mockRejectedValueOnce( + const commandFn = vi.fn().mockRejectedValueOnce( Object.assign( new Error( 'Command failed: npm whoami' ), { stderr: 'npm ERR! code ENEEDAUTH', } ) @@ -403,7 +406,7 @@ describe( 'runNpmPublishPreflight', () => { } ); it( 'accepts a published version from the prepared commit with the expected dist-tag', async () => { - const commandFn = jest + const commandFn = vi .fn() .mockResolvedValueOnce( WHOAMI ) .mockResolvedValueOnce( { @@ -428,7 +431,7 @@ describe( 'runNpmPublishPreflight', () => { } ); it( 'fails when a published version came from another commit', async () => { - const commandFn = jest + const commandFn = vi .fn() .mockResolvedValueOnce( WHOAMI ) .mockResolvedValueOnce( { @@ -454,7 +457,7 @@ describe( 'runNpmPublishPreflight', () => { } ); it( 'fails with an actionable error when a published version has no gitHead', async () => { - const commandFn = jest + const commandFn = vi .fn() .mockResolvedValueOnce( WHOAMI ) .mockResolvedValueOnce( { @@ -480,7 +483,7 @@ describe( 'runNpmPublishPreflight', () => { } ); it( 'fails when a published version has the wrong dist-tag', async () => { - const commandFn = jest + const commandFn = vi .fn() .mockResolvedValueOnce( WHOAMI ) .mockResolvedValueOnce( { @@ -506,7 +509,7 @@ describe( 'runNpmPublishPreflight', () => { } ); it( 'fails when the registry returns a different version', async () => { - const commandFn = jest + const commandFn = vi .fn() .mockResolvedValueOnce( WHOAMI ) .mockResolvedValueOnce( { @@ -534,11 +537,11 @@ describe( 'runNpmPublishPreflight', () => { describe( 'runNpmReleasePhase', () => { it( 'retries a failed phase before surfacing success', async () => { - const task = jest + const task = vi .fn() .mockRejectedValueOnce( new Error( 'transient failure' ) ) .mockResolvedValueOnce(); - const wait = jest.fn(); + const wait = vi.fn(); await runNpmReleasePhase( 'Package tag push', task, { wait } ); @@ -550,10 +553,10 @@ describe( 'runNpmReleasePhase', () => { describe( 'pushNpmReleaseGitMetadata', () => { it( 'pushes the branch before pushing and verifying package tags', async () => { - const git = { raw: jest.fn().mockResolvedValue() }; - const runPhase = jest.fn( async ( _label, task ) => task() ); - const verifyRemoteNpmReleaseBranchFn = jest.fn(); - const verifyRemotePackageTagsFn = jest.fn(); + const git = { raw: vi.fn().mockResolvedValue() }; + const runPhase = vi.fn( async ( _label, task ) => task() ); + const verifyRemoteNpmReleaseBranchFn = vi.fn(); + const verifyRemotePackageTagsFn = vi.fn(); await pushNpmReleaseGitMetadata( { @@ -611,19 +614,19 @@ describe( 'pushNpmReleaseGitMetadata', () => { describe( 'publishVersionedPackagesToNpm', () => { it( 'preflights, publishes from package, and pushes metadata', async () => { - const commandFn = jest.fn().mockResolvedValue(); - const getNpmReleasePackagesFn = jest + const commandFn = vi.fn().mockResolvedValue(); + const getNpmReleasePackagesFn = vi .fn() .mockResolvedValue( [ { name: '@wordpress/a11y', tagName: '@wordpress/a11y@4.50.0' }, ] ); - const runNpmPublishPreflightFn = jest + const runNpmPublishPreflightFn = vi .fn() .mockResolvedValueOnce( [] ) .mockResolvedValueOnce( [ '@wordpress/a11y' ] ); - const pushNpmReleaseGitMetadataFn = jest.fn(); + const pushNpmReleaseGitMetadataFn = vi.fn(); const git = { - revparse: jest.fn().mockResolvedValue( 'publish-sha' ), + revparse: vi.fn().mockResolvedValue( 'publish-sha' ), }; await publishVersionedPackagesToNpm( @@ -671,11 +674,11 @@ describe( 'publishVersionedPackagesToNpm', () => { } ); it( 'rechecks registry state before retrying from-package', async () => { - const commandFn = jest + const commandFn = vi .fn() .mockRejectedValueOnce( new Error( 'partial publish' ) ) .mockResolvedValueOnce(); - const runNpmPublishPreflightFn = jest + const runNpmPublishPreflightFn = vi .fn() .mockResolvedValueOnce( [] ) .mockResolvedValueOnce( [ '@wordpress/a11y' ] ) @@ -684,8 +687,8 @@ describe( 'publishVersionedPackagesToNpm', () => { '@wordpress/blocks', ] ); const git = { - revparse: jest.fn().mockResolvedValue( 'publish-sha' ), - reset: jest.fn(), + revparse: vi.fn().mockResolvedValue( 'publish-sha' ), + reset: vi.fn(), }; await publishVersionedPackagesToNpm( @@ -698,7 +701,7 @@ describe( 'publishVersionedPackagesToNpm', () => { }, { commandFn, - getNpmReleasePackagesFn: jest.fn().mockResolvedValue( [ + getNpmReleasePackagesFn: vi.fn().mockResolvedValue( [ { name: '@wordpress/a11y', tagName: '@wordpress/a11y@4.50.0-next.0', @@ -709,7 +712,7 @@ describe( 'publishVersionedPackagesToNpm', () => { }, ] ), git, - pushNpmReleaseGitMetadataFn: jest.fn(), + pushNpmReleaseGitMetadataFn: vi.fn(), runNpmPublishPreflightFn, } ); @@ -724,9 +727,9 @@ describe( 'publishVersionedPackagesToNpm', () => { } ); it( 'skips Lerna when all package versions are already published', async () => { - const commandFn = jest.fn(); + const commandFn = vi.fn(); const git = { - revparse: jest.fn().mockResolvedValue( 'publish-sha' ), + revparse: vi.fn().mockResolvedValue( 'publish-sha' ), }; await publishVersionedPackagesToNpm( @@ -739,15 +742,15 @@ describe( 'publishVersionedPackagesToNpm', () => { }, { commandFn, - getNpmReleasePackagesFn: jest.fn().mockResolvedValue( [ + getNpmReleasePackagesFn: vi.fn().mockResolvedValue( [ { name: '@wordpress/a11y', tagName: '@wordpress/a11y@4.50.0', }, ] ), git, - pushNpmReleaseGitMetadataFn: jest.fn(), - runNpmPublishPreflightFn: jest + pushNpmReleaseGitMetadataFn: vi.fn(), + runNpmPublishPreflightFn: vi .fn() .mockResolvedValue( [ '@wordpress/a11y' ] ), } @@ -758,12 +761,12 @@ describe( 'publishVersionedPackagesToNpm', () => { } ); it( 'does not push metadata when final registry verification is incomplete', async () => { - const commandFn = jest.fn().mockResolvedValue(); - const pushNpmReleaseGitMetadataFn = jest.fn(); - const runNpmPublishPreflightFn = jest.fn().mockResolvedValue( [] ); - const runPhase = jest.fn( async ( _label, task ) => task() ); + const commandFn = vi.fn().mockResolvedValue(); + const pushNpmReleaseGitMetadataFn = vi.fn(); + const runNpmPublishPreflightFn = vi.fn().mockResolvedValue( [] ); + const runPhase = vi.fn( async ( _label, task ) => task() ); const git = { - revparse: jest.fn().mockResolvedValue( 'publish-sha' ), + revparse: vi.fn().mockResolvedValue( 'publish-sha' ), }; await expect( @@ -777,7 +780,7 @@ describe( 'publishVersionedPackagesToNpm', () => { }, { commandFn, - getNpmReleasePackagesFn: jest.fn().mockResolvedValue( [ + getNpmReleasePackagesFn: vi.fn().mockResolvedValue( [ { name: '@wordpress/a11y', tagName: '@wordpress/a11y@4.50.0', @@ -804,18 +807,18 @@ describe( 'publishVersionedPackagesToNpm', () => { } ); it( 'retries final registry verification after propagation lag', async () => { - const commandFn = jest.fn().mockResolvedValue(); - const pushNpmReleaseGitMetadataFn = jest.fn(); - const runNpmPublishPreflightFn = jest + const commandFn = vi.fn().mockResolvedValue(); + const pushNpmReleaseGitMetadataFn = vi.fn(); + const runNpmPublishPreflightFn = vi .fn() .mockResolvedValueOnce( [] ) .mockResolvedValueOnce( [] ) .mockResolvedValueOnce( [ '@wordpress/a11y' ] ); - const wait = jest.fn(); + const wait = vi.fn(); const runPhase = ( label, task ) => runNpmReleasePhase( label, task, { wait } ); const git = { - revparse: jest.fn().mockResolvedValue( 'publish-sha' ), + revparse: vi.fn().mockResolvedValue( 'publish-sha' ), }; await publishVersionedPackagesToNpm( @@ -828,7 +831,7 @@ describe( 'publishVersionedPackagesToNpm', () => { }, { commandFn, - getNpmReleasePackagesFn: jest.fn().mockResolvedValue( [ + getNpmReleasePackagesFn: vi.fn().mockResolvedValue( [ { name: '@wordpress/a11y', tagName: '@wordpress/a11y@4.50.0', @@ -887,14 +890,14 @@ describe( 'publishPackagesToNpm', () => { ] )( 'routes %s releases through the shared metadata publishing path', async ( releaseType, versionCommand, distTag, npmReleaseBranch ) => { - const commandFn = jest.fn().mockResolvedValue(); + const commandFn = vi.fn().mockResolvedValue(); const git = { - revparse: jest + revparse: vi .fn() .mockResolvedValueOnce( 'before-sha' ) .mockResolvedValueOnce( 'after-sha' ), }; - const publishVersionedPackagesToNpmFn = jest.fn(); + const publishVersionedPackagesToNpmFn = vi.fn(); const config = { ...getConfig( releaseType ), distTag, diff --git a/tools/release/lib/test/version.js b/tools/release/lib/test/version.js index 568309eee3674d..0449f0a9996268 100644 --- a/tools/release/lib/test/version.js +++ b/tools/release/lib/test/version.js @@ -1,4 +1,7 @@ -import { getNextMajorVersion } from '../version'; +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); +const { getNextMajorVersion } = require( '../version' ); describe( 'getNextMajorVersion', () => { it( 'increases the minor number by default', () => { diff --git a/tools/release/package.json b/tools/release/package.json index b020e4791e409b..8b69f60f396e69 100644 --- a/tools/release/package.json +++ b/tools/release/package.json @@ -35,6 +35,9 @@ "simple-git": "^3.32.3", "sprintf-js": "^1.1.1" }, + "devDependencies": { + "vitest": "^4.1.10" + }, "publishConfig": { "access": "public" }, diff --git a/tools/release/test/cherry-pick.js b/tools/release/test/cherry-pick.js index 7cc1a336826c7f..ef0e0d2318ddc6 100644 --- a/tools/release/test/cherry-pick.js +++ b/tools/release/test/cherry-pick.js @@ -1,14 +1,15 @@ -const fs = require( 'node:fs' ); -const os = require( 'node:os' ); -const path = require( 'node:path' ); -const { spawnSync } = require( 'node:child_process' ); -const { +import { spawnSync } from 'node:child_process'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { cherryPickAll, cherryPickOne, isGhVersionSupported, parseGhVersion, MINIMUM_GH_VERSION, -} = require( '../cherry-pick.mjs' ); +} from '../cherry-pick.mjs'; const LOCAL_AUTHOR = { name: 'Marco Ciampini', diff --git a/tools/release/test/cli.js b/tools/release/test/cli.js index 1d9bf4eaedb374..9db862d6a40e2f 100644 --- a/tools/release/test/cli.js +++ b/tools/release/test/cli.js @@ -1,7 +1,12 @@ -const path = require( 'path' ); -const { spawnSync } = require( 'child_process' ); +import { spawnSync } from 'node:child_process'; +import { createRequire } from 'node:module'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; -const cliPath = path.resolve( __dirname, '../cli.js' ); +const require = createRequire( import.meta.url ); +const currentDirectory = path.dirname( fileURLToPath( import.meta.url ) ); +const cliPath = path.resolve( currentDirectory, '../cli.js' ); /* * Resolve from this file: an `-e` script resolves requires from the cwd, * which is the repo root under Jest and has no `commander` installed. diff --git a/tools/release/test/resolve-performance-branches.js b/tools/release/test/resolve-performance-branches.js index 2ba137fbad098d..095729b73934dc 100644 --- a/tools/release/test/resolve-performance-branches.js +++ b/tools/release/test/resolve-performance-branches.js @@ -1,12 +1,13 @@ -const fs = require( 'node:fs' ); -const path = require( 'node:path' ); -const { +import fs from 'node:fs'; +import path from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { resolveBranches, resolveShards, computeBuildKey, getTestedUpToMajor, REFERENCE_COMMIT, -} = require( '../resolve-performance-branches.mjs' ); +} from '../resolve-performance-branches.mjs'; const sha = 'a'.repeat( 40 ); const baseSha = 'b'.repeat( 40 ); diff --git a/tools/validation/package.json b/tools/validation/package.json index 49ef8573c0c743..17387e57e991d1 100644 --- a/tools/validation/package.json +++ b/tools/validation/package.json @@ -31,7 +31,8 @@ "esbuild": "^0.27.2", "glob": "^13.0.6", "jsonc-parser": "^3.3.1", - "simple-git": "^3.32.3" + "simple-git": "^3.32.3", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/tools/validation/validate-package-contents.test.js b/tools/validation/validate-package-contents.test.js index 5125e4c280bca3..eefaadab447df2 100644 --- a/tools/validation/validate-package-contents.test.js +++ b/tools/validation/validate-package-contents.test.js @@ -1,10 +1,12 @@ -/* global afterEach, expect, test */ -const { spawnSync } = require( 'node:child_process' ); -const { mkdtempSync, mkdirSync, rmSync, writeFileSync } = require( 'node:fs' ); -const { tmpdir } = require( 'node:os' ); -const { dirname, join } = require( 'node:path' ); - -const validatorPath = join( __dirname, 'validate-package-contents.mjs' ); +import { spawnSync } from 'node:child_process'; +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { afterEach, expect, test } from 'vitest'; + +const currentDirectory = dirname( fileURLToPath( import.meta.url ) ); +const validatorPath = join( currentDirectory, 'validate-package-contents.mjs' ); const temporaryRoots = []; afterEach( () => { diff --git a/tools/validation/validate-tsconfig.test.js b/tools/validation/validate-tsconfig.test.js index 1b1bb30f3a49b0..6322d9d33c3697 100644 --- a/tools/validation/validate-tsconfig.test.js +++ b/tools/validation/validate-tsconfig.test.js @@ -1,10 +1,13 @@ -/* global afterEach, expect, test */ -const { spawnSync } = require( 'node:child_process' ); -const { mkdtempSync, mkdirSync, rmSync, writeFileSync } = require( 'node:fs' ); -const { tmpdir } = require( 'node:os' ); -const { dirname, join } = require( 'node:path' ); - -const validatorPath = join( __dirname, 'validate-tsconfig.mjs' ); +import { spawnSync } from 'node:child_process'; +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { afterEach, expect, test } from 'vitest'; + +const validatorPath = fileURLToPath( + new URL( 'validate-tsconfig.mjs', import.meta.url ) +); const temporaryRoots = []; afterEach( () => { diff --git a/typings/gutenberg-vitest-test-env/index.d.ts b/typings/gutenberg-vitest-test-env/index.d.ts new file mode 100644 index 00000000000000..f8b46e79726683 --- /dev/null +++ b/typings/gutenberg-vitest-test-env/index.d.ts @@ -0,0 +1,35 @@ +// eslint-disable-next-line import/no-extraneous-dependencies -- The owning test workspaces declare Vitest. +import 'vitest'; + +interface GutenbergVitestMatchers< R = unknown > { + toBePositionedPopover: () => R; + toHaveErrored: () => R; + toHaveErroredWith: ( ...args: unknown[] ) => R; + toHaveInformed: () => R; + toHaveInformedWith: ( ...args: unknown[] ) => R; + toHaveLogged: () => R; + toHaveLoggedWith: ( ...args: unknown[] ) => R; + toHaveWarned: () => R; + toHaveWarnedWith: ( ...args: unknown[] ) => R; + toMatchDiffSnapshot: ( expected: unknown ) => R; + toMatchStyleDiffSnapshot: ( expected: Element | null ) => R; +} + +interface GutenbergVitestEnvironment { + mockCSSSupports: () => void; + mockMatchMedia: () => void; + mockPointerEvent: () => void; + mockResizeObserver: () => void; + mockScrollIntoView: () => void; + mockVisibleElements: () => void; + timers: typeof import('vitest').vi; +} + +declare global { + var wpVitest: GutenbergVitestEnvironment; +} + +declare module 'vitest' { + interface Assertion< T = any > extends GutenbergVitestMatchers< T > {} + interface AsymmetricMatchersContaining extends GutenbergVitestMatchers {} +} From 1b6beecb34d14a41a74050bdd146017efed48758 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Sat, 29 Aug 2026 14:02:49 +0200 Subject: [PATCH 02/13] Test: Type custom Vitest matchers canonically --- typings/gutenberg-vitest-test-env/index.d.ts | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/typings/gutenberg-vitest-test-env/index.d.ts b/typings/gutenberg-vitest-test-env/index.d.ts index f8b46e79726683..ce941ecf6e109e 100644 --- a/typings/gutenberg-vitest-test-env/index.d.ts +++ b/typings/gutenberg-vitest-test-env/index.d.ts @@ -1,18 +1,18 @@ // eslint-disable-next-line import/no-extraneous-dependencies -- The owning test workspaces declare Vitest. import 'vitest'; -interface GutenbergVitestMatchers< R = unknown > { - toBePositionedPopover: () => R; - toHaveErrored: () => R; - toHaveErroredWith: ( ...args: unknown[] ) => R; - toHaveInformed: () => R; - toHaveInformedWith: ( ...args: unknown[] ) => R; - toHaveLogged: () => R; - toHaveLoggedWith: ( ...args: unknown[] ) => R; - toHaveWarned: () => R; - toHaveWarnedWith: ( ...args: unknown[] ) => R; - toMatchDiffSnapshot: ( expected: unknown ) => R; - toMatchStyleDiffSnapshot: ( expected: Element | null ) => R; +interface GutenbergVitestMatchers { + toBePositionedPopover: () => void; + toHaveErrored: () => void; + toHaveErroredWith: ( ...args: unknown[] ) => void; + toHaveInformed: () => void; + toHaveInformedWith: ( ...args: unknown[] ) => void; + toHaveLogged: () => void; + toHaveLoggedWith: ( ...args: unknown[] ) => void; + toHaveWarned: () => void; + toHaveWarnedWith: ( ...args: unknown[] ) => void; + toMatchDiffSnapshot: ( expected: unknown ) => void; + toMatchStyleDiffSnapshot: ( expected: Element | null ) => void; } interface GutenbergVitestEnvironment { @@ -30,6 +30,6 @@ declare global { } declare module 'vitest' { - interface Assertion< T = any > extends GutenbergVitestMatchers< T > {} - interface AsymmetricMatchersContaining extends GutenbergVitestMatchers {} + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Matchers< T = any > extends GutenbergVitestMatchers {} } From a35d71693f02790bf404f5bf503450fa676d2fb9 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 31 Aug 2026 13:47:16 +0200 Subject: [PATCH 03/13] Test: Reconcile Node migration with current trunk --- package-lock.json | 14 ++++++++++++++ .../src/components/dimensions-tool/test/utils.js | 1 + .../block-editor/src/store/test/performance.js | 2 +- packages/components/package.json | 3 +++ packages/core-data/package.json | 3 +++ packages/dataviews/package.json | 1 + packages/grid/package.json | 1 + packages/interactivity/package.json | 1 + packages/sync/package.json | 2 ++ packages/theme/package.json | 1 + packages/ui/package.json | 1 + .../src/store/utils/test/video-conversion.ts | 6 +++--- packages/widget-dashboard/package.json | 1 + .../src/test/get-action-route.test.ts | 1 + test/unit/test-migration.json | 2 ++ 15 files changed, 36 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f4f0af9938aca1..91129ad778791c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50648,8 +50648,11 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@types/react-dom": "^18.3.1", + "@wordpress/jest-console": "file:../jest-console", "@wordpress/stylelint-tools": "file:../../tools/stylelint", + "snapshot-diff": "^0.10.0", "storybook": "^10.5.3", "timezone-mock": "^1.3.6", "vitest": "^4.1.10" @@ -50866,8 +50869,11 @@ "uuid": "^14.0.0" }, "devDependencies": { + "@jest/globals": "^30.5.0", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", + "@types/jest": "^30.0.0", + "@types/node": "^20.19.39", "@wordpress/block-library": "file:../block-library", "deep-freeze": "^0.0.1", "vitest": "^4.1.10" @@ -51124,6 +51130,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "esbuild": "^0.27.2", "storybook": "^10.5.3", @@ -52305,6 +52312,7 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", + "@types/jest": "^30.0.0", "@wordpress/icons": "file:../icons", "@wordpress/ui": "file:../ui", "storybook": "^10.5.3", @@ -52443,6 +52451,7 @@ "preact": "^10.29.1" }, "devDependencies": { + "@types/jest": "^30.0.0", "vitest": "^4.1.10" }, "engines": { @@ -54206,6 +54215,8 @@ "yjs": "^13.6.29" }, "devDependencies": { + "@jest/globals": "^30.5.0", + "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "vitest": "^4.1.10" }, @@ -54236,6 +54247,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "@wordpress/validation-tools": "file:../../tools/validation", "esbuild": "^0.27.2", @@ -54409,6 +54421,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "@wordpress/base-styles": "file:../base-styles", "@wordpress/jest-console": "file:../jest-console", @@ -54629,6 +54642,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@wordpress/admin-ui": "file:../admin-ui", "storybook": "^10.5.3", "vitest": "^4.1.10" diff --git a/packages/block-editor/src/components/dimensions-tool/test/utils.js b/packages/block-editor/src/components/dimensions-tool/test/utils.js index 5c9de506e468c5..ef6e681aa1f6ea 100644 --- a/packages/block-editor/src/components/dimensions-tool/test/utils.js +++ b/packages/block-editor/src/components/dimensions-tool/test/utils.js @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { findAspectRatioOption, parseAspectRatio } from '../utils'; describe( 'parseAspectRatio', () => { diff --git a/packages/block-editor/src/store/test/performance.js b/packages/block-editor/src/store/test/performance.js index c75e0891eb207b..379640c406a7b3 100644 --- a/packages/block-editor/src/store/test/performance.js +++ b/packages/block-editor/src/store/test/performance.js @@ -20,7 +20,7 @@ describe( 'performance', () => { blocks, } ); expect( preparedState ).toBeDefined(); - }, 30_000 ); + }, 60_000 ); it( 'should update blocks', () => { const updatedState = reducer( preparedState, { diff --git a/packages/components/package.json b/packages/components/package.json index aa0f1b3fab6fb4..424f510d857614 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -109,8 +109,11 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@types/react-dom": "^18.3.1", + "@wordpress/jest-console": "file:../jest-console", "@wordpress/stylelint-tools": "file:../../tools/stylelint", + "snapshot-diff": "^0.10.0", "storybook": "^10.5.3", "timezone-mock": "^1.3.6", "vitest": "^4.1.10" diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 048f298d9d89f1..952739eb804f29 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -76,8 +76,11 @@ "uuid": "^14.0.0" }, "devDependencies": { + "@jest/globals": "^30.5.0", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", + "@types/jest": "^30.0.0", + "@types/node": "^20.19.39", "@wordpress/block-library": "file:../block-library", "deep-freeze": "^0.0.1", "vitest": "^4.1.10" diff --git a/packages/dataviews/package.json b/packages/dataviews/package.json index fac5cee3f37b30..a466d878073005 100644 --- a/packages/dataviews/package.json +++ b/packages/dataviews/package.json @@ -81,6 +81,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "esbuild": "^0.27.2", "storybook": "^10.5.3", diff --git a/packages/grid/package.json b/packages/grid/package.json index 96390ab6982031..98be8163aed051 100644 --- a/packages/grid/package.json +++ b/packages/grid/package.json @@ -58,6 +58,7 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", + "@types/jest": "^30.0.0", "@wordpress/icons": "file:../icons", "@wordpress/ui": "file:../ui", "storybook": "^10.5.3", diff --git a/packages/interactivity/package.json b/packages/interactivity/package.json index 49485cb417b1d1..66f7b711bf698f 100644 --- a/packages/interactivity/package.json +++ b/packages/interactivity/package.json @@ -38,6 +38,7 @@ "preact": "^10.29.1" }, "devDependencies": { + "@types/jest": "^30.0.0", "vitest": "^4.1.10" }, "publishConfig": { diff --git a/packages/sync/package.json b/packages/sync/package.json index 0a9f62a2584e34..a7f167e543535f 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -54,6 +54,8 @@ "yjs": "^13.6.29" }, "devDependencies": { + "@jest/globals": "^30.5.0", + "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "vitest": "^4.1.10" }, diff --git a/packages/theme/package.json b/packages/theme/package.json index 3d5f33a122dcf3..f178443d0de13a 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -99,6 +99,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "@wordpress/validation-tools": "file:../../tools/validation", "esbuild": "^0.27.2", diff --git a/packages/ui/package.json b/packages/ui/package.json index 0cee725287bb5d..3e054cdc7126e5 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -67,6 +67,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@types/node": "^20.19.39", "@wordpress/base-styles": "file:../base-styles", "@wordpress/jest-console": "file:../jest-console", diff --git a/packages/upload-media/src/store/utils/test/video-conversion.ts b/packages/upload-media/src/store/utils/test/video-conversion.ts index c0b6eb0dc1f426..b52d2ba4e8da0e 100644 --- a/packages/upload-media/src/store/utils/test/video-conversion.ts +++ b/packages/upload-media/src/store/utils/test/video-conversion.ts @@ -80,9 +80,9 @@ describe( 'isConversionTimeoutError', () => { /* * The remaining functions wrap the lazily-imported - * @wordpress/video-conversion/worker module (mapped to a jest stub via - * moduleNameMapper). Each test starts from a fresh module registry so the - * module-level "has the worker loaded yet?" state is deterministic. + * @wordpress/video-conversion/worker module (mapped to a test stub by the + * Vitest configuration). Each test starts from a fresh module registry so + * the module-level "has the worker loaded yet?" state is deterministic. */ describe( 'convertGifToVideo', () => { beforeEach( () => { diff --git a/packages/widget-dashboard/package.json b/packages/widget-dashboard/package.json index d0ac1cc1154809..867e4adb0b3360 100644 --- a/packages/widget-dashboard/package.json +++ b/packages/widget-dashboard/package.json @@ -71,6 +71,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/jest": "^30.0.0", "@wordpress/admin-ui": "file:../admin-ui", "storybook": "^10.5.3", "vitest": "^4.1.10" diff --git a/packages/widget-dashboard/src/test/get-action-route.test.ts b/packages/widget-dashboard/src/test/get-action-route.test.ts index e7e076ead1f089..9dce3a58c32374 100644 --- a/packages/widget-dashboard/src/test/get-action-route.test.ts +++ b/packages/widget-dashboard/src/test/get-action-route.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { WidgetAction, WidgetHostLinks, diff --git a/test/unit/test-migration.json b/test/unit/test-migration.json index 212ab9994949e5..6f59d5ec377a76 100644 --- a/test/unit/test-migration.json +++ b/test/unit/test-migration.json @@ -29,6 +29,7 @@ "packages/block-editor/src/components/block-visibility/test/utils.js", "packages/block-editor/src/components/border-radius-control/test/utils.js", "packages/block-editor/src/components/colors/test/utils.js", + "packages/block-editor/src/components/dimensions-tool/test/utils.js", "packages/block-editor/src/components/font-sizes/test/fluid-utils.js", "packages/block-editor/src/components/font-sizes/test/utils.js", "packages/block-editor/src/components/global-styles/test/typography-utils.js", @@ -442,6 +443,7 @@ "packages/warning/src/test/index.js", "packages/warning/test/babel-plugin.js", "packages/widget-dashboard/src/test/create-dashboard-widget.test.ts", + "packages/widget-dashboard/src/test/get-action-route.test.ts", "packages/widget-dashboard/src/utils/resolve-dashboard-column-count/test/resolve-dashboard-column-count.test.ts", "packages/widget-primitives/src/field-types/test/field-types.ts", "packages/widget-primitives/src/icon-resolver/test/icon-resolver.ts", From 09f6684f0e09dc1e00e3e7441140eabaca6b165f Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 31 Aug 2026 16:24:02 +0200 Subject: [PATCH 04/13] Test: Make Vitest the default Node runner --- docs/contributors/code/testing-overview.md | 2 +- package-lock.json | 6 +- packages/jest-console/package.json | 3 +- packages/jest-console/src/test/index.test.ts | 72 ++- packages/jest-preset-default/package.json | 3 +- packages/jest-preset-default/test/index.js | 3 + .../jest-preset-default/test/style-mock.js | 5 +- test/unit/VITEST_MIGRATION.md | 17 +- test/unit/jest.config.js | 9 - test/unit/scripts/discover-test-files.mjs | 9 +- test/unit/scripts/test/babel-transformer.js | 63 ++- .../scripts/test/discover-test-files.test.js | 20 +- test/unit/scripts/validate-test-routing.mjs | 28 +- test/unit/test-migration.json | 478 +----------------- 14 files changed, 163 insertions(+), 555 deletions(-) diff --git a/docs/contributors/code/testing-overview.md b/docs/contributors/code/testing-overview.md index 537025929afb40..673173cd6bf81b 100644 --- a/docs/contributors/code/testing-overview.md +++ b/docs/contributors/code/testing-overview.md @@ -39,7 +39,7 @@ During the Jest-to-Vitest migration, run both `npm run test:unit` and `npm run t Keep your tests in a `test` folder in your working directory. The test file should have the same name as the test subject file. -Use `*.jsdom.test.*` for tests that only require a virtual DOM. Use `*.browser.test.*` for tests that require Vitest Browser Mode. Leave Node-compatible test names without an environment suffix. The filename selects the test environment in both Jest and Vitest. During the migration, the manifest only selects the runner. Do not use per-file environment overrides. +Use `*.jsdom.test.*` for tests that only require a virtual DOM. Use `*.browser.test.*` for tests that require Vitest Browser Mode. Leave Node-compatible test names without an environment suffix. Node tests run in Vitest automatically. The filename selects the test environment in both Jest and Vitest. During the remaining migration, the manifest selects the runner only for JSDOM and Browser Mode tests. Do not use per-file environment overrides. ``` +-- test diff --git a/package-lock.json b/package-lock.json index 91129ad778791c..ba1c108f68e706 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52535,7 +52535,8 @@ "jest-mock": "^30.5.0" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", @@ -52639,7 +52640,8 @@ "change-case": "^4.1.2" }, "devDependencies": { - "clsx": "^2.1.1" + "clsx": "^2.1.1", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json index 0552dcadcc8f14..1831c985aba9fc 100644 --- a/packages/jest-console/package.json +++ b/packages/jest-console/package.json @@ -45,7 +45,8 @@ "jest-mock": "^30.5.0" }, "devDependencies": { - "@types/jest": "^30.0.0" + "@types/jest": "^30.0.0", + "vitest": "^4.1.10" }, "peerDependencies": { "jest": ">=30" diff --git a/packages/jest-console/src/test/index.test.ts b/packages/jest-console/src/test/index.test.ts index 85aaa69a0b5f70..c2c4832755f358 100644 --- a/packages/jest-console/src/test/index.test.ts +++ b/packages/jest-console/src/test/index.test.ts @@ -1,7 +1,37 @@ /* eslint-disable no-console */ -import '../matchers'; +import { + afterAll, + afterEach as vitestAfterEach, + beforeAll as vitestBeforeAll, + beforeEach as vitestBeforeEach, + describe, + expect as vitestExpect, + it, + test, + vi, +} from 'vitest'; +import { spyOn as jestSpyOn } from 'jest-mock'; import type { ExtendedMock } from '../types'; +declare global { + const afterEach: typeof vitestAfterEach; + const beforeAll: typeof vitestBeforeAll; + const beforeEach: typeof vitestBeforeEach; + const expect: typeof vitestExpect; + const jest: { spyOn: typeof jestSpyOn }; +} + +vi.restoreAllMocks(); +vi.stubGlobal( 'afterEach', vitestAfterEach ); +vi.stubGlobal( 'beforeAll', vitestBeforeAll ); +vi.stubGlobal( 'beforeEach', vitestBeforeEach ); +vi.stubGlobal( 'expect', vitestExpect ); +vi.stubGlobal( 'jest', { spyOn: jestSpyOn } ); + +await import( '../index' ); + +afterAll( () => vi.unstubAllGlobals() ); + // The matchers replace the console methods with counting spies. function getSpy( methodName: 'error' | 'info' | 'log' | 'warn' ) { return console[ methodName ] as unknown as ExtendedMock; @@ -20,26 +50,26 @@ describe( 'jest-console', () => { test( `${ matcherName } works`, () => { console[ methodName ]( message ); - expect( console )[ matcherName ](); + vitestExpect( console )[ matcherName ](); } ); test( `${ matcherName } works when not called`, () => { - expect( console ).not[ matcherName ](); - expect( () => expect( console )[ matcherName ]() ).toThrow( - 'Expected mock function to be called.' - ); + vitestExpect( console ).not[ matcherName ](); + vitestExpect( () => + vitestExpect( console )[ matcherName ]() + ).toThrow( 'Expected mock function to be called.' ); } ); test( `${ matcherNameWith } works with arguments that match`, () => { console[ methodName ]( message ); - expect( console )[ matcherNameWith ]( message ); + vitestExpect( console )[ matcherNameWith ]( message ); } ); test( `${ matcherNameWith } works when not called`, () => { - expect( console ).not[ matcherNameWith ]( message ); - expect( () => - expect( console )[ matcherNameWith ]( message ) + vitestExpect( console ).not[ matcherNameWith ]( message ); + vitestExpect( () => + vitestExpect( console )[ matcherNameWith ]( message ) ).toThrow( /Expected mock function to be called with:.*but it was called with:/s ); @@ -49,9 +79,9 @@ describe( 'jest-console', () => { console[ methodName ]( 'Unknown message.' ); console[ methodName ]( message, 'Unknown param.' ); - expect( console ).not[ matcherNameWith ]( message ); - expect( () => - expect( console )[ matcherNameWith ]( message ) + vitestExpect( console ).not[ matcherNameWith ]( message ); + vitestExpect( () => + vitestExpect( console )[ matcherNameWith ]( message ) ).toThrow( /Expected mock function to be called with:.*but it was called with:.*Unknown param./s ); @@ -60,33 +90,31 @@ describe( 'jest-console', () => { test( 'assertions number gets incremented after every matcher call', () => { const spy = getSpy( methodName ); - expect( spy.assertionsNumber ).toBe( 0 ); + vitestExpect( spy.assertionsNumber ).toBe( 0 ); console[ methodName ]( message ); - expect( console )[ matcherName ](); - expect( spy.assertionsNumber ).toBe( 1 ); + vitestExpect( console )[ matcherName ](); + vitestExpect( spy.assertionsNumber ).toBe( 1 ); - expect( console )[ matcherNameWith ]( message ); - expect( spy.assertionsNumber ).toBe( 2 ); + vitestExpect( console )[ matcherNameWith ]( message ); + vitestExpect( spy.assertionsNumber ).toBe( 2 ); } ); describe( 'lifecycle', () => { - beforeAll( () => { + vitestBeforeAll( () => { // Disable reason: // This is a difficult one to test, since the matcher's // own lifecycle is defined to run before ours. Infer // that we're being watched by testing the console // method as being a spy. - // eslint-disable-next-line jest/no-standalone-expect - expect( + vitestExpect( getSpy( methodName ).assertionsNumber ).toBeGreaterThanOrEqual( 0 ); } ); // Disable reason: // See beforeAll implementation and explanation added there. - // eslint-disable-next-line jest/expect-expect it( 'captures logging in lifecycle', () => {} ); } ); } ); diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json index f5aa03a367fe1f..fc4e6265608d85 100644 --- a/packages/jest-preset-default/package.json +++ b/packages/jest-preset-default/package.json @@ -41,7 +41,8 @@ "change-case": "^4.1.2" }, "devDependencies": { - "clsx": "^2.1.1" + "clsx": "^2.1.1", + "vitest": "^4.1.10" }, "peerDependencies": { "@babel/core": ">=7", diff --git a/packages/jest-preset-default/test/index.js b/packages/jest-preset-default/test/index.js index bdc69cc1a06758..3dd1d39929841c 100644 --- a/packages/jest-preset-default/test/index.js +++ b/packages/jest-preset-default/test/index.js @@ -1,3 +1,6 @@ +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const preset = require( '@wordpress/jest-preset-default' ); describe( '@wordpress/jest-preset-default', () => { diff --git a/packages/jest-preset-default/test/style-mock.js b/packages/jest-preset-default/test/style-mock.js index bd176cd7ce8d43..beedee47f11309 100644 --- a/packages/jest-preset-default/test/style-mock.js +++ b/packages/jest-preset-default/test/style-mock.js @@ -1,4 +1,7 @@ -const clsx = require( 'clsx' ); +import { createRequire } from 'node:module'; +import clsx from 'clsx'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const styles = require( '../scripts/style-mock' ); describe( 'style mock', () => { diff --git a/test/unit/VITEST_MIGRATION.md b/test/unit/VITEST_MIGRATION.md index 0a8d5d054aea20..400f23db6b8343 100644 --- a/test/unit/VITEST_MIGRATION.md +++ b/test/unit/VITEST_MIGRATION.md @@ -11,10 +11,12 @@ metadata updates. When changing test ownership: -- Add individual tests to `vitest.files`, or use `vitest.directories` when an - entire directory can move as one independently revertible unit. -- Vitest tests run in Node by default. Name tests `*.jsdom.test.*` to opt into - JSDOM or `*.browser.test.*` to opt into Browser Mode. +- Node tests run in Vitest automatically. They do not need migration metadata. +- Add migrated JSDOM and Browser Mode tests to `vitest.files`, or use + `vitest.directories` when an entire directory can move as one independently + revertible unit. +- Name tests `*.jsdom.test.*` to use JSDOM or `*.browser.test.*` to use Browser + Mode. - Use Node for pure JavaScript, data, stores, schemas, build tooling, and server-side logic. - Use Browser Mode for real CSS, layout, geometry, viewport behavior, media @@ -28,10 +30,9 @@ When changing test ownership: - Before running Browser Mode tests locally for the first time, install Chromium with `npm exec --workspace @wordpress/unit-tests -- playwright install chromium`. -- New Jest tests require no migration metadata; runner discovery picks them up - automatically. Their filenames still select the environment: Node by - default or JSDOM through `*.jsdom.test.*`. A new `*.browser.test.*` must be - assigned to Vitest so it can run in Browser Mode. +- New Node tests run in Vitest without migration metadata. A new + `*.jsdom.test.*` runs in Jest until it is assigned to Vitest, and a new + `*.browser.test.*` must be assigned to Vitest so it can run in Browser Mode. - Do not use per-file Jest or Vitest environment overrides. The filename is the single source of truth. - Run `npm test` for the complete lint and unit-test suite. For focused diff --git a/test/unit/jest.config.js b/test/unit/jest.config.js index 27148ddf7346af..1542f04d312d86 100644 --- a/test/unit/jest.config.js +++ b/test/unit/jest.config.js @@ -137,15 +137,6 @@ const commonProjectConfig = { module.exports = { rootDir: ROOT_DIR, projects: [ - { - ...commonProjectConfig, - displayName: 'node', - testEnvironment: 'node', - testPathIgnorePatterns: [ - ...commonProjectConfig.testPathIgnorePatterns, - '\\.(?:browser|jsdom)\\.test\\.[jt]sx?$', - ], - }, { ...commonProjectConfig, displayName: 'jsdom', diff --git a/test/unit/scripts/discover-test-files.mjs b/test/unit/scripts/discover-test-files.mjs index d315f7cde72bac..e7630d56abf712 100644 --- a/test/unit/scripts/discover-test-files.mjs +++ b/test/unit/scripts/discover-test-files.mjs @@ -45,6 +45,9 @@ export function discoverTestFiles( rootDir ) { } export function getVitestTests( discoveredTests, manifest ) { + const nodeTests = discoveredTests.filter( + ( testPath ) => getTestEnvironmentName( testPath ) === 'node' + ); const directoryTests = discoveredTests.filter( ( testPath ) => manifest.vitest.directories.some( ( directoryPath ) => @@ -54,7 +57,11 @@ export function getVitestTests( discoveredTests, manifest ) { ); return [ - ...new Set( [ ...manifest.vitest.files, ...directoryTests ] ), + ...new Set( [ + ...nodeTests, + ...manifest.vitest.files, + ...directoryTests, + ] ), ].sort(); } diff --git a/test/unit/scripts/test/babel-transformer.js b/test/unit/scripts/test/babel-transformer.js index c8271d506583c0..cea0bfdc829942 100644 --- a/test/unit/scripts/test/babel-transformer.js +++ b/test/unit/scripts/test/babel-transformer.js @@ -1,24 +1,31 @@ -/* eslint-disable import/order -- The transformer must load after its dependency is mocked. */ -const fs = require( 'node:fs' ); -const os = require( 'node:os' ); -const path = require( 'node:path' ); - -const mockGetCacheKey = jest.fn( ( source ) => source ); - -jest.mock( 'babel-jest', () => ( { - createTransformer: () => ( { - getCacheKey: mockGetCacheKey, - } ), -} ) ); - +import fs from 'node:fs'; +import { createRequire } from 'node:module'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const transformer = require( '../babel-transformer' ); -/* eslint-enable import/order */ + +function getTransformOptions() { + return { + cacheFS: new Map(), + config: { + cwd: process.cwd(), + rootDir: process.cwd(), + }, + configString: '{}', + instrument: false, + supportsDynamicImport: false, + supportsExportNamespaceFrom: false, + supportsStaticESM: false, + supportsTopLevelAwait: false, + }; +} describe( 'Babel transformer cache key', () => { let temporaryDirectory; afterEach( () => { - mockGetCacheKey.mockClear(); if ( temporaryDirectory ) { fs.rmSync( temporaryDirectory, { force: true, recursive: true } ); temporaryDirectory = undefined; @@ -26,7 +33,7 @@ describe( 'Babel transformer cache key', () => { } ); it.each( [ 'js', 'jsx' ] )( - 'includes block.json for block index.%s files', + 'changes when block.json changes for block index.%s files', ( extension ) => { temporaryDirectory = fs.mkdtempSync( path.join( os.tmpdir(), 'gutenberg-babel-transformer-' ) @@ -38,17 +45,29 @@ describe( 'Babel transformer cache key', () => { 'example' ); fs.mkdirSync( blockDirectory, { recursive: true } ); - fs.writeFileSync( - path.join( blockDirectory, 'block.json' ), - '{"name":"core/example"}' + const blockJSONPath = path.join( blockDirectory, 'block.json' ); + const blockIndexPath = path.join( + blockDirectory, + `index.${ extension }` + ); + fs.writeFileSync( blockJSONPath, '{"name":"core/example"}' ); + const firstCacheKey = transformer.getCacheKey( + 'block source', + blockIndexPath, + getTransformOptions() ); - const cacheKey = transformer.getCacheKey( + fs.writeFileSync( + blockJSONPath, + '{"name":"core/example","version":2}' + ); + const secondCacheKey = transformer.getCacheKey( 'block source', - path.join( blockDirectory, `index.${ extension }` ) + blockIndexPath, + getTransformOptions() ); - expect( cacheKey ).toBe( 'block source\n{"name":"core/example"}' ); + expect( secondCacheKey ).not.toBe( firstCacheKey ); } ); } ); diff --git a/test/unit/scripts/test/discover-test-files.test.js b/test/unit/scripts/test/discover-test-files.test.js index e940e1e7530f5e..a699a8c6ec85c2 100644 --- a/test/unit/scripts/test/discover-test-files.test.js +++ b/test/unit/scripts/test/discover-test-files.test.js @@ -1,5 +1,8 @@ import { describe, expect, it } from 'vitest'; -import { getTestEnvironmentName } from '../discover-test-files.mjs'; +import { + getTestEnvironmentName, + getVitestTestsByProject, +} from '../discover-test-files.mjs'; import { sourceHasTestEnvironmentOverride } from '../test-environment-overrides.mjs'; describe( 'getTestEnvironmentName', () => { @@ -20,6 +23,21 @@ describe( 'getTestEnvironmentName', () => { } ); } ); +describe( 'getVitestTestsByProject', () => { + it( 'routes new Node tests to Vitest without migration metadata', () => { + expect( + getVitestTestsByProject( + [ 'example.test.ts', 'example.jsdom.test.tsx' ], + { vitest: { directories: [], files: [] } } + ) + ).toEqual( { + browser: [], + jsdom: [], + node: [ 'example.test.ts' ], + } ); + } ); +} ); + describe( 'sourceHasTestEnvironmentOverride', () => { it( 'detects Jest and Vitest environment override comments', () => { for ( const source of [ diff --git a/test/unit/scripts/validate-test-routing.mjs b/test/unit/scripts/validate-test-routing.mjs index 1ffb92f2031ae8..826f750436965b 100644 --- a/test/unit/scripts/validate-test-routing.mjs +++ b/test/unit/scripts/validate-test-routing.mjs @@ -1,6 +1,7 @@ import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; import { existsSync, readFileSync, statSync } from 'node:fs'; +import { createRequire } from 'node:module'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { @@ -20,6 +21,7 @@ const ROOT_DIR = path.resolve( ); const JEST_CONFIG = 'test/unit/jest.config.js'; const VITEST_CONFIG = 'test/unit/vitest.config.mjs'; +const require = createRequire( import.meta.url ); const manifest = JSON.parse( readFileSync( path.join( ROOT_DIR, 'test/unit/test-migration.json' ), @@ -127,15 +129,23 @@ const expectedVitestTestsByProject = getVitestTestsByProject( ); const expectedVitestTests = getVitestTests( staticInventory, manifest ); const expectedVitestTestSet = new Set( expectedVitestTests ); -const expectedJestTestsByProject = Object.fromEntries( - [ 'node', 'jsdom' ].map( ( projectName ) => [ - projectName, - staticInventory.filter( - ( testPath ) => - ! expectedVitestTestSet.has( testPath ) && - getTestEnvironmentName( testPath ) === projectName - ), - ] ) +const expectedJestTestsByProject = { + jsdom: staticInventory.filter( + ( testPath ) => + ! expectedVitestTestSet.has( testPath ) && + getTestEnvironmentName( testPath ) === 'jsdom' + ), +}; +const jestConfig = require( path.join( ROOT_DIR, JEST_CONFIG ) ); +const jestProjectNames = jestConfig.projects.map( ( project ) => + typeof project.displayName === 'string' + ? project.displayName + : project.displayName?.name +); +assert.deepEqual( + jestProjectNames.sort(), + Object.keys( expectedJestTestsByProject ).sort(), + 'Jest must only define projects for tests that have not migrated to Vitest.' ); const jestTestsByProject = Object.fromEntries( Object.keys( expectedJestTestsByProject ).map( ( projectName ) => [ diff --git a/test/unit/test-migration.json b/test/unit/test-migration.json index 6f59d5ec377a76..1ad2aeaf765366 100644 --- a/test/unit/test-migration.json +++ b/test/unit/test-migration.json @@ -1,486 +1,10 @@ { "vitest": { "files": [ - "packages/a11y/src/shared/test/filter-message.test.ts", - "packages/abilities/src/store/tests/actions.test.ts", - "packages/abilities/src/store/tests/reducer.test.ts", - "packages/abilities/src/store/tests/selectors.test.ts", - "packages/abilities/src/tests/api.test.ts", - "packages/abilities/src/tests/validation.test.ts", - "packages/annotations/src/store/test/reducer.ts", - "packages/api-fetch/src/middlewares/test/fetch-all-middleware.ts", - "packages/api-fetch/src/middlewares/test/http-v1.ts", - "packages/api-fetch/src/middlewares/test/media-upload.ts", - "packages/api-fetch/src/middlewares/test/namespace-endpoint.ts", - "packages/api-fetch/src/middlewares/test/nonce.ts", - "packages/api-fetch/src/middlewares/test/root-url.ts", - "packages/api-fetch/src/middlewares/test/user-locale.ts", - "packages/api-fetch/src/test/exports.test.ts", - "packages/autop/src/test/index.test.ts", - "packages/babel-plugin-import-jsx-pragma/test/index.js", - "packages/babel-plugin-makepot/test/index.js", - "packages/babel-preset-default/test/index.js", - "packages/block-directory/src/store/test/reducer.js", - "packages/block-editor/src/autocompleters/test/block.js", - "packages/block-editor/src/components/block-mover/test/mover-description.js", - "packages/block-editor/src/components/block-styles/test/utils.js", - "packages/block-editor/src/components/block-switcher/test/use-transformed.patterns.js", - "packages/block-editor/src/components/block-switcher/test/utils.js", - "packages/block-editor/src/components/block-visibility/test/utils.js", - "packages/block-editor/src/components/border-radius-control/test/utils.js", - "packages/block-editor/src/components/colors/test/utils.js", - "packages/block-editor/src/components/dimensions-tool/test/utils.js", - "packages/block-editor/src/components/font-sizes/test/fluid-utils.js", - "packages/block-editor/src/components/font-sizes/test/utils.js", - "packages/block-editor/src/components/global-styles/test/typography-utils.js", - "packages/block-editor/src/components/global-styles/test/utils.js", - "packages/block-editor/src/components/image-editor/test/index.js", - "packages/block-editor/src/components/inserter/test/get-appender-label.js", - "packages/block-editor/src/components/inserter/test/search-items.js", - "packages/block-editor/src/components/link-control/test/is-url-like.js", - "packages/block-editor/src/components/list-view/test/use-list-view-drop-zone.js", - "packages/block-editor/src/components/list-view/test/utils.js", - "packages/block-editor/src/components/media-placeholder/test/get-computed-accept-attribute.js", - "packages/block-editor/src/components/preset-input-control/test/utils.js", - "packages/block-editor/src/components/spacing-sizes-control/test/utils.js", - "packages/block-editor/src/components/use-block-drop-zone/test/index.js", - "packages/block-editor/src/components/use-on-block-drop/test/index.js", - "packages/block-editor/src/hooks/test/allowed-blocks.js", - "packages/block-editor/src/hooks/test/gap.js", - "packages/block-editor/src/hooks/test/generate-fields-from-attributes.js", - "packages/block-editor/src/hooks/test/generated-class-name.js", - "packages/block-editor/src/hooks/test/metadata.js", - "packages/block-editor/src/hooks/test/preset-round-trip.js", - "packages/block-editor/src/hooks/test/settings.jsx", - "packages/block-editor/src/hooks/test/state-utils.js", - "packages/block-editor/src/layouts/test/utils.js", - "packages/block-editor/src/store/test/array.js", - "packages/block-editor/src/store/test/get-block-settings.js", - "packages/block-editor/src/store/test/performance.js", - "packages/block-editor/src/store/test/private-actions.js", - "packages/block-editor/src/store/test/private-selectors.js", - "packages/block-editor/src/store/test/reducer.js", - "packages/block-editor/src/utils/test/block-bindings.js", - "packages/block-editor/src/utils/test/color-values.js", - "packages/block-editor/src/utils/test/format-font-style.js", - "packages/block-editor/src/utils/test/format-font-weight.js", - "packages/block-editor/src/utils/test/get-font-styles-and-weights.js", - "packages/block-editor/src/utils/test/math.js", - "packages/block-editor/src/utils/test/object.js", - "packages/block-editor/src/utils/test/pasting.js", - "packages/block-editor/src/utils/test/selection.js", - "packages/block-editor/src/utils/test/sorting.js", - "packages/block-editor/src/utils/test/transform-styles.js", - "packages/block-library/src/button/test/get-updated-link-attributes.js", - "packages/block-library/src/button/test/utils.js", - "packages/block-library/src/code/test/utils.js", - "packages/block-library/src/columns/test/utils.js", - "packages/block-library/src/comment-template/test/index.js", - "packages/block-library/src/gallery/test/dynamic-source.js", - "packages/block-library/src/gallery/test/responsive-styles.js", - "packages/block-library/src/gallery/test/shared.js", - "packages/block-library/src/gallery/test/variations.js", - "packages/block-library/src/heading/test/shared.js", - "packages/block-library/src/media-text/test/image-fill.js", - "packages/block-library/src/navigation-link/shared/test/update-attributes.test.js", - "packages/block-library/src/navigation-link/test/block-labelling.js", - "packages/block-library/src/navigation/edit/test/are-blocks-dirty.js", - "packages/block-library/src/navigation/edit/test/utils.js", - "packages/block-library/src/pattern/test/index.js", - "packages/block-library/src/table/test/state.js", - "packages/block-library/src/table/test/utils.js", - "packages/block-library/src/utils/test/search-patterns.js", - "packages/block-library/src/video/test/variations.js", - "packages/block-serialization-default-parser/test/index.js", - "packages/block-serialization-spec-parser/test/index.js", - "packages/blocks/src/api/parser/test/convert-legacy-block.js", - "packages/blocks/src/api/parser/test/serialize-raw-block.js", - "packages/blocks/src/api/raw-handling/test/latex-to-math.js", - "packages/blocks/src/api/raw-handling/test/markdown-converter.js", - "packages/blocks/src/api/test/factory.js", - "packages/blocks/src/api/test/registration.jsx", - "packages/blocks/src/api/test/serializer.jsx", - "packages/blocks/src/api/test/templates.js", - "packages/blocks/src/store/test/actions.js", - "packages/blocks/src/store/test/private-selectors.js", - "packages/blocks/src/store/test/reducer.js", - "packages/browserslist-config/test/index.test.js", - "packages/components/src/autocomplete/test/get-autocomplete-match.ts", - "packages/components/src/border-box-control/test/utils.ts", - "packages/components/src/context/test/context-connect.tsx", - "packages/components/src/context/test/wordpress-component.tsx", - "packages/components/src/custom-gradient-picker/test/serializer.ts", - "packages/components/src/date-time/test/utils.test.ts", - "packages/components/src/font-size-picker/test/utils.ts", - "packages/components/src/navigator/test/router.ts", - "packages/components/src/query-controls/test/terms.ts", - "packages/components/src/unit-control/test/utils.ts", - "packages/components/src/utils/test/colors.js", - "packages/components/src/utils/test/get-node-text.tsx", - "packages/components/src/utils/test/math.js", - "packages/components/src/utils/test/rtl.js", - "packages/components/src/utils/test/space.js", - "packages/components/src/utils/test/strings.js", - "packages/components/src/utils/test/unit-values.js", - "packages/compose/src/higher-order/test/compose.ts", - "packages/compose/src/higher-order/test/pipe.ts", - "packages/compose/src/hooks/use-media-query/test/ssr.jsx", - "packages/compose/src/utils/create-higher-order-component/test/index.jsx", - "packages/compose/src/utils/debounce/test/index.ts", - "packages/compose/src/utils/observable-map/test/index.js", - "packages/compose/src/utils/throttle/test/index.ts", - "packages/connectors/src/test/store.ts", - "packages/core-commands/src/utils/test/order-entity-records-by-search.js", - "packages/core-data/src/batch/test/create-batch.js", - "packages/core-data/src/batch/test/default-processor.js", - "packages/core-data/src/entity-types/test/attachment.test.ts", - "packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js", - "packages/core-data/src/fetch/test/__experimental-fetch-url-data.js", - "packages/core-data/src/locks/test/engine.js", - "packages/core-data/src/locks/test/reducer.js", - "packages/core-data/src/locks/test/selectors.js", - "packages/core-data/src/locks/test/utils.js", - "packages/core-data/src/queried-data/test/actions.js", - "packages/core-data/src/test/parsed-blocks-cache.js", - "packages/core-data/src/utils/test/clear-unchanged-edits.js", - "packages/core-data/src/utils/test/conservative-map-item.js", - "packages/core-data/src/utils/test/get-nested-value.js", - "packages/core-data/src/utils/test/get-normalized-comma-separable.js", - "packages/core-data/src/utils/test/get-pagination-meta.js", - "packages/core-data/src/utils/test/get-template-info.js", - "packages/core-data/src/utils/test/if-matching-action.js", - "packages/core-data/src/utils/test/is-numeric-id.js", - "packages/core-data/src/utils/test/log-entity-deprecation.js", - "packages/core-data/src/utils/test/replace-action.js", - "packages/core-data/src/utils/test/save-crdt-doc.js", - "packages/core-data/src/utils/test/set-nested-value.js", - "packages/core-data/src/utils/test/with-weak-map-cache.js", - "packages/data-controls/src/test/index.js", - "packages/data/src/plugins/persistence/storage/test/object.js", - "packages/data/src/plugins/persistence/test/index.js", - "packages/data/src/redux-store/metadata/test/reducer.js", - "packages/data/src/redux-store/metadata/test/selectors.js", - "packages/data/src/redux-store/metadata/test/utils.js", - "packages/data/src/redux-store/test/combine-reducers.js", - "packages/data/src/redux-store/test/index.js", - "packages/data/src/redux-store/test/keyed-reducer.js", - "packages/data/src/test/controls.js", - "packages/data/src/test/privateAPIs.js", - "packages/data/src/test/registry-selectors.js", - "packages/data/src/test/registry.js", - "packages/dataviews/src/components/dataform-controls/utils/test/get-calendar-locale.ts", - "packages/dataviews/src/components/dataform-layouts/test/normalize-form.ts", - "packages/dataviews/src/components/dataviews-layouts/utils/test/use-selection-props.ts", - "packages/dataviews/src/field-types/test/normalize-fields.ts", - "packages/dataviews/src/field-types/test/time.ts", - "packages/dataviews/src/utils/test/filter-sort-and-paginate.js", - "packages/date/src/test/index.js", - "packages/dependency-extraction-webpack-plugin/test/build.js", - "packages/dependency-extraction-webpack-plugin/test/util.js", - "packages/deprecated/src/test/index.ts", - "packages/design-system-mcp/src/test/data.ts", - "packages/design-system-mcp/src/test/format.ts", - "packages/design-system-mcp/src/test/parse-components.ts", - "packages/design-system-mcp/src/tools/test/get-component-details.ts", - "packages/docgen/test/engine.js", - "packages/docgen/test/formatter-markdown.js", - "packages/docgen/test/get-export-entries.js", - "packages/docgen/test/get-intermediate-representation.js", - "packages/docgen/test/get-jsdoc-from-token.js", - "packages/docgen/test/get-type-annotation.js", - "packages/dom/src/test/utils.js", - "packages/edit-post/src/store/test/reducer.js", - "packages/edit-site/src/utils/test/get-filtered-template-parts.js", - "packages/edit-widgets/src/store/test/reducer.js", - "packages/editor/src/components/media-categories/test/index.js", - "packages/editor/src/components/post-revisions-preview/test/preserve-client-ids.js", - "packages/editor/src/components/post-text-editor/test/utils.js", - "packages/editor/src/components/style-book/test/categories.js", - "packages/editor/src/hooks/push-changes-to-global-styles/test/format-style-value.js", - "packages/editor/src/hooks/push-changes-to-global-styles/test/style-labels.js", - "packages/editor/src/store/utils/test/notice-builder.js", - "packages/editor/src/utils/media-sideload-from-url/test/index.js", - "packages/editor/src/utils/test/device-type.js", - "packages/editor/src/utils/test/get-collaborator-display-name.ts", - "packages/editor/src/utils/test/media-upload.js", - "packages/editor/src/utils/test/normalize-text-string.js", - "packages/editor/src/utils/test/sync-error-messages.js", - "packages/editor/src/utils/test/terms.js", - "packages/element/src/test/index.jsx", - "packages/element/src/test/platform.jsx", - "packages/element/src/test/serialize.jsx", - "packages/element/src/test/utils.js", - "packages/env/lib/config/test/add-or-replace-port.js", - "packages/env/lib/config/test/config-integration.js", - "packages/env/lib/config/test/get-cache-directory.js", - "packages/env/lib/config/test/merge-configs.js", - "packages/env/lib/config/test/parse-config.js", - "packages/env/lib/config/test/parse-source-string.js", - "packages/env/lib/config/test/post-process-config.js", - "packages/env/lib/config/test/read-raw-config-file.js", - "packages/env/lib/config/test/validate-config.js", - "packages/env/lib/test/build-docker-compose-config.js", - "packages/env/lib/test/cache.js", - "packages/env/lib/test/cli.js", - "packages/env/lib/test/execute-lifecycle-script.js", - "packages/env/lib/test/md5.js", - "packages/env/lib/test/parse-spx-mode.js", - "packages/env/lib/test/parse-xdebug-mode.js", - "packages/env/lib/test/port-utils.js", - "packages/env/lib/test/resolve-available-ports.js", - "packages/escape-html/src/test/index.ts", - "packages/eslint-plugin/rules/__tests__/components-no-missing-40px-size-prop.js", - "packages/eslint-plugin/rules/__tests__/components-no-unsafe-button-disabled.js", - "packages/eslint-plugin/rules/__tests__/data-no-store-string-literals.js", - "packages/eslint-plugin/rules/__tests__/dependency-group.js", - "packages/eslint-plugin/rules/__tests__/i18n-ellipsis.js", - "packages/eslint-plugin/rules/__tests__/i18n-hyphenated-range.js", - "packages/eslint-plugin/rules/__tests__/i18n-no-collapsible-whitespace.js", - "packages/eslint-plugin/rules/__tests__/i18n-no-flanking-whitespace.js", - "packages/eslint-plugin/rules/__tests__/i18n-no-placeholders-only.js", - "packages/eslint-plugin/rules/__tests__/i18n-no-variables.js", - "packages/eslint-plugin/rules/__tests__/i18n-text-domain.js", - "packages/eslint-plugin/rules/__tests__/i18n-translator-comments.js", - "packages/eslint-plugin/rules/__tests__/no-base-control-with-label-without-id.js", - "packages/eslint-plugin/rules/__tests__/no-dom-globals-in-constructor.js", - "packages/eslint-plugin/rules/__tests__/no-dom-globals-in-module-scope.js", - "packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-cc-render.js", - "packages/eslint-plugin/rules/__tests__/no-dom-globals-in-react-fc.js", - "packages/eslint-plugin/rules/__tests__/no-ds-tokens.js", - "packages/eslint-plugin/rules/__tests__/no-global-active-element.js", - "packages/eslint-plugin/rules/__tests__/no-global-get-selection.js", - "packages/eslint-plugin/rules/__tests__/no-i18n-in-save.js", - "packages/eslint-plugin/rules/__tests__/no-non-module-stylesheet-imports.js", - "packages/eslint-plugin/rules/__tests__/no-setting-ds-tokens.js", - "packages/eslint-plugin/rules/__tests__/no-unguarded-get-range-at.js", - "packages/eslint-plugin/rules/__tests__/no-unknown-ds-tokens.js", - "packages/eslint-plugin/rules/__tests__/no-unmerged-classname.js", - "packages/eslint-plugin/rules/__tests__/no-unsafe-render-order.js", - "packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js", - "packages/eslint-plugin/rules/__tests__/no-unused-vars-before-return.js", - "packages/eslint-plugin/rules/__tests__/no-wp-process-env.js", - "packages/eslint-plugin/rules/__tests__/react-no-unsafe-timeout.js", - "packages/eslint-plugin/rules/__tests__/use-import-as.js", - "packages/eslint-plugin/rules/__tests__/use-recommended-components.js", - "packages/eslint-plugin/rules/__tests__/valid-sprintf.js", - "packages/eslint-plugin/rules/__tests__/wp-global-usage.js", - "packages/eslint-plugin/utils/test/is-package-installed.js", - "packages/format-library/src/link/test/utils.js", - "packages/global-styles-engine/src/test/get-global-styles-changes.test.ts", - "packages/global-styles-engine/src/test/render.test.ts", - "packages/global-styles-engine/src/test/resolve-style.test.ts", - "packages/global-styles-engine/src/test/style-state-back-compat.test.ts", - "packages/global-styles-engine/src/test/typography-utils.test.ts", - "packages/global-styles-engine/src/test/utils.test.ts", - "packages/global-styles-engine/src/test/variation.test.ts", - "packages/global-styles-engine/src/test/viewport-utils.test.ts", - "packages/global-styles-ui/src/font-library/utils/test/preview-styles.js", - "packages/global-styles-ui/src/test/shadow-utils.js", - "packages/global-styles-ui/src/test/utils.js", - "packages/grid/src/dashboard-grid/test/resolve-fill-widths.test.ts", - "packages/grid/src/dashboard-lanes/test/lane-placement.test.ts", - "packages/grid/src/shared/test/array-move-with-pinned.test.ts", - "packages/grid/src/shared/test/resize-snap.test.ts", "packages/html-entities/src/test/entities.jsdom.test.ts", - "packages/i18n/src/test/create-i18n.ts", - "packages/i18n/src/test/default-i18n.ts", - "packages/i18n/src/test/sprintf.ts", - "packages/i18n/src/test/subscribe-i18n.ts", - "packages/icons/lib/test/generate-library.js", - "packages/interactivity-router/src/assets/test/scs.test.ts", - "packages/interface/src/store/test/actions.js", - "packages/interface/src/store/test/reducer.js", - "packages/interface/src/store/test/selectors.js", - "packages/is-shallow-equal/test/index.ts", - "packages/kebab-case/src/test/index.js", - "packages/keycodes/src/test/platform.ts", - "packages/keycodes/src/test/with-ignore-ime-events.ts", - "packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts", - "packages/media-editor/src/image-editor/core/math/test/sanitize.ts", - "packages/media-editor/src/image-editor/core/test/camera.ts", - "packages/media-editor/src/image-editor/core/test/interaction-controller.ts", - "packages/media-editor/src/image-editor/core/test/preview-export-parity.ts", - "packages/media-editor/src/image-editor/core/test/render-camera-bridge.ts", - "packages/media-editor/src/image-editor/core/test/state.ts", - "packages/media-editor/src/image-editor/core/test/stencil-math.ts", - "packages/media-editor/src/image-editor/core/transforms/test/pipeline.ts", - "packages/media-editor/src/store/test/reducer.ts", - "packages/media-fields/src/filename/test/index.test.ts", - "packages/media-fields/src/filesize/test/index.test.tsx", - "packages/media-fields/src/media_dimensions/test/index.test.ts", - "packages/media-utils/src/utils/test/flatten-form-data.ts", - "packages/media-utils/src/utils/test/get-mime-types-array.ts", - "packages/media-utils/src/utils/test/get-upload-error-message.ts", - "packages/media-utils/src/utils/test/upload-error.ts", - "packages/notices/src/store/test/actions.ts", - "packages/notices/src/store/test/reducer.ts", - "packages/notices/src/store/test/selectors.ts", - "packages/npm-package-json-lint-config/test/index.test.js", - "packages/nux/src/store/test/actions.js", - "packages/nux/src/store/test/reducer.js", - "packages/nux/src/store/test/selectors.js", - "packages/plugins/src/api/test/index.js", - "packages/postcss-themes/test/index.js", - "packages/preferences-persistence/src/create/test/debounce-async.js", - "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/convert-edit-post-panels.js", - "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/index.js", - "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-feature-preferences.js", - "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-individual-preference.js", - "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-interface-enable-items.js", - "packages/preferences-persistence/src/migrations/legacy-local-storage-data/test/move-third-party-feature-preferences.js", - "packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-complementary-areas.js", - "packages/preferences-persistence/src/migrations/preferences-package-data/test/convert-editor-settings.js", - "packages/preferences-persistence/src/migrations/preferences-package-data/test/index.js", - "packages/preferences/src/store/test/actions.ts", - "packages/preferences/src/store/test/reducer.ts", - "packages/preferences/src/store/test/selectors.ts", - "packages/prettier-config/test/index.js", - "packages/priority-queue/src/test/index.ts", - "packages/private-apis/src/test/index.jsx", - "packages/project-management-automation/lib/tasks/add-milestone/test/index.js", - "packages/project-management-automation/lib/tasks/assign-fixed-issues/test/index.js", - "packages/project-management-automation/lib/tasks/first-time-contributor-account-link/test/index.js", - "packages/project-management-automation/lib/tasks/first-time-contributor-label/test/index.js", - "packages/project-management-automation/lib/test/get-associated-pull-request.js", - "packages/readable-js-assets-webpack-plugin/test/build.js", - "packages/redux-routine/src/test/index.js", - "packages/redux-routine/src/test/is-action.js", - "packages/redux-routine/src/test/is-generator.js", - "packages/report-flaky-tests/src/__tests__/markdown.test.ts", - "packages/report-flaky-tests/src/__tests__/run.test.ts", - "packages/rich-text/src/store/test/actions.js", - "packages/rich-text/src/store/test/reducer.js", - "packages/rich-text/src/store/test/selectors.js", - "packages/rich-text/src/test/apply-format.js", - "packages/rich-text/src/test/concat.js", - "packages/rich-text/src/test/get-active-format.js", - "packages/rich-text/src/test/get-active-object.js", - "packages/rich-text/src/test/get-format-type.js", - "packages/rich-text/src/test/get-format-types.js", - "packages/rich-text/src/test/insert-object.js", - "packages/rich-text/src/test/insert.js", - "packages/rich-text/src/test/is-collapsed.js", - "packages/rich-text/src/test/is-empty.js", - "packages/rich-text/src/test/is-format-equal.js", - "packages/rich-text/src/test/join.js", - "packages/rich-text/src/test/normalise-formats.js", - "packages/rich-text/src/test/remove-format.js", - "packages/rich-text/src/test/replace.js", - "packages/rich-text/src/test/slice.js", - "packages/rich-text/src/test/split.js", - "packages/rich-text/src/test/toggle-format.js", - "packages/scripts/scripts/test/build-blocks-manifest.js", - "packages/scripts/utils/test/index.js", - "packages/scripts/utils/test/license.js", - "packages/server-side-render/src/test/index.ts", - "packages/shortcode/src/test/index.ts", - "packages/style-engine/src/styles/test/utils.js", - "packages/style-engine/src/test/index.js", - "packages/stylelint-config/test/commenting.js", - "packages/stylelint-config/test/functions.js", - "packages/stylelint-config/test/index.js", - "packages/stylelint-config/test/media-queries.js", - "packages/stylelint-config/test/properties.js", - "packages/stylelint-config/test/scss.js", - "packages/stylelint-config/test/selectors-scss.js", - "packages/stylelint-config/test/selectors.js", - "packages/stylelint-config/test/structure.js", - "packages/stylelint-config/test/themes.js", - "packages/stylelint-config/test/values.js", - "packages/stylelint-config/test/vendor-prefixes.js", - "packages/sync/src/providers/http-polling/test/config.test.ts", - "packages/sync/src/providers/http-polling/test/utils.test.ts", - "packages/sync/src/quill-delta/test/Delta.ts", - "packages/sync/src/test/crdt-snapshot.ts", - "packages/sync/src/test/performance.test.ts", - "packages/sync/src/test/undo-manager.test.ts", - "packages/sync/src/test/utils.ts", - "packages/theme/bin/terrazzo-plugin-ds-token-fallbacks/test/index.test.ts", - "packages/theme/bin/terrazzo-plugin-mode-overrides/test/index.test.ts", - "packages/theme/postcss-plugins/test/add-fallback-to-var.test.ts", - "packages/theme/postcss-plugins/test/ds-token-fallbacks.test.ts", - "packages/theme/src/color-ramps/test/index.test.ts", - "packages/theme/src/color-ramps/test/seed-input.test.ts", - "packages/theme/src/test/build-plugin-parity.test.ts", - "packages/theme/src/test/private-apis.test.ts", - "packages/theme/src/test/theme-provider-color-warnings.test.ts", - "packages/theme/stylelint-plugins/test/no-setting-wpds-custom-properties.test.ts", - "packages/theme/stylelint-plugins/test/no-token-fallback-values.test.ts", - "packages/theme/stylelint-plugins/test/no-unknown-ds-tokens.test.ts", - "packages/token-list/src/test/index.ts", - "packages/ui/src/test/index.test.ts", - "packages/ui/src/utils/test/theme-provider.test.ts", - "packages/undo-manager/src/test/index.js", - "packages/upload-media/src/store/test/actions.ts", - "packages/upload-media/src/store/test/private-actions.js", - "packages/upload-media/src/store/test/reducer.ts", - "packages/upload-media/src/store/test/retry.ts", - "packages/upload-media/src/store/test/selectors.ts", - "packages/upload-media/src/store/utils/test/video-conversion.ts", - "packages/upload-media/src/test/canvas-utils.ts", - "packages/upload-media/src/test/convert-blob-to-file.ts", - "packages/upload-media/src/test/error-messages.ts", - "packages/upload-media/src/test/get-file-basename.ts", - "packages/upload-media/src/test/get-file-extension.ts", - "packages/upload-media/src/test/get-file-name-from-url.ts", - "packages/upload-media/src/test/get-image-dimensions.ts", - "packages/upload-media/src/test/get-mime-types-array.ts", - "packages/upload-media/src/test/heic-parser.ts", - "packages/upload-media/src/test/is-animated-gif.ts", - "packages/upload-media/src/test/rename-and-clone-file.ts", - "packages/upload-media/src/test/upload-error.ts", - "packages/video-conversion/src/test/index.ts", - "packages/viewport/src/store/test/reducer.js", - "packages/viewport/src/store/test/selectors.js", - "packages/views/src/test/resolve-view.ts", - "packages/vips/src/test/convert-image-format.ts", - "packages/vips/src/test/has-transparency.ts", - "packages/vips/src/test/highbitdepth-avif.ts", - "packages/vips/src/test/resize-image.ts", - "packages/vips/src/test/rotate-image-integration.ts", - "packages/vips/src/test/rotate-image.ts", - "packages/vips/src/test/ultrahdr.ts", - "packages/warning/src/test/index.js", - "packages/warning/test/babel-plugin.js", - "packages/widget-dashboard/src/test/create-dashboard-widget.test.ts", - "packages/widget-dashboard/src/test/get-action-route.test.ts", - "packages/widget-dashboard/src/utils/resolve-dashboard-column-count/test/resolve-dashboard-column-count.test.ts", - "packages/widget-primitives/src/field-types/test/field-types.ts", - "packages/widget-primitives/src/icon-resolver/test/icon-resolver.ts", - "packages/wordcount/src/test/index.test.ts", - "packages/worker-threads/src/test/error-propagation.test.ts", - "packages/worker-threads/src/test/main-thread.test.ts", - "packages/worker-threads/src/test/worker-thread.test.ts", - "packages/wp-build/lib/test/browserslist.js", - "packages/wp-build/lib/test/package-utils.js", - "packages/wp-build/lib/test/php-generator.js", - "packages/wp-build/lib/test/source-files.js", - "routes/dashboard/widget-host/test/match-dashboard-href.test.ts", - "storybook/test/load-error-recovery.test.ts", - "test/integration/blocks-schema.test.js", - "test/integration/theme-schema.test.js", - "test/integration/wp-env-schema.test.js", - "test/unit/config/console.vitest.test.js", "test/unit/config/emotion-compiler.vitest.jsdom.test.jsx", "test/unit/config/matchers/test/to-match-diff-snapshot.vitest.jsdom.test.js", - "test/unit/config/setup.vitest.jsdom.test.jsx", - "test/unit/config/setup.vitest.node.test.js", - "test/unit/scripts/test/discover-test-files.test.js", - "test/unit/scripts/test/resolver.js", - "test/unit/scripts/test/vitest-policy-rules.test.js", - "tools/agents/test/setup-skills.test.js", - "tools/build-scripts/test/check-esm-package-types.test.js", - "tools/build-scripts/test/process-windows.test.js", - "tools/build-scripts/test/process.test.js", - "tools/release/commands/test/changelog.js", - "tools/release/commands/test/common.js", - "tools/release/commands/test/packages.js", - "tools/release/lib/test/version.js", - "tools/release/test/cherry-pick.js", - "tools/release/test/cli.js", - "tools/release/test/resolve-performance-branches.js", - "tools/validation/validate-package-contents.test.js", - "tools/validation/validate-tsconfig.test.js" + "test/unit/config/setup.vitest.jsdom.test.jsx" ], "directories": [] } From 5a2ad8186d750eeec5870f018a52c37530dfb6cc Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 31 Aug 2026 16:36:37 +0200 Subject: [PATCH 05/13] Test: Type Jest globals without ambient declarations --- packages/jest-console/src/index.ts | 35 ++++++++++++++------ packages/jest-console/src/matchers.ts | 8 ++++- packages/jest-console/src/test/index.test.ts | 8 ----- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/packages/jest-console/src/index.ts b/packages/jest-console/src/index.ts index 6116e1f9030f74..8df2168f959550 100644 --- a/packages/jest-console/src/index.ts +++ b/packages/jest-console/src/index.ts @@ -1,7 +1,27 @@ +import type { spyOn as jestSpyOn } from 'jest-mock'; import './matchers'; import supportedMatchers from './supported-matchers'; import type { ExtendedMock } from './types'; +type MatcherName = `toHave${ 'Errored' | 'Informed' | 'Logged' | 'Warned' }`; +type ConsoleMatchers = Record< MatcherName, () => void >; + +const { + afterEach: registerAfterEach, + beforeAll: registerBeforeAll, + beforeEach: registerBeforeEach, + expect: expectConsole, + jest: jestGlobals, +} = globalThis as unknown as { + afterEach: ( callback: () => unknown ) => void; + beforeAll: ( callback: () => unknown ) => void; + beforeEach: ( callback: () => unknown ) => void; + expect: ( received: Console ) => ConsoleMatchers & { + not: ConsoleMatchers; + }; + jest: { spyOn: typeof jestSpyOn }; +}; + /** * Sets spy on the console object's method to make it possible to fail test when method called without assertion. * @@ -9,7 +29,7 @@ import type { ExtendedMock } from './types'; */ const setConsoleMethodSpy = ( args: [ string, string ] ) => { const [ methodName, matcherName ] = args; - const spy = jest + const spy = jestGlobals .spyOn( console, methodName as 'error' | 'info' | 'log' | 'warn' ) .mockName( `console.${ methodName }` ) as ExtendedMock; @@ -27,25 +47,20 @@ const setConsoleMethodSpy = ( args: [ string, string ] ) => { function assertExpectedCalls() { if ( spy.assertionsNumber === 0 && spy.mock.calls.length > 0 ) { // Using 'as' to satisfy TypeScript compiler about the matcher name. - type MatcherName = `toHave${ - | 'Errored' - | 'Informed' - | 'Logged' - | 'Warned' }`; const name = matcherName as MatcherName; - expect( console ).not[ name ](); + expectConsole( console ).not[ name ](); } } - beforeAll( resetSpy ); + registerBeforeAll( resetSpy ); - beforeEach( () => { + registerBeforeEach( () => { assertExpectedCalls(); resetSpy(); } ); - afterEach( assertExpectedCalls ); + registerAfterEach( assertExpectedCalls ); }; Object.entries( supportedMatchers ).forEach( setConsoleMethodSpy ); diff --git a/packages/jest-console/src/matchers.ts b/packages/jest-console/src/matchers.ts index 8b855e0c1f499e..1c638b7a31b0b8 100644 --- a/packages/jest-console/src/matchers.ts +++ b/packages/jest-console/src/matchers.ts @@ -92,7 +92,13 @@ type MatchersObject = Record< MatcherFunction | MatcherWithArgsFunction >; -expect.extend( +const jestExpect = ( + globalThis as unknown as { + expect: { extend: ( matchers: MatchersObject ) => void }; + } + ).expect; + +jestExpect.extend( Object.entries( supportedMatchers ).reduce< MatchersObject >( ( result, [ methodName, matcherName ] ) => { const matcherNameWith = `${ matcherName }With`; diff --git a/packages/jest-console/src/test/index.test.ts b/packages/jest-console/src/test/index.test.ts index c2c4832755f358..e54101dda951bd 100644 --- a/packages/jest-console/src/test/index.test.ts +++ b/packages/jest-console/src/test/index.test.ts @@ -13,14 +13,6 @@ import { import { spyOn as jestSpyOn } from 'jest-mock'; import type { ExtendedMock } from '../types'; -declare global { - const afterEach: typeof vitestAfterEach; - const beforeAll: typeof vitestBeforeAll; - const beforeEach: typeof vitestBeforeEach; - const expect: typeof vitestExpect; - const jest: { spyOn: typeof jestSpyOn }; -} - vi.restoreAllMocks(); vi.stubGlobal( 'afterEach', vitestAfterEach ); vi.stubGlobal( 'beforeAll', vitestBeforeAll ); From bda94dc4d6eb250320d0cce7e2186829fa97ddb0 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Mon, 31 Aug 2026 16:53:53 +0200 Subject: [PATCH 06/13] Test: Fix CI after Node migration --- packages/element/src/test/index.jsx | 3 +++ packages/element/src/test/serialize.jsx | 3 +++ packages/jest-console/src/index.ts | 11 +++++------ packages/jest-console/src/test/index.test.ts | 2 -- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/element/src/test/index.jsx b/packages/element/src/test/index.jsx index bf0298f63268d4..a4791e5338db29 100644 --- a/packages/element/src/test/index.jsx +++ b/packages/element/src/test/index.jsx @@ -1,3 +1,4 @@ +/* eslint-disable testing-library/render-result-naming-convention */ import { describe, expect, it } from 'vitest'; import { concatChildren, @@ -125,3 +126,5 @@ describe( 'element', () => { } ); } ); } ); + +/* eslint-enable testing-library/render-result-naming-convention */ diff --git a/packages/element/src/test/serialize.jsx b/packages/element/src/test/serialize.jsx index f608db6f4a0e21..5f64e5cc6de516 100644 --- a/packages/element/src/test/serialize.jsx +++ b/packages/element/src/test/serialize.jsx @@ -1,3 +1,4 @@ +/* eslint-disable testing-library/render-result-naming-convention */ import { describe, expect, it } from 'vitest'; import { Component, @@ -756,3 +757,5 @@ describe( 'renderStyle()', () => { } ); } ); } ); + +/* eslint-enable testing-library/render-result-naming-convention */ diff --git a/packages/jest-console/src/index.ts b/packages/jest-console/src/index.ts index 8df2168f959550..a9ae07b9fdb5df 100644 --- a/packages/jest-console/src/index.ts +++ b/packages/jest-console/src/index.ts @@ -1,4 +1,4 @@ -import type { spyOn as jestSpyOn } from 'jest-mock'; +import { spyOn } from 'jest-mock'; import './matchers'; import supportedMatchers from './supported-matchers'; import type { ExtendedMock } from './types'; @@ -11,7 +11,6 @@ const { beforeAll: registerBeforeAll, beforeEach: registerBeforeEach, expect: expectConsole, - jest: jestGlobals, } = globalThis as unknown as { afterEach: ( callback: () => unknown ) => void; beforeAll: ( callback: () => unknown ) => void; @@ -19,7 +18,6 @@ const { expect: ( received: Console ) => ConsoleMatchers & { not: ConsoleMatchers; }; - jest: { spyOn: typeof jestSpyOn }; }; /** @@ -29,9 +27,10 @@ const { */ const setConsoleMethodSpy = ( args: [ string, string ] ) => { const [ methodName, matcherName ] = args; - const spy = jestGlobals - .spyOn( console, methodName as 'error' | 'info' | 'log' | 'warn' ) - .mockName( `console.${ methodName }` ) as ExtendedMock; + const spy = spyOn( + console, + methodName as 'error' | 'info' | 'log' | 'warn' + ).mockName( `console.${ methodName }` ) as ExtendedMock; /** * Resets the spy to its initial state. diff --git a/packages/jest-console/src/test/index.test.ts b/packages/jest-console/src/test/index.test.ts index e54101dda951bd..5ebb1a01afc249 100644 --- a/packages/jest-console/src/test/index.test.ts +++ b/packages/jest-console/src/test/index.test.ts @@ -10,7 +10,6 @@ import { test, vi, } from 'vitest'; -import { spyOn as jestSpyOn } from 'jest-mock'; import type { ExtendedMock } from '../types'; vi.restoreAllMocks(); @@ -18,7 +17,6 @@ vi.stubGlobal( 'afterEach', vitestAfterEach ); vi.stubGlobal( 'beforeAll', vitestBeforeAll ); vi.stubGlobal( 'beforeEach', vitestBeforeEach ); vi.stubGlobal( 'expect', vitestExpect ); -vi.stubGlobal( 'jest', { spyOn: jestSpyOn } ); await import( '../index' ); From 23f70fd7ac9fe839dfcb80b7f010c85ca4a05f1a Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 10:31:55 +0200 Subject: [PATCH 07/13] Test: Address Node migration review feedback --- .../url-input/test/index.jsdom.test.jsx | 12 ++- .../block-editor/src/store/test/reducer.js | 2 +- .../env/lib/config/test/config-integration.js | 93 ++++++++++++------- .../lib/config/test/get-cache-directory.js | 9 +- .../lib/config/test/parse-source-string.js | 4 +- .../lib/config/test/read-raw-config-file.js | 10 +- packages/env/lib/test/cache.js | 16 +++- packages/env/lib/test/port-utils.js | 14 +-- .../env/lib/test/resolve-available-ports.js | 15 ++- packages/jest-console/CHANGELOG.md | 4 + .../ui/src/breadcrumb/test/layout.test.ts | 1 + packages/ui/src/breadcrumb/test/ssr.test.tsx | 3 +- .../upload-media/src/store/test/actions.ts | 12 +-- .../src/store/test/private-actions.js | 9 +- .../upload-media/src/store/test/reducer.ts | 8 +- .../upload-media/src/store/test/selectors.ts | 4 +- test/unit/scripts/test/babel-transformer.js | 9 ++ tools/eslint/config.mjs | 7 ++ tools/release/commands/test/changelog.js | 54 ++++++----- .../test/find-reusable-plugin-artifacts.js | 3 + 20 files changed, 193 insertions(+), 96 deletions(-) diff --git a/packages/block-editor/src/components/url-input/test/index.jsdom.test.jsx b/packages/block-editor/src/components/url-input/test/index.jsdom.test.jsx index b7088f39703a20..3e3463359eab5b 100644 --- a/packages/block-editor/src/components/url-input/test/index.jsdom.test.jsx +++ b/packages/block-editor/src/components/url-input/test/index.jsdom.test.jsx @@ -1,4 +1,10 @@ -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { speak } from '@wordpress/a11y'; import { useState } from '@wordpress/element'; @@ -37,8 +43,8 @@ const KEY_EVENTS = { * Waits long enough for the suggestions request debounce to elapse, so that * assertions about a request _not_ being made are meaningful. */ -function flushDebounce() { - return new Promise( ( resolve ) => setTimeout( resolve, 250 ) ); +async function flushDebounce() { + await act( () => new Promise( ( resolve ) => setTimeout( resolve, 250 ) ) ); } /** diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 42848e60834017..1d7be5e3192aa4 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -3476,7 +3476,7 @@ describe( 'state', () => { it( 'should warn about __unstableIsPreviewMode deprecation', () => { const consoleWarn = vi .spyOn( global.console, 'warn' ) - .mockImplementation(); + .mockImplementation( () => {} ); const settingsObject = settings( undefined, { type: 'UPDATE_SETTINGS', diff --git a/packages/env/lib/config/test/config-integration.js b/packages/env/lib/config/test/config-integration.js index dc18a6b01961ae..6d21cea9d97837 100644 --- a/packages/env/lib/config/test/config-integration.js +++ b/packages/env/lib/config/test/config-integration.js @@ -1,22 +1,12 @@ import { createRequire } from 'node:module'; -import { - afterAll, - afterEach, - beforeEach, - describe, - expect, - it, - vi, -} from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; const require = createRequire( import.meta.url ); -const dns = require( 'node:dns' ); -const fs = require( 'node:fs' ); -const readFile = vi.spyOn( fs.promises, 'readFile' ); -const stat = vi.spyOn( fs.promises, 'stat' ); -vi.spyOn( fs.promises, 'mkdir' ).mockResolvedValue(); -vi.spyOn( fs.promises, 'writeFile' ).mockResolvedValue(); -const existsSync = vi.spyOn( fs, 'existsSync' ); -const resolveDns = vi.spyOn( dns.promises, 'resolve' ); +const readFile = vi.fn(); +const stat = vi.fn(); +const mkdir = vi.fn(); +const writeFile = vi.fn(); +const existsSync = vi.fn(); +const resolveDns = vi.fn(); const gotPath = require.resolve( 'got' ); const originalGot = require( gotPath ); const got = vi.fn( ( url ) => ( { @@ -31,30 +21,71 @@ const got = vi.fn( ( url ) => ( { } }, } ) ); -require.cache[ gotPath ].exports = got; const detectDirectoryTypePath = require.resolve( '../detect-directory-type' ); const originalDetectDirectoryType = require( detectDirectoryTypePath ); const detectDirectoryType = vi.fn(); -require.cache[ detectDirectoryTypePath ].exports = detectDirectoryType; -const loadConfig = require( '../load-config' ); -const md5 = require( '../../md5' ); -afterAll( () => { +const loadConfigPath = require.resolve( '../load-config' ); +const parseConfigPath = require.resolve( '../parse-config' ); +const wordpressPath = require.resolve( '../../wordpress' ); +const cachePath = require.resolve( '../../cache' ); +const getCacheDirectoryPath = require.resolve( '../get-cache-directory' ); +const readRawConfigFilePath = require.resolve( '../read-raw-config-file' ); +const mockedModulePaths = [ + 'fs', + 'dns', + loadConfigPath, + parseConfigPath, + wordpressPath, + cachePath, + getCacheDirectoryPath, + readRawConfigFilePath, +]; +const originalModules = new Map( + mockedModulePaths.map( ( modulePath ) => [ + modulePath, + require.cache[ modulePath ], + ] ) +); +for ( const modulePath of mockedModulePaths ) { + delete require.cache[ modulePath ]; +} +let loadConfig; +try { + require.cache.fs = { + exports: { + promises: { readFile, stat, mkdir, writeFile }, + existsSync, + }, + }; + require.cache.dns = { + exports: { promises: { resolve: resolveDns } }, + }; + require.cache[ gotPath ].exports = got; + require.cache[ detectDirectoryTypePath ].exports = detectDirectoryType; + loadConfig = require( loadConfigPath ); +} finally { + for ( const [ modulePath, originalModule ] of originalModules ) { + if ( originalModule ) { + require.cache[ modulePath ] = originalModule; + } else { + delete require.cache[ modulePath ]; + } + } require.cache[ gotPath ].exports = originalGot; require.cache[ detectDirectoryTypePath ].exports = originalDetectDirectoryType; - vi.restoreAllMocks(); - delete require.cache[ require.resolve( '../load-config' ) ]; - delete require.cache[ require.resolve( '../parse-config' ) ]; - delete require.cache[ require.resolve( '../../wordpress' ) ]; -} ); +} +const md5 = require( '../../md5' ); describe( 'Config Integration', () => { beforeEach( () => { - vi.clearAllMocks(); + readFile.mockReset().mockRejectedValue( { code: 'ENOENT' } ); + stat.mockReset().mockResolvedValue( true ); + mkdir.mockReset().mockResolvedValue(); + writeFile.mockReset().mockResolvedValue(); + existsSync.mockReset().mockReturnValue( false ); + resolveDns.mockReset().mockResolvedValue( [ '198.143.164.252' ] ); process.env.WP_ENV_HOME = '/cache'; - stat.mockResolvedValue( true ); - existsSync.mockReturnValue( false ); - resolveDns.mockResolvedValue( [ '198.143.164.252' ] ); detectDirectoryType.mockResolvedValue( null ); } ); diff --git a/packages/env/lib/config/test/get-cache-directory.js b/packages/env/lib/config/test/get-cache-directory.js index 20f10455aa94ef..e8a39f4e82fac8 100644 --- a/packages/env/lib/config/test/get-cache-directory.js +++ b/packages/env/lib/config/test/get-cache-directory.js @@ -11,8 +11,10 @@ import { const require = createRequire( import.meta.url ); const fs = require( 'node:fs' ); const os = require( 'node:os' ); -const stat = vi.spyOn( fs.promises, 'stat' ); -const homedir = vi.spyOn( os, 'homedir' ); +const stat = vi + .spyOn( fs.promises, 'stat' ) + .mockImplementation( () => undefined ); +const homedir = vi.spyOn( os, 'homedir' ).mockImplementation( () => undefined ); const getCacheDirectory = require( '../get-cache-directory' ); afterAll( () => { @@ -21,7 +23,8 @@ afterAll( () => { describe( 'getCacheDirectory', () => { beforeEach( () => { - vi.clearAllMocks(); + stat.mockReset().mockImplementation( () => undefined ); + homedir.mockReset().mockImplementation( () => undefined ); } ); afterEach( () => { diff --git a/packages/env/lib/config/test/parse-source-string.js b/packages/env/lib/config/test/parse-source-string.js index 1de8eed8dcd394..4556507f80c677 100644 --- a/packages/env/lib/config/test/parse-source-string.js +++ b/packages/env/lib/config/test/parse-source-string.js @@ -3,7 +3,7 @@ import path from 'node:path'; import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; const require = createRequire( import.meta.url ); const os = require( 'node:os' ); -const homedir = vi.spyOn( os, 'homedir' ); +const homedir = vi.spyOn( os, 'homedir' ).mockImplementation( () => undefined ); const { ValidationError } = require( '../validate-config' ); const { parseSourceString } = require( '../parse-source-string' ); @@ -17,7 +17,7 @@ describe( 'parseSourceString', () => { }; beforeEach( () => { - homedir.mockReturnValue( '/home/test' ); + homedir.mockReset().mockReturnValue( '/home/test' ); } ); it( 'should do nothing when given an empty source', () => { diff --git a/packages/env/lib/config/test/read-raw-config-file.js b/packages/env/lib/config/test/read-raw-config-file.js index 4e52bb9f1a8bf3..f9e5e3f6e098ce 100644 --- a/packages/env/lib/config/test/read-raw-config-file.js +++ b/packages/env/lib/config/test/read-raw-config-file.js @@ -1,8 +1,10 @@ import { createRequire } from 'node:module'; -import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'; +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; const require = createRequire( import.meta.url ); const fs = require( 'node:fs' ); -const readFile = vi.spyOn( fs.promises, 'readFile' ); +const readFile = vi + .spyOn( fs.promises, 'readFile' ) + .mockImplementation( () => undefined ); const readRawConfigFile = require( '../read-raw-config-file' ); const { ValidationError } = require( '../validate-config' ); @@ -11,8 +13,8 @@ afterAll( () => { } ); describe( 'readRawConfigFile', () => { - afterEach( () => { - vi.clearAllMocks(); + beforeEach( () => { + readFile.mockReset().mockImplementation( () => undefined ); } ); it( 'returns null if it cannot find a file', async () => { diff --git a/packages/env/lib/test/cache.js b/packages/env/lib/test/cache.js index 940e837ef0cefd..8cd5f19db18e16 100644 --- a/packages/env/lib/test/cache.js +++ b/packages/env/lib/test/cache.js @@ -2,9 +2,13 @@ import { createRequire } from 'node:module'; import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; const require = createRequire( import.meta.url ); const fs = require( 'node:fs' ); -const readFile = vi.spyOn( fs.promises, 'readFile' ); -const writeFile = vi.spyOn( fs.promises, 'writeFile' ); -vi.spyOn( fs.promises, 'mkdir' ).mockResolvedValue(); +const readFile = vi + .spyOn( fs.promises, 'readFile' ) + .mockImplementation( () => undefined ); +const writeFile = vi + .spyOn( fs.promises, 'writeFile' ) + .mockImplementation( () => undefined ); +const mkdir = vi.spyOn( fs.promises, 'mkdir' ).mockResolvedValue(); const { didCacheChange, setCache, @@ -39,7 +43,10 @@ function setupWriteFile() { describe( 'cache file', () => { beforeEach( () => { - vi.clearAllMocks(); + readFile.mockReset().mockImplementation( () => undefined ); + writeFile.mockReset().mockImplementation( () => undefined ); + mkdir.mockReset().mockResolvedValue(); + setupWriteFile(); } ); describe( 'didCacheChange', () => { @@ -76,7 +83,6 @@ describe( 'cache file', () => { describe( 'setCache', () => { it( 'saves a new cache value to the file', async () => { - setupWriteFile(); await setCache( 'test', 'abc', cacheOptions ); const result = await getCacheFile( cacheOptions ); expect( result ).toEqual( { test: 'abc' } ); diff --git a/packages/env/lib/test/port-utils.js b/packages/env/lib/test/port-utils.js index 86216e5aac02e1..67f10170bebeff 100644 --- a/packages/env/lib/test/port-utils.js +++ b/packages/env/lib/test/port-utils.js @@ -1,8 +1,10 @@ import { createRequire } from 'node:module'; -import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'; +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; const require = createRequire( import.meta.url ); const net = require( 'node:net' ); -vi.spyOn( net, 'createServer' ); +const createServer = vi + .spyOn( net, 'createServer' ) + .mockImplementation( () => undefined ); const { isPortAvailable, findAvailablePort, @@ -15,8 +17,8 @@ afterAll( () => { } ); describe( 'port-utils', () => { - afterEach( () => { - vi.clearAllMocks(); + beforeEach( () => { + createServer.mockReset().mockImplementation( () => undefined ); } ); /** @@ -26,7 +28,7 @@ describe( 'port-utils', () => { * @param {Function} isAvailable A function (port) => boolean. */ function mockPortAvailability( isAvailable ) { - net.createServer.mockImplementation( () => { + createServer.mockImplementation( () => { let errorCb, listenCb; const server = { @@ -67,7 +69,7 @@ describe( 'port-utils', () => { } ); it( 'returns false for EACCES error', async () => { - net.createServer.mockImplementation( () => { + createServer.mockImplementation( () => { let errorCb; const server = { once: vi.fn( ( event, cb ) => { diff --git a/packages/env/lib/test/resolve-available-ports.js b/packages/env/lib/test/resolve-available-ports.js index 753ebddecc913a..f2377cf27db8e5 100644 --- a/packages/env/lib/test/resolve-available-ports.js +++ b/packages/env/lib/test/resolve-available-ports.js @@ -1,9 +1,13 @@ import { createRequire } from 'node:module'; -import { afterAll, afterEach, describe, expect, it, vi } from 'vitest'; +import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest'; const require = createRequire( import.meta.url ); const portUtils = require( '../port-utils' ); -const findAvailablePort = vi.spyOn( portUtils, 'findAvailablePort' ); -const isPortAvailable = vi.spyOn( portUtils, 'isPortAvailable' ); +const findAvailablePort = vi + .spyOn( portUtils, 'findAvailablePort' ) + .mockImplementation( () => undefined ); +const isPortAvailable = vi + .spyOn( portUtils, 'isPortAvailable' ) + .mockImplementation( () => undefined ); const { createPortResolver, resolveConfigPorts, @@ -14,8 +18,9 @@ afterAll( () => { } ); describe( 'resolve-available-ports', () => { - afterEach( () => { - vi.clearAllMocks(); + beforeEach( () => { + findAvailablePort.mockReset().mockImplementation( () => undefined ); + isPortAvailable.mockReset().mockImplementation( () => undefined ); } ); describe( 'createPortResolver', () => { diff --git a/packages/jest-console/CHANGELOG.md b/packages/jest-console/CHANGELOG.md index d0776e8b6dbecc..182ee2bd613cdc 100644 --- a/packages/jest-console/CHANGELOG.md +++ b/packages/jest-console/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fixes + +- Keep console spies active when Jest restores its own mocks ([#82211](https://github.com/WordPress/gutenberg/pull/82211)). + ### Internal - Update Jest matcher and mock dependencies to 30.5.0 ([#82181](https://github.com/WordPress/gutenberg/pull/82181)). diff --git a/packages/ui/src/breadcrumb/test/layout.test.ts b/packages/ui/src/breadcrumb/test/layout.test.ts index 649bcae73e8583..219e07db87c1d6 100644 --- a/packages/ui/src/breadcrumb/test/layout.test.ts +++ b/packages/ui/src/breadcrumb/test/layout.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getCollapsedLayout } from '../layout'; const BASE_METRICS = { diff --git a/packages/ui/src/breadcrumb/test/ssr.test.tsx b/packages/ui/src/breadcrumb/test/ssr.test.tsx index df988673b68109..14688db6006050 100644 --- a/packages/ui/src/breadcrumb/test/ssr.test.tsx +++ b/packages/ui/src/breadcrumb/test/ssr.test.tsx @@ -1,3 +1,4 @@ +import { describe, expect, it, type Mock } from 'vitest'; import { renderToStaticMarkup } from 'react-dom/server'; import * as Breadcrumb from '../index'; @@ -20,7 +21,7 @@ describe( 'Breadcrumb server rendering', () => { expect( view ).toContain( 'aria-current="page"' ); expect( view ).not.toContain( 'aria-haspopup="menu"' ); // eslint-disable-next-line no-console -- Inspect the captured arguments so the assertion can match React's exact warning while allowing its generated component stack. - const firstErrorCall = ( console.error as jest.Mock ).mock.calls[ 0 ]; + const firstErrorCall = ( console.error as Mock ).mock.calls[ 0 ]; expect( console ).toHaveErroredWith( "Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.%s", firstErrorCall[ 1 ] diff --git a/packages/upload-media/src/store/test/actions.ts b/packages/upload-media/src/store/test/actions.ts index f60f083b83e759..34e7dbbb72f2cb 100644 --- a/packages/upload-media/src/store/test/actions.ts +++ b/packages/upload-media/src/store/test/actions.ts @@ -53,10 +53,10 @@ vi.mock( vi.mock( import( '../utils/video-conversion' ), async ( importOriginal ) => { const actual = await importOriginal(); return { - ...actual, convertGifToVideo: vi.fn(), cancelGifToVideoOperations: vi.fn( () => Promise.resolve( true ) ), terminateVideoConversionWorker: vi.fn(), + isUnsupportedConversionError: actual.isUnsupportedConversionError, }; } ); @@ -1243,7 +1243,7 @@ describe( 'actions', () => { ).toHaveLength( 0 ); // Advance timers — the old retry timer should NOT fire. - vi.runOnlyPendingTimers(); + await vi.runAllTimersAsync(); // Queue should still be empty (timer was cleared). expect( @@ -1365,7 +1365,7 @@ describe( 'actions', () => { expect( updatedItem.status ).toBe( ItemStatus.PendingRetry ); // Fire all timers to trigger executeRetry. - vi.runOnlyPendingTimers(); + await vi.runAllTimersAsync(); // Item should now be back in Processing status with incremented retryCount. updatedItem = unlock( @@ -1542,7 +1542,7 @@ describe( 'actions', () => { // Advance timers — the old retry timer must NOT re-add or // touch the item. - vi.runOnlyPendingTimers(); + await vi.runAllTimersAsync(); expect( unlock( registry.select( uploadStore ) ).getAllItems() @@ -1573,7 +1573,7 @@ describe( 'actions', () => { // executes the retry (incrementing retryCount), then we simulate // another failure. for ( let attempt = 1; attempt <= 3; attempt++ ) { - vi.runOnlyPendingTimers(); + await vi.runAllTimersAsync(); const inProgress = unlock( registry.select( uploadStore ) @@ -1636,7 +1636,7 @@ describe( 'actions', () => { // Fire the retry timer while paused — executeRetry should bail // without mutating state. - vi.runOnlyPendingTimers(); + await vi.runAllTimersAsync(); const pausedItem = unlock( registry.select( uploadStore ) diff --git a/packages/upload-media/src/store/test/private-actions.js b/packages/upload-media/src/store/test/private-actions.js index d15da9f6edce86..f09e7691902cef 100644 --- a/packages/upload-media/src/store/test/private-actions.js +++ b/packages/upload-media/src/store/test/private-actions.js @@ -36,12 +36,15 @@ vi.mock( import( '@wordpress/blob' ), () => ( { vi.mock( import( '../utils' ), async ( importOriginal ) => { const actual = await importOriginal(); return { - ...actual, vipsHasTransparency: vi.fn(), vipsGetUltraHdrInfo: vi.fn(), vipsRotateImage: vi.fn(), terminateVipsWorker: vi.fn(), maybeRecycleVipsWorker: vi.fn(), + isAnimatedGif: actual.isAnimatedGif, + cloneFile: actual.cloneFile, + convertBlobToFile: actual.convertBlobToFile, + renameFile: actual.renameFile, }; } ); @@ -51,10 +54,12 @@ vi.mock( import( '../utils' ), async ( importOriginal ) => { vi.mock( import( '../utils/video-conversion' ), async ( importOriginal ) => { const actual = await importOriginal(); return { - ...actual, convertGifToVideo: vi.fn(), cancelGifToVideoOperations: vi.fn(), terminateVideoConversionWorker: vi.fn(), + isUnsupportedConversionError: actual.isUnsupportedConversionError, + isSizeLimitConversionError: actual.isSizeLimitConversionError, + isConversionTimeoutError: actual.isConversionTimeoutError, }; } ); diff --git a/packages/upload-media/src/store/test/reducer.ts b/packages/upload-media/src/store/test/reducer.ts index d581a4f8b4f55d..40ebb912d223a6 100644 --- a/packages/upload-media/src/store/test/reducer.ts +++ b/packages/upload-media/src/store/test/reducer.ts @@ -115,7 +115,9 @@ describe( 'reducer', () => { failureCount: 0, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [ { @@ -144,7 +146,9 @@ describe( 'reducer', () => { failureCount: 2, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, queue: [], }; diff --git a/packages/upload-media/src/store/test/selectors.ts b/packages/upload-media/src/store/test/selectors.ts index 35b6c0271c63d9..09589e1f5f7174 100644 --- a/packages/upload-media/src/store/test/selectors.ts +++ b/packages/upload-media/src/store/test/selectors.ts @@ -34,7 +34,9 @@ describe( 'selectors', () => { failureCount: 3, blobUrls: {}, settings: { - mediaUpload: jest.fn(), + mediaUpload: vi.fn(), + maxConcurrentUploads: 5, + maxConcurrentImageProcessing: 2, }, }; diff --git a/test/unit/scripts/test/babel-transformer.js b/test/unit/scripts/test/babel-transformer.js index cea0bfdc829942..bfe23028f1c546 100644 --- a/test/unit/scripts/test/babel-transformer.js +++ b/test/unit/scripts/test/babel-transformer.js @@ -68,6 +68,15 @@ describe( 'Babel transformer cache key', () => { ); expect( secondCacheKey ).not.toBe( firstCacheKey ); + + fs.writeFileSync( blockJSONPath, '{"name":"core/example"}' ); + const restoredCacheKey = transformer.getCacheKey( + 'block source', + blockIndexPath, + getTransformOptions() + ); + + expect( restoredCacheKey ).toBe( firstCacheKey ); } ); } ); diff --git a/tools/eslint/config.mjs b/tools/eslint/config.mjs index 42378a476c7682..cba3b4e9edd586 100644 --- a/tools/eslint/config.mjs +++ b/tools/eslint/config.mjs @@ -10,6 +10,10 @@ import testingLibraryPlugin from 'eslint-plugin-testing-library'; import jestPlugin from 'eslint-plugin-jest'; import tseslint from 'typescript-eslint'; import wpBuildConfig from '../../packages/wp-build/eslint-overrides.cjs'; +import { + discoverTestFiles, + getTestEnvironmentName, +} from '../../test/unit/scripts/discover-test-files.mjs'; const require = createRequire( import.meta.url ); const rootDir = resolve( import.meta.dirname, '../..' ); const wpPlugin = require( '@wordpress/eslint-plugin' ); @@ -18,6 +22,9 @@ const testMigration = require( ); const vitestTestPatterns = [ + ...discoverTestFiles( rootDir ).filter( + ( testPath ) => getTestEnvironmentName( testPath ) === 'node' + ), ...testMigration.vitest.files, ...testMigration.vitest.directories.flatMap( ( directory ) => [ `${ directory }/**/__tests__/**/*.[jt]s?(x)`, diff --git a/tools/release/commands/test/changelog.js b/tools/release/commands/test/changelog.js index 8a0967b7f9a670..093a284e4b259b 100644 --- a/tools/release/commands/test/changelog.js +++ b/tools/release/commands/test/changelog.js @@ -1,30 +1,44 @@ import { createRequire } from 'node:module'; -import { afterAll, beforeEach, describe, expect, it, test, vi } from 'vitest'; +import { beforeEach, describe, expect, it, test, vi } from 'vitest'; import _pullRequests from './fixtures/pull-requests.json'; import botPullRequestFixture from './fixtures/bot-pull-requests.json'; const require = createRequire( import.meta.url ); +const octokitPath = require.resolve( '@octokit/rest' ); const octokitModule = require( '@octokit/rest' ); -const originalOctokit = octokitModule.Octokit; const Octokit = vi.fn(); -octokitModule.Octokit = Octokit; +const milestonePath = require.resolve( '../../lib/milestone' ); const milestoneModule = require( '../../lib/milestone' ); -const originalGetMilestoneByTitle = milestoneModule.getMilestoneByTitle; -const originalGetIssuesByMilestone = milestoneModule.getIssuesByMilestone; const getMilestoneByTitle = vi.fn(); const getIssuesByMilestone = vi.fn(); -milestoneModule.getMilestoneByTitle = getMilestoneByTitle; -milestoneModule.getIssuesByMilestone = getIssuesByMilestone; +const loggerPath = require.resolve( '../../lib/logger' ); const loggerModule = require( '../../lib/logger' ); -const originalLogger = { ...loggerModule }; const log = vi.fn(); const warn = vi.fn(); -loggerModule.log = log; -loggerModule.warn = warn; -loggerModule.formats = { - title: vi.fn( ( message ) => message ), - error: vi.fn( ( message ) => message ), - warning: vi.fn( ( message ) => message ), -}; +const changelogPath = require.resolve( '../changelog' ); +let changelog; +try { + require.cache[ octokitPath ].exports = { ...octokitModule, Octokit }; + require.cache[ milestonePath ].exports = { + getMilestoneByTitle, + getIssuesByMilestone, + }; + require.cache[ loggerPath ].exports = { + log, + warn, + formats: { + title: vi.fn( ( message ) => message ), + error: vi.fn( ( message ) => message ), + warning: vi.fn( ( message ) => message ), + success: vi.fn( ( message ) => message ), + }, + }; + changelog = require( changelogPath ); +} finally { + require.cache[ octokitPath ].exports = octokitModule; + require.cache[ milestonePath ].exports = milestoneModule; + require.cache[ loggerPath ].exports = loggerModule; + delete require.cache[ changelogPath ]; +} const { getNormalizedTitle, reword, @@ -47,15 +61,7 @@ const { createChangelog, fetchAllPullRequests, getManualChangelogInstructions, -} = require( '../changelog' ); - -afterAll( () => { - octokitModule.Octokit = originalOctokit; - milestoneModule.getMilestoneByTitle = originalGetMilestoneByTitle; - milestoneModule.getIssuesByMilestone = originalGetIssuesByMilestone; - Object.assign( loggerModule, originalLogger ); - delete require.cache[ require.resolve( '../changelog' ) ]; -} ); +} = changelog; /** * pull-requests.json is a static snapshot of real data from the GitHub API. diff --git a/tools/release/test/find-reusable-plugin-artifacts.js b/tools/release/test/find-reusable-plugin-artifacts.js index d2ccc28b0be93c..2ec171a2f1047d 100644 --- a/tools/release/test/find-reusable-plugin-artifacts.js +++ b/tools/release/test/find-reusable-plugin-artifacts.js @@ -1,3 +1,6 @@ +import { createRequire } from 'node:module'; +import { describe, expect, it } from 'vitest'; +const require = createRequire( import.meta.url ); const { findReusableRuns } = require( '../find-reusable-plugin-artifacts.mjs' ); const sha = 'a'.repeat( 40 ); From 29e8ab6918d0598ed11efa831fbef9fc6ce4041f Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 10:52:59 +0200 Subject: [PATCH 08/13] Test: Migrate newly added Node tests to Vitest --- package-lock.json | 4 +- .../src/__tests__/index.test.ts | 12 +++--- tools/pr-meta/package.json | 4 +- tools/pr-meta/src/__tests__/comment.test.ts | 1 + tools/pr-meta/src/__tests__/run.test.ts | 38 ++++++++++++------- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index ba1c108f68e706..ae2950dfd22509 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55892,8 +55892,8 @@ "version": "0.0.0", "license": "GPL-2.0-or-later", "devDependencies": { - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "engines": { "node": ">=18.12.0", diff --git a/packages/report-flaky-tests/src/__tests__/index.test.ts b/packages/report-flaky-tests/src/__tests__/index.test.ts index 769b54a7b44cb1..3440947c5cd4ee 100644 --- a/packages/report-flaky-tests/src/__tests__/index.test.ts +++ b/packages/report-flaky-tests/src/__tests__/index.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it, vi } from 'vitest'; + /** * Loads the entry point against a fresh set of mocks. * @@ -7,12 +9,12 @@ * @return The mocked core module, to assert against. */ async function loadEntryPoint( result: () => Promise< void > ) { - jest.resetModules(); - jest.doMock( '@actions/core', () => ( { setFailed: jest.fn() } ) ); - jest.doMock( '../run.ts', () => ( { run: result } ) ); + vi.resetModules(); + vi.doMock( '@actions/core', () => ( { setFailed: vi.fn() } ) ); + vi.doMock( '../run.ts', () => ( { run: result } ) ); - const core = require( '@actions/core' ); - require( '../index.ts' ); + const core = await import( '@actions/core' ); + await import( '../index.ts' ); await new Promise( process.nextTick ); return core; diff --git a/tools/pr-meta/package.json b/tools/pr-meta/package.json index caa87e42f2e3b2..5b9193af1f5ad5 100644 --- a/tools/pr-meta/package.json +++ b/tools/pr-meta/package.json @@ -26,8 +26,8 @@ "type": "module", "main": "src/index.ts", "devDependencies": { - "@types/jest": "^30.0.0", - "@types/node": "^20.19.39" + "@types/node": "^20.19.39", + "vitest": "^4.1.10" }, "publishConfig": { "access": "public" diff --git a/tools/pr-meta/src/__tests__/comment.test.ts b/tools/pr-meta/src/__tests__/comment.test.ts index 87177860de86bb..73f27a8f8529d1 100644 --- a/tools/pr-meta/src/__tests__/comment.test.ts +++ b/tools/pr-meta/src/__tests__/comment.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { COMMENT_MARKER, isParseable, diff --git a/tools/pr-meta/src/__tests__/run.test.ts b/tools/pr-meta/src/__tests__/run.test.ts index 7e798947853b85..875ec6f2a8f56d 100644 --- a/tools/pr-meta/src/__tests__/run.test.ts +++ b/tools/pr-meta/src/__tests__/run.test.ts @@ -1,19 +1,30 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { run } from '../run.ts'; -import { GitHubAPI } from '../github-api.ts'; -jest.mock( '../github-api.ts', () => ( { - GitHubAPI: jest.fn(), +const api = vi.hoisted( () => ( { + getHeadSha: vi.fn(), + findComment: vi.fn(), + createComment: vi.fn(), + updateComment: vi.fn(), + deleteComment: vi.fn(), } ) ); -const HEAD = 'a'.repeat( 40 ); +vi.mock( import( '../github-api.ts' ), async ( importOriginal ) => { + const original = await importOriginal(); + + return { + ...original, + GitHubAPI: vi.fn( + class MockGitHubAPI { + constructor() { + return api; + } + } + ) as unknown as typeof original.GitHubAPI, + }; +} ); -const api = { - getHeadSha: jest.fn(), - findComment: jest.fn(), - createComment: jest.fn(), - updateComment: jest.fn(), - deleteComment: jest.fn(), -}; +const HEAD = 'a'.repeat( 40 ); function withInputs( inputs: Record< string, string > ) { for ( const [ name, value ] of Object.entries( inputs ) ) { @@ -23,8 +34,9 @@ function withInputs( inputs: Record< string, string > ) { describe( 'run', () => { beforeEach( () => { - jest.clearAllMocks(); - ( GitHubAPI as jest.Mock ).mockImplementation( () => api ); + for ( const mock of Object.values( api ) ) { + mock.mockReset(); + } process.env.GITHUB_REPOSITORY = 'WordPress/gutenberg'; api.getHeadSha.mockResolvedValue( HEAD ); api.findComment.mockResolvedValue( undefined ); From 86a76c8f71bc8ff01754290d77c399d9b6d3ce65 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 11:00:56 +0200 Subject: [PATCH 09/13] Docs: Add Vitest snapshot and watch commands --- docs/contributors/code/testing-overview.md | 30 +++++++++++++++++----- package.json | 2 ++ test/unit/package.json | 2 ++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/contributors/code/testing-overview.md b/docs/contributors/code/testing-overview.md index 673173cd6bf81b..e5bef3250a8b91 100644 --- a/docs/contributors/code/testing-overview.md +++ b/docs/contributors/code/testing-overview.md @@ -325,8 +325,12 @@ When a snapshot test fails, it just means that a component's rendering has chang However, if the change was intentional, follow these steps to update the snapshot. Run the following to update the snapshots: ```sh -# --testPathPatterns is optional but will be much faster by only running matching tests -npm run test:unit -- --updateSnapshot --testPathPatterns path/to/tests +# Update snapshots for Node or Vitest-owned DOM tests +npm run test:unit:vitest:update -- path/to/tests + +# Update snapshots for Jest-owned JSDOM tests +# --testPathPatterns is optional but runs only matching tests +npm run test:unit:update -- --testPathPatterns path/to/tests # Update snapshot for e2e tests npm run test:e2e -- --update-snapshots path/to/spec @@ -402,15 +406,29 @@ Reducer tests are also a great fit for snapshots. They are often large, complex #### Working with snapshots -You might be blindsided by CI tests failing when snapshots don't match. You'll need to [update snapshots] if the changes are expected. The quick and dirty solution is to invoke Jest with `--updateSnapshot`. That can be done as follows: +You might be blindsided by CI tests failing when snapshots don't match. You'll need to [update snapshots] if the changes are expected. Use the command for the test's runner: ```sh -npm run test:unit -- --updateSnapshot --testPathPatterns path/to/tests +# Node or Vitest-owned DOM tests +npm run test:unit:vitest:update -- path/to/tests + +# Jest-owned JSDOM tests +npm run test:unit:update -- --testPathPatterns path/to/tests ``` -`--testPathPatterns` is not required, but specifying a path will speed things up by running a subset of tests. +The path is not required, but specifying one runs only matching tests and is faster. + +Keep the applicable watch command running in the background as you work: + +```sh +# Node or Vitest-owned DOM tests +npm run test:unit:vitest:watch -- path/to/tests + +# Jest-owned JSDOM tests +npm run test:unit:watch -- --testPathPatterns path/to/tests +``` -It's a great idea to keep `npm run test:unit:watch` running in the background as you work. Jest will run only the relevant tests for changed files, and when snapshot tests fail, just hit `u` to update a snapshot! +When a snapshot test fails in either runner, press `u` to update the snapshot. #### Pain points diff --git a/package.json b/package.json index 04e4f07062ae87..54262d04368c52 100644 --- a/package.json +++ b/package.json @@ -179,6 +179,8 @@ "test:unit": "npm run --workspace @wordpress/unit-tests test:unit --", "test:unit:conventions": "npm run --workspace @wordpress/unit-tests test:unit:conventions", "test:unit:vitest": "npm run --workspace @wordpress/unit-tests test:unit:vitest --", + "test:unit:vitest:update": "npm run --workspace @wordpress/unit-tests test:unit:vitest:update --", + "test:unit:vitest:watch": "npm run --workspace @wordpress/unit-tests test:unit:vitest:watch --", "test:unit:routing": "npm run --workspace @wordpress/unit-tests test:unit:routing", "test:unit:date": "bash ./bin/unit-test-date.sh", "test:unit:debug": "npm run --workspace @wordpress/unit-tests test:unit:debug --", diff --git a/test/unit/package.json b/test/unit/package.json index 47e4d388669687..8ec5f66967e135 100644 --- a/test/unit/package.json +++ b/test/unit/package.json @@ -63,6 +63,8 @@ "test:unit": "wp-scripts test-unit-js --config jest.config.js", "test:unit:conventions": "node scripts/validate-vitest-conventions.mjs", "test:unit:vitest": "vitest run --config vitest.config.mjs", + "test:unit:vitest:update": "vitest run --config vitest.config.mjs --update=true", + "test:unit:vitest:watch": "vitest --config vitest.config.mjs", "test:unit:profile": "wp-scripts --cpu-prof test-unit-js --runInBand --no-cache --verbose --config jest.config.js", "test:unit:watch": "npm run test:unit -- --watch", "test:unit:debug": "wp-scripts --inspect-brk test-unit-js --runInBand --no-cache --verbose --config jest.config.js", From c7be58dcd957672078a8b3a43bbc97ac1b263199 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 11:11:51 +0200 Subject: [PATCH 10/13] Test: Fix Vitest lint overrides --- packages/element/src/test/index.jsx | 3 --- packages/element/src/test/serialize.jsx | 3 --- packages/jest-console/src/test/index.test.ts | 1 + 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/element/src/test/index.jsx b/packages/element/src/test/index.jsx index a4791e5338db29..bf0298f63268d4 100644 --- a/packages/element/src/test/index.jsx +++ b/packages/element/src/test/index.jsx @@ -1,4 +1,3 @@ -/* eslint-disable testing-library/render-result-naming-convention */ import { describe, expect, it } from 'vitest'; import { concatChildren, @@ -126,5 +125,3 @@ describe( 'element', () => { } ); } ); } ); - -/* eslint-enable testing-library/render-result-naming-convention */ diff --git a/packages/element/src/test/serialize.jsx b/packages/element/src/test/serialize.jsx index 5f64e5cc6de516..f608db6f4a0e21 100644 --- a/packages/element/src/test/serialize.jsx +++ b/packages/element/src/test/serialize.jsx @@ -1,4 +1,3 @@ -/* eslint-disable testing-library/render-result-naming-convention */ import { describe, expect, it } from 'vitest'; import { Component, @@ -757,5 +756,3 @@ describe( 'renderStyle()', () => { } ); } ); } ); - -/* eslint-enable testing-library/render-result-naming-convention */ diff --git a/packages/jest-console/src/test/index.test.ts b/packages/jest-console/src/test/index.test.ts index 5ebb1a01afc249..800f77bfd02283 100644 --- a/packages/jest-console/src/test/index.test.ts +++ b/packages/jest-console/src/test/index.test.ts @@ -98,6 +98,7 @@ describe( 'jest-console', () => { // own lifecycle is defined to run before ours. Infer // that we're being watched by testing the console // method as being a spy. + // eslint-disable-next-line jest/no-standalone-expect vitestExpect( getSpy( methodName ).assertionsNumber ).toBeGreaterThanOrEqual( 0 ); From 8cdd7ae49c11e52a65040dbf8a7e3e121e9c7c6b Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 11:37:32 +0200 Subject: [PATCH 11/13] Test: Address Copilot review feedback --- package-lock.json | 1 + packages/jest-console/package.json | 1 + .../test/fixtures/jest-restore-all-mocks.cjs | 15 +++++++ packages/jest-console/src/test/index.test.ts | 41 +++++++++++++++++++ packages/jest-console/tsconfig.json | 3 ++ packages/wp-build/lib/test/browserslist.js | 16 +++++--- 6 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 packages/jest-console/src/test/fixtures/jest-restore-all-mocks.cjs diff --git a/package-lock.json b/package-lock.json index ae2950dfd22509..4161d7192b2a7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52536,6 +52536,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", + "@types/node": "^20.19.39", "vitest": "^4.1.10" }, "engines": { diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json index 1831c985aba9fc..71b3eb90daf3bc 100644 --- a/packages/jest-console/package.json +++ b/packages/jest-console/package.json @@ -46,6 +46,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", + "@types/node": "^20.19.39", "vitest": "^4.1.10" }, "peerDependencies": { diff --git a/packages/jest-console/src/test/fixtures/jest-restore-all-mocks.cjs b/packages/jest-console/src/test/fixtures/jest-restore-all-mocks.cjs new file mode 100644 index 00000000000000..4eaa65485a9aee --- /dev/null +++ b/packages/jest-console/src/test/fixtures/jest-restore-all-mocks.cjs @@ -0,0 +1,15 @@ +/* global jest */ +const { expect, test } = require( '@jest/globals' ); + +test( 'keeps console spies active when Jest restores runtime mocks', () => { + // eslint-disable-next-line no-console + const errorSpy = console.error; + + jest.restoreAllMocks(); + + // eslint-disable-next-line no-console + expect( console.error ).toBe( errorSpy ); + // eslint-disable-next-line no-console + console.error( 'Expected error.' ); + expect( console ).toHaveErroredWith( 'Expected error.' ); +} ); diff --git a/packages/jest-console/src/test/index.test.ts b/packages/jest-console/src/test/index.test.ts index 800f77bfd02283..54dd9016046ae6 100644 --- a/packages/jest-console/src/test/index.test.ts +++ b/packages/jest-console/src/test/index.test.ts @@ -1,4 +1,7 @@ /* eslint-disable no-console */ +import { spawnSync } from 'node:child_process'; +import { createRequire } from 'node:module'; +import { dirname, join } from 'node:path'; import { afterAll, afterEach as vitestAfterEach, @@ -12,6 +15,8 @@ import { } from 'vitest'; import type { ExtendedMock } from '../types'; +const require = createRequire( import.meta.url ); + vi.restoreAllMocks(); vi.stubGlobal( 'afterEach', vitestAfterEach ); vi.stubGlobal( 'beforeAll', vitestBeforeAll ); @@ -28,6 +33,42 @@ function getSpy( methodName: 'error' | 'info' | 'log' | 'warn' ) { } describe( 'jest-console', () => { + it( 'keeps its spies active when Jest restores runtime mocks', () => { + const jestPackageDirectory = dirname( + require.resolve( 'jest/package.json' ) + ); + const fixturePath = join( + import.meta.dirname, + 'fixtures/jest-restore-all-mocks.cjs' + ); + const config = JSON.stringify( { + rootDir: process.cwd(), + setupFilesAfterEnv: [ + require.resolve( '@wordpress/jest-console' ), + ], + testEnvironment: 'node', + testRegex: 'jest-restore-all-mocks\\.cjs$', + } ); + const result = spawnSync( + process.execPath, + [ + join( jestPackageDirectory, 'bin/jest.js' ), + '--config', + config, + '--runInBand', + '--runTestsByPath', + fixturePath, + ], + { encoding: 'utf8' } + ); + + if ( result.status !== 0 ) { + throw new Error( `${ result.stdout }\n${ result.stderr }` ); + } + + vitestExpect( result.status ).toBe( 0 ); + } ); + describe.each( [ [ 'error', 'toHaveErrored' ], [ 'info', 'toHaveInformed' ], diff --git a/packages/jest-console/tsconfig.json b/packages/jest-console/tsconfig.json index 11f488c262ff66..4ebe52e8789889 100644 --- a/packages/jest-console/tsconfig.json +++ b/packages/jest-console/tsconfig.json @@ -1,6 +1,9 @@ { "$schema": "https://json.schemastore.org/tsconfig.json", "extends": "../../tsconfig.dev.base.json", + "compilerOptions": { + "types": [ "jest", "node" ] + }, "include": [ "src" ], "files": [ "declarations.d.ts" ] } diff --git a/packages/wp-build/lib/test/browserslist.js b/packages/wp-build/lib/test/browserslist.js index 2b88970fdd219a..3619529fc0c443 100644 --- a/packages/wp-build/lib/test/browserslist.js +++ b/packages/wp-build/lib/test/browserslist.js @@ -4,12 +4,16 @@ import wordpressBrowserslistConfig from '@wordpress/browserslist-config'; import { getBrowserslistQueries } from '../browserslist.mjs'; describe( 'browserslist targeting', () => { + let findConfigSpy; + afterEach( () => { - vi.restoreAllMocks(); + findConfigSpy.mockRestore(); } ); it( 'falls back to @wordpress/browserslist-config when the project has no config', () => { - vi.spyOn( browserslist, 'findConfig' ).mockReturnValue( undefined ); + findConfigSpy = vi + .spyOn( browserslist, 'findConfig' ) + .mockReturnValue( undefined ); expect( getBrowserslistQueries() ).toEqual( wordpressBrowserslistConfig @@ -17,9 +21,11 @@ describe( 'browserslist targeting', () => { } ); it( 'uses the project browserslist config when one is present', () => { - vi.spyOn( browserslist, 'findConfig' ).mockReturnValue( { - defaults: [ 'last 1 chrome version' ], - } ); + findConfigSpy = vi + .spyOn( browserslist, 'findConfig' ) + .mockReturnValue( { + defaults: [ 'last 1 chrome version' ], + } ); expect( getBrowserslistQueries() ).toEqual( [ 'last 1 chrome version', From ac5d63769ead7996132fc4d7ee7fd91d7c9cc4a6 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 11:54:56 +0200 Subject: [PATCH 12/13] Test: Preserve Vitest table labels --- .../test/build-modifiers.test.ts | 36 ++++++++----- .../core/test/preview-export-parity.ts | 53 +++++++++++-------- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts b/packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts index 4be7bbbbdbf80f..e0a57141664a59 100644 --- a/packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts +++ b/packages/media-editor/src/components/media-editor-modal/test/build-modifiers.test.ts @@ -331,21 +331,29 @@ function outputSizeFor( modifiers: Modifier[], imageSize: Size ): Size { describe( 'buildModifiers ↔ Export-camera parity', () => { const rows = buildRows(); - it.each( rows )( '$label', ( { state, u, v } ) => { - const modifiers = buildModifiers( state, IMAGE ); + for ( const { label, state, u, v } of rows ) { + it( label, () => { + const modifiers = buildModifiers( state, IMAGE ); - // Output canvas is whatever the server would emit: the crop rect - // if present, otherwise the full rotated AABB. This makes (u, v) - // map to the same *output* pixel on both paths. - const outputSize = outputSizeFor( modifiers, IMAGE ); + // Output canvas is whatever the server would emit: the crop rect + // if present, otherwise the full rotated AABB. This makes (u, v) + // map to the same *output* pixel on both paths. + const outputSize = outputSizeFor( modifiers, IMAGE ); - const exported = exportSourcePixel( state, IMAGE, outputSize, u, v ); - const server = serverSourcePixel( modifiers, IMAGE, u, v ); + const exported = exportSourcePixel( + state, + IMAGE, + outputSize, + u, + v + ); + const server = serverSourcePixel( modifiers, IMAGE, u, v ); - // Tolerance of 1 source pixel. The two paths compose rotations - // and scales in different orders, so sub-pixel disagreement is - // expected from floating-point. Anything larger is a real bug. - expect( server.x ).toBeCloseTo( exported.x, 0 ); - expect( server.y ).toBeCloseTo( exported.y, 0 ); - } ); + // Tolerance of 1 source pixel. The two paths compose rotations + // and scales in different orders, so sub-pixel disagreement is + // expected from floating-point. Anything larger is a real bug. + expect( server.x ).toBeCloseTo( exported.x, 0 ); + expect( server.y ).toBeCloseTo( exported.y, 0 ); + } ); + } } ); diff --git a/packages/media-editor/src/image-editor/core/test/preview-export-parity.ts b/packages/media-editor/src/image-editor/core/test/preview-export-parity.ts index 93a6e3b5c0e770..b05b4ce3997acb 100644 --- a/packages/media-editor/src/image-editor/core/test/preview-export-parity.ts +++ b/packages/media-editor/src/image-editor/core/test/preview-export-parity.ts @@ -125,8 +125,8 @@ const PROBES: { label: string; u: number; v: number }[] = [ { label: 'interior-2', u: 0.8, v: 0.25 }, ]; -// All (state × output-size × probe) combinations flattened into rows -// for `it.each`. Each row is self-describing; on failure, the label +// All (state × output-size × probe) combinations flattened into rows. +// Each row is self-describing; on failure, the label // pinpoints the exact state and probe point where preview / export // disagreed. interface ParityRow { @@ -217,26 +217,33 @@ describe( 'Preview ↔ Export parity', () => { // miss. const rows = buildRows(); - it.each( rows )( '$label', ( { state, outputSize, u, v } ) => { - const screen = stencilScreenPoint( state, CONTAINER, IMAGE, u, v ); - const outputPoint = { - x: u * outputSize.width, - y: v * outputSize.height, - }; - const preview = previewSourcePixel( state, CONTAINER, IMAGE, screen ); - const exported = exportSourcePixel( - state, - IMAGE, - outputSize, - outputPoint - ); + for ( const { label, state, outputSize, u, v } of rows ) { + it( label, () => { + const screen = stencilScreenPoint( state, CONTAINER, IMAGE, u, v ); + const outputPoint = { + x: u * outputSize.width, + y: v * outputSize.height, + }; + const preview = previewSourcePixel( + state, + CONTAINER, + IMAGE, + screen + ); + const exported = exportSourcePixel( + state, + IMAGE, + outputSize, + outputPoint + ); - // Tolerance of 1 source pixel. Intermediate scale factors differ - // between the preview (container pixels) and export (output - // pixels) paths, so sub-pixel agreement is unrealistic. A drift - // of ~1px is still visibly correct; anything larger is a real - // coordinate-system mismatch. - expect( preview.x ).toBeCloseTo( exported.x, 0 ); - expect( preview.y ).toBeCloseTo( exported.y, 0 ); - } ); + // Tolerance of 1 source pixel. Intermediate scale factors differ + // between the preview (container pixels) and export (output + // pixels) paths, so sub-pixel agreement is unrealistic. A drift + // of ~1px is still visibly correct; anything larger is a real + // coordinate-system mismatch. + expect( preview.x ).toBeCloseTo( exported.x, 0 ); + expect( preview.y ).toBeCloseTo( exported.y, 0 ); + } ); + } } ); From 96d135f5dc35a560be9ec57aa1d8cf55cdad72ae Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 1 Sep 2026 12:07:28 +0200 Subject: [PATCH 13/13] Test: Run Jest compatibility probe from source --- packages/jest-console/src/test/index.test.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/jest-console/src/test/index.test.ts b/packages/jest-console/src/test/index.test.ts index 54dd9016046ae6..a4cf47397253c1 100644 --- a/packages/jest-console/src/test/index.test.ts +++ b/packages/jest-console/src/test/index.test.ts @@ -41,13 +41,18 @@ describe( 'jest-console', () => { import.meta.dirname, 'fixtures/jest-restore-all-mocks.cjs' ); + const sourceEntryPath = join( import.meta.dirname, '../index.ts' ); const config = JSON.stringify( { rootDir: process.cwd(), - setupFilesAfterEnv: [ - require.resolve( '@wordpress/jest-console' ), - ], + setupFilesAfterEnv: [ sourceEntryPath ], testEnvironment: 'node', testRegex: 'jest-restore-all-mocks\\.cjs$', + transform: { + '^.+\\.m?[jt]sx?$': join( + process.cwd(), + 'test/unit/scripts/babel-transformer.js' + ), + }, } ); const result = spawnSync( process.execPath,