Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pretty-format): allow to opt out from sorting object keys with compareKeys: null #12443

Merged
merged 6 commits into from Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,7 @@
- `[@jest/test-result, @jest/types]` [**BREAKING**] Replace `Bytes` and `Milliseconds` types with `number` ([#13155](https://github.com/facebook/jest/pull/13155))
- `[jest-worker]` Adds `workerIdleMemoryLimit` option which is used as a check for worker memory leaks >= Node 16.11.0 and recycles child workers as required ([#13056](https://github.com/facebook/jest/pull/13056), [#13105](https://github.com/facebook/jest/pull/13105), [#13106](https://github.com/facebook/jest/pull/13106), [#13107](https://github.com/facebook/jest/pull/13107))
- `[pretty-format]` [**BREAKING**] Remove `ConvertAnsi` plugin in favour of `jest-serializer-ansi-escapes` ([#13040](https://github.com/facebook/jest/pull/13040))
- `[pretty-format]` Allow to opt out from sorting object keys with `compareKeys: null` ([#12443](https://github.com/facebook/jest/pull/12443))

### Fixes

Expand Down
1 change: 1 addition & 0 deletions packages/jest-schemas/src/index.ts
Expand Up @@ -10,6 +10,7 @@ import {Static, Type} from '@sinclair/typebox';
const RawSnapshotFormat = Type.Partial(
Type.Object({
callToJSON: Type.Readonly(Type.Boolean()),
compareKeys: Type.Readonly(Type.Null()),
SimenB marked this conversation as resolved.
Show resolved Hide resolved
escapeRegex: Type.Readonly(Type.Boolean()),
escapeString: Type.Readonly(Type.Boolean()),
highlight: Type.Readonly(Type.Boolean()),
Expand Down
58 changes: 29 additions & 29 deletions packages/pretty-format/README.md
Expand Up @@ -66,21 +66,21 @@ console.log(prettyFormat(onClick, options));
```

<!-- prettier-ignore -->
| key | type | default | description |
| :-------------------- | :-------- | :--------- | :------------------------------------------------------ |
| `callToJSON` | `boolean` | `true` | call `toJSON` method (if it exists) on objects |
| `compareKeys` | `function`| `undefined`| compare function used when sorting object keys |
| `escapeRegex` | `boolean` | `false` | escape special characters in regular expressions |
| `escapeString` | `boolean` | `true` | escape special characters in strings |
| `highlight` | `boolean` | `false` | highlight syntax with colors in terminal (some plugins) |
| `indent` | `number` | `2` | spaces in each level of indentation |
| `maxDepth` | `number` | `Infinity` | levels to print in arrays, objects, elements, and so on |
| `maxWidth` | `number` | `Infinity` | number of elements to print in arrays, sets, and so on |
| `min` | `boolean` | `false` | minimize added space: no indentation nor line breaks |
| `plugins` | `array` | `[]` | plugins to serialize application-specific data types |
| `printBasicPrototype` | `boolean` | `false` | print the prototype for plain objects and arrays |
| `printFunctionName` | `boolean` | `true` | include or omit the name of a function |
| `theme` | `object` | | colors to highlight syntax in terminal |
| key | type | default | description |
| :-------------------- | :--------------- | :---------- | :-------------------------------------------------------------------------------------- |
| `callToJSON` | `boolean` | `true` | call `toJSON` method (if it exists) on objects |
| `compareKeys` | `function\|null` | `undefined` | compare function used when sorting object keys, `null` can be used to skip over sorting |
| `escapeRegex` | `boolean` | `false` | escape special characters in regular expressions |
| `escapeString` | `boolean` | `true` | escape special characters in strings |
| `highlight` | `boolean` | `false` | highlight syntax with colors in terminal (some plugins) |
| `indent` | `number` | `2` | spaces in each level of indentation |
| `maxDepth` | `number` | `Infinity` | levels to print in arrays, objects, elements, and so on |
| `maxWidth` | `number` | `Infinity` | number of elements to print in arrays, sets, and so on |
| `min` | `boolean` | `false` | minimize added space: no indentation nor line breaks |
| `plugins` | `array` | `[]` | plugins to serialize application-specific data types |
| `printBasicPrototype` | `boolean` | `false` | print the prototype for plain objects and arrays |
| `printFunctionName` | `boolean` | `true` | include or omit the name of a function |
| `theme` | `object` | | colors to highlight syntax in terminal |

Property values of `theme` are from [ansi-styles colors](https://github.com/chalk/ansi-styles#colors)

Expand Down Expand Up @@ -206,20 +206,20 @@ Write `serialize` to return a string, given the arguments:
### config

<!-- prettier-ignore -->
| key | type | description |
| :------------------ | :-------- | :------------------------------------------------------ |
| `callToJSON` | `boolean` | call `toJSON` method (if it exists) on objects |
| `compareKeys` | `function`| compare function used when sorting object keys |
| `colors` | `Object` | escape codes for colors to highlight syntax |
| `escapeRegex` | `boolean` | escape special characters in regular expressions |
| `escapeString` | `boolean` | escape special characters in strings |
| `indent` | `string` | spaces in each level of indentation |
| `maxDepth` | `number` | levels to print in arrays, objects, elements, and so on |
| `min` | `boolean` | minimize added space: no indentation nor line breaks |
| `plugins` | `array` | plugins to serialize application-specific data types |
| `printFunctionName` | `boolean` | include or omit the name of a function |
| `spacingInner` | `string` | spacing to separate items in a list |
| `spacingOuter` | `string` | spacing to enclose a list of items |
| key | type | description |
| :------------------ | :--------------- | :-------------------------------------------------------------------------------------- |
| `callToJSON` | `boolean` | call `toJSON` method (if it exists) on objects |
| `compareKeys` | `function\|null` | compare function used when sorting object keys, `null` can be used to skip over sorting |
| `colors` | `Object` | escape codes for colors to highlight syntax |
| `escapeRegex` | `boolean` | escape special characters in regular expressions |
| `escapeString` | `boolean` | escape special characters in strings |
| `indent` | `string` | spaces in each level of indentation |
| `maxDepth` | `number` | levels to print in arrays, objects, elements, and so on |
| `min` | `boolean` | minimize added space: no indentation nor line breaks |
| `plugins` | `array` | plugins to serialize application-specific data types |
| `printFunctionName` | `boolean` | include or omit the name of a function |
| `spacingInner` | `string` | spacing to separate items in a list |
| `spacingOuter` | `string` | spacing to enclose a list of items |

Each property of `colors` in `config` corresponds to a property of `theme` in `options`:

Expand Down
10 changes: 9 additions & 1 deletion packages/pretty-format/src/__tests__/prettyFormat.test.ts
Expand Up @@ -334,7 +334,7 @@ describe('prettyFormat()', () => {
expect(prettyFormat(val)).toEqual('Object {\n "a": 2,\n "b": 1,\n}');
});

it('prints an object with keys in their original order', () => {
it('prints an object with keys in their original order with the appropriate comparing function', () => {
// eslint-disable-next-line sort-keys
const val = {b: 1, a: 2};
const compareKeys = () => 0;
Expand All @@ -343,6 +343,14 @@ describe('prettyFormat()', () => {
);
});

it('prints an object with keys in their original order with compareKeys set to null', () => {
// eslint-disable-next-line sort-keys
const val = {b: 1, a: 2};
expect(prettyFormat(val, {compareKeys: null})).toEqual(
'Object {\n "b": 1,\n "a": 2,\n}',
);
});

it('prints an object with keys sorted in reverse order', () => {
const val = {a: 1, b: 2};
const compareKeys = (a: string, b: string) => (a > b ? -1 : 1);
Expand Down
4 changes: 3 additions & 1 deletion packages/pretty-format/src/collections.ts
Expand Up @@ -12,7 +12,9 @@ const getKeysOfEnumerableProperties = (
object: Record<string, unknown>,
compareKeys: CompareKeys,
) => {
const keys: Array<string | symbol> = Object.keys(object).sort(compareKeys);
const rawKeys = Object.keys(object);
const keys: Array<string | symbol> =
compareKeys !== null ? rawKeys.sort(compareKeys) : rawKeys;

if (Object.getOwnPropertySymbols) {
Object.getOwnPropertySymbols(object).forEach(symbol => {
Expand Down
9 changes: 6 additions & 3 deletions packages/pretty-format/src/index.ts
Expand Up @@ -400,7 +400,10 @@ const DEFAULT_THEME_KEYS = Object.keys(DEFAULT_THEME) as Array<
keyof typeof DEFAULT_THEME
>;

export const DEFAULT_OPTIONS: Options = {
// could be replaced by `satisfies` operator in the future: https://github.com/microsoft/TypeScript/issues/47920
const toOptionsSubtype = <T extends Options>(options: T) => options;

export const DEFAULT_OPTIONS = toOptionsSubtype({
callToJSON: true,
compareKeys: undefined,
escapeRegex: false,
Expand All @@ -414,7 +417,7 @@ export const DEFAULT_OPTIONS: Options = {
printBasicPrototype: true,
printFunctionName: true,
theme: DEFAULT_THEME,
};
});

function validateOptions(options: OptionsReceived) {
Object.keys(options).forEach(key => {
Expand Down Expand Up @@ -482,7 +485,7 @@ const getConfig = (options?: OptionsReceived): Config => ({
callToJSON: options?.callToJSON ?? DEFAULT_OPTIONS.callToJSON,
colors: options?.highlight ? getColorsHighlight(options) : getColorsEmpty(),
compareKeys:
typeof options?.compareKeys === 'function'
typeof options?.compareKeys === 'function' || options?.compareKeys === null
? options.compareKeys
: DEFAULT_OPTIONS.compareKeys,
escapeRegex: getEscapeRegex(options),
Expand Down
5 changes: 3 additions & 2 deletions packages/pretty-format/src/types.ts
Expand Up @@ -20,7 +20,7 @@ type Print = (arg0: unknown) => string;

export type Theme = Options['theme'];

export type CompareKeys = ((a: string, b: string) => number) | undefined;
export type CompareKeys = ((a: string, b: string) => number) | null | undefined;

type RequiredOptions = Required<PrettyFormatOptions>;

Expand All @@ -30,7 +30,8 @@ export interface Options
theme: Required<RequiredOptions['theme']>;
}

export interface PrettyFormatOptions extends SnapshotFormat {
export interface PrettyFormatOptions
extends Omit<SnapshotFormat, 'compareKeys'> {
compareKeys?: CompareKeys;
plugins?: Plugins;
}
Expand Down