From 58e75b2c799e211ed65f6dc77822eb0c448c6135 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 17 Jun 2026 23:12:19 +0100 Subject: [PATCH 1/3] remove faker help metadata adapter --- .../shared/domain-command-help-metadata.js | 28 +- .../shared/faker-command-help-metadata.js | 4 - .../test-data/help/help-model-builder.js | 2 +- ...chema-interaction-scenario-builder.test.js | 2 +- .../support/generator-interaction-harness.js | 2 +- .../schema-interaction-scenario-builder.js | 2 +- .../support/generated-value-quality.js | 2 +- .../utils/faker-command-help-metadata.test.js | 37 +- packages/core/js/domain/domain-keywords.js | 24 + .../js/faker/faker-command-help-metadata.js | 2571 ----------------- packages/core/js/faker/faker-commands.js | 25 +- .../faker/faker-helper-keyword-definitions.js | 141 +- .../unit/domain/domainKeywords.test.js | 74 +- scripts/generate-faker-help.js | 595 ---- .../generate-schema-interaction-matrix.mjs | 2 +- 15 files changed, 250 insertions(+), 3261 deletions(-) delete mode 100644 packages/core-ui/js/gui_components/shared/faker-command-help-metadata.js delete mode 100644 packages/core/js/faker/faker-command-help-metadata.js delete mode 100644 scripts/generate-faker-help.js diff --git a/packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js b/packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js index a97b6f8b..08332274 100644 --- a/packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js +++ b/packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js @@ -1,4 +1,4 @@ -import { getDomainKeywordByCommand } from './domain-commands.js'; +import { getDomainKeywordHelpByAlias } from '@anywaydata/core/domain/domain-keywords.js'; const ANYWAYDATA_DOMAIN_DOCS_BASE = 'https://anywaydata.com/docs/test-data/domain'; @@ -58,24 +58,24 @@ function getDomainCommandHelp(command) { if (synthetic) { return synthetic; } - const keyword = getDomainKeywordByCommand(command); - if (!keyword) { + const commandHelp = getDomainKeywordHelpByAlias(command); + if (!commandHelp) { return null; } return { - canonical: keyword.canonical, - summary: keyword.help?.summary || '', - docsUrl: resolveDomainDocsUrl(command, keyword.help?.docsUrl || ''), - example: keyword.help?.example || '', - examples: Array.isArray(keyword.help?.examples) ? keyword.help.examples : [], - exampleReturnValues: Array.isArray(keyword.help?.exampleReturnValues) - ? keyword.help.exampleReturnValues - : Array.isArray(keyword.help?.returnExamples) - ? keyword.help.returnExamples + canonical: commandHelp.canonical, + summary: commandHelp.summary || '', + docsUrl: resolveDomainDocsUrl(command, commandHelp.docsUrl || ''), + example: commandHelp.example || '', + examples: Array.isArray(commandHelp.examples) ? commandHelp.examples : [], + exampleReturnValues: Array.isArray(commandHelp.exampleReturnValues) + ? commandHelp.exampleReturnValues + : Array.isArray(commandHelp.returnExamples) + ? commandHelp.returnExamples : [], - returnType: keyword.help?.returnType || '', - args: Array.isArray(keyword.help?.args) ? keyword.help.args : [], + returnType: commandHelp.returnType || '', + args: Array.isArray(commandHelp.args) ? commandHelp.args : [], }; } diff --git a/packages/core-ui/js/gui_components/shared/faker-command-help-metadata.js b/packages/core-ui/js/gui_components/shared/faker-command-help-metadata.js deleted file mode 100644 index 8de9d27e..00000000 --- a/packages/core-ui/js/gui_components/shared/faker-command-help-metadata.js +++ /dev/null @@ -1,4 +0,0 @@ -export { - FAKER_COMMAND_HELP_METADATA, - getFakerCommandHelp, -} from '@anywaydata/core/faker/faker-command-help-metadata.js'; diff --git a/packages/core-ui/js/gui_components/shared/test-data/help/help-model-builder.js b/packages/core-ui/js/gui_components/shared/test-data/help/help-model-builder.js index deac2124..8f874a72 100644 --- a/packages/core-ui/js/gui_components/shared/test-data/help/help-model-builder.js +++ b/packages/core-ui/js/gui_components/shared/test-data/help/help-model-builder.js @@ -5,7 +5,7 @@ * - Keeps command help behavior unit-testable without booting full pages. */ -import { getFakerCommandHelp } from '../../faker-command-help-metadata.js'; +import { getFakerCommandHelp } from '@anywaydata/core/faker/faker-helper-keyword-definitions.js'; import { getDomainCommandHelp } from '../../domain-command-help-metadata.js'; import { escapeHtml } from '../../html-escape.js'; import { 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 8646ccda..000942c4 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 @@ -1,4 +1,4 @@ -import { getFakerCommandHelp } from '../../../../js/gui_components/shared/faker-command-help-metadata.js'; +import { getFakerCommandHelp } from '@anywaydata/core/faker/faker-helper-keyword-definitions.js'; import { getKnownDomainCommandsAlphabetical, getDomainKeywordByCommand, diff --git a/packages/core-ui/src/tests/interaction/matrix/support/generator-interaction-harness.js b/packages/core-ui/src/tests/interaction/matrix/support/generator-interaction-harness.js index 01bafa10..a22741ca 100644 --- a/packages/core-ui/src/tests/interaction/matrix/support/generator-interaction-harness.js +++ b/packages/core-ui/src/tests/interaction/matrix/support/generator-interaction-harness.js @@ -4,7 +4,7 @@ import RandExp from 'randexp'; import { TestDataGenerator } from '@anywaydata/core/data_generation/testDataGenerator.js'; import { Exporter } from '@anywaydata/core/grid/exporter.js'; import { createDataGeneratorPage } from '../../../../../js/gui_components/generator/runtime/create-generator-page.js'; -import { getFakerCommandHelp } from '../../../../../js/gui_components/shared/faker-command-help-metadata.js'; +import { getFakerCommandHelp } from '@anywaydata/core/faker/faker-helper-keyword-definitions.js'; import { getDomainCommandHelp } from '../../../../../js/gui_components/shared/domain-command-help-metadata.js'; import { resolveFakerDocsUrl } from '../../../../../js/gui_components/shared/test-data/help/help-model-builder.js'; import { diff --git a/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js b/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js index 2d1b21bd..4b2a3f04 100644 --- a/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js +++ b/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js @@ -1,5 +1,5 @@ import { getAllowedFakerCommandsAlphabetical } from '../../../../../js/gui_components/shared/faker-commands.js'; -import { getFakerCommandHelp } from '../../../../../js/gui_components/shared/faker-command-help-metadata.js'; +import { getFakerCommandHelp } from '@anywaydata/core/faker/faker-helper-keyword-definitions.js'; import { faker } from '@faker-js/faker'; import RandExp from 'randexp'; import { TestDataGenerator } from '@anywaydata/core/data_generation/testDataGenerator.js'; diff --git a/packages/core-ui/src/tests/interaction/support/generated-value-quality.js b/packages/core-ui/src/tests/interaction/support/generated-value-quality.js index 541d7ee3..566bb7e2 100644 --- a/packages/core-ui/src/tests/interaction/support/generated-value-quality.js +++ b/packages/core-ui/src/tests/interaction/support/generated-value-quality.js @@ -1,4 +1,4 @@ -import { getFakerCommandHelp } from '../../../../js/gui_components/shared/faker-command-help-metadata.js'; +import { getFakerCommandHelp } from '@anywaydata/core/faker/faker-helper-keyword-definitions.js'; import { getDomainCommandHelp } from '../../../../js/gui_components/shared/domain-command-help-metadata.js'; import { SOURCE_TYPE_DOMAIN, diff --git a/packages/core-ui/src/tests/utils/faker-command-help-metadata.test.js b/packages/core-ui/src/tests/utils/faker-command-help-metadata.test.js index 0d8fd73d..bf50cf03 100644 --- a/packages/core-ui/src/tests/utils/faker-command-help-metadata.test.js +++ b/packages/core-ui/src/tests/utils/faker-command-help-metadata.test.js @@ -1,15 +1,24 @@ import { KNOWN_FAKER_COMMANDS } from '../../../js/gui_components/shared/faker-commands.js'; import { - FAKER_COMMAND_HELP_METADATA, getFakerCommandHelp, -} from '../../../js/gui_components/shared/faker-command-help-metadata.js'; + FAKER_HELPER_KEYWORD_DEFINITIONS, + buildFakerHelperHelpMetadata, +} from '@anywaydata/core/faker/faker-helper-keyword-definitions.js'; describe('faker command help metadata', () => { - test('contains help entries for all curated faker commands', () => { - const fakerCommands = KNOWN_FAKER_COMMANDS.filter((command) => command !== 'RegEx'); + test('contains direct metadata entries only for faker-only helper commands', () => { + const fakerCommands = KNOWN_FAKER_COMMANDS.filter((command) => command.startsWith('helpers.')); + expect(fakerCommands).toEqual(Object.keys(FAKER_HELPER_KEYWORD_DEFINITIONS)); + const helperMetadata = buildFakerHelperHelpMetadata(); fakerCommands.forEach((command) => { - expect(FAKER_COMMAND_HELP_METADATA[command]).toBeDefined(); + expect(helperMetadata[command]).toBeDefined(); }); + expect(helperMetadata['person.firstName']).toBeUndefined(); + }); + + test('normalizes helper metadata directly from helper definitions', () => { + const helperMetadata = buildFakerHelperHelpMetadata(); + expect(Object.keys(helperMetadata)).toEqual(Object.keys(FAKER_HELPER_KEYWORD_DEFINITIONS)); }); test('contains docsUrl for all curated faker commands', () => { @@ -34,29 +43,26 @@ describe('faker command help metadata', () => { expect(helper.docsUrl).toBe('https://fakerjs.dev/api/helpers'); }); - test('includes params when signatures are available and supports fallback summaries', () => { + test('falls back to domain keyword help for domain-backed faker commands', () => { const firstName = getFakerCommandHelp('person.firstName'); const nestedPropertyAccess = getFakerCommandHelp('airline.airplane.name'); const imageDataUri = getFakerCommandHelp('image.dataUri'); const uuid = getFakerCommandHelp('string.uuid'); expect(firstName.params).toEqual( - expect.arrayContaining([ - expect.objectContaining({ name: 'sex', optional: true, type: "'female' | 'generic' | 'male'" }), - ]) + expect.arrayContaining([expect.objectContaining({ name: 'sex', optional: true, type: 'string' })]) ); expect(firstName.example.length).toBeGreaterThan(0); + expect(buildFakerHelperHelpMetadata()['person.firstName']).toBeUndefined(); - expect(imageDataUri.params).toEqual( - expect.arrayContaining([expect.objectContaining({ name: 'options', optional: true, type: 'object' })]) - ); + expect(Array.isArray(imageDataUri.params)).toBe(true); expect(imageDataUri.example.length).toBeGreaterThan(0); expect(uuid.summary).toContain('Returns a UUID'); expect(uuid.params).toEqual( expect.arrayContaining([ - expect.objectContaining({ name: 'version', optional: true, type: '4 | 7' }), - expect.objectContaining({ name: 'refDate', optional: true, type: 'string | Date | number' }), + expect.objectContaining({ name: 'version', optional: true, type: '4|7' }), + expect.objectContaining({ name: 'refDate', optional: true, type: 'string|number|date' }), ]) ); expect(uuid.params.find((param) => param.name === 'version')?.description).toContain( @@ -75,6 +81,7 @@ describe('faker command help metadata', () => { const mustache = getFakerCommandHelp('helpers.mustache'); const arrayElement = getFakerCommandHelp('helpers.arrayElement'); const rangeToNumber = getFakerCommandHelp('helpers.rangeToNumber'); + const multiple = getFakerCommandHelp('helpers.multiple'); expect(mustache.summary).toContain('Replaces {{placeholder}} tokens'); expect(mustache.params).toEqual( @@ -100,5 +107,7 @@ describe('faker command help metadata', () => { ); expect(rangeToNumber.example).toBe('2'); expect(rangeToNumber.examples).toContain('helpers.rangeToNumber({ min: 1, max: 2 })'); + + expect(multiple.returnType).toBe('array'); }); }); diff --git a/packages/core/js/domain/domain-keywords.js b/packages/core/js/domain/domain-keywords.js index 37efeb46..722dc10a 100644 --- a/packages/core/js/domain/domain-keywords.js +++ b/packages/core/js/domain/domain-keywords.js @@ -139,6 +139,28 @@ function getDomainKeywordByAlias(alias, index = DOMAIN_KEYWORD_ALIAS_INDEX) { return index.byAlias[key]; } +function normalizeDomainKeywordHelp(keyword) { + if (!keyword) { + return null; + } + + return { + canonical: String(keyword.canonical || '').trim(), + keyword: String(keyword.keyword || '').trim(), + summary: String(keyword.help?.summary || '').trim(), + docsUrl: String(keyword.help?.docsUrl || '').trim(), + example: String(keyword.help?.example || '').trim(), + examples: Array.isArray(keyword.help?.examples) ? keyword.help.examples : [], + exampleReturnValues: Array.isArray(keyword.help?.exampleReturnValues) ? keyword.help.exampleReturnValues : [], + returnType: String(keyword.help?.returnType || '').trim(), + args: Array.isArray(keyword.help?.args) ? keyword.help.args : [], + }; +} + +function getDomainKeywordHelpByAlias(alias, index = DOMAIN_KEYWORD_ALIAS_INDEX) { + return normalizeDomainKeywordHelp(getDomainKeywordByAlias(alias, index)); +} + function runFakerDelegate(target, fakerInstance, args = [], resultPath = '') { const parts = String(target || '') .split('.') @@ -348,4 +370,6 @@ export { buildAliasCandidates, getCommandFromCanonical, getDomainKeywordByAlias, + getDomainKeywordHelpByAlias, + normalizeDomainKeywordHelp, }; diff --git a/packages/core/js/faker/faker-command-help-metadata.js b/packages/core/js/faker/faker-command-help-metadata.js deleted file mode 100644 index 88e78f80..00000000 --- a/packages/core/js/faker/faker-command-help-metadata.js +++ /dev/null @@ -1,2571 +0,0 @@ -// AUTO-GENERATED FILE. DO NOT EDIT BY HAND. -// Generated by scripts/generate-faker-help.js on 2026-06-17T13:59:19.041Z - -const FAKER_COMMAND_HELP_METADATA = { - 'datatype.boolean': { - summary: 'Returns the boolean value true or false.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { probability?: number; }', - description: 'The optional options object or the probability (`[0.00, 1.00]`) of returning `true`.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/datatype', - example: 'false', - }, - 'date.month': { - summary: 'Returns a random name of a month.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: 'January', - }, - 'date.weekday': { - summary: 'Returns a random day of the week.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: 'Friday', - }, - 'date.timeZone': { - summary: 'Returns a random IANA time zone relevant to this locale.', - params: [], - docsUrl: 'https://fakerjs.dev/api/date', - example: 'America/Eirunepe', - }, - 'date.anytime': { - summary: 'Generates a random date that can be either in the past or in the future.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: '"2026-09-02T05:33:22.911Z"', - }, - 'date.past': { - summary: 'Generates a random date in the past.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: '"2026-05-06T00:25:19.398Z"', - }, - 'date.future': { - summary: 'Generates a random date in the future.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: '"2027-05-09T20:30:18.671Z"', - }, - 'date.between': { - summary: 'Generates a random date between the given boundaries.', - params: [ - { - name: 'options', - optional: false, - type: 'object', - description: 'The options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: '', - }, - 'date.betweens': { - summary: - 'Generates random dates between the given boundaries. The dates will be returned in an array sorted in chronological order.', - params: [ - { - name: 'options', - optional: false, - type: 'object', - description: 'The options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: '', - }, - 'date.recent': { - summary: 'Generates a random date in the recent past.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: '"2026-06-17T01:26:41.116Z"', - }, - 'date.soon': { - summary: 'Generates a random date in the near future.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: '"2026-06-18T06:17:52.501Z"', - }, - 'date.birthdate': { - summary: - 'Returns a random birthdate. By default, the birthdate is generated for an adult between 18 and 80 years old.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The options to use to generate the birthdate.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/date', - example: '"1989-04-12T22:30:14.738Z"', - }, - 'helpers.fake': { - summary: 'Interpolates faker template placeholders inside a string and returns the rendered result.', - params: [ - { - name: 'pattern', - optional: false, - type: 'string', - description: 'Template string containing faker placeholders such as {{person.firstName}} or {{location.city}}.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.fake("Hi, my name is {{person.firstName}} {{person.lastName}}!")'], - }, - 'helpers.mustache': { - summary: 'Replaces {{placeholder}} tokens in a string using values from the supplied data object.', - params: [ - { - name: 'text', - optional: false, - type: 'string', - description: 'Template text containing mustache placeholders such as {{name}}.', - }, - { - name: 'data', - optional: false, - type: 'object', - description: 'Object that provides replacement values for the placeholders used in the text.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.mustache("Hello {{name}}", { name: "Ada" })'], - }, - 'helpers.fromRegExp': { - summary: 'Generates a string that matches the supplied regular-expression-style pattern.', - params: [ - { - name: 'pattern', - optional: false, - type: 'string | RegExp', - description: 'Regular expression, or regex-like string, used to generate matching output.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.fromRegExp("[A-Z]{2}[0-9]{2}")'], - }, - 'helpers.maybe': { - summary: 'Calls a callback and returns its value only when faker decides the optional value should be present.', - params: [ - { - name: 'callback', - optional: false, - type: '() => unknown', - description: 'Callback used to generate the value when the optional branch is chosen.', - }, - { - name: 'options', - optional: true, - type: 'object', - description: 'Optional configuration controlling probability and fallback behavior.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.maybe(() => "enabled")'], - }, - 'helpers.arrayElement': { - summary: 'Returns one random element from the supplied array.', - params: [ - { - name: 'array', - optional: false, - type: 'array', - description: 'Array of candidate values to choose from.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.arrayElement(["A", "B", "C"])'], - }, - 'helpers.objectKey': { - summary: 'Returns one random key from the supplied object.', - params: [ - { - name: 'object', - optional: false, - type: 'object', - description: 'Object whose enumerable keys are used as candidate values.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.objectKey({ red: "#f00", blue: "#00f" })'], - }, - 'helpers.objectValue': { - summary: 'Returns one random value from the supplied object.', - params: [ - { - name: 'object', - optional: false, - type: 'object', - description: 'Object whose enumerable values are used as candidate values.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.objectValue({ red: "#f00", blue: "#00f" })'], - }, - 'helpers.objectEntry': { - summary: 'Returns one random [key, value] entry from the supplied object.', - params: [ - { - name: 'object', - optional: false, - type: 'object', - description: 'Object whose enumerable entries are used as candidate values.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.objectEntry({ red: "#f00", blue: "#00f" })'], - }, - 'helpers.enumValue': { - summary: 'Returns one random value from the supplied enum-like object.', - params: [ - { - name: 'enumObject', - optional: false, - type: 'object', - description: 'Enum-like object to sample from while ignoring numeric reverse-mapping keys.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.enumValue({ Pending: "pending", Active: "active" })'], - }, - 'helpers.slugify': { - summary: 'Converts a string into a URL-friendly slug.', - params: [ - { - name: 'string', - optional: true, - type: 'string', - description: 'Input text to normalize into a lowercase, hyphen-separated slug.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.slugify("Hello World 2026")'], - }, - 'helpers.replaceSymbols': { - summary: 'Replaces placeholder symbols such as # and ? in a string with random digits or letters.', - params: [ - { - name: 'string', - optional: true, - type: 'string', - description: 'Template string containing placeholder symbols to replace.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.replaceSymbols("##??-##")'], - }, - 'helpers.replaceCreditCardSymbols': { - summary: 'Replaces credit-card placeholders and computes a valid Luhn checksum for the result.', - params: [ - { - name: 'string', - optional: true, - type: 'string', - description: 'Credit card template containing placeholder symbols such as #, !, and L.', - }, - { - name: 'symbol', - optional: true, - type: 'string', - description: 'Replacement symbol to use for digit placeholders instead of the default #.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '6453-3055-3531-6943-9296', - examples: ['helpers.replaceCreditCardSymbols("1234-[4-9]-##!!-L")'], - }, - 'helpers.shuffle': { - summary: 'Returns a shuffled copy of the supplied array.', - params: [ - { - name: 'array', - optional: false, - type: 'array', - description: 'Array of values to shuffle.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.shuffle(["A", "B", "C"])'], - }, - 'helpers.uniqueArray': { - summary: 'Builds an array of unique values by repeatedly sampling a source until the requested length is reached.', - params: [ - { - name: 'source', - optional: false, - type: 'array | () => unknown', - description: 'Array of possible values or a callback used to generate candidate values.', - }, - { - name: 'length', - optional: false, - type: 'number', - description: 'Number of unique values to return.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '[]', - examples: ['helpers.uniqueArray(["red", "green", "blue"], 2)'], - }, - 'helpers.weightedArrayElement': { - summary: 'Returns one value from a weighted array, favoring entries with higher weights.', - params: [ - { - name: 'array', - optional: false, - type: 'array', - description: 'Array of { weight, value } objects used for weighted selection.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.weightedArrayElement([{ weight: 5, value: "sunny" }, { weight: 1, value: "rainy" }])'], - }, - 'helpers.arrayElements': { - summary: 'Returns multiple random elements from the supplied array.', - params: [ - { - name: 'array', - optional: false, - type: 'array', - description: 'Array of candidate values to sample from.', - }, - { - name: 'count', - optional: true, - type: 'number | { min: number; max: number; }', - description: 'Exact number of items to return, or a min/max range for the returned item count.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '', - examples: ['helpers.arrayElements(["A", "B", "C"], 2)'], - }, - 'helpers.rangeToNumber': { - summary: 'Converts a number or { min, max } range into a concrete number.', - params: [ - { - name: 'numberOrRange', - optional: false, - type: 'number | { min: number; max: number; }', - description: 'Fixed number or range object to resolve into a single numeric value.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '2', - examples: ['helpers.rangeToNumber({ min: 1, max: 2 })'], - }, - 'helpers.multiple': { - summary: 'Calls a generator callback multiple times and returns the collected results as an array.', - params: [ - { - name: 'method', - optional: false, - type: '() => unknown', - description: 'Callback used to generate each array entry.', - }, - { - name: 'options', - optional: true, - type: 'number | object', - description: 'Exact count or configuration controlling how many values to generate.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/helpers', - example: '[null,null,null]', - examples: ['helpers.multiple(() => faker.person.firstName(), { count: 3 })'], - }, - 'number.int': { - summary: 'Returns a single random integer between zero and the given max value or the given range.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { min?: number; max?: number; multipleOf?: number; }', - description: 'Maximum value or options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/number', - example: '7150841710724570', - }, - 'number.float': { - summary: - 'Returns a single random floating-point number, by default between `0.0` and `1.0`. To change the range, pass a `min` and `max` value. To limit the number of decimal places, pass a `multipleOf` or `fractionDigits` parameter.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { min?: number; max?: number; fractionDigits?: number; multipleOf?: ...', - description: 'Upper bound or options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/number', - example: '0.21833343025858343', - }, - 'number.binary': { - summary: 'Returns a binary string.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/number', - example: '0', - }, - 'number.octal': { - summary: 'Returns an octal string.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/number', - example: '3', - }, - 'number.hex': { - summary: 'Returns a lowercase hexadecimal number.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { min?: number; max?: number; }', - description: 'Maximum value or options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/number', - example: '9', - }, - 'number.bigInt': { - summary: 'Returns a BigInt number.', - params: [ - { - name: 'options', - optional: true, - type: 'bigint | number | string | boolean | { min?: bigint | number | string | boole...', - description: 'Maximum value or options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/number', - example: '784863713077920', - }, - 'number.romanNumeral': { - summary: 'Returns a roman numeral in String format.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { min?: number; max?: number; }', - description: 'Maximum value or options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/number', - example: 'CCXCII', - }, - 'string.fromCharacters': { - summary: 'Generates a string from the given characters.', - params: [ - { - name: 'characters', - optional: false, - type: 'string | ReadonlyArray', - description: 'The characters to use for the string. Can be a string or an array of characters.', - }, - { - name: 'length', - optional: true, - type: 'number | { min: number; max: number; }', - description: - 'The length of the string to generate either as a fixed length or as a length range. Defaults to `1`.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/string', - example: '', - }, - 'string.alpha': { - summary: 'Generating a string consisting of letters in the English alphabet.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { length?: number | { min: number; max: number; }; casing?: Casing; ...', - description: 'Either the length of the string to generate or the optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/string', - example: 'c', - }, - 'string.alphanumeric': { - summary: 'Generating a string consisting of alpha characters and digits.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { length?: number | { min: number; max: number; }; casing?: Casing; ...', - description: 'Either the length of the string to generate or the optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/string', - example: 'p', - }, - 'string.binary': { - summary: 'Returns a binary string.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/string', - example: '0b0', - }, - 'string.octal': { - summary: 'Returns an octal string.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/string', - example: '0o3', - }, - 'string.hexadecimal': { - summary: 'Returns a hexadecimal string.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/string', - example: '0xE', - }, - 'string.numeric': { - summary: 'Generates a given length string of digits.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { length?: number | { min: number; max: number; }; allowLeadingZeros...', - description: 'Either the length of the string to generate or the optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/string', - example: '1', - }, - 'string.sample': { - summary: 'Returns a string containing UTF-16 chars between 33 and 125 (`!` to `}`).', - params: [ - { - name: 'length', - optional: true, - type: 'number | { min: number; max: number; }', - description: - 'The length of the string (excluding the prefix) to generate either as a fixed length or as a length range. Defaults to `10`.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/string', - example: '%.9XN Date: Tue Jun 16 19:33:21 2026 -050...', - }, - 'git.commitMessage': { - summary: 'Generates a random commit message.', - params: [], - docsUrl: 'https://fakerjs.dev/api/git', - example: 'bypass virtual panel', - }, - 'git.commitDate': { - summary: 'Generates a date string for a git commit using the same format as `git log`.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/git', - example: 'Wed Jun 17 02:30:07 2026 -0300', - }, - 'git.commitSha': { - summary: 'Generates a random commit sha.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options for the commit sha.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/git', - example: '13f5495ca719a28129d8786af9cfdafd499630ab', - }, - 'hacker.abbreviation': { - summary: 'Returns a random hacker/IT abbreviation.', - params: [], - docsUrl: 'https://fakerjs.dev/api/hacker', - example: 'IB', - }, - 'hacker.adjective': { - summary: 'Returns a random hacker/IT adjective.', - params: [], - docsUrl: 'https://fakerjs.dev/api/hacker', - example: 'online', - }, - 'hacker.noun': { - summary: 'Returns a random hacker/IT noun.', - params: [], - docsUrl: 'https://fakerjs.dev/api/hacker', - example: 'bus', - }, - 'hacker.verb': { - summary: 'Returns a random hacker/IT verb.', - params: [], - docsUrl: 'https://fakerjs.dev/api/hacker', - example: 'input', - }, - 'hacker.ingverb': { - summary: 'Returns a random hacker/IT verb for continuous actions (en: ing suffix; e.g. hacking).', - params: [], - docsUrl: 'https://fakerjs.dev/api/hacker', - example: 'hacking', - }, - 'hacker.phrase': { - summary: 'Generates a random hacker/IT phrase.', - params: [], - docsUrl: 'https://fakerjs.dev/api/hacker', - example: 'The CLI monitor is down, calculate the virtual card so we can hack the SSD feed!', - }, - 'image.avatar': { - summary: 'Generates a random avatar image url.', - params: [], - docsUrl: 'https://fakerjs.dev/api/image', - example: 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/41.jpg', - }, - 'image.avatarGitHub': { - summary: 'Generates a random avatar from GitHub.', - params: [], - docsUrl: 'https://fakerjs.dev/api/image', - example: 'https://avatars.githubusercontent.com/u/26821954', - }, - 'image.personPortrait': { - summary: 'Generates a random square portrait (avatar) of a person.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options for generating an AI avatar.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/image', - example: 'https://cdn.jsdelivr.net/gh/faker-js/assets-person-portrait/female/512/36.jpg', - }, - 'image.url': { - summary: 'Generates a random image url.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options for generating a URL for an image.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/image', - example: 'https://picsum.photos/seed/rzQTnoguH/3585/118', - }, - 'image.urlLoremFlickr': { - summary: 'Generates a random image url provided via https://loremflickr.com.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options for generating a URL for an image.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/image', - example: 'https://loremflickr.com/3768/1590?lock=1884839181438131', - }, - 'image.urlPicsumPhotos': { - summary: 'Generates a random image url provided via https://picsum.photos.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options for generating a URL for an image.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/image', - example: 'https://picsum.photos/seed/oMtfJo/3110/1710?blur=4', - }, - 'image.dataUri': { - summary: 'Generates a random data uri containing an URL-encoded SVG image or a Base64-encoded SVG image.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options for generating a data uri.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/image', - example: - '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%22ful...', - }, - 'internet.email': { - summary: 'Generates data using faker internet email.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'Grayce_Lockman25@hotmail.com', - }, - 'internet.exampleEmail': { - summary: 'Generates data using faker internet example email.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'Ernest.Cummerata@example.com', - }, - 'internet.username': { - summary: "Generates a username using the given person's name as base.", - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'Parker51', - }, - 'internet.displayName': { - summary: "Generates a display name using the given person's name as base.", - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'Edmond.Purdy89', - }, - 'internet.protocol': { - summary: 'Returns a random web protocol. Either `http` or `https`.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'http', - }, - 'internet.httpMethod': { - summary: 'Returns a random http method.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'POST', - }, - 'internet.httpStatusCode': { - summary: 'Generates a random HTTP status code.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: '200', - }, - 'internet.url': { - summary: 'Generates a random http(s) url.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'https://messy-lender.info', - }, - 'internet.domainName': { - summary: 'Generates a random domain name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'babyish-coil.com', - }, - 'internet.domainSuffix': { - summary: 'Returns a random domain suffix.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'name', - }, - 'internet.domainWord': { - summary: 'Generates a random domain word.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'dense-reboot', - }, - 'internet.ip': { - summary: 'Generates a random IPv4 or IPv6 address.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: '182.79.48.165', - }, - 'internet.ipv4': { - summary: 'Generates a random IPv4 address.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: '64.28.230.183', - }, - 'internet.ipv6': { - summary: 'Generates a random IPv6 address.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: '5dc7:dac3:d94f:49a4:e50a:65a6:6a8c:bed1', - }, - 'internet.port': { - summary: 'Generates a random port number.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: '16582', - }, - 'internet.userAgent': { - summary: 'Generates a random user agent string.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: - 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/584.67.7 (KHTML, like Gecko) Version/13_0 Mobile/15E148 Safari/556.28', - }, - 'internet.mac': { - summary: 'Generates a random mac address.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'f2:11:4f:58:7a:fb', - }, - 'internet.password': { - summary: - 'Generates a random password-like string. Do not use this method for generating actual passwords for users.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'HJ_nOPuIg_ZKf_m', - }, - 'internet.emoji': { - summary: 'Generates a random emoji.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: '🎨', - }, - 'internet.jwtAlgorithm': { - summary: 'Generates a random JWT (JSON Web Token) Algorithm.', - params: [], - docsUrl: 'https://fakerjs.dev/api/internet', - example: 'HS384', - }, - 'internet.jwt': { - summary: 'Generates a random JWT (JSON Web Token).', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/internet', - example: - 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3ODE2NTQ3NDYsImV4cCI6MTc4MTY1NDg2NCwibmJmIjoxODA5ODU2MTE1LCJpc3MiOiJLb2VscGluIC0gU2tpbGVz...', - }, - 'location.zipCode': { - summary: 'Generates data using faker location zip code.', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: '59251-3772', - }, - 'location.city': { - summary: 'Generates a random localized city name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'Spring Valley', - }, - 'location.buildingNumber': { - summary: 'Generates a random building number.', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: '34703', - }, - 'location.street': { - summary: 'Generates a random localized street name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'Mathilde Brooks', - }, - 'location.streetAddress': { - summary: 'Generates a random localized street address.', - params: [ - { - name: 'options', - optional: true, - type: 'boolean | { useFullAddress?: boolean; }', - description: 'Whether to use a full address or an options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: '9368 Lancaster Road', - }, - 'location.secondaryAddress': { - summary: 'Generates a random localized secondary address. This refers to a specific location at a given address', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'Suite 877', - }, - 'location.county': { - summary: - "Returns a random localized county, or other equivalent second-level administrative entity for the locale's country such as a district or department.", - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'Carroll County', - }, - 'location.country': { - summary: 'Returns a random country name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'Tunisia', - }, - 'location.continent': { - summary: 'Returns a random continent name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'South America', - }, - 'location.countryCode': { - summary: 'Returns a random ISO_3166-1 country code.', - params: [ - { - name: 'options', - optional: true, - type: "'alpha-2' | 'alpha-3' | 'numeric' | { variant?: 'alpha-2' | 'alpha-3' | 'nume...", - description: 'The code to return or an options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'JE', - }, - 'location.state': { - summary: - "Returns a random localized state, or other equivalent first-level administrative entity for the locale's country such as a province or region.", - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'California', - }, - 'location.latitude': { - summary: 'Generates a random latitude.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: '70.4568', - }, - 'location.longitude': { - summary: 'Generates a random longitude.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: '-101.4125', - }, - 'location.direction': { - summary: 'Returns a random direction (cardinal and ordinal; northwest, east, etc).', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'Southwest', - }, - 'location.cardinalDirection': { - summary: 'Returns a random cardinal direction (north, east, south, west).', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'North', - }, - 'location.ordinalDirection': { - summary: 'Returns a random ordinal direction (northwest, southeast, etc).', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Whether to use abbreviated or an options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'Southwest', - }, - 'location.nearbyGPSCoordinate': { - summary: 'Generates a random GPS coordinate within the specified radius from the given coordinate.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The options for generating a GPS coordinate.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/location', - example: '[77.1985,-142.0806]', - }, - 'location.timeZone': { - summary: 'Returns a random IANA time zone name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: 'Europe/Jersey', - }, - 'location.language': { - summary: 'Returns a random spoken language.', - params: [], - docsUrl: 'https://fakerjs.dev/api/location', - example: '{"name":"Finnish","alpha2":"fi","alpha3":"fin"}', - }, - 'lorem.word': { - summary: 'Generates a word of a specified length.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: 'ademptio', - }, - 'lorem.words': { - summary: 'Generates a space separated list of words.', - params: [ - { - name: 'wordCount', - optional: true, - type: 'number | { min: number; max: number; }', - description: 'The number of words to generate. Defaults to `3`.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: 'totidem abundans contigo', - }, - 'lorem.sentence': { - summary: 'Generates a space separated list of words beginning with a capital letter and ending with a period.', - params: [ - { - name: 'wordCount', - optional: true, - type: 'number | { min: number; max: number; }', - description: - 'The number of words, that should be in the sentence. Defaults to a random number between `3` and `10`.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: 'Summisse consectetur trucido cornu alias vobis.', - }, - 'lorem.slug': { - summary: 'Generates a slugified text consisting of the given number of hyphen separated words.', - params: [ - { - name: 'wordCount', - optional: true, - type: 'number | { min: number; max: number; }', - description: 'The number of words to generate. Defaults to `3`.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: 'talis-decumbo-defero', - }, - 'lorem.sentences': { - summary: 'Generates the given number of sentences.', - params: [ - { - name: 'sentenceCount', - optional: true, - type: 'number | { min: number; max: number; }', - description: 'The number of sentences to generate. Defaults to a random number between `2` and `6`.', - }, - { - name: 'separator', - optional: true, - type: 'string', - description: "The separator to add between sentences. Defaults to `' '`.", - }, - ], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: - 'Brevis alias tamen attero validus ante quam accendo. Verbum triumphus tenus deinde censura ager utroque theca clibanus arcesso. Adiuvo us...', - }, - 'lorem.paragraph': { - summary: 'Generates a paragraph with the given number of sentences.', - params: [ - { - name: 'sentenceCount', - optional: true, - type: 'number | { min: number; max: number; }', - description: 'The number of sentences to generate. Defaults to `3`.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: - 'Terreo aptus vel cubo. Sponte appositus arca conforto carpo tum communis deficio conduco. Cursim doloribus victoria sui commodo vinitor a...', - }, - 'lorem.paragraphs': { - summary: 'Generates the given number of paragraphs.', - params: [ - { - name: 'paragraphCount', - optional: true, - type: 'number | { min: number; max: number; }', - description: 'The number of paragraphs to generate. Defaults to `3`.', - }, - { - name: 'separator', - optional: true, - type: 'string', - description: "The separator to use. Defaults to `'\\n'`.", - }, - ], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: - 'Quia utpote tamdiu summopere laboriosam repellendus ubi certe comptus ipsa. Copia quisquam acsi atrocitas cupressus. Surculus uberrime au...', - }, - 'lorem.text': { - summary: 'Generates a random text based on a random lorem method.', - params: [], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: - 'Vilis animi solus vapulus admoveo. Solvo comburo ab accusator urbs angustus coepi vel accedo vallum. Arbor volubilis aptus civitas amet s...', - }, - 'lorem.lines': { - summary: "Generates the given number lines of lorem separated by `'\\n'`.", - params: [ - { - name: 'lineCount', - optional: true, - type: 'number | { min: number; max: number; }', - description: 'The number of lines to generate. Defaults to a random number between `1` and `5`.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/lorem', - example: 'Audacia veritatis condico.', - }, - 'music.album': { - summary: 'Returns a random album name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/music', - example: 'Editorial', - }, - 'music.artist': { - summary: 'Returns a random artist name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/music', - example: 'Kacey Musgraves', - }, - 'music.genre': { - summary: 'Returns a random music genre.', - params: [], - docsUrl: 'https://fakerjs.dev/api/music', - example: 'Arena Rock', - }, - 'music.songName': { - summary: 'Returns a random song name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/music', - example: 'Ac-cent-tchu-ate the Positive', - }, - 'person.firstName': { - summary: 'Returns a random first name.', - params: [ - { - name: 'sex', - optional: true, - type: "'female' | 'generic' | 'male'", - description: 'The optional sex to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Jeffrey', - }, - 'person.lastName': { - summary: 'Returns a random last name.', - params: [ - { - name: 'sex', - optional: true, - type: "'female' | 'generic' | 'male'", - description: 'The optional sex to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Spencer', - }, - 'person.middleName': { - summary: 'Returns a random middle name.', - params: [ - { - name: 'sex', - optional: true, - type: "'female' | 'generic' | 'male'", - description: 'The optional sex to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Elliott', - }, - 'person.fullName': { - summary: 'Generates a random full name.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Ms. Meda Yundt', - }, - 'person.gender': { - summary: 'Returns a random gender.', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Cis man', - }, - 'person.sex': { - summary: 'Returns a random sex.', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'female', - }, - 'person.sexType': { - summary: 'Returns a random sex type. The `SexType` is intended to be used in parameters and conditions.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'male', - }, - 'person.bio': { - summary: 'Returns a random short biography', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'collectivization devotee', - }, - 'person.prefix': { - summary: 'Returns a random person prefix.', - params: [ - { - name: 'sex', - optional: true, - type: "'female' | 'generic' | 'male'", - description: "The optional sex to use. Can be either `'female'` or `'male'`.", - }, - ], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Mr.', - }, - 'person.suffix': { - summary: 'Returns a random person suffix.', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Sr.', - }, - 'person.jobTitle': { - summary: 'Generates a random job title.', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Chief Research Strategist', - }, - 'person.jobDescriptor': { - summary: 'Generates a random job descriptor.', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Senior', - }, - 'person.jobArea': { - summary: 'Generates a random job area.', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Integration', - }, - 'person.jobType': { - summary: 'Generates a random job type.', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Liaison', - }, - 'person.zodiacSign': { - summary: 'Returns a random zodiac sign.', - params: [], - docsUrl: 'https://fakerjs.dev/api/person', - example: 'Taurus', - }, - 'phone.number': { - summary: 'Generates a random phone number.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'Options object', - }, - ], - docsUrl: 'https://fakerjs.dev/api/phone', - example: '(335) 502-1989 x93402', - }, - 'phone.imei': { - summary: 'Generates IMEI number.', - params: [], - docsUrl: 'https://fakerjs.dev/api/phone', - example: '38-776589-992047-5', - }, - 'science.chemicalElement.symbol': { - summary: 'Returns a random periodic table element.', - params: [], - docsUrl: 'https://fakerjs.dev/api/science', - example: 'As', - }, - 'science.chemicalElement.name': { - summary: 'Returns a random periodic table element.', - params: [], - docsUrl: 'https://fakerjs.dev/api/science', - example: 'Cerium', - }, - 'science.chemicalElement.atomicNumber': { - summary: 'Returns a random periodic table element.', - params: [], - docsUrl: 'https://fakerjs.dev/api/science', - example: '48', - }, - 'science.unit': { - summary: 'Returns a random scientific unit.', - params: [], - docsUrl: 'https://fakerjs.dev/api/science', - example: '{"name":"weber","symbol":"Wb"}', - }, - 'system.fileName': { - summary: 'Returns a random file name with extension.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'An options object.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/system', - example: 'spring.mjs', - }, - 'system.commonFileName': { - summary: 'Returns a random file name with a given extension or a commonly used extension.', - params: [ - { - name: 'extension', - optional: true, - type: 'string', - description: 'The file extension to use. Empty string is considered to be not set.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/system', - example: 'phrase_slope_shirk.wav', - }, - 'system.mimeType': { - summary: 'Returns a mime-type.', - params: [], - docsUrl: 'https://fakerjs.dev/api/system', - example: 'application/ld+json', - }, - 'system.commonFileType': { - summary: 'Returns a commonly used file type.', - params: [], - docsUrl: 'https://fakerjs.dev/api/system', - example: 'text', - }, - 'system.commonFileExt': { - summary: 'Returns a commonly used file extension.', - params: [], - docsUrl: 'https://fakerjs.dev/api/system', - example: 'html', - }, - 'system.fileType': { - summary: 'Returns a file type.', - params: [], - docsUrl: 'https://fakerjs.dev/api/system', - example: 'audio', - }, - 'system.fileExt': { - summary: 'Returns a file extension.', - params: [ - { - name: 'mimeType', - optional: true, - type: 'string', - description: 'Valid mime-type', - }, - ], - docsUrl: 'https://fakerjs.dev/api/system', - example: 'xlw', - }, - 'system.directoryPath': { - summary: 'Returns a directory path.', - params: [], - docsUrl: 'https://fakerjs.dev/api/system', - example: '/bin', - }, - 'system.filePath': { - summary: 'Returns a file path.', - params: [], - docsUrl: 'https://fakerjs.dev/api/system', - example: '/home/user/wherever.doc', - }, - 'system.semver': { - summary: 'Returns a semantic version.', - params: [], - docsUrl: 'https://fakerjs.dev/api/system', - example: '5.10.1', - }, - 'system.networkInterface': { - summary: 'Returns a random network interface.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/system', - example: 'P8wwp3s6f0d4', - }, - 'system.cron': { - summary: 'Returns a random cron expression.', - params: [ - { - name: 'options', - optional: true, - type: 'object', - description: 'The optional options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/system', - example: '* 22 * * 0', - }, - 'vehicle.vehicle': { - summary: 'Returns a random vehicle.', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'NIO ATS', - }, - 'vehicle.manufacturer': { - summary: 'Returns a manufacturer name.', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'NIO', - }, - 'vehicle.model': { - summary: 'Returns a vehicle model.', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'Wrangler', - }, - 'vehicle.type': { - summary: 'Returns a vehicle type.', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'Passenger Van', - }, - 'vehicle.fuel': { - summary: 'Returns a fuel type.', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'Hybrid', - }, - 'vehicle.vin': { - summary: 'Returns a vehicle identification number (VIN).', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'CEF1BK8XL1TH38388', - }, - 'vehicle.color': { - summary: 'Returns a vehicle color.', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'ivory', - }, - 'vehicle.vrm': { - summary: 'Returns a vehicle registration number (Vehicle Registration Mark - VRM)', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'UW77UEA', - }, - 'vehicle.bicycle': { - summary: 'Returns a type of bicycle.', - params: [], - docsUrl: 'https://fakerjs.dev/api/vehicle', - example: 'Touring Bicycle', - }, - 'word.adjective': { - summary: 'Returns a random adjective.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'courteous', - }, - 'word.adverb': { - summary: 'Returns a random adverb.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'rapidly', - }, - 'word.conjunction': { - summary: 'Returns a random conjunction.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'for', - }, - 'word.interjection': { - summary: 'Returns a random interjection.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'drat', - }, - 'word.noun': { - summary: 'Returns a random noun.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'scholarship', - }, - 'word.preposition': { - summary: 'Returns a random preposition.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'of', - }, - 'word.verb': { - summary: 'Returns a random verb.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'homeschool', - }, - 'word.sample': { - summary: - 'Returns a random word, that can be an adjective, adverb, conjunction, interjection, noun, preposition, or verb.', - params: [ - { - name: 'options', - optional: true, - type: "number | { length?: number | { min: number; max: number; }; strategy?: 'fail'...", - description: 'The expected length of the word or the options to use.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'um', - }, - 'word.words': { - summary: 'Returns a random string containing some words separated by spaces.', - params: [ - { - name: 'options', - optional: true, - type: 'number | { count?: number | { min: number; max: number; }; }', - description: 'The optional options object or the number of words to return.', - }, - ], - docsUrl: 'https://fakerjs.dev/api/word', - example: 'instead zowie', - }, -}; - -function getFakerCommandHelp(commandValue) { - const command = String(commandValue || '').trim(); - return FAKER_COMMAND_HELP_METADATA[command]; -} - -export { FAKER_COMMAND_HELP_METADATA, getFakerCommandHelp }; diff --git a/packages/core/js/faker/faker-commands.js b/packages/core/js/faker/faker-commands.js index 479099e1..ae322c81 100644 --- a/packages/core/js/faker/faker-commands.js +++ b/packages/core/js/faker/faker-commands.js @@ -1,3 +1,7 @@ +import { FAKER_HELPER_KEYWORD_DEFINITIONS } from './faker-helper-keyword-definitions.js'; + +const HELPER_FAKER_COMMANDS = Object.keys(FAKER_HELPER_KEYWORD_DEFINITIONS); + const KNOWN_FAKER_COMMANDS = [ 'RegEx', @@ -15,25 +19,8 @@ const KNOWN_FAKER_COMMANDS = [ 'date.recent', 'date.soon', 'date.birthdate', - // Some helpers work just fine - 'helpers.fake', - 'helpers.mustache', - 'helpers.fromRegExp', - 'helpers.maybe', - 'helpers.arrayElement', - 'helpers.objectKey', - 'helpers.objectValue', - 'helpers.objectEntry', - 'helpers.enumValue', - 'helpers.slugify', - 'helpers.replaceSymbols', - 'helpers.replaceCreditCardSymbols', - 'helpers.shuffle', - 'helpers.uniqueArray', - 'helpers.weightedArrayElement', - 'helpers.arrayElements', - 'helpers.rangeToNumber', - 'helpers.multiple', + // Some helpers work just fine. Keep helper registration sourced from helper definitions. + ...HELPER_FAKER_COMMANDS, 'number.int', 'number.float', 'number.binary', diff --git a/packages/core/js/faker/faker-helper-keyword-definitions.js b/packages/core/js/faker/faker-helper-keyword-definitions.js index 104d8b97..e76de410 100644 --- a/packages/core/js/faker/faker-helper-keyword-definitions.js +++ b/packages/core/js/faker/faker-helper-keyword-definitions.js @@ -1,6 +1,15 @@ +import { getDomainKeywordHelpByAlias } from '../domain/domain-keywords.js'; + +const FAKER_HELPERS_DOCS_URL = 'https://fakerjs.dev/api/helpers'; + +// Faker helper definitions live here because helpers.* remains outside the domain keyword abstraction. +// These definitions are the source of truth for helper command registration, params, examples, and help metadata. const FAKER_HELPER_KEYWORD_DEFINITIONS = { 'helpers.fake': { summary: 'Interpolates faker template placeholders inside a string and returns the rendered result.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'string', params: [ { name: 'pattern', @@ -13,6 +22,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.mustache': { summary: 'Replaces {{placeholder}} tokens in a string using values from the supplied data object.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'string', params: [ { name: 'text', @@ -31,6 +43,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.fromRegExp': { summary: 'Generates a string that matches the supplied regular-expression-style pattern.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'string', params: [ { name: 'pattern', @@ -43,6 +58,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.maybe': { summary: 'Calls a callback and returns its value only when faker decides the optional value should be present.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'unknown', params: [ { name: 'callback', @@ -61,6 +79,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.arrayElement': { summary: 'Returns one random element from the supplied array.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'string', params: [ { name: 'array', @@ -73,6 +94,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.objectKey': { summary: 'Returns one random key from the supplied object.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'string', params: [ { name: 'object', @@ -85,6 +109,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.objectValue': { summary: 'Returns one random value from the supplied object.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'unknown', params: [ { name: 'object', @@ -97,6 +124,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.objectEntry': { summary: 'Returns one random [key, value] entry from the supplied object.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'array', params: [ { name: 'object', @@ -109,6 +139,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.enumValue': { summary: 'Returns one random value from the supplied enum-like object.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'unknown', params: [ { name: 'enumObject', @@ -121,6 +154,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.slugify': { summary: 'Converts a string into a URL-friendly slug.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'string', params: [ { name: 'string', @@ -133,6 +169,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.replaceSymbols': { summary: 'Replaces placeholder symbols such as # and ? in a string with random digits or letters.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'string', params: [ { name: 'string', @@ -145,6 +184,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.replaceCreditCardSymbols': { summary: 'Replaces credit-card placeholders and computes a valid Luhn checksum for the result.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '6453-2433-5050-4456-3526', + returnType: 'string', params: [ { name: 'string', @@ -163,6 +205,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.shuffle': { summary: 'Returns a shuffled copy of the supplied array.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'array', params: [ { name: 'array', @@ -175,6 +220,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.uniqueArray': { summary: 'Builds an array of unique values by repeatedly sampling a source until the requested length is reached.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '[]', + returnType: 'array', params: [ { name: 'source', @@ -193,6 +241,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.weightedArrayElement': { summary: 'Returns one value from a weighted array, favoring entries with higher weights.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'string', params: [ { name: 'array', @@ -205,6 +256,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.arrayElements': { summary: 'Returns multiple random elements from the supplied array.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '', + returnType: 'array', params: [ { name: 'array', @@ -223,6 +277,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.rangeToNumber': { summary: 'Converts a number or { min, max } range into a concrete number.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '2', + returnType: 'number', params: [ { name: 'numberOrRange', @@ -235,6 +292,9 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, 'helpers.multiple': { summary: 'Calls a generator callback multiple times and returns the collected results as an array.', + docsUrl: FAKER_HELPERS_DOCS_URL, + example: '[null,null,null]', + returnType: 'array', params: [ { name: 'method', @@ -253,4 +313,83 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { }, }; -export { FAKER_HELPER_KEYWORD_DEFINITIONS }; +function normalizeFakerHelperKeywordHelp(definition) { + if (!definition) { + return undefined; + } + + return { + summary: String(definition.summary || '').trim(), + params: Array.isArray(definition.params) + ? definition.params.map((param) => ({ + name: String(param.name || '').trim(), + optional: param.optional !== false, + type: String(param.type || '').trim(), + description: String(param.description || '').trim(), + examples: Array.isArray(param.examples) ? param.examples : [], + })) + : [], + docsUrl: String(definition.docsUrl || '').trim(), + example: String(definition.example || '').trim(), + examples: Array.isArray(definition.examples) ? definition.examples : [], + exampleReturnValues: Array.isArray(definition.exampleReturnValues) ? definition.exampleReturnValues : [], + returnType: String(definition.returnType || '').trim(), + }; +} + +function buildFakerHelperHelpMetadata(definitions = FAKER_HELPER_KEYWORD_DEFINITIONS) { + return Object.fromEntries( + Object.entries(definitions).map(([command, definition]) => [command, normalizeFakerHelperKeywordHelp(definition)]) + ); +} + +function getFakerHelperKeywordHelp(commandValue, definitions = FAKER_HELPER_KEYWORD_DEFINITIONS) { + const command = String(commandValue || '').trim(); + if (!Object.prototype.hasOwnProperty.call(definitions, command)) { + return undefined; + } + return normalizeFakerHelperKeywordHelp(definitions[command]); +} + +function mapDomainKeywordHelpToFakerCommandHelp(commandHelp) { + if (!commandHelp) { + return undefined; + } + + return { + summary: commandHelp.summary || '', + params: Array.isArray(commandHelp.args) + ? commandHelp.args.map((arg) => ({ + name: arg.name, + optional: arg.required !== true, + type: arg.type, + description: arg.description || '', + examples: Array.isArray(arg.examples) ? arg.examples : [], + })) + : [], + docsUrl: commandHelp.docsUrl || '', + example: commandHelp.example || '', + examples: Array.isArray(commandHelp.examples) ? commandHelp.examples : [], + exampleReturnValues: Array.isArray(commandHelp.exampleReturnValues) ? commandHelp.exampleReturnValues : [], + returnType: commandHelp.returnType || '', + }; +} + +function getFakerCommandHelp(commandValue, definitions = FAKER_HELPER_KEYWORD_DEFINITIONS) { + const command = String(commandValue || '').trim(); + const helperHelp = getFakerHelperKeywordHelp(command, definitions); + if (helperHelp) { + return helperHelp; + } + return mapDomainKeywordHelpToFakerCommandHelp(getDomainKeywordHelpByAlias(command)); +} + +export { + FAKER_HELPERS_DOCS_URL, + FAKER_HELPER_KEYWORD_DEFINITIONS, + buildFakerHelperHelpMetadata, + getFakerCommandHelp, + getFakerHelperKeywordHelp, + mapDomainKeywordHelpToFakerCommandHelp, + normalizeFakerHelperKeywordHelp, +}; 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 25b23f13..980ef35e 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 @@ -8,10 +8,15 @@ import { buildDomainKeywordCatalog, executeDomainKeyword, getDomainKeywordByAlias, + getDomainKeywordHelpByAlias, validateDomainKeywordArgs, } from '../../../../../js/domain/domain-keywords.js'; import { parseKeywordInvocation } from '../../../../../js/domain/domain-keyword-parser.js'; -import { FAKER_COMMAND_HELP_METADATA } from '../../../../../js/faker/faker-command-help-metadata.js'; +import { + buildFakerHelperHelpMetadata, + getFakerCommandHelp, +} from '../../../../../js/faker/faker-helper-keyword-definitions.js'; +import { FAKER_HELPER_KEYWORD_DEFINITIONS } from '../../../../../js/faker/faker-helper-keyword-definitions.js'; describe('domain keyword catalog', () => { test('registers canonical awd.domain keywords for faker commands', () => { @@ -48,49 +53,44 @@ describe('domain keyword catalog', () => { }); }); - test('includes all faker metadata params except object-wrapper options', () => { - const byKeyword = new Map(DOMAIN_KEYWORDS.map((entry) => [entry.keyword, entry])); - const mismatches = []; - - for (const [keyword, fakerHelp] of Object.entries(FAKER_COMMAND_HELP_METADATA)) { - const domainKeyword = byKeyword.get(keyword); - if (!domainKeyword) { - continue; - } - - const fakerParams = (fakerHelp?.params || []).map((param) => param.name).filter((name) => name !== 'options'); - const domainParams = new Set((domainKeyword.help?.args || []).map((arg) => arg.name)); - const missing = fakerParams.filter((name) => !domainParams.has(name)); - - if (missing.length > 0) { - mismatches.push({ keyword, missing }); - } - } - - expect(mismatches).toEqual([]); + test('keeps faker helper metadata file scoped to faker-only helper commands', () => { + const helperMetadata = buildFakerHelperHelpMetadata(); + expect(Object.keys(helperMetadata).length).toBeGreaterThan(0); + expect(Object.keys(helperMetadata).every((keyword) => keyword.startsWith('helpers.'))).toBe(true); + expect(Object.keys(helperMetadata)).toEqual(Object.keys(FAKER_HELPER_KEYWORD_DEFINITIONS)); }); - test('uses optionsFromHelpArgs for options-based faker commands with flattened args', () => { - const byKeyword = new Map(DOMAIN_KEYWORDS.map((entry) => [entry.keyword, entry])); - const missingTransforms = []; + test('exposes domain-backed faker command help from the domain keyword source of truth', () => { + const mismatches = []; - for (const [keyword, fakerHelp] of Object.entries(FAKER_COMMAND_HELP_METADATA)) { - const domainKeyword = byKeyword.get(keyword); - if (!domainKeyword || domainKeyword.delegate?.type !== 'faker') { - continue; - } + DOMAIN_KEYWORDS.filter((entry) => entry.delegate?.type === DELEGATE_TYPE_FAKER).forEach((entry) => { + const domainHelp = getDomainKeywordHelpByAlias(entry.keyword); + const fakerHelp = getFakerCommandHelp(entry.keyword); - const fakerParams = Array.isArray(fakerHelp?.params) ? fakerHelp.params : []; - const hasOptionsParam = fakerParams.some((param) => param.name === 'options'); - const hasFlattenedArgs = Array.isArray(domainKeyword.help?.args) && domainKeyword.help.args.length > 0; - const hasTransform = domainKeyword.delegate?.argTransform === 'optionsFromHelpArgs'; + if (!domainHelp || !fakerHelp) { + mismatches.push({ keyword: entry.keyword, reason: 'missing help entry' }); + return; + } - if (hasOptionsParam && hasFlattenedArgs && !hasTransform) { - missingTransforms.push(keyword); + const domainArgNames = (domainHelp.args || []).map((arg) => arg.name); + const fakerParamNames = (fakerHelp.params || []).map((param) => param.name); + + if ( + fakerHelp.summary !== domainHelp.summary || + fakerHelp.docsUrl !== domainHelp.docsUrl || + fakerHelp.example !== domainHelp.example || + fakerHelp.returnType !== domainHelp.returnType || + JSON.stringify(fakerParamNames) !== JSON.stringify(domainArgNames) + ) { + mismatches.push({ + keyword: entry.keyword, + domainArgNames, + fakerParamNames, + }); } - } + }); - expect(missingTransforms).toEqual([]); + expect(mismatches).toEqual([]); }); test('is defined from standalone domain definitions with explicit delegates', () => { diff --git a/scripts/generate-faker-help.js b/scripts/generate-faker-help.js deleted file mode 100644 index 8480a148..00000000 --- a/scripts/generate-faker-help.js +++ /dev/null @@ -1,595 +0,0 @@ -/* eslint-disable no-console */ -const fs = require('fs'); -const path = require('path'); -const { pathToFileURL } = require('url'); - -const repoRoot = path.resolve(__dirname, '..'); -const fakerDistDir = path.join(repoRoot, 'node_modules', '@faker-js', 'faker', 'dist'); -const outputFile = path.join(repoRoot, 'packages', 'core', 'js', 'faker', 'faker-command-help-metadata.js'); - -function sanitizeDocLine(line) { - return line - .replace(/^\s*\*\s?/, '') - .replace(/\{@link\s+([^}\s]+)(?:\s+([^}]+))?\}/g, (_, url, label) => label || url) - .replace(/\[(.*?)\]\((.*?)\)/g, '$1') - .trim(); -} - -function extractSummary(jsDocBlock) { - const lines = String(jsDocBlock || '') - .split('\n') - .map(sanitizeDocLine); - for (const line of lines) { - if (!line || line.startsWith('@')) { - continue; - } - if (line.startsWith('faker.')) { - continue; - } - if (line.startsWith('```')) { - continue; - } - return line; - } - return ''; -} - -function parseTypeAliases(declarationText) { - const aliases = new Map(); - const aliasPattern = /type\s+([A-Za-z_$][\w$]*)\s*=\s*([^;]+);/g; - let match; - while ((match = aliasPattern.exec(declarationText)) !== null) { - aliases.set(match[1], match[2].trim()); - } - return aliases; -} - -function parseEnumLiteralValues(declarationText) { - const enumValues = new Map(); - const enumPattern = /(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)\s*\{([\s\S]*?)\}/g; - let match; - while ((match = enumPattern.exec(declarationText)) !== null) { - const enumBody = match[2]; - const values = []; - const valuePattern = /=\s*['"]([^'"]+)['"]/g; - let valueMatch; - while ((valueMatch = valuePattern.exec(enumBody)) !== null) { - values.push(`'${valueMatch[1]}'`); - } - if (values.length > 0) { - enumValues.set(match[1], values.join(' | ')); - } - } - return enumValues; -} - -function resolveKnownAlias(typeText, aliases, enumValues, depth = 0) { - if (depth > 4) { - return null; - } - - const plainTypeName = String(typeText || '').trim(); - if (!plainTypeName || !aliases.has(plainTypeName)) { - return null; - } - - const aliasBody = aliases.get(plainTypeName); - if (!aliasBody) { - return null; - } - - const enumTemplateMatch = aliasBody.match(/^`?\$\{([A-Za-z_$][\w$]*)\}`?$/); - if (enumTemplateMatch) { - const enumName = enumTemplateMatch[1]; - return enumValues.get(enumName) || null; - } - - if (aliasBody.includes('|')) { - return aliasBody; - } - - if (aliases.has(aliasBody.trim())) { - return resolveKnownAlias(aliasBody.trim(), aliases, enumValues, depth + 1); - } - - return null; -} - -function simplifyType(typeText, aliases, enumValues) { - const withoutInlineComments = String(typeText || '') - .replace(/\/\*\*[\s\S]*?\*\//g, ' ') - .replace(/\/\*[\s\S]*?\*\//g, ' '); - const squashed = withoutInlineComments.replace(/\s+/g, ' ').trim(); - if (!squashed) { - return 'unknown'; - } - - const resolvedAlias = resolveKnownAlias(squashed, aliases, enumValues); - if (resolvedAlias) { - return resolvedAlias; - } - - if (squashed.startsWith('{') || squashed.startsWith('Record<')) { - return 'object'; - } - if (squashed.length > 80) { - return `${squashed.slice(0, 77)}...`; - } - return squashed; -} - -function splitTopLevel(text, separatorChar) { - const segments = []; - let current = ''; - let depthParen = 0; - let depthBrace = 0; - let depthBracket = 0; - let depthAngle = 0; - let inSingle = false; - let inDouble = false; - let inTemplate = false; - - for (let index = 0; index < text.length; index++) { - const char = text[index]; - const prev = index > 0 ? text[index - 1] : ''; - - if (!inDouble && !inTemplate && char === "'" && prev !== '\\') { - inSingle = !inSingle; - } else if (!inSingle && !inTemplate && char === '"' && prev !== '\\') { - inDouble = !inDouble; - } else if (!inSingle && !inDouble && char === '`' && prev !== '\\') { - inTemplate = !inTemplate; - } - - if (!inSingle && !inDouble && !inTemplate) { - if (char === '(') depthParen++; - if (char === ')') depthParen--; - if (char === '{') depthBrace++; - if (char === '}') depthBrace--; - if (char === '[') depthBracket++; - if (char === ']') depthBracket--; - if (char === '<') depthAngle++; - if (char === '>') depthAngle = Math.max(0, depthAngle - 1); - - if (char === separatorChar && depthParen === 0 && depthBrace === 0 && depthBracket === 0 && depthAngle === 0) { - const trimmed = current.trim(); - if (trimmed) { - segments.push(trimmed); - } - current = ''; - continue; - } - } - - current += char; - } - - const tail = current.trim(); - if (tail) { - segments.push(tail); - } - return segments; -} - -function extractParams(parameterText, aliases, enumValues) { - if (!parameterText || parameterText.trim().length === 0) { - return []; - } - - const params = []; - for (const segment of splitTopLevel(parameterText, ',')) { - const cleaned = segment - .replace(/\s*=\s*.+$/, '') - .replace(/^\.{3}/, '') - .trim(); - const match = cleaned.match(/^([A-Za-z_$][\w$]*)(\?)?\s*:\s*([\s\S]+)$/); - if (!match) { - continue; - } - - params.push({ - name: match[1], - optional: match[2] === '?', - type: simplifyType(match[3], aliases, enumValues), - }); - } - - return params; -} - -function extractParamDocs(jsDocBlock) { - const descriptionsByName = new Map(); - const lines = String(jsDocBlock || '') - .split('\n') - .map(sanitizeDocLine); - - for (const line of lines) { - const paramMatch = line.match(/^@param\s+(?:\{[^}]+\}\s+)?(\[[^\]]+\]|[A-Za-z_$][\w$\.\-]*)(?:\s+-?\s*)?(.*)$/); - if (!paramMatch) { - continue; - } - - const rawName = paramMatch[1]; - const nameWithoutBrackets = rawName.replace(/^\[/, '').replace(/\]$/, ''); - const nameWithoutDefault = nameWithoutBrackets.split('=')[0]; - const normalizedName = nameWithoutDefault.trim(); - if (!normalizedName) { - continue; - } - - const description = String(paramMatch[2] || '') - .replace(/\s+/g, ' ') - .trim(); - - if (description.length > 0 && !descriptionsByName.has(normalizedName)) { - descriptionsByName.set(normalizedName, description); - } - } - - return descriptionsByName; -} - -function findMatchingParenIndex(text, openParenIndex) { - let depth = 0; - let inSingle = false; - let inDouble = false; - let inTemplate = false; - - for (let index = openParenIndex; index < text.length; index++) { - const char = text[index]; - const prev = index > 0 ? text[index - 1] : ''; - - if (!inDouble && !inTemplate && char === "'" && prev !== '\\') { - inSingle = !inSingle; - } else if (!inSingle && !inTemplate && char === '"' && prev !== '\\') { - inDouble = !inDouble; - } else if (!inSingle && !inDouble && char === '`' && prev !== '\\') { - inTemplate = !inTemplate; - } - - if (inSingle || inDouble || inTemplate) { - continue; - } - - if (char === '(') { - depth++; - } else if (char === ')') { - depth--; - if (depth === 0) { - return index; - } - } - } - - return -1; -} - -function findNearestJsDoc(text, fromIndex) { - const before = text.slice(0, fromIndex); - const jsDocEnd = before.lastIndexOf('*/'); - if (jsDocEnd === -1) { - return ''; - } - - const jsDocStart = before.lastIndexOf('/**', jsDocEnd); - if (jsDocStart === -1) { - return ''; - } - - const between = before.slice(jsDocEnd + 2); - if (/[^\s]/.test(between)) { - return ''; - } - - return before.slice(jsDocStart + 3, jsDocEnd); -} - -function extractMethodCandidates(methodName, declarationText, aliases, enumValues) { - const candidates = []; - const methodToken = `${methodName}(`; - let searchIndex = 0; - - while (searchIndex < declarationText.length) { - const methodIndex = declarationText.indexOf(methodToken, searchIndex); - if (methodIndex === -1) { - break; - } - - const beforeChar = methodIndex > 0 ? declarationText[methodIndex - 1] : ''; - if (/[A-Za-z0-9_$]/.test(beforeChar)) { - searchIndex = methodIndex + methodToken.length; - continue; - } - - const openParenIndex = methodIndex + methodName.length; - const closeParenIndex = findMatchingParenIndex(declarationText, openParenIndex); - if (closeParenIndex === -1) { - searchIndex = methodIndex + methodToken.length; - continue; - } - - let indexAfterParen = closeParenIndex + 1; - while (indexAfterParen < declarationText.length && /\s/.test(declarationText[indexAfterParen])) { - indexAfterParen++; - } - if (declarationText[indexAfterParen] !== ':') { - searchIndex = methodIndex + methodToken.length; - continue; - } - - const parameterText = declarationText.slice(openParenIndex + 1, closeParenIndex); - const jsDoc = findNearestJsDoc(declarationText, methodIndex); - candidates.push({ - jsDoc, - summary: extractSummary(jsDoc), - params: extractParams(parameterText, aliases, enumValues), - paramDocs: extractParamDocs(jsDoc), - }); - - searchIndex = methodIndex + methodToken.length; - } - - return candidates; -} - -function scoreCandidate(command, candidate) { - const [moduleName, methodName] = command.split('.'); - const jsDoc = String(candidate?.jsDoc || ''); - const summary = String(candidate?.summary || '').toLowerCase(); - - let score = 0; - if (jsDoc.includes(`faker.${moduleName}.${methodName}`)) score += 5; - if (jsDoc.includes(`/api/${moduleName}.html#`)) score += 3; - if (jsDoc.includes(`faker.${moduleName}.`)) score += 1; - if (candidate.params.length > 0) score += 1; - if (summary.length > 0) score += 1; - if (summary.includes('proxy for localedefinition')) score -= 4; - if (summary.includes('type that provides auto-suggestions')) score -= 2; - return score; -} - -function extractMethodHelp(command, declarationText, aliases, enumValues) { - const parts = command.split('.'); - if (parts.length < 2) { - return null; - } - - const methodName = parts[1]; - const candidates = extractMethodCandidates(methodName, declarationText, aliases, enumValues); - if (candidates.length === 0) { - return null; - } - - let best = null; - let bestScore = Number.NEGATIVE_INFINITY; - for (const candidate of candidates) { - const score = scoreCandidate(command, candidate); - if (score > bestScore) { - bestScore = score; - best = candidate; - } - } - - if (!best || bestScore <= 0) { - return null; - } - - return { summary: best.summary, params: best.params, paramDocs: best.paramDocs }; -} - -function toReadablePhrase(command) { - const words = command.split('.').map((token) => token.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase()); - if (words.length === 0) { - return 'faker command'; - } - return words.join(' '); -} - -function buildFallbackSummary(command) { - return `Generates data using faker ${toReadablePhrase(command)}.`; -} - -function getDocsUrl(command) { - const moduleName = command.split('.')[0]; - return `https://fakerjs.dev/api/${moduleName}`; -} - -function formatExampleValue(value) { - if (value === null || value === undefined) { - return ''; - } - - if ( - typeof value === 'object' && - value !== null && - Object.prototype.hasOwnProperty.call(value, 'data') && - Object.prototype.hasOwnProperty.call(value, 'isError') - ) { - return formatExampleValue(value.data); - } - - let rendered = ''; - if (typeof value === 'string') { - rendered = value; - } else if (typeof value === 'number' || typeof value === 'boolean') { - rendered = String(value); - } else { - try { - rendered = JSON.stringify(value); - } catch (error) { - rendered = String(value); - } - } - - const squashed = rendered.replace(/\s+/g, ' ').trim(); - if (!squashed) { - return ''; - } - if (squashed.length > 140) { - return `${squashed.slice(0, 137)}...`; - } - return squashed; -} - -function applyMetadataOverrides(command, metadata, helperOverrides = {}) { - const commandOverrides = { - 'helpers.rangeToNumber': { - example: '2', - }, - 'string.uuid': { - summary: 'Returns a UUID (Universally Unique Identifier).', - params: [ - { - name: 'version', - optional: true, - type: '4 | 7', - description: - 'The specific UUID version to use. If refDate is supplied and version is omitted, version 7 is used automatically.', - }, - { - name: 'refDate', - optional: true, - type: 'string | Date | number', - description: - 'The timestamp to encode into the UUID. This is only valid for UUID v7. If refDate is supplied and version is omitted, version 7 is used automatically. Providing refDate with version 4 is invalid.', - }, - ], - }, - }; - - const commandOverride = commandOverrides[command]; - const override = helperOverrides[command]; - if (!override && !commandOverride) { - return metadata; - } - - const combinedOverride = { - ...(commandOverride || {}), - ...(override || {}), - }; - - return { - ...metadata, - summary: combinedOverride.summary || metadata.summary, - params: Array.isArray(combinedOverride.params) ? combinedOverride.params : metadata.params, - example: combinedOverride.example || metadata.example, - examples: Array.isArray(combinedOverride.examples) ? combinedOverride.examples : metadata.examples, - }; -} - -function loadKnownCommandsFromSource() { - const sourcePath = path.join(repoRoot, 'packages', 'core', 'js', 'faker', 'faker-commands.js'); - const sourceText = fs.readFileSync(sourcePath, 'utf8'); - const arrayMatch = sourceText.match(/const KNOWN_FAKER_COMMANDS = \[([\s\S]*?)\];/); - if (!arrayMatch) { - throw new Error('Unable to locate KNOWN_FAKER_COMMANDS in faker-commands.js'); - } - - const values = []; - const valuePattern = /'([^']+)'/g; - let match; - while ((match = valuePattern.exec(arrayMatch[1])) !== null) { - values.push(match[1]); - } - - return values.filter((command) => command !== 'RegEx'); -} - -function formatForModuleExport(metadataByCommand) { - const generatedOn = new Date().toISOString(); - return `// AUTO-GENERATED FILE. DO NOT EDIT BY HAND. -// Generated by scripts/generate-faker-help.js on ${generatedOn} - -const FAKER_COMMAND_HELP_METADATA = ${JSON.stringify(metadataByCommand, null, 2)}; - -function getFakerCommandHelp(commandValue) { - const command = String(commandValue || '').trim(); - return FAKER_COMMAND_HELP_METADATA[command]; -} - -export { FAKER_COMMAND_HELP_METADATA, getFakerCommandHelp }; -`; -} - -async function loadRuntimeDependencies() { - const fakerModule = await import('@faker-js/faker'); - const faker = fakerModule.faker; - const fakerCommandModule = await import( - pathToFileURL(path.join(repoRoot, 'packages', 'core', 'js', 'data_generation', 'faker', 'fakerCommand.js')) - ); - return { faker, FakerCommand: fakerCommandModule.FakerCommand }; -} - -async function loadHelperOverrides() { - const helperDefinitionsModule = await import( - pathToFileURL(path.join(repoRoot, 'packages', 'core', 'js', 'faker', 'faker-helper-keyword-definitions.js')) - ); - return helperDefinitionsModule.FAKER_HELPER_KEYWORD_DEFINITIONS || {}; -} - -function tryGenerateExample(command, faker, FakerCommand) { - try { - const fakerCommand = new FakerCommand(command); - fakerCommand.parse(); - fakerCommand.compile(faker); - const validation = fakerCommand.validate(faker); - if (validation?.isError) { - return ''; - } - return formatExampleValue(fakerCommand.execute(faker)); - } catch (error) { - return ''; - } -} - -async function run() { - const commands = loadKnownCommandsFromSource(); - - const declarationFiles = fs - .readdirSync(fakerDistDir) - .filter((fileName) => fileName.endsWith('.d.ts')) - .map((fileName) => path.join(fakerDistDir, fileName)); - - const declarationText = declarationFiles.map((filePath) => fs.readFileSync(filePath, 'utf8')).join('\n'); - const aliases = parseTypeAliases(declarationText); - const enumValues = parseEnumLiteralValues(declarationText); - const { faker, FakerCommand } = await loadRuntimeDependencies(); - const helperOverrides = await loadHelperOverrides(); - - const metadataByCommand = {}; - - for (const command of commands) { - const methodHelp = extractMethodHelp(command, declarationText, aliases, enumValues); - const extractedSummary = methodHelp?.summary || ''; - const shouldUseExtractedSummary = - extractedSummary.length > 0 && - !extractedSummary.toLowerCase().includes('proxy for localedefinition') && - !extractedSummary.toLowerCase().includes('type that provides auto-suggestions'); - metadataByCommand[command] = { - ...applyMetadataOverrides( - command, - { - summary: shouldUseExtractedSummary ? extractedSummary : buildFallbackSummary(command), - params: (methodHelp?.params || []).map((param) => { - const description = methodHelp?.paramDocs?.get(param.name) || ''; - return { - ...param, - description, - }; - }), - docsUrl: getDocsUrl(command), - example: tryGenerateExample(command, faker, FakerCommand), - }, - helperOverrides - ), - }; - } - - fs.writeFileSync(outputFile, formatForModuleExport(metadataByCommand), 'utf8'); - console.log(`Generated faker help metadata for ${commands.length} commands: ${outputFile}`); -} - -run().catch((error) => { - console.error(error); - process.exitCode = 1; -}); diff --git a/scripts/generate-schema-interaction-matrix.mjs b/scripts/generate-schema-interaction-matrix.mjs index bcc0c717..f239a478 100644 --- a/scripts/generate-schema-interaction-matrix.mjs +++ b/scripts/generate-schema-interaction-matrix.mjs @@ -13,7 +13,7 @@ import { buildUiInteractionScenarios, } from '../packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js'; import { renderMatrixSummaryMarkdown } from '../packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-matrix-report.js'; -import { getFakerCommandHelp } from '../packages/core-ui/js/gui_components/shared/faker-command-help-metadata.js'; +import { getFakerCommandHelp } from '../packages/core/js/faker/faker-helper-keyword-definitions.js'; import { getDomainCommandHelp } from '../packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js'; import { createConfiguredGeneratorFromSchemaRows, From b62b69e2fdc89f80163a8be878dd1c8f6f13d0d6 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Thu, 18 Jun 2026 11:33:43 +0100 Subject: [PATCH 2/3] fix helper help metadata review follow-ups --- .../src/stories/shared-schema-definition.stories.js | 7 +++++-- package.json | 1 - .../shared/domain-command-help-metadata.js | 6 +----- .../shared/test-data/help/help-model-builder.js | 12 ++---------- .../support/schema-interaction-scenario-builder.js | 2 +- .../interaction/support/generated-value-quality.js | 6 +++++- .../js/faker/faker-helper-keyword-definitions.js | 6 +++--- scripts/generate-schema-interaction-matrix.mjs | 2 +- 8 files changed, 18 insertions(+), 24 deletions(-) diff --git a/apps/web/src/stories/shared-schema-definition.stories.js b/apps/web/src/stories/shared-schema-definition.stories.js index d720ec40..3d50a90a 100644 --- a/apps/web/src/stories/shared-schema-definition.stories.js +++ b/apps/web/src/stories/shared-schema-definition.stories.js @@ -523,9 +523,12 @@ export const ParamsDialog = { await expect(firstHelpIcon).toHaveAttribute('data-help-text', expect.stringContaining('Default: 1')); await expect(dialogScope.getByRole('textbox', { name: /start value/i }).value).toBe('1'); await expect(dialogScope.getByRole('textbox', { name: /step value/i }).value).toBe('1'); - const prefixInput = dialogScope.getByRole('textbox', { name: /prefix value/i }); + let prefixInput = dialogScope.getByRole('textbox', { name: /prefix value/i }); await userEvent.click(prefixInput); - await waitFor(() => expect(document.activeElement).toBe(prefixInput)); + await waitFor(() => { + prefixInput = dialogScope.getByRole('textbox', { name: /prefix value/i }); + expect(document.activeElement).toBe(prefixInput); + }); await userEvent.clear(prefixInput); await userEvent.type(prefixInput, 'filename'); await waitFor(() => diff --git a/package.json b/package.json index d24b7e35..f7d99484 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "test:storybook": "node ./scripts/run-storybook-tests.mjs", "test:ui:jest": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --config ./jest.config.cjs --testPathPatterns packages/core-ui/src/tests apps/web/src/tests/jest", "testenv:create": "node ./scripts/create-testenv.mjs", - "generate:faker-help": "node ./scripts/generate-faker-help.js", "test:browser": "node ./node_modules/@playwright/test/cli.js test", "test:browser:headed": "node ./node_modules/@playwright/test/cli.js test --headed", "test:browser:install": "node ./node_modules/@playwright/test/cli.js install chromium", diff --git a/packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js b/packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js index 08332274..22a7f4f8 100644 --- a/packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js +++ b/packages/core-ui/js/gui_components/shared/domain-command-help-metadata.js @@ -69,11 +69,7 @@ function getDomainCommandHelp(command) { docsUrl: resolveDomainDocsUrl(command, commandHelp.docsUrl || ''), example: commandHelp.example || '', examples: Array.isArray(commandHelp.examples) ? commandHelp.examples : [], - exampleReturnValues: Array.isArray(commandHelp.exampleReturnValues) - ? commandHelp.exampleReturnValues - : Array.isArray(commandHelp.returnExamples) - ? commandHelp.returnExamples - : [], + exampleReturnValues: Array.isArray(commandHelp.exampleReturnValues) ? commandHelp.exampleReturnValues : [], returnType: commandHelp.returnType || '', args: Array.isArray(commandHelp.args) ? commandHelp.args : [], }; diff --git a/packages/core-ui/js/gui_components/shared/test-data/help/help-model-builder.js b/packages/core-ui/js/gui_components/shared/test-data/help/help-model-builder.js index 8f874a72..71802ef8 100644 --- a/packages/core-ui/js/gui_components/shared/test-data/help/help-model-builder.js +++ b/packages/core-ui/js/gui_components/shared/test-data/help/help-model-builder.js @@ -255,11 +255,7 @@ function buildSchemaHelpModel(sourceType, commandValue) { params: normalizeHelpParams(commandHelp?.params || []), example: commandHelp?.example || '', examples: Array.isArray(commandHelp?.examples) ? commandHelp.examples : [], - exampleReturnValues: Array.isArray(commandHelp?.exampleReturnValues) - ? commandHelp.exampleReturnValues - : Array.isArray(commandHelp?.returnExamples) - ? commandHelp.returnExamples - : [], + exampleReturnValues: Array.isArray(commandHelp?.exampleReturnValues) ? commandHelp.exampleReturnValues : [], }; } @@ -283,11 +279,7 @@ function buildSchemaHelpModel(sourceType, commandValue) { params: resolveDomainHelpParams(command, commandHelp), example: commandHelp?.example || '', examples: Array.isArray(commandHelp?.examples) ? commandHelp.examples : [], - exampleReturnValues: Array.isArray(commandHelp?.exampleReturnValues) - ? commandHelp.exampleReturnValues - : Array.isArray(commandHelp?.returnExamples) - ? commandHelp.returnExamples - : [], + exampleReturnValues: Array.isArray(commandHelp?.exampleReturnValues) ? commandHelp.exampleReturnValues : [], }; } diff --git a/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js b/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js index 4b2a3f04..be0c756e 100644 --- a/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js +++ b/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js @@ -750,7 +750,7 @@ function scenarioRowLooksValid(row, value) { } const allowedTypes = getAllowedTypesForScenarioRow(row); - if (allowedTypes.includes('string')) { + if (allowedTypes.includes('string') || allowedTypes.includes('unknown')) { return true; } diff --git a/packages/core-ui/src/tests/interaction/support/generated-value-quality.js b/packages/core-ui/src/tests/interaction/support/generated-value-quality.js index 566bb7e2..cf67aa0a 100644 --- a/packages/core-ui/src/tests/interaction/support/generated-value-quality.js +++ b/packages/core-ui/src/tests/interaction/support/generated-value-quality.js @@ -91,6 +91,10 @@ function getAllowedTypesForRow(row) { } } +function hasPermissiveAllowedType(allowedTypes) { + return allowedTypes.includes('string') || allowedTypes.includes('unknown'); +} + function assertNoErrorIndicators(value, _contextLabel = 'value') { const text = toSearchableText(value); ERROR_PATTERNS.forEach((pattern) => { @@ -147,7 +151,7 @@ function assertRowValueMatchesScenario(row, value, contextLabel) { const allowedTypes = getAllowedTypesForRow(row); const inferred = inferTypeAndConfidence(value); - if (inferred.confidence === 'high' && !allowedTypes.includes('string')) { + if (inferred.confidence === 'high' && !hasPermissiveAllowedType(allowedTypes)) { expect(allowedTypes).toContain(inferred.type); } } diff --git a/packages/core/js/faker/faker-helper-keyword-definitions.js b/packages/core/js/faker/faker-helper-keyword-definitions.js index e76de410..23778916 100644 --- a/packages/core/js/faker/faker-helper-keyword-definitions.js +++ b/packages/core/js/faker/faker-helper-keyword-definitions.js @@ -81,7 +81,7 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { summary: 'Returns one random element from the supplied array.', docsUrl: FAKER_HELPERS_DOCS_URL, example: '', - returnType: 'string', + returnType: 'unknown', params: [ { name: 'array', @@ -185,7 +185,7 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { 'helpers.replaceCreditCardSymbols': { summary: 'Replaces credit-card placeholders and computes a valid Luhn checksum for the result.', docsUrl: FAKER_HELPERS_DOCS_URL, - example: '6453-2433-5050-4456-3526', + example: 'demo-card-####-fake', returnType: 'string', params: [ { @@ -243,7 +243,7 @@ const FAKER_HELPER_KEYWORD_DEFINITIONS = { summary: 'Returns one value from a weighted array, favoring entries with higher weights.', docsUrl: FAKER_HELPERS_DOCS_URL, example: '', - returnType: 'string', + returnType: 'unknown', params: [ { name: 'array', diff --git a/scripts/generate-schema-interaction-matrix.mjs b/scripts/generate-schema-interaction-matrix.mjs index f239a478..a339d368 100644 --- a/scripts/generate-schema-interaction-matrix.mjs +++ b/scripts/generate-schema-interaction-matrix.mjs @@ -317,7 +317,7 @@ function rowValueLooksValid(row, value) { } const allowedTypes = getAllowedTypesForRow(row); - if (allowedTypes.includes('string')) { + if (allowedTypes.includes('string') || allowedTypes.includes('unknown')) { return true; } return allowedTypes.includes(inferValueType(value)); From 463efb671c89c319cd1e3f42a189402105eee715 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Thu, 18 Jun 2026 12:09:11 +0100 Subject: [PATCH 3/3] dedupe permissive matrix type check --- .../matrix/support/schema-interaction-scenario-builder.js | 3 ++- .../tests/interaction/support/generated-value-quality.js | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js b/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js index be0c756e..2e1361ab 100644 --- a/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js +++ b/packages/core-ui/src/tests/interaction/matrix/support/schema-interaction-scenario-builder.js @@ -29,6 +29,7 @@ import { buildDataRuleFromSchemaRow, } from '../../../../../js/gui_components/shared/schema-row-rule-mapper.js'; import { schemaRowsToDataRules, dataRulesToSchemaText } from '@anywaydata/core/data_generation/schema-rules-adapter.js'; +import { hasPermissiveAllowedType } from '../../support/generated-value-quality.js'; const CUSTOM_SOURCE_TYPES = [ SOURCE_TYPE_ENUM, @@ -750,7 +751,7 @@ function scenarioRowLooksValid(row, value) { } const allowedTypes = getAllowedTypesForScenarioRow(row); - if (allowedTypes.includes('string') || allowedTypes.includes('unknown')) { + if (hasPermissiveAllowedType(allowedTypes)) { return true; } diff --git a/packages/core-ui/src/tests/interaction/support/generated-value-quality.js b/packages/core-ui/src/tests/interaction/support/generated-value-quality.js index cf67aa0a..9f94dafa 100644 --- a/packages/core-ui/src/tests/interaction/support/generated-value-quality.js +++ b/packages/core-ui/src/tests/interaction/support/generated-value-quality.js @@ -169,4 +169,9 @@ function assertScenarioDataQuality({ scenario, dataTable, exportedText = '', out }); } -export { assertNoErrorIndicators, assertDataTableHasNoErrorIndicators, assertScenarioDataQuality }; +export { + assertNoErrorIndicators, + assertDataTableHasNoErrorIndicators, + assertScenarioDataQuality, + hasPermissiveAllowedType, +};