Skip to content

Commit

Permalink
Add color to displayName in project configuration. (#8025)
Browse files Browse the repository at this point in the history
  • Loading branch information
natealcedo authored and SimenB committed Mar 26, 2019
1 parent 9705b3d commit bd34500
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@
- `[jest-runner]` Support default exports for test environments ([#8163](https://github.com/facebook/jest/pull/8163))
- `[pretty-format]` Support React.Suspense ([#8180](https://github.com/facebook/jest/pull/8180))
- `[jest-snapshot]` Indent inline snapshots ([#8198](https://github.com/facebook/jest/pull/8198))
- `[jest-config]` Support colors in `displayName` configuration ([#8025](https://github.com/facebook/jest/pull/8025))

### Fixes

Expand Down
25 changes: 25 additions & 0 deletions docs/Configuration.md
Expand Up @@ -288,6 +288,31 @@ The `extract` function should return an iterable (`Array`, `Set`, etc.) with the

That module can also contain a `getCacheKey` function to generate a cache key to determine if the logic has changed and any cached artifacts relying on it should be discarded.

### `displayName` [string, object]

default: `undefined`

Allows for a label to be printed along side a test while it is running. This becomes more useful in multiproject repositories where there can be many jest configuration files. This visually tells which project a test belongs to. Here are sample valid values.

```js
module.exports = {
displayName: 'CLIENT',
};
```

or

```js
module.exports = {
displayName: {
name: 'CLIENT',
color: 'blue',
},
};
```

As a secondary option, an object with the properties `name` and `color` can be passed. This allows for a custom configuration of the background color of the displayName. `displayName` defaults to white when its value is a string. Jest uses [chalk](https://github.com/chalk/chalk) to provide the color. As such, all of the valid options for colors supported by chalk are also supported by jest.

### `errorOnDeprecated` [boolean]

Default: `false`
Expand Down
6 changes: 5 additions & 1 deletion packages/jest-config/src/ValidConfig.ts
Expand Up @@ -39,7 +39,11 @@ const initialOptions: Config.InitialOptions = {
},
},
dependencyExtractor: '<rootDir>/dependencyExtractor.js',
displayName: 'project-name',
// @ts-ignore TODO: type this properly
displayName: multipleValidOptions('test-config', {
color: 'blue',
name: 'test-config',
}),
errorOnDeprecated: false,
expand: false,
extraGlobals: [],
Expand Down
Expand Up @@ -17,6 +17,70 @@ exports[`Upgrade help logs a warning when \`scriptPreprocessor\` and/or \`prepro
<yellow></>"
`;
exports[`displayName should throw an error when displayName is is an empty object 1`] = `
"<red><bold><bold>● <bold>Validation Error</>:</>
<red></>
<red> Option \\"<bold>displayName</>\\" must be of type:</>
<red></>
<red> {</>
<red> name: string;</>
<red> color: string;</>
<red> }</>
<red></>
<red></>
<red> <bold>Configuration Documentation:</></>
<red> https://jestjs.io/docs/configuration.html</>
<red></>"
`;
exports[`displayName should throw an error when displayName is missing color 1`] = `
"<red><bold><bold>● <bold>Validation Error</>:</>
<red></>
<red> Option \\"<bold>displayName</>\\" must be of type:</>
<red></>
<red> {</>
<red> name: string;</>
<red> color: string;</>
<red> }</>
<red></>
<red></>
<red> <bold>Configuration Documentation:</></>
<red> https://jestjs.io/docs/configuration.html</>
<red></>"
`;
exports[`displayName should throw an error when displayName is missing name 1`] = `
"<red><bold><bold>● <bold>Validation Error</>:</>
<red></>
<red> Option \\"<bold>displayName</>\\" must be of type:</>
<red></>
<red> {</>
<red> name: string;</>
<red> color: string;</>
<red> }</>
<red></>
<red></>
<red> <bold>Configuration Documentation:</></>
<red> https://jestjs.io/docs/configuration.html</>
<red></>"
`;
exports[`displayName should throw an error when displayName is using invalid values 1`] = `
"<red><bold><bold>● <bold>Validation Error</>:</>
<red></>
<red> Option \\"<bold>displayName</>\\" must be of type:</>
<red></>
<red> {</>
<red> name: string;</>
<red> color: string;</>
<red> }</>
<red></>
<red></>
<red> <bold>Configuration Documentation:</></>
<red> https://jestjs.io/docs/configuration.html</>
<red></>"
`;
exports[`preset throws when module was found but no "jest-preset.js" or "jest-preset.json" files 1`] = `
"<red><bold><bold>● <bold>Validation Error</>:</>
<red></>
Expand Down
23 changes: 23 additions & 0 deletions packages/jest-config/src/__tests__/normalize.test.js
Expand Up @@ -1539,3 +1539,26 @@ describe('Defaults', () => {
expect(console.warn).not.toHaveBeenCalled();
});
});

describe('displayName', () => {
test.each`
displayName | description
${{}} | ${'is an empty object'}
${{name: 'hello'}} | ${'missing color'}
${{color: 'green'}} | ${'missing name'}
${{color: 2, name: []}} | ${'using invalid values'}
`(
'should throw an error when displayName is $description',
({displayName}) => {
expect(() => {
normalize(
{
rootDir: '/root/',
displayName,
},
{},
);
}).toThrowErrorMatchingSnapshot();
},
);
});
46 changes: 45 additions & 1 deletion packages/jest-config/src/normalize.ts
Expand Up @@ -16,6 +16,7 @@ import micromatch from 'micromatch';
import {sync as realpath} from 'realpath-native';
import Resolver from 'jest-resolve';
import {replacePathSepForRegex} from 'jest-regex-util';
import getType from 'jest-get-type';
import validatePattern from './validatePattern';
import getMaxWorkers from './getMaxWorkers';
import {
Expand Down Expand Up @@ -738,6 +739,50 @@ export default function normalize(
}
break;
}
case 'displayName': {
const displayName = oldOptions[key] as Config.DisplayName;
if (typeof displayName === 'string') {
value = displayName;
break;
}
/**
* Ensuring that displayName shape is correct here so that the
* reporters can trust the shape of the data
* TODO: Normalize "displayName" such that given a config option
* {
* "displayName": "Test"
* }
* becomes
* {
* displayName: {
* name: "Test",
* color: "white"
* }
* }
*
* This can't be done now since this will be a breaking change
* for custom reporters
*/
if (getType(displayName) === 'object') {
const errorMessage =
` Option "${chalk.bold('displayName')}" must be of type:\n\n` +
' {\n' +
' name: string;\n' +
' color: string;\n' +
' }\n';
const {name, color} = displayName;
if (
!name ||
!color ||
typeof name !== 'string' ||
typeof color !== 'string'
) {
throw createConfigError(errorMessage);
}
}
value = oldOptions[key];
break;
}
case 'automock':
case 'browser':
case 'cache':
Expand All @@ -749,7 +794,6 @@ export default function normalize(
case 'coverageThreshold':
case 'detectLeaks':
case 'detectOpenHandles':
case 'displayName':
case 'errorOnDeprecated':
case 'expand':
case 'extraGlobals':
Expand Down
@@ -1,5 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`printDisplayName should correctly print the displayName when color and name are valid values 1`] = `"</><inverse><green> hello </></></>"`;
exports[`printDisplayName should default displayName color to white when color is not a valid value 1`] = `"</><inverse><white> hello </></></>"`;
exports[`printDisplayName should default displayName color to white when displayName is a string 1`] = `"</><inverse><white> hello </></></>"`;
exports[`trimAndFormatPath() does not trim anything 1`] = `"<dim>1234567890/1234567890/</><bold>1234.js</>"`;
exports[`trimAndFormatPath() split at the path.sep index 1`] = `"<dim>.../</><bold>1234.js</>"`;
Expand Down
34 changes: 33 additions & 1 deletion packages/jest-reporters/src/__tests__/utils.test.js
Expand Up @@ -8,7 +8,7 @@
import path from 'path';
import chalk from 'chalk';
import stripAnsi from 'strip-ansi';
import {trimAndFormatPath, wrapAnsiString} from '../utils';
import {trimAndFormatPath, wrapAnsiString, printDisplayName} from '../utils';

describe('wrapAnsiString()', () => {
it('wraps a long string containing ansi chars', () => {
Expand Down Expand Up @@ -111,3 +111,35 @@ describe('trimAndFormatPath()', () => {
expect(stripAnsi(result).length).toBe(columns - pad);
});
});

describe('printDisplayName', () => {
it('should default displayName color to white when displayName is a string', () => {
const config = {
displayName: 'hello',
};

expect(printDisplayName(config)).toMatchSnapshot();
});

it('should default displayName color to white when color is not a valid value', () => {
const config = {
displayName: {
color: 'rubbish',
name: 'hello',
},
};

expect(printDisplayName(config)).toMatchSnapshot();
});

it('should correctly print the displayName when color and name are valid values', () => {
const config = {
displayName: {
color: 'green',
name: 'hello',
},
};

expect(printDisplayName(config)).toMatchSnapshot();
});
});
16 changes: 11 additions & 5 deletions packages/jest-reporters/src/utils.ts
Expand Up @@ -17,14 +17,20 @@ const PROGRESS_BAR_WIDTH = 40;

export const printDisplayName = (config: Config.ProjectConfig) => {
const {displayName} = config;
const white = chalk.reset.inverse.white;
if (!displayName) {
return '';
}

if (displayName) {
return chalk.supportsColor
? chalk.reset.inverse.white(` ${displayName} `)
: displayName;
if (typeof displayName === 'string') {
return chalk.supportsColor ? white(` ${displayName} `) : displayName;
}

return '';
const {name, color} = displayName;
const chosenColor = chalk.reset.inverse[color]
? chalk.reset.inverse[color]
: white;
return chalk.supportsColor ? chosenColor(` ${name} `) : name;
};

export const trimAndFormatPath = (
Expand Down
3 changes: 2 additions & 1 deletion packages/jest-test-result/src/types.ts
Expand Up @@ -8,6 +8,7 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import {CoverageMap, CoverageMapData} from 'istanbul-lib-coverage';
import {ConsoleBuffer} from '@jest/console';
import {Config} from '@jest/types';

export type SerializableError = {
code?: unknown;
Expand Down Expand Up @@ -102,7 +103,7 @@ export type Suite = {
export type TestResult = {
console?: ConsoleBuffer | null;
coverage?: CoverageMapData;
displayName?: string | null;
displayName?: Config.DisplayName;
failureMessage?: string | null;
leaks: boolean;
memoryUsage?: Bytes;
Expand Down
52 changes: 50 additions & 2 deletions packages/jest-types/src/Config.ts
Expand Up @@ -104,6 +104,13 @@ export type DefaultOptions = {
watchman: boolean;
};

export type DisplayName =
| string
| {
name: string;
color: DisplayNameColor;
};

export type InitialOptions = {
automock?: boolean;
bail?: boolean | number;
Expand All @@ -129,7 +136,7 @@ export type InitialOptions = {
dependencyExtractor?: string;
detectLeaks?: boolean;
detectOpenHandles?: boolean;
displayName?: string;
displayName?: DisplayName;
expand?: boolean;
extraGlobals?: Array<string>;
filter?: Path;
Expand Down Expand Up @@ -223,6 +230,47 @@ type NotifyMode =
| 'success-change'
| 'failure-change';

/**
* Hard coding this until
* https://github.com/chalk/chalk/pull/336
* gets merged
*/
type DisplayNameColor =
| 'black'
| 'red'
| 'green'
| 'yellow'
| 'blue'
| 'magenta'
| 'cyan'
| 'white'
| 'gray'
| 'grey'
| 'blackBright'
| 'redBright'
| 'greenBright'
| 'yellowBright'
| 'blueBright'
| 'magentaBright'
| 'cyanBright'
| 'whiteBright'
| 'bgBlack'
| 'bgRed'
| 'bgGreen'
| 'bgYellow'
| 'bgBlue'
| 'bgMagenta'
| 'bgCyan'
| 'bgWhite'
| 'bgBlackBright'
| 'bgRedBright'
| 'bgGreenBright'
| 'bgYellowBright'
| 'bgBlueBright'
| 'bgMagentaBright'
| 'bgCyanBright'
| 'bgWhiteBright';

type CoverageThreshold = {
[path: string]: {
[key: string]: number;
Expand Down Expand Up @@ -318,7 +366,7 @@ export type ProjectConfig = {
dependencyExtractor?: string;
detectLeaks: boolean;
detectOpenHandles: boolean;
displayName: string | null | undefined;
displayName?: DisplayName;
errorOnDeprecated: boolean;
extraGlobals: Array<keyof NodeJS.Global>;
filter: Path | null | undefined;
Expand Down

0 comments on commit bd34500

Please sign in to comment.