From 896573b12f1823611bf9668307c063efe2d212d8 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 27 Dec 2023 11:03:52 +0100 Subject: [PATCH] feat: move `InitialOptions` to JSON schema (#14776) --- packages/jest-schemas/src/index.ts | 34 +-- packages/jest-schemas/src/raw-types.ts | 354 +++++++++++++++++++++++++ packages/jest-types/src/Config.ts | 115 +------- 3 files changed, 364 insertions(+), 139 deletions(-) create mode 100644 packages/jest-schemas/src/raw-types.ts diff --git a/packages/jest-schemas/src/index.ts b/packages/jest-schemas/src/index.ts index 155b1c7ce04e..973c64edf105 100644 --- a/packages/jest-schemas/src/index.ts +++ b/packages/jest-schemas/src/index.ts @@ -6,33 +6,13 @@ */ import {type Static, Type} from '@sinclair/typebox'; - -const RawSnapshotFormat = Type.Partial( - Type.Object({ - callToJSON: Type.Readonly(Type.Boolean()), - compareKeys: Type.Readonly(Type.Null()), - escapeRegex: Type.Readonly(Type.Boolean()), - escapeString: Type.Readonly(Type.Boolean()), - highlight: Type.Readonly(Type.Boolean()), - indent: Type.Readonly(Type.Number({minimum: 0})), - maxDepth: Type.Readonly(Type.Number({minimum: 0})), - maxWidth: Type.Readonly(Type.Number({minimum: 0})), - min: Type.Readonly(Type.Boolean()), - printBasicPrototype: Type.Readonly(Type.Boolean()), - printFunctionName: Type.Readonly(Type.Boolean()), - theme: Type.Readonly( - Type.Partial( - Type.Object({ - comment: Type.Readonly(Type.String()), - content: Type.Readonly(Type.String()), - prop: Type.Readonly(Type.String()), - tag: Type.Readonly(Type.String()), - value: Type.Readonly(Type.String()), - }), - ), - ), - }), -); +import {RawFakeTimers, RawInitialOptions, RawSnapshotFormat} from './raw-types'; export const SnapshotFormat = Type.Strict(RawSnapshotFormat); export type SnapshotFormat = Static; + +export const InitialOptions = Type.Strict(RawInitialOptions); +export type InitialOptions = Static; + +export const FakeTimers = Type.Strict(RawFakeTimers); +export type FakeTimers = Static; diff --git a/packages/jest-schemas/src/raw-types.ts b/packages/jest-schemas/src/raw-types.ts new file mode 100644 index 000000000000..f340582fc106 --- /dev/null +++ b/packages/jest-schemas/src/raw-types.ts @@ -0,0 +1,354 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* eslint-disable sort-keys */ + +import {type Static, Type} from '@sinclair/typebox'; + +export const RawSnapshotFormat = Type.Partial( + Type.Object({ + callToJSON: Type.Boolean(), + compareKeys: Type.Null(), + escapeRegex: Type.Boolean(), + escapeString: Type.Boolean(), + highlight: Type.Boolean(), + indent: Type.Integer({minimum: 0}), + maxDepth: Type.Integer({minimum: 0}), + maxWidth: Type.Integer({minimum: 0}), + min: Type.Boolean(), + printBasicPrototype: Type.Boolean(), + printFunctionName: Type.Boolean(), + theme: Type.Partial( + Type.Object({ + comment: Type.String(), + content: Type.String(), + prop: Type.String(), + tag: Type.String(), + value: Type.String(), + }), + ), + }), +); + +const RawCoverageProvider = Type.Union([ + Type.Literal('babel'), + Type.Literal('v8'), +]); + +const RawCoverageThresholdValue = Type.Partial( + Type.Object({ + branches: Type.Number({minimum: 0, maximum: 100}), + functions: Type.Number({minimum: 0, maximum: 100}), + lines: Type.Number({minimum: 0, maximum: 100}), + statements: Type.Number({minimum: 0, maximum: 100}), + }), +); + +const RawCoverageThresholdBase = Type.Object( + {global: RawCoverageThresholdValue}, + {additionalProperties: RawCoverageThresholdValue}, +); + +const RawCoverageThreshold = Type.Unsafe<{ + global: Static; + [path: string]: Static; +}>(RawCoverageThresholdBase); + +// TODO: add type test that these are all the colors available in chalk.ForegroundColor +export const ChalkForegroundColors = Type.Union([ + Type.Literal('black'), + Type.Literal('red'), + Type.Literal('green'), + Type.Literal('yellow'), + Type.Literal('blue'), + Type.Literal('magenta'), + Type.Literal('cyan'), + Type.Literal('white'), + Type.Literal('gray'), + Type.Literal('grey'), + Type.Literal('blackBright'), + Type.Literal('redBright'), + Type.Literal('greenBright'), + Type.Literal('yellowBright'), + Type.Literal('blueBright'), + Type.Literal('magentaBright'), + Type.Literal('cyanBright'), + Type.Literal('whiteBright'), +]); + +const RawDisplayName = Type.Object({ + name: Type.String(), + color: ChalkForegroundColors, +}); + +// TODO: verify these are the names of istanbulReport.ReportOptions +export const RawCoverageReporterNames = Type.Union([ + Type.Literal('clover'), + Type.Literal('cobertura'), + Type.Literal('html-spa'), + Type.Literal('html'), + Type.Literal('json'), + Type.Literal('json-summary'), + Type.Literal('lcov'), + Type.Literal('lcovonly'), + Type.Literal('none'), + Type.Literal('teamcity'), + Type.Literal('text'), + Type.Literal('text-lcov'), + Type.Literal('text-summary'), +]); + +const RawCoverageReporters = Type.Array( + Type.Union([ + RawCoverageReporterNames, + Type.Tuple([ + RawCoverageReporterNames, + Type.Record(Type.String(), Type.Unknown()), + ]), + ]), +); + +const RawGlobalFakeTimersConfig = Type.Partial( + Type.Object({ + enableGlobally: Type.Boolean({ + description: + 'Whether fake timers should be enabled globally for all test files.', + default: false, + }), + }), +); + +const RawFakeableAPI = Type.Union([ + Type.Literal('Date'), + Type.Literal('hrtime'), + Type.Literal('nextTick'), + Type.Literal('performance'), + Type.Literal('queueMicrotask'), + Type.Literal('requestAnimationFrame'), + Type.Literal('cancelAnimationFrame'), + Type.Literal('requestIdleCallback'), + Type.Literal('cancelIdleCallback'), + Type.Literal('setImmediate'), + Type.Literal('clearImmediate'), + Type.Literal('setInterval'), + Type.Literal('clearInterval'), + Type.Literal('setTimeout'), + Type.Literal('clearTimeout'), +]); + +const RawFakeTimersConfig = Type.Partial( + Type.Object({ + advanceTimers: Type.Union([Type.Boolean(), Type.Number({minimum: 0})], { + description: + 'If set to `true` all timers will be advanced automatically by 20 milliseconds every 20 milliseconds. A custom ' + + 'time delta may be provided by passing a number.', + default: false, + }), + doNotFake: Type.Array(RawFakeableAPI, { + description: + 'List of names of APIs (e.g. `Date`, `nextTick()`, `setImmediate()`, `setTimeout()`) that should not be faked.' + + '\n\nThe default is `[]`, meaning all APIs are faked.', + default: [], + }), + now: Type.Integer({ + minimum: 0, + description: + 'Sets current system time to be used by fake timers.\n\nThe default is `Date.now()`.', + }), + timerLimit: Type.Number({ + description: + 'The maximum number of recursive timers that will be run when calling `jest.runAllTimers()`.', + default: 100_000, + minimum: 0, + }), + legacyFakeTimers: Type.Literal(false, { + description: + 'Use the old fake timers implementation instead of one backed by `@sinonjs/fake-timers`.', + default: false, + }), + }), +); + +const RawLegacyFakeTimersConfig = Type.Partial( + Type.Object({ + legacyFakeTimers: Type.Literal(true, { + description: + 'Use the old fake timers implementation instead of one backed by `@sinonjs/fake-timers`.', + default: true, + }), + }), +); + +export const RawFakeTimers = Type.Intersect([ + RawGlobalFakeTimersConfig, + Type.Union([RawFakeTimersConfig, RawLegacyFakeTimersConfig]), +]); + +const RawHasteConfig = Type.Partial( + Type.Object({ + computeSha1: Type.Boolean({ + description: 'Whether to hash files using SHA-1.', + }), + defaultPlatform: Type.Union([Type.String(), Type.Null()], { + description: 'The platform to use as the default, e.g. `ios`.', + }), + forceNodeFilesystemAPI: Type.Boolean({ + description: + "Whether to force the use of Node's `fs` API when reading files rather than shelling out to `find`.", + }), + enableSymlinks: Type.Boolean({ + description: + 'Whether to follow symlinks when crawling for files.' + + '\n\tThis options cannot be used in projects which use watchman.' + + '\n\tProjects with `watchman` set to true will error if this option is set to true.', + }), + hasteImplModulePath: Type.String({ + description: 'Path to a custom implementation of Haste.', + }), + platforms: Type.Array(Type.String(), { + description: "All platforms to target, e.g ['ios', 'android'].", + }), + throwOnModuleCollision: Type.Boolean({ + description: 'Whether to throw on error on module collision.', + }), + hasteMapModulePath: Type.String({ + description: 'Custom HasteMap module', + }), + retainAllFiles: Type.Boolean({ + description: + 'Whether to retain all files, allowing e.g. search for tests in `node_modules`.', + }), + }), +); + +export const RawInitialOptions = Type.Partial( + Type.Object({ + automock: Type.Boolean(), + bail: Type.Union([Type.Boolean(), Type.Number()]), + cache: Type.Boolean(), + cacheDirectory: Type.String(), + ci: Type.Boolean(), + clearMocks: Type.Boolean(), + changedFilesWithAncestor: Type.Boolean(), + changedSince: Type.String(), + collectCoverage: Type.Boolean(), + collectCoverageFrom: Type.Array(Type.String()), + coverageDirectory: Type.String(), + coveragePathIgnorePatterns: Type.Array(Type.String()), + coverageProvider: RawCoverageProvider, + coverageReporters: RawCoverageReporters, + coverageThreshold: RawCoverageThreshold, + dependencyExtractor: Type.String(), + detectLeaks: Type.Boolean(), + detectOpenHandles: Type.Boolean(), + displayName: Type.Union([Type.String(), RawDisplayName]), + expand: Type.Boolean(), + extensionsToTreatAsEsm: Type.Array(Type.String()), + fakeTimers: RawFakeTimers, + filter: Type.String(), + findRelatedTests: Type.Boolean(), + forceCoverageMatch: Type.Array(Type.String()), + forceExit: Type.Boolean(), + json: Type.Boolean(), + globals: Type.Record(Type.String(), Type.Unknown()), + globalSetup: Type.Union([Type.String(), Type.Null()]), + globalTeardown: Type.Union([Type.String(), Type.Null()]), + haste: RawHasteConfig, + id: Type.String(), + injectGlobals: Type.Boolean(), + reporters: Type.Array( + Type.Union([ + Type.String(), + Type.Tuple([Type.String(), Type.Record(Type.String(), Type.Unknown())]), + ]), + ), + logHeapUsage: Type.Boolean(), + lastCommit: Type.Boolean(), + listTests: Type.Boolean(), + maxConcurrency: Type.Integer(), + maxWorkers: Type.Union([Type.String(), Type.Integer()]), + moduleDirectories: Type.Array(Type.String()), + moduleFileExtensions: Type.Array(Type.String()), + moduleNameMapper: Type.Record( + Type.String(), + Type.Union([Type.String(), Type.Array(Type.String())]), + ), + modulePathIgnorePatterns: Type.Array(Type.String()), + modulePaths: Type.Array(Type.String()), + noStackTrace: Type.Boolean(), + notify: Type.Boolean(), + notifyMode: Type.String(), + onlyChanged: Type.Boolean(), + onlyFailures: Type.Boolean(), + openHandlesTimeout: Type.Number(), + outputFile: Type.String(), + passWithNoTests: Type.Boolean(), + preset: Type.Union([Type.String(), Type.Null()]), + prettierPath: Type.Union([Type.String(), Type.Null()]), + projects: Type.Array( + Type.Union([ + Type.String(), + // TODO: Make sure to type these correctly + Type.Record(Type.String(), Type.Unknown()), + ]), + ), + randomize: Type.Boolean(), + replname: Type.Union([Type.String(), Type.Null()]), + resetMocks: Type.Boolean(), + resetModules: Type.Boolean(), + resolver: Type.Union([Type.String(), Type.Null()]), + restoreMocks: Type.Boolean(), + rootDir: Type.String(), + roots: Type.Array(Type.String()), + runner: Type.String(), + runTestsByPath: Type.Boolean(), + runtime: Type.String(), + sandboxInjectedGlobals: Type.Array(Type.String()), + setupFiles: Type.Array(Type.String()), + setupFilesAfterEnv: Type.Array(Type.String()), + showSeed: Type.Boolean(), + silent: Type.Boolean(), + skipFilter: Type.Boolean(), + skipNodeResolution: Type.Boolean(), + slowTestThreshold: Type.Number(), + snapshotResolver: Type.String(), + snapshotSerializers: Type.Array(Type.String()), + snapshotFormat: RawSnapshotFormat, + errorOnDeprecated: Type.Boolean(), + testEnvironment: Type.String(), + testEnvironmentOptions: Type.Record(Type.String(), Type.Unknown()), + testFailureExitCode: Type.Union([Type.String(), Type.Integer()]), + testLocationInResults: Type.Boolean(), + testMatch: Type.Array(Type.String()), + testNamePattern: Type.String(), + testPathIgnorePatterns: Type.Array(Type.String()), + testRegex: Type.Union([Type.String(), Type.Array(Type.String())]), + testResultsProcessor: Type.String(), + testRunner: Type.String(), + testSequencer: Type.String(), + testTimeout: Type.Number(), + transform: Type.Record( + Type.String(), + Type.Union([Type.String(), Type.Tuple([Type.String(), Type.Unknown()])]), + ), + transformIgnorePatterns: Type.Array(Type.String()), + watchPathIgnorePatterns: Type.Array(Type.String()), + unmockedModulePathPatterns: Type.Array(Type.String()), + updateSnapshot: Type.Boolean(), + useStderr: Type.Boolean(), + verbose: Type.Boolean(), + waitNextEventLoopTurnForUnhandledRejectionEvents: Type.Boolean(), + watch: Type.Boolean(), + watchAll: Type.Boolean(), + watchman: Type.Boolean(), + watchPlugins: Type.Array( + Type.Union([Type.String(), Type.Tuple([Type.String(), Type.Unknown()])]), + ), + workerIdleMemoryLimit: Type.Union([Type.Number(), Type.String()]), + workerThreads: Type.Boolean(), + }), +); diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 0e64e304fd52..4562c4ec4936 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -8,7 +8,9 @@ import type {ForegroundColor} from 'chalk'; import type {ReportOptions} from 'istanbul-reports'; import type {Arguments} from 'yargs'; -import type {SnapshotFormat} from '@jest/schemas'; +import type {InitialOptions, SnapshotFormat} from '@jest/schemas'; + +export type {InitialOptions} from '@jest/schemas'; type CoverageProvider = 'babel' | 'v8'; @@ -224,117 +226,6 @@ export type InitialProjectOptions = Pick< keyof ProjectConfig >; -export type InitialOptions = Partial<{ - automock: boolean; - bail: boolean | number; - cache: boolean; - cacheDirectory: string; - ci: boolean; - clearMocks: boolean; - changedFilesWithAncestor: boolean; - changedSince: string; - collectCoverage: boolean; - collectCoverageFrom: Array; - coverageDirectory: string; - coveragePathIgnorePatterns: Array; - coverageProvider: CoverageProvider; - coverageReporters: CoverageReporters; - coverageThreshold: CoverageThreshold; - dependencyExtractor: string; - detectLeaks: boolean; - detectOpenHandles: boolean; - displayName: string | DisplayName; - expand: boolean; - extensionsToTreatAsEsm: Array; - fakeTimers: FakeTimers; - filter: string; - findRelatedTests: boolean; - forceCoverageMatch: Array; - forceExit: boolean; - json: boolean; - globals: ConfigGlobals; - globalSetup: string | null | undefined; - globalTeardown: string | null | undefined; - haste: HasteConfig; - id: string; - injectGlobals: boolean; - reporters: Array; - logHeapUsage: boolean; - lastCommit: boolean; - listTests: boolean; - maxConcurrency: number; - maxWorkers: number | string; - moduleDirectories: Array; - moduleFileExtensions: Array; - moduleNameMapper: { - [key: string]: string | Array; - }; - modulePathIgnorePatterns: Array; - modulePaths: Array; - noStackTrace: boolean; - notify: boolean; - notifyMode: string; - onlyChanged: boolean; - onlyFailures: boolean; - openHandlesTimeout: number; - outputFile: string; - passWithNoTests: boolean; - preset: string | null | undefined; - prettierPath: string | null | undefined; - projects: Array; - randomize: boolean; - replname: string | null | undefined; - resetMocks: boolean; - resetModules: boolean; - resolver: string | null | undefined; - restoreMocks: boolean; - rootDir: string; - roots: Array; - runner: string; - runTestsByPath: boolean; - runtime: string; - sandboxInjectedGlobals: Array; - setupFiles: Array; - setupFilesAfterEnv: Array; - showSeed: boolean; - silent: boolean; - skipFilter: boolean; - skipNodeResolution: boolean; - slowTestThreshold: number; - snapshotResolver: string; - snapshotSerializers: Array; - snapshotFormat: SnapshotFormat; - errorOnDeprecated: boolean; - testEnvironment: string; - testEnvironmentOptions: Record; - testFailureExitCode: string | number; - testLocationInResults: boolean; - testMatch: Array; - testNamePattern: string; - testPathIgnorePatterns: Array; - testRegex: string | Array; - testResultsProcessor: string; - testRunner: string; - testSequencer: string; - testTimeout: number; - transform: { - [regex: string]: string | TransformerConfig; - }; - transformIgnorePatterns: Array; - watchPathIgnorePatterns: Array; - unmockedModulePathPatterns: Array; - updateSnapshot: boolean; - useStderr: boolean; - verbose?: boolean; - waitNextEventLoopTurnForUnhandledRejectionEvents: boolean; - watch: boolean; - watchAll: boolean; - watchman: boolean; - watchPlugins: Array]>; - workerIdleMemoryLimit: number | string; - workerThreads: boolean; -}>; - export type SnapshotUpdateState = 'all' | 'new' | 'none'; type NotifyMode =