fix(typedoc-plugin-appium): fix type and test problems
Re-enable `strict` mode
Fix failures arising from that
Fix E2E test failures
MODIFY
packages/typedoc-plugin-appium/lib/converter/base-converter.ts
packages/typedoc-plugin-appium/lib/converter/base-converter.ts
@@ -3,11 +3,7 @@ import {AppiumPluginLogger} from '../logger';
import {ModuleCommands} from '../model';
export abstract class BaseConverter<Result> {
- constructor(
- protected ctx: Context,
- protected log: AppiumPluginLogger,
- protected readonly builtinCommands?: ModuleCommands
- ) {}
+ constructor(protected ctx: Context, protected log: AppiumPluginLogger, ...args: any[]) {}
abstract convert(): Result;
}

MODIFY
packages/typedoc-plugin-appium/lib/converter/comment.ts
packages/typedoc-plugin-appium/lib/converter/comment.ts
*/
import _ from 'lodash';
-import {SetOptional} from 'type-fest';
-import {Comment, CommentDisplayPart, CommentTag} from 'typedoc';
+import {SetOptional, ValueOf} from 'type-fest';
+import {Comment, CommentTag} from 'typedoc';
import {CommandMethodDeclarationReflection, KnownMethods} from './types';
export const NAME_EXAMPLE_TAG = '@example';
/**
* Languages which can be used in example code blocks
+ *
+ * The key is the identifier used in a fenced code block, and the value is the "display" value
*/
-export enum ExampleLanguage {
- ts = 'TypeScript',
- typescript = 'TypeScript',
- js = 'JavaScript',
- javascript = 'JavaScript',
- py = 'Python',
- python = 'Python',
- rb = 'Ruby',
- ruby = 'Ruby',
- java = 'Java',
-}
+export const ExampleLanguage = {
+ ts: 'TypeScript',
+ typescript: 'TypeScript',
+ js: 'JavaScript',
+ javascript: 'JavaScript',
+ py: 'Python',
+ python: 'Python',
+ rb: 'Ruby',
+ ruby: 'Ruby',
+ java: 'Java',
+} as const;
+/**
+ * The beginning of fenced code block looks like this.
+ *
+ * @todo Ensure the _end_ of the fenced code block exists
+ */
const FENCED_CODE_BLOCK_REGEX = /^\s*```(\w+)?/;
/**
@@ -62,7 +69,7 @@ export interface CommentData {
*/
export interface Example {
text: string;
- lang: ExampleLanguage;
+ lang: ValueOf<typeof ExampleLanguage>;
}
/**
@@ -265,6 +272,15 @@ export function cloneComment(comment: Comment, blockTags?: CommentTag[]): Commen
return newComment;
}
+/**
+ * Type guard to narrow a string to a key of {@linkcode ExampleLanguage}
+ * @param value anything
+ * @returns `true` if the value is a valid language
+ */
+function isValidLang(value: any): value is keyof typeof ExampleLanguage {
+ return value in ExampleLanguage;
+}
+
/**
* Finds any `@example` tags within a comment and creates an {@linkcode ExtractedExamples} object
* for them. Creates a new comment with the examples removed, so we can handle them in a
@@ -292,11 +308,12 @@ export const extractExamples = (comment?: Comment): ExtractedExamples | undefine
return {comment};
}
- const examples: Example[] = exampleTags.flatMap(({content}) =>
- content.reduce((examples, {text}: CommentDisplayPart) => {
+ const examples = exampleTags.flatMap(({content}) =>
+ content.reduce((examples, {text}) => {
const match = text.match(FENCED_CODE_BLOCK_REGEX);
if (match) {
- const lang = match[1] ?? ExampleLanguage.js;
+ const matchedLang = match[1] ?? 'js';
+ const lang = isValidLang(matchedLang) ? matchedLang : 'js';
return [
...examples,
{
@@ -306,7 +323,7 @@ export const extractExamples = (comment?: Comment): ExtractedExamples | undefine
];
}
return examples;
- }, [])
+ }, [] as Example[])
);
if (examples.length) {

MODIFY
packages/typedoc-plugin-appium/lib/converter/exec-method-map.ts
packages/typedoc-plugin-appium/lib/converter/exec-method-map.ts
@@ -72,7 +72,7 @@ export function convertExecuteMethodMap({
if (!commandProp) {
// this is a bug in the driver implementation
- this.log.warn(
+ log.warn(
'Execute method map in %s has no "command" property for %s',
parentRefl.name,
script
@@ -82,7 +82,7 @@ export function convertExecuteMethodMap({
if (!_.isString(commandProp.type.value) || _.isEmpty(commandProp.type.value)) {
// this is a bug in the driver implementation
- this.log.warn(
+ log.warn(
'Execute method map in %s has an empty or invalid "command" property for %s',
parentRefl.name,
script

MODIFY
packages/typedoc-plugin-appium/lib/theme/index.ts
packages/typedoc-plugin-appium/lib/theme/index.ts
+import _ from 'lodash';
import {
Application,
ContainerReflection,
@@ -50,10 +51,9 @@ export class AppiumTheme extends MarkdownTheme {
this.#extensionTemplateRenderer = this.#createTemplateRenderer(AppiumThemeTemplate.Extension);
- this.#opts = Object.keys(declarations).reduce((opts, name) => {
- opts[name] = this.application.options.getValue(name);
- return opts;
- }, {} as AppiumPluginOptions);
+ this.#opts = _.fromPairs(
+ Object.keys(declarations).map((name) => [[name, this.application.options.getValue(name)]])
+ ) as AppiumPluginOptions;
/**
* We do not want to show breadcrumbs by default, but `MarkdownTheme` does. We cannot override

MODIFY
packages/typedoc-plugin-appium/test/e2e/converter/builtin-method-map.e2e.spec.ts
packages/typedoc-plugin-appium/test/e2e/converter/builtin-method-map.e2e.spec.ts
@@ -12,6 +12,7 @@ import {
import {BuiltinCommands} from '../../../lib/model/builtin-commands';
import {AppiumPluginLogger} from '../../../lib/logger';
import {initConverter, NAME_FAKE_DRIVER_MODULE} from '../helpers';
+import {CommandData} from '../../../lib/model';
describe('@appium/typedoc-plugin-appium', function () {
describe('BuiltinMethodMapConverter', function () {
@@ -41,6 +42,7 @@ describe('@appium/typedoc-plugin-appium', function () {
describe('when provided the correct module', function () {
let builtinSource: BuiltinCommands;
let converter: BuiltinMethodMapConverter;
+
before(async function () {
const knownMethods = (
await initConverter(BuiltinExternalDriverConverter, NAME_TYPES_MODULE)
@@ -63,12 +65,14 @@ describe('@appium/typedoc-plugin-appium', function () {
);
});
- it('should contain command data per route', function () {
- const firstCommand = [
- ...builtinSource.moduleCmds!.routeMap.get('/session')!.values(),
- ][0];
- expect(firstCommand).to.exist;
- expect(_.omit(firstCommand, 'methodRefl', 'parentRefl', 'comment', 'log')).to.eql({
+ describe('command data', function () {
+ let cmdData: CommandData;
+ before(function () {
+ cmdData = [...builtinSource.moduleCmds!.routeMap.get('/session')!.values()][0];
+ });
+
+ it('should contain the expected properties in the getSession command data', function () {
+ expect(_.omit(cmdData, 'methodRefl', 'parentRefl', 'comment', 'log')).to.eql({
command: 'createSession',
httpMethod: 'POST',
commentSource: 'method-signature',
@@ -77,10 +81,15 @@ describe('@appium/typedoc-plugin-appium', function () {
optionalParams: ['desiredCapabilities', 'requiredCapabilities', 'capabilities'],
isPluginCommand: false,
});
- expect(firstCommand.methodRefl!.name).to.equal('createSession');
- expect(firstCommand.comment).to.be.an.instanceof(Comment);
- // @ts-expect-error
- expect(firstCommand.log).to.be.an.instanceof(AppiumPluginLogger);
+ });
+
+ it('should associate the command data for getSession with the createSession method', function () {
+ expect(cmdData.methodRefl!.name).to.equal('createSession');
+ });
+
+ it('should derive a comment for the getSession command', function () {
+ expect(cmdData.comment).to.be.an.instanceof(Comment);
+ });
});
});

MODIFY
packages/typedoc-plugin-appium/test/e2e/converter/external.e2e.spec.ts
packages/typedoc-plugin-appium/test/e2e/converter/external.e2e.spec.ts
@@ -73,7 +73,13 @@ describe('@appium/typedoc-plugin-appium', function () {
it('should find commands', function () {
expect(driverCmds).not.to.be.empty;
+ });
+
+ it('should find commands in FakeDriver', function () {
expect(fakeDriverCmds).to.exist;
+ });
+
+ it('should find the createSession command', function () {
expect(sessionCmdSet).to.exist;
});

MODIFY
packages/typedoc-plugin-appium/test/e2e/helpers.ts
packages/typedoc-plugin-appium/test/e2e/helpers.ts
@@ -116,19 +116,19 @@ export function initAppForPkgs({
* @param extraArgs Extra args to `cls`' constructor
* @returns Converter class instance
*/
-async function convert<
- ConvertResult,
- SomeConverter extends BaseConverter<ConvertResult>,
- Args extends any = any
->(
+async function convert<T, C extends BaseConverter<T>, Args extends readonly any[] = any[]>(
app: Application,
- cls: Constructor<SomeConverter, [Context, AppiumPluginLogger, ...Args[]]>,
- extraArgs: Args[] = []
-): Promise<SomeConverter> {
+ cls: ConverterConstructor<T, C, Args>,
+ extraArgs?: Args
+): Promise<C> {
return await new Promise((resolve, reject) => {
const listener = (ctx: Context) => {
const log = new AppiumPluginLogger(app.logger, `test-${cls.name}`);
+ if (extraArgs?.length) {
resolve(new cls(ctx, log, ...extraArgs));
+ } else {
+ resolve(new cls(ctx, log));
+ }
};
app.converter.once(Converter.EVENT_RESOLVE_BEGIN, listener);
try {
@@ -150,11 +150,11 @@ async function convert<
* @returns Converter class instance
*/
export async function initConverter<
- ConverterResult,
- C extends BaseConverter<ConverterResult>,
- Args extends any = any
+ T,
+ C extends BaseConverter<T>,
+ Args extends readonly any[] = any[]
>(
- cls: Constructor<C, [Context, AppiumPluginLogger, ...Args[]]>,
+ cls: ConverterConstructor<T, C, Args>,
pkgName: string,
opts: InitConverterOptions<Args> = {}
): Promise<C> {
@@ -167,6 +167,11 @@ export async function initConverter<
/**
* Options for {@linkcode initConverter}
*/
-export interface InitConverterOptions<Args extends any = any> extends Partial<TypeDocOptions> {
- extraArgs?: Args[];
+export interface InitConverterOptions<Args extends readonly any[] = any[]>
+ extends Partial<TypeDocOptions> {
+ extraArgs?: Args;
}
+
+type ConverterConstructor<T, C extends BaseConverter<T>, Args extends readonly any[] = any[]> =
+ | Constructor<C, [Context, AppiumPluginLogger, ...Args]>
+ | Constructor<C, [Context, AppiumPluginLogger]>;

MODIFY
packages/typedoc-plugin-appium/test/e2e/index.e2e.spec.ts
packages/typedoc-plugin-appium/test/e2e/index.e2e.spec.ts
@@ -77,7 +77,7 @@ describe('@appium/typedoc-plugin-appium', function () {
(refl) => refl.name === '@appium/base-driver'
)!;
expect(baseDriverRefl).to.exist;
- const cmdRefl = baseDriverRefl.getChildByName('/status')! as CommandReflection;
+ const cmdRefl = baseDriverRefl.getChildByName('getStatus')! as CommandReflection;
expect(cmdRefl).to.have.property('comment').and.to.not.be.undefined;
cmdRefl;
expect(cmdRefl).to.have.property('hasExample', true);

MODIFY
packages/typedoc-plugin-appium/test/unit/converter/comment.spec.ts
packages/typedoc-plugin-appium/test/unit/converter/comment.spec.ts
@@ -143,7 +143,7 @@ describe('@appium/typedoc-plugin-appium', function () {
* @param a some thing
* @param b another thing
*/
- function assertDeepEqual(a: object, b: object) {
+ function assertDeepEqual(a: any, b: any): void {
for (const [key, value] of Object.entries(a)) {
if (typeof value === 'object') {
expect(typeof b[key]).to.equal('object');

MODIFY
packages/typedoc-plugin-appium/tsconfig.json
packages/typedoc-plugin-appium/tsconfig.json
"compilerOptions": {
"rootDir": ".",
"outDir": "build",
- "types": ["node", "mocha", "chai", "sinon-chai", "chai-as-promised"]
+ "types": ["node", "mocha", "chai", "sinon-chai", "chai-as-promised"],
+ "strict": true
+ },
+ "ts-node": {
+ "transpileOnly": true,
+ "files": true
},
"include": ["lib", "test"]
}

fix(typedoc-plugin-appium): fix type and test problems

- Re-enable `strict` mode
- Fix failures arising from that
- Fix E2E test failures