UNPKG

react-docgen-typescript

Version:

[![Build Status](https://github.com/styleguidist/react-docgen-typescript/actions/workflows/nodejs.yml/badge.svg)](https://github.com/styleguidist/react-docgen-typescript/actions/workflows/nodejs.yml)

1,450 lines 65.1 kB
"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 () {
        (0, 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 () {
        (0, 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 () {
        (0, testUtils_1.check)('ColumnWithDefaultExport', {
            Column: {
                prop1: { type: 'string', required: false },
                prop2: { type: 'number' },
                prop3: { type: '() => void' },
                prop4: { type: '"option1" | "option2" | "option3"' }
            }
        });
    });
    describe('file path', function () {
        it('should return the correct filepath for a parsed component', function () {
            var results = (0, parser_1.parse)([(0, 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 = (0, parser_1.parse)([
                (0, testUtils_1.fixturePath)('FilePathCheck'),
                (0, testUtils_1.fixturePath)('Column'),
                (0, 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 = (0, parser_1.parse)([
            (0, testUtils_1.fixturePath)('Column'),
            (0, testUtils_1.fixturePath)('ColumnWithDefaultExportOnly')
        ]);
        (0, testUtils_1.checkComponent)(result, {
            Column: {},
            ColumnWithDefaultExportOnly: {}
        }, false);
    });
    it('should parse simple react class component as default export only', function () {
        (0, 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 () {
        (0, 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 () {
        (0, testUtils_1.check)('AppMenu', {
            AppMenu: {
                menu: { type: 'any' }
            }
        });
    });
    it('should parse simple react class component with picked properties', function () {
        (0, 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 () {
        (0, testUtils_1.check)('ColumnWithPropsWithExternalType', {
            ColumnWithPropsWithExternalType: {
                prop1: { type: 'string', required: false },
                prop2: { type: 'number' },
                prop3: { type: 'MyExternalType' }
            }
        });
    });
    it('should parse HOCs', function () {
        (0, 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 () {
        (0, 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 () {
        (0, testUtils_1.check)('ColumnWithoutExportedProps', {
            Column: {
                prop1: { type: 'string', required: false },
                prop2: { type: 'number' }
            }
        });
    });
    it('should parse functional component exported as const', function () {
        (0, 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 () {
        (0, testUtils_1.check)('ExternalPropsComponent', {
            ExternalPropsComponent: {
                prop1: { type: 'string' }
            }
        });
    });
    it('should parse static sub components', function () {
        (0, testUtils_1.check)('StatelessStaticComponents', {
            StatelessStaticComponents: {
                myProp: { type: 'string' }
            },
            'StatelessStaticComponents.Label': {
                title: { type: 'string' }
            }
        });
    });
    it('should parse static sub components on class components', function () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 () {
        (0, testUtils_1.check)('InlineConst', {
            MyComponent: {
                foo: { type: 'any' }
            }
        });
    });
    it('should parse default interface export', function () {
        (0, testUtils_1.check)('ExportsDefaultInterface', {
            Component: {
                foo: { type: 'any' }
            }
        });
    });
    it('should parse react component that exports a prop type const', function () {
        (0, testUtils_1.check)('ExportsPropTypeShape', {
            ExportsPropTypes: {
                foo: { type: 'any' }
            }
        });
    });
    it('should parse react component that exports a prop type thats imported', function () {
        (0, 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 () {
        (0, 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 () {
            (0, testUtils_1.check)('ComponentWithDefaultProps', expectation);
        });
        it('should parse referenced props', function () {
            (0, 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 () {
            (0, testUtils_1.check)('ComponentWithTypeJsDocTag', expectation);
        });
    });
    it('should parse react PureComponent', function () {
        (0, testUtils_1.check)('PureRow', {
            Row: {
                prop1: { type: 'string', required: false },
                prop2: { type: 'number' }
            }
        });
    });
    it('should parse react PureComponent - regression test', function () {
        (0, 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 () {
        (0, testUtils_1.check)('Row', {
            Row: {
                prop1: { type: 'string', required: false },
                prop2: { type: 'number' }
            }
        });
    });
    it('should parse react stateless component', function () {
        (0, testUtils_1.check)('Stateless', {
            Stateless: {
                myProp: { type: 'string' }
            }
        });
    });
    it('should get name for default export', function () {
        (0, testUtils_1.check)('ForwardRefDefaultExport', {
            ForwardRefDefaultExport: {
                myProp: { type: 'string' }
            }
        }, false);
    });
    it('should get name for default export 2', function () {
        (0, testUtils_1.check)('ForwardRefDefaultExportAtExport', {
            ForwardRefDefaultExport: {
                myProp: { type: 'string' }
            }
        }, false);
    });
    it('should component where last line is a comment', function () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 () {
        (0, testUtils_1.check)('StatelessIntersectionExternalProps', {
            StatelessIntersectionExternalProps: {
                myProp: { type: 'string' },
                prop1: { type: 'string', required: false }
            }
        });
    });
    it('should parse react stateless component with generic intersection props', function () {
        (0, testUtils_1.check)('StatelessIntersectionGenericProps', {
            StatelessIntersectionGenericProps: {
                myProp: { type: 'string' }
            }
        });
    });
    it('should parse react stateless component with intersection + union props', function () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 () {
        (0, testUtils_1.check)('StatefulIntersectionProps', {
            StatefulIntersectionProps: {
                moreProp: { type: 'number' },
                myProp: { type: 'string' }
            }
        });
    });
    it('should parse react stateful component with external intersection props', function () {
        (0, 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 () {
        (0, 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'
                }
            }
        };
        it('should parse defined props', function () {
            (0, testUtils_1.check)('StatelessWithDefaultProps', expectation);
        });
        it('should parse props with shorthands', function () {
            (0, 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 () {
            (0, testUtils_1.check)('StatelessWithDestructuredProps', expectation);
        });
        it('supports destructuring for arrow functions', function () {
            (0, testUtils_1.check)('StatelessWithDestructuredPropsArrow', expectation);
        });
        it('supports typescript 3.0 style defaulted props', function () {
            (0, testUtils_1.check)('StatelessWithDefaultPropsTypescript3', expectation);
        });
    });
    it('should parse components with unioned types', function () {
        (0, 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 () {
        (0, 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 () {
        (0, testUtils_1.check)('StatelessWithDefaultOnlyJsDoc', {
            StatelessWithDefaultOnlyJsDoc: {
                myProp: { defaultValue: 'hello', description: '', type: 'string' }
            }
        });
    });
    it('should parse functional component component defined as function', function () {
        (0, testUtils_1.check)('FunctionDeclaration', {
            Jumbotron: {
                prop1: { type: 'string', required: true }
            }
        });
    });
    it('should parse functional component component defined as const', function () {
        (0, testUtils_1.check)('FunctionalComponentAsConst', {
            Jumbotron: {
                prop1: { type: 'string', required: true }
            }
        });
    });
    it('should parse functional component defined as const with default value assignments in immediately destructured props', function () {
        (0, 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 () {
        (0, 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 () {
        (0, testUtils_1.check)('FunctionDeclarationVisibleName', {
            'Awesome Jumbotron': {
                prop1: { type: 'string', required: true }
            }
        });
    });
    it('should parse React.SFC component defined as const', function () {
        (0, testUtils_1.check)('ReactSFCAsConst', {
            Jumbotron: {
                prop1: { type: 'string', required: true }
            }
        });
    });
    it('should parse functional component component defined as function as default export', function () {
        (0, testUtils_1.check)('FunctionDeclarationAsDefaultExport', {
            Jumbotron: {
                prop1: { type: 'string', required: true }
            }
        });
    });
    it('should parse functional component component thats been wrapped in React.memo', function () {
        (0, testUtils_1.check)('FunctionDeclarationAsDefaultExportWithMemo', {
            Jumbotron: {
                prop1: { type: 'string', required: true }
            }
        });
    });
    it('should parse JSDoc correctly', function () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 () {
        (0, 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 = (0, parser_1.parse)((0, 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 = (0, parser_1.parse)((0, 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 = (0, parser_1.parse)((0, 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 = (0, parser_1.parse)((0, 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 = (0, parser_1.parse)((0, 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 = (0, parser_1.parse)((0, 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 = (0, parser_1.parse)((0, 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 = (0, parser_1.parse)((0, 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 = (0, parser_1.parse)((0, testUtils_1.fixturePath)('StatefulDisplayNameFolder/index'))[0];
            chai_1.assert.equal(parsed.displayName, 'StatefulDisplayNameFolder');
        });
    });
    describe('Parser options', function () {
        describe('Property filtering', function () {
            describe('children', function () {
                it('should ignore property "children" if not explicitly documented', function () {
                    (0, 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 () {
                    (0, 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';
                    };
                    (0, 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;
                    };
                    (0, testUtils_1.check)('Column', {
                        Column: {
                            prop2: { type: 'number' },
                            prop3: { type: '() => void' },
                            prop4: { type: '"option1" | "option2" | "option3"' }
                        }
                    }, true, undefined, { propFilter: propFilter });
                    (0, 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');
                    };
                    (0, 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 () {
                    (0, 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((0, 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;
                };
                (0, 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' };
                    (0, 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'] };
                    (0, 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 };
                    (0, testUtils_1.check)('ColumnWithUndocumentedProps', {
                        Column: {
                            prop1: { type: 'string', required: false },
                            prop2: { type: 'number' }
                        }
                    }, true, undefined, { propFilter: propFilter });
                });
            });
        });
        it('should defaultProps in variable', function () {
            (0, 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 () {
            (0, 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 () {
                (0, 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: true
                });
            });
            it('Should infer types from constraint type (generic with extends)', function () {
                (0, testUtils_1.check)('GenericWithExtends', {
                    GenericWithExtends: {
                        sampleUnionProp: {
                            raw: 'SampleUnion',
                            type: 'enum',
                            value: [
                                {
                                    value: '"value 1"'
                                },
                                {
                                    value: '"value 2"'
                                },
                                {
                                    value: '"value 3"'
                                },
                                {
                                    value: '"value 4"'
                                },
                                {
                                    value: '"value n"'
                                }
                            ]
                        },
                        sampleEnumProp: {
                            raw: 'SampleEnum',
                            type: 'enum',
                            value: [
                                { value: '0', description: '', fullComment: '', tags: {} },
                                { value: '1', description: '', fullComment: '', tags: {} },
                                { value: '"c"', description: '', fullComment: '', tags: {} }
                            ]
                        },
                        sampleUnionNonGeneric: {
                            type: 'SampleUnionNonGeneric'
                        },
                        sampleObjectProp: {
                            type: 'SampleObject'
                        },
                        sampleNumberProp: {
                            type: 'number'
                        },
                        sampleGenericArray: {
                            type: 'number[]'
                        },
                        sampleGenericObject: {
                            type: '{ prop1: number; }'
                        },
                        sampleInlineObject: {
                            type: '{ propA: string; }'
                        }
                    }
                }, true, null, { shouldExtractLiteralValuesFromEnum: true });
            });
        });
        describe('Extracting values from unions', function () {
            it('extracts all values from union', function () {
                (0, testUtils_1.check)('ExtractLiteralValuesFromUnion', {
                    ExtractLiteralValuesFromUnion: {
                        sampleComplexUnion: {
                            raw: 'number | "string1" | "string2"',
                            type: 'enum',
                            value: [
                                { value: 'number' },
                                { value: '"string1"' },
                                { value: '"string2"' }
                            ]
                        }
                    }
                }, false, null, {
                    shouldExtractValuesFromUnion: true
                });
            });
            it('extracts numbers from a union', function () {
                (0, testUtils_1.check)('ExtractLiteralValuesFromUnion', {
                    ExtractLiteralValuesFromUnion: {
                        sampleNumberUnion: {
                            raw: '1 | 2 | 3',
                            type: 'enum',
                            value: [{ value: '1' }, { value: '2' }, { value: '3' }]
                        }
                    }
                }, false, null, {
                    shouldExtractValuesFromUnion: true
                });
            });
            it('extracts numbers and strings from a mixed union', function () {
                (0, testUtils_1.check)('ExtractLiteralValuesFromUnion', {
                    ExtractLiteralValuesFromUnion: {
                        sampleMixedUnion: {
                            raw: '"string1" | "string2" | 1 | 2',
                            type: 'enum',
                            value: [
                                { value: '"string1"' },
                                { value: '"string2"' },
                                { value: '1' },
                                { value: '2' }
                            ]
                        }
                    }
                }, false, null, {
                    shouldExtractValuesFromUnion: true
                });
            });
        });
        describe('Returning not string default props ', function () {
            it('returns not string defaultProps', function () {
                (0, testUtils_1.check)('StatelessWithDefaultPropsAsString', {
                    StatelessWithDefaultPropsAsString: {
                        sampleFalse: {
                            defaultValue: 'false',
                            required: false,
                            type: 'boolean'
                        },
                        sampleNull: {
                            defaultValue: 'null',
                            required: false,
                            type: 'null'
                        },
                        sampleNumber: {
                            defaultValue: '1',
                            required: false,
                            type: 'number'
                        },
                        sampleNumberWithPrefix: {
                            defaultValue: '-1',
                            required: false,
                            type: 'number'
                        },
                        sampleTrue: {
                            defaultValue: 'true',
                            required: false,
                            type: 'boolean'
                        },
                        sampleUndefined: {
                            defaultValue: 'undefined',
                            required: false,
                            type: 'undefined'
                        }
                    }
                }, true, null, {
                    savePropValueAsString: true
                });
            });
        });
        describe("Extract prop's JSDoc/TSDoc tags", function () {
            it('should extract all prop JSDoc/TSDoc tags', function () {
                (0, testUtils_1.check)('ExtractPropTags', {
                    ExtractPropTags: {
                        prop1: {
                            type: 'Pick<Todo, "title" | "completed">',
                            required: false,
                            tags: {
                                ignore: 'ignoreMe',
                                kind: 'category 2',
                                custom123: 'something'
                            }
                        },
                        prop2: {
                            type: 'string',
                            tags: { internal: 'some internal prop', kind: 'category 1' }
                        }
                    }
                }, true, null, { shouldIncludePropTagMap: true });
            });
        });
        describe('shouldIncludeExpression', function () {
            it('should be disabled by default', function () {
                var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('StatelessDisplayName'))[0];
                chai_1.assert.equal(parsed.expression, undefined);
            });
            it('should cause the parser to return the component expression when set to true', function () {
                var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('StatelessDisplayName'), {
                    shouldIncludeExpression: true
                })[0];
                chai_1.assert.equal(parsed.expression.name, 'Stateless');
            });
        });
    });
    describe('withCustomConfig', function () {
        it('should accept tsconfigs that typescript accepts', function () {
            chai_1.assert.ok((0, parser_1.withCustomConfig)(
            // need to navigate to root because tests run on compiled tests
            // and tsc does not include json files
            path.join(__dirname, '../../src/__tests__/data/tsconfig.json'), {}));
        });
    });
    describe('typescript strict mode', function () {
        // typescript strict mode adds an extra `undefined` to enums
        // may have other funky behavior
        describe('remove undefined from optional', function () {
            var options = {
                shouldExtractLiteralValuesFromEnum: true,
                shouldRemoveUndefinedFromOptional: true,
                savePropValueAsString: true
            };
            var parser = (0, parser_1.withCustomConfig)(
            // tsconfig with strict: true
            path.join(__dirname, '../../src/__tests__/data/tsconfig.json'), options);
            it('removes undefined from enums', function () {
                var result = parser.parse((0, testUtils_1.fixturePath)('RemoveOptionalValuesFromEnum'));
                var expected = {
                    RemoveOptionalValuesFromEnum: {
                        sampleBoolean: { type: 'boolean', required: false },
                        sampleEnum: {
                            raw: 'sampleEnum',
                            required: false,
                            type: 'enum',
                            value: [
                                {
                                    value: '"one"',
                                    description: 'test comment',
                                    fullComment: 'test comment',
                                    tags: {}
                                },
                                { value: '"two"', description: '', fullComment: '', tags: {} },
                                { value: '"three"', description: '', fullComment: '', tags: {} }
                            ]
                        },
                        sampleString: { type: 'string', required: false }
                    }
                };
                (0, testUtils_1.checkComponent)(result, expected, false);
            });
            it('removes undefined from unions', function () {
                var result = parser.parse((0, testUtils_1.fixturePath)('RemoveOptionalValuesFromUnion'));
                var expected = {
                    RemoveOptionalValuesFromUnion: {
                        sampleStringUnion: {
                            required: false,
                            raw: '"string1" | "string2"',
                            type: 'enum',
                            value: [{ value: '"string1"' }, { value: '"string2"' }]
                        },
                        sampleNumberUnion: {
                            required: false,
                            raw: '1 | 2 | 3',
                            type: 'enum',
                            value: [{ value: '1' }, { value: '2' }, { value: '3' }]
                        },
                        sampleMixedUnion: {
                            required: false,
                            raw: '"string1" | "string2" | 1 | 2',
                            type: 'enum',
                            value: [
                                { value: '"string1"' },
                                { value: '"string2"' },
                                { value: '1' },
                                { value: '2' }
                            ]
                        }
                    }
                };
                (0, testUtils_1.check)('RemoveOptionalValuesFromUnion', expected, false, null, options);
            });
        });
    });
    describe('parseWithProgramProvider', function () {
        it('should accept existing ts.Program instance', function () {
            var programProviderInvoked = false;
            // mimic a third party library providing a ts.Program instance.
            var programProvider = function () {
                // need to navigate to root because tests run on compiled tests
                // and tsc does not include json files
                var tsconfigPath = path.join(__dirname, '../../src/__tests__/data/tsconfig.json');
                var basePath = path.dirname(tsconfigPath);
                var _a = ts.readConfigFile(tsconfigPath, function (filename) {
                    return fs.readFileSync(filename, 'utf8');
                }), config = _a.config, error = _a.error;
                chai_1.assert.isUndefined(error);
                var _b = ts.parseJsonConfigFileContent(config, ts.sys, basePath, {}, tsconfigPath), options = _b.options, errors = _b.errors;
                chai_1.assert.lengthOf(errors, 0);
                programProviderInvoked = true;
                return ts.createProgram([(0, testUtils_1.fixturePath)('Column')], options);
            };
            var result = (0, parser_1.withDefaultConfig)().parseWithProgramProvider([(0, testUtils_1.fixturePath)('Column')], programProvider);
            (0, testUtils_1.checkComponent)(result, {
                Column: {}
            }, false);
            chai_1.assert.isTrue(programProviderInvoked);
        });
    });
    describe('componentNameResolver', function () {
        it('should override default behavior', function () {
            var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('StatelessDisplayNameStyledComponent'), {
                componentNameResolver: function (exp, source) {
                    return exp.getName() === 'StyledComponentClass' &&
                        (0, parser_1.getDefaultExportForFile)(source);
                }
            })[0];
            chai_1.assert.equal(parsed.displayName, 'StatelessDisplayNameStyledComponent');
        });
        it('should fallback to default behavior without a match', function () {
            var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('StatelessDisplayNameStyledComponent'), {
                componentNameResolver: function () { return false; }
            })[0];
            chai_1.assert.equal(parsed.displayName, 'StatelessDisplayNameStyledComponent');
        });
    });
    describe('methods', function () {
        it('should properly parse methods', function () {
            var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('ColumnWithMethods'))[0];
            var methods = parsed.methods;
            var myCoolMethod = methods[0];
            chai_1.assert.equal(myCoolMethod.description, 'My super cool method');
            chai_1.assert.equal(myCoolMethod.docblock, 'My super cool method\n@param myParam Documentation for parameter 1\n@public\n@returns The answer to the universe' // tslint:disable-line max-line-length
            );
            chai_1.assert.deepEqual(myCoolMethod.modifiers, []);
            chai_1.assert.equal(myCoolMethod.name, 'myCoolMethod');
            chai_1.assert.deepEqual(myCoolMethod.params, [
                {
                    description: 'Documentation for parameter 1',
                    name: 'myParam',
                    type: { name: 'number' }
                },
                {
                    description: null,
                    name: 'mySecondParam?',
                    type: { name: 'string' }
                }
            ]);
            chai_1.assert.deepEqual(myCoolMethod.returns, {
                description: 'The answer to the universe',
                type: 'number'
            });
        });
        it('should properly parse static methods', function () {
            var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('ColumnWithStaticMethods'))[0];
            var methods = parsed.methods;
            chai_1.assert.equal(methods[0].name, 'myStaticMethod');
            chai_1.assert.deepEqual(methods[0].modifiers, ['static']);
        });
        it('should handle method with no information', function () {
            var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('ColumnWithMethods'))[0];
            var methods = parsed.methods;
            chai_1.assert.equal(methods[1].name, 'myBasicMethod');
        });
        it('should handle arrow function', function () {
            var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('ColumnWithMethods'))[0];
            var methods = parsed.methods;
            chai_1.assert.equal(methods[2].name, 'myArrowFunction');
        });
        it('should not parse functions not marked with @public', function () {
            var parsed = (0, parser_1.parse)((0, testUtils_1.fixturePath)('ColumnWithMethods'))[0];
            var methods = parsed.methods;
            chai_1.assert.equal(Boolean(methods.find(function (method) { return method.name === 'myPrivateFunction'; })), false);
        });
    });
    describe('getDefaultExportForFile', function () {
        it('should filter out forbidden symbols', function () {
            var result = (0, parser_1.getDefaultExportForFile)({
                fileName: 'a-b'
            });
            chai_1.assert.equal(result, 'ab');
        });
        it('should remove leading non-letters', function () {
            var result = (0, parser_1.getDefaultExportForFile)({
                fileName: '---123aba'
            });
            chai_1.assert.equal(result, 'aba');
        });
        it('should preserve numbers in the middle', function () {
            var result = (0, parser_1.getDefaultExportForFile)({
                fileName: '1Body2Text3'
            });
            chai_1.assert.equal(result, 'Body2Text3');
        });
        it('should not return empty string', function () {
            var result = (0, parser_1.getDefaultExportForFile)({
                fileName: '---123'
            });
            chai_1.assert.equal(result.length > 0, true);
        });
    });
    describe('issues tests', function () {
        it('188', function () {
            (0, testUtils_1.check)('Issue188', {
                Header: {
                    content: { type: 'string', required: true, description: '' }
                }
            }, true, '');
        });
        it('should return prop types for custom component type', function () {
            (0, testUtils_1.check)('Issue320', {
                Issue320: {
                    color: {
                        type: 'string',
                        required: false,
                        description: ''
                    },
                    text: { type: 'string', required: true, description: '' }
                }
            }, true, null, {
                customComponentTypes: ['OverridableComponent'],
                savePropValueAsString: true
            });
        });
        it('should parse imported default props for class component', function () {
            (0, testUtils_1.check)('ComponentWithImportedDefaultProps', {
                ComponentWithImportedDefaultProps: {
                    name: {
                        type: 'string',
                        defaultValue: 'Node',
                        required: false,
                        description: ''
                    }
                }
            }, false, '');
        });
    });
});
//# sourceMappingURL=parser.js.map