From 7d304d32ae6a9edda1428162ff6a391bb780999a Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 1 Jul 2026 16:27:08 +0100 Subject: [PATCH 1/5] Expand domain command params --- .../docs/040-test-data/domain/020-airline.md | 54 +++- docs-src/docs/040-test-data/domain/140-git.md | 68 ++++- .../docs/040-test-data/domain/160-image.md | 129 +++++++++- .../docs/040-test-data/domain/170-internet.md | 85 +++++- .../docs/040-test-data/domain/190-location.md | 15 +- .../docs/040-test-data/domain/200-lorem.md | 18 -- .../docs/040-test-data/domain/220-number.md | 26 +- .../docs/040-test-data/domain/230-person.md | 54 +++- .../docs/040-test-data/domain/270-system.md | 45 +++- .../docs/040-test-data/domain/290-word.md | 81 ------ docs/domain-faker-param-comparison.md | 97 +++++++ ...chema-interaction-scenario-builder.test.js | 12 +- packages/core/js/domain/domain-keywords.js | 1 + .../flight-number-keyword-definition.js | 35 ++- .../record-locator-keyword-definition.js | 30 ++- .../git/commit-date-keyword-definition.js | 18 +- .../git/commit-entry-keyword-definition.js | 69 ++++- .../git/commit-sha-keyword-definition.js | 18 +- .../image/data-uri-keyword-definition.js | 64 ++++- .../person-portrait-keyword-definition.js | 35 ++- .../url-picsum-photos-keyword-definition.js | 59 ++++- .../display-name-keyword-definition.js | 35 ++- .../example-email-keyword-definition.js | 47 +++- .../http-status-code-keyword-definition.js | 19 +- .../location/zip-code-keyword-definition.js | 26 +- .../domain/lorem/word-keyword-definition.js | 22 -- .../number/big-int-keyword-definition.js | 53 +++- .../person/full-name-keyword-definition.js | 47 +++- .../person/sex-type-keyword-definition.js | 20 +- .../system/file-name-keyword-definition.js | 18 +- .../network-interface-keyword-definition.js | 35 ++- .../word/adjective-keyword-definition.js | 11 - .../domain/word/adverb-keyword-definition.js | 11 - .../word/conjunction-keyword-definition.js | 11 - .../word/interjection-keyword-definition.js | 11 - .../domain/word/noun-keyword-definition.js | 11 - .../word/preposition-keyword-definition.js | 11 - .../domain/word/sample-keyword-definition.js | 11 - .../domain/word/verb-keyword-definition.js | 11 - .../domain/word/words-keyword-definition.js | 11 - .../command-help-examples.test-support.js | 9 + .../command-help-examples.test.js | 7 +- .../domain-faker-param-comparison.test.js | 37 +++ .../domain-keyword-params-usage.test.js | 15 +- .../unit/domain/domain-keyword-parser.test.js | 6 +- ...n-param-invocation-coverage.test-helper.js | 11 +- .../unit/domain/domainKeywords.test.js | 3 +- scripts/compare-domain-faker-params.mjs | 241 ++++++++++++++++++ 48 files changed, 1453 insertions(+), 310 deletions(-) create mode 100644 docs/domain-faker-param-comparison.md create mode 100644 packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js create mode 100644 scripts/compare-domain-faker-params.mjs diff --git a/docs-src/docs/040-test-data/domain/020-airline.md b/docs-src/docs/040-test-data/domain/020-airline.md index 37b2e074..666dd99e 100644 --- a/docs-src/docs/040-test-data/domain/020-airline.md +++ b/docs-src/docs/040-test-data/domain/020-airline.md @@ -40,18 +40,45 @@ Returns a random flight number. Flight numbers are always 1 to 4 digits long and - Canonical: `awd.domain.airline.flightNumber` - Faker docs: [https://fakerjs.dev/api/airline](https://fakerjs.dev/api/airline) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `length` | `number` | no | Desired flight-number length. | +| `addLeadingZeros` | `boolean` | no | Whether shorter flight numbers should be padded with leading zeros. | Examples: Shows the default airline.flightNumber call. ```txt -airline.flightNumber +airline.flightNumber() ``` Returns: `70` +Shows airline.flightNumber using length and leading-zero options. + +```txt +airline.flightNumber(length=4, addLeadingZeros=true) +``` + +Returns: `4703` + +Shows airline.flightNumber using a fixed length. + +```txt +airline.flightNumber(length=4) +``` + +Returns: `4703` + +Shows airline.flightNumber padding shorter values with leading zeros. + +```txt +airline.flightNumber(addLeadingZeros=true) +``` + +Returns: `0070` + ### `airline.iataCode` Generate an airline IATA code. @@ -97,18 +124,37 @@ Generates a random record locator. Record locators are 6-character alphanumeric - Canonical: `awd.domain.airline.recordLocator` - Faker docs: [https://fakerjs.dev/api/airline](https://fakerjs.dev/api/airline) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `allowNumerics` | `boolean` | no | Whether numeric characters can be included. | +| `allowVisuallySimilarCharacters` | `boolean` | no | Whether visually similar characters such as I, 1, O, and 0 can be included. | Examples: Shows the default airline.recordLocator call. ```txt -airline.recordLocator +airline.recordLocator() ``` Returns: `KTAGDC` +Shows airline.recordLocator allowing numeric characters. + +```txt +airline.recordLocator(allowNumerics=true) +``` + +Returns: `ER2B64` + +Shows airline.recordLocator allowing visually similar characters. + +```txt +airline.recordLocator(allowVisuallySimilarCharacters=true) +``` + +Returns: `KSAHDC` + ### `airline.seat` Generates a random seat. diff --git a/docs-src/docs/040-test-data/domain/140-git.md b/docs-src/docs/040-test-data/domain/140-git.md index d7684f9f..3c66aead 100644 --- a/docs-src/docs/040-test-data/domain/140-git.md +++ b/docs-src/docs/040-test-data/domain/140-git.md @@ -40,18 +40,28 @@ Generates a date string for a git commit using the same format as `git log`. - Canonical: `awd.domain.git.commitDate` - Faker docs: [https://fakerjs.dev/api/git](https://fakerjs.dev/api/git) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `refDate` | `string\|date\|number` | no | The date to use as reference point for generated commit dates. | Examples: Shows the default git.commitDate call. ```txt -git.commitDate +git.commitDate() ``` Returns: `Thu Jun 18 01:55:50 2026 +0600` +Shows git.commitDate using a reference date. + +```txt +git.commitDate(refDate="2024-01-01T00:00:00.000Z") +``` + +Returns: `Sun Dec 31 10:00:30 2023 +0600` + ### `git.commitEntry` Generates a random commit entry as printed by `git log`. @@ -59,14 +69,50 @@ Generates a random commit entry as printed by `git log`. - Canonical: `awd.domain.git.commitEntry` - Faker docs: [https://fakerjs.dev/api/git](https://fakerjs.dev/api/git) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `merge` | `boolean` | no | Whether to generate a merge commit entry. | +| `eol` | `LF\|CRLF` | no | Line ending to use in the generated commit entry. | +| `refDate` | `string\|date\|number` | no | The date to use as reference point for generated commit dates. | Examples: Shows the default git.commitEntry call. ```txt -git.commitEntry +git.commitEntry() +``` + +Returns: `commit f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0af\nAuthor: Ricardo Upton \nDate: Wed Jun 17 19:26:37 2026 +0300\n\n    parse auxiliary feed\n` + +Shows git.commitEntry using merge and line-ending options. + +```txt +git.commitEntry(merge=true, eol="LF") +``` + +Returns: `commit 9f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0a\nMerge: fa6b29d 6620e45\nAuthor: Lindsey_Price41 \nDate: Thu Jun 18 13:36:13 2026 -0800\n\n    calculate redundant feed\n` + +Shows git.commitEntry using a reference date. + +```txt +git.commitEntry(refDate="2024-01-01T00:00:00.000Z") +``` + +Returns: `commit f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0af\nAuthor: Ricardo Upton \nDate: Sun Dec 31 03:31:17 2023 +0300\n\n    parse auxiliary feed\n` + +Shows git.commitEntry forcing a merge commit entry. + +```txt +git.commitEntry(merge=true) +``` + +Returns: `commit 9f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0a\nMerge: fa6b29d 6620e45\nAuthor: Lindsey_Price41 \nDate: Thu Jun 18 13:36:13 2026 -0800\n\n    calculate redundant feed\n` + +Shows git.commitEntry using LF line endings. + +```txt +git.commitEntry(eol="LF") ``` Returns: `commit f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0af\nAuthor: Ricardo Upton \nDate: Wed Jun 17 19:26:37 2026 +0300\n\n    parse auxiliary feed\n` @@ -97,14 +143,24 @@ Generates a random commit sha. - Canonical: `awd.domain.git.commitSha` - Faker docs: [https://fakerjs.dev/api/git](https://fakerjs.dev/api/git) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `length` | `number` | no | The length of the generated commit SHA. | Examples: Shows the default git.commitSha call. ```txt -git.commitSha +git.commitSha() ``` Returns: `9f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0a` + +Shows git.commitSha using a shorter SHA length. + +```txt +git.commitSha(length=7) +``` + +Returns: `9f06324` diff --git a/docs-src/docs/040-test-data/domain/160-image.md b/docs-src/docs/040-test-data/domain/160-image.md index db5a6808..2a2421db 100644 --- a/docs-src/docs/040-test-data/domain/160-image.md +++ b/docs-src/docs/040-test-data/domain/160-image.md @@ -59,18 +59,63 @@ Generates a random data uri containing an URL-encoded SVG image or a Base64-enco - Canonical: `awd.domain.image.dataUri` - Faker docs: [https://fakerjs.dev/api/image](https://fakerjs.dev/api/image) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `width` | `number` | no | Width of the generated image. | +| `height` | `number` | no | Height of the generated image. | +| `color` | `string` | no | Fill color for the generated SVG image. | +| `type` | `svg-uri\|svg-base64` | no | Encoding format for the generated SVG data URI. | Examples: Shows the default image.dataUri call. ```txt -image.dataUri +image.dataUri() ``` Returns: `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%221668%22%20height%3D%222881%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23063247%22%2F%3E%3Ctext%20x%3D%22834%22%20y%3D%221440.5%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E1668x2881%3C%2Ftext%3E%3C%2Fsvg%3E` +Shows image.dataUri using explicit image dimensions, color, and encoding type. + +```txt +image.dataUri(width=320, height=240, color="red", type="svg-base64") +``` + +Returns: `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgYmFzZVByb2ZpbGU9ImZ1bGwiIHdpZHRoPSIzMjAiIGhlaWdodD0iMjQwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJyZWQiLz48dGV4dCB4PSIxNjAiIHk9IjEyMCIgZm9udC1zaXplPSIyMCIgYWxpZ25tZW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIj4zMjB4MjQwPC90ZXh0Pjwvc3ZnPg==` + +Shows image.dataUri using an explicit width. + +```txt +image.dataUri(width=320) +``` + +Returns: `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22320%22%20height%3D%221668%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23f06324%22%2F%3E%3Ctext%20x%3D%22160%22%20y%3D%22834%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E320x1668%3C%2Ftext%3E%3C%2Fsvg%3E` + +Shows image.dataUri using an explicit height. + +```txt +image.dataUri(height=240) +``` + +Returns: `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%221668%22%20height%3D%22240%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23f06324%22%2F%3E%3Ctext%20x%3D%22834%22%20y%3D%22120%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E1668x240%3C%2Ftext%3E%3C%2Fsvg%3E` + +Shows image.dataUri using an explicit SVG fill color. + +```txt +image.dataUri(color="red") +``` + +Returns: `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%221668%22%20height%3D%222881%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22red%22%2F%3E%3Ctext%20x%3D%22834%22%20y%3D%221440.5%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E1668x2881%3C%2Ftext%3E%3C%2Fsvg%3E` + +Shows image.dataUri using Base64 SVG encoding. + +```txt +image.dataUri(type="svg-base64") +``` + +Returns: `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgYmFzZVByb2ZpbGU9ImZ1bGwiIHdpZHRoPSIxNjY4IiBoZWlnaHQ9IjI4ODEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiMwNjMyNDciLz48dGV4dCB4PSI4MzQiIHk9IjE0NDAuNSIgZm9udC1zaXplPSIyMCIgYWxpZ25tZW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIj4xNjY4eDI4ODE8L3RleHQ+PC9zdmc+` + ### `image.personPortrait` Generates a random square portrait (avatar) of a person. @@ -78,18 +123,45 @@ Generates a random square portrait (avatar) of a person. - Canonical: `awd.domain.image.personPortrait` - Faker docs: [https://fakerjs.dev/api/image](https://fakerjs.dev/api/image) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `sex` | `female\|generic\|male` | no | Sex category used for the generated portrait. | +| `size` | `512\|256\|128\|64\|32` | no | Square image size in pixels. | Examples: Shows the default image.personPortrait call. ```txt -image.personPortrait +image.personPortrait() ``` Returns: `https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/72.jpg` +Shows image.personPortrait using sex and size options. + +```txt +image.personPortrait(sex="female", size=128) +``` + +Returns: `https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/128/41.jpg` + +Shows image.personPortrait using a sex category. + +```txt +image.personPortrait(sex="female") +``` + +Returns: `https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/41.jpg` + +Shows image.personPortrait using an explicit image size. + +```txt +image.personPortrait(size=128) +``` + +Returns: `https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/128/72.jpg` + ### `image.url` Generates a random image url. @@ -135,14 +207,59 @@ Generates a random image url provided via https://picsum.photos. - Canonical: `awd.domain.image.urlPicsumPhotos` - Faker docs: [https://fakerjs.dev/api/image](https://fakerjs.dev/api/image) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `width` | `number` | no | Width of the generated image URL. | +| `height` | `number` | no | Height of the generated image URL. | +| `grayscale` | `boolean` | no | Whether the generated image URL should request a grayscale image. | +| `blur` | `0\|1\|2\|3\|4\|5\|6\|7\|8\|9\|10` | no | Blur amount for the generated image URL. | Examples: Shows the default image.urlPicsumPhotos call. ```txt -image.urlPicsumPhotos +image.urlPicsumPhotos() ``` Returns: `https://picsum.photos/seed/5blox/1668/2881?grayscale&blur=3` + +Shows image.urlPicsumPhotos using dimension and filter options. + +```txt +image.urlPicsumPhotos(width=320, height=240, grayscale=true, blur=3) +``` + +Returns: `https://picsum.photos/seed/I0i95bl/320/240?grayscale&blur=3` + +Shows image.urlPicsumPhotos using an explicit width. + +```txt +image.urlPicsumPhotos(width=320) +``` + +Returns: `https://picsum.photos/seed/95blox/320/1668` + +Shows image.urlPicsumPhotos using an explicit height. + +```txt +image.urlPicsumPhotos(height=240) +``` + +Returns: `https://picsum.photos/seed/95blox/1668/240` + +Shows image.urlPicsumPhotos requesting a grayscale image. + +```txt +image.urlPicsumPhotos(grayscale=true) +``` + +Returns: `https://picsum.photos/seed/95blox/1668/2881?grayscale` + +Shows image.urlPicsumPhotos requesting a blur amount. + +```txt +image.urlPicsumPhotos(blur=3) +``` + +Returns: `https://picsum.photos/seed/95blox/1668/2881?grayscale&blur=3` diff --git a/docs-src/docs/040-test-data/domain/170-internet.md b/docs-src/docs/040-test-data/domain/170-internet.md index 17d9d71a..d8fc7df3 100644 --- a/docs-src/docs/040-test-data/domain/170-internet.md +++ b/docs-src/docs/040-test-data/domain/170-internet.md @@ -21,18 +21,45 @@ Generates a display name using the given person's name as base. - Canonical: `awd.domain.internet.displayName` - Faker docs: [https://fakerjs.dev/api/internet](https://fakerjs.dev/api/internet) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `firstName` | `string` | no | Optional first name to use as the basis for the display name. | +| `lastName` | `string` | no | Optional last name to use as the basis for the display name. | Examples: Shows the default internet.displayName call. ```txt -internet.displayName +internet.displayName() ``` Returns: `Aaliyah.Bosco` +Shows internet.displayName using firstName and lastName options. + +```txt +internet.displayName(firstName="Ada", lastName="Lovelace") +``` + +Returns: `Ada72` + +Shows internet.displayName using an explicit first name. + +```txt +internet.displayName(firstName="Ada") +``` + +Returns: `Ada14` + +Shows internet.displayName using an explicit last name. + +```txt +internet.displayName(lastName="Lovelace") +``` + +Returns: `Aaliyah14` + ### `internet.domainName` Generates a random domain name. @@ -182,18 +209,54 @@ Generates data using faker internet example email. - Canonical: `awd.domain.internet.exampleEmail` - Faker docs: [https://fakerjs.dev/api/internet](https://fakerjs.dev/api/internet) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `firstName` | `string` | no | Optional first name to use as the basis for the email address. | +| `lastName` | `string` | no | Optional last name to use as the basis for the email address. | +| `allowSpecialCharacters` | `boolean` | no | Whether special characters are allowed in the generated email address. | Examples: Shows the default internet.exampleEmail call. ```txt -internet.exampleEmail +internet.exampleEmail() +``` + +Returns: `Edwin.Dibbert@example.net` + +Shows internet.exampleEmail using firstName and lastName options. + +```txt +internet.exampleEmail(firstName="Ada", lastName="Lovelace") +``` + +Returns: `Ada_Lovelace0@example.net` + +Shows internet.exampleEmail allowing special characters. + +```txt +internet.exampleEmail(allowSpecialCharacters=true) ``` Returns: `Edwin.Dibbert@example.net` +Shows internet.exampleEmail using an explicit first name. + +```txt +internet.exampleEmail(firstName="Ada") +``` + +Returns: `Ada.Gutmann9@example.net` + +Shows internet.exampleEmail using an explicit last name. + +```txt +internet.exampleEmail(lastName="Lovelace") +``` + +Returns: `Edwin.Lovelace9@example.net` + ### `internet.httpMethod` Returns a random HTTP request method from an AnywayData-defined pool of GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS, TRACE, and CONNECT, with optional filtering for common methods and exclusions. @@ -238,18 +301,28 @@ Generates a random HTTP status code. - Canonical: `awd.domain.internet.httpStatusCode` - Faker docs: [https://fakerjs.dev/api/internet](https://fakerjs.dev/api/internet) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `types` | `array` | no | HTTP status categories to choose from. Allowed categories are informational, success, redirection, clientError, and serverError. | Examples: Shows the default internet.httpStatusCode call. ```txt -internet.httpStatusCode +internet.httpStatusCode() ``` Returns: `306` +Shows internet.httpStatusCode constrained to success status codes. + +```txt +internet.httpStatusCode(types=["success"]) +``` + +Returns: `204` + ### `internet.ip` Generates a random IPv4 or IPv6 address. diff --git a/docs-src/docs/040-test-data/domain/190-location.md b/docs-src/docs/040-test-data/domain/190-location.md index b7607a17..e7c36e6c 100644 --- a/docs-src/docs/040-test-data/domain/190-location.md +++ b/docs-src/docs/040-test-data/domain/190-location.md @@ -498,14 +498,25 @@ Generates data using faker location zip code. - Canonical: `awd.domain.location.zipCode` - Faker docs: [https://fakerjs.dev/api/location](https://fakerjs.dev/api/location) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `state` | `string` | no | State abbreviation used to constrain the generated zip code where supported by the locale. | +| `format` | `string` | no | Format pattern used for the generated zip code. | Examples: Shows the default location.zipCode call. ```txt -location.zipCode +location.zipCode() ``` Returns: `70310` + +Shows location.zipCode using a format option. + +```txt +location.zipCode(format="#####") +``` + +Returns: `47031` diff --git a/docs-src/docs/040-test-data/domain/200-lorem.md b/docs-src/docs/040-test-data/domain/200-lorem.md index 8768fd24..4c805985 100644 --- a/docs-src/docs/040-test-data/domain/200-lorem.md +++ b/docs-src/docs/040-test-data/domain/200-lorem.md @@ -450,8 +450,6 @@ Generates a word of a specified length. | Arg | Type | Required | Description | | --- | --- | --- | --- | -| `min` | `number` | no | Minimum word length when generating a ranged length. | -| `max` | `number` | no | Maximum word length when generating a ranged length. | | `length` | `number` | no | Exact word length to generate. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | @@ -465,22 +463,6 @@ lorem.word() Returns: `cur` -Shows lorem.word using min. - -```txt -lorem.word(max=10, min=1) -``` - -Returns: `cur` - -Shows lorem.word using max. - -```txt -lorem.word(max=5) -``` - -Returns: `cur` - Shows lorem.word using length. ```txt diff --git a/docs-src/docs/040-test-data/domain/220-number.md b/docs-src/docs/040-test-data/domain/220-number.md index 3f714100..3de6c2d0 100644 --- a/docs-src/docs/040-test-data/domain/220-number.md +++ b/docs-src/docs/040-test-data/domain/220-number.md @@ -23,7 +23,9 @@ Returns a BigInt number. | Arg | Type | Required | Description | | --- | --- | --- | --- | -| `value` | `bigint\|number\|string\|boolean` | no | Base value used for generation. Supports bigint, number, string, or boolean inputs. For range constraints use min, max, and multipleOf. | +| `min` | `bigint\|number\|string\|boolean` | no | Optional minimum bound for the generated BigInt value. | +| `max` | `bigint\|number\|string\|boolean` | no | Optional maximum bound for the generated BigInt value. | +| `multipleOf` | `bigint\|number\|string\|boolean` | no | Generated BigInt will be a multiple of the given value. | Examples: @@ -35,13 +37,29 @@ number.bigInt() Returns: `703101335462806n` -Shows number.bigInt using a boolean base value. +Shows number.bigInt using min and max bounds. ```txt -number.bigInt(value=true) +number.bigInt(min=100, max=1000) ``` -Returns: `703101335462806n` +Returns: `570n` + +Shows number.bigInt constrained to a multiple. + +```txt +number.bigInt(multipleOf=7) +``` + +Returns: `292170934823957n` + +Shows number.bigInt using an upper bound. + +```txt +number.bigInt(max=1000) +``` + +Returns: `699n` ### `number.binary` diff --git a/docs-src/docs/040-test-data/domain/230-person.md b/docs-src/docs/040-test-data/domain/230-person.md index ae3ca922..0ad3c61b 100644 --- a/docs-src/docs/040-test-data/domain/230-person.md +++ b/docs-src/docs/040-test-data/domain/230-person.md @@ -69,18 +69,54 @@ Generates a random full name. - Canonical: `awd.domain.person.fullName` - Faker docs: [https://fakerjs.dev/api/person](https://fakerjs.dev/api/person) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `firstName` | `string` | no | Optional first name to use as the basis for the full name. | +| `lastName` | `string` | no | Optional last name to use as the basis for the full name. | +| `sex` | `female\|generic\|male` | no | Sex category used for the generated full name. | Examples: Shows the default person.fullName call. ```txt -person.fullName +person.fullName() ``` Returns: `Aaliyah Corkery` +Shows person.fullName using name and sex options. + +```txt +person.fullName(firstName="Ada", lastName="Lovelace", sex="female") +``` + +Returns: `Ada Lovelace` + +Shows person.fullName using an explicit first name. + +```txt +person.fullName(firstName="Ada") +``` + +Returns: `Ada Abbott` + +Shows person.fullName using an explicit last name. + +```txt +person.fullName(lastName="Lovelace") +``` + +Returns: `Aaliyah Lovelace` + +Shows person.fullName using a sex category. + +```txt +person.fullName(sex="female") +``` + +Returns: `Monique Gutmann` + ### `person.gender` Returns a random gender. @@ -289,14 +325,24 @@ Returns a random sex type. The `SexType` is intended to be used in parameters an - Canonical: `awd.domain.person.sexType` - Faker docs: [https://fakerjs.dev/api/person](https://fakerjs.dev/api/person) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `includeGeneric` | `boolean` | no | Whether the generic sex type can be returned. | Examples: Shows the default person.sexType call. ```txt -person.sexType +person.sexType() +``` + +Returns: `female` + +Shows person.sexType excluding the generic value. + +```txt +person.sexType(includeGeneric=false) ``` Returns: `female` diff --git a/docs-src/docs/040-test-data/domain/270-system.md b/docs-src/docs/040-test-data/domain/270-system.md index fe889729..1f169ad0 100644 --- a/docs-src/docs/040-test-data/domain/270-system.md +++ b/docs-src/docs/040-test-data/domain/270-system.md @@ -174,18 +174,28 @@ Returns a random file name with extension. - Canonical: `awd.domain.system.fileName` - Faker docs: [https://fakerjs.dev/api/system](https://fakerjs.dev/api/system) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `extensionCount` | `number` | no | How many extensions the generated file name should have. | Examples: Shows the default system.fileName call. ```txt -system.fileName +system.fileName() ``` Returns: `fog_aboard.otf` +Shows system.fileName using extensionCount. + +```txt +system.fileName(extensionCount=2) +``` + +Returns: `fog_aboard.otf.7z` + ### `system.filePath` Returns a file path. @@ -250,18 +260,45 @@ Returns a random network interface. - Canonical: `awd.domain.system.networkInterface` - Faker docs: [https://fakerjs.dev/api/system](https://fakerjs.dev/api/system) -No parameters. +| Arg | Type | Required | Description | +| --- | --- | --- | --- | +| `interfaceType` | `en\|wl\|ww` | no | Network interface type prefix. | +| `interfaceSchema` | `index\|slot\|mac\|pci` | no | Network interface naming schema. | Examples: Shows the default system.networkInterface call. ```txt -system.networkInterface +system.networkInterface() ``` Returns: `wlx042125686a3e` +Shows system.networkInterface using interface type and schema options. + +```txt +system.networkInterface(interfaceType="en", interfaceSchema="mac") +``` + +Returns: `enx6b042125686a` + +Shows system.networkInterface using an explicit interface type. + +```txt +system.networkInterface(interfaceType="en") +``` + +Returns: `ens7f3d0` + +Shows system.networkInterface using an explicit interface schema. + +```txt +system.networkInterface(interfaceSchema="mac") +``` + +Returns: `wlxb042125686a3` + ### `system.semver` Returns a semantic version. diff --git a/docs-src/docs/040-test-data/domain/290-word.md b/docs-src/docs/040-test-data/domain/290-word.md index 02e8b2db..1ad6f148 100644 --- a/docs-src/docs/040-test-data/domain/290-word.md +++ b/docs-src/docs/040-test-data/domain/290-word.md @@ -24,7 +24,6 @@ Returns a random adjective. | Arg | Type | Required | Description | | --- | --- | --- | --- | | `length` | `number` | no | Desired length of the generated value. | -| `max` | `number` | no | Maximum bound used when generating a value. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | Examples: @@ -45,14 +44,6 @@ word.adjective(length=5) Returns: `major` -Shows word.adjective using max. - -```txt -word.adjective(max=5) -``` - -Returns: `inferior` - Shows word.adjective using strategy. ```txt @@ -71,7 +62,6 @@ Returns a random adverb. | Arg | Type | Required | Description | | --- | --- | --- | --- | | `length` | `number` | no | Desired length of the generated value. | -| `max` | `number` | no | Maximum bound used when generating a value. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | Examples: @@ -92,14 +82,6 @@ word.adverb(length=5) Returns: `never` -Shows word.adverb using max. - -```txt -word.adverb(max=5) -``` - -Returns: `knavishly` - Shows word.adverb using strategy. ```txt @@ -118,7 +100,6 @@ Returns a random conjunction. | Arg | Type | Required | Description | | --- | --- | --- | --- | | `length` | `number` | no | Desired length of the generated value. | -| `max` | `number` | no | Maximum bound used when generating a value. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | Examples: @@ -139,14 +120,6 @@ word.conjunction(length=5) Returns: `until` -Shows word.conjunction using max. - -```txt -word.conjunction(max=5) -``` - -Returns: `likewise` - Shows word.conjunction using strategy. ```txt @@ -165,7 +138,6 @@ Returns a random interjection. | Arg | Type | Required | Description | | --- | --- | --- | --- | | `length` | `number` | no | Desired length of the generated value. | -| `max` | `number` | no | Maximum bound used when generating a value. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | Examples: @@ -186,14 +158,6 @@ word.interjection(length=5) Returns: `fooey` -Shows word.interjection using max. - -```txt -word.interjection(max=5) -``` - -Returns: `woot` - Shows word.interjection using strategy. ```txt @@ -212,7 +176,6 @@ Returns a random noun. | Arg | Type | Required | Description | | --- | --- | --- | --- | | `length` | `number` | no | Desired length of the generated value. | -| `max` | `number` | no | Maximum bound used when generating a value. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | Examples: @@ -233,14 +196,6 @@ word.noun(length=5) Returns: `humor` -Shows word.noun using max. - -```txt -word.noun(max=5) -``` - -Returns: `heating` - Shows word.noun using strategy. ```txt @@ -259,7 +214,6 @@ Returns a random preposition. | Arg | Type | Required | Description | | --- | --- | --- | --- | | `length` | `number` | no | Desired length of the generated value. | -| `max` | `number` | no | Maximum bound used when generating a value. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | Examples: @@ -280,14 +234,6 @@ word.preposition(length=5) Returns: `aside` -Shows word.preposition using max. - -```txt -word.preposition(max=5) -``` - -Returns: `except` - Shows word.preposition using strategy. ```txt @@ -306,7 +252,6 @@ Returns a random word, that can be an adjective, adverb, conjunction, interjecti | Arg | Type | Required | Description | | --- | --- | --- | --- | | `length` | `number` | no | Desired length of the generated value. | -| `max` | `number` | no | Maximum bound used when generating a value. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | Examples: @@ -327,14 +272,6 @@ word.sample(length=5) Returns: `yowza` -Shows word.sample using max. - -```txt -word.sample(max=5) -``` - -Returns: `boohoo` - Shows word.sample using strategy. ```txt @@ -353,7 +290,6 @@ Returns a random verb. | Arg | Type | Required | Description | | --- | --- | --- | --- | | `length` | `number` | no | Desired length of the generated value. | -| `max` | `number` | no | Maximum bound used when generating a value. | | `strategy` | `fail\|closest\|shortest\|longest\|any-length` | no | The strategy to apply when no words with a matching length are found. Available error handling strategies: fail: Throws an error if no words with the given length are found. shortest: Returns any of the shortest words. closest: Returns any of the words closest to the given length. longest: Returns any of the longest words. any-length: Returns a word with any length. | Examples: @@ -374,14 +310,6 @@ word.verb(length=5) Returns: `mould` -Shows word.verb using max. - -```txt -word.verb(max=5) -``` - -Returns: `hunger` - Shows word.verb using strategy. ```txt @@ -400,7 +328,6 @@ Returns a random string containing some words separated by spaces. | Arg | Type | Required | Description | | --- | --- | --- | --- | | `count` | `number` | no | Number of items to generate. | -| `max` | `number` | no | Maximum bound used when generating a value. | Examples: @@ -419,11 +346,3 @@ word.words(count=5) ``` Returns: `boohoo pish tenderly above pop` - -Shows word.words using max. - -```txt -word.words(max=5) -``` - -Returns: `fog aboard` diff --git a/docs/domain-faker-param-comparison.md b/docs/domain-faker-param-comparison.md new file mode 100644 index 00000000..94b11078 --- /dev/null +++ b/docs/domain-faker-param-comparison.md @@ -0,0 +1,97 @@ +# Domain/Faker Param Comparison + +Generated by `node scripts/compare-domain-faker-params.mjs --markdown` from the installed `@faker-js/faker` declaration bundle and the domain keyword catalog. + +## Summary + +- Faker option-backed domain commands compared: 77 +- Commands with Faker option params missing from domain metadata: 0 +- Commands with domain params not present in Faker options: 0 + +## Comparison + +| Command | Faker option params | Domain params | Missing in domain | Domain-only params | +| --- | --- | --- | --- | --- | +| `airline.flightNumber` | `length`, `addLeadingZeros` | `length`, `addLeadingZeros` | none | none | +| `airline.recordLocator` | `allowNumerics`, `allowVisuallySimilarCharacters` | `allowNumerics`, `allowVisuallySimilarCharacters` | none | none | +| `airline.seat` | `aircraftType` | `aircraftType` | none | none | +| `color.cmyk` | `format` | `format` | none | none | +| `color.colorByCSSColorSpace` | `format`, `space` | `format`, `space` | none | none | +| `color.hsl` | `format`, `includeAlpha` | `format`, `includeAlpha` | none | none | +| `color.hwb` | `format` | `format` | none | none | +| `color.lab` | `format` | `format` | none | none | +| `color.lch` | `format` | `format` | none | none | +| `color.rgb` | `prefix`, `casing`, `format`, `includeAlpha` | `casing`, `format`, `includeAlpha`, `prefix` | none | none | +| `commerce.isbn` | `variant`, `separator` | `separator`, `variant` | none | none | +| `commerce.price` | `min`, `max`, `dec`, `symbol` | `dec`, `max`, `min`, `symbol` | none | none | +| `commerce.upc` | `prefix` | `prefix` | none | none | +| `datatype.boolean` | `probability` | `probability` | none | none | +| `date.month` | `abbreviated`, `context` | `abbreviated`, `context` | none | none | +| `date.weekday` | `abbreviated`, `context` | `abbreviated`, `context` | none | none | +| `finance.accountNumber` | `length` | `length` | none | none | +| `finance.amount` | `min`, `max`, `dec`, `symbol`, `autoFormat` | `autoFormat`, `dec`, `max`, `min`, `symbol` | none | none | +| `finance.bic` | `includeBranchCode` | `includeBranchCode` | none | none | +| `finance.bitcoinAddress` | `type`, `network` | `type`, `network` | none | none | +| `finance.creditCardNumber` | `issuer` | `issuer` | none | none | +| `finance.iban` | `formatted`, `countryCode` | `countryCode`, `formatted` | none | none | +| `finance.pin` | `length` | `length` | none | none | +| `git.commitDate` | `refDate` | `refDate` | none | none | +| `git.commitEntry` | `merge`, `eol`, `refDate` | `merge`, `eol`, `refDate` | none | none | +| `git.commitSha` | `length` | `length` | none | none | +| `image.dataUri` | `width`, `height`, `color`, `type` | `width`, `height`, `color`, `type` | none | none | +| `image.personPortrait` | `sex`, `size` | `sex`, `size` | none | none | +| `image.url` | `width`, `height` | `height`, `width` | none | none | +| `image.urlPicsumPhotos` | `width`, `height`, `grayscale`, `blur` | `width`, `height`, `grayscale`, `blur` | none | none | +| `internet.displayName` | `firstName`, `lastName` | `firstName`, `lastName` | none | none | +| `internet.email` | `firstName`, `lastName`, `provider`, `allowSpecialCharacters` | `allowSpecialCharacters`, `firstName`, `lastName`, `provider` | none | none | +| `internet.emoji` | `types` | `types` | none | none | +| `internet.exampleEmail` | `firstName`, `lastName`, `allowSpecialCharacters` | `firstName`, `lastName`, `allowSpecialCharacters` | none | none | +| `internet.httpStatusCode` | `types` | `types` | none | none | +| `internet.ipv4` | `cidrBlock`, `network` | `cidrBlock`, `network` | none | none | +| `internet.jwt` | `header`, `payload`, `refDate` | `header`, `payload`, `refDate` | none | none | +| `internet.mac` | `separator` | `separator` | none | none | +| `internet.password` | `length`, `memorable`, `pattern`, `prefix` | `length`, `memorable`, `pattern`, `prefix` | none | none | +| `internet.url` | `appendSlash`, `protocol` | `appendSlash`, `protocol` | none | none | +| `internet.username` | `firstName`, `lastName` | `firstName`, `lastName` | none | none | +| `location.cardinalDirection` | `abbreviated` | `abbreviated` | none | none | +| `location.countryCode` | `variant` | `variant` | none | none | +| `location.direction` | `abbreviated` | `abbreviated` | none | none | +| `location.ordinalDirection` | `abbreviated` | `abbreviated` | none | none | +| `location.state` | `abbreviated` | `abbreviated` | none | none | +| `location.streetAddress` | `useFullAddress` | `useFullAddress` | none | none | +| `location.zipCode` | `state`, `format` | `state`, `format` | none | none | +| `lorem.word` | `length`, `strategy` | `length`, `strategy` | none | none | +| `number.bigInt` | `min`, `max`, `multipleOf` | `min`, `max`, `multipleOf` | none | none | +| `number.binary` | `min`, `max` | `max`, `min` | none | none | +| `number.float` | `min`, `max`, `fractionDigits`, `multipleOf` | `fractionDigits`, `max`, `min`, `multipleOf` | none | none | +| `number.hex` | `min`, `max` | `min`, `max` | none | none | +| `number.int` | `min`, `max`, `multipleOf` | `min`, `max`, `multipleOf` | none | none | +| `number.octal` | `min`, `max` | `max`, `min` | none | none | +| `number.romanNumeral` | `min`, `max` | `min`, `max` | none | none | +| `person.fullName` | `firstName`, `lastName`, `sex` | `firstName`, `lastName`, `sex` | none | none | +| `person.sexType` | `includeGeneric` | `includeGeneric` | none | none | +| `phone.number` | `style` | `style` | none | none | +| `string.alpha` | `length`, `casing`, `exclude` | `length`, `casing`, `exclude` | none | none | +| `string.alphanumeric` | `length`, `casing`, `exclude` | `length`, `casing`, `exclude` | none | none | +| `string.binary` | `length`, `prefix` | `length`, `prefix` | none | none | +| `string.hexadecimal` | `length`, `casing`, `prefix` | `casing`, `length`, `prefix` | none | none | +| `string.numeric` | `length`, `allowLeadingZeros`, `exclude` | `length`, `allowLeadingZeros`, `exclude` | none | none | +| `string.octal` | `length`, `prefix` | `length`, `prefix` | none | none | +| `system.cron` | `includeYear`, `includeNonStandard` | `includeNonStandard`, `includeYear` | none | none | +| `system.fileName` | `extensionCount` | `extensionCount` | none | none | +| `system.networkInterface` | `interfaceType`, `interfaceSchema` | `interfaceType`, `interfaceSchema` | none | none | +| `word.adjective` | `length`, `strategy` | `length`, `strategy` | none | none | +| `word.adverb` | `length`, `strategy` | `length`, `strategy` | none | none | +| `word.conjunction` | `length`, `strategy` | `length`, `strategy` | none | none | +| `word.interjection` | `length`, `strategy` | `length`, `strategy` | none | none | +| `word.noun` | `length`, `strategy` | `length`, `strategy` | none | none | +| `word.preposition` | `length`, `strategy` | `length`, `strategy` | none | none | +| `word.sample` | `length`, `strategy` | `length`, `strategy` | none | none | +| `word.verb` | `length`, `strategy` | `length`, `strategy` | none | none | +| `word.words` | `count` | `count` | none | none | + +## Review Notes + +- `Missing in domain` must stay at `none` for every row. +- `Domain-only params` must stay at `none` for every Faker-backed domain command. + diff --git a/packages/core-ui/src/tests/interaction/matrix/schema-interaction-scenario-builder.test.js b/packages/core-ui/src/tests/interaction/matrix/schema-interaction-scenario-builder.test.js index d757b5e7..9de1cd3b 100644 --- a/packages/core-ui/src/tests/interaction/matrix/schema-interaction-scenario-builder.test.js +++ b/packages/core-ui/src/tests/interaction/matrix/schema-interaction-scenario-builder.test.js @@ -16,6 +16,10 @@ import { getScenarioExecutionStatus, } from './support/schema-interaction-scenario-builder.js'; +function isScenarioArgCoverageRequired(command, argName) { + return !(command === 'location.zipCode' && argName === 'state'); +} + describe('schema interaction scenario builder', () => { test('exposes datatype.enum in domain command list with help metadata', () => { const visibleCommands = getVisibleDomainCommands({ @@ -82,9 +86,11 @@ describe('schema interaction scenario builder', () => { const bucket = byCommand.get(`domain:${command}`); expect(bucket.scenarios.length).toBeGreaterThan(0); - (metadata.args || []).forEach((arg) => { - expect(bucket.coveredArgs.has(arg.name)).toBe(true); - }); + (metadata.args || []) + .filter((arg) => isScenarioArgCoverageRequired(command, arg.name)) + .forEach((arg) => { + expect(bucket.coveredArgs.has(arg.name)).toBe(true); + }); const exampleScenarioCount = bucket.scenarios.filter((scenario) => scenario.origins.includes('example')).length; const expectedExamples = Array.isArray(getDomainKeywordByCommand(command)?.help?.usageExamples) diff --git a/packages/core/js/domain/domain-keywords.js b/packages/core/js/domain/domain-keywords.js index 04f9cd3f..01ecd589 100644 --- a/packages/core/js/domain/domain-keywords.js +++ b/packages/core/js/domain/domain-keywords.js @@ -279,6 +279,7 @@ function isTypeMatch(value, typeName) { for (const item of allowed) { if (/^[+-]?\d+(\.\d+)?$/.test(item) && typeof value === 'number' && Object.is(value, Number(item))) return true; if (item === 'string' && typeof value === 'string') return true; + if (item === 'bigint' && typeof value === 'bigint') return true; if (item === 'comma-separated list' && typeof value === 'string') return true; if (item === 'integer' && typeof value === 'number' && Number.isInteger(value)) return true; if (item === 'number' && typeof value === 'number' && Number.isFinite(value)) return true; diff --git a/packages/core/js/keywords/domain/airline/flight-number-keyword-definition.js b/packages/core/js/keywords/domain/airline/flight-number-keyword-definition.js index 0c91aab2..6afaf0e9 100644 --- a/packages/core/js/keywords/domain/airline/flight-number-keyword-definition.js +++ b/packages/core/js/keywords/domain/airline/flight-number-keyword-definition.js @@ -5,6 +5,7 @@ const AIRLINE_FLIGHT_NUMBER_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'airline.flightNumber', + argTransform: 'optionsFromHelpArgs', }, help: { summary: @@ -15,12 +16,42 @@ const AIRLINE_FLIGHT_NUMBER_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'airline.flightNumber', + functionCall: 'airline.flightNumber()', sampleReturnValue: '70', description: 'Shows the default airline.flightNumber call.', }, + { + functionCall: 'airline.flightNumber(length=4, addLeadingZeros=true)', + sampleReturnValue: '4703', + description: 'Shows airline.flightNumber using length and leading-zero options.', + }, + { + functionCall: 'airline.flightNumber(length=4)', + sampleReturnValue: '4703', + description: 'Shows airline.flightNumber using a fixed length.', + }, + { + functionCall: 'airline.flightNumber(addLeadingZeros=true)', + sampleReturnValue: '0070', + description: 'Shows airline.flightNumber padding shorter values with leading zeros.', + }, + ], + args: [ + { + name: 'length', + type: 'number', + required: false, + description: 'Desired flight-number length.', + examples: [4], + }, + { + name: 'addLeadingZeros', + type: 'boolean', + required: false, + description: 'Whether shorter flight numbers should be padded with leading zeros.', + examples: [true], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/airline/record-locator-keyword-definition.js b/packages/core/js/keywords/domain/airline/record-locator-keyword-definition.js index fb3c984a..441bedfc 100644 --- a/packages/core/js/keywords/domain/airline/record-locator-keyword-definition.js +++ b/packages/core/js/keywords/domain/airline/record-locator-keyword-definition.js @@ -5,6 +5,7 @@ const AIRLINE_RECORD_LOCATOR_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'airline.recordLocator', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a random record locator. Record locators are 6-character alphanumeric booking references.', @@ -14,12 +15,37 @@ const AIRLINE_RECORD_LOCATOR_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'airline.recordLocator', + functionCall: 'airline.recordLocator()', sampleReturnValue: 'KTAGDC', description: 'Shows the default airline.recordLocator call.', }, + { + functionCall: 'airline.recordLocator(allowNumerics=true)', + sampleReturnValue: 'ER2B64', + description: 'Shows airline.recordLocator allowing numeric characters.', + }, + { + functionCall: 'airline.recordLocator(allowVisuallySimilarCharacters=true)', + sampleReturnValue: 'KSAHDC', + description: 'Shows airline.recordLocator allowing visually similar characters.', + }, + ], + args: [ + { + name: 'allowNumerics', + type: 'boolean', + required: false, + description: 'Whether numeric characters can be included.', + examples: [true], + }, + { + name: 'allowVisuallySimilarCharacters', + type: 'boolean', + required: false, + description: 'Whether visually similar characters such as I, 1, O, and 0 can be included.', + examples: [true], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/git/commit-date-keyword-definition.js b/packages/core/js/keywords/domain/git/commit-date-keyword-definition.js index bad1ad90..7dcc7003 100644 --- a/packages/core/js/keywords/domain/git/commit-date-keyword-definition.js +++ b/packages/core/js/keywords/domain/git/commit-date-keyword-definition.js @@ -5,6 +5,7 @@ const GIT_COMMIT_DATE_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'git.commitDate', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a date string for a git commit using the same format as `git log`.', @@ -14,12 +15,25 @@ const GIT_COMMIT_DATE_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'git.commitDate', + functionCall: 'git.commitDate()', sampleReturnValue: 'Thu Jun 18 01:55:50 2026 +0600', description: 'Shows the default git.commitDate call.', }, + { + functionCall: 'git.commitDate(refDate="2024-01-01T00:00:00.000Z")', + sampleReturnValue: 'Sun Dec 31 10:00:30 2023 +0600', + description: 'Shows git.commitDate using a reference date.', + }, + ], + args: [ + { + name: 'refDate', + type: 'string|date|number', + required: false, + description: 'The date to use as reference point for generated commit dates.', + examples: ['2024-01-01T00:00:00.000Z'], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/git/commit-entry-keyword-definition.js b/packages/core/js/keywords/domain/git/commit-entry-keyword-definition.js index 83969bb0..5f931899 100644 --- a/packages/core/js/keywords/domain/git/commit-entry-keyword-definition.js +++ b/packages/core/js/keywords/domain/git/commit-entry-keyword-definition.js @@ -5,6 +5,7 @@ const GIT_COMMIT_ENTRY_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'git.commitEntry', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a random commit entry as printed by `git log`.', @@ -14,7 +15,7 @@ const GIT_COMMIT_ENTRY_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'git.commitEntry', + functionCall: 'git.commitEntry()', sampleReturnValue: 'commit f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0af\r\n' + 'Author: Ricardo Upton \r\n' + @@ -23,8 +24,72 @@ const GIT_COMMIT_ENTRY_KEYWORD_DEFINITION = { '    parse auxiliary feed\r\n', description: 'Shows the default git.commitEntry call.', }, + { + functionCall: 'git.commitEntry(merge=true, eol="LF")', + sampleReturnValue: + 'commit 9f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0a\n' + + 'Merge: fa6b29d 6620e45\n' + + 'Author: Lindsey_Price41 \n' + + 'Date: Thu Jun 18 13:36:13 2026 -0800\n' + + '\n' + + '    calculate redundant feed\n', + description: 'Shows git.commitEntry using merge and line-ending options.', + }, + { + functionCall: 'git.commitEntry(refDate="2024-01-01T00:00:00.000Z")', + sampleReturnValue: + 'commit f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0af\r\n' + + 'Author: Ricardo Upton \r\n' + + 'Date: Sun Dec 31 03:31:17 2023 +0300\r\n' + + '\r\n' + + '    parse auxiliary feed\r\n', + description: 'Shows git.commitEntry using a reference date.', + }, + { + functionCall: 'git.commitEntry(merge=true)', + sampleReturnValue: + 'commit 9f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0a\r\n' + + 'Merge: fa6b29d 6620e45\r\n' + + 'Author: Lindsey_Price41 \r\n' + + 'Date: Thu Jun 18 13:36:13 2026 -0800\r\n' + + '\r\n' + + '    calculate redundant feed\r\n', + description: 'Shows git.commitEntry forcing a merge commit entry.', + }, + { + functionCall: 'git.commitEntry(eol="LF")', + sampleReturnValue: + 'commit f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0af\n' + + 'Author: Ricardo Upton \n' + + 'Date: Wed Jun 17 19:26:37 2026 +0300\n' + + '\n' + + '    parse auxiliary feed\n', + description: 'Shows git.commitEntry using LF line endings.', + }, + ], + args: [ + { + name: 'merge', + type: 'boolean', + required: false, + description: 'Whether to generate a merge commit entry.', + examples: [true], + }, + { + name: 'eol', + type: 'LF|CRLF', + required: false, + description: 'Line ending to use in the generated commit entry.', + examples: ['LF'], + }, + { + name: 'refDate', + type: 'string|date|number', + required: false, + description: 'The date to use as reference point for generated commit dates.', + examples: ['2024-01-01T00:00:00.000Z'], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/git/commit-sha-keyword-definition.js b/packages/core/js/keywords/domain/git/commit-sha-keyword-definition.js index 672939e4..3353d9d4 100644 --- a/packages/core/js/keywords/domain/git/commit-sha-keyword-definition.js +++ b/packages/core/js/keywords/domain/git/commit-sha-keyword-definition.js @@ -5,6 +5,7 @@ const GIT_COMMIT_SHA_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'git.commitSha', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a random commit sha.', @@ -14,12 +15,25 @@ const GIT_COMMIT_SHA_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'git.commitSha', + functionCall: 'git.commitSha()', sampleReturnValue: '9f0632478b9f4d0e9c34bf6fdd103d29fbf6fc0a', description: 'Shows the default git.commitSha call.', }, + { + functionCall: 'git.commitSha(length=7)', + sampleReturnValue: '9f06324', + description: 'Shows git.commitSha using a shorter SHA length.', + }, + ], + args: [ + { + name: 'length', + type: 'number', + required: false, + description: 'The length of the generated commit SHA.', + examples: [7], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/image/data-uri-keyword-definition.js b/packages/core/js/keywords/domain/image/data-uri-keyword-definition.js index fd3da1f7..8a6e42e5 100644 --- a/packages/core/js/keywords/domain/image/data-uri-keyword-definition.js +++ b/packages/core/js/keywords/domain/image/data-uri-keyword-definition.js @@ -5,6 +5,7 @@ const IMAGE_DATA_URI_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'image.dataUri', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a random data uri containing an URL-encoded SVG image or a Base64-encoded SVG image.', @@ -14,13 +15,72 @@ const IMAGE_DATA_URI_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'image.dataUri', + functionCall: 'image.dataUri()', sampleReturnValue: 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%221668%22%20height%3D%222881%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23063247%22%2F%3E%3Ctext%20x%3D%22834%22%20y%3D%221440.5%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E1668x2881%3C%2Ftext%3E%3C%2Fsvg%3E', description: 'Shows the default image.dataUri call.', }, + { + functionCall: 'image.dataUri(width=320, height=240, color="red", type="svg-base64")', + sampleReturnValue: + 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgYmFzZVByb2ZpbGU9ImZ1bGwiIHdpZHRoPSIzMjAiIGhlaWdodD0iMjQwIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJyZWQiLz48dGV4dCB4PSIxNjAiIHk9IjEyMCIgZm9udC1zaXplPSIyMCIgYWxpZ25tZW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIj4zMjB4MjQwPC90ZXh0Pjwvc3ZnPg==', + description: 'Shows image.dataUri using explicit image dimensions, color, and encoding type.', + }, + { + functionCall: 'image.dataUri(width=320)', + sampleReturnValue: + 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22320%22%20height%3D%221668%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23f06324%22%2F%3E%3Ctext%20x%3D%22160%22%20y%3D%22834%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E320x1668%3C%2Ftext%3E%3C%2Fsvg%3E', + description: 'Shows image.dataUri using an explicit width.', + }, + { + functionCall: 'image.dataUri(height=240)', + sampleReturnValue: + 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%221668%22%20height%3D%22240%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23f06324%22%2F%3E%3Ctext%20x%3D%22834%22%20y%3D%22120%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E1668x240%3C%2Ftext%3E%3C%2Fsvg%3E', + description: 'Shows image.dataUri using an explicit height.', + }, + { + functionCall: 'image.dataUri(color="red")', + sampleReturnValue: + 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%221668%22%20height%3D%222881%22%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22red%22%2F%3E%3Ctext%20x%3D%22834%22%20y%3D%221440.5%22%20font-size%3D%2220%22%20alignment-baseline%3D%22middle%22%20text-anchor%3D%22middle%22%20fill%3D%22white%22%3E1668x2881%3C%2Ftext%3E%3C%2Fsvg%3E', + description: 'Shows image.dataUri using an explicit SVG fill color.', + }, + { + functionCall: 'image.dataUri(type="svg-base64")', + sampleReturnValue: + 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgYmFzZVByb2ZpbGU9ImZ1bGwiIHdpZHRoPSIxNjY4IiBoZWlnaHQ9IjI4ODEiPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9IiMwNjMyNDciLz48dGV4dCB4PSI4MzQiIHk9IjE0NDAuNSIgZm9udC1zaXplPSIyMCIgYWxpZ25tZW50LWJhc2VsaW5lPSJtaWRkbGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIj4xNjY4eDI4ODE8L3RleHQ+PC9zdmc+', + description: 'Shows image.dataUri using Base64 SVG encoding.', + }, + ], + args: [ + { + name: 'width', + type: 'number', + required: false, + description: 'Width of the generated image.', + examples: [320], + }, + { + name: 'height', + type: 'number', + required: false, + description: 'Height of the generated image.', + examples: [240], + }, + { + name: 'color', + type: 'string', + required: false, + description: 'Fill color for the generated SVG image.', + examples: ['red'], + }, + { + name: 'type', + type: 'svg-uri|svg-base64', + required: false, + description: 'Encoding format for the generated SVG data URI.', + examples: ['svg-base64'], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/image/person-portrait-keyword-definition.js b/packages/core/js/keywords/domain/image/person-portrait-keyword-definition.js index 1a43867b..a591d34e 100644 --- a/packages/core/js/keywords/domain/image/person-portrait-keyword-definition.js +++ b/packages/core/js/keywords/domain/image/person-portrait-keyword-definition.js @@ -5,6 +5,7 @@ const IMAGE_PERSON_PORTRAIT_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'image.personPortrait', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a random square portrait (avatar) of a person.', @@ -14,12 +15,42 @@ const IMAGE_PERSON_PORTRAIT_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'image.personPortrait', + functionCall: 'image.personPortrait()', sampleReturnValue: 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/72.jpg', description: 'Shows the default image.personPortrait call.', }, + { + functionCall: 'image.personPortrait(sex="female", size=128)', + sampleReturnValue: 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/128/41.jpg', + description: 'Shows image.personPortrait using sex and size options.', + }, + { + functionCall: 'image.personPortrait(sex="female")', + sampleReturnValue: 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/41.jpg', + description: 'Shows image.personPortrait using a sex category.', + }, + { + functionCall: 'image.personPortrait(size=128)', + sampleReturnValue: 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/128/72.jpg', + description: 'Shows image.personPortrait using an explicit image size.', + }, + ], + args: [ + { + name: 'sex', + type: 'female|generic|male', + required: false, + description: 'Sex category used for the generated portrait.', + examples: ['female'], + }, + { + name: 'size', + type: '512|256|128|64|32', + required: false, + description: 'Square image size in pixels.', + examples: [128], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/image/url-picsum-photos-keyword-definition.js b/packages/core/js/keywords/domain/image/url-picsum-photos-keyword-definition.js index 9144c887..ce05c34b 100644 --- a/packages/core/js/keywords/domain/image/url-picsum-photos-keyword-definition.js +++ b/packages/core/js/keywords/domain/image/url-picsum-photos-keyword-definition.js @@ -5,6 +5,7 @@ const IMAGE_URL_PICSUM_PHOTOS_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'image.urlPicsumPhotos', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a random image url provided via https://picsum.photos.', @@ -14,12 +15,66 @@ const IMAGE_URL_PICSUM_PHOTOS_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'image.urlPicsumPhotos', + functionCall: 'image.urlPicsumPhotos()', sampleReturnValue: 'https://picsum.photos/seed/5blox/1668/2881?grayscale&blur=3', description: 'Shows the default image.urlPicsumPhotos call.', }, + { + functionCall: 'image.urlPicsumPhotos(width=320, height=240, grayscale=true, blur=3)', + sampleReturnValue: 'https://picsum.photos/seed/I0i95bl/320/240?grayscale&blur=3', + description: 'Shows image.urlPicsumPhotos using dimension and filter options.', + }, + { + functionCall: 'image.urlPicsumPhotos(width=320)', + sampleReturnValue: 'https://picsum.photos/seed/95blox/320/1668', + description: 'Shows image.urlPicsumPhotos using an explicit width.', + }, + { + functionCall: 'image.urlPicsumPhotos(height=240)', + sampleReturnValue: 'https://picsum.photos/seed/95blox/1668/240', + description: 'Shows image.urlPicsumPhotos using an explicit height.', + }, + { + functionCall: 'image.urlPicsumPhotos(grayscale=true)', + sampleReturnValue: 'https://picsum.photos/seed/95blox/1668/2881?grayscale', + description: 'Shows image.urlPicsumPhotos requesting a grayscale image.', + }, + { + functionCall: 'image.urlPicsumPhotos(blur=3)', + sampleReturnValue: 'https://picsum.photos/seed/95blox/1668/2881?grayscale&blur=3', + description: 'Shows image.urlPicsumPhotos requesting a blur amount.', + }, + ], + args: [ + { + name: 'width', + type: 'number', + required: false, + description: 'Width of the generated image URL.', + examples: [320], + }, + { + name: 'height', + type: 'number', + required: false, + description: 'Height of the generated image URL.', + examples: [240], + }, + { + name: 'grayscale', + type: 'boolean', + required: false, + description: 'Whether the generated image URL should request a grayscale image.', + examples: [true], + }, + { + name: 'blur', + type: '0|1|2|3|4|5|6|7|8|9|10', + required: false, + description: 'Blur amount for the generated image URL.', + examples: [3], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/internet/display-name-keyword-definition.js b/packages/core/js/keywords/domain/internet/display-name-keyword-definition.js index 08c460ba..698f9fc1 100644 --- a/packages/core/js/keywords/domain/internet/display-name-keyword-definition.js +++ b/packages/core/js/keywords/domain/internet/display-name-keyword-definition.js @@ -5,6 +5,7 @@ const INTERNET_DISPLAY_NAME_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'internet.displayName', + argTransform: 'optionsFromHelpArgs', }, help: { summary: "Generates a display name using the given person's name as base.", @@ -14,12 +15,42 @@ const INTERNET_DISPLAY_NAME_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'internet.displayName', + functionCall: 'internet.displayName()', sampleReturnValue: 'Aaliyah.Bosco', description: 'Shows the default internet.displayName call.', }, + { + functionCall: 'internet.displayName(firstName="Ada", lastName="Lovelace")', + sampleReturnValue: 'Ada72', + description: 'Shows internet.displayName using firstName and lastName options.', + }, + { + functionCall: 'internet.displayName(firstName="Ada")', + sampleReturnValue: 'Ada14', + description: 'Shows internet.displayName using an explicit first name.', + }, + { + functionCall: 'internet.displayName(lastName="Lovelace")', + sampleReturnValue: 'Aaliyah14', + description: 'Shows internet.displayName using an explicit last name.', + }, + ], + args: [ + { + name: 'firstName', + type: 'string', + required: false, + description: 'Optional first name to use as the basis for the display name.', + examples: ['Ada'], + }, + { + name: 'lastName', + type: 'string', + required: false, + description: 'Optional last name to use as the basis for the display name.', + examples: ['Lovelace'], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/internet/example-email-keyword-definition.js b/packages/core/js/keywords/domain/internet/example-email-keyword-definition.js index 6fc8c677..0bac6c8e 100644 --- a/packages/core/js/keywords/domain/internet/example-email-keyword-definition.js +++ b/packages/core/js/keywords/domain/internet/example-email-keyword-definition.js @@ -5,6 +5,7 @@ const INTERNET_EXAMPLE_EMAIL_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'internet.exampleEmail', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates data using faker internet example email.', @@ -14,12 +15,54 @@ const INTERNET_EXAMPLE_EMAIL_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'internet.exampleEmail', + functionCall: 'internet.exampleEmail()', sampleReturnValue: 'Edwin.Dibbert@example.net', description: 'Shows the default internet.exampleEmail call.', }, + { + functionCall: 'internet.exampleEmail(firstName="Ada", lastName="Lovelace")', + sampleReturnValue: 'Ada_Lovelace0@example.net', + description: 'Shows internet.exampleEmail using firstName and lastName options.', + }, + { + functionCall: 'internet.exampleEmail(allowSpecialCharacters=true)', + sampleReturnValue: 'Edwin.Dibbert@example.net', + description: 'Shows internet.exampleEmail allowing special characters.', + }, + { + functionCall: 'internet.exampleEmail(firstName="Ada")', + sampleReturnValue: 'Ada.Gutmann9@example.net', + description: 'Shows internet.exampleEmail using an explicit first name.', + }, + { + functionCall: 'internet.exampleEmail(lastName="Lovelace")', + sampleReturnValue: 'Edwin.Lovelace9@example.net', + description: 'Shows internet.exampleEmail using an explicit last name.', + }, + ], + args: [ + { + name: 'firstName', + type: 'string', + required: false, + description: 'Optional first name to use as the basis for the email address.', + examples: ['Ada'], + }, + { + name: 'lastName', + type: 'string', + required: false, + description: 'Optional last name to use as the basis for the email address.', + examples: ['Lovelace'], + }, + { + name: 'allowSpecialCharacters', + type: 'boolean', + required: false, + description: 'Whether special characters are allowed in the generated email address.', + examples: [true], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js b/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js index de5af63d..e38a1ac5 100644 --- a/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js +++ b/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js @@ -5,6 +5,7 @@ const INTERNET_HTTP_STATUS_CODE_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'internet.httpStatusCode', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a random HTTP status code.', @@ -14,12 +15,26 @@ const INTERNET_HTTP_STATUS_CODE_KEYWORD_DEFINITION = { returnType: 'number', usageExamples: [ { - functionCall: 'internet.httpStatusCode', + functionCall: 'internet.httpStatusCode()', sampleReturnValue: 306, description: 'Shows the default internet.httpStatusCode call.', }, + { + functionCall: 'internet.httpStatusCode(types=["success"])', + sampleReturnValue: 204, + description: 'Shows internet.httpStatusCode constrained to success status codes.', + }, + ], + args: [ + { + name: 'types', + type: 'array', + required: false, + description: + 'HTTP status categories to choose from. Allowed categories are informational, success, redirection, clientError, and serverError.', + examples: [['success']], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/location/zip-code-keyword-definition.js b/packages/core/js/keywords/domain/location/zip-code-keyword-definition.js index fb5b1ae1..a3fade4e 100644 --- a/packages/core/js/keywords/domain/location/zip-code-keyword-definition.js +++ b/packages/core/js/keywords/domain/location/zip-code-keyword-definition.js @@ -5,6 +5,7 @@ const LOCATION_ZIP_CODE_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'location.zipCode', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates data using faker location zip code.', @@ -14,12 +15,33 @@ const LOCATION_ZIP_CODE_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'location.zipCode', + functionCall: 'location.zipCode()', sampleReturnValue: '70310', description: 'Shows the default location.zipCode call.', }, + { + functionCall: 'location.zipCode(format="#####")', + sampleReturnValue: '47031', + description: 'Shows location.zipCode using a format option.', + }, + ], + args: [ + { + name: 'state', + type: 'string', + required: false, + usageExampleSupported: false, + description: 'State abbreviation used to constrain the generated zip code where supported by the locale.', + examples: ['CA'], + }, + { + name: 'format', + type: 'string', + required: false, + description: 'Format pattern used for the generated zip code.', + examples: ['#####'], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/lorem/word-keyword-definition.js b/packages/core/js/keywords/domain/lorem/word-keyword-definition.js index e243cb92..55cbe6a8 100644 --- a/packages/core/js/keywords/domain/lorem/word-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/word-keyword-definition.js @@ -21,16 +21,6 @@ const LOREM_WORD_KEYWORD_DEFINITION = { sampleReturnValue: 'cur', description: 'Shows lorem.word when optional params are omitted.', }, - { - functionCall: 'lorem.word(max=10, min=1)', - sampleReturnValue: 'cur', - description: 'Shows lorem.word using min.', - }, - { - functionCall: 'lorem.word(max=5)', - sampleReturnValue: 'cur', - description: 'Shows lorem.word using max.', - }, { functionCall: 'lorem.word(length=5)', sampleReturnValue: 'curvo', @@ -43,18 +33,6 @@ const LOREM_WORD_KEYWORD_DEFINITION = { }, ], args: [ - { - name: 'min', - type: 'number', - required: false, - description: 'Minimum word length when generating a ranged length.', - }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum word length when generating a ranged length.', - }, { name: 'length', type: 'number', diff --git a/packages/core/js/keywords/domain/number/big-int-keyword-definition.js b/packages/core/js/keywords/domain/number/big-int-keyword-definition.js index 9aa23184..6d5e65ac 100644 --- a/packages/core/js/keywords/domain/number/big-int-keyword-definition.js +++ b/packages/core/js/keywords/domain/number/big-int-keyword-definition.js @@ -1,4 +1,19 @@ import { validateIntegerValue } from '../../../command-help/command-help-validators.js'; +import { + composeArgsValidators, + createOrderedArgsValidator, + createNumericArgRangeValidator, +} from '../../../domain/domain-keyword-arg-validators.js'; + +const validateBigIntBounds = composeArgsValidators( + createOrderedArgsValidator({ lowerName: 'min', upperName: 'max' }), + createNumericArgRangeValidator({ + argName: 'multipleOf', + min: 0, + inclusiveMin: false, + description: 'Invalid keyword arguments: argument "multipleOf" must be greater than 0', + }) +); const NUMBER_BIG_INT_KEYWORD_DEFINITION = { keyword: 'number.bigInt', @@ -12,6 +27,7 @@ const NUMBER_BIG_INT_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/number', fakerDocsUrl: 'https://fakerjs.dev/api/number', validator: validateIntegerValue, + argsValidator: validateBigIntBounds, returnType: 'integer', usageExamples: [ { @@ -20,19 +36,42 @@ const NUMBER_BIG_INT_KEYWORD_DEFINITION = { description: 'Shows number.bigInt with all optional params omitted.', }, { - functionCall: 'number.bigInt(value=true)', - sampleReturnValue: 703101335462806n, - description: 'Shows number.bigInt using a boolean base value.', + functionCall: 'number.bigInt(min=100, max=1000)', + sampleReturnValue: 570n, + description: 'Shows number.bigInt using min and max bounds.', + }, + { + functionCall: 'number.bigInt(multipleOf=7)', + sampleReturnValue: 292170934823957n, + description: 'Shows number.bigInt constrained to a multiple.', + }, + { + functionCall: 'number.bigInt(max=1000)', + sampleReturnValue: 699n, + description: 'Shows number.bigInt using an upper bound.', }, ], args: [ { - name: 'value', + name: 'min', + type: 'bigint|number|string|boolean', + required: false, + description: 'Optional minimum bound for the generated BigInt value.', + examples: [100], + }, + { + name: 'max', + type: 'bigint|number|string|boolean', + required: false, + description: 'Optional maximum bound for the generated BigInt value.', + examples: [1000], + }, + { + name: 'multipleOf', type: 'bigint|number|string|boolean', required: false, - description: - 'Base value used for generation. Supports bigint, number, string, or boolean inputs. For range constraints use min, max, and multipleOf.', - examples: [true], + description: 'Generated BigInt will be a multiple of the given value.', + examples: [7], }, ], }, diff --git a/packages/core/js/keywords/domain/person/full-name-keyword-definition.js b/packages/core/js/keywords/domain/person/full-name-keyword-definition.js index 93bfe1e2..09abd58d 100644 --- a/packages/core/js/keywords/domain/person/full-name-keyword-definition.js +++ b/packages/core/js/keywords/domain/person/full-name-keyword-definition.js @@ -5,6 +5,7 @@ const PERSON_FULL_NAME_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'person.fullName', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Generates a random full name.', @@ -14,12 +15,54 @@ const PERSON_FULL_NAME_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'person.fullName', + functionCall: 'person.fullName()', sampleReturnValue: 'Aaliyah Corkery', description: 'Shows the default person.fullName call.', }, + { + functionCall: 'person.fullName(firstName="Ada", lastName="Lovelace", sex="female")', + sampleReturnValue: 'Ada Lovelace', + description: 'Shows person.fullName using name and sex options.', + }, + { + functionCall: 'person.fullName(firstName="Ada")', + sampleReturnValue: 'Ada Abbott', + description: 'Shows person.fullName using an explicit first name.', + }, + { + functionCall: 'person.fullName(lastName="Lovelace")', + sampleReturnValue: 'Aaliyah Lovelace', + description: 'Shows person.fullName using an explicit last name.', + }, + { + functionCall: 'person.fullName(sex="female")', + sampleReturnValue: 'Monique Gutmann', + description: 'Shows person.fullName using a sex category.', + }, + ], + args: [ + { + name: 'firstName', + type: 'string', + required: false, + description: 'Optional first name to use as the basis for the full name.', + examples: ['Ada'], + }, + { + name: 'lastName', + type: 'string', + required: false, + description: 'Optional last name to use as the basis for the full name.', + examples: ['Lovelace'], + }, + { + name: 'sex', + type: 'female|generic|male', + required: false, + description: 'Sex category used for the generated full name.', + examples: ['female'], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/person/sex-type-keyword-definition.js b/packages/core/js/keywords/domain/person/sex-type-keyword-definition.js index d6aaeb96..5167068e 100644 --- a/packages/core/js/keywords/domain/person/sex-type-keyword-definition.js +++ b/packages/core/js/keywords/domain/person/sex-type-keyword-definition.js @@ -1,6 +1,6 @@ import { createStringEnumValidator } from '../../../command-help/command-help-validators.js'; -const PERSON_SEX_TYPE = 'female|male'; +const PERSON_SEX_TYPE = 'female|generic|male'; const validatePersonSexTypeValue = createStringEnumValidator(PERSON_SEX_TYPE.split('|')); @@ -9,6 +9,7 @@ const PERSON_SEX_TYPE_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'person.sexType', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Returns a random sex type. The `SexType` is intended to be used in parameters and conditions.', @@ -18,12 +19,25 @@ const PERSON_SEX_TYPE_KEYWORD_DEFINITION = { returnType: PERSON_SEX_TYPE, usageExamples: [ { - functionCall: 'person.sexType', + functionCall: 'person.sexType()', sampleReturnValue: 'female', description: 'Shows the default person.sexType call.', }, + { + functionCall: 'person.sexType(includeGeneric=false)', + sampleReturnValue: 'female', + description: 'Shows person.sexType excluding the generic value.', + }, + ], + args: [ + { + name: 'includeGeneric', + type: 'boolean', + required: false, + description: 'Whether the generic sex type can be returned.', + examples: [false], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/system/file-name-keyword-definition.js b/packages/core/js/keywords/domain/system/file-name-keyword-definition.js index 45a64bdf..6df54997 100644 --- a/packages/core/js/keywords/domain/system/file-name-keyword-definition.js +++ b/packages/core/js/keywords/domain/system/file-name-keyword-definition.js @@ -5,6 +5,7 @@ const SYSTEM_FILE_NAME_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'system.fileName', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Returns a random file name with extension.', @@ -14,12 +15,25 @@ const SYSTEM_FILE_NAME_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'system.fileName', + functionCall: 'system.fileName()', sampleReturnValue: 'fog_aboard.otf', description: 'Shows the default system.fileName call.', }, + { + functionCall: 'system.fileName(extensionCount=2)', + sampleReturnValue: 'fog_aboard.otf.7z', + description: 'Shows system.fileName using extensionCount.', + }, + ], + args: [ + { + name: 'extensionCount', + type: 'number', + required: false, + description: 'How many extensions the generated file name should have.', + examples: [2], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/system/network-interface-keyword-definition.js b/packages/core/js/keywords/domain/system/network-interface-keyword-definition.js index 53362a3a..1db234ea 100644 --- a/packages/core/js/keywords/domain/system/network-interface-keyword-definition.js +++ b/packages/core/js/keywords/domain/system/network-interface-keyword-definition.js @@ -5,6 +5,7 @@ const SYSTEM_NETWORK_INTERFACE_KEYWORD_DEFINITION = { delegate: { type: 'faker', target: 'system.networkInterface', + argTransform: 'optionsFromHelpArgs', }, help: { summary: 'Returns a random network interface.', @@ -14,12 +15,42 @@ const SYSTEM_NETWORK_INTERFACE_KEYWORD_DEFINITION = { returnType: 'string', usageExamples: [ { - functionCall: 'system.networkInterface', + functionCall: 'system.networkInterface()', sampleReturnValue: 'wlx042125686a3e', description: 'Shows the default system.networkInterface call.', }, + { + functionCall: 'system.networkInterface(interfaceType="en", interfaceSchema="mac")', + sampleReturnValue: 'enx6b042125686a', + description: 'Shows system.networkInterface using interface type and schema options.', + }, + { + functionCall: 'system.networkInterface(interfaceType="en")', + sampleReturnValue: 'ens7f3d0', + description: 'Shows system.networkInterface using an explicit interface type.', + }, + { + functionCall: 'system.networkInterface(interfaceSchema="mac")', + sampleReturnValue: 'wlxb042125686a3', + description: 'Shows system.networkInterface using an explicit interface schema.', + }, + ], + args: [ + { + name: 'interfaceType', + type: 'en|wl|ww', + required: false, + description: 'Network interface type prefix.', + examples: ['en'], + }, + { + name: 'interfaceSchema', + type: 'index|slot|mac|pci', + required: false, + description: 'Network interface naming schema.', + examples: ['mac'], + }, ], - args: [], }, }; diff --git a/packages/core/js/keywords/domain/word/adjective-keyword-definition.js b/packages/core/js/keywords/domain/word/adjective-keyword-definition.js index 4aa505ee..9a7884bc 100644 --- a/packages/core/js/keywords/domain/word/adjective-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/adjective-keyword-definition.js @@ -26,11 +26,6 @@ const WORD_ADJECTIVE_KEYWORD_DEFINITION = { sampleReturnValue: 'major', description: 'Shows word.adjective using length.', }, - { - functionCall: 'word.adjective(max=5)', - sampleReturnValue: 'inferior', - description: 'Shows word.adjective using max.', - }, { functionCall: 'word.adjective(strategy="any-length")', sampleReturnValue: 'inferior', @@ -44,12 +39,6 @@ const WORD_ADJECTIVE_KEYWORD_DEFINITION = { required: false, description: 'Desired length of the generated value.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, { name: 'strategy', type: WORD_SELECTION_STRATEGY_TYPE, diff --git a/packages/core/js/keywords/domain/word/adverb-keyword-definition.js b/packages/core/js/keywords/domain/word/adverb-keyword-definition.js index 350f0784..48149b45 100644 --- a/packages/core/js/keywords/domain/word/adverb-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/adverb-keyword-definition.js @@ -26,11 +26,6 @@ const WORD_ADVERB_KEYWORD_DEFINITION = { sampleReturnValue: 'never', description: 'Shows word.adverb using length.', }, - { - functionCall: 'word.adverb(max=5)', - sampleReturnValue: 'knavishly', - description: 'Shows word.adverb using max.', - }, { functionCall: 'word.adverb(strategy="any-length")', sampleReturnValue: 'knavishly', @@ -44,12 +39,6 @@ const WORD_ADVERB_KEYWORD_DEFINITION = { required: false, description: 'Desired length of the generated value.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, { name: 'strategy', type: WORD_SELECTION_STRATEGY_TYPE, diff --git a/packages/core/js/keywords/domain/word/conjunction-keyword-definition.js b/packages/core/js/keywords/domain/word/conjunction-keyword-definition.js index abe5a25b..464b2bd3 100644 --- a/packages/core/js/keywords/domain/word/conjunction-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/conjunction-keyword-definition.js @@ -26,11 +26,6 @@ const WORD_CONJUNCTION_KEYWORD_DEFINITION = { sampleReturnValue: 'until', description: 'Shows word.conjunction using length.', }, - { - functionCall: 'word.conjunction(max=5)', - sampleReturnValue: 'likewise', - description: 'Shows word.conjunction using max.', - }, { functionCall: 'word.conjunction(strategy="any-length")', sampleReturnValue: 'likewise', @@ -44,12 +39,6 @@ const WORD_CONJUNCTION_KEYWORD_DEFINITION = { required: false, description: 'Desired length of the generated value.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, { name: 'strategy', type: WORD_SELECTION_STRATEGY_TYPE, diff --git a/packages/core/js/keywords/domain/word/interjection-keyword-definition.js b/packages/core/js/keywords/domain/word/interjection-keyword-definition.js index c8d38511..bc4c00eb 100644 --- a/packages/core/js/keywords/domain/word/interjection-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/interjection-keyword-definition.js @@ -26,11 +26,6 @@ const WORD_INTERJECTION_KEYWORD_DEFINITION = { sampleReturnValue: 'fooey', description: 'Shows word.interjection using length.', }, - { - functionCall: 'word.interjection(max=5)', - sampleReturnValue: 'woot', - description: 'Shows word.interjection using max.', - }, { functionCall: 'word.interjection(strategy="any-length")', sampleReturnValue: 'woot', @@ -44,12 +39,6 @@ const WORD_INTERJECTION_KEYWORD_DEFINITION = { required: false, description: 'Desired length of the generated value.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, { name: 'strategy', type: WORD_SELECTION_STRATEGY_TYPE, diff --git a/packages/core/js/keywords/domain/word/noun-keyword-definition.js b/packages/core/js/keywords/domain/word/noun-keyword-definition.js index 2d2db083..5f9fae5e 100644 --- a/packages/core/js/keywords/domain/word/noun-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/noun-keyword-definition.js @@ -26,11 +26,6 @@ const WORD_NOUN_KEYWORD_DEFINITION = { sampleReturnValue: 'humor', description: 'Shows word.noun using length.', }, - { - functionCall: 'word.noun(max=5)', - sampleReturnValue: 'heating', - description: 'Shows word.noun using max.', - }, { functionCall: 'word.noun(strategy="any-length")', sampleReturnValue: 'heating', @@ -44,12 +39,6 @@ const WORD_NOUN_KEYWORD_DEFINITION = { required: false, description: 'Desired length of the generated value.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, { name: 'strategy', type: WORD_SELECTION_STRATEGY_TYPE, diff --git a/packages/core/js/keywords/domain/word/preposition-keyword-definition.js b/packages/core/js/keywords/domain/word/preposition-keyword-definition.js index 2f17bc1d..fc4f105d 100644 --- a/packages/core/js/keywords/domain/word/preposition-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/preposition-keyword-definition.js @@ -26,11 +26,6 @@ const WORD_PREPOSITION_KEYWORD_DEFINITION = { sampleReturnValue: 'aside', description: 'Shows word.preposition using length.', }, - { - functionCall: 'word.preposition(max=5)', - sampleReturnValue: 'except', - description: 'Shows word.preposition using max.', - }, { functionCall: 'word.preposition(strategy="any-length")', sampleReturnValue: 'except', @@ -44,12 +39,6 @@ const WORD_PREPOSITION_KEYWORD_DEFINITION = { required: false, description: 'Desired length of the generated value.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, { name: 'strategy', type: WORD_SELECTION_STRATEGY_TYPE, diff --git a/packages/core/js/keywords/domain/word/sample-keyword-definition.js b/packages/core/js/keywords/domain/word/sample-keyword-definition.js index a2d2ea2f..7e089b44 100644 --- a/packages/core/js/keywords/domain/word/sample-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/sample-keyword-definition.js @@ -27,11 +27,6 @@ const WORD_SAMPLE_KEYWORD_DEFINITION = { sampleReturnValue: 'yowza', description: 'Shows word.sample using length.', }, - { - functionCall: 'word.sample(max=5)', - sampleReturnValue: 'boohoo', - description: 'Shows word.sample using max.', - }, { functionCall: 'word.sample(strategy="any-length")', sampleReturnValue: 'boohoo', @@ -45,12 +40,6 @@ const WORD_SAMPLE_KEYWORD_DEFINITION = { required: false, description: 'Desired length of the generated value.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, { name: 'strategy', type: WORD_SELECTION_STRATEGY_TYPE, diff --git a/packages/core/js/keywords/domain/word/verb-keyword-definition.js b/packages/core/js/keywords/domain/word/verb-keyword-definition.js index cdaa6bba..010952f2 100644 --- a/packages/core/js/keywords/domain/word/verb-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/verb-keyword-definition.js @@ -26,11 +26,6 @@ const WORD_VERB_KEYWORD_DEFINITION = { sampleReturnValue: 'mould', description: 'Shows word.verb using length.', }, - { - functionCall: 'word.verb(max=5)', - sampleReturnValue: 'hunger', - description: 'Shows word.verb using max.', - }, { functionCall: 'word.verb(strategy="any-length")', sampleReturnValue: 'hunger', @@ -44,12 +39,6 @@ const WORD_VERB_KEYWORD_DEFINITION = { required: false, description: 'Desired length of the generated value.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, { name: 'strategy', type: WORD_SELECTION_STRATEGY_TYPE, diff --git a/packages/core/js/keywords/domain/word/words-keyword-definition.js b/packages/core/js/keywords/domain/word/words-keyword-definition.js index c760b807..91860696 100644 --- a/packages/core/js/keywords/domain/word/words-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/words-keyword-definition.js @@ -24,11 +24,6 @@ const WORD_WORDS_KEYWORD_DEFINITION = { sampleReturnValue: 'boohoo pish tenderly above pop', description: 'Shows word.words using count.', }, - { - functionCall: 'word.words(max=5)', - sampleReturnValue: 'fog aboard', - description: 'Shows word.words using max.', - }, ], args: [ { @@ -37,12 +32,6 @@ const WORD_WORDS_KEYWORD_DEFINITION = { required: false, description: 'Number of items to generate.', }, - { - name: 'max', - type: 'number', - required: false, - description: 'Maximum bound used when generating a value.', - }, ], }, }; diff --git a/packages/core/src/tests/command-help/command-help-examples.test-support.js b/packages/core/src/tests/command-help/command-help-examples.test-support.js index f821ce16..d82f27ca 100644 --- a/packages/core/src/tests/command-help/command-help-examples.test-support.js +++ b/packages/core/src/tests/command-help/command-help-examples.test-support.js @@ -30,6 +30,14 @@ function isRequiredParam(param) { return param?.required === true || param?.optional === false; } +function isUsageExampleSupportedParam(entry, param) { + if (entry?.command === 'location.zipCode' && param?.name === 'state') { + return false; + } + + return param?.usageExampleSupported !== false; +} + function splitTopLevelArguments(argsText = '') { const args = []; let current = ''; @@ -277,5 +285,6 @@ export { getOptionalCompanionNames, isForbiddenFakerCommand, isRequiredParam, + isUsageExampleSupportedParam, normalizeExampleValue, }; diff --git a/packages/core/src/tests/command-help/command-help-examples.test.js b/packages/core/src/tests/command-help/command-help-examples.test.js index c4d6b42e..17c890c8 100644 --- a/packages/core/src/tests/command-help/command-help-examples.test.js +++ b/packages/core/src/tests/command-help/command-help-examples.test.js @@ -4,6 +4,7 @@ import { getHelperExampleArgCount, getOptionalCompanionNames, isRequiredParam, + isUsageExampleSupportedParam, } from './command-help-examples.test-support.js'; describe('command help usage examples', () => { @@ -20,8 +21,9 @@ describe('command help usage examples', () => { test('commands expose at least one usage example per optional param, plus a zero-arg example when all params are optional', () => { const missingCoverage = commandCases .map((entry) => { - const optionalCount = entry.params.filter((param) => !isRequiredParam(param)).length; - const allParamsOptional = entry.params.length > 0 && optionalCount === entry.params.length; + const exampleSupportedParams = entry.params.filter((param) => isUsageExampleSupportedParam(entry, param)); + const optionalCount = exampleSupportedParams.filter((param) => !isRequiredParam(param)).length; + const allParamsOptional = exampleSupportedParams.length > 0 && optionalCount === exampleSupportedParams.length; const minimumExampleCount = Math.max(1, optionalCount + (allParamsOptional ? 1 : 0)); return { ...entry, @@ -102,6 +104,7 @@ describe('command help usage examples', () => { const usageExamples = Array.isArray(entry.help?.usageExamples) ? entry.help.usageExamples : []; const optionalParams = entry.params .map((param, index) => ({ ...param, index })) + .filter((param) => isUsageExampleSupportedParam(entry, param)) .filter((param) => !isRequiredParam(param)); const requiredParamNames = new Set(entry.params.filter(isRequiredParam).map((param) => param.name)); const lastRequiredIndex = entry.params.reduce( diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js new file mode 100644 index 00000000..c4425f16 --- /dev/null +++ b/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js @@ -0,0 +1,37 @@ +import { pathToFileURL } from 'node:url'; +import path from 'node:path'; + +async function loadComparisonScript() { + const scriptPath = path.resolve(process.cwd(), 'scripts/compare-domain-faker-params.mjs'); + return import(pathToFileURL(scriptPath).href); +} + +describe('domain faker option param comparison', () => { + test('documents every Faker top-level options object param in domain metadata', async () => { + const { getFakerOptionParamComparison } = await loadComparisonScript(); + const comparison = getFakerOptionParamComparison(); + + expect(comparison.filter((row) => row.missingInDomain.length > 0)).toEqual([]); + }); + + test('does not expose domain params that Faker does not implement', async () => { + const { getFakerOptionParamComparison } = await loadComparisonScript(); + const comparison = getFakerOptionParamComparison(); + + expect(comparison.filter((row) => row.domainOnlyParams.length > 0)).toEqual([]); + }); + + test('number.bigInt exposes Faker range params instead of the old value placeholder', async () => { + const { getFakerOptionParamComparison } = await loadComparisonScript(); + const comparison = getFakerOptionParamComparison(); + const bigIntRow = comparison.find((row) => row.keyword === 'number.bigInt'); + + expect(bigIntRow).toEqual( + expect.objectContaining({ + fakerOptionParams: ['min', 'max', 'multipleOf'], + domainParams: ['min', 'max', 'multipleOf'], + missingInDomain: [], + }) + ); + }); +}); diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js index b60cc86a..6fae32af 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js @@ -55,7 +55,7 @@ function sampleValueForType(type) { const stringLiterals = allowed .filter( (entry) => - !['string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && + !['bigint', 'string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && !/^[+-]?\d+(\.\d+)?$/.test(entry) ) .map((entry) => @@ -69,6 +69,9 @@ function sampleValueForType(type) { } if (stringLiterals.length > 0) return stringLiterals[0]; + if (allowed.includes('bigint')) { + return 7; + } if (allowed.includes('integer')) { return 7; } @@ -102,6 +105,13 @@ function sampleValueForKeywordArg(keywordName, argName, typeName) { if (key === 'date.birthdate.max') return 65; if (key === 'date.birthdate.mode') return 'age'; if (key === 'datatype.boolean.probability') return 0.5; + if (key === 'airline.flightNumber.length') return 4; + if (key === 'image.dataUri.type') return 'svg-base64'; + if (key === 'internet.httpStatusCode.types') return ['success']; + if (key === 'location.zipCode.state') return 'CA'; + if (key === 'location.zipCode.format') return '#####'; + if (key === 'system.networkInterface.interfaceType') return 'en'; + if (key === 'system.networkInterface.interfaceSchema') return 'mac'; if (key === 'location.latitude.min' || key === 'location.longitude.min') return -10; if (key === 'location.latitude.max' || key === 'location.longitude.max') return 10; @@ -201,8 +211,7 @@ function expectsRuntimeRejection(keywordName, argName) { } function shouldSkipRuntimeExecution(keywordName, argName) { - void keywordName; - void argName; + if (keywordName === 'location.zipCode' && argName === 'state') return true; return false; } diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-parser.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-parser.test.js index bd467068..315fdc87 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-parser.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-parser.test.js @@ -88,11 +88,11 @@ describe('domain keyword parser', () => { expect(parsedKeyword.errors).toEqual([]); }); - test('parses lorem.word(min=2, max=67) as a valid domain invocation', () => { + test('rejects lorem.word params that Faker does not implement', () => { const parsedKeyword = parseKeywordInvocation('lorem.word(min=2, max=67)'); expect(parsedKeyword.keyword).toBe('lorem.word'); - expect(parsedKeyword.args).toEqual([2, 67]); - expect(parsedKeyword.errors).toEqual([]); + expect(parsedKeyword.args).toEqual([]); + expect(parsedKeyword.errors).toEqual(['Invalid keyword arguments: unknown named argument "min"']); }); test('parses named args with broader spacing combinations', () => { diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-param-invocation-coverage.test-helper.js b/packages/core/src/tests/data_generation/unit/domain/domain-param-invocation-coverage.test-helper.js index b7726e7f..d5e8d630 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-param-invocation-coverage.test-helper.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-param-invocation-coverage.test-helper.js @@ -23,7 +23,7 @@ function sampleValueForType(typeName) { const stringLiterals = types .filter( (entry) => - !['string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && + !['bigint', 'string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && !/^[+-]?\d+(\.\d+)?$/.test(entry) ) .map((entry) => @@ -32,7 +32,7 @@ function sampleValueForType(typeName) { : entry ); - if (types.includes('number') || types.includes('integer')) { + if (types.includes('number') || types.includes('integer') || types.includes('bigint')) { return 7; } if (stringLiterals.length > 0) { @@ -58,6 +58,13 @@ function sampleValueForKeywordArg(keywordName, argName, typeName) { if (argName === 'from') return 1577836800000; if (argName === 'to') return 1580428800000; if (argName === 'refDate') return 1716110400000; + if (key === 'airline.flightNumber.length') return 4; + if (key === 'image.dataUri.type') return 'svg-base64'; + if (key === 'internet.httpStatusCode.types') return ['success']; + if (key === 'location.zipCode.state') return 'CA'; + if (key === 'location.zipCode.format') return '#####'; + if (key === 'system.networkInterface.interfaceType') return 'en'; + if (key === 'system.networkInterface.interfaceSchema') return 'mac'; if (argName === 'version') return 7; if (argName === 'count') return 3; if (argName === 'min') return 1; diff --git a/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js b/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js index e8ad3ac1..69e5e674 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js @@ -562,7 +562,7 @@ function sampleValueForType(type) { const stringLiterals = allowed .filter( (entry) => - !['string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && + !['bigint', 'string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && !/^[+-]?\d+(\.\d+)?$/.test(entry) ) .map((entry) => @@ -576,6 +576,7 @@ function sampleValueForType(type) { } if (stringLiterals.length > 0) return stringLiterals[0]; + if (allowed.includes('bigint')) return 7; if (allowed.includes('integer')) return 7; if (allowed.includes('number')) return 7; if (allowed.includes('regexp')) return '[A-Z]'; diff --git a/scripts/compare-domain-faker-params.mjs b/scripts/compare-domain-faker-params.mjs new file mode 100644 index 00000000..d7cacff3 --- /dev/null +++ b/scripts/compare-domain-faker-params.mjs @@ -0,0 +1,241 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { createRequire } from 'node:module'; +import { fileURLToPath } from 'node:url'; +import { DOMAIN_KEYWORDS } from '../packages/core/js/domain/domain-keywords.js'; + +const require = createRequire(import.meta.url); + +function findMatching(source, openIndex, openChar, closeChar) { + let depth = 0; + for (let index = openIndex; index < source.length; index += 1) { + if (source[index] === openChar) { + depth += 1; + } + if (source[index] === closeChar) { + depth -= 1; + if (depth === 0) { + return index; + } + } + } + return -1; +} + +function getTopLevelObjectKeys(objectBody) { + const keys = []; + let depth = 0; + let tokenStart = 0; + const normalizedBody = objectBody.replace(/\/\*[\s\S]*?\*\//g, ' '); + + for (let index = 0; index <= normalizedBody.length; index += 1) { + const character = normalizedBody[index] || ';'; + if (character === '{' || character === '(' || character === '[' || character === '<') { + depth += 1; + } + if (character === '}' || character === ')' || character === ']' || character === '>') { + depth = Math.max(0, depth - 1); + } + if ((character === ';' || character === ',') && depth === 0) { + const segment = normalizedBody.slice(tokenStart, index).trim(); + const match = segment.match(/^([A-Za-z_$][\w$]*)\??\s*:/); + if (match) { + keys.push(match[1]); + } + tokenStart = index + 1; + } + } + + return [...new Set(keys)]; +} + +function getObjectOptionKeys(optionsSignature) { + const keys = []; + for (let index = 0; index < optionsSignature.length; index += 1) { + if (optionsSignature[index] !== '{') { + continue; + } + const objectEnd = findMatching(optionsSignature, index, '{', '}'); + if (objectEnd < 0) { + continue; + } + keys.push(...getTopLevelObjectKeys(optionsSignature.slice(index + 1, objectEnd))); + index = objectEnd; + } + return [...new Set(keys)]; +} + +function getFakerDeclarationPath() { + const packageJsonPath = require.resolve('@faker-js/faker/package.json'); + const distDir = path.join(path.dirname(packageJsonPath), 'dist'); + + for (const fileName of fs.readdirSync(distDir)) { + if (!fileName.endsWith('.d.ts') || fileName === 'index.d.ts') { + continue; + } + const filePath = path.join(distDir, fileName); + const contents = fs.readFileSync(filePath, 'utf8'); + if (contents.includes('declare class NumberModule')) { + return filePath; + } + } + + throw new Error('Unable to find Faker declaration bundle with module method signatures.'); +} + +function extractFakerOptionMethods(declarationText) { + const modules = new Map(); + const classRegex = /declare class (\w+)Module\b/g; + let classMatch = classRegex.exec(declarationText); + + while (classMatch) { + const className = classMatch[1]; + const classOpen = declarationText.indexOf('{', classMatch.index); + if (classOpen < 0) { + classRegex.lastIndex = classMatch.index + classMatch[0].length; + classMatch = classRegex.exec(declarationText); + continue; + } + const classClose = findMatching(declarationText, classOpen, '{', '}'); + if (classClose < 0) { + classRegex.lastIndex = classMatch.index + classMatch[0].length; + classMatch = classRegex.exec(declarationText); + continue; + } + + const body = declarationText.slice(classOpen + 1, classClose); + const methods = new Map(); + const methodStartRegex = /^\s*(\w+)\s*(?:<[^\n;]+>)?\s*\(/gm; + let methodMatch = methodStartRegex.exec(body); + + while (methodMatch) { + const methodName = methodMatch[1]; + const paramsOpen = body.indexOf('(', methodMatch.index); + const paramsClose = findMatching(body, paramsOpen, '(', ')'); + if (paramsClose < 0) { + methodMatch = methodStartRegex.exec(body); + continue; + } + + const paramsText = body + .slice(paramsOpen + 1, paramsClose) + .replace(/\s+/g, ' ') + .trim(); + const optionsIndex = paramsText.indexOf('options?:'); + if (optionsIndex < 0) { + methodMatch = methodStartRegex.exec(body); + continue; + } + + const optionsSignature = paramsText.slice(optionsIndex + 'options?:'.length).trim(); + methods.set(methodName, { + signature: optionsSignature, + optionParams: getObjectOptionKeys(optionsSignature), + }); + methodMatch = methodStartRegex.exec(body); + } + + const moduleName = className.charAt(0).toLowerCase() + className.slice(1); + modules.set(moduleName, methods); + classRegex.lastIndex = classClose + 1; + classMatch = classRegex.exec(declarationText); + } + + return modules; +} + +function getFakerOptionParamComparison({ declarationPath = getFakerDeclarationPath() } = {}) { + const declarationText = fs.readFileSync(declarationPath, 'utf8'); + const fakerMethods = extractFakerOptionMethods(declarationText); + + return DOMAIN_KEYWORDS.filter((keyword) => keyword.delegate?.type === 'faker') + .map((keyword) => { + const [moduleName, methodName] = String(keyword.delegate.target || '').split('.'); + const method = fakerMethods.get(moduleName)?.get(methodName); + if (!method || method.optionParams.length === 0) { + return null; + } + + const domainParams = (keyword.help?.args || []) + .map((arg) => String(arg?.name || '').trim()) + .filter((name) => name.length > 0); + const domainParamSet = new Set(domainParams); + const fakerParamSet = new Set(method.optionParams); + + return { + keyword: keyword.keyword, + fakerTarget: keyword.delegate.target, + fakerOptionParams: method.optionParams, + domainParams, + missingInDomain: method.optionParams.filter((param) => !domainParamSet.has(param)), + domainOnlyParams: domainParams.filter((param) => !fakerParamSet.has(param)), + fakerOptionsSignature: method.signature, + }; + }) + .filter(Boolean) + .sort((left, right) => left.keyword.localeCompare(right.keyword)); +} + +function formatList(values) { + return values.length > 0 ? values.map((value) => `\`${value}\``).join(', ') : 'none'; +} + +function formatMarkdownComparisonReport(rows = getFakerOptionParamComparison()) { + const missingRows = rows.filter((row) => row.missingInDomain.length > 0); + const domainOnlyRows = rows.filter((row) => row.domainOnlyParams.length > 0); + + const lines = [ + '# Domain/Faker Param Comparison', + '', + 'Generated by `node scripts/compare-domain-faker-params.mjs --markdown` from the installed `@faker-js/faker` declaration bundle and the domain keyword catalog.', + '', + '## Summary', + '', + `- Faker option-backed domain commands compared: ${rows.length}`, + `- Commands with Faker option params missing from domain metadata: ${missingRows.length}`, + `- Commands with domain params not present in Faker options: ${domainOnlyRows.length}`, + '', + '## Comparison', + '', + '| Command | Faker option params | Domain params | Missing in domain | Domain-only params |', + '| --- | --- | --- | --- | --- |', + ]; + + for (const row of rows) { + lines.push( + `| \`${row.keyword}\` | ${formatList(row.fakerOptionParams)} | ${formatList(row.domainParams)} | ${formatList(row.missingInDomain)} | ${formatList(row.domainOnlyParams)} |` + ); + } + + lines.push(''); + lines.push('## Review Notes'); + lines.push(''); + lines.push('- `Missing in domain` must stay at `none` for every row.'); + lines.push('- `Domain-only params` must stay at `none` for every Faker-backed domain command.'); + lines.push(''); + + return `${lines.join('\n')}\n`; +} + +function runCli() { + const args = process.argv.slice(2); + const rows = getFakerOptionParamComparison(); + const missingRows = rows.filter((row) => row.missingInDomain.length > 0); + + if (args.includes('--markdown')) { + process.stdout.write(formatMarkdownComparisonReport(rows)); + } else { + process.stdout.write(`${JSON.stringify(rows, null, 2)}\n`); + } + + if (args.includes('--check') && missingRows.length > 0) { + process.exitCode = 1; + } +} + +const isCli = fileURLToPath(import.meta.url) === path.resolve(process.argv[1] || ''); +if (isCli) { + runCli(); +} + +export { formatMarkdownComparisonReport, getFakerOptionParamComparison }; From f63eb15fb7f312dcc87f907daa5ac9b178828ea6 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 1 Jul 2026 16:57:10 +0100 Subject: [PATCH 2/5] Narrow bigInt command arg types --- docs-src/docs/040-test-data/domain/220-number.md | 6 +++--- .../js/keywords/domain/number/big-int-keyword-definition.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs-src/docs/040-test-data/domain/220-number.md b/docs-src/docs/040-test-data/domain/220-number.md index 3de6c2d0..0a785c57 100644 --- a/docs-src/docs/040-test-data/domain/220-number.md +++ b/docs-src/docs/040-test-data/domain/220-number.md @@ -23,9 +23,9 @@ Returns a BigInt number. | Arg | Type | Required | Description | | --- | --- | --- | --- | -| `min` | `bigint\|number\|string\|boolean` | no | Optional minimum bound for the generated BigInt value. | -| `max` | `bigint\|number\|string\|boolean` | no | Optional maximum bound for the generated BigInt value. | -| `multipleOf` | `bigint\|number\|string\|boolean` | no | Generated BigInt will be a multiple of the given value. | +| `min` | `integer` | no | Optional minimum bound for the generated BigInt value. | +| `max` | `integer` | no | Optional maximum bound for the generated BigInt value. | +| `multipleOf` | `integer` | no | Generated BigInt will be a multiple of the given value. | Examples: diff --git a/packages/core/js/keywords/domain/number/big-int-keyword-definition.js b/packages/core/js/keywords/domain/number/big-int-keyword-definition.js index 6d5e65ac..b362e5fe 100644 --- a/packages/core/js/keywords/domain/number/big-int-keyword-definition.js +++ b/packages/core/js/keywords/domain/number/big-int-keyword-definition.js @@ -54,21 +54,21 @@ const NUMBER_BIG_INT_KEYWORD_DEFINITION = { args: [ { name: 'min', - type: 'bigint|number|string|boolean', + type: 'integer', required: false, description: 'Optional minimum bound for the generated BigInt value.', examples: [100], }, { name: 'max', - type: 'bigint|number|string|boolean', + type: 'integer', required: false, description: 'Optional maximum bound for the generated BigInt value.', examples: [1000], }, { name: 'multipleOf', - type: 'bigint|number|string|boolean', + type: 'integer', required: false, description: 'Generated BigInt will be a multiple of the given value.', examples: [7], From f47a6e9b53ce30dcd2be5b9ba20dc50b49ea5d16 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 1 Jul 2026 18:55:07 +0100 Subject: [PATCH 3/5] Address command param review feedback --- .../command-help-examples.test.js | 3 +- .../domain-faker-param-comparison.test.js | 29 +++++++++++++++++++ scripts/compare-domain-faker-params.mjs | 9 ++++-- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/core/src/tests/command-help/command-help-examples.test.js b/packages/core/src/tests/command-help/command-help-examples.test.js index 17c890c8..8bd87265 100644 --- a/packages/core/src/tests/command-help/command-help-examples.test.js +++ b/packages/core/src/tests/command-help/command-help-examples.test.js @@ -102,6 +102,7 @@ describe('command help usage examples', () => { test('each optional parameter has a focused usage example and all-optional commands include a zero-arg example', () => { const failures = commandCases.flatMap((entry) => { const usageExamples = Array.isArray(entry.help?.usageExamples) ? entry.help.usageExamples : []; + const exampleSupportedParams = entry.params.filter((param) => isUsageExampleSupportedParam(entry, param)); const optionalParams = entry.params .map((param, index) => ({ ...param, index })) .filter((param) => isUsageExampleSupportedParam(entry, param)) @@ -113,7 +114,7 @@ describe('command help usage examples', () => { ); const entryFailures = []; - if (entry.params.length > 0 && optionalParams.length === entry.params.length) { + if (optionalParams.length > 0 && optionalParams.length === exampleSupportedParams.length) { const hasZeroArgExample = usageExamples.some((usageExample) => { if (entry.command.startsWith('helpers.')) { return getHelperExampleArgCount(usageExample.functionCall) === 0; diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js index c4425f16..d146cff8 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js @@ -21,6 +21,35 @@ describe('domain faker option param comparison', () => { expect(comparison.filter((row) => row.domainOnlyParams.length > 0)).toEqual([]); }); + test('check failures include params missing in either direction', async () => { + const { hasParamComparisonFailures } = await loadComparisonScript(); + + expect( + hasParamComparisonFailures([ + { + missingInDomain: [], + domainOnlyParams: ['legacyOnly'], + }, + ]) + ).toBe(true); + expect( + hasParamComparisonFailures([ + { + missingInDomain: ['fakerOnly'], + domainOnlyParams: [], + }, + ]) + ).toBe(true); + expect( + hasParamComparisonFailures([ + { + missingInDomain: [], + domainOnlyParams: [], + }, + ]) + ).toBe(false); + }); + test('number.bigInt exposes Faker range params instead of the old value placeholder', async () => { const { getFakerOptionParamComparison } = await loadComparisonScript(); const comparison = getFakerOptionParamComparison(); diff --git a/scripts/compare-domain-faker-params.mjs b/scripts/compare-domain-faker-params.mjs index d7cacff3..32404cf7 100644 --- a/scripts/compare-domain-faker-params.mjs +++ b/scripts/compare-domain-faker-params.mjs @@ -217,10 +217,13 @@ function formatMarkdownComparisonReport(rows = getFakerOptionParamComparison()) return `${lines.join('\n')}\n`; } +function hasParamComparisonFailures(rows = []) { + return rows.some((row) => row.missingInDomain.length > 0 || row.domainOnlyParams.length > 0); +} + function runCli() { const args = process.argv.slice(2); const rows = getFakerOptionParamComparison(); - const missingRows = rows.filter((row) => row.missingInDomain.length > 0); if (args.includes('--markdown')) { process.stdout.write(formatMarkdownComparisonReport(rows)); @@ -228,7 +231,7 @@ function runCli() { process.stdout.write(`${JSON.stringify(rows, null, 2)}\n`); } - if (args.includes('--check') && missingRows.length > 0) { + if (args.includes('--check') && hasParamComparisonFailures(rows)) { process.exitCode = 1; } } @@ -238,4 +241,4 @@ if (isCli) { runCli(); } -export { formatMarkdownComparisonReport, getFakerOptionParamComparison }; +export { formatMarkdownComparisonReport, getFakerOptionParamComparison, hasParamComparisonFailures }; From 66cb6a4a8cf8dfc966d143654fa89f9baf57faa7 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 1 Jul 2026 20:03:45 +0100 Subject: [PATCH 4/5] Address command param review feedback --- ...chema-interaction-scenario-builder.test.js | 6 +- packages/core/js/domain/domain-keywords.js | 3 + .../faker/faker-helper-keyword-definitions.js | 3 + .../http-status-code-keyword-definition.js | 25 +++ .../command-help-examples.test-support.js | 6 +- .../domain-faker-param-comparison.test.js | 18 +++ .../domain-keyword-params-usage.test.js | 130 +-------------- ...omain-keyword-sample-values.test-helper.js | 153 ++++++++++++++++++ ...n-param-invocation-coverage.test-helper.js | 92 +---------- .../unit/domain/domainKeywords.test.js | 62 ++----- scripts/compare-domain-faker-params.mjs | 51 ++++-- 11 files changed, 261 insertions(+), 288 deletions(-) create mode 100644 packages/core/src/tests/data_generation/unit/domain/domain-keyword-sample-values.test-helper.js diff --git a/packages/core-ui/src/tests/interaction/matrix/schema-interaction-scenario-builder.test.js b/packages/core-ui/src/tests/interaction/matrix/schema-interaction-scenario-builder.test.js index 9de1cd3b..6f9203b3 100644 --- a/packages/core-ui/src/tests/interaction/matrix/schema-interaction-scenario-builder.test.js +++ b/packages/core-ui/src/tests/interaction/matrix/schema-interaction-scenario-builder.test.js @@ -16,8 +16,8 @@ import { getScenarioExecutionStatus, } from './support/schema-interaction-scenario-builder.js'; -function isScenarioArgCoverageRequired(command, argName) { - return !(command === 'location.zipCode' && argName === 'state'); +function isScenarioArgCoverageRequired(arg) { + return arg?.usageExampleSupported !== false; } describe('schema interaction scenario builder', () => { @@ -87,7 +87,7 @@ describe('schema interaction scenario builder', () => { const bucket = byCommand.get(`domain:${command}`); expect(bucket.scenarios.length).toBeGreaterThan(0); (metadata.args || []) - .filter((arg) => isScenarioArgCoverageRequired(command, arg.name)) + .filter((arg) => isScenarioArgCoverageRequired(arg)) .forEach((arg) => { expect(bucket.coveredArgs.has(arg.name)).toBe(true); }); diff --git a/packages/core/js/domain/domain-keywords.js b/packages/core/js/domain/domain-keywords.js index 01ecd589..5bbeea23 100644 --- a/packages/core/js/domain/domain-keywords.js +++ b/packages/core/js/domain/domain-keywords.js @@ -121,6 +121,9 @@ function buildDomainKeywordCatalog(definitions = DOMAIN_KEYWORD_DEFINITIONS) { required: arg?.required === true, optional: arg?.optional === true || arg?.required === false, variadic: arg?.variadic === true, + ...(Object.prototype.hasOwnProperty.call(arg || {}, 'usageExampleSupported') + ? { usageExampleSupported: arg.usageExampleSupported !== false } + : {}), description: String(arg?.description || '').trim(), example: String(arg?.example || '').trim(), ...(Object.prototype.hasOwnProperty.call(arg || {}, 'defaultValue') diff --git a/packages/core/js/faker/faker-helper-keyword-definitions.js b/packages/core/js/faker/faker-helper-keyword-definitions.js index aed8925b..0960e157 100644 --- a/packages/core/js/faker/faker-helper-keyword-definitions.js +++ b/packages/core/js/faker/faker-helper-keyword-definitions.js @@ -107,6 +107,9 @@ function mapDomainKeywordHelpToFakerCommandHelp(commandHelp) { type: arg.type, description: arg.description || '', examples: Array.isArray(arg.examples) ? arg.examples : [], + ...(Object.prototype.hasOwnProperty.call(arg || {}, 'usageExampleSupported') + ? { usageExampleSupported: arg.usageExampleSupported !== false } + : {}), })) : []; const returnType = commandHelp.returnType || ''; diff --git a/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js b/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js index e38a1ac5..bf64f06f 100644 --- a/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js +++ b/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js @@ -1,5 +1,29 @@ import { validateNumberValue } from '../../../command-help/command-help-validators.js'; +const HTTP_STATUS_CODE_TYPES = ['informational', 'success', 'redirection', 'clientError', 'serverError']; + +function validateHttpStatusCodeTypes(_args = [], context = {}) { + const types = context?.argsByName?.types; + if (typeof types === 'undefined') { + return { ok: true }; + } + if (!Array.isArray(types)) { + return { ok: true }; + } + + const invalidType = types.find((type) => !HTTP_STATUS_CODE_TYPES.includes(type)); + if (typeof invalidType !== 'undefined') { + return { + ok: false, + error: `Invalid keyword arguments: argument "types" contains unsupported value "${String( + invalidType + )}". Allowed values are ${HTTP_STATUS_CODE_TYPES.join(', ')}`, + }; + } + + return { ok: true }; +} + const INTERNET_HTTP_STATUS_CODE_KEYWORD_DEFINITION = { keyword: 'internet.httpStatusCode', delegate: { @@ -12,6 +36,7 @@ const INTERNET_HTTP_STATUS_CODE_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/internet', fakerDocsUrl: 'https://fakerjs.dev/api/internet', validator: validateNumberValue, + argsValidator: validateHttpStatusCodeTypes, returnType: 'number', usageExamples: [ { diff --git a/packages/core/src/tests/command-help/command-help-examples.test-support.js b/packages/core/src/tests/command-help/command-help-examples.test-support.js index d82f27ca..6962e0de 100644 --- a/packages/core/src/tests/command-help/command-help-examples.test-support.js +++ b/packages/core/src/tests/command-help/command-help-examples.test-support.js @@ -30,11 +30,7 @@ function isRequiredParam(param) { return param?.required === true || param?.optional === false; } -function isUsageExampleSupportedParam(entry, param) { - if (entry?.command === 'location.zipCode' && param?.name === 'state') { - return false; - } - +function isUsageExampleSupportedParam(_entry, param) { return param?.usageExampleSupported !== false; } diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js index d146cff8..7d744e96 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-faker-param-comparison.test.js @@ -50,6 +50,24 @@ describe('domain faker option param comparison', () => { ).toBe(false); }); + test('merges option params from overloaded Faker method declarations', async () => { + const { extractFakerOptionMethods } = await loadComparisonScript(); + const modules = extractFakerOptionMethods(` + declare class ExampleModule { + sample(options?: { first?: string; shared?: boolean }): string; + sample(options?: { second?: number; shared?: boolean }): string; + sample(value?: string): string; + } + `); + + expect(modules.get('example').get('sample')).toEqual( + expect.objectContaining({ + optionParams: ['first', 'shared', 'second'], + signatures: ['{ first?: string; shared?: boolean }', '{ second?: number; shared?: boolean }'], + }) + ); + }); + test('number.bigInt exposes Faker range params instead of the old value placeholder', async () => { const { getFakerOptionParamComparison } = await loadComparisonScript(); const comparison = getFakerOptionParamComparison(); diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js index 6fae32af..64b00d87 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js @@ -1,6 +1,7 @@ import { DOMAIN_KEYWORDS, executeDomainKeyword } from '../../../../../js/domain/domain-keywords.js'; import { faker } from '@faker-js/faker'; import { assertDomainKeywordResult } from './domain-result-assertions.test-helper.js'; +import { sampleValueForKeywordArg, sampleValueForType } from './domain-keyword-sample-values.test-helper.js'; function setDeepMethod(root, target, fn) { const parts = String(target || '') @@ -47,128 +48,6 @@ function createResultForPath(resultPath) { return root; } -function sampleValueForType(type) { - const allowed = String(type || '') - .split('|') - .map((entry) => entry.trim()); - const numericLiterals = allowed.filter((entry) => /^[+-]?\d+(\.\d+)?$/.test(entry)).map((entry) => Number(entry)); - const stringLiterals = allowed - .filter( - (entry) => - !['bigint', 'string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && - !/^[+-]?\d+(\.\d+)?$/.test(entry) - ) - .map((entry) => - (entry.startsWith('"') && entry.endsWith('"')) || (entry.startsWith("'") && entry.endsWith("'")) - ? entry.slice(1, -1) - : entry - ); - - if (numericLiterals.length === allowed.length && numericLiterals.length > 0) { - return numericLiterals[0]; - } - if (stringLiterals.length > 0) return stringLiterals[0]; - - if (allowed.includes('bigint')) { - return 7; - } - if (allowed.includes('integer')) { - return 7; - } - if (allowed.includes('number')) { - return 7; - } - if (allowed.includes('regexp')) { - return '[A-Z]'; - } - if (allowed.includes('boolean')) { - return true; - } - if (allowed.includes('array')) { - return ['a', 'b']; - } - if (allowed.includes('object')) { - return { key: 'value' }; - } - return 'sample'; -} - -function sampleValueForKeywordArg(keywordName, argName, typeName) { - const key = `${keywordName}.${argName}`; - const type = String(typeName || ''); - - if (key === 'date.between.from' || key === 'date.betweens.from') - return new Date('2020-01-01T00:00:00.000Z').getTime(); - if (key === 'date.between.to' || key === 'date.betweens.to') return new Date('2020-12-31T00:00:00.000Z').getTime(); - if (key === 'date.betweens.count') return 3; - if (key === 'date.birthdate.min') return 18; - if (key === 'date.birthdate.max') return 65; - if (key === 'date.birthdate.mode') return 'age'; - if (key === 'datatype.boolean.probability') return 0.5; - if (key === 'airline.flightNumber.length') return 4; - if (key === 'image.dataUri.type') return 'svg-base64'; - if (key === 'internet.httpStatusCode.types') return ['success']; - if (key === 'location.zipCode.state') return 'CA'; - if (key === 'location.zipCode.format') return '#####'; - if (key === 'system.networkInterface.interfaceType') return 'en'; - if (key === 'system.networkInterface.interfaceSchema') return 'mac'; - - if (key === 'location.latitude.min' || key === 'location.longitude.min') return -10; - if (key === 'location.latitude.max' || key === 'location.longitude.max') return 10; - if (key === 'location.latitude.precision' || key === 'location.longitude.precision') return 2; - - if (key === 'number.int.min') return 1; - if (key === 'number.int.max') return 10; - if (key === 'number.int.multipleOf') return 1; - - if (key === 'commerce.price.dec') return 2; - if (key === 'commerce.price.min') return 10; - if (key === 'commerce.price.max') return 100; - if (key === 'finance.iban.countryCode') return 'GB'; - - if (key === 'airline.seat.aircraftType') return 'narrowbody'; - if (key === 'internet.emoji.types') return ['smiley']; - if (key === 'internet.jwt.header') return { alg: 'HS256', typ: 'JWT' }; - if (key === 'internet.jwt.payload') return { iss: 'Acme' }; - if (key === 'internet.ipv4.cidrBlock') return '192.168.0.0/24'; - if (key === 'internet.ipv4.network') return 'private-a'; - if (key === 'finance.bitcoinAddress.network') return 'testnet'; - if (key === 'internet.password.pattern') return '[A-Za-z0-9]'; - if (key === 'phone.number.style') return 'human'; - if (key === 'string.alpha.casing') return 'lower'; - if (key === 'color.rgb.format') return 'hex'; - if (key === 'color.cmyk.format') return 'css'; - if (key === 'color.hsl.format') return 'css'; - if (key === 'color.hwb.format') return 'css'; - if (key === 'color.lab.format') return 'css'; - if (key === 'color.lch.format') return 'css'; - - if (argName === 'min') return 1; - if (argName === 'max') return 10; - if (argName === 'count') return 3; - if (argName === 'length') return 5; - if (argName === 'lengthMin') return 3; - if (argName === 'lengthMax') return 6; - if (argName === 'precision') return 2; - if (argName === 'dec') return 2; - if (argName === 'multipleOf') return 1; - if (key === 'commerce.upc.prefix') return '01234'; - if (argName === 'prefix') return 'pre'; - if (argName === 'symbol') return '$'; - if (argName === 'version') return 7; - if (argName === 'refDate') return Date.now(); - if (argName === 'from') return Date.now() - 86400000; - if (argName === 'to') return Date.now() + 86400000; - if (argName === 'exclude') return ['x', 'y']; - - if (type.includes('integer')) return 7; - if (type.includes('number')) return 7; - if (type.includes('regexp')) return '[A-Z]'; - if (type.includes('boolean')) return true; - if (type.includes('array')) return ['a', 'b']; - return sampleValueForType(type); -} - function buildValidArgs(keyword) { const args = new Array(keyword.help.args.length).fill(undefined); for (let index = 0; index < keyword.help.args.length; index += 1) { @@ -210,9 +89,8 @@ function expectsRuntimeRejection(keywordName, argName) { return false; } -function shouldSkipRuntimeExecution(keywordName, argName) { - if (keywordName === 'location.zipCode' && argName === 'state') return true; - return false; +function shouldSkipRuntimeExecution(argSpec) { + return argSpec?.usageExampleSupported === false; } describe('domain keyword parameter usage', () => { @@ -256,7 +134,7 @@ describe('domain keyword parameter usage', () => { const args = applyKeywordExecutionDefaults(keyword, buildValidArgs(keyword)); args[argIndex] = sampleValueForKeywordArg(keyword.keyword, argSpec.name, argSpec.type); - if (shouldSkipRuntimeExecution(keyword.keyword, argSpec.name)) { + if (shouldSkipRuntimeExecution(argSpec)) { return; } diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-sample-values.test-helper.js b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-sample-values.test-helper.js new file mode 100644 index 00000000..2a21564e --- /dev/null +++ b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-sample-values.test-helper.js @@ -0,0 +1,153 @@ +const BUILT_IN_TYPE_TOKENS = new Set([ + 'bigint', + 'string', + 'integer', + 'number', + 'date', + 'regexp', + 'boolean', + 'array', + 'object', +]); + +function splitTypeTokens(typeName) { + return String(typeName || '') + .split('|') + .map((entry) => entry.trim()) + .filter(Boolean); +} + +function unquoteLiteralToken(value) { + if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { + return value.slice(1, -1); + } + return value; +} + +function getStringLiteralTypeSamples(types) { + return types + .filter((entry) => !BUILT_IN_TYPE_TOKENS.has(entry) && !/^[+-]?\d+(\.\d+)?$/.test(entry)) + .map(unquoteLiteralToken); +} + +function sampleValueForType(typeName, { arraySample = ['x', 'y'] } = {}) { + const types = splitTypeTokens(typeName); + const numericLiterals = types.filter((entry) => /^[+-]?\d+(\.\d+)?$/.test(entry)).map((entry) => Number(entry)); + const stringLiterals = getStringLiteralTypeSamples(types); + + if (numericLiterals.length === types.length && numericLiterals.length > 0) { + return numericLiterals[0]; + } + if (stringLiterals.length > 0) return stringLiterals[0]; + + if (types.includes('bigint')) return 7; + if (types.includes('integer')) return 7; + if (types.includes('number')) return 7; + if (types.includes('regexp')) return '[A-Z]'; + if (types.includes('boolean')) return true; + if (types.includes('array')) return arraySample; + if (types.includes('object')) return { key: 'value' }; + return 'sample'; +} + +function sampleValueForKeywordArg(keywordName, argName, typeName) { + const key = `${keywordName}.${argName}`; + const type = String(typeName || ''); + + if (key === 'date.between.from' || key === 'date.betweens.from') return 1577836800000; + if (key === 'date.between.to' || key === 'date.betweens.to') return 1609372800000; + if (key === 'date.betweens.count') return 3; + if (key === 'date.birthdate.min') return 18; + if (key === 'date.birthdate.max') return 65; + if (key === 'date.birthdate.mode') return 'age'; + if (key === 'datatype.boolean.probability') return 0.5; + if (key === 'airline.flightNumber.length') return 4; + if (key === 'image.dataUri.type') return 'svg-base64'; + if (key === 'internet.httpStatusCode.types') return ['success']; + if (key === 'location.zipCode.state') return 'CA'; + if (key === 'location.zipCode.format') return '#####'; + if (key === 'system.networkInterface.interfaceType') return 'en'; + if (key === 'system.networkInterface.interfaceSchema') return 'mac'; + if (key === 'location.latitude.min' || key === 'location.longitude.min') return -10; + if (key === 'location.latitude.max' || key === 'location.longitude.max') return 10; + if (key === 'location.latitude.precision' || key === 'location.longitude.precision') return 2; + if (key === 'number.int.min' || key === 'number.bigInt.min') return 1; + if (key === 'number.int.max' || key === 'number.bigInt.max') return 10; + if (key === 'number.int.multipleOf' || key === 'number.bigInt.multipleOf') return 1; + if (key === 'commerce.price.dec') return 2; + if (key === 'commerce.price.min') return 10; + if (key === 'commerce.price.max') return 100; + if (key === 'finance.iban.countryCode') return 'GB'; + if (key === 'airline.seat.aircraftType') return 'narrowbody'; + if (key === 'internet.emoji.types') return ['smiley']; + if (key === 'internet.jwt.header') return { alg: 'HS256', typ: 'JWT' }; + if (key === 'internet.jwt.payload') return { iss: 'Acme' }; + if (key === 'internet.ipv4.cidrBlock') return '192.168.0.0/24'; + if (key === 'internet.ipv4.network') return 'private-a'; + if (key === 'finance.bitcoinAddress.network') return 'testnet'; + if (key === 'internet.password.pattern') return '[A-Za-z0-9]'; + if (key === 'phone.number.style') return 'human'; + if (key === 'string.alpha.casing') return 'lower'; + if (key === 'color.rgb.format') return 'hex'; + if (key === 'color.cmyk.format') return 'css'; + if (key === 'color.hsl.format') return 'css'; + if (key === 'color.hwb.format') return 'css'; + if (key === 'color.lab.format') return 'css'; + if (key === 'color.lch.format') return 'css'; + + if (argName === 'from') return 1577836800000; + if (argName === 'to') return 1580428800000; + if (argName === 'refDate') return 1716110400000; + if (argName === 'version') return 7; + if (argName === 'count') return 3; + if (argName === 'min') return 1; + if (argName === 'max') return 10; + if (argName === 'length') return 8; + if (argName === 'lengthMin') return 3; + if (argName === 'lengthMax') return 6; + if (argName === 'precision') return 2; + if (argName === 'dec') return 2; + if (argName === 'multipleOf') return 1; + if (key === 'commerce.upc.prefix') return '01234'; + if (argName === 'prefix') return 'pre'; + if (argName === 'symbol') return '$'; + if (argName === 'separator') return '-'; + if (argName === 'protocol') return 'https'; + if (argName === 'countryCode') return 'GB'; + if (argName === 'mimeType') return 'image/png'; + if (argName === 'cidrBlock') return '192.168.0.0/24'; + if (argName === 'types') return ['smiley']; + if (argName === 'header') return { alg: 'HS256', typ: 'JWT' }; + if (argName === 'payload') return { iss: 'Acme' }; + if (argName === 'pattern') return '[A-Z]'; + if (argName === 'mode') return 'age'; + if (argName === 'strategy') return 'any-length'; + if (argName === 'sex') return 'female'; + if (argName === 'style') return 'human'; + if (argName === 'exclude') return ['x', 'y']; + + if (type.includes('integer')) return 7; + if (type.includes('number')) return 7; + if (type.includes('regexp')) return '[A-Z]'; + if (type.includes('boolean')) return true; + if (type.includes('array')) return ['x', 'y']; + return sampleValueForType(type); +} + +function valueToInvocationLiteral(value) { + if (typeof value === 'string') { + return JSON.stringify(value); + } + if (typeof value === 'number' || typeof value === 'boolean') { + return String(value); + } + if (Array.isArray(value)) { + return JSON.stringify(value); + } + if (value && typeof value === 'object') { + return JSON.stringify(value); + } + throw new Error(`Unsupported invocation literal value: ${String(value)}`); +} + +export { sampleValueForKeywordArg, sampleValueForType, valueToInvocationLiteral }; diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-param-invocation-coverage.test-helper.js b/packages/core/src/tests/data_generation/unit/domain/domain-param-invocation-coverage.test-helper.js index d5e8d630..a2737f7f 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-param-invocation-coverage.test-helper.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-param-invocation-coverage.test-helper.js @@ -1,5 +1,6 @@ import { DOMAIN_KEYWORDS, executeDomainKeyword } from '../../../../../js/domain/domain-keywords.js'; import { parseKeywordInvocation } from '../../../../../js/domain/domain-keyword-parser.js'; +import { sampleValueForKeywordArg, valueToInvocationLiteral } from './domain-keyword-sample-values.test-helper.js'; function setDeepMethod(root, target, fn) { const parts = String(target || '') @@ -16,97 +17,6 @@ function setDeepMethod(root, target, fn) { node[parts[parts.length - 1]] = fn; } -function sampleValueForType(typeName) { - const types = String(typeName || '') - .split('|') - .map((entry) => entry.trim()); - const stringLiterals = types - .filter( - (entry) => - !['bigint', 'string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && - !/^[+-]?\d+(\.\d+)?$/.test(entry) - ) - .map((entry) => - (entry.startsWith('"') && entry.endsWith('"')) || (entry.startsWith("'") && entry.endsWith("'")) - ? entry.slice(1, -1) - : entry - ); - - if (types.includes('number') || types.includes('integer') || types.includes('bigint')) { - return 7; - } - if (stringLiterals.length > 0) { - return stringLiterals[0]; - } - if (types.includes('regexp')) { - return '[A-Z]'; - } - if (types.includes('boolean')) { - return true; - } - if (types.includes('array')) { - return ['x', 'y']; - } - if (types.includes('object')) { - return { key: 'value' }; - } - return 'sample'; -} - -function sampleValueForKeywordArg(keywordName, argName, typeName) { - const key = `${keywordName}.${argName}`; - if (argName === 'from') return 1577836800000; - if (argName === 'to') return 1580428800000; - if (argName === 'refDate') return 1716110400000; - if (key === 'airline.flightNumber.length') return 4; - if (key === 'image.dataUri.type') return 'svg-base64'; - if (key === 'internet.httpStatusCode.types') return ['success']; - if (key === 'location.zipCode.state') return 'CA'; - if (key === 'location.zipCode.format') return '#####'; - if (key === 'system.networkInterface.interfaceType') return 'en'; - if (key === 'system.networkInterface.interfaceSchema') return 'mac'; - if (argName === 'version') return 7; - if (argName === 'count') return 3; - if (argName === 'min') return 1; - if (argName === 'max') return 10; - if (argName === 'length') return 8; - if (argName === 'precision') return 2; - if (argName === 'multipleOf') return 1; - if (argName === 'separator') return '-'; - if (argName === 'protocol') return 'https'; - if (argName === 'countryCode') return 'GB'; - if (argName === 'mimeType') return 'image/png'; - if (key === 'internet.ipv4.network') return 'private-a'; - if (key === 'finance.bitcoinAddress.network') return 'testnet'; - if (argName === 'cidrBlock') return '192.168.0.0/24'; - if (argName === 'types') return ['smiley']; - if (argName === 'header') return { alg: 'HS256', typ: 'JWT' }; - if (argName === 'payload') return { iss: 'Acme' }; - if (argName === 'pattern') return '[A-Z]'; - if (argName === 'mode') return 'age'; - if (argName === 'strategy') return 'any-length'; - if (argName === 'sex') return 'female'; - if (argName === 'style') return 'human'; - - return sampleValueForType(typeName); -} - -function valueToInvocationLiteral(value) { - if (typeof value === 'string') { - return JSON.stringify(value); - } - if (typeof value === 'number' || typeof value === 'boolean') { - return String(value); - } - if (Array.isArray(value)) { - return JSON.stringify(value); - } - if (value && typeof value === 'object') { - return JSON.stringify(value); - } - throw new Error(`Unsupported literal type for invocation: ${typeof value}`); -} - function addDomainParamInvocationCoverageTests(domainName) { const keywords = DOMAIN_KEYWORDS.filter( (keyword) => diff --git a/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js b/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js index 69e5e674..30ffaf3b 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js @@ -17,6 +17,7 @@ import { getFakerCommandHelp, } from '../../../../../js/faker/faker-helper-keyword-definitions.js'; import { FAKER_HELPER_KEYWORD_DEFINITIONS } from '../../../../../js/faker/faker-helper-keyword-definitions.js'; +import { sampleValueForKeywordArg, valueToInvocationLiteral } from './domain-keyword-sample-values.test-helper.js'; describe('domain keyword catalog', () => { test('registers canonical awd.domain keywords for faker commands', () => { @@ -448,6 +449,17 @@ describe('domain keyword arg validation', () => { }); }); + test('rejects unsupported internet.httpStatusCode types before generation', () => { + const keyword = getDomainKeywordByAlias('internet.httpStatusCode'); + const result = validateDomainKeywordArgs(keyword, [['success', 'redirect']]); + + expect(result).toEqual({ + ok: false, + error: + 'Invalid keyword arguments: argument "types" contains unsupported value "redirect". Allowed values are informational, success, redirection, clientError, serverError', + }); + }); + test('treats comma-separated list metadata as string-compatible for datatype.enum', () => { const keyword = getDomainKeywordByAlias('datatype.enum'); const result = validateDomainKeywordArgs(keyword, ['active,inactive,pending']); @@ -554,38 +566,6 @@ function setDeepMethod(root, target, fn) { node[parts[parts.length - 1]] = fn; } -function sampleValueForType(type) { - const allowed = String(type || '') - .split('|') - .map((entry) => entry.trim()); - const numericLiterals = allowed.filter((entry) => /^[+-]?\d+(\.\d+)?$/.test(entry)).map((entry) => Number(entry)); - const stringLiterals = allowed - .filter( - (entry) => - !['bigint', 'string', 'integer', 'number', 'date', 'regexp', 'boolean', 'array', 'object'].includes(entry) && - !/^[+-]?\d+(\.\d+)?$/.test(entry) - ) - .map((entry) => - (entry.startsWith('"') && entry.endsWith('"')) || (entry.startsWith("'") && entry.endsWith("'")) - ? entry.slice(1, -1) - : entry - ); - - if (numericLiterals.length === allowed.length && numericLiterals.length > 0) { - return numericLiterals[0]; - } - if (stringLiterals.length > 0) return stringLiterals[0]; - - if (allowed.includes('bigint')) return 7; - if (allowed.includes('integer')) return 7; - if (allowed.includes('number')) return 7; - if (allowed.includes('regexp')) return '[A-Z]'; - if (allowed.includes('boolean')) return true; - if (allowed.includes('array')) return ['x', 'y']; - if (allowed.includes('object')) return { key: 'value' }; - return 'sample'; -} - function normalizeExampleValue(value) { if (typeof value === 'bigint') { return { @@ -613,22 +593,6 @@ function serializeExampleValue(value) { return JSON.stringify(normalizeExampleValue(value)); } -function valueToInvocationLiteral(value) { - if (typeof value === 'string') { - return JSON.stringify(value); - } - if (typeof value === 'number' || typeof value === 'boolean') { - return String(value); - } - if (Array.isArray(value)) { - return JSON.stringify(value); - } - if (value && typeof value === 'object') { - return JSON.stringify(value); - } - throw new Error(`Unsupported invocation literal value: ${String(value)}`); -} - function inferTypeFromExampleLiteral(example) { if (example === null || typeof example === 'undefined') { return { type: 'unknown', confidence: 'low' }; @@ -678,7 +642,7 @@ describe('faker keyword invocation styles', () => { for (const keyword of fakerKeywordsWithArgs) { test(`${keyword.keyword} supports equivalent positional and named argument invocation`, () => { - const sampleArgs = keyword.help.args.map((arg) => sampleValueForType(arg.type)); + const sampleArgs = keyword.help.args.map((arg) => sampleValueForKeywordArg(keyword.keyword, arg.name, arg.type)); if (keyword.keyword === 'datatype.boolean') { sampleArgs[0] = 0.5; } diff --git a/scripts/compare-domain-faker-params.mjs b/scripts/compare-domain-faker-params.mjs index 32404cf7..1c4397a8 100644 --- a/scripts/compare-domain-faker-params.mjs +++ b/scripts/compare-domain-faker-params.mjs @@ -67,22 +67,43 @@ function getObjectOptionKeys(optionsSignature) { function getFakerDeclarationPath() { const packageJsonPath = require.resolve('@faker-js/faker/package.json'); - const distDir = path.join(path.dirname(packageJsonPath), 'dist'); + const packageRoot = path.dirname(packageJsonPath); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + const typesEntry = packageJson.exports?.['.']?.default?.types || packageJson.types; - for (const fileName of fs.readdirSync(distDir)) { - if (!fileName.endsWith('.d.ts') || fileName === 'index.d.ts') { - continue; - } - const filePath = path.join(distDir, fileName); - const contents = fs.readFileSync(filePath, 'utf8'); - if (contents.includes('declare class NumberModule')) { - return filePath; + if (!typesEntry) { + throw new Error('Unable to resolve Faker declaration entry from package exports.'); + } + + const typesEntryPath = path.join(packageRoot, typesEntry); + const typesEntryText = fs.readFileSync(typesEntryPath, 'utf8'); + const bundleMatch = typesEntryText.match(/\bfrom\s+['"]\.\/([^'"]+\.js)['"]/); + + if (bundleMatch) { + const bundlePath = path.join(path.dirname(typesEntryPath), bundleMatch[1].replace(/\.js$/u, '.d.ts')); + if (fs.existsSync(bundlePath)) { + return bundlePath; } } + if (typesEntryText.includes('declare class')) { + return typesEntryPath; + } + throw new Error('Unable to find Faker declaration bundle with module method signatures.'); } +function mergeMethodOverload(method, optionsSignature) { + const signatures = [...(method?.signatures || []), optionsSignature]; + const optionParams = [...new Set([...(method?.optionParams || []), ...getObjectOptionKeys(optionsSignature)])]; + + return { + signature: signatures.join(' | '), + signatures, + optionParams, + }; +} + function extractFakerOptionMethods(declarationText) { const modules = new Map(); const classRegex = /declare class (\w+)Module\b/g; @@ -128,10 +149,7 @@ function extractFakerOptionMethods(declarationText) { } const optionsSignature = paramsText.slice(optionsIndex + 'options?:'.length).trim(); - methods.set(methodName, { - signature: optionsSignature, - optionParams: getObjectOptionKeys(optionsSignature), - }); + methods.set(methodName, mergeMethodOverload(methods.get(methodName), optionsSignature)); methodMatch = methodStartRegex.exec(body); } @@ -241,4 +259,9 @@ if (isCli) { runCli(); } -export { formatMarkdownComparisonReport, getFakerOptionParamComparison, hasParamComparisonFailures }; +export { + extractFakerOptionMethods, + formatMarkdownComparisonReport, + getFakerOptionParamComparison, + hasParamComparisonFailures, +}; From e8fd4c727d19d43dcaf4ea114f25f625793f939b Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 1 Jul 2026 23:50:09 +0100 Subject: [PATCH 5/5] Fix domain command parameter validation --- .../docs/040-test-data/domain/020-airline.md | 12 +-- .../docs/040-test-data/domain/190-location.md | 6 +- .../docs/040-test-data/domain/200-lorem.md | 70 +++++++------- .../docs/040-test-data/domain/250-science.md | 10 +- .../domain/domainTestDataRuleValidator.js | 34 ++++++- .../data_generation/testDataRulesCompiler.js | 2 +- .../domain/domain-keyword-arg-validators.js | 52 ++++++++++- packages/core/js/domain/domain-keywords.js | 67 ++++++++++++++ .../flight-number-keyword-definition.js | 4 + .../date/betweens-keyword-definition.js | 8 +- .../account-number-keyword-definition.js | 4 + .../domain/finance/pin-keyword-definition.js | 4 + .../git/commit-sha-keyword-definition.js | 4 + .../image/data-uri-keyword-definition.js | 4 + .../domain/image/url-keyword-definition.js | 4 + .../url-picsum-photos-keyword-definition.js | 4 + .../http-status-code-keyword-definition.js | 11 ++- .../internet/password-keyword-definition.js | 4 + .../domain/lorem/lines-keyword-definition.js | 32 +++++-- .../domain/lorem/lorem-arg-validators.js | 12 +++ .../lorem/paragraph-keyword-definition.js | 20 +++- .../lorem/paragraphs-keyword-definition.js | 37 ++++++-- .../lorem/sentence-keyword-definition.js | 19 +++- .../lorem/sentences-keyword-definition.js | 22 +++-- .../domain/lorem/slug-keyword-definition.js | 19 +++- .../domain/lorem/word-keyword-definition.js | 3 + .../domain/lorem/words-keyword-definition.js | 19 +++- .../number/big-int-keyword-definition.js | 39 +++++++- .../domain/shared/common-arg-validators.js | 16 ++++ .../domain/string/alpha-keyword-definition.js | 3 + .../string/alphanumeric-keyword-definition.js | 3 + .../string/binary-keyword-definition.js | 4 + .../from-characters-keyword-definition.js | 4 + .../string/hexadecimal-keyword-definition.js | 3 + .../string/nanoid-keyword-definition.js | 4 + .../string/numeric-keyword-definition.js | 4 + .../domain/string/octal-keyword-definition.js | 4 + .../string/sample-keyword-definition.js | 4 + .../string/symbol-keyword-definition.js | 4 + .../word/adjective-keyword-definition.js | 3 + .../domain/word/adverb-keyword-definition.js | 3 + .../word/conjunction-keyword-definition.js | 3 + .../word/interjection-keyword-definition.js | 3 + .../domain/word/noun-keyword-definition.js | 3 + .../word/preposition-keyword-definition.js | 3 + .../domain/word/sample-keyword-definition.js | 3 + .../domain/word/verb-keyword-definition.js | 3 + .../domain/word/words-keyword-definition.js | 4 + .../domain-doc-generator-output.test.js | 4 + .../domain-keyword-params-usage.test.js | 15 +++ .../domain-test-data-rule-validator.test.js | 29 ++++++ .../unit/domain/domainKeywords.test.js | 91 +++++++++++++++++++ scripts/generate-domain-docs.mjs | 19 +++- 53 files changed, 666 insertions(+), 99 deletions(-) create mode 100644 packages/core/js/keywords/domain/lorem/lorem-arg-validators.js create mode 100644 packages/core/js/keywords/domain/shared/common-arg-validators.js diff --git a/docs-src/docs/040-test-data/domain/020-airline.md b/docs-src/docs/040-test-data/domain/020-airline.md index 666dd99e..68631d2a 100644 --- a/docs-src/docs/040-test-data/domain/020-airline.md +++ b/docs-src/docs/040-test-data/domain/020-airline.md @@ -93,7 +93,7 @@ Examples: Shows the default airline.airline.iataCode call. ```txt -airline.airline.iataCode +airline.iataCode ``` Returns: `FZ` @@ -112,7 +112,7 @@ Examples: Shows the default airline.airline.name call. ```txt -airline.airline.name +airline.name ``` Returns: `Flydubai` @@ -206,7 +206,7 @@ Examples: Shows the default airline.airplane.iataTypeCode call. ```txt -airline.airplane.iataTypeCode +airplane.iataTypeCode ``` Returns: `74J` @@ -225,7 +225,7 @@ Examples: Shows the default airline.airplane.name call. ```txt -airline.airplane.name +airplane.name ``` Returns: `Boeing 747-400D` @@ -244,7 +244,7 @@ Examples: Shows the default airline.airport.iataCode call. ```txt -airline.airport.iataCode +airport.iataCode ``` Returns: `HRG` @@ -263,7 +263,7 @@ Examples: Shows the default airline.airport.name call. ```txt -airline.airport.name +airport.name ``` Returns: `Hurgada International Airport` diff --git a/docs-src/docs/040-test-data/domain/190-location.md b/docs-src/docs/040-test-data/domain/190-location.md index e7c36e6c..77b9507c 100644 --- a/docs-src/docs/040-test-data/domain/190-location.md +++ b/docs-src/docs/040-test-data/domain/190-location.md @@ -28,7 +28,7 @@ Examples: Shows the default location.language.alpha2 call. ```txt -location.language.alpha2 +language.alpha2 ``` Returns: `pa` @@ -47,7 +47,7 @@ Examples: Shows the default location.language.alpha3 call. ```txt -location.language.alpha3 +language.alpha3 ``` Returns: `pan` @@ -66,7 +66,7 @@ Examples: Shows the default location.language.name call. ```txt -location.language.name +language.name ``` Returns: `Punjabi` diff --git a/docs-src/docs/040-test-data/domain/200-lorem.md b/docs-src/docs/040-test-data/domain/200-lorem.md index 4c805985..4817b3e3 100644 --- a/docs-src/docs/040-test-data/domain/200-lorem.md +++ b/docs-src/docs/040-test-data/domain/200-lorem.md @@ -45,7 +45,7 @@ Shows lorem.lines using min. lorem.lines(max=10, min=1) ``` -Returns: `Suppellex a cognatus arca aliquam audentia.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit.\nStillicidium bardus utrimque acsi spargo cur.\nAqua avaritia thesaurus volo combibo stultus utor.\nAgo adflicto assentator utrimque altus curiositas vita expedita stultus comedo.\nTrucido accusamus tandem voveo tamisium cicuta testimonium amet.` Shows lorem.lines using max. @@ -61,7 +61,7 @@ Shows lorem.lines using lineCount. lorem.lines(lineCount=5) ``` -Returns: `A cognatus arca aliquam audentia coniuratio crux fugit.\nStillicidium bardus utrimque acsi spargo cur.\nAqua avaritia thesaurus volo combibo stultus utor.` +Returns: `Suppellex a cognatus arca aliquam audentia.\nCrux fugit curatio stillicidium bardus.\nAcsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\nVarius ago adflicto assentator utrimque altus curiositas vita expedita stultus.\nStipes trucido accusamus tandem voveo.` Shows lorem.lines using lineCountMax. @@ -77,7 +77,7 @@ Shows lorem.lines using lineCountMin. lorem.lines(lineCountMin=5) ``` -Returns: `A cognatus arca aliquam audentia coniuratio crux fugit.\nStillicidium bardus utrimque acsi spargo cur.\nAqua avaritia thesaurus volo combibo stultus utor.` +Returns: `Suppellex a cognatus arca aliquam audentia.\nCrux fugit curatio stillicidium bardus.\nAcsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\nVarius ago adflicto assentator utrimque altus curiositas vita expedita stultus.\nStipes trucido accusamus tandem voveo.` ### `lorem.paragraph` @@ -110,7 +110,7 @@ Shows lorem.paragraph using min. lorem.paragraph(max=10, min=1) ``` -Returns: `Suppellex a cognatus arca aliquam audentia.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet.` Shows lorem.paragraph using max. @@ -118,7 +118,7 @@ Shows lorem.paragraph using max. lorem.paragraph(max=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.` Shows lorem.paragraph using sentenceCount. @@ -126,7 +126,7 @@ Shows lorem.paragraph using sentenceCount. lorem.paragraph(sentenceCount=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.` +Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus. Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo.` Shows lorem.paragraph using sentenceCountMax. @@ -134,7 +134,7 @@ Shows lorem.paragraph using sentenceCountMax. lorem.paragraph(sentenceCountMax=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.` Shows lorem.paragraph using sentenceCountMin. @@ -142,7 +142,7 @@ Shows lorem.paragraph using sentenceCountMin. lorem.paragraph(sentenceCountMin=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.` +Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus. Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo.` ### `lorem.paragraphs` @@ -176,7 +176,7 @@ Shows lorem.paragraphs using min. lorem.paragraphs(max=10, min=1) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.\nAgo adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet. Ver claudeo civis aperio accusantium spoliatio.\nCena deprimo adnuo natus. Odit subseco ambulo cupio. Cupio admiratio facilis sonitus dolorum vinco occaecati venio.\nApud timor cubicularis asperiores. Conqueror tantillus sursum vacuus quia tantillus conscendo centum vehemens cursus. Somnus quasi amplus vir defaeco nesciunt cumque capillus venio natus.\nProvident compello et. Consuasor ver qui accendo vetus studio vulpes at approbo vicissitudo. Aedificium temeritas tego vesica sum ante accusantium acquiro.` Shows lorem.paragraphs using max. @@ -184,7 +184,7 @@ Shows lorem.paragraphs using max. lorem.paragraphs(max=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.5Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.5Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.\nAgo adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet. Ver claudeo civis aperio accusantium spoliatio.\nCena deprimo adnuo natus. Odit subseco ambulo cupio. Cupio admiratio facilis sonitus dolorum vinco occaecati venio.` Shows lorem.paragraphs using paragraphCount. @@ -192,7 +192,7 @@ Shows lorem.paragraphs using paragraphCount. lorem.paragraphs(paragraphCount=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\nVarius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\nSpoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.` +Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\nVarius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\nSpoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.\nOccaecati venio apto apud timor cubicularis asperiores vestigium conqueror tantillus. Vacuus quia tantillus conscendo centum vehemens cursus vobis. Quasi amplus vir defaeco nesciunt cumque capillus venio.\nAbbas provident compello et valde consuasor ver. Accendo vetus studio vulpes at approbo vicissitudo. Aedificium temeritas tego vesica sum ante accusantium acquiro.` Shows lorem.paragraphs using separator. @@ -200,7 +200,7 @@ Shows lorem.paragraphs using separator. lorem.paragraphs(separator="-") ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\nVarius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\nSpoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.` +Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.-Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.-Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.` Shows lorem.paragraphs using paragraphCountMax. @@ -208,7 +208,7 @@ Shows lorem.paragraphs using paragraphCountMax. lorem.paragraphs(paragraphCountMax=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\nVarius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\nSpoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.\nAgo adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet. Ver claudeo civis aperio accusantium spoliatio.\nCena deprimo adnuo natus. Odit subseco ambulo cupio. Cupio admiratio facilis sonitus dolorum vinco occaecati venio.` Shows lorem.paragraphs using paragraphCountMin. @@ -216,7 +216,7 @@ Shows lorem.paragraphs using paragraphCountMin. lorem.paragraphs(paragraphCountMin=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\nVarius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\nSpoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.` +Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\nVarius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\nSpoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.\nOccaecati venio apto apud timor cubicularis asperiores vestigium conqueror tantillus. Vacuus quia tantillus conscendo centum vehemens cursus vobis. Quasi amplus vir defaeco nesciunt cumque capillus venio.\nAbbas provident compello et valde consuasor ver. Accendo vetus studio vulpes at approbo vicissitudo. Aedificium temeritas tego vesica sum ante accusantium acquiro.` ### `lorem.sentence` @@ -249,7 +249,7 @@ Shows lorem.sentence using min. lorem.sentence(max=10, min=1) ``` -Returns: `Cur.` +Returns: `Suppellex a cognatus arca aliquam.` Shows lorem.sentence using max. @@ -257,7 +257,7 @@ Shows lorem.sentence using max. lorem.sentence(max=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia.` +Returns: `Suppellex a cognatus.` Shows lorem.sentence using wordCount. @@ -265,7 +265,7 @@ Shows lorem.sentence using wordCount. lorem.sentence(wordCount=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia.` +Returns: `Cur suppellex a cognatus arca.` Shows lorem.sentence using wordCountMax. @@ -273,7 +273,7 @@ Shows lorem.sentence using wordCountMax. lorem.sentence(wordCountMax=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia.` +Returns: `Suppellex a cognatus.` Shows lorem.sentence using wordCountMin. @@ -281,7 +281,7 @@ Shows lorem.sentence using wordCountMin. lorem.sentence(wordCountMin=5) ``` -Returns: `Suppellex a cognatus arca aliquam audentia.` +Returns: `Cur suppellex a cognatus arca.` ### `lorem.sentences` @@ -315,7 +315,7 @@ Shows lorem.sentences using min. lorem.sentences(max=10, min=1) ``` -Returns: `Suppellex a cognatus arca aliquam audentia.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet.` Shows lorem.sentences using max. @@ -323,7 +323,7 @@ Shows lorem.sentences using max. lorem.sentences(max=5) ``` -Returns: `A cognatus arca aliquam audentia coniuratio crux fugit.5Stillicidium bardus utrimque acsi spargo cur.5Aqua avaritia thesaurus volo combibo stultus utor.5Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.` Shows lorem.sentences using sentenceCount. @@ -331,7 +331,7 @@ Shows lorem.sentences using sentenceCount. lorem.sentences(sentenceCount=5) ``` -Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.` +Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus. Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo.` Shows lorem.sentences using separator. @@ -339,7 +339,7 @@ Shows lorem.sentences using separator. lorem.sentences(separator="-") ``` -Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit.-Stillicidium bardus utrimque acsi spargo cur.-Aqua avaritia thesaurus volo combibo stultus utor.-Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.` Shows lorem.sentences using sentenceCountMax. @@ -347,7 +347,7 @@ Shows lorem.sentences using sentenceCountMax. lorem.sentences(sentenceCountMax=5) ``` -Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.` +Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.` Shows lorem.sentences using sentenceCountMin. @@ -355,7 +355,7 @@ Shows lorem.sentences using sentenceCountMin. lorem.sentences(sentenceCountMin=5) ``` -Returns: `A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.` +Returns: `Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus. Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo.` ### `lorem.slug` @@ -388,7 +388,7 @@ Shows lorem.slug using min. lorem.slug(max=10, min=1) ``` -Returns: `cur` +Returns: `suppellex-a-cognatus-arca-aliquam` Shows lorem.slug using max. @@ -396,7 +396,7 @@ Shows lorem.slug using max. lorem.slug(max=5) ``` -Returns: `cur-suppellex-a` +Returns: `suppellex-a-cognatus` Shows lorem.slug using wordCount. @@ -404,7 +404,7 @@ Shows lorem.slug using wordCount. lorem.slug(wordCount=5) ``` -Returns: `cur-suppellex-a` +Returns: `cur-suppellex-a-cognatus-arca` Shows lorem.slug using wordCountMax. @@ -412,7 +412,7 @@ Shows lorem.slug using wordCountMax. lorem.slug(wordCountMax=5) ``` -Returns: `cur-suppellex-a` +Returns: `suppellex-a-cognatus` Shows lorem.slug using wordCountMin. @@ -420,7 +420,7 @@ Shows lorem.slug using wordCountMin. lorem.slug(wordCountMin=5) ``` -Returns: `cur-suppellex-a` +Returns: `cur-suppellex-a-cognatus-arca` ### `lorem.text` @@ -510,7 +510,7 @@ Shows lorem.words using min. lorem.words(max=10, min=1) ``` -Returns: `cur` +Returns: `suppellex a cognatus arca aliquam` Shows lorem.words using max. @@ -518,7 +518,7 @@ Shows lorem.words using max. lorem.words(max=5) ``` -Returns: `cur suppellex a` +Returns: `suppellex a cognatus` Shows lorem.words using wordCount. @@ -526,7 +526,7 @@ Shows lorem.words using wordCount. lorem.words(wordCount=5) ``` -Returns: `cur suppellex a` +Returns: `cur suppellex a cognatus arca` Shows lorem.words using wordCountMax. @@ -534,7 +534,7 @@ Shows lorem.words using wordCountMax. lorem.words(wordCountMax=5) ``` -Returns: `cur suppellex a` +Returns: `suppellex a cognatus` Shows lorem.words using wordCountMin. @@ -542,4 +542,4 @@ Shows lorem.words using wordCountMin. lorem.words(wordCountMin=5) ``` -Returns: `cur suppellex a` +Returns: `cur suppellex a cognatus arca` diff --git a/docs-src/docs/040-test-data/domain/250-science.md b/docs-src/docs/040-test-data/domain/250-science.md index 1bfe206c..56681bea 100644 --- a/docs-src/docs/040-test-data/domain/250-science.md +++ b/docs-src/docs/040-test-data/domain/250-science.md @@ -28,7 +28,7 @@ Examples: Shows the default science.chemicalElement.atomicNumber call. ```txt -science.chemicalElement.atomicNumber +chemicalElement.atomicNumber ``` Returns: `50` @@ -47,7 +47,7 @@ Examples: Shows the default science.chemicalElement.name call. ```txt -science.chemicalElement.name +chemicalElement.name ``` Returns: `Tin` @@ -66,7 +66,7 @@ Examples: Shows the default science.chemicalElement.symbol call. ```txt -science.chemicalElement.symbol +chemicalElement.symbol ``` Returns: `Sn` @@ -85,7 +85,7 @@ Examples: Shows the default science.unit.name call. ```txt -science.unit.name +unit.name ``` Returns: `watt` @@ -104,7 +104,7 @@ Examples: Shows the default science.unit.symbol call. ```txt -science.unit.symbol +unit.symbol ``` Returns: `W` diff --git a/packages/core/js/data_generation/domain/domainTestDataRuleValidator.js b/packages/core/js/data_generation/domain/domainTestDataRuleValidator.js index eb016acf..6640e954 100644 --- a/packages/core/js/data_generation/domain/domainTestDataRuleValidator.js +++ b/packages/core/js/data_generation/domain/domainTestDataRuleValidator.js @@ -1,12 +1,14 @@ import { parseKeywordInvocation } from '../../domain/domain-keyword-parser.js'; import { DOMAIN_KEYWORD_ALIAS_INDEX, + executeDomainKeyword, getDomainKeywordByAlias, validateDomainKeywordArgs, } from '../../domain/domain-keywords.js'; class DomainTestDataRuleValidator { - constructor() { + constructor(aFaker = null) { + this.faker = aFaker; this.validationError = ''; this.lastParsed = null; } @@ -52,6 +54,23 @@ class DomainTestDataRuleValidator { return false; } + if ( + this.faker && + keywordDefinition.delegate?.type === 'faker' && + hasFakerDelegateTarget(this.faker, keywordDefinition.delegate?.target) + ) { + try { + executeDomainKeyword(recognizedKeyword, { + faker: this.faker, + args: parsed.args, + autoIncrementState: {}, + }); + } catch (error) { + this.validationError = error?.message || 'Domain keyword failed during validation'; + return false; + } + } + return true; } @@ -64,4 +83,17 @@ class DomainTestDataRuleValidator { } } +function hasFakerDelegateTarget(fakerInstance, target) { + const parts = String(target || '') + .split('.') + .filter((part) => part.length > 0); + let node = fakerInstance; + + for (const part of parts) { + node = node?.[part]; + } + + return typeof node === 'function'; +} + export { DomainTestDataRuleValidator }; diff --git a/packages/core/js/data_generation/testDataRulesCompiler.js b/packages/core/js/data_generation/testDataRulesCompiler.js index bac47312..91111a9c 100644 --- a/packages/core/js/data_generation/testDataRulesCompiler.js +++ b/packages/core/js/data_generation/testDataRulesCompiler.js @@ -298,7 +298,7 @@ export class TestDataRulesCompiler { const fakerValidator = new FakerTestDataRuleValidator(this.faker, this.options); const regexValidator = new RegexTestDataRuleValidator(this.RandExp); const enumValidator = new EnumTestDataRuleValidator(); - const domainValidator = new DomainTestDataRuleValidator(); + const domainValidator = new DomainTestDataRuleValidator(this.faker); this.rules.forEach((rule) => { switch (rule.type) { diff --git a/packages/core/js/domain/domain-keyword-arg-validators.js b/packages/core/js/domain/domain-keyword-arg-validators.js index 63aae6da..40fd6375 100644 --- a/packages/core/js/domain/domain-keyword-arg-validators.js +++ b/packages/core/js/domain/domain-keyword-arg-validators.js @@ -79,4 +79,54 @@ function createNumericArgRangeValidator({ }; } -export { composeArgsValidators, createNumericArgRangeValidator, createOrderedArgsValidator }; +function createIntegerArgValidator({ argName } = {}) { + return (_args = [], context = {}) => { + const argsByName = context?.argsByName || {}; + const value = argsByName[argName]; + + if (typeof value === 'undefined') { + return { ok: true }; + } + + if (typeof value !== 'number' || !Number.isFinite(value)) { + return { ok: true }; + } + + if (!Number.isInteger(value)) { + return { + ok: false, + error: `Invalid keyword arguments: argument "${argName}" must be an integer`, + }; + } + + return { ok: true }; + }; +} + +function createNonEmptyArrayArgValidator({ argName } = {}) { + return (_args = [], context = {}) => { + const argsByName = context?.argsByName || {}; + const value = argsByName[argName]; + + if (typeof value === 'undefined' || !Array.isArray(value)) { + return { ok: true }; + } + + if (value.length === 0) { + return { + ok: false, + error: `Invalid keyword arguments: argument "${argName}" must not be empty`, + }; + } + + return { ok: true }; + }; +} + +export { + composeArgsValidators, + createIntegerArgValidator, + createNonEmptyArrayArgValidator, + createNumericArgRangeValidator, + createOrderedArgsValidator, +}; diff --git a/packages/core/js/domain/domain-keywords.js b/packages/core/js/domain/domain-keywords.js index 5bbeea23..7d3a2e6c 100644 --- a/packages/core/js/domain/domain-keywords.js +++ b/packages/core/js/domain/domain-keywords.js @@ -375,6 +375,67 @@ function normaliseStringUuidOptions(options) { return options; } +function buildHelpArgsByName(keyword, args = []) { + const argSchema = Array.isArray(keyword?.help?.args) ? keyword.help.args : []; + const argsByName = {}; + + for (let index = 0; index < argSchema.length; index += 1) { + const key = String(argSchema[index]?.name || '').trim(); + if (!key || typeof args[index] === 'undefined') { + continue; + } + argsByName[key] = coerceHelpArgValue(argSchema[index], args[index]); + } + + return argsByName; +} + +function createLoremCountArg(min, max) { + if (typeof min !== 'undefined' && typeof max !== 'undefined') { + return { min, max }; + } + if (typeof max !== 'undefined') { + return { min: 1, max }; + } + if (typeof min !== 'undefined') { + return min; + } + return undefined; +} + +function getLoremCountArg(keyword, argsByName = {}) { + const argNames = (Array.isArray(keyword?.help?.args) ? keyword.help.args : []) + .map((arg) => String(arg?.name || '').trim()) + .filter(Boolean); + const countName = argNames.find((argName) => /Count$/.test(argName)); + + if (countName && typeof argsByName[countName] !== 'undefined') { + return argsByName[countName]; + } + + const specificMinName = countName ? `${countName}Min` : ''; + const specificMaxName = countName ? `${countName}Max` : ''; + if ( + specificMinName && + (typeof argsByName[specificMinName] !== 'undefined' || typeof argsByName[specificMaxName] !== 'undefined') + ) { + return createLoremCountArg(argsByName[specificMinName], argsByName[specificMaxName]); + } + + return createLoremCountArg(argsByName.min, argsByName.max); +} + +function applyLoremCountArgTransform(keyword, args = []) { + const argsByName = buildHelpArgsByName(keyword, args); + const countArg = getLoremCountArg(keyword, argsByName); + + if (Object.prototype.hasOwnProperty.call(argsByName, 'separator')) { + return [countArg, argsByName.separator]; + } + + return typeof countArg === 'undefined' ? [] : [countArg]; +} + function applyFakerArgTransform(keyword, args = []) { const transformName = String(keyword?.delegate?.argTransform || '').trim(); if (!transformName) { @@ -402,6 +463,12 @@ function applyFakerArgTransform(keyword, args = []) { } return Object.keys(options).length > 0 ? [options] : []; } + if (transformName === 'loremCountFromHelpArgs') { + if (!Array.isArray(args)) { + return args; + } + return applyLoremCountArgTransform(keyword, args); + } return args; } diff --git a/packages/core/js/keywords/domain/airline/flight-number-keyword-definition.js b/packages/core/js/keywords/domain/airline/flight-number-keyword-definition.js index 6afaf0e9..df25e7a0 100644 --- a/packages/core/js/keywords/domain/airline/flight-number-keyword-definition.js +++ b/packages/core/js/keywords/domain/airline/flight-number-keyword-definition.js @@ -1,4 +1,7 @@ import { validateFlightNumberValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateAirlineFlightNumberArgs = createPositiveIntegerArgsValidator(['length']); const AIRLINE_FLIGHT_NUMBER_KEYWORD_DEFINITION = { keyword: 'airline.flightNumber', @@ -13,6 +16,7 @@ const AIRLINE_FLIGHT_NUMBER_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/airline', fakerDocsUrl: 'https://fakerjs.dev/api/airline', validator: validateFlightNumberValue, + argsValidator: validateAirlineFlightNumberArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/date/betweens-keyword-definition.js b/packages/core/js/keywords/domain/date/betweens-keyword-definition.js index 01c07545..cc4e1b1f 100644 --- a/packages/core/js/keywords/domain/date/betweens-keyword-definition.js +++ b/packages/core/js/keywords/domain/date/betweens-keyword-definition.js @@ -1,7 +1,11 @@ import { validateArrayValue } from '../../../command-help/command-help-validators.js'; -import { createOrderedArgsValidator } from '../../../domain/domain-keyword-arg-validators.js'; +import { composeArgsValidators, createOrderedArgsValidator } from '../../../domain/domain-keyword-arg-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; -const validateDateBetweensBounds = createOrderedArgsValidator({ lowerName: 'from', upperName: 'to' }); +const validateDateBetweensBounds = composeArgsValidators( + createPositiveIntegerArgsValidator(['count']), + createOrderedArgsValidator({ lowerName: 'from', upperName: 'to' }) +); const DATE_BETWEENS_KEYWORD_DEFINITION = { keyword: 'date.betweens', diff --git a/packages/core/js/keywords/domain/finance/account-number-keyword-definition.js b/packages/core/js/keywords/domain/finance/account-number-keyword-definition.js index 5becfc2d..b23f0dca 100644 --- a/packages/core/js/keywords/domain/finance/account-number-keyword-definition.js +++ b/packages/core/js/keywords/domain/finance/account-number-keyword-definition.js @@ -1,4 +1,7 @@ import { validateAccountNumberValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateFinanceAccountNumberArgs = createPositiveIntegerArgsValidator(['length']); const FINANCE_ACCOUNT_NUMBER_KEYWORD_DEFINITION = { keyword: 'finance.accountNumber', @@ -11,6 +14,7 @@ const FINANCE_ACCOUNT_NUMBER_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/finance', fakerDocsUrl: 'https://fakerjs.dev/api/finance', validator: validateAccountNumberValue, + argsValidator: validateFinanceAccountNumberArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/finance/pin-keyword-definition.js b/packages/core/js/keywords/domain/finance/pin-keyword-definition.js index 3f12b9b1..ee97814b 100644 --- a/packages/core/js/keywords/domain/finance/pin-keyword-definition.js +++ b/packages/core/js/keywords/domain/finance/pin-keyword-definition.js @@ -1,4 +1,7 @@ import { validatePinValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateFinancePinArgs = createPositiveIntegerArgsValidator(['length']); const FINANCE_PIN_KEYWORD_DEFINITION = { keyword: 'finance.pin', @@ -11,6 +14,7 @@ const FINANCE_PIN_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/finance', fakerDocsUrl: 'https://fakerjs.dev/api/finance', validator: validatePinValue, + argsValidator: validateFinancePinArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/git/commit-sha-keyword-definition.js b/packages/core/js/keywords/domain/git/commit-sha-keyword-definition.js index 3353d9d4..7d55d2f2 100644 --- a/packages/core/js/keywords/domain/git/commit-sha-keyword-definition.js +++ b/packages/core/js/keywords/domain/git/commit-sha-keyword-definition.js @@ -1,4 +1,7 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateGitCommitShaArgs = createPositiveIntegerArgsValidator(['length']); const GIT_COMMIT_SHA_KEYWORD_DEFINITION = { keyword: 'git.commitSha', @@ -12,6 +15,7 @@ const GIT_COMMIT_SHA_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/git', fakerDocsUrl: 'https://fakerjs.dev/api/git', validator: validateStringValue, + argsValidator: validateGitCommitShaArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/image/data-uri-keyword-definition.js b/packages/core/js/keywords/domain/image/data-uri-keyword-definition.js index 8a6e42e5..a4cc4887 100644 --- a/packages/core/js/keywords/domain/image/data-uri-keyword-definition.js +++ b/packages/core/js/keywords/domain/image/data-uri-keyword-definition.js @@ -1,4 +1,7 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateImageDataUriArgs = createPositiveIntegerArgsValidator(['width', 'height']); const IMAGE_DATA_URI_KEYWORD_DEFINITION = { keyword: 'image.dataUri', @@ -12,6 +15,7 @@ const IMAGE_DATA_URI_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/image', fakerDocsUrl: 'https://fakerjs.dev/api/image', validator: validateStringValue, + argsValidator: validateImageDataUriArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/image/url-keyword-definition.js b/packages/core/js/keywords/domain/image/url-keyword-definition.js index 70810d2f..71d58d8a 100644 --- a/packages/core/js/keywords/domain/image/url-keyword-definition.js +++ b/packages/core/js/keywords/domain/image/url-keyword-definition.js @@ -1,4 +1,7 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateImageUrlArgs = createPositiveIntegerArgsValidator(['height', 'width']); const IMAGE_URL_KEYWORD_DEFINITION = { keyword: 'image.url', @@ -12,6 +15,7 @@ const IMAGE_URL_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/image', fakerDocsUrl: 'https://fakerjs.dev/api/image', validator: validateStringValue, + argsValidator: validateImageUrlArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/image/url-picsum-photos-keyword-definition.js b/packages/core/js/keywords/domain/image/url-picsum-photos-keyword-definition.js index ce05c34b..a365fe42 100644 --- a/packages/core/js/keywords/domain/image/url-picsum-photos-keyword-definition.js +++ b/packages/core/js/keywords/domain/image/url-picsum-photos-keyword-definition.js @@ -1,4 +1,7 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateImageUrlPicsumPhotosArgs = createPositiveIntegerArgsValidator(['width', 'height']); const IMAGE_URL_PICSUM_PHOTOS_KEYWORD_DEFINITION = { keyword: 'image.urlPicsumPhotos', @@ -12,6 +15,7 @@ const IMAGE_URL_PICSUM_PHOTOS_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/image', fakerDocsUrl: 'https://fakerjs.dev/api/image', validator: validateStringValue, + argsValidator: validateImageUrlPicsumPhotosArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js b/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js index bf64f06f..2639a8d7 100644 --- a/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js +++ b/packages/core/js/keywords/domain/internet/http-status-code-keyword-definition.js @@ -1,4 +1,8 @@ import { validateNumberValue } from '../../../command-help/command-help-validators.js'; +import { + composeArgsValidators, + createNonEmptyArrayArgValidator, +} from '../../../domain/domain-keyword-arg-validators.js'; const HTTP_STATUS_CODE_TYPES = ['informational', 'success', 'redirection', 'clientError', 'serverError']; @@ -24,6 +28,11 @@ function validateHttpStatusCodeTypes(_args = [], context = {}) { return { ok: true }; } +const validateHttpStatusCodeArgs = composeArgsValidators( + createNonEmptyArrayArgValidator({ argName: 'types' }), + validateHttpStatusCodeTypes +); + const INTERNET_HTTP_STATUS_CODE_KEYWORD_DEFINITION = { keyword: 'internet.httpStatusCode', delegate: { @@ -36,7 +45,7 @@ const INTERNET_HTTP_STATUS_CODE_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/internet', fakerDocsUrl: 'https://fakerjs.dev/api/internet', validator: validateNumberValue, - argsValidator: validateHttpStatusCodeTypes, + argsValidator: validateHttpStatusCodeArgs, returnType: 'number', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/internet/password-keyword-definition.js b/packages/core/js/keywords/domain/internet/password-keyword-definition.js index ccbb3a23..976ea430 100644 --- a/packages/core/js/keywords/domain/internet/password-keyword-definition.js +++ b/packages/core/js/keywords/domain/internet/password-keyword-definition.js @@ -1,4 +1,7 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateInternetPasswordArgs = createPositiveIntegerArgsValidator(['length']); const INTERNET_PASSWORD_KEYWORD_DEFINITION = { keyword: 'internet.password', @@ -13,6 +16,7 @@ const INTERNET_PASSWORD_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/internet', fakerDocsUrl: 'https://fakerjs.dev/api/internet', validator: validateStringValue, + argsValidator: validateInternetPasswordArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/lorem/lines-keyword-definition.js b/packages/core/js/keywords/domain/lorem/lines-keyword-definition.js index 09d91fe9..e6aaafe6 100644 --- a/packages/core/js/keywords/domain/lorem/lines-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/lines-keyword-definition.js @@ -1,16 +1,25 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createLoremCountArgsValidator } from './lorem-arg-validators.js'; + +const validateLoremLinesArgs = createLoremCountArgsValidator({ + countName: 'lineCount', + minName: 'lineCountMin', + maxName: 'lineCountMax', +}); const LOREM_LINES_KEYWORD_DEFINITION = { keyword: 'lorem.lines', delegate: { type: 'faker', target: 'lorem.lines', + argTransform: 'loremCountFromHelpArgs', }, help: { summary: "Generates the given number lines of lorem separated by `'\\n'`.", docsUrl: 'https://anywaydata.com/docs/test-data/domain/lorem', fakerDocsUrl: 'https://fakerjs.dev/api/lorem', validator: validateStringValue, + argsValidator: validateLoremLinesArgs, returnType: 'string', usageExamples: [ { @@ -23,7 +32,12 @@ const LOREM_LINES_KEYWORD_DEFINITION = { }, { functionCall: 'lorem.lines(max=10, min=1)', - sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia.', + sampleReturnValue: + 'A cognatus arca aliquam audentia coniuratio crux fugit.\n' + + 'Stillicidium bardus utrimque acsi spargo cur.\n' + + 'Aqua avaritia thesaurus volo combibo stultus utor.\n' + + 'Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.\n' + + 'Trucido accusamus tandem voveo tamisium cicuta testimonium amet.', description: 'Shows lorem.lines using min.', }, { @@ -37,9 +51,11 @@ const LOREM_LINES_KEYWORD_DEFINITION = { { functionCall: 'lorem.lines(lineCount=5)', sampleReturnValue: - 'A cognatus arca aliquam audentia coniuratio crux fugit.\n' + - 'Stillicidium bardus utrimque acsi spargo cur.\n' + - 'Aqua avaritia thesaurus volo combibo stultus utor.', + 'Suppellex a cognatus arca aliquam audentia.\n' + + 'Crux fugit curatio stillicidium bardus.\n' + + 'Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\n' + + 'Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus.\n' + + 'Stipes trucido accusamus tandem voveo.', description: 'Shows lorem.lines using lineCount.', }, { @@ -53,9 +69,11 @@ const LOREM_LINES_KEYWORD_DEFINITION = { { functionCall: 'lorem.lines(lineCountMin=5)', sampleReturnValue: - 'A cognatus arca aliquam audentia coniuratio crux fugit.\n' + - 'Stillicidium bardus utrimque acsi spargo cur.\n' + - 'Aqua avaritia thesaurus volo combibo stultus utor.', + 'Suppellex a cognatus arca aliquam audentia.\n' + + 'Crux fugit curatio stillicidium bardus.\n' + + 'Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\n' + + 'Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus.\n' + + 'Stipes trucido accusamus tandem voveo.', description: 'Shows lorem.lines using lineCountMin.', }, ], diff --git a/packages/core/js/keywords/domain/lorem/lorem-arg-validators.js b/packages/core/js/keywords/domain/lorem/lorem-arg-validators.js new file mode 100644 index 00000000..9d77f1dc --- /dev/null +++ b/packages/core/js/keywords/domain/lorem/lorem-arg-validators.js @@ -0,0 +1,12 @@ +import { composeArgsValidators, createOrderedArgsValidator } from '../../../domain/domain-keyword-arg-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +function createLoremCountArgsValidator({ countName, minName, maxName }) { + return composeArgsValidators( + createPositiveIntegerArgsValidator(['min', 'max', countName, minName, maxName]), + createOrderedArgsValidator({ lowerName: 'min', upperName: 'max' }), + createOrderedArgsValidator({ lowerName: minName, upperName: maxName }) + ); +} + +export { createLoremCountArgsValidator }; diff --git a/packages/core/js/keywords/domain/lorem/paragraph-keyword-definition.js b/packages/core/js/keywords/domain/lorem/paragraph-keyword-definition.js index 40697f93..9d6a2300 100644 --- a/packages/core/js/keywords/domain/lorem/paragraph-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/paragraph-keyword-definition.js @@ -1,16 +1,25 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createLoremCountArgsValidator } from './lorem-arg-validators.js'; + +const validateLoremParagraphArgs = createLoremCountArgsValidator({ + countName: 'sentenceCount', + minName: 'sentenceCountMin', + maxName: 'sentenceCountMax', +}); const LOREM_PARAGRAPH_KEYWORD_DEFINITION = { keyword: 'lorem.paragraph', delegate: { type: 'faker', target: 'lorem.paragraph', + argTransform: 'loremCountFromHelpArgs', }, help: { summary: 'Generates a paragraph with the given number of sentences.', docsUrl: 'https://anywaydata.com/docs/test-data/domain/lorem', fakerDocsUrl: 'https://fakerjs.dev/api/lorem', validator: validateStringValue, + argsValidator: validateLoremParagraphArgs, returnType: 'string', usageExamples: [ { @@ -21,31 +30,32 @@ const LOREM_PARAGRAPH_KEYWORD_DEFINITION = { }, { functionCall: 'lorem.paragraph(max=10, min=1)', - sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia.', + sampleReturnValue: + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet.', description: 'Shows lorem.paragraph using min.', }, { functionCall: 'lorem.paragraph(max=5)', sampleReturnValue: - 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.', + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.', description: 'Shows lorem.paragraph using max.', }, { functionCall: 'lorem.paragraph(sentenceCount=5)', sampleReturnValue: - 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.', + 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus. Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo.', description: 'Shows lorem.paragraph using sentenceCount.', }, { functionCall: 'lorem.paragraph(sentenceCountMax=5)', sampleReturnValue: - 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.', + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.', description: 'Shows lorem.paragraph using sentenceCountMax.', }, { functionCall: 'lorem.paragraph(sentenceCountMin=5)', sampleReturnValue: - 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.', + 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus. Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo.', description: 'Shows lorem.paragraph using sentenceCountMin.', }, ], diff --git a/packages/core/js/keywords/domain/lorem/paragraphs-keyword-definition.js b/packages/core/js/keywords/domain/lorem/paragraphs-keyword-definition.js index e54e225d..b3588c00 100644 --- a/packages/core/js/keywords/domain/lorem/paragraphs-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/paragraphs-keyword-definition.js @@ -1,16 +1,25 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createLoremCountArgsValidator } from './lorem-arg-validators.js'; + +const validateLoremParagraphsArgs = createLoremCountArgsValidator({ + countName: 'paragraphCount', + minName: 'paragraphCountMin', + maxName: 'paragraphCountMax', +}); const LOREM_PARAGRAPHS_KEYWORD_DEFINITION = { keyword: 'lorem.paragraphs', delegate: { type: 'faker', target: 'lorem.paragraphs', + argTransform: 'loremCountFromHelpArgs', }, help: { summary: 'Generates the given number of paragraphs.', docsUrl: 'https://anywaydata.com/docs/test-data/domain/lorem', fakerDocsUrl: 'https://fakerjs.dev/api/lorem', validator: validateStringValue, + argsValidator: validateLoremParagraphsArgs, returnType: 'string', usageExamples: [ { @@ -24,13 +33,19 @@ const LOREM_PARAGRAPHS_KEYWORD_DEFINITION = { { functionCall: 'lorem.paragraphs(max=10, min=1)', sampleReturnValue: - 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.', + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.\n' + + 'Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet. Ver claudeo civis aperio accusantium spoliatio.\n' + + 'Cena deprimo adnuo natus. Odit subseco ambulo cupio. Cupio admiratio facilis sonitus dolorum vinco occaecati venio.\n' + + 'Apud timor cubicularis asperiores. Conqueror tantillus sursum vacuus quia tantillus conscendo centum vehemens cursus. Somnus quasi amplus vir defaeco nesciunt cumque capillus venio natus.\n' + + 'Provident compello et. Consuasor ver qui accendo vetus studio vulpes at approbo vicissitudo. Aedificium temeritas tego vesica sum ante accusantium acquiro.', description: 'Shows lorem.paragraphs using min.', }, { functionCall: 'lorem.paragraphs(max=5)', sampleReturnValue: - 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.5Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.5Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.', + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.\n' + + 'Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet. Ver claudeo civis aperio accusantium spoliatio.\n' + + 'Cena deprimo adnuo natus. Odit subseco ambulo cupio. Cupio admiratio facilis sonitus dolorum vinco occaecati venio.', description: 'Shows lorem.paragraphs using max.', }, { @@ -38,23 +53,25 @@ const LOREM_PARAGRAPHS_KEYWORD_DEFINITION = { sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\n' + 'Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\n' + - 'Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.', + 'Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.\n' + + 'Occaecati venio apto apud timor cubicularis asperiores vestigium conqueror tantillus. Vacuus quia tantillus conscendo centum vehemens cursus vobis. Quasi amplus vir defaeco nesciunt cumque capillus venio.\n' + + 'Abbas provident compello et valde consuasor ver. Accendo vetus studio vulpes at approbo vicissitudo. Aedificium temeritas tego vesica sum ante accusantium acquiro.', description: 'Shows lorem.paragraphs using paragraphCount.', }, { functionCall: 'lorem.paragraphs(separator="-")', sampleReturnValue: - 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\n' + - 'Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\n' + + 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.-' + + 'Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.-' + 'Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.', description: 'Shows lorem.paragraphs using separator.', }, { functionCall: 'lorem.paragraphs(paragraphCountMax=5)', sampleReturnValue: - 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\n' + - 'Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\n' + - 'Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.', + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.\n' + + 'Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet. Ver claudeo civis aperio accusantium spoliatio.\n' + + 'Cena deprimo adnuo natus. Odit subseco ambulo cupio. Cupio admiratio facilis sonitus dolorum vinco occaecati venio.', description: 'Shows lorem.paragraphs using paragraphCountMax.', }, { @@ -62,7 +79,9 @@ const LOREM_PARAGRAPHS_KEYWORD_DEFINITION = { sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus.\n' + 'Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo. Cicuta testimonium amet dedico ver claudeo civis aperio.\n' + - 'Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.', + 'Spoliatio beneficium cena. Adnuo natus arca odit subseco ambulo. Suasoria cupio admiratio facilis sonitus dolorum.\n' + + 'Occaecati venio apto apud timor cubicularis asperiores vestigium conqueror tantillus. Vacuus quia tantillus conscendo centum vehemens cursus vobis. Quasi amplus vir defaeco nesciunt cumque capillus venio.\n' + + 'Abbas provident compello et valde consuasor ver. Accendo vetus studio vulpes at approbo vicissitudo. Aedificium temeritas tego vesica sum ante accusantium acquiro.', description: 'Shows lorem.paragraphs using paragraphCountMin.', }, ], diff --git a/packages/core/js/keywords/domain/lorem/sentence-keyword-definition.js b/packages/core/js/keywords/domain/lorem/sentence-keyword-definition.js index 906872d0..e6160c75 100644 --- a/packages/core/js/keywords/domain/lorem/sentence-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/sentence-keyword-definition.js @@ -1,16 +1,25 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createLoremCountArgsValidator } from './lorem-arg-validators.js'; + +const validateLoremSentenceArgs = createLoremCountArgsValidator({ + countName: 'wordCount', + minName: 'wordCountMin', + maxName: 'wordCountMax', +}); const LOREM_SENTENCE_KEYWORD_DEFINITION = { keyword: 'lorem.sentence', delegate: { type: 'faker', target: 'lorem.sentence', + argTransform: 'loremCountFromHelpArgs', }, help: { summary: 'Generates a space separated list of words beginning with a capital letter and ending with a period.', docsUrl: 'https://anywaydata.com/docs/test-data/domain/lorem', fakerDocsUrl: 'https://fakerjs.dev/api/lorem', validator: validateStringValue, + argsValidator: validateLoremSentenceArgs, returnType: 'string', usageExamples: [ { @@ -20,27 +29,27 @@ const LOREM_SENTENCE_KEYWORD_DEFINITION = { }, { functionCall: 'lorem.sentence(max=10, min=1)', - sampleReturnValue: 'Cur.', + sampleReturnValue: 'Suppellex a cognatus arca aliquam.', description: 'Shows lorem.sentence using min.', }, { functionCall: 'lorem.sentence(max=5)', - sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia.', + sampleReturnValue: 'Suppellex a cognatus.', description: 'Shows lorem.sentence using max.', }, { functionCall: 'lorem.sentence(wordCount=5)', - sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia.', + sampleReturnValue: 'Cur suppellex a cognatus arca.', description: 'Shows lorem.sentence using wordCount.', }, { functionCall: 'lorem.sentence(wordCountMax=5)', - sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia.', + sampleReturnValue: 'Suppellex a cognatus.', description: 'Shows lorem.sentence using wordCountMax.', }, { functionCall: 'lorem.sentence(wordCountMin=5)', - sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia.', + sampleReturnValue: 'Cur suppellex a cognatus arca.', description: 'Shows lorem.sentence using wordCountMin.', }, ], diff --git a/packages/core/js/keywords/domain/lorem/sentences-keyword-definition.js b/packages/core/js/keywords/domain/lorem/sentences-keyword-definition.js index 6467e0e0..d781c291 100644 --- a/packages/core/js/keywords/domain/lorem/sentences-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/sentences-keyword-definition.js @@ -1,16 +1,25 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createLoremCountArgsValidator } from './lorem-arg-validators.js'; + +const validateLoremSentencesArgs = createLoremCountArgsValidator({ + countName: 'sentenceCount', + minName: 'sentenceCountMin', + maxName: 'sentenceCountMax', +}); const LOREM_SENTENCES_KEYWORD_DEFINITION = { keyword: 'lorem.sentences', delegate: { type: 'faker', target: 'lorem.sentences', + argTransform: 'loremCountFromHelpArgs', }, help: { summary: 'Generates the given number of sentences.', docsUrl: 'https://anywaydata.com/docs/test-data/domain/lorem', fakerDocsUrl: 'https://fakerjs.dev/api/lorem', validator: validateStringValue, + argsValidator: validateLoremSentencesArgs, returnType: 'string', usageExamples: [ { @@ -21,37 +30,38 @@ const LOREM_SENTENCES_KEYWORD_DEFINITION = { }, { functionCall: 'lorem.sentences(max=10, min=1)', - sampleReturnValue: 'Suppellex a cognatus arca aliquam audentia.', + sampleReturnValue: + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo. Trucido accusamus tandem voveo tamisium cicuta testimonium amet.', description: 'Shows lorem.sentences using min.', }, { functionCall: 'lorem.sentences(max=5)', sampleReturnValue: - 'A cognatus arca aliquam audentia coniuratio crux fugit.5Stillicidium bardus utrimque acsi spargo cur.5Aqua avaritia thesaurus volo combibo stultus utor.5Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.', + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.', description: 'Shows lorem.sentences using max.', }, { functionCall: 'lorem.sentences(sentenceCount=5)', sampleReturnValue: - 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.', + 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus. Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo.', description: 'Shows lorem.sentences using sentenceCount.', }, { functionCall: 'lorem.sentences(separator="-")', sampleReturnValue: - 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.', + 'A cognatus arca aliquam audentia coniuratio crux fugit.-Stillicidium bardus utrimque acsi spargo cur.-Aqua avaritia thesaurus volo combibo stultus utor.-Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.', description: 'Shows lorem.sentences using separator.', }, { functionCall: 'lorem.sentences(sentenceCountMax=5)', sampleReturnValue: - 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.', + 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor.', description: 'Shows lorem.sentences using sentenceCountMax.', }, { functionCall: 'lorem.sentences(sentenceCountMin=5)', sampleReturnValue: - 'A cognatus arca aliquam audentia coniuratio crux fugit. Stillicidium bardus utrimque acsi spargo cur. Aqua avaritia thesaurus volo combibo stultus utor. Ago adflicto assentator utrimque altus curiositas vita expedita stultus comedo.', + 'Suppellex a cognatus arca aliquam audentia. Crux fugit curatio stillicidium bardus. Acsi spargo cur laboriosam aqua avaritia thesaurus volo combibo stultus. Varius ago adflicto assentator utrimque altus curiositas vita expedita stultus. Stipes trucido accusamus tandem voveo.', description: 'Shows lorem.sentences using sentenceCountMin.', }, ], diff --git a/packages/core/js/keywords/domain/lorem/slug-keyword-definition.js b/packages/core/js/keywords/domain/lorem/slug-keyword-definition.js index 5fc275ed..3220a916 100644 --- a/packages/core/js/keywords/domain/lorem/slug-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/slug-keyword-definition.js @@ -1,16 +1,25 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createLoremCountArgsValidator } from './lorem-arg-validators.js'; + +const validateLoremSlugArgs = createLoremCountArgsValidator({ + countName: 'wordCount', + minName: 'wordCountMin', + maxName: 'wordCountMax', +}); const LOREM_SLUG_KEYWORD_DEFINITION = { keyword: 'lorem.slug', delegate: { type: 'faker', target: 'lorem.slug', + argTransform: 'loremCountFromHelpArgs', }, help: { summary: 'Generates a slugified text consisting of the given number of hyphen separated words.', docsUrl: 'https://anywaydata.com/docs/test-data/domain/lorem', fakerDocsUrl: 'https://fakerjs.dev/api/lorem', validator: validateStringValue, + argsValidator: validateLoremSlugArgs, returnType: 'string', usageExamples: [ { @@ -20,27 +29,27 @@ const LOREM_SLUG_KEYWORD_DEFINITION = { }, { functionCall: 'lorem.slug(max=10, min=1)', - sampleReturnValue: 'cur', + sampleReturnValue: 'suppellex-a-cognatus-arca-aliquam', description: 'Shows lorem.slug using min.', }, { functionCall: 'lorem.slug(max=5)', - sampleReturnValue: 'cur-suppellex-a', + sampleReturnValue: 'suppellex-a-cognatus', description: 'Shows lorem.slug using max.', }, { functionCall: 'lorem.slug(wordCount=5)', - sampleReturnValue: 'cur-suppellex-a', + sampleReturnValue: 'cur-suppellex-a-cognatus-arca', description: 'Shows lorem.slug using wordCount.', }, { functionCall: 'lorem.slug(wordCountMax=5)', - sampleReturnValue: 'cur-suppellex-a', + sampleReturnValue: 'suppellex-a-cognatus', description: 'Shows lorem.slug using wordCountMax.', }, { functionCall: 'lorem.slug(wordCountMin=5)', - sampleReturnValue: 'cur-suppellex-a', + sampleReturnValue: 'cur-suppellex-a-cognatus-arca', description: 'Shows lorem.slug using wordCountMin.', }, ], diff --git a/packages/core/js/keywords/domain/lorem/word-keyword-definition.js b/packages/core/js/keywords/domain/lorem/word-keyword-definition.js index 55cbe6a8..bf81c42c 100644 --- a/packages/core/js/keywords/domain/lorem/word-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/word-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const LOREM_WORD_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateLoremWordArgs = createPositiveIntegerArgsValidator(['length']); const LOREM_WORD_KEYWORD_DEFINITION = { keyword: 'lorem.word', @@ -14,6 +16,7 @@ const LOREM_WORD_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/lorem', fakerDocsUrl: 'https://fakerjs.dev/api/lorem', validator: validateStringValue, + argsValidator: validateLoremWordArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/lorem/words-keyword-definition.js b/packages/core/js/keywords/domain/lorem/words-keyword-definition.js index 2f6f97c6..0ecbf264 100644 --- a/packages/core/js/keywords/domain/lorem/words-keyword-definition.js +++ b/packages/core/js/keywords/domain/lorem/words-keyword-definition.js @@ -1,16 +1,25 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createLoremCountArgsValidator } from './lorem-arg-validators.js'; + +const validateLoremWordsArgs = createLoremCountArgsValidator({ + countName: 'wordCount', + minName: 'wordCountMin', + maxName: 'wordCountMax', +}); const LOREM_WORDS_KEYWORD_DEFINITION = { keyword: 'lorem.words', delegate: { type: 'faker', target: 'lorem.words', + argTransform: 'loremCountFromHelpArgs', }, help: { summary: 'Generates a space separated list of words.', docsUrl: 'https://anywaydata.com/docs/test-data/domain/lorem', fakerDocsUrl: 'https://fakerjs.dev/api/lorem', validator: validateStringValue, + argsValidator: validateLoremWordsArgs, returnType: 'string', usageExamples: [ { @@ -20,27 +29,27 @@ const LOREM_WORDS_KEYWORD_DEFINITION = { }, { functionCall: 'lorem.words(max=10, min=1)', - sampleReturnValue: 'cur', + sampleReturnValue: 'suppellex a cognatus arca aliquam', description: 'Shows lorem.words using min.', }, { functionCall: 'lorem.words(max=5)', - sampleReturnValue: 'cur suppellex a', + sampleReturnValue: 'suppellex a cognatus', description: 'Shows lorem.words using max.', }, { functionCall: 'lorem.words(wordCount=5)', - sampleReturnValue: 'cur suppellex a', + sampleReturnValue: 'cur suppellex a cognatus arca', description: 'Shows lorem.words using wordCount.', }, { functionCall: 'lorem.words(wordCountMax=5)', - sampleReturnValue: 'cur suppellex a', + sampleReturnValue: 'suppellex a cognatus', description: 'Shows lorem.words using wordCountMax.', }, { functionCall: 'lorem.words(wordCountMin=5)', - sampleReturnValue: 'cur suppellex a', + sampleReturnValue: 'cur suppellex a cognatus arca', description: 'Shows lorem.words using wordCountMin.', }, ], diff --git a/packages/core/js/keywords/domain/number/big-int-keyword-definition.js b/packages/core/js/keywords/domain/number/big-int-keyword-definition.js index b362e5fe..16d33e81 100644 --- a/packages/core/js/keywords/domain/number/big-int-keyword-definition.js +++ b/packages/core/js/keywords/domain/number/big-int-keyword-definition.js @@ -12,9 +12,46 @@ const validateBigIntBounds = composeArgsValidators( min: 0, inclusiveMin: false, description: 'Invalid keyword arguments: argument "multipleOf" must be greater than 0', - }) + }), + validateBigIntMultipleOfCanMatchRange ); +function validateBigIntMultipleOfCanMatchRange(_args = [], context = {}) { + const argsByName = context?.argsByName || {}; + const { min, max, multipleOf } = argsByName; + + if ( + typeof min === 'undefined' || + typeof max === 'undefined' || + typeof multipleOf === 'undefined' || + typeof min !== 'number' || + typeof max !== 'number' || + typeof multipleOf !== 'number' || + !Number.isInteger(min) || + !Number.isInteger(max) || + !Number.isInteger(multipleOf) || + multipleOf <= 0 + ) { + return { ok: true }; + } + + const minValue = BigInt(min); + const maxValue = BigInt(max); + const step = BigInt(multipleOf); + const remainder = ((minValue % step) + step) % step; + const firstMultiple = remainder === 0n ? minValue : minValue + (step - remainder); + + if (firstMultiple > maxValue) { + return { + ok: false, + error: + 'Invalid keyword arguments: arguments "min", "max", and "multipleOf" do not allow any generated BigInt value', + }; + } + + return { ok: true }; +} + const NUMBER_BIG_INT_KEYWORD_DEFINITION = { keyword: 'number.bigInt', delegate: { diff --git a/packages/core/js/keywords/domain/shared/common-arg-validators.js b/packages/core/js/keywords/domain/shared/common-arg-validators.js new file mode 100644 index 00000000..672eb5d7 --- /dev/null +++ b/packages/core/js/keywords/domain/shared/common-arg-validators.js @@ -0,0 +1,16 @@ +import { + composeArgsValidators, + createIntegerArgValidator, + createNumericArgRangeValidator, +} from '../../../domain/domain-keyword-arg-validators.js'; + +function createPositiveIntegerArgsValidator(argNames = []) { + return composeArgsValidators( + ...argNames.flatMap((argName) => [ + createIntegerArgValidator({ argName }), + createNumericArgRangeValidator({ argName, min: 0, inclusiveMin: false }), + ]) + ); +} + +export { createPositiveIntegerArgsValidator }; diff --git a/packages/core/js/keywords/domain/string/alpha-keyword-definition.js b/packages/core/js/keywords/domain/string/alpha-keyword-definition.js index 12fffec6..a4692668 100644 --- a/packages/core/js/keywords/domain/string/alpha-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/alpha-keyword-definition.js @@ -1,6 +1,8 @@ import { validateAlphaStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const STRING_CASING_TYPE = 'upper|lower|mixed'; +const validateStringAlphaArgs = createPositiveIntegerArgsValidator(['length']); const STRING_ALPHA_KEYWORD_DEFINITION = { keyword: 'string.alpha', @@ -14,6 +16,7 @@ const STRING_ALPHA_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateAlphaStringValue, + argsValidator: validateStringAlphaArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/alphanumeric-keyword-definition.js b/packages/core/js/keywords/domain/string/alphanumeric-keyword-definition.js index 0315cbf6..121b3a3c 100644 --- a/packages/core/js/keywords/domain/string/alphanumeric-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/alphanumeric-keyword-definition.js @@ -1,6 +1,8 @@ import { validateAlphanumericStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const STRING_CASING_TYPE = 'upper|lower|mixed'; +const validateStringAlphanumericArgs = createPositiveIntegerArgsValidator(['length']); const STRING_ALPHANUMERIC_KEYWORD_DEFINITION = { keyword: 'string.alphanumeric', @@ -14,6 +16,7 @@ const STRING_ALPHANUMERIC_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateAlphanumericStringValue, + argsValidator: validateStringAlphanumericArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/binary-keyword-definition.js b/packages/core/js/keywords/domain/string/binary-keyword-definition.js index e97789d0..361ce09f 100644 --- a/packages/core/js/keywords/domain/string/binary-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/binary-keyword-definition.js @@ -1,4 +1,7 @@ import { validateBinaryStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateStringBinaryArgs = createPositiveIntegerArgsValidator(['length']); const STRING_BINARY_KEYWORD_DEFINITION = { keyword: 'string.binary', @@ -12,6 +15,7 @@ const STRING_BINARY_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateBinaryStringValue, + argsValidator: validateStringBinaryArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/from-characters-keyword-definition.js b/packages/core/js/keywords/domain/string/from-characters-keyword-definition.js index 0c7fa822..2e27103f 100644 --- a/packages/core/js/keywords/domain/string/from-characters-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/from-characters-keyword-definition.js @@ -1,4 +1,7 @@ import { validateFromCharactersStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateStringFromCharactersArgs = createPositiveIntegerArgsValidator(['length']); const STRING_FROM_CHARACTERS_KEYWORD_DEFINITION = { keyword: 'string.fromCharacters', @@ -11,6 +14,7 @@ const STRING_FROM_CHARACTERS_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateFromCharactersStringValue, + argsValidator: validateStringFromCharactersArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/hexadecimal-keyword-definition.js b/packages/core/js/keywords/domain/string/hexadecimal-keyword-definition.js index 99b2b516..85e4d476 100644 --- a/packages/core/js/keywords/domain/string/hexadecimal-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/hexadecimal-keyword-definition.js @@ -1,6 +1,8 @@ import { validateHexadecimalStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const STRING_CASING_TYPE = 'upper|lower|mixed'; +const validateStringHexadecimalArgs = createPositiveIntegerArgsValidator(['length']); const STRING_HEXADECIMAL_KEYWORD_DEFINITION = { keyword: 'string.hexadecimal', @@ -14,6 +16,7 @@ const STRING_HEXADECIMAL_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateHexadecimalStringValue, + argsValidator: validateStringHexadecimalArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/nanoid-keyword-definition.js b/packages/core/js/keywords/domain/string/nanoid-keyword-definition.js index 8dfc0c2a..6efbc0d8 100644 --- a/packages/core/js/keywords/domain/string/nanoid-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/nanoid-keyword-definition.js @@ -1,4 +1,7 @@ import { validateNanoIdValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateStringNanoidArgs = createPositiveIntegerArgsValidator(['length']); const STRING_NANOID_KEYWORD_DEFINITION = { keyword: 'string.nanoid', @@ -11,6 +14,7 @@ const STRING_NANOID_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateNanoIdValue, + argsValidator: validateStringNanoidArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/numeric-keyword-definition.js b/packages/core/js/keywords/domain/string/numeric-keyword-definition.js index 6bb02ee7..1113a0b1 100644 --- a/packages/core/js/keywords/domain/string/numeric-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/numeric-keyword-definition.js @@ -1,4 +1,7 @@ import { validateNumericStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateStringNumericArgs = createPositiveIntegerArgsValidator(['length']); const STRING_NUMERIC_KEYWORD_DEFINITION = { keyword: 'string.numeric', @@ -12,6 +15,7 @@ const STRING_NUMERIC_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateNumericStringValue, + argsValidator: validateStringNumericArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/octal-keyword-definition.js b/packages/core/js/keywords/domain/string/octal-keyword-definition.js index 45c6b0a3..64c89b26 100644 --- a/packages/core/js/keywords/domain/string/octal-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/octal-keyword-definition.js @@ -1,4 +1,7 @@ import { validateOctalStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateStringOctalArgs = createPositiveIntegerArgsValidator(['length']); const STRING_OCTAL_KEYWORD_DEFINITION = { keyword: 'string.octal', @@ -12,6 +15,7 @@ const STRING_OCTAL_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateOctalStringValue, + argsValidator: validateStringOctalArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/sample-keyword-definition.js b/packages/core/js/keywords/domain/string/sample-keyword-definition.js index 63d069a5..f9ce7fd7 100644 --- a/packages/core/js/keywords/domain/string/sample-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/sample-keyword-definition.js @@ -1,4 +1,7 @@ import { validateSampleStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateStringSampleArgs = createPositiveIntegerArgsValidator(['length']); const STRING_SAMPLE_KEYWORD_DEFINITION = { keyword: 'string.sample', @@ -11,6 +14,7 @@ const STRING_SAMPLE_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateSampleStringValue, + argsValidator: validateStringSampleArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/string/symbol-keyword-definition.js b/packages/core/js/keywords/domain/string/symbol-keyword-definition.js index 53f6b329..189199d4 100644 --- a/packages/core/js/keywords/domain/string/symbol-keyword-definition.js +++ b/packages/core/js/keywords/domain/string/symbol-keyword-definition.js @@ -1,4 +1,7 @@ import { validateSymbolStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateStringSymbolArgs = createPositiveIntegerArgsValidator(['length']); const STRING_SYMBOL_KEYWORD_DEFINITION = { keyword: 'string.symbol', @@ -12,6 +15,7 @@ const STRING_SYMBOL_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/string', fakerDocsUrl: 'https://fakerjs.dev/api/string', validator: validateSymbolStringValue, + argsValidator: validateStringSymbolArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/adjective-keyword-definition.js b/packages/core/js/keywords/domain/word/adjective-keyword-definition.js index 9a7884bc..425ba1c1 100644 --- a/packages/core/js/keywords/domain/word/adjective-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/adjective-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const WORD_SELECTION_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateWordSelectionArgs = createPositiveIntegerArgsValidator(['length']); const WORD_ADJECTIVE_KEYWORD_DEFINITION = { keyword: 'word.adjective', @@ -14,6 +16,7 @@ const WORD_ADJECTIVE_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordSelectionArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/adverb-keyword-definition.js b/packages/core/js/keywords/domain/word/adverb-keyword-definition.js index 48149b45..f1a6009f 100644 --- a/packages/core/js/keywords/domain/word/adverb-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/adverb-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const WORD_SELECTION_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateWordSelectionArgs = createPositiveIntegerArgsValidator(['length']); const WORD_ADVERB_KEYWORD_DEFINITION = { keyword: 'word.adverb', @@ -14,6 +16,7 @@ const WORD_ADVERB_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordSelectionArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/conjunction-keyword-definition.js b/packages/core/js/keywords/domain/word/conjunction-keyword-definition.js index 464b2bd3..df49000a 100644 --- a/packages/core/js/keywords/domain/word/conjunction-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/conjunction-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const WORD_SELECTION_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateWordSelectionArgs = createPositiveIntegerArgsValidator(['length']); const WORD_CONJUNCTION_KEYWORD_DEFINITION = { keyword: 'word.conjunction', @@ -14,6 +16,7 @@ const WORD_CONJUNCTION_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordSelectionArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/interjection-keyword-definition.js b/packages/core/js/keywords/domain/word/interjection-keyword-definition.js index bc4c00eb..ce7c9c60 100644 --- a/packages/core/js/keywords/domain/word/interjection-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/interjection-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const WORD_SELECTION_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateWordSelectionArgs = createPositiveIntegerArgsValidator(['length']); const WORD_INTERJECTION_KEYWORD_DEFINITION = { keyword: 'word.interjection', @@ -14,6 +16,7 @@ const WORD_INTERJECTION_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordSelectionArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/noun-keyword-definition.js b/packages/core/js/keywords/domain/word/noun-keyword-definition.js index 5f9fae5e..dda301c8 100644 --- a/packages/core/js/keywords/domain/word/noun-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/noun-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const WORD_SELECTION_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateWordSelectionArgs = createPositiveIntegerArgsValidator(['length']); const WORD_NOUN_KEYWORD_DEFINITION = { keyword: 'word.noun', @@ -14,6 +16,7 @@ const WORD_NOUN_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordSelectionArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/preposition-keyword-definition.js b/packages/core/js/keywords/domain/word/preposition-keyword-definition.js index fc4f105d..5439359a 100644 --- a/packages/core/js/keywords/domain/word/preposition-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/preposition-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const WORD_SELECTION_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateWordSelectionArgs = createPositiveIntegerArgsValidator(['length']); const WORD_PREPOSITION_KEYWORD_DEFINITION = { keyword: 'word.preposition', @@ -14,6 +16,7 @@ const WORD_PREPOSITION_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordSelectionArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/sample-keyword-definition.js b/packages/core/js/keywords/domain/word/sample-keyword-definition.js index 7e089b44..c4951350 100644 --- a/packages/core/js/keywords/domain/word/sample-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/sample-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const WORD_SELECTION_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateWordSelectionArgs = createPositiveIntegerArgsValidator(['length']); const WORD_SAMPLE_KEYWORD_DEFINITION = { keyword: 'word.sample', @@ -15,6 +17,7 @@ const WORD_SAMPLE_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordSelectionArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/verb-keyword-definition.js b/packages/core/js/keywords/domain/word/verb-keyword-definition.js index 010952f2..23255e34 100644 --- a/packages/core/js/keywords/domain/word/verb-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/verb-keyword-definition.js @@ -1,6 +1,8 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; const WORD_SELECTION_STRATEGY_TYPE = 'fail|closest|shortest|longest|any-length'; +const validateWordSelectionArgs = createPositiveIntegerArgsValidator(['length']); const WORD_VERB_KEYWORD_DEFINITION = { keyword: 'word.verb', @@ -14,6 +16,7 @@ const WORD_VERB_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordSelectionArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/js/keywords/domain/word/words-keyword-definition.js b/packages/core/js/keywords/domain/word/words-keyword-definition.js index 91860696..9ebc74cb 100644 --- a/packages/core/js/keywords/domain/word/words-keyword-definition.js +++ b/packages/core/js/keywords/domain/word/words-keyword-definition.js @@ -1,4 +1,7 @@ import { validateStringValue } from '../../../command-help/command-help-validators.js'; +import { createPositiveIntegerArgsValidator } from '../shared/common-arg-validators.js'; + +const validateWordWordsArgs = createPositiveIntegerArgsValidator(['count']); const WORD_WORDS_KEYWORD_DEFINITION = { keyword: 'word.words', @@ -12,6 +15,7 @@ const WORD_WORDS_KEYWORD_DEFINITION = { docsUrl: 'https://anywaydata.com/docs/test-data/domain/word', fakerDocsUrl: 'https://fakerjs.dev/api/word', validator: validateStringValue, + argsValidator: validateWordWordsArgs, returnType: 'string', usageExamples: [ { diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-doc-generator-output.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-doc-generator-output.test.js index 28e0bd0c..c2f701cd 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-doc-generator-output.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-doc-generator-output.test.js @@ -164,6 +164,10 @@ describe('domain docs generator output', () => { expect(airlineDoc).not.toContain('### `airline.airplane`'); expect(airlineDoc).toContain('### `airplane.name`'); expect(airlineDoc).toContain('### `airplane.iataTypeCode`'); + expect(airlineDoc).toContain('### `airline.iataCode`'); + expect(airlineDoc).toContain('```txt\nairline.iataCode\n```'); + expect(airlineDoc).not.toContain('```txt\nairline.airline.iataCode\n```'); + expect(airlineDoc).not.toContain('```txt\nawd.domain.airline.airline.iataCode\n```'); expect(financeDoc).not.toContain('### `finance.currency`'); expect(financeDoc).toContain('### `finance.currencyCode`'); diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js index 64b00d87..58ecd469 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-keyword-params-usage.test.js @@ -83,6 +83,19 @@ function applyKeywordExecutionDefaults(keyword, args) { return args; } +function expectedLoremCountArgs(argName, sample) { + if (argName === 'separator') { + return [undefined, sample]; + } + if (argName === 'max' || /CountMax$/.test(argName)) { + return [{ min: 1, max: sample }]; + } + if (argName === 'min' || /CountMin$/.test(argName) || /Count$/.test(argName)) { + return [sample]; + } + return []; +} + function expectsRuntimeRejection(keywordName, argName) { void keywordName; void argName; @@ -125,6 +138,8 @@ describe('domain keyword parameter usage', () => { expect(receivedArgs[0]).toEqual( expect.objectContaining({ [argSpec.name]: expectedDelegatedArgValue(argSpec, sample) }) ); + } else if (keyword.delegate.argTransform === 'loremCountFromHelpArgs') { + expect(receivedArgs).toEqual(expectedLoremCountArgs(argSpec.name, sample)); } else { expect(receivedArgs[argIndex]).toEqual(sample); } diff --git a/packages/core/src/tests/data_generation/unit/domain/domain-test-data-rule-validator.test.js b/packages/core/src/tests/data_generation/unit/domain/domain-test-data-rule-validator.test.js index 53683f37..b30b14b8 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domain-test-data-rule-validator.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domain-test-data-rule-validator.test.js @@ -1,3 +1,4 @@ +import { faker } from '@faker-js/faker'; import { DomainTestDataRuleValidator } from '../../../../../js/data_generation/domain/domainTestDataRuleValidator.js'; describe('DomainTestDataRuleValidator', () => { @@ -74,4 +75,32 @@ describe('DomainTestDataRuleValidator', () => { }) ); }); + + test('accepts syntactically valid domain commands without dry-run execution when no faker is supplied', () => { + const validator = new DomainTestDataRuleValidator(); + + const isValid = validator.validate({ ruleSpec: 'word.adjective(length=999, strategy="fail")' }); + + expect(isValid).toBe(true); + }); + + test('rejects domain commands that would throw during generation when faker is supplied', () => { + const validator = new DomainTestDataRuleValidator(faker); + + const isValid = validator.validate({ ruleSpec: 'word.adjective(length=999, strategy="fail")' }); + + expect(isValid).toBe(false); + expect(validator.getValidationError()).toContain('No words found that match the given length.'); + }); + + test('rejects unsatisfiable BigInt params before generation', () => { + const validator = new DomainTestDataRuleValidator(faker); + + const isValid = validator.validate({ ruleSpec: 'number.bigInt(min=1, max=5, multipleOf=10)' }); + + expect(isValid).toBe(false); + expect(validator.getValidationError()).toBe( + 'Invalid keyword arguments: arguments "min", "max", and "multipleOf" do not allow any generated BigInt value' + ); + }); }); diff --git a/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js b/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js index 30ffaf3b..e67c66c4 100644 --- a/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js +++ b/packages/core/src/tests/data_generation/unit/domain/domainKeywords.test.js @@ -422,6 +422,46 @@ describe('domain keyword delegation', () => { }); expect(result).toBe('should-run'); }); + + test('passes lorem wordCount to faker using the positional count argument', () => { + const callArgs = []; + const fakeFaker = { + lorem: { + words: (...receivedArgs) => { + callArgs.push(receivedArgs); + return 'one two three four five'; + }, + }, + }; + + const result = executeDomainKeyword('lorem.words', { + faker: fakeFaker, + args: [undefined, undefined, 5], + }); + + expect(result).toBe('one two three four five'); + expect(callArgs).toEqual([[5]]); + }); + + test('passes lorem separator after transformed count argument', () => { + const callArgs = []; + const fakeFaker = { + lorem: { + sentences: (...receivedArgs) => { + callArgs.push(receivedArgs); + return 'One.-Two.'; + }, + }, + }; + + const result = executeDomainKeyword('lorem.sentences', { + faker: fakeFaker, + args: [undefined, undefined, 2, '-', undefined, undefined], + }); + + expect(result).toBe('One.-Two.'); + expect(callArgs).toEqual([[2, '-']]); + }); }); describe('domain keyword arg validation', () => { @@ -460,6 +500,57 @@ describe('domain keyword arg validation', () => { }); }); + test('rejects empty internet.httpStatusCode type arrays before generation', () => { + const keyword = getDomainKeywordByAlias('internet.httpStatusCode'); + const result = validateDomainKeywordArgs(keyword, [[]]); + + expect(result).toEqual({ + ok: false, + error: 'Invalid keyword arguments: argument "types" must not be empty', + }); + }); + + test.each([ + ['git.commitSha', [0], 'length'], + ['word.words', [0], 'count'], + ['lorem.word', [0], 'length'], + ['string.alpha', [0], 'length'], + ['finance.accountNumber', [0], 'length'], + ['date.betweens', [0, 1577836800000, 1609372800000], 'count'], + ])('rejects zero %s size/count parameters before generation', (keywordName, args, argName) => { + const keyword = getDomainKeywordByAlias(keywordName); + const result = validateDomainKeywordArgs(keyword, args); + + expect(result).toEqual({ + ok: false, + error: `Invalid keyword arguments: argument "${argName}" must be greater than 0`, + }); + }); + + test.each([ + ['image.dataUri', [10, -1], 'height'], + ['image.urlPicsumPhotos', [-1, 10], 'width'], + ['image.url', [-1, 10], 'height'], + ])('rejects negative image dimension parameters before generation', (keywordName, args, argName) => { + const keyword = getDomainKeywordByAlias(keywordName); + const result = validateDomainKeywordArgs(keyword, args); + + expect(result).toEqual({ + ok: false, + error: `Invalid keyword arguments: argument "${argName}" must be greater than 0`, + }); + }); + + test('rejects reversed lorem count bounds before generation', () => { + const keyword = getDomainKeywordByAlias('lorem.words'); + const result = validateDomainKeywordArgs(keyword, [undefined, undefined, undefined, 3, 5]); + + expect(result).toEqual({ + ok: false, + error: 'Invalid keyword arguments: argument "wordCountMin" must be less than or equal to argument "wordCountMax"', + }); + }); + test('treats comma-separated list metadata as string-compatible for datatype.enum', () => { const keyword = getDomainKeywordByAlias('datatype.enum'); const result = validateDomainKeywordArgs(keyword, ['active,inactive,pending']); diff --git a/scripts/generate-domain-docs.mjs b/scripts/generate-domain-docs.mjs index 84fd8250..dd0009a8 100644 --- a/scripts/generate-domain-docs.mjs +++ b/scripts/generate-domain-docs.mjs @@ -138,10 +138,27 @@ function escapeMarkdownTableCell(value) { return escapeMdxText(normalized.replaceAll('\n', '
')).replaceAll('|', '\\|'); } +function toDisplayFunctionCall(functionCall, entry) { + const call = String(functionCall || '').trim(); + const keyword = String(entry?.keyword || '').trim(); + const displayCommand = String(entry?.displayCommand || keyword).trim(); + if (!call || !keyword || !displayCommand || displayCommand === keyword) { + return call; + } + + const prefixes = [`awd.domain.${keyword}`, `domain.${keyword}`, keyword]; + const matchedPrefix = prefixes.find((prefix) => call === prefix || call.startsWith(`${prefix}(`)); + if (!matchedPrefix) { + return call; + } + + return `${displayCommand}${call.slice(matchedPrefix.length)}`; +} + function getDefinitionUsageExamples(entry) { return (Array.isArray(entry?.help?.usageExamples) ? entry.help.usageExamples : []) .map((usageExample) => { - const functionCall = String(usageExample?.functionCall || '').trim(); + const functionCall = toDisplayFunctionCall(usageExample?.functionCall, entry); if (!functionCall) { return null; }