@knodes/typedoc-pluginutils
Version:
A set of utilities for TypeDoc plugins
159 lines (148 loc) • 7.52 kB
text/typescript
/* eslint-disable @typescript-eslint/ban-ts-comment, @typescript-eslint/no-var-requires */
import assert from 'assert';
import { writeFileSync } from 'fs';
import { expectTypeOf } from 'expect-type';
import { fileSync } from 'tmp';
import { Application, LogLevel, ParameterType } from 'typedoc';
jest.mock( '../base-plugin' );
const { ABasePlugin } = require( '../base-plugin' ) as jest.Mocked<typeof import( '../base-plugin' )>;
import { OptionGroup } from './option-group';
import { DeclarationOptionConfig, TypeErr } from './utils';
import type { PluginLogger } from '../plugin-logger';
describe( 'Typings', () => {
const maybe = true as boolean;
const any = {} as any;
it( 'should have correct builder types', () => {
if( maybe ) return;
interface ITest {
foo: string;
bar?: any;
}
OptionGroup.factory<ITest>( any );
// Simple param
expectTypeOf( OptionGroup.factory<{foo: number}>( any )
.add ).parameters.toEqualTypeOf<['foo', DeclarationOptionConfig, ( v: unknown ) => number]>();
const grp1 = OptionGroup.factory<{foo: number}>( any )
.add( 'foo', { type: ParameterType.Number, help: 'Test' } )
.build();
expectTypeOf( grp1 )
.toEqualTypeOf<OptionGroup<{foo: number}, {foo: {name: 'foo'; type: ParameterType.Number; help: string}}>>();
expectTypeOf( grp1.getValue() ).toEqualTypeOf<{foo: number}>();
expectTypeOf( grp1.setValue ).parameters.toEqualTypeOf<[{foo?: number} | string] | ['foo', number]>();
expectTypeOf( OptionGroup.factory<{foo: number}>( any )
.build )
.toEqualTypeOf<TypeErr<['Missing declarations for keys', 'foo']>>();
// Multi params
const grp2 = OptionGroup.factory<{foo: number; bar?: string}>( any )
.add( 'foo', { type: ParameterType.Number, help: 'Test' } )
.add( 'bar', { type: ParameterType.String, help: 'Test' } )
.build();
expectTypeOf( grp2 )
.toEqualTypeOf<OptionGroup<{foo: number; bar?: string}, {
foo: {name: 'foo'; type: ParameterType.Number; help: string};
bar: {name: 'bar'; type: ParameterType.String; help: string};
}>>();
expectTypeOf( grp2.getValue() ).toEqualTypeOf<{foo: number; bar?: string}>();
expectTypeOf( grp2.setValue ).parameters.toEqualTypeOf<[{foo?: number; bar?: string} | string] | ['foo' | 'bar', string | number]>(); // Should include `undefined` though
expectTypeOf( OptionGroup.factory<{foo: number; bar?: string}>( any )
.build )
.toEqualTypeOf<TypeErr<['Missing declarations for keys', 'foo' | 'bar']>>();
expectTypeOf( OptionGroup.factory<{foo: number; bar?: string}>( any )
.add( 'foo', { type: ParameterType.Number, help: 'Test' } )
.build )
.toEqualTypeOf<TypeErr<['Missing declarations for keys', 'bar']>>();
OptionGroup.factory<{foo: number; bar?: string}>( any )
// @ts-expect-error
.add( 'bar', { type: ParameterType.Number, help: 'Test' } );
OptionGroup.factory<{foo: number; bar?: string}>( any )
.add( 'bar', { type: ParameterType.Number, help: 'Test' }, ( v: number ) => `${v}` as string | undefined );
OptionGroup.factory<{foo: number; bar?: string}>( any )
.add( 'bar', { type: ParameterType.Number, help: 'Test' }, ( v: number ) => `${v}` as string );
expectTypeOf( OptionGroup.factory<{foo: number; bar?: string}>( any )
.add( 'bar', { type: ParameterType.Number, help: 'Test' }, v => `${v}` )
.build )
.toEqualTypeOf<TypeErr<['Missing declarations for keys', 'foo']>>();
expectTypeOf( OptionGroup.factory<{foo: number; bar?: string}>( any )
.add ).parameters.toEqualTypeOf<['foo' | 'bar', DeclarationOptionConfig, ( v: unknown ) => string | number | undefined]>();
const grp3 = OptionGroup.factory<{qux: string; bar: string}>( any )
.add( 'qux', { type: ParameterType.Number, help: 'Test' }, ( v => `${v}` as string ) )
.add( 'bar', { type: ParameterType.String, help: 'Test' } )
.build();
expectTypeOf( grp3.getValue() ).toEqualTypeOf<{qux: string; bar: string}>();
expectTypeOf( grp3.setValue ).parameters.toEqualTypeOf<[{qux?: number; bar?: string} | string] | ['qux' | 'bar', string | number]>();
OptionGroup.factory<{qux: string; bar: string}>( any )
// @ts-expect-error
.add( 'qux', { type: ParameterType.Number, help: 'Test' }, ( ( v: number ) => `${v}` as string | undefined ) );
OptionGroup.factory<{qux: string; bar: string}>( any )
.add( 'qux', { type: ParameterType.Number, help: 'Test' }, ( ( v: number ) => `${v}` as string ) );
OptionGroup.factory<{foo: LogLevel}>( any )
.add( 'foo', { type: ParameterType.Map, help: 'Test', map: LogLevel, defaultValue: LogLevel.Error } );
} );
} );
describe( 'Behavior', () => {
class TestPlugin extends ABasePlugin {
public readonly optionsPrefix = 'TEST';
public readonly package = {
name: 'typedoc-TEST',
} as any;
public readonly logger: PluginLogger = {
error: assert.fail,
verbose: jest.fn(),
} as any;
public constructor( public readonly application: Application ){
super( application, __filename );
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
public initialize(): void {}
}
let app: Application;
let plugin: TestPlugin;
let opts: OptionGroup<{foo: number;bar?: string | undefined}, any>;
beforeEach( () => {
app = new Application();
plugin = new TestPlugin( app );
opts = OptionGroup.factory<{foo: number; bar?: string}>( plugin )
.add( 'bar', { type: ParameterType.String, help: '' } )
.add( 'foo', { type: ParameterType.Number, help: '' } )
.build();
} );
it( 'should have options subset', () => {
app.options.freeze();
expect( app.options.getRawValues() ).toMatchObject( { 'TEST:foo': 0, 'TEST:bar': '', 'TEST': { foo: 0, bar: '' }} );
expect( opts.getValue() ).toMatchObject( { foo: 0, bar: '' } );
} );
it( 'should set options (combined)', () => {
opts.setValue( { bar: 'Hello', foo: 42 } );
app.options.freeze();
expect( app.options.getRawValues() ).toMatchObject( { 'TEST:foo': 42, 'TEST:bar': 'Hello', 'TEST': { foo: 42, bar: 'Hello' }} );
expect( opts.getValue() ).toMatchObject( { foo: 42, bar: 'Hello' } );
} );
it( 'should set options (partial)', () => {
opts.setValue( { bar: 'Hello' } );
app.options.freeze();
expect( app.options.getRawValues() ).toMatchObject( { 'TEST:foo': 0, 'TEST:bar': 'Hello', 'TEST': { foo: 0, bar: 'Hello' }} );
expect( opts.getValue() ).toMatchObject( { foo: 0, bar: 'Hello' } );
} );
it( 'should set options (JSON)', () => {
opts.setValue( JSON.stringify( { bar: 'World' } ) );
app.options.freeze();
expect( app.options.getRawValues() ).toMatchObject( { 'TEST:foo': 0, 'TEST:bar': 'World', 'TEST': { foo: 0, bar: 'World' }} );
expect( opts.getValue() ).toMatchObject( { foo: 0, bar: 'World' } );
} );
it( 'should set options (filename, JSON)', () => {
const tmp = fileSync( { postfix: '.json' } );
writeFileSync( tmp.name, JSON.stringify( { bar: 'Doh' } ) );
opts.setValue( tmp.name );
app.options.freeze();
expect( app.options.getRawValues() ).toMatchObject( { 'TEST:foo': 0, 'TEST:bar': 'Doh', 'TEST': { foo: 0, bar: 'Doh' }} );
expect( opts.getValue() ).toMatchObject( { foo: 0, bar: 'Doh' } );
} );
it( 'should set options (filename, JS)', () => {
const tmp = fileSync( { postfix: '.js' } );
writeFileSync( tmp.name, 'module.exports = { bar: \'DohJS\' }' );
opts.setValue( tmp.name );
app.options.freeze();
expect( app.options.getRawValues() ).toMatchObject( { 'TEST:foo': 0, 'TEST:bar': 'DohJS', 'TEST': { foo: 0, bar: 'DohJS' }} );
expect( opts.getValue() ).toMatchObject( { foo: 0, bar: 'DohJS' } );
} );
} );