diff --git a/fixtures/test-utils/advanced-types/index.ts b/fixtures/test-utils/advanced-types/index.ts index 5abda27..c8de9a5 100644 --- a/fixtures/test-utils/advanced-types/index.ts +++ b/fixtures/test-utils/advanced-types/index.ts @@ -1,9 +1,26 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 export class TestUtilWrapper { + /** + * Generic return value + */ findAll(): Array { return []; } + /** + * Generic arguments + */ setAll(all: Array) {} + + /** + * Method overload example + */ + keydown(keyCode: number): void; + keydown(keyboardEventProps: KeyboardEventInit): void; + keydown(args: KeyboardEventInit | number) {} +} + +export default function createWrapper() { + return new TestUtilWrapper(); } diff --git a/src/test-utils-new/extractor.ts b/src/test-utils-new/extractor.ts index fe6668b..2850792 100644 --- a/src/test-utils-new/extractor.ts +++ b/src/test-utils-new/extractor.ts @@ -25,7 +25,11 @@ function getDefaultValue(declaration: ts.Declaration) { return declaration.initializer.getText(); } -export default function extractDocumentation(sourceFile: ts.SourceFile, checker: ts.TypeChecker): Array { +export default function extractDocumentation( + sourceFile: ts.SourceFile, + checker: ts.TypeChecker, + extraExports: Array +): Array { const moduleSymbol = checker.getSymbolAtLocation(sourceFile); if (!moduleSymbol) { throw new Error(`Unable to resolve module: ${sourceFile.fileName}`); @@ -35,14 +39,21 @@ export default function extractDocumentation(sourceFile: ts.SourceFile, checker: const definitions: Array = []; for (const symbol of exportSymbols) { + const className = symbol.getName(); + if (extraExports.includes(className)) { + continue; + } if (!(symbol.flags & ts.SymbolFlags.Class)) { throw new Error(`Exported symbol is not a class, got ${checker.symbolToString(symbol)}`); } - const className = symbol.getName(); + const classType = checker.getTypeAtLocation(extractDeclaration(symbol)); const classDefinition: TestUtilsDoc = { name: className, methods: [] }; for (const property of classType.getProperties()) { - const declaration = extractDeclaration(property); + const declaration = property.valueDeclaration; + if (!declaration) { + throw new Error(`Unexpected member on ${className} – ${property.getName()}`); + } const modifiers = (ts.canHaveModifiers(declaration) && ts.getModifiers(declaration)) || []; if ( modifiers.find( @@ -52,26 +63,25 @@ export default function extractDocumentation(sourceFile: ts.SourceFile, checker: continue; } const type = checker.getTypeAtLocation(declaration); - if (type.getCallSignatures().length !== 1) { - throw new Error(`Unexpected member on ${className} – ${property.getName()}: ${stringifyType(type, checker)}`); + for (const signature of type.getCallSignatures()) { + // report each function signature as a separate method + classDefinition.methods.push({ + name: property.getName(), + description: getDescription(property.getDocumentationComment(checker), declaration).text, + returnType: { name: stringifyType(signature.getReturnType(), checker) }, + parameters: signature.parameters.map(parameter => { + const paramType = checker.getTypeAtLocation(extractDeclaration(parameter)); + return { + name: parameter.name, + typeName: stringifyType(paramType, checker), + description: getDescription(parameter.getDocumentationComment(checker), declaration).text, + flags: { isOptional: isOptional(paramType) }, + defaultValue: getDefaultValue(extractDeclaration(parameter)), + }; + }), + inheritedFrom: getInheritedFrom(declaration, className), + }); } - const returnType = type.getCallSignatures()[0].getReturnType(); - classDefinition.methods.push({ - name: property.getName(), - description: getDescription(property.getDocumentationComment(checker), declaration).text, - inheritedFrom: getInheritedFrom(declaration, className), - parameters: type.getCallSignatures()[0].parameters.map(parameter => { - const paramType = checker.getTypeAtLocation(extractDeclaration(parameter)); - return { - name: parameter.name, - typeName: stringifyType(paramType, checker), - description: getDescription(parameter.getDocumentationComment(checker), declaration).text, - flags: { isOptional: isOptional(paramType) }, - defaultValue: getDefaultValue(extractDeclaration(parameter)), - }; - }), - returnType: { name: stringifyType(returnType, checker) }, - }); } classDefinition.methods.sort((a, b) => a.name.localeCompare(b.name)); diff --git a/src/test-utils-new/index.ts b/src/test-utils-new/index.ts index aa80492..74c9ea0 100644 --- a/src/test-utils-new/index.ts +++ b/src/test-utils-new/index.ts @@ -6,10 +6,15 @@ import { bootstrapTypescriptProject } from '../bootstrap/typescript'; import extractDocumentation from './extractor'; import { TestUtilsDoc } from '../test-utils/interfaces'; +export interface TestUtilsVariantOptions { + root: string; + extraExports?: Array; +} + export interface TestUtilsDocumenterOptions { tsconfigPath: string; - domUtilsRoot: string; - selectorsUtilsRoot: string; + domUtils: TestUtilsVariantOptions; + selectorsUtils: TestUtilsVariantOptions; } interface TestUtilsDefinitions { @@ -18,8 +23,8 @@ interface TestUtilsDefinitions { } export function documentTestUtilsNew(options: TestUtilsDocumenterOptions): TestUtilsDefinitions { - const domUtilsRoot = pathe.resolve(options.domUtilsRoot); - const selectorsUtilsRoot = pathe.resolve(options.selectorsUtilsRoot); + const domUtilsRoot = pathe.resolve(options.domUtils.root); + const selectorsUtilsRoot = pathe.resolve(options.selectorsUtils.root); const program = bootstrapTypescriptProject(options.tsconfigPath); const checker = program.getTypeChecker(); @@ -33,8 +38,8 @@ export function documentTestUtilsNew(options: TestUtilsDocumenterOptions): TestU throw new Error(`File '${selectorsUtilsFile}' not found`); } return { - domDefinitions: extractDocumentation(domUtilsFile, checker), - selectorsDefinitions: extractDocumentation(selectorsUtilsFile, checker), + domDefinitions: extractDocumentation(domUtilsFile, checker, options.domUtils.extraExports ?? []), + selectorsDefinitions: extractDocumentation(selectorsUtilsFile, checker, options.selectorsUtils.extraExports ?? []), }; } diff --git a/test/test-utils/__snapshots__/doc-generation.test.ts.snap b/test/test-utils/__snapshots__/doc-generation.test.ts.snap index 7ec4f12..b18560b 100644 --- a/test/test-utils/__snapshots__/doc-generation.test.ts.snap +++ b/test/test-utils/__snapshots__/doc-generation.test.ts.snap @@ -17,13 +17,70 @@ exports[`Generate documentation > For simple cases 1`] = ` exports[`Generate documentation > deal with more complex types 1`] = ` [ { - "defaultValue": undefined, - "description": undefined, - "flags": { - "isOptional": false, + "description": "Generic return value", + "inheritedFrom": undefined, + "name": "findAll", + "parameters": [], + "returnType": { + "name": "Array", + }, + }, + { + "description": "Method overload example", + "inheritedFrom": undefined, + "name": "keydown", + "parameters": [ + { + "defaultValue": undefined, + "description": undefined, + "flags": { + "isOptional": false, + }, + "name": "keyCode", + "typeName": "number", + }, + ], + "returnType": { + "name": "void", + }, + }, + { + "description": "Method overload example", + "inheritedFrom": undefined, + "name": "keydown", + "parameters": [ + { + "defaultValue": undefined, + "description": undefined, + "flags": { + "isOptional": false, + }, + "name": "keyboardEventProps", + "typeName": "KeyboardEventInit", + }, + ], + "returnType": { + "name": "void", + }, + }, + { + "description": "Generic arguments", + "inheritedFrom": undefined, + "name": "setAll", + "parameters": [ + { + "defaultValue": undefined, + "description": undefined, + "flags": { + "isOptional": false, + }, + "name": "all", + "typeName": "Array", + }, + ], + "returnType": { + "name": "void", }, - "name": "all", - "typeName": "Array", }, ] `; diff --git a/test/test-utils/doc-generation.test.ts b/test/test-utils/doc-generation.test.ts index 177be9a..ed9770d 100644 --- a/test/test-utils/doc-generation.test.ts +++ b/test/test-utils/doc-generation.test.ts @@ -49,7 +49,7 @@ describe('Generate documentation', () => { }); test('deal with more complex types', () => { - const results = buildTestUtilsProject('advanced-types'); + const results = buildTestUtilsProject('advanced-types', { extraExports: ['default'] }); expect(results.length).toBe(1); const classDoc = results[0]; @@ -57,21 +57,8 @@ describe('Generate documentation', () => { expect(classDoc.name).toBe('TestUtilWrapper'); const methods = classDoc.methods; - expect(methods.length).toBe(2); - - const findAllMethod = methods.find(method => method.name === 'findAll'); - expect(findAllMethod).toBeDefined(); - expect(findAllMethod?.returnType).toEqual({ name: 'Array' }); - expect(findAllMethod?.parameters).toEqual([]); - expect(findAllMethod?.description).toBeUndefined(); - expect(findAllMethod?.inheritedFrom).toBeUndefined(); - - const setAllMethod = methods.find(method => method.name === 'setAll'); - expect(setAllMethod).toBeDefined(); - expect(setAllMethod?.returnType).toEqual({ name: 'void' }); - expect(setAllMethod?.parameters).toMatchSnapshot(); - expect(setAllMethod?.description).toBeUndefined(); - expect(setAllMethod?.inheritedFrom).toBeUndefined(); + expect(methods.length).toBe(4); + expect(methods).toMatchSnapshot(); }); test('and deal with inheritance', () => { diff --git a/test/test-utils/test-helpers.ts b/test/test-utils/test-helpers.ts index 5ab22e4..9d76bc8 100644 --- a/test/test-utils/test-helpers.ts +++ b/test/test-utils/test-helpers.ts @@ -1,16 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { documentTestUtilsNew, TestUtilsDocumenterOptions } from '../../src/test-utils-new'; +import { documentTestUtilsNew, TestUtilsVariantOptions } from '../../src/test-utils-new'; import { TestUtilsDoc } from '../../src/test-utils/interfaces'; export function buildTestUtilsProject( name: string, - configOverrides?: Partial + configOverrides?: Partial ): TestUtilsDoc[] { return documentTestUtilsNew({ tsconfigPath: require.resolve(`../../fixtures/test-utils/${name}/tsconfig.json`), - domUtilsRoot: `fixtures/test-utils/${name}/index.ts`, - selectorsUtilsRoot: `fixtures/test-utils/${name}/index.ts`, - ...configOverrides, + domUtils: { root: `fixtures/test-utils/${name}/index.ts`, ...configOverrides }, + selectorsUtils: { root: `fixtures/test-utils/${name}/index.ts`, ...configOverrides }, }).domDefinitions; } diff --git a/test/test-utils/usage.test.ts b/test/test-utils/usage.test.ts index 331a714..0df73fb 100644 --- a/test/test-utils/usage.test.ts +++ b/test/test-utils/usage.test.ts @@ -19,7 +19,7 @@ describe('documentTestUtils throws error for ', () => { test('having no input files because of a non-matching glob', () => { expect(() => buildTestUtilsProject('simple', { - domUtilsRoot: 'fixtures/does-not-exist/index.ts', + root: 'fixtures/does-not-exist/index.ts', }) ).toThrow(/File '.*fixtures\/does-not-exist\/index.ts' not found/); }); diff --git a/test/test-utils/writer.test.ts b/test/test-utils/writer.test.ts index e9ab46e..b3f81a9 100644 --- a/test/test-utils/writer.test.ts +++ b/test/test-utils/writer.test.ts @@ -14,8 +14,8 @@ test('should write documentation files into the outDir', async () => { writeTestUtilsDocumentation({ tsconfigPath: pathe.resolve('fixtures/test-utils/simple/tsconfig.json'), - domUtilsRoot: 'fixtures/test-utils/simple/index.ts', - selectorsUtilsRoot: 'fixtures/test-utils/simple/index.ts', + domUtils: { root: 'fixtures/test-utils/simple/index.ts' }, + selectorsUtils: { root: 'fixtures/test-utils/simple/index.ts' }, outDir, });