Skip to content

Commit

Permalink
✨ Add support for string-unit on object/anything arbitrary (#5366)
Browse files Browse the repository at this point in the history
**Description**

<!-- Please provide a short description and potentially linked issues
justifying the need for this PR -->

The `unit` field has been added pretty recently to `string` in order to
offer ways for our users to control in a more granular way what kind of
"character" they would like to be generated for their strings. In order
not to mix "character" with the characters in JavaScript and to allow
any feasible grapheme or combination of code-points to be a valid
"character" we refer to them as "unit".

This PR adds supports for units of strings from object builders.

<!-- * Your PR is fixing a bug or regression? Check for existing issues
related to this bug and link them -->
<!-- * Your PR is adding a new feature? Make sure there is a related
issue or discussion attached to it -->

<!-- You can provide any additional context to help into understanding
what's this PR is attempting to solve: reproduction of a bug, code
snippets... -->

**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)

<!-- More about contributing at
https://github.com/dubzzz/fast-check/blob/main/CONTRIBUTING.md -->

**Advanced**

<!-- How to fill the advanced section is detailed below! -->

- [x] Category: ✨ Introduce new features
- [x] Impacts: New flags for finer control on some arbitraries

<!-- [Category] Please use one of the categories below, it will help us
into better understanding the urgency of the PR -->
<!-- * ✨ Introduce new features -->
<!-- * 📝 Add or update documentation -->
<!-- * ✅ Add or update tests -->
<!-- * 🐛 Fix a bug -->
<!-- * 🏷️ Add or update types -->
<!-- * ⚡️ Improve performance -->
<!-- * _Other(s):_ ... -->

<!-- [Impacts] Please provide a comma separated list of the potential
impacts that might be introduced by this change -->
<!-- * Generated values: Can your change impact any of the existing
generators in terms of generated values, if so which ones? when? -->
<!-- * Shrink values: Can your change impact any of the existing
generators in terms of shrink values, if so which ones? when? -->
<!-- * Performance: Can it require some typings changes on user side?
Please give more details -->
<!-- * Typings: Is there a potential performance impact? In which cases?
-->
  • Loading branch information
dubzzz authored Oct 25, 2024
1 parent 4244de4 commit 50c4f2a
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-apes-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fast-check": minor
---

✨ Add support for string-`unit` on `object`/`anything` arbitrary
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fullUnicodeString } from '../../fullUnicodeString';
import { maxSafeInteger } from '../../maxSafeInteger';
import { oneof } from '../../oneof';
import { string } from '../../string';
import type { StringConstraints } from '../../string';
import { boxedArbitraryBuilder } from '../builders/BoxedArbitraryBuilder';
import type { DepthSize, SizeForArbitrary } from './MaxLengthFromMinLength';

Expand Down Expand Up @@ -107,10 +108,17 @@ export interface ObjectConstraints {
/**
* Replace the arbitrary of strings defaulted for key and values by one able to generate unicode strings with non-ascii characters.
* If you override key and/or values constraint, this flag will not apply to your override.
* @deprecated Prefer using `stringUnit` to customize the kind of strings that will be generated by default.
* @defaultValue false
* @remarks Since 3.19.0
*/
withUnicodeString?: boolean;
/**
* Replace the default unit for strings.
* @defaultValue undefined
* @remarks Since 3.23.0
*/
stringUnit?: StringConstraints['unit'];
}

/** @internal */
Expand All @@ -121,7 +129,7 @@ type ObjectConstraintsOptionalValues = 'depthSize' | 'maxDepth' | 'maxKeys' | 's
* @internal
*/
export type QualifiedObjectConstraints = Required<
Omit<ObjectConstraints, 'withBoxedValues' | 'withUnicodeString' | ObjectConstraintsOptionalValues>
Omit<ObjectConstraints, 'withBoxedValues' | 'withUnicodeString' | 'stringUnit' | ObjectConstraintsOptionalValues>
> &
Pick<ObjectConstraints, ObjectConstraintsOptionalValues>;

