├── .github ├── FUNDING.yml └── workflows │ ├── node.yml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .npmignore ├── README.md ├── jest-preset.js ├── jest.config.js ├── package-lock.json ├── package.json ├── renovate.json ├── src ├── __fixtures__ │ ├── bad.json │ ├── bad.jsx │ ├── good.json │ └── good.jsx ├── __snapshots__ │ └── run.test.ts.snap ├── index.ts ├── run.test.ts └── run.ts └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: keplersj 2 | -------------------------------------------------------------------------------- /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: lts/* 14 | - name: Install npm dependencies 15 | run: npm ci 16 | - name: Run tests 17 | env: 18 | FORCE_COLOR: 1 19 | run: npm test -- --ci 20 | - name: Upload coverage to Codecov 21 | uses: codecov/codecov-action@v3.1.1 22 | with: 23 | token: ${{secrets.CODECOV_TOKEN}} 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release (Weekly on Tuesdays) 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 0 * * 2" 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: lts/* 17 | - name: Install npm dependencies 18 | run: npm ci 19 | - name: Run tests 20 | env: 21 | FORCE_COLOR: 1 22 | run: npm test -- --ci 23 | - name: Release 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 27 | run: npx semantic-release 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (http://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # Typescript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | 65 | # End of https://www.gitignore.io/api/node 66 | dist/ 67 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no -- commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (http://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # Typescript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | 65 | # End of https://www.gitignore.io/api/node 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm](https://img.shields.io/npm/v/jest-runner-prettier)](https://www.npmjs.com/package/jest-runner-prettier) 2 | [![npm](https://img.shields.io/npm/dw/jest-runner-prettier)](https://www.npmjs.com/package/jest-runner-prettier) 3 | [![Codecov](https://img.shields.io/codecov/c/github/keplersj/jest-runner-prettier)](https://app.codecov.io/gh/keplersj/jest-runner-prettier) 4 | [![Bundle Size](https://img.shields.io/bundlephobia/min/jest-runner-prettier)](https://bundlephobia.com/package/jest-runner-prettier) 5 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://www.conventionalcommits.org/) 6 | [![Mentioned in Awesome Jest](https://awesome.re/mentioned-badge.svg)](https://github.com/jest-community/awesome-jest) 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 |

jest-runner-prettier

15 |

Prettier runner for Jest

16 |
17 | 18 |
19 | 20 | 21 |
22 | 23 | ## Usage 24 | 25 | ### Install 26 | 27 | Install `jest`, `prettier` and `jest-runner-prettier` 28 | 29 | ```bash 30 | yarn add --dev jest prettier jest-runner-prettier 31 | 32 | # or with NPM 33 | 34 | npm install --save-dev jest prettier jest-runner-prettier 35 | ``` 36 | 37 | ### Add it to your Jest config 38 | 39 | #### Using Built-in Preset 40 | 41 | This package includes a [Jest preset](https://jestjs.io/docs/en/configuration#preset-string) which configures Jest to run Prettier on all files supported by Prettier. To use it set the following in your `package.json`: 42 | 43 | ```json 44 | { 45 | "jest": { 46 | "preset": "jest-runner-prettier" 47 | } 48 | } 49 | ``` 50 | 51 | or `jest.config.js`: 52 | 53 | ```js 54 | module.exports = { 55 | preset: "jest-runner-prettier", 56 | }; 57 | ``` 58 | 59 | #### Manually 60 | 61 | In your `package.json` 62 | 63 | ```json 64 | { 65 | "jest": { 66 | "runner": "prettier", 67 | "moduleFileExtensions": [ 68 | "js", 69 | "mjs", 70 | "jsx", 71 | "vue", 72 | "ts", 73 | "tsx", 74 | "css", 75 | "less", 76 | "scss", 77 | "html", 78 | "json", 79 | "graphql", 80 | "md", 81 | "markdown", 82 | "mdx", 83 | "yaml", 84 | "yml" 85 | ], 86 | "testMatch": [ 87 | "**/*.js", 88 | "**/*.mjs", 89 | "**/*.jsx", 90 | "**/*.vue", 91 | "**/*.ts", 92 | "**/*.tsx", 93 | "**/*.css", 94 | "**/*.less", 95 | "**/*.scss", 96 | "**/*.html", 97 | "**/*.json", 98 | "**/*.graphql", 99 | "**/*.md", 100 | "**/*.markdown", 101 | "**/*.mdx", 102 | "**/*.yaml", 103 | "**/*.yml" 104 | ] 105 | } 106 | } 107 | ``` 108 | 109 | Or in `jest.config.js` 110 | 111 | ```js 112 | module.exports = { 113 | runner: "prettier", 114 | moduleFileExtensions: [ 115 | "js", 116 | "mjs", 117 | "jsx", 118 | "vue", 119 | "ts", 120 | "tsx", 121 | "css", 122 | "less", 123 | "scss", 124 | "html", 125 | "json", 126 | "graphql", 127 | "md", 128 | "markdown", 129 | "mdx", 130 | "yaml", 131 | "yml", 132 | ], 133 | testMatch: [ 134 | "**/*.js", 135 | "**/*.mjs", 136 | "**/*.jsx", 137 | "**/*.vue", 138 | "**/*.ts", 139 | "**/*.tsx", 140 | "**/*.css", 141 | "**/*.less", 142 | "**/*.scss", 143 | "**/*.html", 144 | "**/*.json", 145 | "**/*.graphql", 146 | "**/*.md", 147 | "**/*.markdown", 148 | "**/*.mdx", 149 | "**/*.yaml", 150 | "**/*.yml", 151 | ], 152 | }; 153 | ``` 154 | 155 | ### Run Jest 156 | 157 | ```bash 158 | npx jest 159 | 160 | # or, with yarn 161 | 162 | yarn jest 163 | ``` 164 | 165 | ## License 166 | 167 | Copyright [Kepler Sticka-Jones](https://keplersj.com) 2017-2022. Licensed MIT. 168 | -------------------------------------------------------------------------------- /jest-preset.js: -------------------------------------------------------------------------------- 1 | export default { 2 | runner: "prettier", 3 | moduleFileExtensions: [ 4 | "js", 5 | "mjs", 6 | "jsx", 7 | "vue", 8 | "ts", 9 | "tsx", 10 | "css", 11 | "less", 12 | "scss", 13 | "html", 14 | "json", 15 | "graphql", 16 | "md", 17 | "markdown", 18 | "mdx", 19 | "yaml", 20 | "yml", 21 | ], 22 | testMatch: [ 23 | "**/*.js", 24 | "**/*.mjs", 25 | "**/*.jsx", 26 | "**/*.vue", 27 | "**/*.ts", 28 | "**/*.tsx", 29 | "**/*.css", 30 | "**/*.less", 31 | "**/*.scss", 32 | "**/*.html", 33 | "**/*.json", 34 | "**/*.graphql", 35 | "**/*.md", 36 | "**/*.markdown", 37 | "**/*.mdx", 38 | "**/*.yaml", 39 | "**/*.yml", 40 | ], 41 | }; 42 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | collectCoverage: true, 3 | coverageReporters: ["json", "text"], 4 | projects: [ 5 | { 6 | preset: "ts-jest/presets/default-esm", 7 | globals: { 8 | "ts-jest": { 9 | useESM: true, 10 | }, 11 | }, 12 | moduleNameMapper: { 13 | "^(\\.{1,2}/.*)\\.js$": "$1", 14 | }, 15 | collectCoverage: true, 16 | testPathIgnorePatterns: ["/node_modules/", "/dist/"], 17 | coverageReporters: ["json", "text"], 18 | displayName: "test", 19 | }, 20 | { 21 | preset: "./jest-preset.js", 22 | runner: "./dist/index.js", 23 | displayName: "lint:prettier", 24 | testPathIgnorePatterns: [ 25 | "/node_modules/", 26 | "/src/__fixtures__/", 27 | "/coverage/", 28 | "/dist/", 29 | ], 30 | }, 31 | { 32 | runner: "eslint", 33 | displayName: "lint:eslint", 34 | testMatch: ["/src/**/*.ts"], 35 | }, 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jest-runner-prettier", 3 | "version": "0.0.0-development", 4 | "type": "module", 5 | "description": "A prettier runner for Jest", 6 | "main": "dist/index.js", 7 | "repository": "https://github.com/keplersj/jest-runner-prettier", 8 | "author": "Kepler Sticka-Jones ", 9 | "funding": { 10 | "type": "github-sponsors", 11 | "url": "https://github.com/sponsors/keplersj" 12 | }, 13 | "license": "MIT", 14 | "private": false, 15 | "scripts": { 16 | "build": "tsc", 17 | "test": "npm run build; node --experimental-vm-modules node_modules/jest/bin/jest.js", 18 | "prepack": "npm run build", 19 | "prepare": "husky install" 20 | }, 21 | "lint-staged": { 22 | "*": "node --experimental-vm-modules node_modules/jest/bin/jest.js --bail --findRelatedTests" 23 | }, 24 | "release": { 25 | "verifyConditions": "@semantic-release/github" 26 | }, 27 | "dependencies": { 28 | "create-lite-jest-runner": "^1.0.2", 29 | "emphasize": "^5.0.0", 30 | "jest-diff": "^29.0.0" 31 | }, 32 | "peerDependencies": { 33 | "jest": ">= 27.0.0", 34 | "prettier": ">= 1.8" 35 | }, 36 | "devDependencies": { 37 | "@commitlint/cli": "17.2.0", 38 | "@commitlint/config-conventional": "17.2.0", 39 | "@types/jest": "29.2.2", 40 | "@types/node": "18.11.9", 41 | "eslint": "8.27.0", 42 | "eslint-config-starstuff": "1.5.14", 43 | "husky": "8.0.3", 44 | "jest": "29.3.1", 45 | "jest-runner-eslint": "1.1.0", 46 | "lint-staged": "13.0.3", 47 | "prettier": "2.7.1", 48 | "ts-jest": "29.0.3", 49 | "typescript": "4.8.4" 50 | }, 51 | "eslintConfig": { 52 | "extends": "starstuff/auto", 53 | "env": { 54 | "node": true 55 | } 56 | }, 57 | "commitlint": { 58 | "extends": [ 59 | "@commitlint/config-conventional" 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["starstuff"] 3 | } 4 | -------------------------------------------------------------------------------- /src/__fixtures__/bad.json: -------------------------------------------------------------------------------- 1 | {"ohMyGodLongArray": ["prettyLongValue", "decentlyLongValue", 2 | "prettyDecentlyLongValue", "wowWowWowWhatALongValue"], "woahThisObjectIsDisapperingOffTheScreen": {"foo": "bar", 3 | "bar": "foo", "faz": false}, "truthy": true, "aNumber": 123, "aString": "hi" 4 | } -------------------------------------------------------------------------------- /src/__fixtures__/bad.jsx: -------------------------------------------------------------------------------- 1 | function HelloWorld({greeting = "hello", greeted = '"World"', silent = false, onMouseOver,}) { 2 | 3 | if(!greeting){return null}; 4 | 5 | // TODO: Don't use random in render 6 | let num = Math.floor (Math.random() * 1E+7).toString().replace(/\.\d+/ig, "") 7 | 8 | return
9 | 10 | { greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() } 11 | {greeting.endsWith(",") ? " " : ", " } 12 | 13 | { greeted } 14 | 15 | { (silent) 16 | ? "." 17 | : "!"} 18 | 19 |
; 20 | 21 | } -------------------------------------------------------------------------------- /src/__fixtures__/good.json: -------------------------------------------------------------------------------- 1 | { 2 | "ohMyGodLongArray": [ 3 | "prettyLongValue", 4 | "decentlyLongValue", 5 | "prettyDecentlyLongValue", 6 | "wowWowWowWhatALongValue" 7 | ], 8 | "woahThisObjectIsDisapperingOffTheScreen": { 9 | "foo": "bar", 10 | "bar": "foo", 11 | "faz": false 12 | }, 13 | "truthy": true, 14 | "aNumber": 123, 15 | "aString": "hi" 16 | } 17 | -------------------------------------------------------------------------------- /src/__fixtures__/good.jsx: -------------------------------------------------------------------------------- 1 | function HelloWorld({ 2 | greeting = "hello", 3 | greeted = '"World"', 4 | silent = false, 5 | onMouseOver, 6 | }) { 7 | if (!greeting) { 8 | return null; 9 | } 10 | 11 | // TODO: Don't use random in render 12 | let num = Math.floor(Math.random() * 1e7) 13 | .toString() 14 | .replace(/\.\d+/gi, ""); 15 | 16 | return ( 17 |
22 | 23 | {greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase()} 24 | 25 | {greeting.endsWith(",") ? ( 26 | " " 27 | ) : ( 28 | ", " 29 | )} 30 | {greeted} 31 | {silent ? "." : "!"} 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/__snapshots__/run.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`jest-runner-prettier JSON bad fixture matches snapshot 1`] = ` 4 | Object { 5 | "console": undefined, 6 | "coverage": undefined, 7 | "displayName": undefined, 8 | "failureMessage": "- Expected 9 | + Received 10 | 11 | - { 12 | - \\"ohMyGodLongArray\\": [ 13 | - \\"prettyLongValue\\", 14 | - \\"decentlyLongValue\\", 15 | - \\"prettyDecentlyLongValue\\", 16 | - \\"wowWowWowWhatALongValue\\" 17 | - ], 18 | - \\"woahThisObjectIsDisapperingOffTheScreen\\": { 19 | - \\"foo\\": \\"bar\\", 20 | - \\"bar\\": \\"foo\\", 21 | - \\"faz\\": false 22 | - }, 23 | - \\"truthy\\": true, 24 | - \\"aNumber\\": 123, 25 | - \\"aString\\": \\"hi\\" 26 | - } 27 | - 28 | + {\\"ohMyGodLongArray\\": [\\"prettyLongValue\\", \\"decentlyLongValue\\", 29 | + \\"prettyDecentlyLongValue\\", \\"wowWowWowWhatALongValue\\"], \\"woahThisObjectIsDisapperingOffTheScreen\\": {\\"foo\\": \\"bar\\", 30 | + \\"bar\\": \\"foo\\", \\"faz\\": false}, \\"truthy\\": true, \\"aNumber\\": 123, \\"aString\\": \\"hi\\" 31 | + }", 32 | "leaks": false, 33 | "memoryUsage": undefined, 34 | "numFailingTests": 1, 35 | "numPassingTests": 0, 36 | "numPendingTests": 0, 37 | "numTodoTests": 0, 38 | "openHandles": Array [], 39 | "skipped": false, 40 | "snapshot": Object { 41 | "added": 0, 42 | "fileDeleted": false, 43 | "matched": 0, 44 | "unchecked": 0, 45 | "uncheckedKeys": Array [], 46 | "unmatched": 0, 47 | "updated": 0, 48 | }, 49 | "testExecError": undefined, 50 | "testResults": Array [ 51 | Object { 52 | "ancestorTitles": Array [], 53 | "failureDetails": Array [], 54 | "failureMessages": Array [ 55 | "- Expected 56 | + Received 57 | 58 | - { 59 | - \\"ohMyGodLongArray\\": [ 60 | - \\"prettyLongValue\\", 61 | - \\"decentlyLongValue\\", 62 | - \\"prettyDecentlyLongValue\\", 63 | - \\"wowWowWowWhatALongValue\\" 64 | - ], 65 | - \\"woahThisObjectIsDisapperingOffTheScreen\\": { 66 | - \\"foo\\": \\"bar\\", 67 | - \\"bar\\": \\"foo\\", 68 | - \\"faz\\": false 69 | - }, 70 | - \\"truthy\\": true, 71 | - \\"aNumber\\": 123, 72 | - \\"aString\\": \\"hi\\" 73 | - } 74 | - 75 | + {\\"ohMyGodLongArray\\": [\\"prettyLongValue\\", \\"decentlyLongValue\\", 76 | + \\"prettyDecentlyLongValue\\", \\"wowWowWowWhatALongValue\\"], \\"woahThisObjectIsDisapperingOffTheScreen\\": {\\"foo\\": \\"bar\\", 77 | + \\"bar\\": \\"foo\\", \\"faz\\": false}, \\"truthy\\": true, \\"aNumber\\": 123, \\"aString\\": \\"hi\\" 78 | + }", 79 | ], 80 | "fullName": "", 81 | "numPassingAsserts": 0, 82 | "status": "failed", 83 | "title": "", 84 | }, 85 | ], 86 | "v8Coverage": undefined, 87 | } 88 | `; 89 | 90 | exports[`jest-runner-prettier JSON good fixture matches snapshot 1`] = ` 91 | Object { 92 | "console": undefined, 93 | "coverage": undefined, 94 | "displayName": undefined, 95 | "failureMessage": undefined, 96 | "leaks": false, 97 | "memoryUsage": undefined, 98 | "numFailingTests": 0, 99 | "numPassingTests": 1, 100 | "numPendingTests": 0, 101 | "numTodoTests": 0, 102 | "openHandles": Array [], 103 | "skipped": false, 104 | "snapshot": Object { 105 | "added": 0, 106 | "fileDeleted": false, 107 | "matched": 0, 108 | "unchecked": 0, 109 | "uncheckedKeys": Array [], 110 | "unmatched": 0, 111 | "updated": 0, 112 | }, 113 | "testExecError": undefined, 114 | "testResults": Array [ 115 | Object { 116 | "ancestorTitles": Array [], 117 | "failureDetails": Array [], 118 | "failureMessages": Array [], 119 | "fullName": "", 120 | "numPassingAsserts": 1, 121 | "status": "passed", 122 | "title": "", 123 | }, 124 | ], 125 | "v8Coverage": undefined, 126 | } 127 | `; 128 | 129 | exports[`jest-runner-prettier JSX bad fixture matches snapshot 1`] = ` 130 | Object { 131 | "console": undefined, 132 | "coverage": undefined, 133 | "displayName": undefined, 134 | "failureMessage": "- Expected 135 | + Received 136 | 137 | - function HelloWorld({ 138 | - greeting = \\"hello\\", 139 | - greeted = '\\"World\\"', 140 | - silent = false, 141 | - onMouseOver, 142 | - }) { 143 | - if (!greeting) { 144 | - return null; 145 | - } 146 | + function HelloWorld({greeting = \\"hello\\", greeted = '\\"World\\"', silent = false, onMouseOver,}) { 147 | + 148 | + if(!greeting){return null}; 149 | + 150 | + // TODO: Don't use random in render 151 | + let num = Math.floor (Math.random() * 1E+7).toString().replace(/\\\\.\\\\d+/ig, \\"\\") 152 | + 153 | + return <div className='HelloWorld' title={\`You are visitor number \${ num }\`} onMouseOver={onMouseOver}> 154 | + 155 | + <strong>{ greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() } 156 | + {greeting.endsWith(\\",\\") ? \\" \\" : <span style={{color: '\\\\grey'}}>\\", \\" } 157 | + <em> 158 | + { greeted } 159 | +  160 | + { (silent) 161 | + ? \\".\\" 162 | + : \\"!\\"} 163 | 164 | - // TODO: Don't use random in render 165 | - let num = Math.floor(Math.random() * 1e7) 166 | - .toString() 167 | - .replace(/\\\\.\\\\d+/gi, \\"\\"); 168 | + ; 169 | 170 | - return ( 171 | - <div 172 | - className=\\"HelloWorld\\" 173 | - title={\`You are visitor number \${num}\`} 174 | - onMouseOver={onMouseOver} 175 | - > 176 | - <strong> 177 | - {greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase()} 178 | -  179 | - {greeting.endsWith(\\",\\") ? ( 180 | - \\" \\" 181 | - ) : ( 182 | - <span style={{ color: \\"grey\\" }}>\\", \\" 183 | - )} 184 | - <em>{greeted} 185 | - {silent ? \\".\\" : \\"!\\"} 186 | -  187 | - ); 188 |  } 189 | -", 190 | "leaks": false, 191 | "memoryUsage": undefined, 192 | "numFailingTests": 1, 193 | "numPassingTests": 0, 194 | "numPendingTests": 0, 195 | "numTodoTests": 0, 196 | "openHandles": Array [], 197 | "skipped": false, 198 | "snapshot": Object { 199 | "added": 0, 200 | "fileDeleted": false, 201 | "matched": 0, 202 | "unchecked": 0, 203 | "uncheckedKeys": Array [], 204 | "unmatched": 0, 205 | "updated": 0, 206 | }, 207 | "testExecError": undefined, 208 | "testResults": Array [ 209 | Object { 210 | "ancestorTitles": Array [], 211 | "failureDetails": Array [], 212 | "failureMessages": Array [ 213 | "- Expected 214 | + Received 215 | 216 | - function HelloWorld({ 217 | - greeting = \\"hello\\", 218 | - greeted = '\\"World\\"', 219 | - silent = false, 220 | - onMouseOver, 221 | - }) { 222 | - if (!greeting) { 223 | - return null; 224 | - } 225 | + function HelloWorld({greeting = \\"hello\\", greeted = '\\"World\\"', silent = false, onMouseOver,}) { 226 | + 227 | + if(!greeting){return null}; 228 | + 229 | + // TODO: Don't use random in render 230 | + let num = Math.floor (Math.random() * 1E+7).toString().replace(/\\\\.\\\\d+/ig, \\"\\") 231 | + 232 | + return <div className='HelloWorld' title={\`You are visitor number \${ num }\`} onMouseOver={onMouseOver}> 233 | + 234 | + <strong>{ greeting.slice( 0, 1 ).toUpperCase() + greeting.slice(1).toLowerCase() } 235 | + {greeting.endsWith(\\",\\") ? \\" \\" : <span style={{color: '\\\\grey'}}>\\", \\" } 236 | + <em> 237 | + { greeted } 238 | +  239 | + { (silent) 240 | + ? \\".\\" 241 | + : \\"!\\"} 242 | 243 | - // TODO: Don't use random in render 244 | - let num = Math.floor(Math.random() * 1e7) 245 | - .toString() 246 | - .replace(/\\\\.\\\\d+/gi, \\"\\"); 247 | + ; 248 | 249 | - return ( 250 | - <div 251 | - className=\\"HelloWorld\\" 252 | - title={\`You are visitor number \${num}\`} 253 | - onMouseOver={onMouseOver} 254 | - > 255 | - <strong> 256 | - {greeting.slice(0, 1).toUpperCase() + greeting.slice(1).toLowerCase()} 257 | -  258 | - {greeting.endsWith(\\",\\") ? ( 259 | - \\" \\" 260 | - ) : ( 261 | - <span style={{ color: \\"grey\\" }}>\\", \\" 262 | - )} 263 | - <em>{greeted} 264 | - {silent ? \\".\\" : \\"!\\"} 265 | -  266 | - ); 267 |  } 268 | -", 269 | ], 270 | "fullName": "", 271 | "numPassingAsserts": 0, 272 | "status": "failed", 273 | "title": "", 274 | }, 275 | ], 276 | "v8Coverage": undefined, 277 | } 278 | `; 279 | 280 | exports[`jest-runner-prettier JSX good fixture matches snapshot 1`] = ` 281 | Object { 282 | "console": undefined, 283 | "coverage": undefined, 284 | "displayName": undefined, 285 | "failureMessage": undefined, 286 | "leaks": false, 287 | "memoryUsage": undefined, 288 | "numFailingTests": 0, 289 | "numPassingTests": 1, 290 | "numPendingTests": 0, 291 | "numTodoTests": 0, 292 | "openHandles": Array [], 293 | "skipped": false, 294 | "snapshot": Object { 295 | "added": 0, 296 | "fileDeleted": false, 297 | "matched": 0, 298 | "unchecked": 0, 299 | "uncheckedKeys": Array [], 300 | "unmatched": 0, 301 | "updated": 0, 302 | }, 303 | "testExecError": undefined, 304 | "testResults": Array [ 305 | Object { 306 | "ancestorTitles": Array [], 307 | "failureDetails": Array [], 308 | "failureMessages": Array [], 309 | "fullName": "", 310 | "numPassingAsserts": 1, 311 | "status": "passed", 312 | "title": "", 313 | }, 314 | ], 315 | "v8Coverage": undefined, 316 | } 317 | `; 318 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import createJestRunner from "create-lite-jest-runner"; 2 | import run from "./run.js"; 3 | 4 | export default createJestRunner(run); 5 | -------------------------------------------------------------------------------- /src/run.test.ts: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import run from "./run"; 3 | import { fileURLToPath } from "node:url"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = path.dirname(__filename); 7 | 8 | // Remove undeterministic data from test reports 9 | expect.addSnapshotSerializer({ 10 | print: (value: any, serialize) => { 11 | delete value.perfStats; 12 | delete value.testFilePath; 13 | for (const result of value.testResults) { 14 | delete result.duration; 15 | } 16 | return serialize(value); 17 | }, 18 | test: (value) => 19 | value && value.perfStats && value.testFilePath && value.testResults, 20 | }); 21 | 22 | describe("jest-runner-prettier", () => { 23 | describe("JSON", () => { 24 | describe("good fixture", () => { 25 | it("matches snapshot", () => { 26 | return run({ 27 | testPath: path.join(__dirname, "__fixtures__", `good.json`), 28 | }).then((result) => expect(result).toMatchSnapshot()); 29 | }); 30 | }); 31 | 32 | describe("bad fixture", () => { 33 | it("matches snapshot", () => { 34 | return run({ 35 | testPath: path.join(__dirname, "__fixtures__", `bad.json`), 36 | }).then((result) => expect(result).toMatchSnapshot()); 37 | }); 38 | }); 39 | }); 40 | 41 | describe("JSX", () => { 42 | describe("good fixture", () => { 43 | it("matches snapshot", () => { 44 | return run({ 45 | testPath: path.join(__dirname, "__fixtures__", `good.jsx`), 46 | }).then((result) => expect(result).toMatchSnapshot()); 47 | }); 48 | }); 49 | 50 | describe("bad fixture", () => { 51 | it("matches snapshot", () => { 52 | return run({ 53 | testPath: path.join(__dirname, "__fixtures__", `bad.jsx`), 54 | }).then((result) => expect(result).toMatchSnapshot()); 55 | }); 56 | }); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/run.ts: -------------------------------------------------------------------------------- 1 | import { TestResult } from "@jest/test-result"; 2 | import { emphasize } from "emphasize"; 3 | import { pass, fail } from "create-lite-jest-runner"; 4 | import * as fs from "node:fs/promises"; 5 | import { diff } from "jest-diff"; 6 | import prettier from "prettier"; 7 | 8 | interface Parameters { 9 | testPath: string; 10 | } 11 | 12 | export default async ({ testPath }: Parameters): Promise => { 13 | const start = Date.now(); 14 | const contents = await fs.readFile(testPath, "utf8"); 15 | const config = await prettier.resolveConfig(testPath); 16 | 17 | const prettierConfig = { 18 | ...config, 19 | filepath: testPath, 20 | }; 21 | 22 | const isPretty = prettier.check(contents, prettierConfig); 23 | if (isPretty) { 24 | return pass({ 25 | start, 26 | end: Date.now(), 27 | test: { path: testPath }, 28 | }); 29 | } 30 | 31 | const formatted = prettier.format(contents, prettierConfig); 32 | 33 | return fail({ 34 | start, 35 | end: Date.now(), 36 | test: { 37 | path: testPath, 38 | errorMessage: diff( 39 | emphasize.highlightAuto(formatted).value, 40 | emphasize.highlightAuto(contents).value, 41 | { 42 | expand: false, 43 | } 44 | ) as string | undefined, 45 | }, 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "esnext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "esnext" /* Specify what module code is generated. */, 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | // "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, 46 | "declarationMap": true /* Create sourcemaps for d.ts files. */, 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | "sourceMap": true /* Create source map files for emitted JavaScript files. */, 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | "outDir": "./dist/" /* Specify an output folder for all emitted files. */, 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | // "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 75 | 76 | /* Type Checking */ 77 | "strict": true /* Enable all strict type-checking options. */, 78 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ 87 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | } 101 | } 102 | --------------------------------------------------------------------------------