Skip to content

Commit f1dba1b

Browse files
authored
test: ensure working examples and no console spam (#908)
1 parent 39b74c0 commit f1dba1b

3 files changed

Lines changed: 130 additions & 1 deletion

File tree

test/scripts/apidoc/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
*.actuals.json
2+
temp/
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { mkdirSync, writeFileSync } from 'node:fs';
2+
import { resolve } from 'node:path';
3+
import type { DeclarationReflection, SignatureReflection } from 'typedoc';
4+
import { ReflectionKind } from 'typedoc';
5+
import type { SpyInstance } from 'vitest';
6+
import { afterAll, beforeEach, describe, expect, it, vi } from 'vitest';
7+
import { faker } from '../../../src';
8+
import { loadProject } from './utils';
9+
10+
/*
11+
* This test ensures, that every method
12+
* - has working examples
13+
* - and running these does not log anything, unless the method is deprecated
14+
*/
15+
16+
const locales: Record<string, string> = {
17+
GH: 'en_GH',
18+
US: 'en_US',
19+
};
20+
21+
describe('examples and deprecations', () => {
22+
const project = loadProject();
23+
24+
const directs: DeclarationReflection[] = project
25+
.getChildrenByKind(ReflectionKind.Class)
26+
.filter((ref) => ref.name === 'Faker')[0]
27+
.getChildrenByKind(ReflectionKind.Property)
28+
.filter((ref) => ['fake', 'unique'].includes(ref.name));
29+
30+
const modules: Record<string, DeclarationReflection[]> = project
31+
.getChildrenByKind(ReflectionKind.Namespace)[0]
32+
.getChildrenByKind(ReflectionKind.Class)
33+
.filter((ref) => faker[ref.name.toLowerCase()] && ref.name !== 'Mersenne')
34+
.reduce(
35+
(a, v) => ({
36+
...a,
37+
[v.name]: v.getChildrenByKind(ReflectionKind.Method),
38+
}),
39+
{ directs }
40+
);
41+
42+
const consoleSpies: Array<SpyInstance> = Object.keys(console)
43+
.filter((key) => typeof console[key] === 'function')
44+
.map((methodName) => vi.spyOn(console, methodName as keyof typeof console));
45+
46+
afterAll(() => {
47+
faker.locale = 'en';
48+
for (const spy of consoleSpies) {
49+
spy.mockRestore();
50+
}
51+
});
52+
53+
describe.each(Object.entries(modules))('%s', (moduleName, methods) => {
54+
const methodsByName: Record<string, DeclarationReflection> = methods.reduce(
55+
(a, v) => ({ ...a, [v.name]: v }),
56+
{}
57+
);
58+
59+
beforeEach(() => {
60+
faker.locale = 'en';
61+
for (const spy of consoleSpies) {
62+
spy.mockReset();
63+
}
64+
});
65+
66+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
67+
it.each(Object.entries(methodsByName))('%s', async (methodName, method) => {
68+
const signatures: SignatureReflection[] =
69+
method.signatures || method.type['declaration'].signatures;
70+
const signature = signatures[signatures.length - 1];
71+
72+
// Extract examples and make them runnable
73+
let examples =
74+
signature?.comment?.tags
75+
.filter((tag) => tag.tagName === 'example')
76+
.map((tag) => tag.text.trimEnd())
77+
.join('')
78+
.trim() ?? '';
79+
examples = examples.replace(
80+
/faker([A-Z]{2})\./g,
81+
(_, locale: string) => `faker.locale = '${locales[locale]}';\nfaker.`
82+
);
83+
84+
expect(examples, `${moduleName}.${methodName} to have examples`).not.toBe(
85+
''
86+
);
87+
88+
// Save examples to a file to run it
89+
const dir = resolve(__dirname, 'temp', moduleName);
90+
mkdirSync(dir, { recursive: true });
91+
const path = resolve(dir, `${methodName}.ts`);
92+
writeFileSync(
93+
path,
94+
`import { faker } from '../../../../../src';\n${examples}`
95+
);
96+
97+
// Run the examples
98+
await import(path);
99+
100+
// Verify logging
101+
const deprecatedFlag = signature.comment?.hasTag('deprecated') ?? false;
102+
if (deprecatedFlag) {
103+
expect(consoleSpies[1]).toHaveBeenCalled();
104+
} else {
105+
for (const spy of consoleSpies) {
106+
expect(spy).not.toHaveBeenCalled();
107+
}
108+
}
109+
});
110+
});
111+
});

test/scripts/apidoc/utils.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { DeclarationReflection } from 'typedoc';
1+
import type { DeclarationReflection, ProjectReflection } from 'typedoc';
22
import { ReflectionKind } from 'typedoc';
33
import { newTypeDocApp, patchProject } from '../../../scripts/apidoc/utils';
44

@@ -24,3 +24,20 @@ export function loadExampleMethods(): Record<string, DeclarationReflection> {
2424

2525
return methods;
2626
}
27+
28+
/**
29+
* Loads the project using TypeDoc.
30+
*/
31+
export function loadProject(): ProjectReflection {
32+
const app = newTypeDocApp();
33+
34+
app.bootstrap({
35+
entryPoints: ['src/index.ts'],
36+
});
37+
38+
const project = app.convert();
39+
40+
patchProject(project);
41+
42+
return project;
43+
}

0 commit comments

Comments
 (0)