From 49abe92023587b70d543c671978a186f4dcc03f5 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Wed, 22 May 2024 21:23:17 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20option=20to=20generate=20unic?= =?UTF-8?q?ode=20values=20in=20`json`=20(#5011)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Description** Same idea as for #5010 but extended to `json` and `jsonValue`. With this new flag, we also deprecate unicode versions as they don't need to live on their own anymore. As such they can just be replaced by the default version. The flag is by default to true but will be moved to false in v4. **Checklist** โ€” _Don't delete this checklist and make sure you do the following before opening the PR_ - [x] The name of my PR follows [gitmoji](https://gitmoji.dev/) specification - [x] My PR references one of several related issues (if any) - [x] New features or breaking changes must come with an associated Issue or Discussion - [x] My PR does not add any new dependency without an associated Issue or Discussion - [x] My PR includes bumps details, please run `yarn bump` and flag the impacts properly - [x] My PR adds relevant tests and they would have failed without my PR (when applicable) **Advanced** - [x] Category: โœจ Introduce new features - [x] Impacts: Deprecate arbitraries --- .yarn/versions/dd3b347d.yml | 8 +++++ .../helpers/JsonConstraintsBuilder.ts | 32 +++++++++++++++++-- .../fast-check/src/arbitrary/jsonValue.ts | 5 ++- .../fast-check/src/arbitrary/unicodeJson.ts | 7 ++-- .../src/arbitrary/unicodeJsonValue.ts | 7 ++-- packages/fast-check/src/fast-check-default.ts | 2 ++ .../test/unit/arbitrary/jsonValue.spec.ts | 1 + .../unit/arbitrary/unicodeJsonValue.spec.ts | 4 +-- .../core-blocks/arbitraries/fake-data/file.md | 32 +++++++++++++++---- 9 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 .yarn/versions/dd3b347d.yml diff --git a/.yarn/versions/dd3b347d.yml b/.yarn/versions/dd3b347d.yml new file mode 100644 index 00000000000..a9650bcd747 --- /dev/null +++ b/.yarn/versions/dd3b347d.yml @@ -0,0 +1,8 @@ +releases: + fast-check: minor + +declined: + - "@fast-check/ava" + - "@fast-check/jest" + - "@fast-check/vitest" + - "@fast-check/worker" diff --git a/packages/fast-check/src/arbitrary/_internals/helpers/JsonConstraintsBuilder.ts b/packages/fast-check/src/arbitrary/_internals/helpers/JsonConstraintsBuilder.ts index 6adb263366a..3d1cf0584c6 100644 --- a/packages/fast-check/src/arbitrary/_internals/helpers/JsonConstraintsBuilder.ts +++ b/packages/fast-check/src/arbitrary/_internals/helpers/JsonConstraintsBuilder.ts @@ -8,9 +8,7 @@ import type { ObjectConstraints } from './QualifiedObjectConstraints'; /** * Shared constraints for: * - {@link json}, - * - {@link unicodeJson}, * - {@link jsonValue}, - * - {@link unicodeJsonValue} * * @remarks Since 2.5.0 * @public @@ -29,6 +27,36 @@ export interface JsonSharedConstraints { * @remarks Since 2.5.0 */ maxDepth?: number; + /** + * Only generate instances having keys and values made of ascii strings (when true) + * @defaultValue true + * @remarks Since 3.19.0 + */ + noUnicodeString?: boolean; +} + +/** + * Shared constraints for: + * - {@link unicodeJson}, + * - {@link unicodeJsonValue} + * + * @remarks Since 3.19.0 + * @public + */ +export interface UnicodeJsonSharedConstraints { + /** + * Limit the depth of the object by increasing the probability to generate simple values (defined via values) + * as we go deeper in the object. + * + * @remarks Since 2.20.0 + */ + depthSize?: DepthSize; + /** + * Maximal depth allowed + * @defaultValue Number.POSITIVE_INFINITY โ€” _defaulting seen as "max non specified" when `defaultSizeToMaxWhenMaxSpecified=true`_ + * @remarks Since 2.5.0 + */ + maxDepth?: number; } /** diff --git a/packages/fast-check/src/arbitrary/jsonValue.ts b/packages/fast-check/src/arbitrary/jsonValue.ts index de66fa9e345..f9558b6dd2b 100644 --- a/packages/fast-check/src/arbitrary/jsonValue.ts +++ b/packages/fast-check/src/arbitrary/jsonValue.ts @@ -3,6 +3,7 @@ import { string } from './string'; import type { JsonSharedConstraints, JsonValue } from './_internals/helpers/JsonConstraintsBuilder'; import { jsonConstraintsBuilder } from './_internals/helpers/JsonConstraintsBuilder'; import { anything } from './anything'; +import { fullUnicodeString } from './fullUnicodeString'; export type { JsonSharedConstraints, JsonValue }; @@ -20,5 +21,7 @@ export type { JsonSharedConstraints, JsonValue }; * @public */ export function jsonValue(constraints: JsonSharedConstraints = {}): Arbitrary { - return anything(jsonConstraintsBuilder(string(), constraints)) as Arbitrary; + const noUnicodeString = constraints.noUnicodeString === undefined || constraints.noUnicodeString === true; + const stringArbitrary = noUnicodeString ? string() : fullUnicodeString(); + return anything(jsonConstraintsBuilder(stringArbitrary, constraints)) as Arbitrary; } diff --git a/packages/fast-check/src/arbitrary/unicodeJson.ts b/packages/fast-check/src/arbitrary/unicodeJson.ts index a45fbbe0aea..9b6ff88ec01 100644 --- a/packages/fast-check/src/arbitrary/unicodeJson.ts +++ b/packages/fast-check/src/arbitrary/unicodeJson.ts @@ -1,8 +1,8 @@ import type { Arbitrary } from '../check/arbitrary/definition/Arbitrary'; import { unicodeJsonValue } from './unicodeJsonValue'; -import type { JsonSharedConstraints } from './_internals/helpers/JsonConstraintsBuilder'; +import type { UnicodeJsonSharedConstraints } from './_internals/helpers/JsonConstraintsBuilder'; -export type { JsonSharedConstraints }; +export type { UnicodeJsonSharedConstraints }; /** * For any JSON strings with unicode support @@ -11,10 +11,11 @@ export type { JsonSharedConstraints }; * * @param constraints - Constraints to be applied onto the generated instance (since 2.5.0) * + * @deprecated Prefer using {@link json} with `noUnicodeString: false`, it will generate even more unicode strings: includings some having characters outside of BMP plan * @remarks Since 0.0.7 * @public */ -export function unicodeJson(constraints: JsonSharedConstraints = {}): Arbitrary { +export function unicodeJson(constraints: UnicodeJsonSharedConstraints = {}): Arbitrary { const arb = unicodeJsonValue(constraints); return arb.map(JSON.stringify); } diff --git a/packages/fast-check/src/arbitrary/unicodeJsonValue.ts b/packages/fast-check/src/arbitrary/unicodeJsonValue.ts index 585bbe20d1f..7d08e33cb7b 100644 --- a/packages/fast-check/src/arbitrary/unicodeJsonValue.ts +++ b/packages/fast-check/src/arbitrary/unicodeJsonValue.ts @@ -1,10 +1,10 @@ import type { Arbitrary } from '../check/arbitrary/definition/Arbitrary'; import { unicodeString } from './unicodeString'; -import type { JsonSharedConstraints, JsonValue } from './_internals/helpers/JsonConstraintsBuilder'; +import type { UnicodeJsonSharedConstraints, JsonValue } from './_internals/helpers/JsonConstraintsBuilder'; import { jsonConstraintsBuilder } from './_internals/helpers/JsonConstraintsBuilder'; import { anything } from './anything'; -export type { JsonSharedConstraints, JsonValue }; +export type { UnicodeJsonSharedConstraints, JsonValue }; /** * For any JSON compliant values with unicode support @@ -16,9 +16,10 @@ export type { JsonSharedConstraints, JsonValue }; * * @param constraints - Constraints to be applied onto the generated instance * + * @deprecated Prefer using {@link jsonValue} with `noUnicodeString: false`, it will generate even more unicode strings: includings some having characters outside of BMP plan * @remarks Since 2.20.0 * @public */ -export function unicodeJsonValue(constraints: JsonSharedConstraints = {}): Arbitrary { +export function unicodeJsonValue(constraints: UnicodeJsonSharedConstraints = {}): Arbitrary { return anything(jsonConstraintsBuilder(unicodeString(), constraints)) as Arbitrary; } diff --git a/packages/fast-check/src/fast-check-default.ts b/packages/fast-check/src/fast-check-default.ts index c621212caee..7a2ed2fa3a9 100644 --- a/packages/fast-check/src/fast-check-default.ts +++ b/packages/fast-check/src/fast-check-default.ts @@ -88,6 +88,7 @@ import { mixedCase } from './arbitrary/mixedCase'; import type { ObjectConstraints } from './arbitrary/object'; import { object } from './arbitrary/object'; import type { JsonSharedConstraints } from './arbitrary/json'; +import type { UnicodeJsonSharedConstraints } from './arbitrary/unicodeJson'; import { json } from './arbitrary/json'; import { anything } from './arbitrary/anything'; import { unicodeJsonValue } from './arbitrary/unicodeJsonValue'; @@ -277,6 +278,7 @@ export type { IntArrayConstraints, IntegerConstraints, JsonSharedConstraints, + UnicodeJsonSharedConstraints, LoremConstraints, MixedCaseConstraints, NatConstraints, diff --git a/packages/fast-check/test/unit/arbitrary/jsonValue.spec.ts b/packages/fast-check/test/unit/arbitrary/jsonValue.spec.ts index d6b3ae6b1c2..dce5ba0b0fa 100644 --- a/packages/fast-check/test/unit/arbitrary/jsonValue.spec.ts +++ b/packages/fast-check/test/unit/arbitrary/jsonValue.spec.ts @@ -21,6 +21,7 @@ describe('jsonValue (integration)', () => { { depthSize: fc.oneof(fc.double({ min: 0.1, noNaN: true }), sizeArb), maxDepth: fc.nat({ max: 5 }), + noUnicodeString: fc.boolean(), }, { requiredKeys: [] }, ) diff --git a/packages/fast-check/test/unit/arbitrary/unicodeJsonValue.spec.ts b/packages/fast-check/test/unit/arbitrary/unicodeJsonValue.spec.ts index 1ebbcc6d00b..2b7c9e7920c 100644 --- a/packages/fast-check/test/unit/arbitrary/unicodeJsonValue.spec.ts +++ b/packages/fast-check/test/unit/arbitrary/unicodeJsonValue.spec.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import fc from 'fast-check'; -import type { JsonSharedConstraints } from '../../../src/arbitrary/unicodeJsonValue'; +import type { UnicodeJsonSharedConstraints } from '../../../src/arbitrary/unicodeJsonValue'; import { unicodeJsonValue } from '../../../src/arbitrary/unicodeJsonValue'; import { assertProduceCorrectValues, @@ -14,7 +14,7 @@ import { isObjectWithNumericKeys } from './__test-helpers__/ObjectWithNumericKey import { sizeArb } from './__test-helpers__/SizeHelpers'; describe('unicodeJsonValue (integration)', () => { - type Extra = JsonSharedConstraints | undefined; + type Extra = UnicodeJsonSharedConstraints | undefined; const extraParameters: fc.Arbitrary = fc.option( fc .record( diff --git a/website/docs/core-blocks/arbitraries/fake-data/file.md b/website/docs/core-blocks/arbitraries/fake-data/file.md index d2301780959..0824a52021b 100644 --- a/website/docs/core-blocks/arbitraries/fake-data/file.md +++ b/website/docs/core-blocks/arbitraries/fake-data/file.md @@ -57,12 +57,13 @@ All the string values (from keys to values) are generated using `fc.string()`. **Signatures:** - `fc.json()` -- `fc.json({depthSize?, maxDepth?})` +- `fc.json({depthSize?, maxDepth?, noUnicodeString?})` **with:** - `depthSize?` โ€” default: `undefined` [more](/docs/configuration/larger-entries-by-default/#depth-size-explained) โ€” _how much we allow our recursive structures to be deep?_ - `maxDepth?` โ€” default: `Number.POSITIVE_INFINITY` โ€” _maximal depth of generated objects_ +- `noUnicodeString?` โ€” default: `true` โ€” _toggle on/off the generation of strings used either as keys or values of the instance and including non-ascii characters_ **Usages:** @@ -76,6 +77,15 @@ fc.json(); // โ€ข "[null,-1.5485504457576672e+192,null,{},-1.417727947024272e-287,null,null,null]" // โ€ข โ€ฆ +fc.json({ noUnicodeString: false }); +// Examples of generated values: +// โ€ข "{}" +// โ€ข "[{\"๓œณ๒ฟณ๒Ž—ฏ๔ˆค˜๑–‡…\":null,\"๒ˆทฉ๐ซจน๔ฅƒ๒คตช๑ฅ‰จ๑ขฆœ๊ฃ™\":[null,\"๒‰ฒจ๒›จฐ๐œฅฒ๑† ‰๒€ฟ๑‡†พ๔€คฏ๑พฑ„\"],\"__def\":\"๑ฅ›ก\",\"๐ด‚๒ฐทณ๐ฉชŽ๑กจซ\":true,\"2๔ฟบ\":\"\",\"๔ฅš๓›‚พ๐“ด’\":false},[3.5931489320423776e+139,[true,\"๓Œ˜…๑ชœ†๓—›ƒ๓Žฉป๐™น–๒ž š๒บณต๑จถ–\",false,{\"๔Š†ช๒“”๒˜ฅฌ๐”งฅ๓ด“Œ๒ฉ†\":null,\"\":\"๓Œฝก๐—€ฅ๓šจฟ๓Šญน๒ŽปŽ๑€“œ๒ง…˜๒œฃ๓จ“š๑ฏ„ˆ\",\"๐ฝธง๒ฝ‚ต๑ฏ†Ž๑ทกฐ๐‘ดต๑žฑ’\":[true,\"๒€ฒ‘๒ฟ’ฆ\",true,\"๔Š”น๏…ฆ๑’šก๐ฃ‰Ÿ๐ณกธ๑ฎ‹ณ๓ณถ\",false,-4.119935921393037e+259,null,-8.9364525362984475e+248]},\"๒ธ€ฟ๓ณฟด๑ฅ˜ก๒ช พ๒ƒฐง๒ฃ–\",\"๓ฑ‡๒นข–๐ฌ‚๑ คซ๓ด• ๒’ง\"]],[false,-6.0502670401327095e+112,1.1096547717393745e-177,null,null,null,false,[null,\"๓˜ณ‘ใจฆ๐ญฆ„๑ฑน‚๐šƒœ๒……ช๓ชƒ—๒Ÿ““๓Š•๒ —บ\",1.288654068889961e-213,null,1.6406299790913147e-206]]]" +// โ€ข "\"๒ค‡๐ซฏ๔ฟฌ$๑ž‹ฐ%๒Ÿฑ‰๒ณŸ”๓ฝพ\"" +// โ€ข "[null,[{\"ๅฃ\":true,\"๐ฎ€ณ๑ ž๓—ˆŒ\":\"่€•๒ฐถคไฐ…๐ธฌฃ\",\"๎ ‚\":null,\"๐˜ฅฃ๑ฏ™๐–นŸ๓—จŸ๐ฏตฝ๒ฟˆค๔Š‡ฆ๓ฃŒ™๓ธซจ๓ธ…”\":true,\"๓’พ ๒ˆ„•๎’๓ฌ€˜๐šจถ๓‹ค๑ƒžœ๐ฎขŒ๔‡ถธ๑ญ˜\":null,\"๑ฎนท๑€šค๓ท…“๓ฐชผ๔€†Œ๐ฅฐ‚๐ซƒฉ๐ง†”๐นทน๓ญผœ\":true,\"๓›ถ‹๒ฃ„š๑ผ‡๒กญ‡๓นƒค๓ขฌ๐žฒข\":-4.059178361848322e-91,\"๒‰€๒ พซ๐“ฆž๐‘ฌž๓ตซฝ๒ฅท๑นบ๔Œ—ˆ\":true},null],[3.6448982683876056e+131]]" +// โ€ข "[null,false]" +// โ€ข โ€ฆ + fc.json({ maxDepth: 0 }); // Examples of generated values: "null", "\"T\"", "-1.6050118268310372e-215", "true", "\"Ep\""โ€ฆ @@ -109,12 +119,13 @@ As `JSON.parse` preserves `-0`, `jsonValue` can also have `-0` as a value. **Signatures:** - `fc.jsonValue()` -- `fc.jsonValue({depthSize?, maxDepth?})` +- `fc.jsonValue({depthSize?, maxDepth?, noUnicodeString?})` **with:** - `depthSize?` โ€” default: `undefined` [more](/docs/configuration/larger-entries-by-default/#depth-size-explained) โ€” _how much we allow our recursive structures to be deep?_ - `maxDepth?` โ€” default: `Number.POSITIVE_INFINITY` โ€” _maximal depth for generated objects (Map and Set included into objects)_ +- `noUnicodeString?` โ€” default: `true` โ€” _toggle on/off the generation of strings used either as keys or values of the instance and including non-ascii characters_ **Usages:** @@ -128,6 +139,15 @@ fc.jsonValue(); // โ€ข [null,true,true,"`l+$I","kSros",null] // โ€ข โ€ฆ +fc.jsonValue({ noUnicodeString: false }); +// Examples of generated values: +// โ€ข ["๒ดพผ๓นคท๐ก…ค๑คฑ“๒›—ก"] +// โ€ข {"๔Žต”๒ฒผ๒€Žˆ๐ธ”๔ƒŒ…๔Šฟ›๑น™ฆ":[false],"๒จŠ—๐คฎˆ๐กˆก๓ต‘‘๑—€๒—”๐™””๐ธต๑‡˜ผ":556603.8398649627,"๔ฟฝ+๒ธ‘ฝ":{"๑€ž๓ด•ƒ๓™‰…๑‚Š ๐ด›๓ป•€ใข‹๑ฆ”˜":true,"๑Šˆ’๔‹šญ๓ทช™๐ซช€๓Œงถ๑‰’๐ฑฃ†":null,"":5.539268054957889e+74,"๒ฆนท":"๑œโŒณ๒ปœ๑‡“ท๑–‹ฆ","๑ฅธฑ๑ฅŠ”๒ฆน—":4.847354156832373e-25,"๑œ‚‘๒น๓žฆ":"๐ปฌซ๐ณคฒ๓ตนƒ๒•ง๑ƒต","๐“งŽ๐–ฐฆ":false,"๓›ปณ๒œš๑ƒ›ท๑Œ›‘๐œ€๓ž…ค๑ช‰บ":false}} +// โ€ข [null,["๓ฟฆผ๑Œ…ก๓ฏปพ๐€น๒ฒ“‹๑†บ๑ฟ๓ƒขฐ",-2.4628931920258706e-282,null,false,2.681696006505804e-238,"๒ขฐฎ"]] +// โ€ข "๒ฉ" +// โ€ข [] +// โ€ข โ€ฆ + fc.jsonValue({ maxDepth: 0 }); // Examples of generated values: true, null, false, "prototype", "L4)5M"โ€ฆ @@ -210,8 +230,8 @@ All the string values (from keys to values) are generated using `fc.unicodeStrin **Signatures:** -- `fc.unicodeJson()` -- `fc.unicodeJson({depthSize?, maxDepth?})` +- `fc.unicodeJson()` โ€” _deprecated since v3.19.0 ([#5011](https://github.com/dubzzz/fast-check/pull/5011))_ +- `fc.unicodeJson({depthSize?, maxDepth?})` โ€” _deprecated since v3.19.0 ([#5011](https://github.com/dubzzz/fast-check/pull/5011))_ **with:** @@ -268,8 +288,8 @@ As `JSON.parse` preserves `-0`, `unicodeJsonValue` can also have `-0` as a value **Signatures:** -- `fc.unicodeJsonValue()` -- `fc.unicodeJsonValue({depthSize?, maxDepth?})` +- `fc.unicodeJsonValue()` โ€” _deprecated since v3.19.0 ([#5011](https://github.com/dubzzz/fast-check/pull/5011))_ +- `fc.unicodeJsonValue({depthSize?, maxDepth?})` โ€” _deprecated since v3.19.0 ([#5011](https://github.com/dubzzz/fast-check/pull/5011))_ **with:**