From 6191a5d883048b694404dbf42527caba395828ea Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Mon, 1 Apr 2024 10:21:18 +0200 Subject: docs: rewrite api-docs generation using ts-morph (#2628) --- test/scripts/apidoc/.gitignore | 1 - .../apidoc/__snapshots__/module.spec.ts.snap | 54 - .../apidoc/__snapshots__/signature.spec.ts.snap | 768 --------- test/scripts/apidoc/module.example.ts | 33 - test/scripts/apidoc/module.spec.ts | 26 - test/scripts/apidoc/signature.debug.ts | 17 - test/scripts/apidoc/signature.example.ts | 391 ----- test/scripts/apidoc/signature.spec.ts | 26 - test/scripts/apidoc/utils.ts | 66 - test/scripts/apidoc/verify-jsdoc-tags.spec.ts | 315 ---- test/scripts/apidocs/.gitignore | 1 + .../apidocs/__snapshots__/class.spec.ts.snap | 75 + .../apidocs/__snapshots__/method.spec.ts.snap | 1671 ++++++++++++++++++++ test/scripts/apidocs/class.example.ts | 33 + test/scripts/apidocs/class.spec.ts | 22 + test/scripts/apidocs/method.example.ts | 469 ++++++ test/scripts/apidocs/method.spec.ts | 30 + test/scripts/apidocs/utils.ts | 38 + test/scripts/apidocs/verify-jsdoc-tags.spec.ts | 286 ++++ 19 files changed, 2625 insertions(+), 1697 deletions(-) delete mode 100644 test/scripts/apidoc/.gitignore delete mode 100644 test/scripts/apidoc/__snapshots__/module.spec.ts.snap delete mode 100644 test/scripts/apidoc/__snapshots__/signature.spec.ts.snap delete mode 100644 test/scripts/apidoc/module.example.ts delete mode 100644 test/scripts/apidoc/module.spec.ts delete mode 100644 test/scripts/apidoc/signature.debug.ts delete mode 100644 test/scripts/apidoc/signature.example.ts delete mode 100644 test/scripts/apidoc/signature.spec.ts delete mode 100644 test/scripts/apidoc/utils.ts delete mode 100644 test/scripts/apidoc/verify-jsdoc-tags.spec.ts create mode 100644 test/scripts/apidocs/.gitignore create mode 100644 test/scripts/apidocs/__snapshots__/class.spec.ts.snap create mode 100644 test/scripts/apidocs/__snapshots__/method.spec.ts.snap create mode 100644 test/scripts/apidocs/class.example.ts create mode 100644 test/scripts/apidocs/class.spec.ts create mode 100644 test/scripts/apidocs/method.example.ts create mode 100644 test/scripts/apidocs/method.spec.ts create mode 100644 test/scripts/apidocs/utils.ts create mode 100644 test/scripts/apidocs/verify-jsdoc-tags.spec.ts (limited to 'test/scripts') diff --git a/test/scripts/apidoc/.gitignore b/test/scripts/apidoc/.gitignore deleted file mode 100644 index a6d7ecd9..00000000 --- a/test/scripts/apidoc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -temp/ diff --git a/test/scripts/apidoc/__snapshots__/module.spec.ts.snap b/test/scripts/apidoc/__snapshots__/module.spec.ts.snap deleted file mode 100644 index d4275860..00000000 --- a/test/scripts/apidoc/__snapshots__/module.spec.ts.snap +++ /dev/null @@ -1,54 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`module > analyzeModule() > ModuleDeprecationTest 1`] = ` -{ - "comment": "This is a description for a module with a code example.", - "deprecated": "Well, this is deprecated.", - "examples": undefined, -} -`; - -exports[`module > analyzeModule() > ModuleExampleTest 1`] = ` -{ - "comment": "This is a description for a module with a code example.", - "deprecated": undefined, - "examples": "
ts
new ModuleExampleTest()
-
", -} -`; - -exports[`module > analyzeModule() > ModuleFakerJsLinkTest 1`] = ` -{ - "comment": "Description with a link to our [website](/) -and [api docs](/api/).", - "deprecated": undefined, - "examples": undefined, -} -`; - -exports[`module > analyzeModule() > ModuleNextFakerJsLinkTest 1`] = ` -{ - "comment": "Description with a link to our [website](/) -and [api docs](/api/).", - "deprecated": undefined, - "examples": undefined, -} -`; - -exports[`module > analyzeModule() > ModuleSimpleTest 1`] = ` -{ - "comment": "A simple module without anything special.", - "deprecated": undefined, - "examples": undefined, -} -`; - -exports[`module > analyzeModule() > expected and actual modules are equal 1`] = ` -[ - "ModuleDeprecationTest", - "ModuleExampleTest", - "ModuleFakerJsLinkTest", - "ModuleNextFakerJsLinkTest", - "ModuleSimpleTest", -] -`; diff --git a/test/scripts/apidoc/__snapshots__/signature.spec.ts.snap b/test/scripts/apidoc/__snapshots__/signature.spec.ts.snap deleted file mode 100644 index 37a9f754..00000000 --- a/test/scripts/apidoc/__snapshots__/signature.spec.ts.snap +++ /dev/null @@ -1,768 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`signature > analyzeSignature() > complexArrayParameter 1`] = ` -{ - "deprecated": undefined, - "description": "

Complex array parameter.

-", - "examples": "
ts
complexArrayParameter<T>(array: readonly Array<{
-  value: T,
-  weight: number
-}>): T
-
", - "name": "complexArrayParameter", - "parameters": [ - { - "description": "

The type of the entries to pick from.

-", - "name": "", - "type": undefined, - }, - { - "default": undefined, - "description": "

Array to pick the value from.

-", - "name": "array", - "type": "Array<{ ... }>", - }, - { - "default": undefined, - "description": "

The value to pick.

-", - "name": "array[].value", - "type": "T", - }, - { - "default": undefined, - "description": "

The weight of the value.

-", - "name": "array[].weight", - "type": "number", - }, - ], - "returns": "T", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L377", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > defaultBooleanParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with a default parameter.

-", - "examples": "
ts
defaultBooleanParamMethod(c: boolean = true): number
-
", - "name": "defaultBooleanParamMethod", - "parameters": [ - { - "default": "true", - "description": "

The boolean parameter.

-", - "name": "c", - "type": "boolean", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L105", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > expected and actual methods are equal 1`] = ` -[ - "complexArrayParameter", - "defaultBooleanParamMethod", - "functionParamMethod", - "literalUnionParamMethod", - "methodWithDeprecated", - "methodWithDeprecatedOption", - "methodWithExample", - "methodWithMultipleSeeMarkers", - "methodWithMultipleSeeMarkersAndBackticks", - "methodWithMultipleThrows", - "methodWithSinceMarker", - "methodWithThrows", - "multiParamMethod", - "noParamMethod", - "optionalStringParamMethod", - "optionsInlineParamMethodWithDefaults", - "optionsInterfaceParamMethodWithDefaults", - "optionsParamMethod", - "optionsTypeParamMethodWithDefaults", - "recordParamMethod", - "requiredNumberParamMethod", - "stringUnionParamMethod", -] -`; - -exports[`signature > analyzeSignature() > functionParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with a function parameters.

-", - "examples": "
ts
functionParamMethod(fn: (a: string) => number): number
-
", - "name": "functionParamMethod", - "parameters": [ - { - "default": undefined, - "description": "

The function parameter.

-", - "name": "fn", - "type": "(a: string) => number", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L125", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > literalUnionParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with LiteralUnion.

-", - "examples": "
ts
literalUnionParamMethod(value: 'a' | 'b' | ?, namedValue: 'a' | 'b' | ?, array: readonly Array<'a' | 'b' | ?>, namedArray: readonly Array<'a' | 'b' | ?>, mixed: 'a' | 'b' | ? | readonly Array<'a' | 'b' | ?>, namedMixed: 'a' | 'b' | ? | readonly Array<'a' | 'b' | ?>): string
-
", - "name": "literalUnionParamMethod", - "parameters": [ - { - "default": undefined, - "description": "

'a' or 'b'.

-", - "name": "value", - "type": "'a' | 'b' | ?", - }, - { - "default": undefined, - "description": "

'a' or 'b'.

-", - "name": "namedValue", - "type": "'a' | 'b' | ?", - }, - { - "default": undefined, - "description": "

Array of 'a' or 'b'.

-", - "name": "array", - "type": "Array<'a' | 'b' | ?>", - }, - { - "default": undefined, - "description": "

Array of 'a' or 'b'.

-", - "name": "namedArray", - "type": "Array<'a' | 'b' | ?>", - }, - { - "default": undefined, - "description": "

Value 'a' or 'b' or an array thereof.

-", - "name": "mixed", - "type": "'a' | 'b' | ? | Array<'a' | 'b' | ?>", - }, - { - "default": undefined, - "description": "

Value 'a' or 'b' or an array thereof.

-", - "name": "namedMixed", - "type": "'a' | 'b' | ? | Array<'a' | 'b' | ?>", - }, - ], - "returns": "string", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L159", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > methodWithDeprecated 1`] = ` -{ - "deprecated": "

do something else

-", - "description": "

Test with deprecated and see marker.

-", - "examples": "
ts
methodWithDeprecated(): number
-
", - "name": "methodWithDeprecated", - "parameters": [], - "returns": "number", - "seeAlsos": [ - "test.apidoc.methodWithExample()", - ], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L287", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > methodWithDeprecatedOption 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with deprecated option.

-", - "examples": "
ts
methodWithDeprecatedOption(option: {
-  a: string,
-  b: () => number,
-  c: number
-}): number
-
", - "name": "methodWithDeprecatedOption", - "parameters": [ - { - "default": undefined, - "description": "

The options.

-", - "name": "option", - "type": "{ ... }", - }, - { - "default": undefined, - "description": "

Some deprecated option.

-

DEPRECATED: do something else.

-", - "name": "option.a", - "type": "string", - }, - { - "default": undefined, - "description": "

Some other deprecated option.

-

DEPRECATED: do something else.

-", - "name": "option.b", - "type": "() => number", - }, - { - "default": undefined, - "description": "

Some other option.

-", - "name": "option.c", - "type": "number", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L318", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > methodWithExample 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with example marker.

-", - "examples": "
ts
methodWithExample(): number
-test.apidoc.methodWithExample() // 0
-
", - "name": "methodWithExample", - "parameters": [], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L276", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > methodWithMultipleSeeMarkers 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with multiple see markers.

-", - "examples": "
ts
methodWithMultipleSeeMarkers(): number
-
", - "name": "methodWithMultipleSeeMarkers", - "parameters": [], - "returns": "number", - "seeAlsos": [ - "test.apidoc.methodWithExample()", - "test.apidoc.methodWithDeprecated()", - ], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L345", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > methodWithMultipleSeeMarkersAndBackticks 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with multiple see markers and backticks.

-", - "examples": "
ts
methodWithMultipleSeeMarkersAndBackticks(): number
-
", - "name": "methodWithMultipleSeeMarkersAndBackticks", - "parameters": [], - "returns": "number", - "seeAlsos": [ - "test.apidoc.methodWithExample() with parameter foo.", - "test.apidoc.methodWithDeprecated() with parameter bar and baz.", - ], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L355", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > methodWithMultipleThrows 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with multiple throws.

-", - "examples": "
ts
methodWithMultipleThrows(): number
-
", - "name": "methodWithMultipleThrows", - "parameters": [], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L306", - "throws": "First error case. -Another error case.", -} -`; - -exports[`signature > analyzeSignature() > methodWithSinceMarker 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with since marker.

-", - "examples": "
ts
methodWithSinceMarker(): number
-
", - "name": "methodWithSinceMarker", - "parameters": [], - "returns": "number", - "seeAlsos": [], - "since": "1.0.0", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L364", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > methodWithThrows 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with throws.

-", - "examples": "
ts
methodWithThrows(): number
-
", - "name": "methodWithThrows", - "parameters": [], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L296", - "throws": "Everytime.", -} -`; - -exports[`signature > analyzeSignature() > multiParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with multiple parameters.

