From d33014697e602b035a9187711676b410183d8887 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 19 Feb 2021 15:46:55 -0500 Subject: [PATCH 1/7] tests(lib): peer dep validator --- package.json | 10 +- src/index.ts | 6 +- src/lib/{package.ts => peerDepValidator.ts} | 148 +++++++++------- tests/lib/package.spec.ts | 183 ++++++++++++++++++++ tests/test.spec.ts | 3 - tests/tsconfig.json | 6 +- yarn.lock | 96 +++++++++- 7 files changed, 374 insertions(+), 78 deletions(-) rename src/lib/{package.ts => peerDepValidator.ts} (54%) create mode 100644 tests/lib/package.spec.ts delete mode 100644 tests/test.spec.ts diff --git a/package.json b/package.json index 44d40e03f..3ca1f1fd8 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dev": "tsc --build --watch", "build": "yarn clean && tsc", "test": "jest", + "tdd": "jest --watch", "clean": "rm -rf dist && rm -rf node_modules/.cache", "release:pr": "dripip pr", "release:canary": "dripip preview", @@ -21,13 +22,19 @@ }, "devDependencies": { "@prisma-labs/prettier-config": "0.1.0", + "@prisma/client": "2.17.0", "@types/jest": "26.0.20", + "@types/lodash": "^4.14.168", "@types/semver": "^7.3.4", "dripip": "0.10.0", + "execa": "^5.0.0", + "fs-jetpack": "^4.1.0", "jest": "26.6.3", "jest-watch-typeahead": "0.6.1", + "lodash": "^4.17.20", "nexus": "^1.0.0", "prettier": "2.2.1", + "prisma": "2.17.0", "ts-jest": "26.5.1", "ts-node": "^9.1.1", "type-fest": "^0.21.1", @@ -35,10 +42,11 @@ }, "prettier": "@prisma-labs/prettier-config", "peerDependencies": { - "@prisma/client": "^2.17.0", + "@prisma/client": "2.17.x", "nexus": "^1.0.0" }, "dependencies": { + "endent": "^2.0.1", "kleur": "^4.1.4", "semver": "^7.3.4" } diff --git a/src/index.ts b/src/index.ts index d94a85730..4574b4758 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -import { validatePeerDependencies } from './lib/package' +import { enforceValidPeerDependencies } from './lib/peerDepValidator' -validatePeerDependencies({ - packageJson: require('../package.json'), +enforceValidPeerDependencies({ + packageJson: require('../../package.json'), }) const nexusPrisma = 'todo' diff --git a/src/lib/package.ts b/src/lib/peerDepValidator.ts similarity index 54% rename from src/lib/package.ts rename to src/lib/peerDepValidator.ts index c790c7963..034428f1e 100644 --- a/src/lib/package.ts +++ b/src/lib/peerDepValidator.ts @@ -2,54 +2,107 @@ import kleur = require('kleur') import * as Semver from 'semver' import { PackageJson } from 'type-fest' +type Failure = + | { message: string; kind: 'peer_dep_not_installed' } + | { message: string; kind: 'peer_dep_invalid_json'; error: unknown } + | { message: string; kind: 'peer_dep_invalid_package_json' } + | { message: string; kind: 'peer_dep_invalid_version' } + | { message: string; kind: 'unexpected_error'; error: unknown } + +export function enforceValidPeerDependencies({ packageJson }: { packageJson: PackageJson }): void { + if (['true', '1'].includes(process.env.NO_PEER_DEPENDENCY_CHECK ?? '')) return + if (['false', '0'].includes(process.env.PEER_DEPENDENCY_CHECK ?? '')) return + + const failure = validatePeerDependencies({ packageJson }) + + if (failure) { + console.log(failure.message) + + if ('error' in failure) { + console.error(failure.error) + } + + if (failure.kind === 'peer_dep_not_installed') { + process.exit(1) + } + } +} + /** * Check that the given package's peer dependency requirements are met. * - * NO-op if PEER_DEPENDENCY_CHECK envar is set to false or 0 - * NO-op if NO_PEER_DEPENDENCY_CHECK envar is set to true or 1 + * When envar skipping enabled then: + * + * 1. NO-op if PEER_DEPENDENCY_CHECK envar is set to false or 0 + * 2. NO-op if NO_PEER_DEPENDENCY_CHECK envar is set to true or 1 */ -export function validatePeerDependencies({ packageJson }: { packageJson: PackageJson }): void { - if (['true', '1'].includes(process.env.NO_PEER_DEPENDENCY_CHECK ?? '')) return - if (['false', '0'].includes(process.env.PEER_DEPENDENCY_CHECK ?? '')) return - +export function validatePeerDependencies({ packageJson }: { packageJson: PackageJson }): null | Failure { try { - const name = packageJson.name ?? '' const peerDependencies = packageJson['peerDependencies'] ?? [] for (const [pdName, _] of Object.entries(peerDependencies)) { - checkPeerDependencyIsImportableOrFatal({ requiredBy: name, dependencyName: pdName }) - - checkPeerDependencyRangeSatisfiedOrWarn({ + const failure = validatePeerDependencyRangeSatisfied({ peerDependencyName: pdName, requireer: packageJson, }) + + if (failure) return failure + } + } catch (error) { + return { + kind: 'unexpected_error', + message: renderWarning(`Something went wrong while trying to validate peer dependencies`), + error, } - } catch (e) { - console.warn( - renderWarning(`Something went wrong while trying to validate peer dependencies:\n\n${e.stack}`) - ) } + + return null } -export function checkPeerDependencyRangeSatisfiedOrWarn({ +export function validatePeerDependencyRangeSatisfied({ peerDependencyName, requireer, }: { peerDependencyName: string requireer: PackageJson -}): void { - const pdPackageJson = require(`${peerDependencyName}/package.json`) as PackageJson +}): null | Failure { + let pdPackageJson: PackageJson + + try { + pdPackageJson = require(`${peerDependencyName}/package.json`) as PackageJson + } catch (error: unknown) { + if (!isModuleNotFoundError(error)) { + return { + kind: 'peer_dep_invalid_json', + message: `Peer dependency check found ${peerDependencyName} requried by ${requireer.name} to be installed but encountered an error while reading its package.json.`, + error, + } + } + + return { + kind: 'peer_dep_not_installed', + message: renderError( + `${kleur.green(peerDependencyName)} is a peer dependency required by ${kleur.yellow( + requireer.name ?? '' + )}. But you have not installed it into this project yet. Please run \`${kleur.green( + renderPackageCommand(`add ${peerDependencyName}`) + )}\`.` + ), + } + } + const pdVersion = pdPackageJson.version const pdVersionRangeSupported = requireer.peerDependencies?.[peerDependencyName] - // npm enforces that package manifests have a valid "version" field so this case _should_ never happen under normal circumstances. + // npm enforces that package manifests have a valid "version" field so this + // case _should_ never happen under normal circumstances. if (!pdVersion) { - console.warn( - renderWarning( + return { + kind: 'peer_dep_invalid_package_json', + message: renderWarning( `Peer dependency validation check failed unexpectedly. ${requireer.name} requires peer dependency ${pdPackageJson.name}. No version info for ${pdPackageJson.name} could be found in its package.json thus preventing a check if its version satisfies the peer dependency version range.` - ) - ) - return + ), + } } if (!pdVersionRangeSupported) { @@ -58,53 +111,18 @@ export function checkPeerDependencyRangeSatisfiedOrWarn({ `Peer dependency validation check failed unexpectedly. ${requireer.name} apparently requires peer dependency ${pdPackageJson.name} yet ${pdPackageJson.name} is not listed in the peer dependency listing of ${requireer.name}.` ) ) - return + return null } if (Semver.satisfies(pdVersion, pdVersionRangeSupported)) { - return + return null } - console.warn( - renderWarning( + return { + kind: 'peer_dep_invalid_version', + message: renderWarning( `Peer dependency validation check failed: ${requireer.name}@${requireer.version} does not officially support ${pdPackageJson.name}@${pdPackageJson.version}. The officially supported range is: \`${pdVersionRangeSupported}\`. This could lead to undefined behaviors and bugs.` - ) - ) -} - -/** - * Ensure that some package has been installed as a peer dep by the user. - */ -export function checkPeerDependencyIsImportableOrFatal({ - dependencyName, - requiredBy, -}: { - dependencyName: string - requiredBy: string -}): void { - try { - require(dependencyName) - } catch (error: unknown) { - if (!isModuleNotFoundError(error)) { - console.warn( - `Peer dependency check confirmed that ${dependencyName} requried by ${requiredBy} is importable however an error occured during import. This probably means something is wrong and your application will not work.\n\n${ - error instanceof Error ? error.stack : error - }` - ) - return - } - - console.error( - renderError( - `${kleur.green(dependencyName)} is a peer dependency required by ${kleur.yellow( - requiredBy - )}. But you have not installed it into this project yet. Please run \`${kleur.green( - renderPackageCommand(`add ${dependencyName}`) - )}\`.` - ) - ) - - process.exit(1) + ), } } @@ -128,7 +146,7 @@ function renderPackageCommand(command: string): string { } function isModuleNotFoundError(error: any): error is Error { - if (error instanceof Error && (error as any).code !== 'MODULE_NOT_FOUND') { + if (error instanceof Error && (error as any).code === 'MODULE_NOT_FOUND') { return true } diff --git a/tests/lib/package.spec.ts b/tests/lib/package.spec.ts new file mode 100644 index 000000000..a2b39c6b0 --- /dev/null +++ b/tests/lib/package.spec.ts @@ -0,0 +1,183 @@ +import endent from 'endent' +import * as execa from 'execa' +import * as fs from 'fs-jetpack' +import { FSJetpack } from 'fs-jetpack/types' +import { merge, omit } from 'lodash' +import { PackageJson } from 'type-fest' + +/** + * setup + */ + +const badJson = ';' + +const requireer = { + name: 'alpha', +} + +const peerDep = { + name: 'charlie', +} + +let tmpdir: FSJetpack + +beforeAll(() => { + if (fs.exists('../../dist')) { + throw new Error(`Please build package before running this test`) + } + + tmpdir = fs.tmpDir() + // console.log(tmpdir.cwd()) + + // Setup project + + tmpdir.write('package.json', { + name: 'myapp', + version: '1.0.0', + main: 'index.js', + dependencies: {}, + }) + + // setup alpha dep that has peer dep requirements + + execa.commandSync(`yarn add kleur semver endent --production`, { cwd: tmpdir.cwd() }) + + tmpdir.write(`node_modules/${requireer.name}/package.json`, { + name: requireer.name, + version: '1.0.0', + main: 'dist/index.js', + }) + + tmpdir.copy(`${process.cwd()}/dist`, `${tmpdir.cwd()}/node_modules/${requireer.name}/dist`) + + tmpdir.write( + 'index.js', + endent` + const assert = require('assert') + const { validatePeerDependencies } = require('${requireer.name}/dist/lib/peerDepValidator') + + const packageJson = require('${requireer.name}/package.json') + assert(packageJson) + + const failure = validatePeerDependencies({ + packageJson, + }) + + console.log(failure) + ` + ) + + setupPeerDepRequirement({ + name: peerDep.name, + range: '2.0.x', + }) +}) + +/** + * helpers + */ + +function setupDep({ + name, + version = '0.0.0', + main = 'exports.hello = "world"', + packageJson = (x) => x, +}: { + name: string + version?: string + main?: string + packageJson?: (defaultPackageJson: PackageJson) => PackageJson | string +}): void { + const depdir = `node_modules/${name}` + tmpdir.write(`${depdir}/package.json`, packageJson({ name, version, main: './index.js' })) + tmpdir.write(`${depdir}/index.js`, main) + beforeEach(() => { + fs.remove(depdir) + }) +} + +function setupPeerDepRequirement({ name, range }: { name: string; range: string }) { + const old = tmpdir.read(`node_modules/${requireer.name}/package.json`, 'json') + tmpdir.write( + `node_modules/${requireer.name}/package.json`, + merge(old, { peerDependencies: { [name]: range } }) + ) +} + +function runPeerDepValidator(params?: { env?: Record }) { + return execa.commandSync('node index.js', { + cwd: tmpdir.cwd(), + env: { + ...process.env, + ...params?.env, + }, + }).stdout +} + +/** + * tests + */ + +it('if project is missing peer dependency an error is thrown', () => { + expect(runPeerDepValidator()).toMatchInlineSnapshot(` + "{ + kind: 'peer_dep_not_installed', + message: '\\\\x1B[31mERROR:\\\\x1B[39m \\\\x1B[32mcharlie\\\\x1B[39m is a peer dependency required by \\\\x1B[33malpha\\\\x1B[39m. But you have not installed it into this project yet. Please run \`\\\\x1B[32myarn add charlie\\\\x1B[39m\`.' + }" + `) +}) + +it('if project has peer dep, but upon import it fails somehow, then a warning given', () => { + setupDep({ + name: peerDep.name, + packageJson() { + return badJson + }, + }) + expect(runPeerDepValidator()).toMatch(/peer_dep_invalid_json/) +}) + +it('if project peer dep version does not satisfy required range, then a warning given', () => { + setupDep({ + name: peerDep.name, + version: '1.0.0', + }) + expect(runPeerDepValidator()).toMatchInlineSnapshot(` + "{ + kind: 'peer_dep_invalid_version', + message: '\\\\x1B[33mWARNING:\\\\x1B[39m Peer dependency validation check failed: alpha@1.0.0 does not officially support charlie@1.0.0. The officially supported range is: \`2.0.x\`. This could lead to undefined behaviors and bugs.' + }" + `) +}) + +it('if project peer dep version satisfies required range, then nothing happens', () => { + setupDep({ + name: peerDep.name, + version: '2.0.1', + }) + expect(runPeerDepValidator()).toMatchInlineSnapshot(`"null"`) +}) + +it('if peer dep package.json missing version field, then a warning given', () => { + setupDep({ + name: peerDep.name, + version: '1.0.0', + packageJson(packageJson) { + return omit(packageJson, 'version') + }, + }) + expect(runPeerDepValidator()).toMatchInlineSnapshot(` + "{ + kind: 'peer_dep_invalid_package_json', + message: '\\\\x1B[33mWARNING:\\\\x1B[39m Peer dependency validation check failed unexpectedly. alpha requires peer dependency charlie. No version info for charlie could be found in its package.json thus preventing a check if its version satisfies the peer dependency version range.' + }" + `) +}) + +// it('if PEER_DEPENDENCY_CHECK=false then no validation happens', () => { +// expect(getResult({ env: { PEER_DEPENDENCY_CHECK: 'false' } })).toMatchInlineSnapshot(`"null"`) +// }) + +// it('if NO_PEER_DEPENDENCY_CHECK=true then no validation happens', () => { +// expect(getResult({ env: { NO_PEER_DEPENDENCY_CHECK: 'true' } })).toMatchInlineSnapshot(`"null"`) +// }) diff --git a/tests/test.spec.ts b/tests/test.spec.ts deleted file mode 100644 index 3d71e78e8..000000000 --- a/tests/test.spec.ts +++ /dev/null @@ -1,3 +0,0 @@ -it('works', () => { - expect(true).toEqual(true) -}) diff --git a/tests/tsconfig.json b/tests/tsconfig.json index c2994ac4e..90e41a1df 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -1,5 +1,9 @@ { "extends": "../tsconfig.base.json", "references": [{ "path": ".." }], - "include": ["."] + "include": ["."], + "compilerOptions": { + // for strip-ansi + "esModuleInterop": true + } } diff --git a/yarn.lock b/yarn.lock index 7bdc12dd6..93239c972 100644 --- a/yarn.lock +++ b/yarn.lock @@ -671,6 +671,23 @@ resolved "https://registry.yarnpkg.com/@prisma-labs/prettier-config/-/prettier-config-0.1.0.tgz#ef6cb5f89487974ca997516e9b39d5ccbefeeaad" integrity sha512-P0h2y+gnIxFP2HdsTYSYHWmabGBlxyVjnUepsrRe8gAF36mxOonGsbsQmKt/Q9H9CMjrSkFoDe5F5HLi2iW5/Q== +"@prisma/client@2.17.0": + version "2.17.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.17.0.tgz#e38462796c2e824504763416f5e3a7219d70652d" + integrity sha512-tzsBxtx9J1epOGCiBeXur1tEz81UIdWg2G/HpDmflXKcv/MJb+KCWSKSsEW49eXcvVwRgxNyxLoCO6CwvjQKcg== + dependencies: + "@prisma/engines-version" "2.17.0-35.3c463ebd78b1d21d8fdacdd27899e280cf686223" + +"@prisma/engines-version@2.17.0-35.3c463ebd78b1d21d8fdacdd27899e280cf686223": + version "2.17.0-35.3c463ebd78b1d21d8fdacdd27899e280cf686223" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.17.0-35.3c463ebd78b1d21d8fdacdd27899e280cf686223.tgz#9ae6ed4467a0febff8afaf216c1bb67225f51b84" + integrity sha512-9idv5blqPUlvUPVT48eVi3T0RS/NBklUcjrla3jWyV8AYfB2BdaG/Zci7H5ajyYLnfZZHG9tBpP0LcveQCFH8A== + +"@prisma/engines@2.17.0-35.3c463ebd78b1d21d8fdacdd27899e280cf686223": + version "2.17.0-35.3c463ebd78b1d21d8fdacdd27899e280cf686223" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.17.0-35.3c463ebd78b1d21d8fdacdd27899e280cf686223.tgz#08bc3633fd27fb1935805ef16c37802ed713db5b" + integrity sha512-FKjVD6NYbGiQhwas3hA2uMpNchz+Mf3tv5qA8Ci9cAkKHGqt3jWjjUAK9juVBqeOcv4OPimQYMrkRX6SvaxBjg== + "@sinonjs/commons@^1.7.0": version "1.8.2" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b" @@ -757,6 +774,11 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" +"@types/lodash@^4.14.168": + version "4.14.168" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" + integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== + "@types/node@*": version "14.14.29" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.29.tgz#2bbaefda2b715f9a0cd31c55b55b9092c5920e59" @@ -1321,7 +1343,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1399,6 +1421,11 @@ decompress-response@^4.2.0: dependencies: mimic-response "^2.0.0" +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -1523,6 +1550,15 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" +endent@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/endent/-/endent-2.0.1.tgz#fb18383a3f37ae3213a5d9f6c4a880d1061eb4c5" + integrity sha512-mADztvcC+vCk4XEZaCz6xIPO2NHQuprv5CAEjuVAu6aZwqAj7nVNlMyl1goPFYqCCpS2OJV9jwpumJLkotZrNw== + dependencies: + dedent "^0.7.0" + fast-json-parse "^1.0.3" + objectorarray "^1.0.4" + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1605,6 +1641,21 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + exit-on-epipe@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" @@ -1701,6 +1752,11 @@ fast-glob@^3.1.1: micromatch "^4.0.2" picomatch "^2.2.1" +fast-json-parse@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" + integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -1793,6 +1849,14 @@ fs-jetpack@^3.1.0: minimatch "^3.0.2" rimraf "^2.6.3" +fs-jetpack@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fs-jetpack/-/fs-jetpack-4.1.0.tgz#d693fcffd3cedbd8829226967866b9e89f290f0f" + integrity sha512-h4nHLIcCaxnXfUWhwP+mLnar03R2DBlqicNvKJG44TJob8RV6GB8EKNwJgSaBeDAfqWhqq01y+Ao96vRwpXlPw== + dependencies: + minimatch "^3.0.2" + rimraf "^2.6.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1837,6 +1901,11 @@ get-stream@^5.0.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" + integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -1992,6 +2061,11 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2874,7 +2948,7 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash@4.x, lodash@^4.17.19: +lodash@4.x, lodash@^4.17.19, lodash@^4.17.20: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -3109,7 +3183,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -3149,6 +3223,11 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +objectorarray@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.4.tgz#d69b2f0ff7dc2701903d308bb85882f4ddb49483" + integrity sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3156,7 +3235,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -3331,6 +3410,13 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== +prisma@2.17.0: + version "2.17.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.17.0.tgz#686469914ed13d4b0926ee5f17efb7a9ab741e8a" + integrity sha512-NypJI7OCXCfDkRKubbVb3nmPeRJ1SjQfg6QAwK06KsreBZl1F96rFz2iB2bl4kIrhLAbIySBjwUJlG87Jsxt7g== + dependencies: + "@prisma/engines" "2.17.0-35.3c463ebd78b1d21d8fdacdd27899e280cf686223" + prompts@^2.0.1: version "2.4.0" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" @@ -3649,7 +3735,7 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== From 80e8459281aeed92cf20a18654a4513615caba84 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 19 Feb 2021 16:20:34 -0500 Subject: [PATCH 2/7] force colors --- CONTRIBUTING.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..3b5fa5d81 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +## Tests + +- We enable `FORCE_COLOR=1` in npm test script so that we capture `kleur` colors in snapshots even in CI. We capture colors since we want to avoid regressions of the DX that colours provide. diff --git a/package.json b/package.json index 3ca1f1fd8..aac4f974a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "format": "prettier --write .", "dev": "tsc --build --watch", "build": "yarn clean && tsc", - "test": "jest", + "test": "FORCE_COLOR=1 jest", "tdd": "jest --watch", "clean": "rm -rf dist && rm -rf node_modules/.cache", "release:pr": "dripip pr", From e19bd9d79bcf02cbdc57d4a0ff0a973d371abc88 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 19 Feb 2021 18:07:26 -0500 Subject: [PATCH 3/7] disable colors --- CONTRIBUTING.md | 2 +- package.json | 2 +- tests/lib/package.spec.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b5fa5d81..e2495d2e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,3 @@ ## Tests -- We enable `FORCE_COLOR=1` in npm test script so that we capture `kleur` colors in snapshots even in CI. We capture colors since we want to avoid regressions of the DX that colours provide. +- We do `FORCE_COLOR=0` in npm test script so that we disable `kleur` colors in snapshots all the time. It would be nice to put the DX of colors into tests but needs some work. Node 12/14 results in different codes, [thus different snapshots](https://github.com/prisma/nexus-prisma/pull/3#issuecomment-782432471). See test-mode feature request here: https://github.com/lukeed/kleur/issues/47#issue-812419257. diff --git a/package.json b/package.json index aac4f974a..1bbf7757b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "format": "prettier --write .", "dev": "tsc --build --watch", "build": "yarn clean && tsc", - "test": "FORCE_COLOR=1 jest", + "test": "FORCE_COLOR=0 jest", "tdd": "jest --watch", "clean": "rm -rf dist && rm -rf node_modules/.cache", "release:pr": "dripip pr", diff --git a/tests/lib/package.spec.ts b/tests/lib/package.spec.ts index a2b39c6b0..e3335d0dd 100644 --- a/tests/lib/package.spec.ts +++ b/tests/lib/package.spec.ts @@ -122,7 +122,7 @@ it('if project is missing peer dependency an error is thrown', () => { expect(runPeerDepValidator()).toMatchInlineSnapshot(` "{ kind: 'peer_dep_not_installed', - message: '\\\\x1B[31mERROR:\\\\x1B[39m \\\\x1B[32mcharlie\\\\x1B[39m is a peer dependency required by \\\\x1B[33malpha\\\\x1B[39m. But you have not installed it into this project yet. Please run \`\\\\x1B[32myarn add charlie\\\\x1B[39m\`.' + message: 'ERROR: charlie is a peer dependency required by alpha. But you have not installed it into this project yet. Please run \`yarn add charlie\`.' }" `) }) @@ -145,7 +145,7 @@ it('if project peer dep version does not satisfy required range, then a warning expect(runPeerDepValidator()).toMatchInlineSnapshot(` "{ kind: 'peer_dep_invalid_version', - message: '\\\\x1B[33mWARNING:\\\\x1B[39m Peer dependency validation check failed: alpha@1.0.0 does not officially support charlie@1.0.0. The officially supported range is: \`2.0.x\`. This could lead to undefined behaviors and bugs.' + message: 'WARNING: Peer dependency validation check failed: alpha@1.0.0 does not officially support charlie@1.0.0. The officially supported range is: \`2.0.x\`. This could lead to undefined behaviors and bugs.' }" `) }) @@ -169,7 +169,7 @@ it('if peer dep package.json missing version field, then a warning given', () => expect(runPeerDepValidator()).toMatchInlineSnapshot(` "{ kind: 'peer_dep_invalid_package_json', - message: '\\\\x1B[33mWARNING:\\\\x1B[39m Peer dependency validation check failed unexpectedly. alpha requires peer dependency charlie. No version info for charlie could be found in its package.json thus preventing a check if its version satisfies the peer dependency version range.' + message: 'WARNING: Peer dependency validation check failed unexpectedly. alpha requires peer dependency charlie. No version info for charlie could be found in its package.json thus preventing a check if its version satisfies the peer dependency version range.' }" `) }) From 8c43ee8ff0221fd8936ec182c76b3f1aa44e21b4 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 19 Feb 2021 21:22:56 -0500 Subject: [PATCH 4/7] avoid envars not supported on windows --- package.json | 2 +- tests/lib/package.spec.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1bbf7757b..3ca1f1fd8 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "format": "prettier --write .", "dev": "tsc --build --watch", "build": "yarn clean && tsc", - "test": "FORCE_COLOR=0 jest", + "test": "jest", "tdd": "jest --watch", "clean": "rm -rf dist && rm -rf node_modules/.cache", "release:pr": "dripip pr", diff --git a/tests/lib/package.spec.ts b/tests/lib/package.spec.ts index e3335d0dd..77715cee0 100644 --- a/tests/lib/package.spec.ts +++ b/tests/lib/package.spec.ts @@ -2,9 +2,12 @@ import endent from 'endent' import * as execa from 'execa' import * as fs from 'fs-jetpack' import { FSJetpack } from 'fs-jetpack/types' +import kleur from 'kleur' import { merge, omit } from 'lodash' import { PackageJson } from 'type-fest' +kleur.enabled = false + /** * setup */ From ef67886c4071f2e844b37761f987c0b240e85492 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 19 Feb 2021 21:23:47 -0500 Subject: [PATCH 5/7] update doc --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e2495d2e9..284fdcc10 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,3 @@ ## Tests -- We do `FORCE_COLOR=0` in npm test script so that we disable `kleur` colors in snapshots all the time. It would be nice to put the DX of colors into tests but needs some work. Node 12/14 results in different codes, [thus different snapshots](https://github.com/prisma/nexus-prisma/pull/3#issuecomment-782432471). See test-mode feature request here: https://github.com/lukeed/kleur/issues/47#issue-812419257. +- We disable `kleur` colors so snapshots do not have them. It would be nice to put the DX of colors into tests but needs some work. Node 12/14 results in different codes, [thus different snapshots](https://github.com/prisma/nexus-prisma/pull/3#issuecomment-782432471). See test-mode feature request here: https://github.com/lukeed/kleur/issues/47#issue-812419257. From 4bcc434ac92d303613e095d91ab39f7604162818 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 19 Feb 2021 22:13:43 -0500 Subject: [PATCH 6/7] refactor --- src/lib/peerDepValidator.ts | 1 + .../peerDepValidator.test.ts.snap | 24 ++ tests/lib/package.spec.ts | 186 ---------------- tests/lib/peerDepValidator.test.ts | 207 ++++++++++++++++++ tests/tsconfig.json | 3 +- 5 files changed, 233 insertions(+), 188 deletions(-) create mode 100644 tests/lib/__snapshots__/peerDepValidator.test.ts.snap delete mode 100644 tests/lib/package.spec.ts create mode 100644 tests/lib/peerDepValidator.test.ts diff --git a/src/lib/peerDepValidator.ts b/src/lib/peerDepValidator.ts index 034428f1e..a85722a28 100644 --- a/src/lib/peerDepValidator.ts +++ b/src/lib/peerDepValidator.ts @@ -14,6 +14,7 @@ export function enforceValidPeerDependencies({ packageJson }: { packageJson: Pac if (['false', '0'].includes(process.env.PEER_DEPENDENCY_CHECK ?? '')) return const failure = validatePeerDependencies({ packageJson }) + console.log(failure) if (failure) { console.log(failure.message) diff --git a/tests/lib/__snapshots__/peerDepValidator.test.ts.snap b/tests/lib/__snapshots__/peerDepValidator.test.ts.snap new file mode 100644 index 000000000..ebb50cb35 --- /dev/null +++ b/tests/lib/__snapshots__/peerDepValidator.test.ts.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ValidatePeerDependencies if peer dep missing, then returns failure 1`] = ` +"{ + kind: 'peer_dep_not_installed', + message: 'ERROR: charlie is a peer dependency required by alpha. But you have not installed it into this project yet. Please run \`yarn add charlie\`.' +}" +`; + +exports[`ValidatePeerDependencies if peer dep package.json missing version field, then returns failure 1`] = ` +"{ + kind: 'peer_dep_invalid_package_json', + message: 'WARNING: Peer dependency validation check failed unexpectedly. alpha requires peer dependency charlie. No version info for charlie could be found in its package.json thus preventing a check if its version satisfies the peer dependency version range.' +}" +`; + +exports[`ValidatePeerDependencies if peer dep version satisfies required range, then returns null 1`] = `"null"`; + +exports[`ValidatePeerDependencies if project peer dep version does not satisfy required range, then returns failure 1`] = ` +"{ + kind: 'peer_dep_invalid_version', + message: 'WARNING: Peer dependency validation check failed: alpha@1.0.0 does not officially support charlie@1.0.0. The officially supported range is: \`2.0.x\`. This could lead to undefined behaviors and bugs.' +}" +`; diff --git a/tests/lib/package.spec.ts b/tests/lib/package.spec.ts deleted file mode 100644 index 77715cee0..000000000 --- a/tests/lib/package.spec.ts +++ /dev/null @@ -1,186 +0,0 @@ -import endent from 'endent' -import * as execa from 'execa' -import * as fs from 'fs-jetpack' -import { FSJetpack } from 'fs-jetpack/types' -import kleur from 'kleur' -import { merge, omit } from 'lodash' -import { PackageJson } from 'type-fest' - -kleur.enabled = false - -/** - * setup - */ - -const badJson = ';' - -const requireer = { - name: 'alpha', -} - -const peerDep = { - name: 'charlie', -} - -let tmpdir: FSJetpack - -beforeAll(() => { - if (fs.exists('../../dist')) { - throw new Error(`Please build package before running this test`) - } - - tmpdir = fs.tmpDir() - // console.log(tmpdir.cwd()) - - // Setup project - - tmpdir.write('package.json', { - name: 'myapp', - version: '1.0.0', - main: 'index.js', - dependencies: {}, - }) - - // setup alpha dep that has peer dep requirements - - execa.commandSync(`yarn add kleur semver endent --production`, { cwd: tmpdir.cwd() }) - - tmpdir.write(`node_modules/${requireer.name}/package.json`, { - name: requireer.name, - version: '1.0.0', - main: 'dist/index.js', - }) - - tmpdir.copy(`${process.cwd()}/dist`, `${tmpdir.cwd()}/node_modules/${requireer.name}/dist`) - - tmpdir.write( - 'index.js', - endent` - const assert = require('assert') - const { validatePeerDependencies } = require('${requireer.name}/dist/lib/peerDepValidator') - - const packageJson = require('${requireer.name}/package.json') - assert(packageJson) - - const failure = validatePeerDependencies({ - packageJson, - }) - - console.log(failure) - ` - ) - - setupPeerDepRequirement({ - name: peerDep.name, - range: '2.0.x', - }) -}) - -/** - * helpers - */ - -function setupDep({ - name, - version = '0.0.0', - main = 'exports.hello = "world"', - packageJson = (x) => x, -}: { - name: string - version?: string - main?: string - packageJson?: (defaultPackageJson: PackageJson) => PackageJson | string -}): void { - const depdir = `node_modules/${name}` - tmpdir.write(`${depdir}/package.json`, packageJson({ name, version, main: './index.js' })) - tmpdir.write(`${depdir}/index.js`, main) - beforeEach(() => { - fs.remove(depdir) - }) -} - -function setupPeerDepRequirement({ name, range }: { name: string; range: string }) { - const old = tmpdir.read(`node_modules/${requireer.name}/package.json`, 'json') - tmpdir.write( - `node_modules/${requireer.name}/package.json`, - merge(old, { peerDependencies: { [name]: range } }) - ) -} - -function runPeerDepValidator(params?: { env?: Record }) { - return execa.commandSync('node index.js', { - cwd: tmpdir.cwd(), - env: { - ...process.env, - ...params?.env, - }, - }).stdout -} - -/** - * tests - */ - -it('if project is missing peer dependency an error is thrown', () => { - expect(runPeerDepValidator()).toMatchInlineSnapshot(` - "{ - kind: 'peer_dep_not_installed', - message: 'ERROR: charlie is a peer dependency required by alpha. But you have not installed it into this project yet. Please run \`yarn add charlie\`.' - }" - `) -}) - -it('if project has peer dep, but upon import it fails somehow, then a warning given', () => { - setupDep({ - name: peerDep.name, - packageJson() { - return badJson - }, - }) - expect(runPeerDepValidator()).toMatch(/peer_dep_invalid_json/) -}) - -it('if project peer dep version does not satisfy required range, then a warning given', () => { - setupDep({ - name: peerDep.name, - version: '1.0.0', - }) - expect(runPeerDepValidator()).toMatchInlineSnapshot(` - "{ - kind: 'peer_dep_invalid_version', - message: 'WARNING: Peer dependency validation check failed: alpha@1.0.0 does not officially support charlie@1.0.0. The officially supported range is: \`2.0.x\`. This could lead to undefined behaviors and bugs.' - }" - `) -}) - -it('if project peer dep version satisfies required range, then nothing happens', () => { - setupDep({ - name: peerDep.name, - version: '2.0.1', - }) - expect(runPeerDepValidator()).toMatchInlineSnapshot(`"null"`) -}) - -it('if peer dep package.json missing version field, then a warning given', () => { - setupDep({ - name: peerDep.name, - version: '1.0.0', - packageJson(packageJson) { - return omit(packageJson, 'version') - }, - }) - expect(runPeerDepValidator()).toMatchInlineSnapshot(` - "{ - kind: 'peer_dep_invalid_package_json', - message: 'WARNING: Peer dependency validation check failed unexpectedly. alpha requires peer dependency charlie. No version info for charlie could be found in its package.json thus preventing a check if its version satisfies the peer dependency version range.' - }" - `) -}) - -// it('if PEER_DEPENDENCY_CHECK=false then no validation happens', () => { -// expect(getResult({ env: { PEER_DEPENDENCY_CHECK: 'false' } })).toMatchInlineSnapshot(`"null"`) -// }) - -// it('if NO_PEER_DEPENDENCY_CHECK=true then no validation happens', () => { -// expect(getResult({ env: { NO_PEER_DEPENDENCY_CHECK: 'true' } })).toMatchInlineSnapshot(`"null"`) -// }) diff --git a/tests/lib/peerDepValidator.test.ts b/tests/lib/peerDepValidator.test.ts new file mode 100644 index 000000000..88e82e189 --- /dev/null +++ b/tests/lib/peerDepValidator.test.ts @@ -0,0 +1,207 @@ +import endent from 'endent' +import * as execa from 'execa' +import * as fs from 'fs-jetpack' +import { FSJetpack } from 'fs-jetpack/types' +import { merge, omit } from 'lodash' +import { PackageJson } from 'type-fest' + +/** + * setup + */ + +const badJson = ';' + +const requireer = { + name: 'alpha', +} + +const peerDep = { + name: 'charlie', +} + +process.exit + +let tmpdir: FSJetpack + +beforeAll(() => { + if (fs.exists('../../dist')) { + throw new Error(`Please build package before running this test`) + } + + tmpdir = fs.tmpDir() + // console.log(tmpdir.cwd()) + + // Setup project + + tmpdir.write('package.json', { + name: 'myapp', + version: '1.0.0', + dependencies: {}, + }) + + // setup alpha dep that has peer dep requirements + + execa.commandSync(`yarn add kleur semver endent --production`, { cwd: tmpdir.cwd() }) + + tmpdir.write(`node_modules/${requireer.name}/package.json`, { + name: requireer.name, + version: '1.0.0', + main: 'dist/index.js', + }) + + tmpdir.copy(`${process.cwd()}/dist`, `${tmpdir.cwd()}/node_modules/${requireer.name}/dist`) + + tmpdir.write( + 'validatePeerDependencies.js', + endent` + const assert = require('assert') + const { validatePeerDependencies } = require('${requireer.name}/dist/lib/peerDepValidator') + + const packageJson = require('${requireer.name}/package.json') + assert(packageJson) + + const failure = validatePeerDependencies({ + packageJson, + }) + + console.log(failure) + ` + ) + + tmpdir.write( + 'enforceValidPeerDependencies.js', + endent` + const assert = require('assert') + const { enforceValidPeerDependencies } = require('${requireer.name}/dist/lib/peerDepValidator') + + const packageJson = require('${requireer.name}/package.json') + assert(packageJson) + + enforceValidPeerDependencies({ + packageJson, + }) + ` + ) + + setupPeerDepRequirement({ + name: peerDep.name, + range: '2.0.x', + }) +}) + +beforeEach(() => { + tmpdir.remove(`node_modules/${peerDep.name}`) +}) + +/** + * helpers + */ + +function setupDep({ + name, + version = '0.0.0', + main = 'exports.hello = "world"', + packageJson = (x) => x, +}: { + name: string + version?: string + main?: string + packageJson?: (defaultPackageJson: PackageJson) => PackageJson | string +}): void { + const depdir = `node_modules/${name}` + tmpdir.write(`${depdir}/package.json`, packageJson({ name, version, main: './index.js' })) + tmpdir.write(`${depdir}/index.js`, main) +} + +function setupPeerDepRequirement({ name, range }: { name: string; range: string }) { + const old = tmpdir.read(`node_modules/${requireer.name}/package.json`, 'json') + tmpdir.write( + `node_modules/${requireer.name}/package.json`, + merge(old, { peerDependencies: { [name]: range } }) + ) +} + +function runValidatePeerDependencies() { + return execa.commandSync('node validatePeerDependencies.js', { + cwd: tmpdir.cwd(), + env: { + ...process.env, + FORCE_COLOR: '0', + }, + }) +} + +function runEnforceValidPeerDependencies(params?: { env?: Record }) { + return execa.commandSync('node enforceValidPeerDependencies.js', { + cwd: tmpdir.cwd(), + env: { + ...process.env, + FORCE_COLOR: '0', + ...params?.env, + }, + reject: false, + }) +} + +/** + * tests + */ + +describe('ValidatePeerDependencies', () => { + it('if peer dep missing, then returns failure', () => { + expect(runValidatePeerDependencies().stdout).toMatchSnapshot() + }) + + it('if peer dep installed, but upon import JSON parsing fails somehow, then fails', () => { + setupDep({ + name: peerDep.name, + packageJson() { + return badJson + }, + }) + expect(runValidatePeerDependencies().stdout).toMatch(/peer_dep_invalid_json/) + }) + + it('if project peer dep version does not satisfy required range, then returns failure', () => { + setupDep({ + name: peerDep.name, + version: '1.0.0', + }) + expect(runValidatePeerDependencies().stdout).toMatchSnapshot() + }) + + it('if peer dep version satisfies required range, then returns null', () => { + setupDep({ + name: peerDep.name, + version: '2.0.1', + }) + expect(runValidatePeerDependencies().stdout).toMatchSnapshot() + }) + + it('if peer dep package.json missing version field, then returns failure', () => { + setupDep({ + name: peerDep.name, + version: '1.0.0', + packageJson(packageJson) { + return omit(packageJson, 'version') + }, + }) + expect(runValidatePeerDependencies().stdout).toMatchSnapshot() + }) +}) + +describe('enforceValidPeerDependencies', () => { + it('if PEER_DEPENDENCY_CHECK=false|0 then no validation happens', () => { + expect(runEnforceValidPeerDependencies({ env: { PEER_DEPENDENCY_CHECK: 'false' } }).stdout).toEqual(``) + expect(runEnforceValidPeerDependencies({ env: { PEER_DEPENDENCY_CHECK: '0' } }).stdout).toEqual(``) + }) + + it('if NO_PEER_DEPENDENCY_CHECK=true|1 then no validation happens', () => { + expect(runEnforceValidPeerDependencies({ env: { NO_PEER_DEPENDENCY_CHECK: 'true' } }).stdout).toEqual(``) + expect(runEnforceValidPeerDependencies({ env: { NO_PEER_DEPENDENCY_CHECK: '1' } }).stdout).toEqual(``) + }) + + it('if peer dependency is missing, than process exits 1', () => { + expect(runEnforceValidPeerDependencies().exitCode).toEqual(1) + }) +}) diff --git a/tests/tsconfig.json b/tests/tsconfig.json index 90e41a1df..ffedfc146 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -3,7 +3,6 @@ "references": [{ "path": ".." }], "include": ["."], "compilerOptions": { - // for strip-ansi - "esModuleInterop": true + "tsBuildInfoFile": "node_modules/.cache/.tsbuildinfo.test" } } From 955fdcdaf2e34055a8580edf92277d0fc49519c4 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 19 Feb 2021 22:15:28 -0500 Subject: [PATCH 7/7] snap enforcement test logs --- tests/lib/__snapshots__/peerDepValidator.test.ts.snap | 8 ++++++++ tests/lib/peerDepValidator.test.ts | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/lib/__snapshots__/peerDepValidator.test.ts.snap b/tests/lib/__snapshots__/peerDepValidator.test.ts.snap index ebb50cb35..8b603b42c 100644 --- a/tests/lib/__snapshots__/peerDepValidator.test.ts.snap +++ b/tests/lib/__snapshots__/peerDepValidator.test.ts.snap @@ -22,3 +22,11 @@ exports[`ValidatePeerDependencies if project peer dep version does not satisfy r message: 'WARNING: Peer dependency validation check failed: alpha@1.0.0 does not officially support charlie@1.0.0. The officially supported range is: \`2.0.x\`. This could lead to undefined behaviors and bugs.' }" `; + +exports[`enforceValidPeerDependencies if peer dependency is missing, than logs and process exits 1 1`] = ` +"{ + kind: 'peer_dep_not_installed', + message: 'ERROR: charlie is a peer dependency required by alpha. But you have not installed it into this project yet. Please run \`yarn add charlie\`.' +} +ERROR: charlie is a peer dependency required by alpha. But you have not installed it into this project yet. Please run \`yarn add charlie\`." +`; diff --git a/tests/lib/peerDepValidator.test.ts b/tests/lib/peerDepValidator.test.ts index 88e82e189..7ce7020b9 100644 --- a/tests/lib/peerDepValidator.test.ts +++ b/tests/lib/peerDepValidator.test.ts @@ -201,7 +201,9 @@ describe('enforceValidPeerDependencies', () => { expect(runEnforceValidPeerDependencies({ env: { NO_PEER_DEPENDENCY_CHECK: '1' } }).stdout).toEqual(``) }) - it('if peer dependency is missing, than process exits 1', () => { - expect(runEnforceValidPeerDependencies().exitCode).toEqual(1) + it('if peer dependency is missing, than logs and process exits 1', () => { + const result = runEnforceValidPeerDependencies() + expect(result.exitCode).toEqual(1) + expect(result.stdout).toMatchSnapshot() }) })