Skip to content

Commit

Permalink
fix: add unit testing mocks
Browse files Browse the repository at this point in the history
  • Loading branch information
narol1024 committed Jun 2, 2024
1 parent 54080c8 commit a9c257b
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 95 deletions.
34 changes: 0 additions & 34 deletions Makefile

This file was deleted.

25 changes: 0 additions & 25 deletions build/docker/Dockerfile

This file was deleted.

13 changes: 0 additions & 13 deletions deployments/dev/docker-compose.yml

This file was deleted.

2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = {
preset: 'ts-jest/presets/default-esm',
extensionsToTreatAsEsm: ['.ts'],
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
// testMatch: ['<rootDir>/src/cli/*.spec.ts'],
// testMatch: ['<rootDir>/src/platforms/npm-package/index.spec.ts'],
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
Expand Down
22 changes: 22 additions & 0 deletions src/cli/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,32 @@
* This is a sample test suite.
* Replace this with your implementation.
*/
import fetch from 'node-fetch';
import { exec } from 'child_process';
import { bootstrap } from './index';

const dependantsHtml = `
<html>
<body>
<a id="package-tab-dependents" tabindex="0"><span><svg></svg>10000 Dependents</span></a>
</body>
</html>
`;

jest.mock('child_process');
jest.mock('node-fetch');

describe('CLI tool Tests', () => {
it('main', async () => {
(exec as any).mockImplementationOnce((_: any, callback: any) => {
callback(null, { stdout: '{ "loose-envify": "^1.1.0" }' });
});
(fetch as any).mockReturnValueOnce(
Promise.resolve({
status: 200,
text: () => dependantsHtml,
}),
);
const spy = jest.spyOn(console, 'log');
const program = await bootstrap(['node', 'index.js', 'analyze', 'react']);
await expect(program.commands.map((i: any) => i._name)).toEqual(['analyze']);
Expand Down
22 changes: 22 additions & 0 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,32 @@
* This is a sample test suite.
* Replace this with your implementation.
*/
import fetch from 'node-fetch';
import { exec } from 'child_process';
import { analyze } from './index';

jest.mock('child_process');
jest.mock('node-fetch');

const dependantsHtml = `
<html>
<body>
<a id="package-tab-dependents" tabindex="0"><span><svg></svg>10000 Dependents</span></a>
</body>
</html>
`;

describe('Main Tests', () => {
it('can be analyzed for a valid npm package', async () => {
(exec as any).mockImplementationOnce((_: any, callback: any) => {
callback(null, { stdout: '{ "loose-envify": "^1.1.0" }' });
});
(fetch as any).mockReturnValueOnce(
Promise.resolve({
status: 200,
text: () => dependantsHtml,
}),
);
await expect(analyze('react')).resolves.toMatchSnapshot();
});
it('can be analyzed for local packages', async () => {
Expand Down
8 changes: 4 additions & 4 deletions src/platforms/npm-package/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ exports[`Npm pacakge Tests can be analyzed for multiple packages with the packag
Array [
Object {
"fanIn": 10000,
"fanOut": 1,
"fanOut": 4,
"label": "Stable",
"name": "react",
"stability": 0.00009999000099990002,
"stability": 0.00039984006397441024,
},
Object {
"fanIn": 10000,
"fanOut": 5,
"fanOut": 3,
"label": "Stable",
"name": "vue",
"stability": 0.0004997501249375312,
"stability": 0.00029991002699190244,
},
]
`;
Expand Down
87 changes: 86 additions & 1 deletion src/platforms/npm-package/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,111 @@
* This is a sample test suite.
* Replace this with your implementation.
*/
import { readNpmPackageDependencies, analyze } from './index';
import fetch from 'node-fetch';
import { exec } from 'child_process';
import { readNpmPackageDependencies, countNpmPackageDependants, analyze } from './index';

const dependantsHtml = `
<html>
<body>
<a id="package-tab-dependents" tabindex="0"><span><svg></svg>10000 Dependents</span></a>
</body>
</html>
`;

jest.mock('child_process');
jest.mock('node-fetch');

// TODO:
// describe('Npm pacakge Tests for the real world', () => {
// it('can be analyzed for single package', async () => {
// await expect(analyze('react')).resolves.toMatchSnapshot();
// });
// });

describe('Npm pacakge Tests', () => {
it('should not read a non-existent package', async () => {
(exec as any).mockImplementationOnce((_: any, callback: any) => {
callback(null, { stdout: '{"error": { "code": "E404" }}' });
});
await expect(readNpmPackageDependencies('is-a-non-existent-package')).rejects.toEqual(
new Error('Cannot read is-a-non-existent-package.'),
);
});
it('should read a valid package', async () => {
(exec as any).mockImplementationOnce((_: any, callback: any) => {
callback(null, { stdout: '{ "loose-envify": "^1.1.0" }' });
});
await expect(readNpmPackageDependencies('[email protected]')).resolves.toEqual([
{ name: 'loose-envify', version: '^1.1.0' },
]);
});
it('should count the depandants of a valid package', async () => {
(fetch as any).mockReturnValueOnce(
Promise.resolve({
status: 200,
text: () => dependantsHtml,
}),
);
await expect(countNpmPackageDependants('react', '18.3.1')).resolves.toEqual(10000);
});
it('can not count the depandants of a invalid package', async () => {
(fetch as any).mockReturnValueOnce(
Promise.resolve({
status: 200,
text: () => '',
}),
);
await expect(countNpmPackageDependants('is-a-non-existent-package', '1.0.0')).rejects.toEqual(
new Error(`Cannot count the depandants of the is-a-non-existent-package.`),
);
});
it('can be analyzed for single package', async () => {
(exec as any).mockImplementationOnce((_: any, callback: any) => {
callback(null, { stdout: '{ "loose-envify": "^1.1.0" }' });
});
(fetch as any).mockReturnValueOnce(
Promise.resolve({
status: 200,
text: () => dependantsHtml,
}),
);
await expect(analyze('react')).resolves.toMatchSnapshot();
});
it('can be analyzed for multiple packages', async () => {
(exec as any).mockImplementation((command: string, callback: any) => {
if (command.includes('react')) {
callback(null, { stdout: '{ "loose-envify": "^1.1.0" }' });
} else if (command.includes('vue')) {
callback(null, {
stdout: `{ "@vue/shared": "3.4.27", "@vue/compiler-dom": "3.4.27", "@vue/compiler-sfc": "3.4.27", "@vue/runtime-dom": "3.4.27", "@vue/server-renderer": "3.4.27" }`,
});
} else {
callback(null, { stdout: '{}' });
}
});
(fetch as any).mockReturnValue(
Promise.resolve({
status: 200,
text: () => dependantsHtml,
}),
);
await expect(analyze('react,vue')).resolves.toMatchSnapshot();
});
it('can be analyzed for multiple packages with the package version', async () => {
(exec as any).mockImplementation((command: string, callback: any) => {
if (command.includes('react')) {
callback(null, {
stdout: `{ "fbjs": "^0.8.16", "prop-types": "^15.6.0", "loose-envify": "^1.1.0", "object-assign": "^4.1.1" }`,
});
} else if (command.includes('vue')) {
callback(null, {
stdout: `{ "@vue/shared": "3.0.0", "@vue/compiler-dom": "3.0.0", "@vue/runtime-dom": "3.0.0" }`,
});
} else {
callback(null, { stdout: '{}' });
}
});
await expect(analyze('[email protected],[email protected]')).resolves.toMatchSnapshot();
});
});
22 changes: 10 additions & 12 deletions src/platforms/npm-package/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ export async function readNpmPackageDependencies(packageName: string): Promise<N
});
const npmViewPromise = execAsync(`npm view ${packageName} dependencies --json`).then(result => {
try {
const dependencies = JSON.parse(result.stdout) as NpmDependency;
const resultJson = JSON.parse(result.stdout) as any;
if (!!resultJson.error) {
reject(new Error(`Cannot read ${packageName}.`));
}
resolve(
Object.entries(dependencies).map(([name, version]) => ({
Object.entries(resultJson as NpmDependency).map(([name, version]) => ({
name,
version,
})),
Expand Down Expand Up @@ -61,7 +64,7 @@ export async function countNpmPackageDependants(packageName: string, version: st
const path = version !== null ? `${packageName}/v/${version}` : packageName;
const url = `https://www.npmjs.com/package/${path}?activeTab=dependents&t=${Date.now()}`;
const timeoutPromise = new Promise<void>((_, timeoutReject) => {
countNpmPackageDependantsTimeId = setTimeout(() => timeoutReject(new Error('Operation timed out')), 8000);
countNpmPackageDependantsTimeId = setTimeout(() => timeoutReject(new Error('Operation timed out')), 20000);
});
const fetchPromise = async (): Promise<void> => {
try {
Expand All @@ -71,12 +74,7 @@ export async function countNpmPackageDependants(packageName: string, version: st
const htmlStr = $('#package-tab-dependents span').html();
const match = htmlStr?.match(/(?<=<\/svg>\s*)(\S+)(?=\s*Dependents)/)?.[0];
if (match) {
// As the dependant of an npm package is dynamic, it is necessary to mock a number when testing.
if (typeof __JEST_TEST_ENV__ !== 'undefined' && __JEST_TEST_ENV__) {
resolve(10000);
} else {
resolve(parseInt(match.replace(/,/g, ''), 10));
}
resolve(parseInt(match.replace(/,/g, ''), 10));
} else {
return Promise.reject(new Error(`Operation failed`));
}
Expand All @@ -92,7 +90,7 @@ export async function countNpmPackageDependants(packageName: string, version: st
retriedTimes += 1;
doCountTask();
} else {
reject(new Error(`Cannot read ${packageName}.`));
reject(new Error(`Cannot count the depandants of the ${packageName}.`));
}
})
.finally(() => {
Expand Down Expand Up @@ -129,7 +127,7 @@ export async function analyze(packageNames: string): Promise<Dep[]> {

const deps = await Promise.all(depsPromises);
return Promise.resolve(deps);
} catch (error) {
return Promise.reject(new Error(`Cannot analyze ${packageNames}`));
} catch (error: any) {
return Promise.reject(new Error(`Cannot analyze ${packageNames}, the reason is \"${error.message}\"`));
}
}
6 changes: 4 additions & 2 deletions src/platforms/workspaces/yarn/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ describe('Workspaces Tests', () => {
it('should not read an invalid package', async () => {
await expect(analyze('./src/platforms/workspaces/yarn/fixture/invalid-packages')).rejects.toEqual(
new Error(
`It seems that the 'subpackages' directory has not been found, please check the configuration of yarn workspaces..`,
`It seems that the 'subpackages' directory has not been found, please check the configuration of yarn workspaces.`,
),
);
});
it('can not analyze for the invalid local packages', async () => {
await expect(analyze('./error-path')).rejects.toEqual(new Error(`Cannot analyze ./error-path`));
await expect(analyze('./error-path')).rejects.toEqual(
new Error(`Cannot analyze ./error-path, the reason is \"Cannot find package.json on ./error-path.\".`),
);
});
it('can be analyzed for the local packages', async () => {
await expect(analyze('./src/platforms/workspaces/yarn/fixture/valid-packages')).resolves.toMatchSnapshot();
Expand Down
6 changes: 3 additions & 3 deletions src/platforms/workspaces/yarn/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export async function analyze(packagePath: string) {
if (dependencyMap.length === 0) {
return Promise.reject(
new Error(
"It seems that the 'subpackages' directory has not been found, please check the configuration of yarn workspaces..",
"It seems that the 'subpackages' directory has not been found, please check the configuration of yarn workspaces.",
),
);
}
Expand All @@ -61,7 +61,7 @@ export async function analyze(packagePath: string) {
};
}),
);
} catch (error) {
return Promise.reject(new Error(`Cannot analyze ${packagePath}`));
} catch (error: any) {
return Promise.reject(new Error(`Cannot analyze ${packagePath}, the reason is \"${error.message}\".`));
}
}

0 comments on commit a9c257b

Please sign in to comment.