-", - "examples": "
ts
multiParamMethod(a: number, b?: string, c: boolean = true): number
-
", - "name": "multiParamMethod", - "parameters": [ - { - "default": undefined, - "description": "

The number parameter.

-", - "name": "a", - "type": "number", - }, - { - "default": undefined, - "description": "

The string parameter.

-", - "name": "b?", - "type": "string", - }, - { - "default": "true", - "description": "

The boolean parameter.

-", - "name": "c", - "type": "boolean", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L116", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > noParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with no parameters.

-", - "examples": "
ts
noParamMethod(): number
-
", - "name": "noParamMethod", - "parameters": [], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L78", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > optionalStringParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with an optional parameter.

-", - "examples": "
ts
optionalStringParamMethod(b?: string): number
-
", - "name": "optionalStringParamMethod", - "parameters": [ - { - "default": undefined, - "description": "

The string parameter.

-", - "name": "b?", - "type": "string", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L96", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > optionsInlineParamMethodWithDefaults 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with a function parameters (inline types) with defaults.

-", - "examples": "
ts
optionsInlineParamMethodWithDefaults(a: {
-  value: number
-} = { value: 1 }, b: {
-  value: number
-} = { value: 1 }, c: {
-  value: number
-}): number
-
", - "name": "optionsInlineParamMethodWithDefaults", - "parameters": [ - { - "default": "{ value: 1 }", - "description": "

Parameter with signature default. -It also has a more complex description.

-", - "name": "a", - "type": "{ ... }", - }, - { - "default": undefined, - "description": "

The number parameter.

-", - "name": "a.value?", - "type": "number", - }, - { - "default": "{ value: 1 }", - "description": "

Parameter with jsdocs default.

-

It also has a more complex description.

-", - "name": "b", - "type": "{ ... }", - }, - { - "default": undefined, - "description": "

The number parameter.

-", - "name": "b.value?", - "type": "number", - }, - { - "default": undefined, - "description": "

Parameter with inner jsdocs default.

-", - "name": "c", - "type": "{ ... }", - }, - { - "default": "2", - "description": "

The number parameter. It also has a more complex description.

-", - "name": "c.value?", - "type": "number", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L226", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > optionsInterfaceParamMethodWithDefaults 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with a function parameters with defaults.

-", - "examples": "
ts
optionsInterfaceParamMethodWithDefaults(a: ParameterOptionsInterfaceA = { value: 1 }, b: ParameterOptionsInterfaceB = { value: 1 }, c: ParameterOptionsInterfaceC): number
-
", - "name": "optionsInterfaceParamMethodWithDefaults", - "parameters": [ - { - "default": "{ value: 1 }", - "description": "

Parameter with signature default.

-", - "name": "a", - "type": "ParameterOptionsInterfaceA", - }, - { - "default": "{ value: 1 }", - "description": "

Parameter with jsdocs default.

-", - "name": "b", - "type": "ParameterOptionsInterfaceB", - }, - { - "default": undefined, - "description": "

Parameter with inner jsdocs default.

-", - "name": "c", - "type": "ParameterOptionsInterfaceC", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L262", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > optionsParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with an options parameter.

-", - "examples": "
ts
optionsParamMethod(options: {
-  a: number,
-  b: string,
-  c: boolean,
-  d: () => string,
-  e: 'a' | 'b' | ?
-}): number
-
", - "name": "optionsParamMethod", - "parameters": [ - { - "default": undefined, - "description": "

The options parameter.

-", - "name": "options", - "type": "{ ... }", - }, - { - "default": undefined, - "description": "

The number parameter.

-", - "name": "options.a", - "type": "number", - }, - { - "default": undefined, - "description": "

The string parameter.

-", - "name": "options.b?", - "type": "string", - }, - { - "default": undefined, - "description": "

The boolean parameter.

-", - "name": "options.c", - "type": "boolean", - }, - { - "default": undefined, - "description": "

The method parameter.

-", - "name": "options.d", - "type": "() => string", - }, - { - "default": "'a'", - "description": "

A parameter with inline documentation.

-", - "name": "options.e", - "type": "'a' | 'b' | ?", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L196", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > optionsTypeParamMethodWithDefaults 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with a function parameters with defaults.

-", - "examples": "
ts
optionsTypeParamMethodWithDefaults(a: ParameterOptionsTypeA = { value: 1 }, b: ParameterOptionsTypeB = { value: 1 }, c: ParameterOptionsTypeC): number
-
", - "name": "optionsTypeParamMethodWithDefaults", - "parameters": [ - { - "default": "{ value: 1 }", - "description": "

Parameter with signature default.

-", - "name": "a", - "type": "ParameterOptionsTypeA", - }, - { - "default": "{ value: 1 }", - "description": "

Parameter with jsdocs default.

-", - "name": "b", - "type": "ParameterOptionsTypeB", - }, - { - "default": undefined, - "description": "

Parameter with inner jsdocs default.

-", - "name": "c", - "type": "ParameterOptionsTypeC", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L244", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > recordParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with a Record parameter.

-", - "examples": "
ts
recordParamMethod(object: Record<string, number>): number
-
", - "name": "recordParamMethod", - "parameters": [ - { - "default": undefined, - "description": "

The Record parameter.

-", - "name": "object", - "type": "Record", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L182", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > requiredNumberParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with a required parameter.

-", - "examples": "
ts
requiredNumberParamMethod(a: number): number
-
", - "name": "requiredNumberParamMethod", - "parameters": [ - { - "default": undefined, - "description": "

The number parameter.

