diff --git a/package-lock.json b/package-lock.json index 72f0558b..cf6b7a75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "@cloudscape-design/test-utils-converter": "^1.0.0", "@cloudscape-design/theming-build": "^1", "@juggle/resize-observer": "^3.4.0", + "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@types/jest-image-snapshot": "^6.1.0", "@types/lodash": "^4.14.191", @@ -66,6 +67,12 @@ "react-dom": "^18.2.0" } }, + "node_modules/@adobe/css-tools": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", + "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", + "dev": true + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -1073,6 +1080,55 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.14.3.tgz", + "integrity": "sha512-SlsbRC/RX+/zg4AApWIFNDdkLtFbkq3LNoZWXZCE/nHVKqoIJyaoQyge/I0Y38vLxowUn9KTtXgusLD91+orbg==", + "dev": true, + "dependencies": { + "@formatjs/intl-localematcher": "0.2.32", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.0.1.tgz", + "integrity": "sha512-M2GgV+qJn5WJQAYewz7q2Cdl6fobQa69S1AzSM2y0P68ZDbK5cWrJIcPCO395Of1ksftGZoOt4LYCO/j9BKBSA==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.3.0.tgz", + "integrity": "sha512-xqtlqYAbfJDF4b6e4O828LBNOWXrFcuYadqAbYORlDRwhyJ2bH+xpUBPldZbzRGUN2mxlZ4Ykhm7jvERtmI8NQ==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.14.3", + "@formatjs/icu-skeleton-parser": "1.3.18", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.3.18", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.18.tgz", + "integrity": "sha512-ND1ZkZfmLPcHjAH1sVpkpQxA+QYfOX3py3SjKWMUVGDow18gZ0WPqz3F+pJLYQMpS2LnnQ5zYR2jPVYTbRwMpg==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.14.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.2.32", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz", + "integrity": "sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -2218,6 +2274,95 @@ "node": ">=8" } }, + "node_modules/@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@testing-library/react": { "version": "13.4.0", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", @@ -2539,6 +2684,15 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", + "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "dev": true, + "dependencies": { + "@types/jest": "*" + } + }, "node_modules/@types/ua-parser-js": { "version": "0.7.36", "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz", @@ -7030,6 +7184,18 @@ "node": ">= 0.10" } }, + "node_modules/intl-messageformat": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.3.3.tgz", + "integrity": "sha512-un/f07/g2e/3Q8e1ghDKET+el22Bi49M7O/rHxd597R+oLpPOMykSv5s51cABVfu3FZW+fea4hrzf2MHu1W4hw==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "1.14.3", + "@formatjs/fast-memoize": "2.0.1", + "@formatjs/icu-messageformat-parser": "2.3.0", + "tslib": "^2.4.0" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -12466,6 +12632,19 @@ "node": ">= 0.10" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -14789,6 +14968,12 @@ } }, "dependencies": { + "@adobe/css-tools": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", + "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", + "dev": true + }, "@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -15421,6 +15606,55 @@ } } }, + "@formatjs/ecma402-abstract": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.14.3.tgz", + "integrity": "sha512-SlsbRC/RX+/zg4AApWIFNDdkLtFbkq3LNoZWXZCE/nHVKqoIJyaoQyge/I0Y38vLxowUn9KTtXgusLD91+orbg==", + "dev": true, + "requires": { + "@formatjs/intl-localematcher": "0.2.32", + "tslib": "^2.4.0" + } + }, + "@formatjs/fast-memoize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.0.1.tgz", + "integrity": "sha512-M2GgV+qJn5WJQAYewz7q2Cdl6fobQa69S1AzSM2y0P68ZDbK5cWrJIcPCO395Of1ksftGZoOt4LYCO/j9BKBSA==", + "dev": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.3.0.tgz", + "integrity": "sha512-xqtlqYAbfJDF4b6e4O828LBNOWXrFcuYadqAbYORlDRwhyJ2bH+xpUBPldZbzRGUN2mxlZ4Ykhm7jvERtmI8NQ==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.14.3", + "@formatjs/icu-skeleton-parser": "1.3.18", + "tslib": "^2.4.0" + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.3.18", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.18.tgz", + "integrity": "sha512-ND1ZkZfmLPcHjAH1sVpkpQxA+QYfOX3py3SjKWMUVGDow18gZ0WPqz3F+pJLYQMpS2LnnQ5zYR2jPVYTbRwMpg==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.14.3", + "tslib": "^2.4.0" + } + }, + "@formatjs/intl-localematcher": { + "version": "0.2.32", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.32.tgz", + "integrity": "sha512-k/MEBstff4sttohyEpXxCmC3MqbUn9VvHGlZ8fauLzkbwXmVrEeyzS+4uhrvAk9DWU9/7otYWxyDox4nT/KVLQ==", + "dev": true, + "requires": { + "tslib": "^2.4.0" + } + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -16306,6 +16540,74 @@ } } }, + "@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "@testing-library/react": { "version": "13.4.0", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", @@ -16610,6 +16912,15 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "@types/testing-library__jest-dom": { + "version": "5.14.5", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", + "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", + "dev": true, + "requires": { + "@types/jest": "*" + } + }, "@types/ua-parser-js": { "version": "0.7.36", "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz", @@ -19958,6 +20269,18 @@ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", "dev": true }, + "intl-messageformat": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.3.3.tgz", + "integrity": "sha512-un/f07/g2e/3Q8e1ghDKET+el22Bi49M7O/rHxd597R+oLpPOMykSv5s51cABVfu3FZW+fea4hrzf2MHu1W4hw==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "1.14.3", + "@formatjs/fast-memoize": "2.0.1", + "@formatjs/icu-messageformat-parser": "2.3.0", + "tslib": "^2.4.0" + } + }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -24037,6 +24360,16 @@ "resolve": "^1.1.6" } }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, "regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", diff --git a/package.json b/package.json index 12eb4029..b9e61335 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "test:visual": "run-p -r preview test:visual:vitest", "test:visual:vitest": "vitest run --config vite.e2e-visual.config.js", "test:visual:update": "UPDATE_SCREENSHOTS=true npm run test:visual", - "pretest": "tsc -p tsconfig.test.json", + "pretest": "tsc -p tsconfig.unit.json && tsc -p tsconfig.e2e.json", "test": "run-s lint test:unit test:functional", "preview": "vite preview", "start": "run-p start:server start:watch:ts start:watch:css", @@ -64,6 +64,7 @@ "@cloudscape-design/test-utils-converter": "^1.0.0", "@cloudscape-design/theming-build": "^1", "@juggle/resize-observer": "^3.4.0", + "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@types/jest-image-snapshot": "^6.1.0", "@types/lodash": "^4.14.191", diff --git a/pages/pages.ts b/pages/pages.ts index e7c61f31..37eb7d46 100644 --- a/pages/pages.ts +++ b/pages/pages.ts @@ -5,10 +5,8 @@ import { lazy } from "react"; const pagesRaw = import.meta.glob("./**/*.page.tsx"); const pageIdRegex = /([\w-/]+)\.page\.tsx/; const getPage = (path: string) => path.match(pageIdRegex)![1]; -const getRoute = (page: string) => `/#${page}`; export const pages = Object.keys(pagesRaw).map(getPage); -export const routes = pages.map(getRoute); type ComponentFactory = Parameters[0]; diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts new file mode 100644 index 00000000..67d85546 --- /dev/null +++ b/src/__tests__/setup.ts @@ -0,0 +1,9 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// type-only import, because in runtime it tries to access Jest globals, which do not exist +/// +import matchers from "@testing-library/jest-dom/matchers"; +import { expect } from "vitest"; + +expect.extend(matchers); diff --git a/src/board/utils/__tests__/layout.test.ts b/src/board/utils/__tests__/layout.test.ts index 3dd71d8c..9a1cdd9e 100644 --- a/src/board/utils/__tests__/layout.test.ts +++ b/src/board/utils/__tests__/layout.test.ts @@ -15,6 +15,7 @@ function createMockTransition( ): Transition { return { operation, + acquiredItem: null, interactionType: "keyboard", itemsLayout, insertionDirection: null, diff --git a/src/board/utils/__tests__/path.test.ts b/src/board/utils/__tests__/path.test.ts index 5a0190ac..070dbb58 100644 --- a/src/board/utils/__tests__/path.test.ts +++ b/src/board/utils/__tests__/path.test.ts @@ -8,8 +8,7 @@ import { appendMovePath, appendResizePath, normalizeInsertionPath } from "../pat describe("normalizeInsertionPath", () => { test("keeps path unchanged if empty", () => { - const path = []; - expect(normalizeInsertionPath(path, "right", 4, 1)).toEqual(path); + expect(normalizeInsertionPath([], "right", 4, 1)).toEqual([]); }); test("keeps path unchanged if it has a single step", () => { @@ -68,9 +67,8 @@ describe("normalizeInsertionPath", () => { describe("appendMovePath", () => { test("appends new position as first if the path is empty", () => { - const path = []; const rect = { left: 1, right: 2, top: 3, bottom: 4 }; - expect(appendMovePath(path, rect)).toEqual([new Position({ x: 1, y: 3 })]); + expect(appendMovePath([], rect)).toEqual([new Position({ x: 1, y: 3 })]); }); test("does not append new position if it is the same as previous", () => { @@ -100,9 +98,8 @@ describe("appendMovePath", () => { describe("appendResizePath", () => { test("appends new position as first if the path is empty", () => { - const path = []; const rect = { left: 1, right: 2, top: 3, bottom: 4 }; - expect(appendResizePath(path, rect)).toEqual([new Position({ x: 2, y: 4 })]); + expect(appendResizePath([], rect)).toEqual([new Position({ x: 2, y: 4 })]); }); test("does not append new position if it is the same as previous", () => { diff --git a/src/internal/grid/__tests__/grid.test.tsx b/src/internal/grid/__tests__/grid.test.tsx index 126d21df..10198a36 100644 --- a/src/internal/grid/__tests__/grid.test.tsx +++ b/src/internal/grid/__tests__/grid.test.tsx @@ -47,10 +47,18 @@ test("annotates data attributes on individual elements", () => { columnOffset: "1", rowOffset: "1", }); + expect(items[0]).toHaveStyle({ + "grid-row-start": "1", + "grid-row-end": "span 1", + }); expect(items[1].dataset).toMatchObject({ rowSpan: "1", columnSpan: "2", columnOffset: "3", rowOffset: "1", }); + expect(items[0]).toHaveStyle({ + "grid-row-start": "1", + "grid-row-end": "span 1", + }); }); diff --git a/src/internal/layout-engine/__tests__/engine-chained.test.ts b/src/internal/layout-engine/__tests__/engine-chained.test.ts index 717c0b18..9cfb0899 100644 --- a/src/internal/layout-engine/__tests__/engine-chained.test.ts +++ b/src/internal/layout-engine/__tests__/engine-chained.test.ts @@ -3,6 +3,7 @@ import { expect, test } from "vitest"; import { fromMatrix, toString } from "../../debug-tools"; +import { Position } from "../../utils/position"; import { LayoutEngine } from "../engine"; test("engine operations can be chained", () => { @@ -13,15 +14,12 @@ test("engine operations can be chained", () => { ]); const layoutShift = new LayoutEngine(grid) - .insert({ itemId: "X", width: 1, height: 1, path: [{ x: 1, y: 1 }] }) + .insert({ itemId: "X", width: 1, height: 1, path: [new Position({ x: 1, y: 1 })] }) .move({ itemId: "X", - path: [ - { x: 1, y: 2 }, - { x: 1, y: 3 }, - ], + path: [new Position({ x: 1, y: 2 }), new Position({ x: 1, y: 3 })], }) - .resize({ itemId: "X", path: [{ x: 3, y: 4 }] }) + .resize({ itemId: "X", path: [new Position({ x: 3, y: 4 })] }) .remove("F") .refloat() .getLayoutShift(); @@ -44,13 +42,13 @@ test("engine operations are not chained when executed separately", () => { const engine = new LayoutEngine(grid); // These commands are ignored. - engine.insert({ itemId: "X", width: 1, height: 1, path: [{ x: 1, y: 1 }] }); - engine.move({ itemId: "A", path: [{ x: 0, y: 1 }] }); - engine.resize({ itemId: "A", path: [{ x: 2, y: 1 }] }); + engine.insert({ itemId: "X", width: 1, height: 1, path: [new Position({ x: 1, y: 1 })] }); + engine.move({ itemId: "A", path: [new Position({ x: 0, y: 1 })] }); + engine.resize({ itemId: "A", path: [new Position({ x: 2, y: 1 })] }); engine.remove("A"); // The last command only is reflected in the layoutShift. - engine.move({ itemId: "A", path: [{ x: 1, y: 0 }] }); + engine.move({ itemId: "A", path: [new Position({ x: 1, y: 0 })] }); expect(toString(engine.getLayoutShift().next)).toBe( toString([ diff --git a/src/internal/layout-engine/__tests__/engine-immutable.test.ts b/src/internal/layout-engine/__tests__/engine-immutable.test.ts index dc49aa47..ce0c26fb 100644 --- a/src/internal/layout-engine/__tests__/engine-immutable.test.ts +++ b/src/internal/layout-engine/__tests__/engine-immutable.test.ts @@ -4,6 +4,7 @@ import { cloneDeep } from "lodash"; import { expect, test } from "vitest"; import { generateGrid, generateMove, generateResize } from "../../debug-tools"; +import { Position } from "../../utils/position"; import { LayoutEngine } from "../engine"; test("input arguments stay unchanged when using engine", () => { @@ -16,7 +17,7 @@ test("input arguments stay unchanged when using engine", () => { new LayoutEngine(grid).move(movePath); new LayoutEngine(grid).resize(resize); - new LayoutEngine(grid).insert({ itemId: "X", width: 1, height: 1, path: [{ x: 0, y: 0 }] }); + new LayoutEngine(grid).insert({ itemId: "X", width: 1, height: 1, path: [new Position({ x: 0, y: 0 })] }); new LayoutEngine(grid).remove("A"); new LayoutEngine(grid).refloat(); diff --git a/src/internal/layout-engine/__tests__/engine-insert.test.ts b/src/internal/layout-engine/__tests__/engine-insert.test.ts index ff71f645..1d80c130 100644 --- a/src/internal/layout-engine/__tests__/engine-insert.test.ts +++ b/src/internal/layout-engine/__tests__/engine-insert.test.ts @@ -3,6 +3,7 @@ import { describe, expect, test } from "vitest"; import { fromMatrix, generateGrid, generateInsert, toString } from "../../debug-tools"; +import { Position } from "../../utils/position"; import { LayoutEngine } from "../engine"; import { forEachTimes } from "./helpers"; @@ -24,7 +25,7 @@ describe("insert scenarios", () => { ["A", "A", "D"], ["E", "E", "D"], ], - { itemId: "X", width: 1, height: 1, path: [{ x: 0, y: 0 }] }, + { itemId: "X", width: 1, height: 1, path: [new Position({ x: 0, y: 0 })] }, [ ["X", "B", "C"], ["A", "A", "D"], @@ -38,7 +39,7 @@ describe("insert scenarios", () => { ["A", "A", "D"], ["E", "E", "D"], ], - { itemId: "X", width: 2, height: 2, path: [{ x: 1, y: 1 }] }, + { itemId: "X", width: 2, height: 2, path: [new Position({ x: 1, y: 1 })] }, [ [" ", " ", "B"], [" ", "X", "X"], @@ -60,7 +61,7 @@ describe("insert scenarios", () => { ["C", "C"], ["C", "C"], ], - { itemId: "X", width: 2, height: 4, path: [{ x: 0, y: 0 }] }, + { itemId: "X", width: 2, height: 4, path: [new Position({ x: 0, y: 0 })] }, [ ["X", "X"], ["X", "X"], diff --git a/src/internal/layout-engine/__tests__/engine-resize.test.ts b/src/internal/layout-engine/__tests__/engine-resize.test.ts index c3c7ca23..f615265d 100644 --- a/src/internal/layout-engine/__tests__/engine-resize.test.ts +++ b/src/internal/layout-engine/__tests__/engine-resize.test.ts @@ -3,6 +3,7 @@ import { describe, expect, test } from "vitest"; import { fromMatrix, generateGrid, generateRandomPath, generateResize, toString } from "../../debug-tools"; +import { Position } from "../../utils/position"; import { LayoutEngine } from "../engine"; import { forEachTimes } from "./helpers"; @@ -31,10 +32,10 @@ test("commits no changes if resize path returns to original or smaller", () => { if (resize.path.length > 0) { const lastPathItem = resize.path[resize.path.length - 1]; const resizeTarget = grid.items.find((it) => it.id === resize.itemId)!; - const originalSizePath = { + const originalSizePath = new Position({ x: randomPathValue(resizeTarget.x + resizeTarget.width), y: randomPathValue(resizeTarget.y + resizeTarget.height), - }; + }); resize.path = [...resize.path, ...generateRandomPath(lastPathItem, originalSizePath)]; const moves = new LayoutEngine(grid).resize(resize).getLayoutShift().moves; expect(moves.filter((move) => move.type !== "RESIZE" && move.type !== "FLOAT")).toHaveLength(0); @@ -57,10 +58,7 @@ describe("resize scenarios", () => { ], { itemId: "A", - path: [ - { x: 2, y: 1 }, - { x: 2, y: 2 }, - ], + path: [new Position({ x: 2, y: 1 }), new Position({ x: 2, y: 2 })], }, [ ["A", "A", "B"], @@ -77,10 +75,7 @@ describe("resize scenarios", () => { ], { itemId: "A", - path: [ - { x: 1, y: 2 }, - { x: 2, y: 2 }, - ], + path: [new Position({ x: 1, y: 2 }), new Position({ x: 2, y: 2 })], }, [ ["A", "A", "B"], @@ -97,10 +92,7 @@ describe("resize scenarios", () => { ], { itemId: "A", - path: [ - { x: 3, y: 2 }, - { x: 3, y: 1 }, - ], + path: [new Position({ x: 3, y: 2 }), new Position({ x: 3, y: 1 })], }, [ ["A", "A", "A"], @@ -118,10 +110,7 @@ describe("resize scenarios", () => { ], { itemId: "A", - path: [ - { x: 3, y: 2 }, - { x: 3, y: 3 }, - ], + path: [new Position({ x: 3, y: 2 }), new Position({ x: 3, y: 3 })], }, [ ["A", "A", "A"], @@ -140,7 +129,7 @@ describe("resize scenarios", () => { ["C", "D", "D", "E"], ["C", "F", "F", "F"], ], - { itemId: "B", path: [{ x: 4, y: 3 }] }, + { itemId: "B", path: [new Position({ x: 4, y: 3 })] }, [ ["A", "A", "A", " "], ["B", "B", "B", "B"], @@ -159,10 +148,7 @@ describe("resize scenarios", () => { ], { itemId: "B", - path: [ - { x: 4, y: 3 }, - { x: 4, y: 4 }, - ], + path: [new Position({ x: 4, y: 3 }), new Position({ x: 4, y: 4 })], }, [ ["A", "A", "A", " "], @@ -183,10 +169,7 @@ describe("resize scenarios", () => { ], { itemId: "A", - path: [ - { x: 3, y: 2 }, - { x: 3, y: 3 }, - ], + path: [new Position({ x: 3, y: 2 }), new Position({ x: 3, y: 3 })], }, [ ["A", "A", "A", " "], @@ -207,7 +190,7 @@ describe("resize scenarios", () => { ], { itemId: "B", - path: [{ x: 2, y: 2 }], + path: [new Position({ x: 2, y: 2 })], }, [ ["A", "B", "C", "D"], diff --git a/src/internal/layout-engine/__tests__/engine-validation.test.ts b/src/internal/layout-engine/__tests__/engine-validation.test.ts index 9bb4d3b8..f1997eed 100644 --- a/src/internal/layout-engine/__tests__/engine-validation.test.ts +++ b/src/internal/layout-engine/__tests__/engine-validation.test.ts @@ -3,6 +3,7 @@ import { expect, test } from "vitest"; import { fromMatrix, fromTextPath } from "../../debug-tools"; +import { Position } from "../../utils/position"; import { LayoutEngine } from "../engine"; test("throws if grid definition is not valid", () => { @@ -37,10 +38,10 @@ test("throws if move command is not valid", () => { ["G", "E", "E"], ]); - expect(() => new LayoutEngine(grid).move({ itemId: "X", path: [{ x: 0, y: 0 }] })).toThrowError( + expect(() => new LayoutEngine(grid).move({ itemId: "X", path: [new Position({ x: 0, y: 0 })] })).toThrowError( 'Item with id "X" not found in the grid.' ); - expect(() => new LayoutEngine(grid).move({ itemId: "F", path: [{ x: 3, y: 1 }] })).toThrowError( + expect(() => new LayoutEngine(grid).move({ itemId: "F", path: [new Position({ x: 3, y: 1 })] })).toThrowError( "Invalid move: outside grid." ); }); @@ -52,7 +53,7 @@ test("throws if resize command is not valid", () => { ["G", "E", "E"], ]); - expect(() => new LayoutEngine(grid).resize({ itemId: "X", path: [{ x: 1, y: 1 }] })).toThrowError( + expect(() => new LayoutEngine(grid).resize({ itemId: "X", path: [new Position({ x: 1, y: 1 })] })).toThrowError( 'Item with id "X" not found in the grid.' ); @@ -64,10 +65,7 @@ test("throws if resize command is not valid", () => { ]) ).resize({ itemId: "A", - path: [ - { x: 1, y: 2 }, - { x: 0, y: 2 }, - ], + path: [new Position({ x: 1, y: 2 }), new Position({ x: 0, y: 2 })], }) ).toThrowError("Invalid resize: can't resize to 0."); @@ -79,10 +77,7 @@ test("throws if resize command is not valid", () => { ]) ).resize({ itemId: "A", - path: [ - { x: 2, y: 1 }, - { x: 2, y: 0 }, - ], + path: [new Position({ x: 2, y: 1 }), new Position({ x: 2, y: 0 })], }) ).toThrowError("Invalid resize: can't resize to 0."); @@ -92,7 +87,7 @@ test("throws if resize command is not valid", () => { ["A", "A"], ["A", "A"], ]) - ).resize({ itemId: "A", path: [{ x: 3, y: 2 }] }) + ).resize({ itemId: "A", path: [new Position({ x: 3, y: 2 })] }) ).toThrowError("Invalid resize: outside grid."); }); @@ -104,10 +99,10 @@ test("throws if insert command is not valid", () => { ]); expect(() => - new LayoutEngine(grid).insert({ itemId: "X", width: 2, height: 1, path: [{ x: 2, y: 2 }] }) + new LayoutEngine(grid).insert({ itemId: "X", width: 2, height: 1, path: [new Position({ x: 2, y: 2 })] }) ).toThrowError("Inserting item is outside the boundaries."); expect(() => - new LayoutEngine(grid).insert({ itemId: "X", width: 2, height: 0, path: [{ x: 1, y: 1 }] }) + new LayoutEngine(grid).insert({ itemId: "X", width: 2, height: 0, path: [new Position({ x: 1, y: 1 })] }) ).toThrowError("Inserting item has invalid size."); }); diff --git a/src/test-utils/tsconfig.json b/src/test-utils/tsconfig.json index ac7187c0..a37453d2 100644 --- a/src/test-utils/tsconfig.json +++ b/src/test-utils/tsconfig.json @@ -11,5 +11,5 @@ "experimentalDecorators": true, "tsBuildInfoFile": "../../.cache/test-utils.tsbuildinfo" }, - "include": ["./", "../../types"] + "include": ["./", "./types"] } diff --git a/src/test-utils/types/global.d.ts b/src/test-utils/types/global.d.ts new file mode 100644 index 00000000..883fef88 --- /dev/null +++ b/src/test-utils/types/global.d.ts @@ -0,0 +1,10 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +declare module "*.css.js" { + const styles: Record; + export default styles; +} +declare module "*.selectors.js" { + const styles: Record; + export default styles; +} diff --git a/test/visual/index.test.ts b/test/visual/index.test.ts index 1a947623..4b543370 100644 --- a/test/visual/index.test.ts +++ b/test/visual/index.test.ts @@ -1,11 +1,16 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import path from "path"; import { ScreenshotPageObject } from "@cloudscape-design/browser-test-tools/page-objects"; import { expect, test } from "vitest"; -import { routes } from "../../pages/pages"; import { setupTest } from "../utils"; -test.each(routes)("matches snapshot for %s", (route) => +const pagesMap = import.meta.glob("../../pages/**/*.page.tsx", { as: "raw" }); +const pages = Object.keys(pagesMap) + .map((page) => page.replace(/\.page\.tsx$/, "")) + .map((page) => "/#/" + path.relative("../../pages/", page)); + +test.each(pages)("matches snapshot for %s", (route) => setupTest(route, ScreenshotPageObject, async (page) => { const hasScreenshotArea = await page.isExisting(".screenshot-area"); diff --git a/tsconfig.e2e.json b/tsconfig.e2e.json new file mode 100644 index 00000000..21558155 --- /dev/null +++ b/tsconfig.e2e.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": "test", + "types": ["vite/client"], + "tsBuildInfoFile": "./.cache/e2e-tests.tsbuildinfo" + }, + "include": ["types", "test"], + "exclude": [] +} diff --git a/tsconfig.json b/tsconfig.json index be10079e..e7229a33 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "target": "ES2019", "jsx": "react-jsx", "types": [], - "lib": ["es2019", "dom"], + "lib": ["es2019", "dom", "dom.iterable"], "module": "ESNext", "moduleResolution": "nodenext", "esModuleInterop": true, diff --git a/tsconfig.test.json b/tsconfig.unit.json similarity index 50% rename from tsconfig.test.json rename to tsconfig.unit.json index bb79c655..e0072bd3 100644 --- a/tsconfig.test.json +++ b/tsconfig.unit.json @@ -2,8 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "noEmit": true, - "tsBuildInfoFile": "./.cache/tests.tsbuildinfo" + "tsBuildInfoFile": "./.cache/unit-tests.tsbuildinfo" }, - "include": ["types", "**/*.test.tsx"], + "include": ["types", "**/__tests__"], "exclude": [] } diff --git a/types/global.d.ts b/types/global.d.ts index 883fef88..d0a7d0d4 100644 --- a/types/global.d.ts +++ b/types/global.d.ts @@ -5,6 +5,9 @@ declare module "*.css.js" { export default styles; } declare module "*.selectors.js" { - const styles: Record; + // this is how Node.js and Vitest receive ESM transpiled to CJS + const styles: { + default: Record; + }; export default styles; } diff --git a/vite.unit.config.js b/vite.unit.config.js index 528d9030..64485338 100644 --- a/vite.unit.config.js +++ b/vite.unit.config.js @@ -12,6 +12,7 @@ export default defineConfig({ test: { include: ["./src/**/__tests__/**/*.test.{ts,tsx}"], environment: "jsdom", + setupFiles: ["./src/__tests__/setup.ts"], coverage: { enabled: process.env.CI === "true", provider: "istanbul",