From 247560b4934a064c7c3418800185a096b13da1e8 Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Thu, 26 Oct 2023 12:35:11 -0400 Subject: [PATCH 01/22] scaffold jest matchers --- tools/jest-matchers/README.md | 20 +++++++++++++ tools/jest-matchers/package.json | 29 +++++++++++++++++++ tools/jest-matchers/src/index.ts | 3 ++ tools/jest-matchers/src/matchers/index.ts | 1 + .../src/matchers/toBeLabelled.ts | 8 +++++ .../jest-matchers/src/utils/createMatcher.ts | 5 ++++ tools/jest-matchers/tsconfig.json | 13 +++++++++ tools/test/config/common.setup.js | 1 + tools/test/package.json | 1 + 9 files changed, 81 insertions(+) create mode 100644 tools/jest-matchers/README.md create mode 100644 tools/jest-matchers/package.json create mode 100644 tools/jest-matchers/src/index.ts create mode 100644 tools/jest-matchers/src/matchers/index.ts create mode 100644 tools/jest-matchers/src/matchers/toBeLabelled.ts create mode 100644 tools/jest-matchers/src/utils/createMatcher.ts create mode 100644 tools/jest-matchers/tsconfig.json diff --git a/tools/jest-matchers/README.md b/tools/jest-matchers/README.md new file mode 100644 index 0000000000..2cf1306a1c --- /dev/null +++ b/tools/jest-matchers/README.md @@ -0,0 +1,20 @@ + +# Jest Matchers + +![npm (scoped)](https://img.shields.io/npm/v/@leafygreen-ui/jest-matchers.svg) +#### [View on MongoDB.design](https://www.mongodb.design/component/jest-matchers/example/) + +## Installation + +### Yarn + +```shell +yarn add @leafygreen-ui/jest-matchers +``` + +### NPM + +```shell +npm install @leafygreen-ui/jest-matchers +``` + diff --git a/tools/jest-matchers/package.json b/tools/jest-matchers/package.json new file mode 100644 index 0000000000..046e487e06 --- /dev/null +++ b/tools/jest-matchers/package.json @@ -0,0 +1,29 @@ +{ + "name": "@lg-tools/jest-matchers", + "version": "0.0.1", + "description": "LeafyGreen UI Kit Jest Matchers", + "main": "./dist/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/index.d.ts", + "license": "Apache-2.0", + "scripts": { + "build": "lg build-package", + "tsc": "lg build-ts", + "docs": "lg build-tsdoc" + }, + "publishConfig": { + "access": "public" + }, + "peerDependencies": { + "jest": "^29.6.2", + "@types/jest": "^29.5.3" + }, + "homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/packages/jest-matchers", + "repository": { + "type": "git", + "url": "https://github.com/mongodb/leafygreen-ui" + }, + "bugs": { + "url": "https://jira.mongodb.org/projects/PD/summary" + } +} diff --git a/tools/jest-matchers/src/index.ts b/tools/jest-matchers/src/index.ts new file mode 100644 index 0000000000..67df2d899f --- /dev/null +++ b/tools/jest-matchers/src/index.ts @@ -0,0 +1,3 @@ +import * as matchers from './matchers'; + +expect.extend(matchers); diff --git a/tools/jest-matchers/src/matchers/index.ts b/tools/jest-matchers/src/matchers/index.ts new file mode 100644 index 0000000000..56d94f494b --- /dev/null +++ b/tools/jest-matchers/src/matchers/index.ts @@ -0,0 +1 @@ +export { toBeLabelled } from './toBeLabelled'; diff --git a/tools/jest-matchers/src/matchers/toBeLabelled.ts b/tools/jest-matchers/src/matchers/toBeLabelled.ts new file mode 100644 index 0000000000..10a65b076a --- /dev/null +++ b/tools/jest-matchers/src/matchers/toBeLabelled.ts @@ -0,0 +1,8 @@ +import { createMatcher } from '../utils/createMatcher'; + +export const toBeLabelled = createMatcher((element: Element) => { + return { + pass: true, + message: () => '', + }; +}); diff --git a/tools/jest-matchers/src/utils/createMatcher.ts b/tools/jest-matchers/src/utils/createMatcher.ts new file mode 100644 index 0000000000..e12002d3a0 --- /dev/null +++ b/tools/jest-matchers/src/utils/createMatcher.ts @@ -0,0 +1,5 @@ +export const createMatcher = ( + matcher: jest.CustomMatcher, +): jest.CustomMatcher => { + return matcher; +}; diff --git a/tools/jest-matchers/tsconfig.json b/tools/jest-matchers/tsconfig.json new file mode 100644 index 0000000000..0e81d75f2d --- /dev/null +++ b/tools/jest-matchers/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@lg-tools/build/config/package.tsconfig.json", + "compilerOptions": { + "declarationDir": "dist", + "outDir": "dist", + "rootDir": "src", + "baseUrl": ".", + }, + "include": [ + "src/**/*" + ], + "exclude": ["**/*.spec.*", "**/*.story.*"], +} diff --git a/tools/test/config/common.setup.js b/tools/test/config/common.setup.js index 1b069331fa..08f4ebeef8 100644 --- a/tools/test/config/common.setup.js +++ b/tools/test/config/common.setup.js @@ -1,4 +1,5 @@ require('@testing-library/jest-dom'); +require('@lg-tools/jest-matchers'); const { toHaveNoViolations } = require('jest-axe'); expect.extend(toHaveNoViolations); diff --git a/tools/test/package.json b/tools/test/package.json index 1d61d677ba..13a9fb671a 100644 --- a/tools/test/package.json +++ b/tools/test/package.json @@ -18,6 +18,7 @@ "@emotion/react": "11.11.1", "@emotion/server": "11.11.0", "@lg-tools/build": "0.3.0", + "@lg-tools/jest-matchers": "0.0.1", "@testing-library/dom": "9.3.1", "@testing-library/jest-dom": "5.17.0", "@testing-library/react": "14.0.0", From 872b7cd3d15bb93bdd4f4a27ef463db80dbab1cc Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Thu, 26 Oct 2023 12:54:27 -0400 Subject: [PATCH 02/22] creates `createMatcher` script --- tools/jest-matchers/package.json | 15 ++++-- .../jest-matchers/scripts/createNewMatcher.ts | 46 +++++++++++++++++++ tools/jest-matchers/scripts/tsconfig.json | 21 +++++++++ tools/jest-matchers/tsconfig.json | 2 +- yarn.lock | 13 ++++-- 5 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 tools/jest-matchers/scripts/createNewMatcher.ts create mode 100644 tools/jest-matchers/scripts/tsconfig.json diff --git a/tools/jest-matchers/package.json b/tools/jest-matchers/package.json index 046e487e06..e854e6e434 100644 --- a/tools/jest-matchers/package.json +++ b/tools/jest-matchers/package.json @@ -9,14 +9,23 @@ "scripts": { "build": "lg build-package", "tsc": "lg build-ts", - "docs": "lg build-tsdoc" + "docs": "lg build-tsdoc", + "create-matcher": "npx ts-node ./scripts/createNewMatcher.ts" }, "publishConfig": { "access": "public" }, + "dependencies": { + "lodash": "^4.17.21" + }, + "devDependencies": { + "chalk": "4.1.2", + "commander": "^11.1.0", + "fs-extra": "^11.1.1" + }, "peerDependencies": { - "jest": "^29.6.2", - "@types/jest": "^29.5.3" + "@types/jest": "^29.5.3", + "jest": "^29.6.2" }, "homepage": "https://github.com/mongodb/leafygreen-ui/tree/main/packages/jest-matchers", "repository": { diff --git a/tools/jest-matchers/scripts/createNewMatcher.ts b/tools/jest-matchers/scripts/createNewMatcher.ts new file mode 100644 index 0000000000..d3e8474ea0 --- /dev/null +++ b/tools/jest-matchers/scripts/createNewMatcher.ts @@ -0,0 +1,46 @@ +import { Command } from 'commander'; +import chalk from 'chalk'; +import path from 'path'; +import fse from 'fs-extra'; +import { camelCase, lowerFirst } from 'lodash'; + +const cli = new Command(); +cli + .argument('', 'The name of the new matcher') + .action(createNewMatcher) + .parse(); + +function createNewMatcher(matcherName: string) { + matcherName = lowerFirst(camelCase(matcherName)); + + if (!matcherName.startsWith('to')) { + console.warn( + chalk.yellow( + `Matcher names should start with the word "to". Received \`${matcherName}\``, + ), + ); + } + + const matchersDir = path.resolve(__dirname, '../src/matchers'); + const matchersFilePath = path.resolve(matchersDir, matcherName + '.ts'); + + const matcherFileTemplate = ` +import { createMatcher } from '../utils/createMatcher'; + +export const ${matcherName} = createMatcher((element: Element) => { + return { + pass: true, + message: () => '', + }; +}); +`; + + fse.writeFileSync(matchersFilePath, matcherFileTemplate); + + const indexFilePath = path.resolve(matchersDir, 'index.ts'); + + let indexContents = fse.readFileSync(indexFilePath, 'utf-8'); + indexContents += `export { ${matcherName} } from './${matcherName}';\n`; + + fse.writeFileSync(indexFilePath, indexContents); +} diff --git a/tools/jest-matchers/scripts/tsconfig.json b/tools/jest-matchers/scripts/tsconfig.json new file mode 100644 index 0000000000..d8ad23ab66 --- /dev/null +++ b/tools/jest-matchers/scripts/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "CommonJS", + "noEmit": true, + "tsBuildInfoFile": "./tsconfig.tsbuildinfo", + "incremental": true, + "target": "ES5", + "jsx": "react", + "allowJs": true, + "pretty": true, + "strictNullChecks": true, + "noUnusedLocals": false, + "esModuleInterop": true, + "strict": true, + "allowSyntheticDefaultImports": true, + "moduleResolution": "node", + "baseUrl": ".", + "skipLibCheck": true, + "resolveJsonModule": true, + } +} \ No newline at end of file diff --git a/tools/jest-matchers/tsconfig.json b/tools/jest-matchers/tsconfig.json index 0e81d75f2d..c2eb55349d 100644 --- a/tools/jest-matchers/tsconfig.json +++ b/tools/jest-matchers/tsconfig.json @@ -9,5 +9,5 @@ "include": [ "src/**/*" ], - "exclude": ["**/*.spec.*", "**/*.story.*"], + "exclude": ["**/*.spec.*", "**/*.story.*", "src/tests"], } diff --git a/yarn.lock b/yarn.lock index 4c4040c9e5..330241f4f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5521,9 +5521,9 @@ camelcase@^7.0.0: integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== caniuse-lite@^1.0.30001135, caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001517: - version "1.0.30001519" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" - integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== + version "1.0.30001554" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz#ba80d88dff9acbc0cd4b7535fc30e0191c5e2e2a" + integrity sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ== case-sensitive-paths-webpack-plugin@^2.4.0: version "2.4.0" @@ -5759,6 +5759,11 @@ commander@^11.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67" integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== +commander@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" + integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== + commander@^2.18.0, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -7441,7 +7446,7 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@11.1.1, fs-extra@^11.1.0: +fs-extra@11.1.1, fs-extra@^11.1.0, fs-extra@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== From b2159ad69ec13618a73fc016c02915915f78bd84 Mon Sep 17 00:00:00 2001 From: Adam Thompson Date: Thu, 26 Oct 2023 13:51:42 -0400 Subject: [PATCH 03/22] creates `toBeLabelled` matcher --- .../src/matchers/toBeLabelled.ts | 50 +++++++++++++++++-- .../src/tests/toBeLabelled.spec.ts | 48 ++++++++++++++++++ .../src/tests/utils/test-utils.ts | 29 +++++++++++ 3 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 tools/jest-matchers/src/tests/toBeLabelled.spec.ts create mode 100644 tools/jest-matchers/src/tests/utils/test-utils.ts diff --git a/tools/jest-matchers/src/matchers/toBeLabelled.ts b/tools/jest-matchers/src/matchers/toBeLabelled.ts index 10a65b076a..b5d732afeb 100644 --- a/tools/jest-matchers/src/matchers/toBeLabelled.ts +++ b/tools/jest-matchers/src/matchers/toBeLabelled.ts @@ -1,8 +1,52 @@ +import { isNull } from 'lodash'; import { createMatcher } from '../utils/createMatcher'; -export const toBeLabelled = createMatcher((element: Element) => { +const isValidString = (str: any): str is string => + str && typeof str === 'string' && str.length > 0; + +/** + * Returns whether the provided element has any of the following: + * - a `label` attribute + * - an `aria-label` attribute + * - an `aria-labelledby` attribute with a valid associated element + * - an associated element `