-", - "name": "a", - "type": "number", - }, - ], - "returns": "number", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L87", - "throws": undefined, -} -`; - -exports[`signature > analyzeSignature() > stringUnionParamMethod 1`] = ` -{ - "deprecated": undefined, - "description": "

Test with string union.

-", - "examples": "
ts
stringUnionParamMethod(value: 'a' | 'b', options?: {
-  casing: 'lower' | 'mixed' | 'upper',
-  excludes: readonly AlphaNumericChar[],
-  format: 'binary' | 'css' | 'decimal' | 'hex'
-}): string
-
", - "name": "stringUnionParamMethod", - "parameters": [ - { - "default": undefined, - "description": "

'a' or 'b'.

-", - "name": "value", - "type": "'a' | 'b'", - }, - { - "default": undefined, - "description": "

The options parameter.

-", - "name": "options?", - "type": "{ ... }", - }, - { - "default": undefined, - "description": "

The casing parameter.

-", - "name": "options.casing?", - "type": "'lower' | 'mixed' | 'upper'", - }, - { - "default": undefined, - "description": "

The excludes parameter.

-", - "name": "options.excludes?", - "type": "readonly AlphaNumericChar[]", - }, - { - "default": undefined, - "description": "

The format parameter.

-", - "name": "options.format?", - "type": "'binary' | 'css' | 'decimal' | 'hex'", - }, - ], - "returns": "string", - "seeAlsos": [], - "since": "Missing", - "sourcePath": "test/scripts/apidoc/signature.example.ts#L138", - "throws": undefined, -} -`; diff --git a/test/scripts/apidoc/module.example.ts b/test/scripts/apidoc/module.example.ts deleted file mode 100644 index 0e5d9d89..00000000 --- a/test/scripts/apidoc/module.example.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable @typescript-eslint/no-extraneous-class -- required for tests */ - -/** - * A simple module without anything special. - */ -export class ModuleSimpleTest {} - -/** - * Description with a link to our [website](https://fakerjs.dev/) - * and [api docs](https://fakerjs.dev/api/). - */ -export class ModuleFakerJsLinkTest {} - -/** - * Description with a link to our [website](https://next.fakerjs.dev/) - * and [api docs](https://next.fakerjs.dev/api/). - */ -export class ModuleNextFakerJsLinkTest {} - -/** - * This is a description for a module with a code example. - * - * @deprecated Well, this is deprecated. - */ -export class ModuleDeprecationTest {} - -/** - * This is a description for a module with a code example. - * - * @example - * new ModuleExampleTest() - */ -export class ModuleExampleTest {} diff --git a/test/scripts/apidoc/module.spec.ts b/test/scripts/apidoc/module.spec.ts deleted file mode 100644 index 5e55afea..00000000 --- a/test/scripts/apidoc/module.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { beforeAll, describe, expect, it } from 'vitest'; -import { initMarkdownRenderer } from '../../../scripts/apidoc/markdown'; -import { analyzeModule } from '../../../scripts/apidoc/module-methods'; -import * as ModuleTests from './module.example'; -import { loadExampleModules } from './utils'; - -beforeAll(initMarkdownRenderer); -const modules = await loadExampleModules(); - -describe('module', () => { - describe('analyzeModule()', () => { - it('dummy dependency to rerun the test if the example changes', () => { - expect(Object.keys(ModuleTests)).not.toEqual([]); - }); - - it('expected and actual modules are equal', () => { - expect(Object.keys(modules).sort()).toMatchSnapshot(); - }); - - it.each(Object.entries(modules))('%s', (_, module) => { - const actual = analyzeModule(module); - - expect(actual).toMatchSnapshot(); - }); - }); -}); diff --git a/test/scripts/apidoc/signature.debug.ts b/test/scripts/apidoc/signature.debug.ts deleted file mode 100644 index 704b629c..00000000 --- a/test/scripts/apidoc/signature.debug.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * This file exists, because vitest doesn't allow me to debug code outside of src and test. - * And it's easier to test these features independently from the main project. - */ -import { initMarkdownRenderer } from '../../../scripts/apidoc/markdown'; -import { analyzeSignature } from '../../../scripts/apidoc/signature'; -import { loadExampleMethods } from './utils'; - -/* Run with `pnpm tsx test/scripts/apidoc/signature.debug.ts` */ - -await initMarkdownRenderer(); -const methods = await loadExampleMethods(); -for (const [name, method] of Object.entries(methods)) { - console.log('Analyzing:', name); - const result = await analyzeSignature(method, '', method.name); - console.log('Result:', result); -} diff --git a/test/scripts/apidoc/signature.example.ts b/test/scripts/apidoc/signature.example.ts deleted file mode 100644 index c42d2d45..00000000 --- a/test/scripts/apidoc/signature.example.ts +++ /dev/null @@ -1,391 +0,0 @@ -import type { Casing, ColorFormat } from '../../../src'; -import { FakerError } from '../../../src/errors/faker-error'; -import type { AlphaNumericChar } from '../../../src/modules/string'; -import type { LiteralUnion } from '../../../src/utils/types'; -// explicitly export types so they show up in the docs as decomposed types -export type { NumberColorFormat, StringColorFormat } from '../../../src'; -export type { AlphaNumericChar, Casing, ColorFormat, LiteralUnion }; - -/** - * Parameter options type with default from signature. - */ -export type ParameterOptionsTypeA = { - /** - * Options value. - */ - value?: number; -}; - -/** - * Parameter options type with default from jsdocs. Defaults to `{value: 0}`. - */ -export type ParameterOptionsTypeB = { - /** - * Options value. - */ - value?: number; -}; - -/** - * Parameter options type with default from inner jsdocs. - */ -export type ParameterOptionsTypeC = { - /** - * Options value. Defaults to `0`. - */ - value?: number; -}; - -/** - * Parameter options type with default from signature. - */ -export interface ParameterOptionsInterfaceA { - /** - * Options value. - */ - value?: number; -} - -/** - * Parameter options type with default from jsdocs. - */ -export interface ParameterOptionsInterfaceB { - /** - * Options value. - */ - value?: number; -} - -/** - * Parameter options type with default from inner jsdocs. - */ -export interface ParameterOptionsInterfaceC { - /** - * Options value. Defaults to `0`. - */ - value?: number; -} - -/** - * A or B. - */ -export type AB = 'a' | 'b'; - -export class SignatureTest { - /** - * Test with no parameters. - */ - noParamMethod(): number { - return 0; - } - - /** - * Test with a required parameter. - * - * @param a The number parameter. - */ - requiredNumberParamMethod(a: number): number { - return a; - } - - /** - * Test with an optional parameter. - * - * @param b The string parameter. - */ - optionalStringParamMethod(b?: string): number { - return b ? 0 : 1; - } - - /** - * Test with a default parameter. - * - * @param c The boolean parameter. - */ - defaultBooleanParamMethod(c: boolean = true): number { - return c ? 1 : 0; - } - - /** - * Test with multiple parameters. - * - * @param a The number parameter. - * @param b The string parameter. - * @param c The boolean parameter. - */ - multiParamMethod(a: number, b?: string, c: boolean = true): number { - return c ? a : b ? 0 : 1; - } - - /** - * Test with a function parameters. - * - * @param fn The function parameter. - */ - functionParamMethod(fn: (a: string) => number): number { - return fn('a'); - } - - /** - * Test with string union. - * - * @param value `'a'` or `'b'`. - * @param options The options parameter. - * @param options.casing The casing parameter. - * @param options.format The format parameter. - * @param options.excludes The excludes parameter. - */ - stringUnionParamMethod( - value: 'a' | 'b', - options?: { - casing?: Casing; - format?: 'hex' | ColorFormat; - excludes?: ReadonlyArray; - } - ): string { - return options?.format ?? value; - } - - /** - * Test with LiteralUnion. - * - * @param value `'a'` or `'b'`. - * @param namedValue `'a'` or `'b'`. - * @param array Array of `'a'` or `'b'`. - * @param namedArray Array of `'a'` or `'b'`. - * @param mixed Value `'a'` or `'b'` or an array thereof. - * @param namedMixed Value `'a'` or `'b'` or an array thereof. - */ - literalUnionParamMethod( - value: LiteralUnion<'a' | 'b'>, - namedValue: LiteralUnion, - array: ReadonlyArray>, - namedArray: ReadonlyArray>, - mixed: LiteralUnion<'a' | 'b'> | ReadonlyArray>, - namedMixed: ReadonlyArray> | LiteralUnion - ): string { - return ( - value + - namedValue + - array.join('') + - namedArray.join('') + - String(mixed) + - String(namedMixed) - ); - } - - /** - * Test with a Record parameter. - * - * @param object The Record parameter. - */ - recordParamMethod(object: Record): number { - return object.a; - } - - /** - * Test with an options parameter. - * - * @param options The options parameter. - * @param options.a The number parameter. - * @param options.b The string parameter. - * @param options.c The boolean parameter. - * @param options.d The method parameter. - * @param options.e The LiteralUnion parameter. - */ - optionsParamMethod(options: { - a: number; - b?: string; - c: boolean; - d: () => string; - /** - * A parameter with inline documentation. - * - * @default 'a' - */ - e: LiteralUnion<'a' | 'b'>; - }): number { - return options.a; - } - - /** - * Test with a function parameters (inline types) with defaults. - * - * @param a Parameter with signature default. - * It also has a more complex description. - * @param a.value The number parameter. - * @param b Parameter with jsdocs default. - * - * It also has a more complex description. - * - * Defaults to `{ value: 1 }`. - * @param b.value The number parameter. - * @param c Parameter with inner jsdocs default. - * @param c.value The number parameter. It also has a more complex description. Defaults to `2`. - */ - optionsInlineParamMethodWithDefaults( - a: { value?: number } = { value: 1 }, - b: { value?: number }, - c: { value?: number } - ): number { - return a.value ?? b.value ?? c.value ?? -1; - } - - /** - * Test with a function parameters with defaults. - * - * @param a Parameter with signature default. - * @param a.value The number parameter. - * @param b Parameter with jsdocs default. Defaults to `{ value: 1 }`. - * @param b.value The number parameter. - * @param c Parameter with inner jsdocs default. - * @param c.value The number parameter. Defaults to `2`. - */ - optionsTypeParamMethodWithDefaults( - a: ParameterOptionsTypeA = { value: 1 }, - b: ParameterOptionsTypeB, - c: ParameterOptionsTypeC - ): number { - return a.value ?? b.value ?? c.value ?? -1; - } - - /** - * Test with a function parameters with defaults. - * - * @param a Parameter with signature default. - * @param a.value The number parameter. - * @param b Parameter with jsdocs default. Defaults to `{ value: 1 }`. - * @param b.value The number parameter. - * @param c Parameter with inner jsdocs default. - * @param c.value The number parameter. Defaults to `2`. - */ - optionsInterfaceParamMethodWithDefaults( - a: ParameterOptionsInterfaceA = { value: 1 }, - b: ParameterOptionsInterfaceB, - c: ParameterOptionsInterfaceC - ): number { - return a.value ?? b.value ?? c.value ?? -1; - } - - /** - * Test with example marker. - * - * @example - * test.apidoc.methodWithExample() // 0 - */ - methodWithExample(): number { - return 0; - } - - /** - * Test with deprecated and see marker. - * - * @see test.apidoc.methodWithExample() - * - * @deprecated do something else - */ - methodWithDeprecated(): number { - return 0; - } - - /** - * Test with throws. - * - * @throws Everytime. - */ - methodWithThrows(): number { - throw new FakerError('Test error'); - } - - /** - * Test with multiple throws. - * - * @throws First error case. - * @throws Another error case. - */ - methodWithMultipleThrows(): number { - throw new FakerError('Another test error'); - } - - /** - * Test with deprecated option. - * - * @param option The options. - * @param option.a Some deprecated option. - * @param option.b Some other deprecated option. - * @param option.c Some other option. - */ - methodWithDeprecatedOption(option: { - /** - * Some deprecated option. - * - * @deprecated do something else. - */ - a: string; - /** - * Some other deprecated option. - * - * @deprecated do something else. - */ - b: () => number; - /** - * Some other option. - */ - c: number; - }): number { - return option.c; - } - - /** - * Test with multiple see markers. - * - * @see test.apidoc.methodWithExample() - * @see test.apidoc.methodWithDeprecated() - */ - methodWithMultipleSeeMarkers(): number { - return 0; - } - - /** - * Test with multiple see markers and backticks. - * - * @see test.apidoc.methodWithExample() with parameter `foo`. - * @see test.apidoc.methodWithDeprecated() with parameter `bar` and `baz`. - */ - methodWithMultipleSeeMarkersAndBackticks(): number { - return 0; - } - - /** - * Test with since marker. - * - * @since 1.0.0 - */ - methodWithSinceMarker(): number { - return 0; - } - - /** - * Complex array parameter. - * - * @template T The type of the entries to pick from. - * - * @param array Array to pick the value from. - * @param array[].weight The weight of the value. - * @param array[].value The value to pick. - */ - complexArrayParameter( - array: ReadonlyArray<{ - /** - * The weight of the value. - */ - weight: number; - /** - * The value to pick. - */ - value: T; - }> - ): T { - return array[0].value; - } -} diff --git a/test/scripts/apidoc/signature.spec.ts b/test/scripts/apidoc/signature.spec.ts deleted file mode 100644 index 51935fcf..00000000 --- a/test/scripts/apidoc/signature.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { beforeAll, describe, expect, it } from 'vitest'; -import { initMarkdownRenderer } from '../../../scripts/apidoc/markdown'; -import { analyzeSignature } from '../../../scripts/apidoc/signature'; -import { SignatureTest } from './signature.example'; -import { loadExampleMethods } from './utils'; - -beforeAll(initMarkdownRenderer); -const methods = await loadExampleMethods(); - -describe('signature', () => { - describe('analyzeSignature()', () => { - it('dummy dependency to rerun the test if the example changes', () => { - expect(new SignatureTest()).toBeTruthy(); - }); - - it('expected and actual methods are equal', () => { - expect(Object.keys(methods)).toMatchSnapshot(); - }); - - it.each(Object.entries(methods))('%s', async (name, signature) => { - const actual = await analyzeSignature(signature, '', name); - - expect(actual).toMatchSnapshot(); - }); - }); -}); diff --git a/test/scripts/apidoc/utils.ts b/test/scripts/apidoc/utils.ts deleted file mode 100644 index 2752f25b..00000000 --- a/test/scripts/apidoc/utils.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { - DeclarationReflection, - SignatureReflection, - TypeDocOptions, -} from 'typedoc'; -import { - loadProject, - selectApiMethodSignatures, - selectApiModules, -} from '../../../scripts/apidoc/typedoc'; -import { mapByName } from '../../../scripts/apidoc/utils'; - -/** - * Returns a record with the (Module-Name -> (Method-Name -> Method-Signature)) for the project. - * - * @param options The TypeDoc options. - * @param includeTestModules Whether to include the test modules. - */ -export async function loadProjectModules( - options?: Partial, - includeTestModules = false -): Promise< - Record]> -> { - const [, project] = await loadProject(options); - - const modules = selectApiModules(project, includeTestModules); - - return mapByName(modules, (m) => [m, selectApiMethodSignatures(m)]); -} - -/** - * Loads the example methods using TypeDoc. - */ -export async function loadExampleMethods(): Promise< - Record -> { - const modules = await loadProjectModules( - { - entryPoints: ['test/scripts/apidoc/signature.example.ts'], - }, - true - ); - return modules['SignatureTest'][1]; -} - -/** - * Loads the example modules using TypeDoc. - */ -export async function loadExampleModules(): Promise< - Record -> { - const modules = await loadProjectModules( - { - entryPoints: ['test/scripts/apidoc/module.example.ts'], - }, - true - ); - - const result: Record = {}; - for (const key in modules) { - result[key] = modules[key][0]; - } - - return result; -} diff --git a/test/scripts/apidoc/verify-jsdoc-tags.spec.ts b/test/scripts/apidoc/verify-jsdoc-tags.spec.ts deleted file mode 100644 index da34316e..00000000 --- a/test/scripts/apidoc/verify-jsdoc-tags.spec.ts +++ /dev/null @@ -1,315 +0,0 @@ -import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; -import { dirname, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import type { ReflectionType, SomeType } from 'typedoc'; -import validator from 'validator'; -import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; -import { initMarkdownRenderer } from '../../../scripts/apidoc/markdown'; -import { analyzeSignature } from '../../../scripts/apidoc/signature'; -import { - MISSING_DESCRIPTION, - extractDeprecated, - extractDescription, - extractJoinedRawExamples, - extractModuleFieldName, - extractRawDefault, - extractSeeAlsos, - extractSince, - extractSummaryDefault, - extractTagContent, -} from '../../../scripts/apidoc/typedoc'; -import { loadProjectModules } from './utils'; - -// This test ensures, that every method -// - has working examples -// - running these do not log anything, unless the method is deprecated - -beforeAll(initMarkdownRenderer); - -const tempDir = resolve(dirname(fileURLToPath(import.meta.url)), 'temp'); - -afterAll(() => { - // Remove temp folder - if (existsSync(tempDir)) { - rmSync(tempDir, { recursive: true }); - } -}); - -const modules = await loadProjectModules(); - -function resolveDirToModule(moduleName: string): string { - return resolve(tempDir, moduleName); -} - -function resolvePathToMethodFile( - moduleName: string, - methodName: string -): string { - const dir = resolveDirToModule(moduleName); - return resolve(dir, `${methodName}.ts`); -} - -const allowedReferences = new Set( - Object.values(modules).flatMap(([module, methods]) => { - const moduleFieldName = extractModuleFieldName(module); - return Object.keys(methods).map( - (methodName) => `faker.${moduleFieldName}.${methodName}` - ); - }) -); -const allowedLinks = new Set( - Object.values(modules).flatMap(([module, methods]) => { - const moduleFieldName = extractModuleFieldName(module); - return [ - `/api/${moduleFieldName}.html`, - ...Object.keys(methods).map( - (methodName) => - `/api/${moduleFieldName}.html#${methodName.toLowerCase()}` - ), - ]; - }) -); - -function assertDescription(description: string, isHtml: boolean): void { - const linkRegexp = isHtml ? /(href)="([^"]+)"/g : /\[([^\]]+)\]\(([^)]+)\)/g; - const links = [...description.matchAll(linkRegexp)].map((m) => m[2]); - - for (const link of links) { - if (!isHtml) { - expect(link).toMatch(/^https?:\/\//); - expect(link).toSatisfy(validator.isURL); - } - - if (isHtml ? link.startsWith('/api/') : link.includes('fakerjs.dev/api/')) { - expect(allowedLinks, `${link} to point to a valid target`).toContain( - link.replace(/.*fakerjs.dev\//, '/') - ); - } - } -} - -// keep in sync with analyzeParameterOptions -function assertNestedParameterDefault( - name: string, - parameterType?: SomeType -): void { - if (!parameterType) { - return; - } - - switch (parameterType.type) { - case 'array': { - return assertNestedParameterDefault( - `${name}[]`, - parameterType.elementType - ); - } - - case 'union': { - for (const type of parameterType.types) { - assertNestedParameterDefault(name, type); - } - - return; - } - - case 'reflection': { - for (const property of parameterType.declaration.children ?? []) { - const reflection = property.comment - ? property - : (property.type as ReflectionType)?.declaration?.signatures?.[0]; - const comment = reflection?.comment; - const tagDefault = extractRawDefault({ comment }) || undefined; - const summaryDefault = extractSummaryDefault(comment, false); - - if (summaryDefault) { - expect( - tagDefault, - `Expect jsdoc summary default and @default for ${name}.${property.name} to be the same` - ).toBe(summaryDefault); - } - } - - return; - } - - case 'typeOperator': { - return assertNestedParameterDefault(name, parameterType.target); - } - - default: { - return; - } - } -} - -describe('verify JSDoc tags', () => { - describe.each(Object.entries(modules))( - '%s', - (moduleName, [module, methodsByName]) => { - describe('verify module', () => { - it('verify description', () => { - const description = extractDescription(module); - assertDescription(description, false); - }); - }); - - describe.each(Object.entries(methodsByName))( - '%s', - (methodName, signature) => { - beforeAll(() => { - // Write temp files to disk - - // Extract examples and make them runnable - const examples = extractJoinedRawExamples(signature) ?? ''; - - // Save examples to a file to run them later in the specific tests - const dir = resolveDirToModule(moduleName); - mkdirSync(dir, { recursive: true }); - - const path = resolvePathToMethodFile(moduleName, methodName); - const imports = [ - ...new Set(examples.match(/(? { - const description = extractDescription(signature); - assertDescription(description, false); - }); - - it('verify @example tag', async () => { - // Extract the examples - const examples = extractJoinedRawExamples(signature); - - expect( - examples, - `${moduleName}.${methodName} to have examples` - ).not.toBe(''); - - // Grab path to example file - const path = resolvePathToMethodFile(moduleName, methodName); - - // Executing the examples should not throw - await expect( - import(`${path}?scope=example`) - ).resolves.toBeDefined(); - }); - - // This only checks whether the whole method is deprecated or not - // It does not check whether the method is deprecated for a specific set of arguments - it('verify @deprecated tag', async () => { - // Grab path to example file - const path = resolvePathToMethodFile(moduleName, methodName); - - const consoleWarnSpy = vi.spyOn(console, 'warn'); - - // Run the examples - await import(`${path}?scope=deprecated`); - - // Verify that deprecated methods log a warning - const deprecatedFlag = extractDeprecated(signature) !== undefined; - if (deprecatedFlag) { - expect(consoleWarnSpy).toHaveBeenCalled(); - expect( - extractTagContent('@deprecated', signature).join(''), - '@deprecated tag without message' - ).not.toBe(''); - } else { - expect(consoleWarnSpy).not.toHaveBeenCalled(); - } - }); - - it('verify @param tags', async () => { - // This must run before analyzeSignature - for (const param of signature.parameters ?? []) { - const type = param.type; - const paramDefault = param.defaultValue; - const commentDefault = extractSummaryDefault( - param.comment, - false - ); - if (paramDefault) { - if ( - /^{.*}$/.test(paramDefault) || - paramDefault.includes('\n') - ) { - expect(commentDefault).toBeUndefined(); - } else { - expect( - commentDefault, - `Expect '${param.name}'s js implementation default to be the same as the jsdoc summary default.` - ).toBe(paramDefault); - } - } - - assertNestedParameterDefault(param.name, type); - } - - for (const param of ( - await analyzeSignature(signature, '', methodName) - ).parameters) { - const { name, description } = param; - const plainDescription = description - .replaceAll(/<[^>]+>/g, '') - .trim(); - expect( - plainDescription, - `Expect param ${name} to have a description` - ).not.toBe(MISSING_DESCRIPTION); - assertDescription(description, true); - } - }); - - it('verify @see tags', () => { - for (const link of extractSeeAlsos(signature)) { - if (link.startsWith('faker.')) { - // Expected @see faker.xxx.yyy() - expect(link, 'Expect method reference to contain ()').toContain( - '(' - ); - expect(link, 'Expect method reference to contain ()').toContain( - ')' - ); - expect( - link, - "Expect method reference to have a ': ' after the parenthesis" - ).toContain('): '); - expect( - link, - 'Expect method reference to have a description starting with a capital letter' - ).toMatch(/\): [A-Z]/); - expect( - link, - 'Expect method reference to start with a standard description phrase' - ).toMatch( - /\): (?:For generating |For more information about |For using |For the replacement method)/ - ); - expect( - link, - 'Expect method reference to have a description ending with a dot' - ).toMatch(/\.$/); - expect(allowedReferences).toContain(link.replace(/\(.*/, '')); - } - } - }); - - it('verify @since tag', () => { - const since = extractSince(signature); - expect(since, '@since to be present').toBeTruthy(); - expect(since).not.toBe(MISSING_DESCRIPTION); - expect(since, '@since to be a valid semver').toSatisfy( - validator.isSemVer - ); - }); - } - ); - } - ); -}); diff --git a/test/scripts/apidocs/.gitignore b/test/scripts/apidocs/.gitignore new file mode 100644 index 00000000..a6d7ecd9 --- /dev/null +++ b/test/scripts/apidocs/.gitignore @@ -0,0 +1 @@ +temp/ diff --git a/test/scripts/apidocs/__snapshots__/class.spec.ts.snap b/test/scripts/apidocs/__snapshots__/class.spec.ts.snap new file mode 100644 index 00000000..6461169d --- /dev/null +++ b/test/scripts/apidocs/__snapshots__/class.spec.ts.snap @@ -0,0 +1,75 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`class > expected and actual modules are equal 1`] = ` +[ + "ModuleDeprecationTest", + "ModuleExampleTest", + "ModuleFakerJsLinkTest", + "ModuleNextFakerJsLinkTest", + "ModuleSimpleTest", +] +`; + +exports[`class > processClass(ModuleDeprecationTest) 1`] = ` +{ + "camelTitle": "moduleDeprecationTest", + "category": undefined, + "deprecated": "Well, this is deprecated.", + "description": "This is a description for a module with a code example.", + "examples": [], + "methods": [], + "title": "ModuleDeprecationTest", +} +`; + +exports[`class > processClass(ModuleExampleTest) 1`] = ` +{ + "camelTitle": "moduleExampleTest", + "category": undefined, + "deprecated": undefined, + "description": "This is a description for a module with a code example.", + "examples": [ + "new ModuleExampleTest()", + ], + "methods": [], + "title": "ModuleExampleTest", +} +`; + +exports[`class > processClass(ModuleFakerJsLinkTest) 1`] = ` +{ + "camelTitle": "moduleFakerJsLinkTest", + "category": undefined, + "deprecated": undefined, + "description": "Description with a link to our [website](https://fakerjs.dev/) +and [api docs](https://fakerjs.dev/api/).", + "examples": [], + "methods": [], + "title": "ModuleFakerJsLinkTest", +} +`; + +exports[`class > processClass(ModuleNextFakerJsLinkTest) 1`] = ` +{ + "camelTitle": "moduleNextFakerJsLinkTest", + "category": undefined, + "deprecated": undefined, + "description": "Description with a link to our [website](https://next.fakerjs.dev/) +and [api docs](https://next.fakerjs.dev/api/).", + "examples": [], + "methods": [], + "title": "ModuleNextFakerJsLinkTest", +} +`; + +exports[`class > processClass(ModuleSimpleTest) 1`] = ` +{ + "camelTitle": "moduleSimpleTest", + "category": undefined, + "deprecated": undefined, + "description": "A simple module without anything special.", + "examples": [], + "methods": [], + "title": "ModuleSimpleTest", +} +`; diff --git a/test/scripts/apidocs/__snapshots__/method.spec.ts.snap b/test/scripts/apidocs/__snapshots__/method.spec.ts.snap new file mode 100644 index 00000000..5a5518e4 --- /dev/null +++ b/test/scripts/apidocs/__snapshots__/method.spec.ts.snap @@ -0,0 +1,1671 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`method > expected and actual methods are equal 1`] = ` +[ + "complexArrayParameter", + "defaultBooleanParamMethod", + "functionParamMethod", + "literalUnionParamMethod", + "methodWithDeprecated", + "methodWithDeprecatedOption", + "methodWithExample", + "methodWithMultipleSeeMarkers", + "methodWithMultipleSeeMarkersAndBackticks", + "methodWithMultipleThrows", + "methodWithSinceMarker", + "methodWithThrows", + "multiParamMethod", + "noParamMethod", + "optionalStringParamMethod", + "optionsInlineParamMethodWithDefaults", + "optionsInterfaceParamMethodWithDefaults", + "optionsParamMethod", + "optionsTypeParamMethodWithDefaults", + "recordParamMethod", + "requiredNumberParamMethod", + "stringUnionParamMethod", +] +`; + +exports[`method > processMethodLike(complexArrayParameter) 1`] = ` +{ + "name": "complexArrayParameter", + "signatures": [ + { + "deprecated": undefined, + "description": "Complex array parameter.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "The type of the entries to pick from.", + "name": "", + "type": { + "text": "any", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Array to pick the value from.", + "name": "array", + "type": { + "text": "Array<{ ... }>", + "type": "generic", + "typeParameters": [ + { + "text": "{ ... }", + "type": "simple", + }, + ], + }, + }, + { + "default": undefined, + "description": "The value to pick.", + "name": "array[].value", + "type": { + "resolvedType": { + "text": "any", + "type": "simple", + }, + "text": "T", + "type": "shadow", + }, + }, + { + "default": undefined, + "description": "The weight of the value.", + "name": "array[].weight", + "type": { + "text": "number", + "type": "simple", + }, + }, + ], + "returns": { + "resolvedType": { + "text": "any", + "type": "simple", + }, + "text": "T", + "type": "shadow", + }, + "seeAlsos": [], + "signature": "function complexArrayParameter( + array: ReadonlyArray<{ + weight: number; + value: T; + }> + ): T;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(defaultBooleanParamMethod) 1`] = ` +{ + "name": "defaultBooleanParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with a default parameter.", + "examples": [], + "parameters": [ + { + "default": "true", + "description": "The boolean parameter.", + "name": "c", + "type": { + "text": "boolean", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function defaultBooleanParamMethod(c: boolean = true): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(functionParamMethod) 1`] = ` +{ + "name": "functionParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with a function parameters.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "The function parameter.", + "name": "fn", + "type": { + "text": "(a: string) => number", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function functionParamMethod(fn: (a: string) => number): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(literalUnionParamMethod) 1`] = ` +{ + "name": "literalUnionParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with LiteralUnion.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "\`'a'\` or \`'b'\`.", + "name": "value", + "type": { + "text": "'a' | 'b' | string", + "type": "union", + "types": [ + { + "text": "'a' | 'b'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + ], + }, + { + "text": "string", + "type": "simple", + }, + ], + }, + }, + { + "default": undefined, + "description": "\`'a'\` or \`'b'\`.", + "name": "namedValue", + "type": { + "text": "AB | string", + "type": "union", + "types": [ + { + "resolvedType": { + "text": "'a' | 'b'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + ], + }, + "text": "AB", + "type": "shadow", + }, + { + "text": "string", + "type": "simple", + }, + ], + }, + }, + { + "default": undefined, + "description": "Array of \`'a'\` or \`'b'\`.", + "name": "array", + "type": { + "text": "Array<'a' | 'b' | string>", + "type": "generic", + "typeParameters": [ + { + "text": "'a' | 'b' | string", + "type": "union", + "types": [ + { + "text": "'a' | 'b'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + ], + }, + { + "text": "string", + "type": "simple", + }, + ], + }, + ], + }, + }, + { + "default": undefined, + "description": "Array of \`'a'\` or \`'b'\`.", + "name": "namedArray", + "type": { + "text": "Array", + "type": "generic", + "typeParameters": [ + { + "text": "AB | string", + "type": "union", + "types": [ + { + "resolvedType": { + "text": "'a' | 'b'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + ], + }, + "text": "AB", + "type": "shadow", + }, + { + "text": "string", + "type": "simple", + }, + ], + }, + ], + }, + }, + { + "default": undefined, + "description": "Value \`'a'\` or \`'b'\` or an array thereof.", + "name": "mixed", + "type": { + "text": "'a' | 'b' | string | Array<'a' | 'b' | string>", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + { + "text": "string & { zz_IGNORE_ME?: undefined; }", + "type": "simple", + }, + { + "text": "Array<'a' | 'b' | string>", + "type": "generic", + "typeParameters": [ + { + "text": "'a' | 'b' | string", + "type": "union", + "types": [ + { + "text": "'a' | 'b'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + ], + }, + { + "text": "string", + "type": "simple", + }, + ], + }, + ], + }, + ], + }, + }, + { + "default": undefined, + "description": "Value \`'a'\` or \`'b'\` or an array thereof.", + "name": "namedMixed", + "type": { + "text": "'a' | 'b' | string | Array", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + { + "text": "string & { zz_IGNORE_ME?: undefined; }", + "type": "simple", + }, + { + "text": "Array", + "type": "generic", + "typeParameters": [ + { + "text": "AB | string", + "type": "union", + "types": [ + { + "resolvedType": { + "text": "'a' | 'b'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + ], + }, + "text": "AB", + "type": "shadow", + }, + { + "text": "string", + "type": "simple", + }, + ], + }, + ], + }, + ], + }, + }, + ], + "returns": { + "text": "string", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function literalUnionParamMethod( + value: LiteralUnion<'a' | 'b'>, + namedValue: LiteralUnion, + array: ReadonlyArray>, + namedArray: ReadonlyArray>, + mixed: LiteralUnion<'a' | 'b'> | ReadonlyArray>, + namedMixed: ReadonlyArray> | LiteralUnion + ): string;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(methodWithDeprecated) 1`] = ` +{ + "name": "methodWithDeprecated", + "signatures": [ + { + "deprecated": "do something else", + "description": "Test with deprecated and see marker.", + "examples": [], + "parameters": [], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [ + "test.apidocs.methodWithExample()", + ], + "signature": "function methodWithDeprecated(): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(methodWithDeprecatedOption) 1`] = ` +{ + "name": "methodWithDeprecatedOption", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with deprecated option.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "The options.", + "name": "option", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Some deprecated option. + +**DEPRECATED:** do something else.", + "name": "option.a", + "type": { + "text": "string", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Some other deprecated option. + +**DEPRECATED:** do something else.", + "name": "option.b", + "type": { + "text": "() => number", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Some other option.", + "name": "option.c", + "type": { + "text": "number", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function methodWithDeprecatedOption(option: { + a: string; + b: () => number; + c: number; + }): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(methodWithExample) 1`] = ` +{ + "name": "methodWithExample", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with example marker.", + "examples": [ + "test.apidocs.methodWithExample() // 0", + ], + "parameters": [], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function methodWithExample(): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(methodWithMultipleSeeMarkers) 1`] = ` +{ + "name": "methodWithMultipleSeeMarkers", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with multiple see markers.", + "examples": [], + "parameters": [], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [ + "test.apidocsmethodWithExample()", + "test.apidocsmethodWithDeprecated()", + ], + "signature": "function methodWithMultipleSeeMarkers(): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(methodWithMultipleSeeMarkersAndBackticks) 1`] = ` +{ + "name": "methodWithMultipleSeeMarkersAndBackticks", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with multiple see markers and backticks.", + "examples": [], + "parameters": [], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [ + "test.apidocsmethodWithExample() with parameter \`foo\`.", + "test.apidocsmethodWithDeprecated() with parameter \`bar\` and \`baz\`.", + ], + "signature": "function methodWithMultipleSeeMarkersAndBackticks(): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(methodWithMultipleThrows) 1`] = ` +{ + "name": "methodWithMultipleThrows", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with multiple throws.", + "examples": [], + "parameters": [], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function methodWithMultipleThrows(): number;", + "since": "1.0.0", + "throws": [ + "First error case.", + "Another error case.", + ], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(methodWithSinceMarker) 1`] = ` +{ + "name": "methodWithSinceMarker", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with since marker.", + "examples": [], + "parameters": [], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function methodWithSinceMarker(): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(methodWithThrows) 1`] = ` +{ + "name": "methodWithThrows", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with throws.", + "examples": [], + "parameters": [], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function methodWithThrows(): number;", + "since": "1.0.0", + "throws": [ + "Everytime.", + ], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(multiParamMethod) 1`] = ` +{ + "name": "multiParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with multiple parameters.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "The number parameter.", + "name": "a", + "type": { + "text": "number", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The string parameter.", + "name": "b?", + "type": { + "text": "string", + "type": "simple", + }, + }, + { + "default": "true", + "description": "The boolean parameter.", + "name": "c", + "type": { + "text": "boolean", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function multiParamMethod(a: number, b?: string, c: boolean = true): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(noParamMethod) 1`] = ` +{ + "name": "noParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with no parameters.", + "examples": [], + "parameters": [], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function noParamMethod(): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(optionalStringParamMethod) 1`] = ` +{ + "name": "optionalStringParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with an optional parameter.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "The string parameter.", + "name": "b?", + "type": { + "text": "string", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function optionalStringParamMethod(b?: string): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(optionsInlineParamMethodWithDefaults) 1`] = ` +{ + "name": "optionsInlineParamMethodWithDefaults", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with a function parameters (inline types) with defaults.", + "examples": [], + "parameters": [ + { + "default": "{ value: 1 }", + "description": "Parameter with signature default. +It also has a more complex description.", + "name": "a", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The number parameter.", + "name": "a.value?", + "type": { + "text": "number", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Parameter with jsdocs default. + +It also has a more complex description. + +Defaults to \`{ value: 1 }\`.", + "name": "b", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The number parameter.", + "name": "b.value?", + "type": { + "text": "number", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Parameter with inner jsdocs default.", + "name": "c", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The number parameter.", + "name": "c.value?", + "type": { + "text": "number", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function optionsInlineParamMethodWithDefaults( + a: { + value?: number; + } = { value: 1 }, + b: { + value?: number; + }, + c: { + value?: number; + } + ): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(optionsInterfaceParamMethodWithDefaults) 1`] = ` +{ + "name": "optionsInterfaceParamMethodWithDefaults", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with a function parameters with defaults.", + "examples": [], + "parameters": [ + { + "default": "{ value: 1 }", + "description": "Parameter with signature default.", + "name": "a", + "type": { + "text": "ParameterOptionsInterfaceA", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Parameter with jsdocs default. Defaults to \`{ value: 1 }\`.", + "name": "b", + "type": { + "text": "ParameterOptionsInterfaceB", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Parameter with inner jsdocs default.", + "name": "c", + "type": { + "text": "ParameterOptionsInterfaceC", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function optionsInterfaceParamMethodWithDefaults( + a: ParameterOptionsInterfaceA = { value: 1 }, + b: ParameterOptionsInterfaceB, + c: ParameterOptionsInterfaceC + ): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(optionsParamMethod) 1`] = ` +{ + "name": "optionsParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with an options parameter.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "The options parameter.", + "name": "options", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The number parameter.", + "name": "options.a", + "type": { + "text": "number", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The string parameter.", + "name": "options.b?", + "type": { + "text": "string", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The boolean parameter.", + "name": "options.c", + "type": { + "text": "boolean", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The method parameter.", + "name": "options.d", + "type": { + "text": "() => string", + "type": "simple", + }, + }, + { + "default": "'a'", + "description": "A parameter with inline documentation.", + "name": "options.e", + "type": { + "text": "'a' | 'b' | string", + "type": "union", + "types": [ + { + "text": "'a' | 'b'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + ], + }, + { + "text": "string", + "type": "simple", + }, + ], + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function optionsParamMethod(options: { + a: number; + b?: string; + c: boolean; + d: () => string; + e: LiteralUnion<'a' | 'b'>; + }): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(optionsTypeParamMethodWithDefaults) 1`] = ` +{ + "name": "optionsTypeParamMethodWithDefaults", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with a function parameters with defaults.", + "examples": [], + "parameters": [ + { + "default": "{ value: 1 }", + "description": "Parameter with signature default.", + "name": "a", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Options value.", + "name": "a.value?", + "type": { + "text": "number", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Parameter with jsdocs default. Defaults to \`{ value: 1 }\`.", + "name": "b", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Options value.", + "name": "b.value?", + "type": { + "text": "number", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Parameter with inner jsdocs default.", + "name": "c", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "Options value. Defaults to \`0\`.", + "name": "c.value?", + "type": { + "text": "number", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function optionsTypeParamMethodWithDefaults( + a: ParameterOptionsTypeA = { value: 1 }, + b: ParameterOptionsTypeB, + c: ParameterOptionsTypeC + ): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(recordParamMethod) 1`] = ` +{ + "name": "recordParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with a Record parameter.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "The Record parameter.", + "name": "object", + "type": { + "text": "Record", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function recordParamMethod(object: Record): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(requiredNumberParamMethod) 1`] = ` +{ + "name": "requiredNumberParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with a required parameter.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "The number parameter.", + "name": "a", + "type": { + "text": "number", + "type": "simple", + }, + }, + ], + "returns": { + "text": "number", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function requiredNumberParamMethod(a: number): number;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; + +exports[`method > processMethodLike(stringUnionParamMethod) 1`] = ` +{ + "name": "stringUnionParamMethod", + "signatures": [ + { + "deprecated": undefined, + "description": "Test with string union.", + "examples": [], + "parameters": [ + { + "default": undefined, + "description": "\`'a'\` or \`'b'\`.", + "name": "value", + "type": { + "text": "'a' | 'b'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + ], + }, + }, + { + "default": undefined, + "description": "The options parameter.", + "name": "options?", + "type": { + "text": "{ ... }", + "type": "simple", + }, + }, + { + "default": undefined, + "description": "The casing parameter.", + "name": "options.casing?", + "type": { + "resolvedType": { + "text": "'lower' | 'upper' | 'mixed'", + "type": "union", + "types": [ + { + "text": "'lower'", + "type": "simple", + }, + { + "text": "'upper'", + "type": "simple", + }, + { + "text": "'mixed'", + "type": "simple", + }, + ], + }, + "text": "Casing", + "type": "shadow", + }, + }, + { + "default": undefined, + "description": "The excludes parameter.", + "name": "options.excludes?", + "type": { + "text": "AlphaNumericChar[]", + "type": "generic", + "typeParameters": [ + { + "resolvedType": { + "text": "'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'", + "type": "union", + "types": [ + { + "text": "'a'", + "type": "simple", + }, + { + "text": "'b'", + "type": "simple", + }, + { + "text": "'c'", + "type": "simple", + }, + { + "text": "'d'", + "type": "simple", + }, + { + "text": "'e'", + "type": "simple", + }, + { + "text": "'f'", + "type": "simple", + }, + { + "text": "'g'", + "type": "simple", + }, + { + "text": "'h'", + "type": "simple", + }, + { + "text": "'i'", + "type": "simple", + }, + { + "text": "'j'", + "type": "simple", + }, + { + "text": "'k'", + "type": "simple", + }, + { + "text": "'l'", + "type": "simple", + }, + { + "text": "'m'", + "type": "simple", + }, + { + "text": "'n'", + "type": "simple", + }, + { + "text": "'o'", + "type": "simple", + }, + { + "text": "'p'", + "type": "simple", + }, + { + "text": "'q'", + "type": "simple", + }, + { + "text": "'r'", + "type": "simple", + }, + { + "text": "'s'", + "type": "simple", + }, + { + "text": "'t'", + "type": "simple", + }, + { + "text": "'u'", + "type": "simple", + }, + { + "text": "'v'", + "type": "simple", + }, + { + "text": "'w'", + "type": "simple", + }, + { + "text": "'x'", + "type": "simple", + }, + { + "text": "'y'", + "type": "simple", + }, + { + "text": "'z'", + "type": "simple", + }, + { + "text": "'A'", + "type": "simple", + }, + { + "text": "'B'", + "type": "simple", + }, + { + "text": "'C'", + "type": "simple", + }, + { + "text": "'D'", + "type": "simple", + }, + { + "text": "'E'", + "type": "simple", + }, + { + "text": "'F'", + "type": "simple", + }, + { + "text": "'G'", + "type": "simple", + }, + { + "text": "'H'", + "type": "simple", + }, + { + "text": "'I'", + "type": "simple", + }, + { + "text": "'J'", + "type": "simple", + }, + { + "text": "'K'", + "type": "simple", + }, + { + "text": "'L'", + "type": "simple", + }, + { + "text": "'M'", + "type": "simple", + }, + { + "text": "'N'", + "type": "simple", + }, + { + "text": "'O'", + "type": "simple", + }, + { + "text": "'P'", + "type": "simple", + }, + { + "text": "'Q'", + "type": "simple", + }, + { + "text": "'R'", + "type": "simple", + }, + { + "text": "'S'", + "type": "simple", + }, + { + "text": "'T'", + "type": "simple", + }, + { + "text": "'U'", + "type": "simple", + }, + { + "text": "'V'", + "type": "simple", + }, + { + "text": "'W'", + "type": "simple", + }, + { + "text": "'X'", + "type": "simple", + }, + { + "text": "'Y'", + "type": "simple", + }, + { + "text": "'Z'", + "type": "simple", + }, + { + "text": "'0'", + "type": "simple", + }, + { + "text": "'1'", + "type": "simple", + }, + { + "text": "'2'", + "type": "simple", + }, + { + "text": "'3'", + "type": "simple", + }, + { + "text": "'4'", + "type": "simple", + }, + { + "text": "'5'", + "type": "simple", + }, + { + "text": "'6'", + "type": "simple", + }, + { + "text": "'7'", + "type": "simple", + }, + { + "text": "'8'", + "type": "simple", + }, + { + "text": "'9'", + "type": "simple", + }, + ], + }, + "text": "AlphaNumericChar", + "type": "shadow", + }, + ], + }, + }, + { + "default": undefined, + "description": "The format parameter.", + "name": "options.format?", + "type": { + "text": "'hex' | 'css' | 'binary' | 'decimal'", + "type": "union", + "types": [ + { + "text": "'hex'", + "type": "simple", + }, + { + "text": "'css'", + "type": "simple", + }, + { + "text": "'binary'", + "type": "simple", + }, + { + "text": "'decimal'", + "type": "simple", + }, + ], + }, + }, + ], + "returns": { + "text": "string", + "type": "simple", + }, + "seeAlsos": [], + "signature": "function stringUnionParamMethod( + value: 'a' | 'b', + options?: { + casing?: Casing; + format?: 'hex' | ColorFormat; + excludes?: ReadonlyArray; + } + ): string;", + "since": "1.0.0", + "throws": [], + }, + ], + "source": { + "column": -1, + "filePath": "test/scripts/apidocs/method.example.ts", + "line": -1, + }, +} +`; diff --git a/test/scripts/apidocs/class.example.ts b/test/scripts/apidocs/class.example.ts new file mode 100644 index 00000000..0e5d9d89 --- /dev/null +++ b/test/scripts/apidocs/class.example.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-extraneous-class -- required for tests */ + +/** + * A simple module without anything special. + */ +export class ModuleSimpleTest {} + +/** + * Description with a link to our [website](https://fakerjs.dev/) + * and [api docs](https://fakerjs.dev/api/). + */ +export class ModuleFakerJsLinkTest {} + +/** + * Description with a link to our [website](https://next.fakerjs.dev/) + * and [api docs](https://next.fakerjs.dev/api/). + */ +export class ModuleNextFakerJsLinkTest {} + +/** + * This is a description for a module with a code example. + * + * @deprecated Well, this is deprecated. + */ +export class ModuleDeprecationTest {} + +/** + * This is a description for a module with a code example. + * + * @example + * new ModuleExampleTest() + */ +export class ModuleExampleTest {} diff --git a/test/scripts/apidocs/class.spec.ts b/test/scripts/apidocs/class.spec.ts new file mode 100644 index 00000000..8ebce847 --- /dev/null +++ b/test/scripts/apidocs/class.spec.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest'; +import { processClass } from '../../../scripts/apidocs/processing/class'; +import * as ModuleTests from './class.example'; +import { loadExampleClasses } from './utils'; + +const modules = loadExampleClasses(); + +describe('class', () => { + it('dummy dependency to rerun the test if the example changes', () => { + expect(Object.keys(ModuleTests)).not.toEqual([]); + }); + + it('expected and actual modules are equal', () => { + expect(Object.keys(modules).sort()).toMatchSnapshot(); + }); + + it.each(Object.entries(modules))('processClass(%s)', (_, module) => { + const actual = processClass(module); + + expect(actual).toMatchSnapshot(); + }); +}); diff --git a/test/scripts/apidocs/method.example.ts b/test/scripts/apidocs/method.example.ts new file mode 100644 index 00000000..e72f5f3f --- /dev/null +++ b/test/scripts/apidocs/method.example.ts @@ -0,0 +1,469 @@ +import type { Casing, ColorFormat } from '../../../src'; +import { FakerError } from '../../../src/errors/faker-error'; +import type { AlphaNumericChar } from '../../../src/modules/string'; +import type { LiteralUnion } from '../../../src/utils/types'; +// explicitly export types so they show up in the docs as decomposed types +export type { NumberColorFormat, StringColorFormat } from '../../../src'; +export type { AlphaNumericChar, Casing, ColorFormat, LiteralUnion }; + +/** + * Parameter options type with default from signature. + */ +export type ParameterOptionsTypeA = { + /** + * Options value. + */ + value?: number; +}; + +/** + * Parameter options type with default from jsdocs. Defaults to `{value: 0}`. + */ +export type ParameterOptionsTypeB = { + /** + * Options value. + */ + value?: number; +}; + +/** + * Parameter options type with default from inner jsdocs. + */ +export type ParameterOptionsTypeC = { + /** + * Options value. Defaults to `0`. + */ + value?: number; +}; + +/** + * Parameter options type with default from signature. + */ +export interface ParameterOptionsInterfaceA { + /** + * Options value. + */ + value?: number; +} + +/** + * Parameter options type with default from jsdocs. + */ +export interface ParameterOptionsInterfaceB { + /** + * Options value. + */ + value?: number; +} + +/** + * Parameter options type with default from inner jsdocs. + */ +export interface ParameterOptionsInterfaceC { + /** + * Options value. Defaults to `0`. + */ + value?: number; +} + +/** + * A or B. + */ +export type AB = 'a' | 'b'; + +export class SignatureTest { + /** + * Test with no parameters. + * + * @since 1.0.0 + */ + noParamMethod(): number { + return 0; + } + + /** + * Test with a required parameter. + * + * @param a The number parameter. + * + * @since 1.0.0 + */ + requiredNumberParamMethod(a: number): number { + return a; + } + + /** + * Test with an optional parameter. + * + * @param b The string parameter. + * + * @since 1.0.0 + */ + optionalStringParamMethod(b?: string): number { + return b ? 0 : 1; + } + + /** + * Test with a default parameter. + * + * @param c The boolean parameter. + * + * @since 1.0.0 + */ + defaultBooleanParamMethod(c: boolean = true): number { + return c ? 1 : 0; + } + + /** + * Test with multiple parameters. + * + * @param a The number parameter. + * @param b The string parameter. + * @param c The boolean parameter. + * + * @since 1.0.0 + */ + multiParamMethod(a: number, b?: string, c: boolean = true): number { + return c ? a : b ? 0 : 1; + } + + /** + * Test with a function parameters. + * + * @param fn The function parameter. + * + * @since 1.0.0 + */ + functionParamMethod(fn: (a: string) => number): number { + return fn('a'); + } + + /** + * Test with string union. + * + * @param value `'a'` or `'b'`. + * @param options The options parameter. + * @param options.casing The casing parameter. + * @param options.format The format parameter. + * @param options.excludes The excludes parameter. + * + * @since 1.0.0 + */ + stringUnionParamMethod( + value: 'a' | 'b', + options?: { + /** + * The casing parameter. + */ + casing?: Casing; + /** + * The format parameter. + */ + format?: 'hex' | ColorFormat; + /** + * The excludes parameter. + */ + excludes?: ReadonlyArray; + } + ): string { + return options?.format ?? value; + } + + /** + * Test with LiteralUnion. + * + * @param value `'a'` or `'b'`. + * @param namedValue `'a'` or `'b'`. + * @param array Array of `'a'` or `'b'`. + * @param namedArray Array of `'a'` or `'b'`. + * @param mixed Value `'a'` or `'b'` or an array thereof. + * @param namedMixed Value `'a'` or `'b'` or an array thereof. + * + * @since 1.0.0 + */ + literalUnionParamMethod( + value: LiteralUnion<'a' | 'b'>, + namedValue: LiteralUnion, + array: ReadonlyArray>, + namedArray: ReadonlyArray>, + mixed: LiteralUnion<'a' | 'b'> | ReadonlyArray>, + namedMixed: ReadonlyArray> | LiteralUnion + ): string { + return ( + value + + namedValue + + array.join('') + + namedArray.join('') + + String(mixed) + + String(namedMixed) + ); + } + + /** + * Test with a Record parameter. + * + * @param object The Record parameter. + * + * @since 1.0.0 + */ + recordParamMethod(object: Record): number { + return object.a; + } + + /** + * Test with an options parameter. + * + * @param options The options parameter. + * @param options.a The number parameter. + * @param options.b The string parameter. + * @param options.c The boolean parameter. + * @param options.d The method parameter. + * @param options.e The LiteralUnion parameter. + * + * @since 1.0.0 + */ + optionsParamMethod(options: { + /** + * The number parameter. + */ + a: number; + /** + * The string parameter. + */ + b?: string; + /** + * The boolean parameter. + */ + c: boolean; + /** + * The method parameter. + */ + d: () => string; + /** + * A parameter with inline documentation. + * + * @default 'a' + */ + e: LiteralUnion<'a' | 'b'>; + }): number { + return options.a; + } + + /** + * Test with a function parameters (inline types) with defaults. + * + * @param a Parameter with signature default. + * It also has a more complex description. + * @param a.value The number parameter. + * @param b Parameter with jsdocs default. + * + * It also has a more complex description. + * + * Defaults to `{ value: 1 }`. + * @param b.value The number parameter. + * @param c Parameter with inner jsdocs default. + * @param c.value The number parameter. It also has a more complex description. Defaults to `2`. + * + * @since 1.0.0 + */ + optionsInlineParamMethodWithDefaults( + a: { + /** + * The number parameter. + */ + value?: number; + } = { value: 1 }, + b: { + /** + * The number parameter. + */ + value?: number; + }, + c: { + /** + * The number parameter. + */ + value?: number; + } + ): number { + return a.value ?? b.value ?? c.value ?? -1; + } + + /** + * Test with a function parameters with defaults. + * + * @param a Parameter with signature default. + * @param a.value The number parameter. + * @param b Parameter with jsdocs default. Defaults to `{ value: 1 }`. + * @param b.value The number parameter. + * @param c Parameter with inner jsdocs default. + * @param c.value The number parameter. Defaults to `2`. + * + * @since 1.0.0 + */ + optionsTypeParamMethodWithDefaults( + a: ParameterOptionsTypeA = { value: 1 }, + b: ParameterOptionsTypeB, + c: ParameterOptionsTypeC + ): number { + return a.value ?? b.value ?? c.value ?? -1; + } + + /** + * Test with a function parameters with defaults. + * + * @param a Parameter with signature default. + * @param a.value The number parameter. + * @param b Parameter with jsdocs default. Defaults to `{ value: 1 }`. + * @param b.value The number parameter. + * @param c Parameter with inner jsdocs default. + * @param c.value The number parameter. Defaults to `2`. + * + * @since 1.0.0 + */ + optionsInterfaceParamMethodWithDefaults( + a: ParameterOptionsInterfaceA = { value: 1 }, + b: ParameterOptionsInterfaceB, + c: ParameterOptionsInterfaceC + ): number { + return a.value ?? b.value ?? c.value ?? -1; + } + + /** + * Test with example marker. + * + * @example + * test.apidocs.methodWithExample() // 0 + * + * @since 1.0.0 + */ + methodWithExample(): number { + return 0; + } + + /** + * Test with deprecated and see marker. + * + * @see test.apidocs.methodWithExample() + * + * @since 1.0.0 + * + * @deprecated do something else + */ + methodWithDeprecated(): number { + return 0; + } + + /** + * Test with throws. + * + * @throws Everytime. + * + * @since 1.0.0 + */ + methodWithThrows(): number { + throw new FakerError('Test error'); + } + + /** + * Test with multiple throws. + * + * @throws First error case. + * @throws Another error case. + * + * @since 1.0.0 + */ + methodWithMultipleThrows(): number { + throw new FakerError('Another test error'); + } + + /** + * Test with deprecated option. + * + * @param option The options. + * @param option.a Some deprecated option. + * @param option.b Some other deprecated option. + * @param option.c Some other option. + * + * @since 1.0.0 + */ + methodWithDeprecatedOption(option: { + /** + * Some deprecated option. + * + * @deprecated do something else. + */ + a: string; + /** + * Some other deprecated option. + * + * @deprecated do something else. + */ + b: () => number; + /** + * Some other option. + */ + c: number; + }): number { + return option.c; + } + + /** + * Test with multiple see markers. + * + * @see test.apidocsmethodWithExample() + * @see test.apidocsmethodWithDeprecated() + * + * @since 1.0.0 + */ + methodWithMultipleSeeMarkers(): number { + return 0; + } + + /** + * Test with multiple see markers and backticks. + * + * @see test.apidocsmethodWithExample() with parameter `foo`. + * @see test.apidocsmethodWithDeprecated() with parameter `bar` and `baz`. + * + * @since 1.0.0 + */ + methodWithMultipleSeeMarkersAndBackticks(): number { + return 0; + } + + /** + * Test with since marker. + * + * @since 1.0.0 + */ + methodWithSinceMarker(): number { + return 0; + } + + /** + * Complex array parameter. + * + * @template T The type of the entries to pick from. + * + * @param array Array to pick the value from. + * @param array[].weight The weight of the value. + * @param array[].value The value to pick. + * + * @since 1.0.0 + */ + complexArrayParameter( + array: ReadonlyArray<{ + /** + * The weight of the value. + */ + weight: number; + /** + * The value to pick. + */ + value: T; + }> + ): T { + return array[0].value; + } +} diff --git a/test/scripts/apidocs/method.spec.ts b/test/scripts/apidocs/method.spec.ts new file mode 100644 index 00000000..14c9cfc0 --- /dev/null +++ b/test/scripts/apidocs/method.spec.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from 'vitest'; +import { processMethodLike } from '../../../scripts/apidocs/processing/method'; +import { SignatureTest } from './method.example'; +import { loadExampleMethods } from './utils'; + +const methods = loadExampleMethods(); + +describe('method', () => { + it('dummy dependency to rerun the test if the example changes', () => { + expect(new SignatureTest()).toBeTruthy(); + }); + + it('expected and actual methods are equal', () => { + expect(Object.keys(methods)).toMatchSnapshot(); + }); + + it.each(Object.entries(methods))( + 'processMethodLike(%s)', + (name, signature) => { + const actual = processMethodLike(name, signature); + actual.source = { + filePath: actual.source.filePath, + line: -1, + column: -1, + }; + + expect(actual).toMatchSnapshot(); + } + ); +}); diff --git a/test/scripts/apidocs/utils.ts b/test/scripts/apidocs/utils.ts new file mode 100644 index 00000000..e15bcd8f --- /dev/null +++ b/test/scripts/apidocs/utils.ts @@ -0,0 +1,38 @@ +import type { ClassDeclaration, MethodDeclaration, SourceFile } from 'ts-morph'; +import { getProject } from '../../../scripts/apidocs/project'; + +/** + * Loads the example methods. + */ +export function loadExampleMethods(): Record { + return Object.fromEntries( + loadProjectFile('test/scripts/apidocs/method.example.ts') + .getClassOrThrow('SignatureTest') + .getMethods() + .map((m) => [m.getName(), m] as const) + .sort(([a], [b]) => a.localeCompare(b)) // Relevant for Object.keys() order + ); +} + +/** + * Loads the example classes. + */ +export function loadExampleClasses(): Record { + return Object.fromEntries( + loadProjectFile('test/scripts/apidocs/class.example.ts') + .getClasses() + .map((m) => [m.getNameOrThrow(), m] as const) + .sort(([a], [b]) => a.localeCompare(b)) // Relevant for Object.keys() order + ); +} + +/** + * Loads the project. + * + * @param sourceFile The source file to load. + */ +function loadProjectFile(sourceFile: string): SourceFile { + const project = getProject(); + + return project.addSourceFileAtPath(sourceFile); +} diff --git a/test/scripts/apidocs/verify-jsdoc-tags.spec.ts b/test/scripts/apidocs/verify-jsdoc-tags.spec.ts new file mode 100644 index 00000000..16f0ea51 --- /dev/null +++ b/test/scripts/apidocs/verify-jsdoc-tags.spec.ts @@ -0,0 +1,286 @@ +import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import validator from 'validator'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; +import { processComponents } from '../../../scripts/apidocs/generate'; +import { extractSummaryDefault } from '../../../scripts/apidocs/output/page'; +import { getProject } from '../../../scripts/apidocs/project'; + +// This test suite ensures, that every method +// - has working examples +// - running these do not log anything, unless the method is deprecated +// - has a valid @since tag +// - has valid @see tags +// - has proper links in the description + +const tempDir = resolve(dirname(fileURLToPath(import.meta.url)), 'temp'); +const relativeImportPath = `${'../'.repeat(5)}src`; + +afterAll(() => { + // Remove temp folder + if (existsSync(tempDir)) { + rmSync(tempDir, { recursive: true }); + } +}); + +const modules = processComponents(getProject()); + +function resolveDirToModule(moduleName: string): string { + return resolve(tempDir, moduleName); +} + +function resolvePathToMethodFile( + moduleName: string, + methodName: string, + signature: number +): string { + const dir = resolveDirToModule(moduleName); + return resolve(dir, `${methodName}_${signature}.ts`); +} + +const allowedReferences = new Set( + modules.flatMap(({ camelTitle, methods, category }) => { + return methods.map(({ name }) => + category ? `faker.${camelTitle}.${name}` : `${camelTitle}.${name}` + ); + }) +); +const allowedLinks = new Set( + modules.flatMap(({ camelTitle, methods }) => { + return [ + `/api/${camelTitle}.html`, + ...methods.map( + ({ name }) => `/api/${camelTitle}.html#${name.toLowerCase()}` + ), + ]; + }) +); + +function assertDescription(description: string): void { + const linkRegexp = /\[([^\]]+)\]\(([^)]+)\)/g; + const links = [...description.matchAll(linkRegexp)].map((m) => m[2]); + + for (const link of links) { + expect(link).toMatch(/^https?:\/\//); + expect(link).toSatisfy(validator.isURL); + + if (link.includes('fakerjs.dev/api/')) { + expect(allowedLinks, `${link} to point to a valid target`).toContain( + link.replace(/.*fakerjs.dev\//, '/') + ); + } + } +} + +describe('verify JSDoc tags', () => { + describe.each(modules.map((m) => [m.camelTitle, m]))( + '%s', + (moduleName, module) => { + describe('verify module', () => { + it('verify description', () => { + assertDescription(module.description); + }); + }); + + describe.each(module.methods.map((m) => [m.name, m]))( + '%s', + (methodName, method) => { + describe.each(method.signatures.map((s, i) => [i, s]))( + '%i', + (signatureIndex, signature) => { + beforeAll(() => { + // Write temp files to disk + // By extracting the examples + // Guessing required imports + // And saving them to disk for later execution + + const dir = resolveDirToModule(moduleName); + mkdirSync(dir, { recursive: true }); + const path = resolvePathToMethodFile( + moduleName, + methodName, + signatureIndex + ); + + let examples = signature.examples.join('\n'); + if (moduleName === 'faker' && methodName === 'constructor') { + // That case should demonstrate an error and is thus not suitable for testing + examples = examples.replace( + 'customFaker.music.genre()', + '// customFaker.music.genre()' + ); + } + + // Replace imports for users with our source path + examples = examples.replaceAll( + " from '@faker-js/faker'", + ` from '${relativeImportPath}'` + ); + + if (moduleName === 'randomizer') { + examples = `import { generateMersenne32Randomizer } from '${relativeImportPath}/internal/mersenne'; + +const randomizer = generateMersenne32Randomizer(); + +${examples}`; + } + + // If imports are present, we expect them to be complete + if (!examples.includes('import ')) { + const imports = [ + // collect the imports for the various locales e.g. fakerDE_CH + ...new Set(examples.match(/(? 0) { + examples = `import { ${imports.join( + ', ' + )} } from '${relativeImportPath}';\n\n${examples}`; + } + } + + writeFileSync(path, examples); + }); + + it('verify description', () => { + assertDescription(signature.description); + }); + + it('verify @example tag', async () => { + const examples = signature.examples.join('\n'); + + expect( + examples, + `${moduleName}.${methodName} to have examples` + ).not.toBe(''); + + // Grab path to example file + const path = resolvePathToMethodFile( + moduleName, + methodName, + signatureIndex + ); + + // Executing the examples should not throw + await expect( + import(`${path}?scope=example`), + examples + ).resolves.toBeDefined(); + }); + + // This only checks whether the whole method is deprecated or not + // It does not check whether the method is deprecated for a specific set of arguments + it('verify @deprecated tag', async () => { + // Grab path to example file + const path = resolvePathToMethodFile( + moduleName, + methodName, + signatureIndex + ); + + const consoleWarnSpy = vi.spyOn(console, 'warn'); + + // Run the examples + await import(`${path}?scope=deprecated`); + + // Verify that deprecated methods log a warning + const { deprecated } = signature; + if (deprecated == null) { + expect(consoleWarnSpy).not.toHaveBeenCalled(); + } else { + expect(consoleWarnSpy).toHaveBeenCalled(); + expect(deprecated).not.toBe(''); + } + }); + + describe.each(signature.parameters.map((p) => [p.name, p]))( + '%s', + (_, parameter) => { + it('verify default value', () => { + const { + name, + default: paramDefault, + description, + } = parameter; + + const commentDefault = extractSummaryDefault(description); + if (paramDefault) { + if ( + /^{.*}$/.test(paramDefault) || + paramDefault.includes('\n') + ) { + expect(commentDefault).toBeUndefined(); + } else if ( + !name.includes('.') && + // Skip check of defaults in descriptions if it is a paraphrased function call + (commentDefault || + (!description.includes('Defaults to') && + !paramDefault.includes('('))) + ) { + expect( + commentDefault, + `Expect '${name}'s js implementation default to be the same as the jsdoc summary default` + ).toBe(paramDefault); + } + } + }); + + it('verify description', () => { + assertDescription(parameter.description); + }); + } + ); + + it('verify @see tags', () => { + for (const link of signature.seeAlsos) { + if (link.startsWith('faker.')) { + // Expected @see faker.xxx.yyy() + expect( + link, + 'Expect method reference to contain ()' + ).toContain('('); + expect( + link, + 'Expect method reference to contain ()' + ).toContain(')'); + expect( + link, + "Expect method reference to have a ': ' after the parenthesis" + ).toContain('): '); + expect( + link, + 'Expect method reference to have a description starting with a capital letter' + ).toMatch(/\): [A-Z]/); + expect( + link, + 'Expect method reference to start with a standard description phrase' + ).toMatch( + /\): (?:For generating |For more information about |For using |For the replacement method)/ + ); + expect( + link, + 'Expect method reference to have a description ending with a dot' + ).toMatch(/\.$/); + expect(allowedReferences).toContain( + link.replace(/\(.*/, '') + ); + } + } + }); + + it('verify @since tag', () => { + const { since } = signature; + expect(since, '@since to be present').toBeTruthy(); + expect(since).not.toBe(''); + expect(since, '@since to be a valid semver').toSatisfy( + validator.isSemVer + ); + }); + } + ); + } + ); + } + ); +}); -- cgit v1.2.3