Expand Down Expand Up @@ -160,8 +168,8 @@ export function toQualifiedObjectConstraints(settings: ObjectConstraints = {}):
function orDefault<T>(optionalValue: T | undefined, defaultValue: T): T {
return optionalValue !== undefined ? optionalValue : defaultValue;
}
const stringArbitrary = settings.withUnicodeString ? fullUnicodeString : string;
const valueConstraints = { size: settings.size };
const stringArbitrary = 'stringUnit' in settings ? string : settings.withUnicodeString ? fullUnicodeString : string;
const valueConstraints = { size: settings.size, unit: settings.stringUnit };
return {
key: orDefault(settings.key, stringArbitrary(valueConstraints)),
values: boxArbitrariesIfNeeded(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ describe('anyArbitraryBuilder (integration)', () => {
withSparseArray: fc.boolean(),
withTypedArray: fc.boolean(),
withUnicodeString: fc.boolean(),
stringUnit: fc.constantFrom<ObjectConstraints['stringUnit']>(
'grapheme',
'grapheme-composite',
'grapheme-ascii',
'binary',
'binary-ascii',
),
},
{ requiredKeys: [] },
)
Expand Down Expand Up @@ -149,7 +156,7 @@ describe('anyArbitraryBuilder (integration)', () => {
if (!extra.withTypedArray) {
expect(isTypedArray(v)).toBe(false);
}
if (!extra.withUnicodeString) {
if (!extra.withUnicodeString && !('stringUnit' in extra)) {
expect(stringify(v)).toSatisfy(doesNotIncludeAnySurrogateCharacter);
}
// No check for !extra.withObjectString as nothing prevent normal string builders to build such strings
Expand Down
8 changes: 5 additions & 3 deletions website/docs/core-blocks/arbitraries/composites/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ Generate objects (key/values).
**Signatures:**

- `fc.object()`
- `fc.object({key?, depthSize?, maxDepth?, maxKeys?, size?, withBigInt?, withBoxedValues?, withDate?, withMap?, withNullPrototype?, withObjectString?, withSet?, withTypedArray?, withSparseArray?, withUnicodeString?, values?})`
- `fc.object({key?, depthSize?, maxDepth?, maxKeys?, size?, withBigInt?, withBoxedValues?, withDate?, withMap?, withNullPrototype?, withObjectString?, withSet?, withTypedArray?, withSparseArray?, withUnicodeString?, stringUnit?, values?})`

**with:**

Expand All @@ -183,7 +183,8 @@ Generate objects (key/values).
- `withSet?` — default: `false`_enable `Set` - eg.: `new Set([1, 2, 3])`_
- `withTypedArray?` — default: `false`_enable typed arrays for ints, uints and floats - eg.: `Int8Array.from([1, 2, 3])`_
- `withSparseArray?` — default: `false`_enable sparse arrays - eg.: `[1,,,3]`_
- `withUnicodeString?` — default: `false`_change the default for `key` and `values` so that they produce unicode strings with non-ascii characters_
- `withUnicodeString?` — default: `false`_change the default for `key` and `values` so that they produce unicode strings with non-ascii characters — shadowed by `stringUnit`_
- `stringUnit?` — default: `undefined`_customize the set of characters being used by the `string` arbitrary_
- `values?` — default: _booleans, numbers, strings, null and undefined__array of arbitraries producing the root* values - *non-object ones_

**Usages:**
Expand Down Expand Up @@ -276,7 +277,7 @@ Generate any kind of entities.
**Signatures:**

- `fc.anything()`
- `fc.anything({key?, depthSize?, maxDepth?, maxKeys?, size?, withBigInt?, withBoxedValues?, withDate?, withMap?, withNullPrototype?, withObjectString?, withSet?, withTypedArray?, withSparseArray?, withUnicodeString?, values?})`
- `fc.anything({key?, depthSize?, maxDepth?, maxKeys?, size?, withBigInt?, withBoxedValues?, withDate?, withMap?, withNullPrototype?, withObjectString?, withSet?, withTypedArray?, withSparseArray?, withUnicodeString?, stringUnit?, values?})`

**with:**

Expand All @@ -295,6 +296,7 @@ Generate any kind of entities.
- `withTypedArray?` — default: `false`_enable typed arrays for ints, uints and floats - eg.: `Int8Array.from([1, 2, 3])`_
- `withSparseArray?` — default: `false`_enable sparse arrays - eg.: `[1,,,3]`_
- `withUnicodeString?` — default: `false`_change the default for `key` and `values` so that they produce unicode strings with non-ascii characters_
- `stringUnit?` — default: `undefined`_customize the set of characters being used by the `string` arbitrary — shadowed by `stringUnit`_
- `values?` — default: _booleans, numbers, strings, null and undefined__array of arbitraries producing the root* values - *non-object ones_

**Usages:**
Expand Down

0 comments on commit 50c4f2a

Please sign in to comment.