react-docgen-typescript
Version:
[](https://github.com/styleguidist/react-docgen-typescript/actions/workflows/nodejs.yml)
1,133 lines • 72.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var chai_1 = require("chai");
var fs = require("fs");
var path = require("path");
var ts = require("typescript");
var parser_1 = require("../parser");
var testUtils_1 = require("./testUtils");
describe('parser', function () {
it('should parse simple react class component', function () {
testUtils_1.check('Column', {
Column: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
});
});
it('should parse simple react class component with console.log inside', function () {
testUtils_1.check('ColumnWithLog', {
Column: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
});
});
it('should parse simple react class component as default export', function () {
testUtils_1.check('ColumnWithDefaultExport', {
Column: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
});
});
it('should parse simple typescript definition file with default export', function () {
testUtils_1.check('StatelessDisplayNameFolder/Stateless.d.ts', {
Stateless: {
foo: { description: '', type: 'string', required: false }
}
}, true, '');
});
describe('file path', function () {
it('should return the correct filepath for a parsed component', function () {
var results = parser_1.parse([testUtils_1.fixturePath('FilePathCheck')]);
results.forEach(function (result) {
chai_1.assert.equal(result.filePath, path.resolve('src/__tests__/data/FilePathCheck.tsx'));
});
});
it('should return the correct filepath for multiple parsed components', function () {
var results = parser_1.parse([
testUtils_1.fixturePath('FilePathCheck'),
testUtils_1.fixturePath('Column'),
testUtils_1.fixturePath('ColumnWithDefaultExportOnly')
]);
var paths = [
path.resolve('src/__tests__/data/FilePathCheck.tsx'),
path.resolve('src/__tests__/data/Column.tsx'),
path.resolve('src/__tests__/data/ColumnWithDefaultExportOnly.tsx')
];
results.forEach(function (result, index) {
chai_1.assert.equal(result.filePath, paths[index]);
});
});
});
it('should parse mulitple files', function () {
var result = parser_1.parse([
testUtils_1.fixturePath('Column'),
testUtils_1.fixturePath('ColumnWithDefaultExportOnly')
]);
testUtils_1.checkComponent(result, {
Column: {},
ColumnWithDefaultExportOnly: {}
}, false);
});
it('should parse simple react class component as default export only', function () {
testUtils_1.check('ColumnWithDefaultExportOnly', {
ColumnWithDefaultExportOnly: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
});
});
it('should parse simple react class component as default anonymous export', function () {
testUtils_1.check('ColumnWithDefaultAnonymousExportOnly', {
ColumnWithDefaultAnonymousExportOnly: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
});
});
it('should parse simple react class component with state', function () {
testUtils_1.check('AppMenu', {
AppMenu: {
menu: { type: 'any' }
}
});
});
it('should parse simple react class component with picked properties', function () {
testUtils_1.check('ColumnWithPick', {
Column: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
propx: { type: 'number' }
}
});
});
it('should parse component with props with external type', function () {
testUtils_1.check('ColumnWithPropsWithExternalType', {
ColumnWithPropsWithExternalType: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
prop3: { type: 'MyExternalType' }
}
});
});
it('should parse HOCs', function () {
testUtils_1.check('ColumnHigherOrderComponent', {
ColumnExternalHigherOrderComponent: {
prop1: { type: 'string' }
},
ColumnHigherOrderComponent1: {
prop1: { type: 'string' }
},
ColumnHigherOrderComponent2: {
prop1: { type: 'string' }
},
RowExternalHigherOrderComponent: {
prop1: { type: 'string' }
},
RowHigherOrderComponent1: {
prop1: { type: 'string' }
},
RowHigherOrderComponent2: {
prop1: { type: 'string' }
}
});
});
it('should parse component with inherited properties HtmlAttributes<any>', function () {
testUtils_1.check('ColumnWithHtmlAttributes', {
Column: {
// tslint:disable:object-literal-sort-keys
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
// HtmlAttributes
defaultChecked: {
type: 'boolean',
required: false,
description: ''
}
// ...
// tslint:enable:object-literal-sort-keys
}
}, false);
});
it('should parse component without exported props interface', function () {
testUtils_1.check('ColumnWithoutExportedProps', {
Column: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' }
}
});
});
it('should parse functional component exported as const', function () {
testUtils_1.check('ConstExport', {
Row: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' }
},
// TODO: this wasn't there before, i would guess that that's correct
test: {}
}, false);
});
it('should parse react component with properties defined in external file', function () {
testUtils_1.check('ExternalPropsComponent', {
ExternalPropsComponent: {
prop1: { type: 'string' }
}
});
});
it('should parse static sub components', function () {
testUtils_1.check('StatelessStaticComponents', {
StatelessStaticComponents: {
myProp: { type: 'string' }
},
'StatelessStaticComponents.Label': {
title: { type: 'string' }
}
});
});
it('should parse static exported components variation1', function () {
testUtils_1.check('StatelessStaticComponentsExportVariation1', {
StatelessStaticComponents: {
myProp: { type: 'string' }
},
'StatelessStaticComponents.Label': {
title: { type: 'string' }
}
});
});
it('should parse static exported components variation2', function () {
testUtils_1.check('StatelessStaticComponentsExportVariation2', {
StatelessStaticComponents: {
myProp: { type: 'string' }
},
'StatelessStaticComponents.Label': {
title: { type: 'string' }
}
});
});
it('should parse static sub components exported from named object', function () {
testUtils_1.check('StatelessStaticComponentsNamedObjectExport', {
StatelessStaticComponents: {
myProp: { type: 'string' }
},
SubComponent: {
title: { type: 'string' }
}
}, true, '');
});
it('should parse static sub components exported from named object with keys', function () {
testUtils_1.check('StatelessStaticComponentsNamedObjectExportAsKeys', {
StatelessStaticComponents: {
myProp: { type: 'string' }
},
SubComponent: {
title: { type: 'string' }
}
});
});
it('should parse static sub components on class components', function () {
testUtils_1.check('ColumnWithStaticComponents', {
Column: {
prop1: { type: 'string' }
},
'Column.Label': {
title: { type: 'string' }
},
'Column.SubLabel': {}
});
});
it('should parse react component with properties extended from an external .tsx file', function () {
testUtils_1.check('ExtendsExternalPropsComponent', {
ExtendsExternalPropsComponent: {
prop1: { type: 'number', required: false, description: 'prop1' },
prop2: { type: 'string', required: false, description: 'prop2' }
}
});
});
it('should parse react component with properties defined as type', function () {
testUtils_1.check('FlippableImage', {
FlippableImage: {
isFlippedX: { type: 'boolean', required: false },
isFlippedY: { type: 'boolean', required: false }
}
}, false);
});
it('should parse react component with const definitions', function () {
testUtils_1.check('InlineConst', {
MyComponent: {
foo: { type: 'any' }
}
});
});
it('should parse default interface export', function () {
testUtils_1.check('ExportsDefaultInterface', {
Component: {
foo: { type: 'any' }
}
});
});
it('should parse react component that exports a prop type const', function () {
testUtils_1.check('ExportsPropTypeShape', {
ExportsPropTypes: {
foo: { type: 'any' }
}
});
});
it('should parse react component that exports a prop type thats imported', function () {
testUtils_1.check('ExportsPropTypeImport', {
ExportsPropTypes: {
foo: { type: 'any' }
}
});
});
// see issue #132 (https://github.com/styleguidist/react-docgen-typescript/issues/132)
it('should determine the parent fileName relative to the project directory', function () {
testUtils_1.check('ExportsPropTypeImport', {
ExportsPropTypes: {
foo: {
parent: {
fileName: 'react-docgen-typescript/src/__tests__/data/ExportsPropTypeImport.tsx',
name: 'ExportsPropTypesProps'
},
type: 'any'
}
}
}, true);
});
describe('component with default props', function () {
var expectation = {
ComponentWithDefaultProps: {
sampleDefaultFromJSDoc: {
defaultValue: 'hello',
description: 'sample with default value',
required: true,
type: '"hello" | "goodbye"'
},
sampleFalse: {
defaultValue: false,
required: false,
type: 'boolean'
},
sampleNull: { type: 'null', required: false, defaultValue: null },
sampleNumber: { type: 'number', required: false, defaultValue: -1 },
sampleObject: {
defaultValue: "{ a: '1', b: 2, c: true, d: false, e: undefined, f: null, g: { a: '1' } }",
required: false,
type: '{ [key: string]: any; }'
},
sampleString: {
defaultValue: 'hello',
required: false,
type: 'string'
},
sampleTrue: { type: 'boolean', required: false, defaultValue: true },
sampleUndefined: {
defaultValue: 'undefined',
required: false,
type: 'any'
}
}
};
it('should parse defined props', function () {
testUtils_1.check('ComponentWithDefaultProps', expectation);
});
it('should parse referenced props', function () {
testUtils_1.check('ComponentWithReferencedDefaultProps', expectation);
});
});
describe('component with @type jsdoc tag', function () {
var expectation = {
ComponentWithTypeJsDocTag: {
sampleTypeFromJSDoc: {
description: 'sample with custom type',
required: true,
type: 'string'
}
}
};
it('should parse defined props', function () {
testUtils_1.check('ComponentWithTypeJsDocTag', expectation);
});
});
it('should parse react PureComponent', function () {
testUtils_1.check('PureRow', {
Row: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' }
}
});
});
it('should parse react PureComponent - regression test', function () {
testUtils_1.check('Regression_v0_0_12', {
Zoomable: {
originX: { type: 'number' },
originY: { type: 'number' },
scaleFactor: { type: 'number' }
}
}, false);
});
it('should parse react functional component', function () {
testUtils_1.check('Row', {
Row: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' }
}
});
});
it('should parse react stateless component', function () {
testUtils_1.check('Stateless', {
Stateless: {
myProp: { type: 'string' }
}
});
});
it('should get name for default export', function () {
testUtils_1.check('ForwardRefDefaultExport', {
ForwardRefDefaultExport: {
myProp: { type: 'string' }
}
}, false);
});
it('should get name for default export 2', function () {
testUtils_1.check('ForwardRefDefaultExportAtExport', {
ForwardRefDefaultExport: {
myProp: { type: 'string' }
}
}, false);
});
it('should component where last line is a comment', function () {
testUtils_1.check('ExportObject', {
Baz: {
baz: { description: '', type: 'string' }
},
Bar: {
foo: { description: '', type: 'string' }
},
FooBar: {
foobar: { description: '', type: 'string' }
}
});
});
it('should parse react stateless component with intersection props', function () {
testUtils_1.check('StatelessIntersectionProps', {
StatelessIntersectionProps: {
moreProp: { type: 'number' },
myProp: { type: 'string' }
}
});
});
it('should parse react stateless component default props when declared as a normal function', function () {
testUtils_1.check('FunctionDeclarationDefaultProps', {
FunctionDeclarationDefaultProps: {
id: {
defaultValue: 1,
description: '',
required: false,
type: 'number'
}
}
});
});
it('should parse react stateless component default props when declared as a normal function inside forwardRef', function () {
testUtils_1.check('ForwardRefDefaultValues', {
ForwardRefDefaultValues: {
myProp: {
defaultValue: "I'm default",
description: 'myProp description',
type: 'string',
required: false
}
}
}, false, 'ForwardRefDefaultValues description');
});
it('should parse react stateless component with external intersection props', function () {
testUtils_1.check('StatelessIntersectionExternalProps', {
StatelessIntersectionExternalProps: {
myProp: { type: 'string' },
prop1: { type: 'string', required: false }
}
});
});
it('should parse react stateless component with generic intersection props', function () {
testUtils_1.check('StatelessIntersectionGenericProps', {
StatelessIntersectionGenericProps: {
myProp: { type: 'string' }
}
});
});
it('should parse react stateless component with intersection + union props', function () {
testUtils_1.check('SimpleUnionIntersection', {
SimpleUnionIntersection: {
bar: { type: 'string', description: '' },
baz: { type: 'string', description: '' },
foo: { type: 'string', description: '' }
}
});
});
it('should parse react stateless component with intersection + union overlap props', function () {
testUtils_1.check('SimpleDiscriminatedUnionIntersection', {
SimpleDiscriminatedUnionIntersection: {
bar: { type: '"one" | "other"', description: '' },
baz: { type: 'number', description: '' },
foo: { type: 'string', description: '' },
test: { type: 'number', description: '' }
}
});
});
it('should parse react stateless component with generic intersection + union overlap props - simple', function () {
testUtils_1.check('SimpleGenericUnionIntersection', {
SimpleGenericUnionIntersection: {
as: { type: 'unknown', description: '' },
foo: {
description: 'The foo prop should not repeat the description',
required: false,
type: '"red" | "blue"'
},
gap: {
description: 'The space between children\nYou cannot use gap when using a "space" justify property',
required: false,
type: 'number'
},
hasWrap: { type: 'boolean', description: '', required: false }
}
});
});
it('should parse react stateless component with generic intersection + union overlap props', function () {
testUtils_1.check('ComplexGenericUnionIntersection', {
ComplexGenericUnionIntersection: {
as: {
type: 'ElementType<any>',
required: false,
description: 'Render the component as another component'
},
align: {
description: 'The flex "align" property',
required: false,
type: '"stretch" | "center" | "flex-start" | "flex-end"'
},
justify: {
description: "Use flex 'center' | 'flex-start' | 'flex-end' | 'stretch' with\na gap between each child.\nUse flex 'space-between' | 'space-around' | 'space-evenly' and\nflex will space the children.",
required: false,
type: '"stretch" | "center" | "flex-start" | "flex-end" | "space-between" | "space-around" | "space-evenly"'
},
gap: {
description: 'The space between children\nYou cannot use gap when using a "space" justify property',
required: false,
type: 'string | number'
}
}
});
});
it('should parse react stateless component with generic intersection + union + omit overlap props', function () {
testUtils_1.check('ComplexGenericUnionIntersectionWithOmit', {
ComplexGenericUnionIntersectionWithOmit: {
as: {
type: 'ElementType<any>',
required: false,
description: 'Render the component as another component'
},
align: {
description: 'The flex "align" property',
required: false,
type: '"center" | "flex-start" | "flex-end" | "stretch"'
},
justify: {
description: "Use flex 'center' | 'flex-start' | 'flex-end' | 'stretch' with\na gap between each child.\nUse flex 'space-between' | 'space-around' | 'space-evenly' and\nflex will space the children.",
required: false,
type: '"center" | "flex-start" | "flex-end" | "stretch" | "space-between" | "space-around" | "space-evenly"'
},
gap: {
description: 'The space between children\nYou cannot use gap when using a "space" justify property',
required: false,
type: 'string | number'
}
}
});
});
it('should parse react stateful component with intersection props', function () {
testUtils_1.check('StatefulIntersectionProps', {
StatefulIntersectionProps: {
moreProp: { type: 'number' },
myProp: { type: 'string' }
}
});
});
it('should parse react stateful component with external intersection props', function () {
testUtils_1.check('StatefulIntersectionExternalProps', {
StatefulIntersectionExternalProps: {
myProp: { type: 'string' },
prop1: { type: 'string', required: false }
}
});
});
it('should parse react stateful component (wrapped in HOC) with intersection props', function () {
testUtils_1.check('HOCIntersectionProps', {
HOCIntersectionProps: {
injected: { type: 'boolean' },
myProp: { type: 'string' }
}
});
});
describe('stateless component with default props', function () {
var expectation = {
StatelessWithDefaultProps: {
sampleDefaultFromJSDoc: {
defaultValue: 'hello',
description: 'sample with default value',
required: true,
type: '"hello" | "goodbye"'
},
sampleEnum: {
defaultValue: 'enumSample.HELLO',
required: false,
type: 'enumSample'
},
sampleFalse: {
defaultValue: false,
required: false,
type: 'boolean'
},
sampleNull: { type: 'null', required: false, defaultValue: null },
sampleNumber: { type: 'number', required: false, defaultValue: -1 },
sampleObject: {
defaultValue: "{ a: '1', b: 2, c: true, d: false, e: undefined, f: null, g: { a: '1' } }",
required: false,
type: '{ [key: string]: any; }'
},
sampleString: {
defaultValue: 'hello',
required: false,
type: 'string'
},
sampleTrue: { type: 'boolean', required: false, defaultValue: true },
sampleUndefined: {
defaultValue: undefined,
required: false,
type: 'any'
},
sampleRenamed: {
defaultValue: 'world',
required: false,
type: 'string'
}
}
};
it('should parse defined props', function () {
testUtils_1.check('StatelessWithDefaultProps', expectation);
});
it('should parse props with shorthands', function () {
testUtils_1.check('StatelessShorthandDefaultProps', {
StatelessShorthandDefaultProps: {
onCallback: {
defaultValue: null,
description: 'onCallback description',
required: false,
type: '() => void'
},
regularProp: {
defaultValue: 'foo',
description: 'regularProp description',
required: false,
type: 'string'
},
shorthandProp: {
defaultValue: 123,
description: 'shorthandProp description',
required: false,
type: 'number'
}
}
});
});
it('supports destructuring', function () {
testUtils_1.check('StatelessWithDestructuredProps', expectation);
});
it('supports destructuring for arrow functions', function () {
testUtils_1.check('StatelessWithDestructuredPropsArrow', expectation);
});
it('supports typescript 3.0 style defaulted props', function () {
testUtils_1.check('StatelessWithDefaultPropsTypescript3', expectation);
});
});
it('should parse components with unioned types', function () {
testUtils_1.check('OnlyDefaultExportUnion', {
OnlyDefaultExportUnion: {
content: { description: 'The content', type: 'string' }
}
});
});
it('should parse components with unioned types when re-exported as named export', function () {
testUtils_1.check('OnlyDefaultExportUnionAsExport', {
OnlyDefaultExportUnion: {
content: { description: 'The content', type: 'string' }
}
}, true, 'OnlyDefaultExportUnion description');
});
it('should parse jsdocs with the @default tag and no description', function () {
testUtils_1.check('StatelessWithDefaultOnlyJsDoc', {
StatelessWithDefaultOnlyJsDoc: {
myProp: { defaultValue: 'hello', description: '', type: 'string' }
}
});
});
it('should parse functional component component defined as function', function () {
testUtils_1.check('FunctionDeclaration', {
Jumbotron: {
prop1: { type: 'string', required: true }
}
});
});
it('should parse functional component component defined as const', function () {
testUtils_1.check('FunctionalComponentAsConst', {
Jumbotron: {
prop1: { type: 'string', required: true }
}
});
});
it('should parse functional component declared as React.FC with default import from react', function () {
testUtils_1.check('FunctionalComponentWithDefaultImportReact', {
Jumbotron: {
prop1: { type: 'string', required: true }
}
});
});
it('should parse functional component defined as const with default value assignments in immediately destructured props', function () {
testUtils_1.check('FunctionalComponentWithDesctructuredProps', {
FunctionalComponentWithDesctructuredProps: {
prop1: {
type: 'Property1Type',
required: false,
defaultValue: 'hello'
},
prop2: {
type: '"goodbye" | "farewell"',
required: false,
defaultValue: 'goodbye'
},
prop3: {
type: 'number',
required: false,
defaultValue: 10
},
prop4: {
type: 'string',
required: false,
defaultValue: 'this is a string'
},
prop5: {
type: 'boolean',
required: false,
defaultValue: true
}
}
});
});
it('should parse functional component defined as const with default value (imported from a separate file) assignments in immediately destructured props', function () {
testUtils_1.check('FunctionalComponentWithDesctructuredPropsAndImportedConstants', {
FunctionalComponentWithDesctructuredPropsAndImportedConstants: {
prop1: {
type: 'Property1Type',
required: false,
defaultValue: 'hello'
},
prop2: {
type: '"goodbye" | "farewell"',
required: false,
defaultValue: 'goodbye'
},
prop3: {
type: 'number',
required: false,
defaultValue: 10
},
prop4: {
type: 'string',
required: false,
defaultValue: 'this is a string'
},
prop5: {
type: 'boolean',
required: false,
defaultValue: true
}
}
});
});
it('should parse functional component component defined as const', function () {
testUtils_1.check('FunctionDeclarationVisibleName', {
'Awesome Jumbotron': {
prop1: { type: 'string', required: true }
}
});
});
it('should parse React.SFC component defined as const', function () {
testUtils_1.check('ReactSFCAsConst', {
Jumbotron: {
prop1: { type: 'string', required: true }
}
});
});
it('should parse functional component component defined as function as default export', function () {
testUtils_1.check('FunctionDeclarationAsDefaultExport', {
Jumbotron: {
prop1: { type: 'string', required: true }
}
});
});
it('should parse functional component component thats been wrapped in React.memo', function () {
testUtils_1.check('FunctionDeclarationAsDefaultExportWithMemo', {
Jumbotron: {
prop1: { type: 'string', required: true }
}
});
});
it('should parse functional component component defined as const thats been wrapped in React.memo', function () {
testUtils_1.check('FunctionDeclarationAsConstAsDefaultExportWithMemo', {
// in this case the component name is taken from the file name
FunctionDeclarationAsConstAsDefaultExportWithMemo: {
prop1: { type: 'string', required: true }
}
}, true, 'Jumbotron description');
});
it('should parse JSDoc correctly', function () {
testUtils_1.check('JSDocWithParam', {
JSDocWithParam: {
prop1: { type: 'string', required: true }
}
}, true, 'JSDocWithParamProps description\n\nNOTE: If a parent element of this control is `overflow: hidden` then the\nballoon may not show up.');
});
it('should parse functional component component defined as const as default export', function () {
testUtils_1.check('FunctionalComponentAsConstAsDefaultExport', {
// in this case the component name is taken from the file name
FunctionalComponentAsConstAsDefaultExport: {
prop1: { type: 'string', required: true }
}
}, true, 'Jumbotron description');
});
it('should parse React.SFC component defined as const as default export', function () {
testUtils_1.check('ReactSFCAsConstAsDefaultExport', {
// in this case the component name is taken from the file name
ReactSFCAsConstAsDefaultExport: {
prop1: { type: 'string', required: true }
}
}, true, 'Jumbotron description');
});
it('should parse functional component component defined as const as named export', function () {
testUtils_1.check('FunctionalComponentAsConstAsNamedExport', {
// in this case the component name is taken from the file name
Jumbotron: {
prop1: { type: 'string', required: true }
}
}, true, 'Jumbotron description');
});
it('should parse React.SFC component defined as const as named export', function () {
testUtils_1.check('ReactSFCAsConstAsNamedExport', {
// in this case the component name is taken from the file name
Jumbotron: {
prop1: { type: 'string', required: true }
}
}, true, 'Jumbotron description');
});
describe('displayName', function () {
it('should be taken from stateless component `displayName` property (using named export)', function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatelessDisplayName'))[0];
chai_1.assert.equal(parsed.displayName, 'StatelessDisplayName');
});
it('should be taken from stateful component `displayName` property (using named export)', function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatefulDisplayName'))[0];
chai_1.assert.equal(parsed.displayName, 'StatefulDisplayName');
});
it('should be taken from stateless component `displayName` property (using default export)', function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatelessDisplayNameDefaultExport'))[0];
chai_1.assert.equal(parsed.displayName, 'StatelessDisplayNameDefaultExport');
});
it("should be taken from stateless component `displayName` property (using default export) even if file name doesn't match", function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatelessDisplayNameDefaultExportDifferentFilename'))[0];
chai_1.assert.equal(parsed.displayName, 'ThisNameIsNotTheSameAsThisFilename');
});
it('should be taken from stateful component `displayName` property (using default export)', function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatefulDisplayNameDefaultExport'))[0];
chai_1.assert.equal(parsed.displayName, 'StatefulDisplayNameDefaultExport');
});
it('should be taken from named export when default export is an HOC', function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatelessDisplayNameHOC'))[0];
chai_1.assert.equal(parsed.displayName, 'StatelessDisplayName');
});
it('should be taken from named export when default export is an HOC', function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatefulDisplayNameHOC'))[0];
chai_1.assert.equal(parsed.displayName, 'StatefulDisplayName');
});
it('should be taken from stateless component folder name if file name is "index"', function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatelessDisplayNameFolder/index'))[0];
chai_1.assert.equal(parsed.displayName, 'StatelessDisplayNameFolder');
});
it('should be taken from stateful component folder name if file name is "index"', function () {
var parsed = parser_1.parse(testUtils_1.fixturePath('StatefulDisplayNameFolder/index'))[0];
chai_1.assert.equal(parsed.displayName, 'StatefulDisplayNameFolder');
});
describe('multiple components in one', function () {
it('should parse all `displayName` properties correctly when all are explicitly defined.', function () {
var result = parser_1.parse(testUtils_1.fixturePath('MultipleAllWithDisplayName'));
// Ensure we're not missing any exports.
chai_1.assert.equal(result.length, 3);
var parsed1 = result[0], parsed2 = result[1], parsed3 = result[2];
chai_1.assert.equal(parsed1.displayName, 'First');
chai_1.assert.equal(parsed2.displayName, 'Second');
chai_1.assert.equal(parsed3.displayName, 'Third');
});
it('should parse all `displayName` properties correctly when some are explicitly defined.', function () {
var result = parser_1.parse(testUtils_1.fixturePath('MultipleSomeWithDisplayName'));
// Ensure we're not missing any exports.
chai_1.assert.equal(result.length, 4);
var parsed1 = result[0], parsed2 = result[1], parsed3 = result[2], parsed4 = result[3];
chai_1.assert.equal(parsed1.displayName, 'First');
chai_1.assert.equal(parsed2.displayName, 'NoExplicitDisplayName1');
chai_1.assert.equal(parsed3.displayName, 'Second');
chai_1.assert.equal(parsed4.displayName, 'NoExplicitDisplayName2');
});
it('should parse all `displayName` properties correctly when none are explicitly defined.', function () {
var result = parser_1.parse(testUtils_1.fixturePath('MultipleWithNoExplicitDisplayName'));
// Ensure we're not missing any exports.
chai_1.assert.equal(result.length, 3);
var parsed1 = result[0], parsed2 = result[1], parsed3 = result[2];
chai_1.assert.equal(parsed1.displayName, 'Button');
chai_1.assert.equal(parsed2.displayName, 'SubmitButton');
chai_1.assert.equal(parsed3.displayName, 'ResetButton');
});
});
});
describe('Parser options', function () {
describe('Property filtering', function () {
describe('children', function () {
it('should ignore property "children" if not explicitly documented', function () {
testUtils_1.check('Column', {
Column: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
}, true);
});
it('should not ignore any property that is documented explicitly', function () {
testUtils_1.check('ColumnWithAnnotatedChildren', {
Column: {
children: {
description: 'children description',
required: false,
type: 'ReactNode'
},
prop1: { type: 'string', required: false },
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
}, true);
});
});
describe('propsFilter method', function () {
it('should apply filter function and filter components accordingly', function () {
var propFilter = function (prop, component) {
return prop.name !== 'prop1';
};
testUtils_1.check('Column', {
Column: {
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
}, true, undefined, { propFilter: propFilter });
});
it('should apply filter function and filter components accordingly', function () {
var propFilter = function (prop, component) {
if (component.name === 'Column') {
return prop.name !== 'prop1';
}
return true;
};
testUtils_1.check('Column', {
Column: {
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
}, true, undefined, { propFilter: propFilter });
testUtils_1.check('AppMenu', {
AppMenu: {
menu: { type: 'any' }
}
}, true, undefined, { propFilter: propFilter });
});
it('should allow filtering by parent interface', function () {
var propFilter = function (prop, component) {
if (prop.parent == null) {
return true;
}
return (prop.parent.fileName.indexOf('@types/react') < 0 &&
prop.parent.name !== 'HTMLAttributes');
};
testUtils_1.check('ColumnWithHtmlAttributes', {
Column: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' }
}
}, true, undefined, { propFilter: propFilter });
});
});
it('should collect all `onClick prop` parent declarations', function (done) {
chai_1.assert.doesNotThrow(function () {
parser_1.withDefaultConfig({
propFilter: function (prop) {
if (prop.name === 'onClick') {
chai_1.assert.deepEqual(prop.declarations, [
{
fileName: 'react-docgen-typescript/node_modules/@types/react/index.d.ts',
name: 'DOMAttributes'
},
{
fileName: 'react-docgen-typescript/src/__tests__/data/ButtonWithOnClickComponent.tsx',
name: 'TypeLiteral'
}
]);
done();
}
return true;
}
}).parse(testUtils_1.fixturePath('ButtonWithOnClickComponent'));
});
});
it('should allow filtering by parent declarations', function () {
var propFilter = function (prop) {
if (prop.declarations !== undefined && prop.declarations.length > 0) {
var hasPropAdditionalDescription = prop.declarations.find(function (declaration) {
return !declaration.fileName.includes('@types/react');
});
return Boolean(hasPropAdditionalDescription);
}
return true;
};
testUtils_1.check('ButtonWithOnClickComponent', {
ButtonWithOnClickComponent: {
onClick: {
type: 'MouseEventHandler<HTMLButtonElement>',
required: false,
description: 'onClick event handler'
}
}
}, true, '', {
propFilter: propFilter
});
});
describe('skipPropsWithName', function () {
it('should skip a single property in skipPropsWithName', function () {
var propFilter = { skipPropsWithName: 'prop1' };
testUtils_1.check('Column', {
Column: {
prop2: { type: 'number' },
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
}, true, undefined, { propFilter: propFilter });
});
it('should skip multiple properties in skipPropsWithName', function () {
var propFilter = { skipPropsWithName: ['prop1', 'prop2'] };
testUtils_1.check('Column', {
Column: {
prop3: { type: '() => void' },
prop4: { type: '"option1" | "option2" | "option3"' }
}
}, true, undefined, { propFilter: propFilter });
});
});
describe('skipPropsWithoutDoc', function () {
it('should skip a properties without documentation', function () {
var propFilter = { skipPropsWithoutDoc: false };
testUtils_1.check('ColumnWithUndocumentedProps', {
Column: {
prop1: { type: 'string', required: false },
prop2: { type: 'number' }
}
}, true, undefined, { propFilter: propFilter });
});
});
});
it('should defaultProps in variable', function () {
testUtils_1.check('SeparateDefaultProps', {
SeparateDefaultProps: {
disabled: {
description: '',
required: false,
defaultValue: false,
type: 'boolean'
},
id: {
description: '',
required: false,
defaultValue: 123,
type: 'number'
}
}
});
});
it('should defaultProps accessed variable', function () {
testUtils_1.check('SeparateDefaultPropsIndividual', {
SeparateDefaultPropsIndividual: {
disabled: {
description: '',
required: false,
defaultValue: false,
type: 'boolean'
},
id: {
description: '',
required: false,
defaultValue: 123,
type: 'number'
}
}
});
});
describe('Extracting literal values from enums', function () {
it('extracts literal values from enum', function () {
testUtils_1.check('ExtractLiteralValuesFromEnum', {
ExtractLiteralValuesFromEnum: {
sampleBoolean: { type: 'boolean' },
sampleEnum: {
raw: 'sampleEnum',
type: 'enum',
value: [
{
value: '"one"',
description: '',
fullComment: '',
tags: {}
},
{
value: '"two"',
description: '',
fullComment: '',
tags: {}
},
{
value: '"three"',
description: '',
fullComment: '',
tags: {}
}
]
},
sampleString: { type: 'string' }
}
}, true, null, {
shouldExtractLiteralValuesFromEnum: