Skip to content

Commit

Permalink
feat: improve control-flow and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Jörg Egli committed Jan 8, 2020
1 parent 9930bee commit 9cefd4b
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 55 deletions.
108 changes: 78 additions & 30 deletions __tests__/version-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ const { Response } = jest.requireActual('node-fetch');

import { logMessages } from '../src/log-messages';
import {
isNPMandNodeMatching,
getNPMmatchesNodeLog,
getValidVersionLog,
getVersionCheckers,
isNPMandNodeMatching,
processVersionArgument,
} from '../src/version-checker';
import { ILogMessage } from '../src/const';

const exampleNodeList = [{ version: 'v12.14.0', date: '2019-12-16', npm: '6.13.4', security: true }];
const nodeVersionListURL = 'https://nodejs.org/dist/index.json';
Expand All @@ -33,53 +35,99 @@ describe('isNPMandNodeMatching', () => {
describe('getNPMmatchesNodeLog', () => {
it('should return the correct success-text', async () => {
(fetch as any).mockReturnValue(Promise.resolve(new Response(JSON.stringify(exampleNodeList))));
expect(await getNPMmatchesNodeLog('12.14.0', '6.13.4')).toBe(
logMessages.success.nodeVersionWorksWithNPMVersion('12.14.0', '6.13.4')
);
expect(await getNPMmatchesNodeLog('12.14.0', '6.13.4')).toMatchObject({
error: false,
text: logMessages.success.nodeVersionWorksWithNPMVersion('12.14.0', '6.13.4'),
});
});
it('should return error-text when node and npm versions do not match', async () => {
(fetch as any).mockReturnValue(Promise.resolve(new Response(JSON.stringify(exampleNodeList))));
expect(await getNPMmatchesNodeLog('12.14.0', '6.0.0')).toBe(logMessages.error.changeNPMVersion('12.14.0'));
expect(await getNPMmatchesNodeLog('12.14.0', '6.0.0')).toMatchObject({
error: true,
text: logMessages.error.changeNPMVersion('12.14.0'),
});
});
it('should return error-text if we can not receive node-list', async () => {
(fetch as any).mockReturnValue(Promise.reject());
expect(await getNPMmatchesNodeLog('12.14.0', '6.0.0')).toBe(
logMessages.warning.fetchNodeListError(nodeVersionListURL)
);
expect(await getNPMmatchesNodeLog('12.14.0', '6.0.0')).toMatchObject({
error: false,
text: logMessages.warning.fetchNodeListError(nodeVersionListURL),
});
});
});

describe('getValidVersionLog', () => {
it('should return the correct success-text', async () => {
expect(await getValidVersionLog('node', '12.14.0', '12.x.x')).toBe(
logMessages.success.programVersionSatiesfies('node', '12.14.0', '12.x.x')
);
expect(await getValidVersionLog('node', '12.14.0', '12.x.x')).toMatchObject({
error: false,
text: logMessages.success.programVersionSatisfies('node', '12.14.0', '12.x.x'),
});
});
it('should return the correct error-text', async () => {
expect(await getValidVersionLog('node', '12.14.0', '10.x.x')).toBe(
logMessages.error.changeProgramVersion('node', '12.14.0', '10.x.x')
);
expect(await getValidVersionLog('node', '12.14.0', '10.x.x')).toMatchObject({
error: true,
text: logMessages.error.changeProgramVersion('node', '12.14.0', '10.x.x'),
});
});
});

describe('getVersionCheckers', () => {
it('should return an array of 2 promises', async () => {
(execa as any).mockReturnValue(Promise.resolve({ stdout: '' }));
expect(await getVersionCheckers(['test'])).toStrictEqual([Promise.resolve(), Promise.resolve()]);
describe('processVersionArgument', () => {
it('should return the node version', async () => {
(execa as any).mockReturnValue(Promise.resolve({ stdout: '12.14.0' }));
expect(await processVersionArgument('node=12.14.0')).toMatchObject({
error: false,
text: logMessages.success.programVersionSatisfies('node', '12.14.0', '12.14.0'),
});
});
it('should return an array 2 of promises', async () => {
(execa as any).mockReturnValue(Promise.resolve({ stdout: '' }));
expect(await getVersionCheckers(['node=12.1.4'])).toStrictEqual([Promise.resolve(), Promise.resolve()]);
it('should return the node version from .node-version file', async () => {
(readFile as any).mockReturnValue(Promise.resolve('10.0.0'));
(execa as any).mockReturnValue(Promise.resolve({ stdout: '10.0.0' }));
expect(await processVersionArgument('node')).toMatchObject({
error: false,
text: logMessages.success.programVersionSatisfies('node', '10.0.0', '10.0.0'),
});
});
it('should return an array 3 of promises', async () => {
expect(await getVersionCheckers(['node=12.1.4', 'npm=5.6.1'])).toStrictEqual([
Promise.resolve(),
Promise.resolve(),
Promise.resolve(),
]);
it('should return the yo version', async () => {
(execa as any).mockReturnValue(Promise.resolve({ stdout: '3.0.0' }));
expect(await processVersionArgument('yo=3.x.x')).toMatchObject({
error: false,
text: logMessages.success.programVersionSatisfies('yo', '3.0.0', '3.x.x'),
});
});
it('should return an array 3 of promises 2', async () => {
(readFile as any).mockReturnValue(Promise.reject());
expect(await getVersionCheckers(['node'])).toStrictEqual([Promise.resolve(), Promise.resolve()]);
it('should return a warning for not specifying a yo version', async () => {
(execa as any).mockReturnValue(Promise.resolve({ stdout: '3.0.0' }));
expect(await processVersionArgument('yo')).toMatchObject({
error: false,
text: logMessages.warning.specifyProgramVersion('yo', '3.0.0'),
});
});
});

describe('getVersionCheckers', () => {
it('should resolve two checker promises (specify yo version and npm works with node).', async () => {
(fetch as any).mockReturnValue(
Promise.resolve(new Response(JSON.stringify([{ version: 'v3.0.0', npm: '3.0.0' }])))
);
(execa as any).mockReturnValue(Promise.resolve({ stdout: '3.0.0' }));
(readFile as any).mockReturnValue(Promise.resolve('3.0.0'));
const expectedLogMessages: ILogMessage[] = [
{ error: false, text: logMessages.warning.specifyProgramVersion('yo', '3.0.0') },
{ error: false, text: logMessages.success.nodeVersionWorksWithNPMVersion('3.0.0', '3.0.0') },
];
const checkers = await getVersionCheckers(['yo']);
expect(await Promise.all(checkers)).toMatchObject(expectedLogMessages);
});
it('should resolve two checker promises (node version ok and npm works with node)', async () => {
(fetch as any).mockReturnValue(
Promise.resolve(new Response(JSON.stringify([{ version: 'v3.0.0', npm: '3.0.0' }])))
);
(execa as any).mockReturnValue(Promise.resolve({ stdout: '3.0.0' }));
const expectedLogMessages: ILogMessage[] = [
{ error: false, text: logMessages.success.programVersionSatisfies('node', '3.0.0', '3.x.x') },
{ error: false, text: logMessages.success.programVersionSatisfies('npm', '3.0.0', '3.x.x') },
{ error: false, text: logMessages.success.nodeVersionWorksWithNPMVersion('3.0.0', '3.0.0') },
];
const checkers = await getVersionCheckers(['node=3.x.x', 'npm=3.x.x']);
expect(await Promise.all(checkers)).toMatchObject(expectedLogMessages);
});
});
19 changes: 14 additions & 5 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import chalk from 'chalk';
import { getCwd } from './get-cwd';
import { getVersionCheckers } from './version-checker';
import { IOptions } from './const';
import { IOptions, ILogMessage } from './const';

export interface IApiOptions {
cwd?: string;
Expand All @@ -18,17 +18,26 @@ export async function api(apiOptions: IApiOptions) {
};

try {
const linter: Promise<string>[] = [];
const checkers: Promise<ILogMessage>[] = [];
if (options.versions) {
linter.push(...(await getVersionCheckers(options.versions)));
checkers.push(...(await getVersionCheckers(options.versions)));
}
if (options.hooksInstalled && process.env.NODE_ENV?.toLowerCase() !== 'ci') {
// linter.push(checkHooksInstalledChecker());
// linter.push(getHooksInstalledChecker());
}
if (options.saveExact) {
// linter.push(getSaveExactChecker());
}
linter.forEach(async (lint) => console.log(await lint));

const results = await Promise.all(checkers);
const hasErrors = results.reduce((acc, result) => {
console.info(result.text);
return acc || result.error;
}, false);
if (hasErrors) {
console.error(chalk.red('Stopping any further processes! process.exit(1)'));
process.exit(1);
}
} catch (err) {
console.error(chalk.red(err));
process.exit(1);
Expand Down
2 changes: 1 addition & 1 deletion src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface INodeVersion {
security: boolean;
}

export interface ILogOutput {
export interface ILogMessage {
error: boolean;
text: string;
}
6 changes: 5 additions & 1 deletion src/log-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const logMessages = {
chalk.green(
`${logSymbols.success} Your node version ${usedNodeVersion} works with your npm version ${usedNPMVersion}.`
),
programVersionSatiesfies: (program: string, usedVersion: string, expectedVersion: string) =>
programVersionSatisfies: (program: string, usedVersion: string, expectedVersion: string) =>
chalk.green(
`${logSymbols.success} Your ${program} version ${usedVersion} works with the required version (${expectedVersion}) of your project.`
),
Expand All @@ -27,6 +27,10 @@ export const logMessages = {
chalk.red(`${logSymbols.error} Couldn't find ${file} file in your project root directory.`),
},
warning: {
specifyProgramVersion: (program: string, usedVersion: string) =>
chalk.yellow(
`${logSymbols.warning} Specify ${program} version! You are using ${usedVersion} but you didn't specify a required version for ${program} in this project.`
),
fetchNodeListError: (nodeVersionListURL: string) =>
chalk.yellow(
`${logSymbols.warning} Could not fetch node-list from ${nodeVersionListURL}. Your NPM version might not match your node version.`
Expand Down
49 changes: 31 additions & 18 deletions src/version-checker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import semver from 'semver';
import { INodeVersion } from './const';
import { INodeVersion, ILogMessage } from './const';
import { getNodeList } from './fetch-node-versions';
import { getNodeVersionFromFile } from './get-file-data';
import { getInstalledVersion } from './get-version';
Expand All @@ -14,38 +14,51 @@ export const isNPMandNodeMatching = (nodeList: INodeVersion[], usedNodeVersion:
export const getNPMmatchesNodeLog = async (usedNodeVersion: string, usedNPMVersion: string) => {
const nodeList = await getNodeList();
if (nodeList.error) {
return nodeList.text;
return { error: false, text: nodeList.text };
}
return isNPMandNodeMatching(JSON.parse(nodeList.text), usedNodeVersion, usedNPMVersion)
? logMessages.success.nodeVersionWorksWithNPMVersion(usedNodeVersion, usedNPMVersion)
: logMessages.error.changeNPMVersion(usedNodeVersion);
? { error: false, text: logMessages.success.nodeVersionWorksWithNPMVersion(usedNodeVersion, usedNPMVersion) }
: { error: true, text: logMessages.error.changeNPMVersion(usedNodeVersion) };
};

export const getValidVersionLog = async (program: string, usedVersion: string, expectedVersion: string) => {
return semver.satisfies(usedVersion, expectedVersion)
? logMessages.success.programVersionSatiesfies(program, usedVersion, expectedVersion)
: logMessages.error.changeProgramVersion(program, usedVersion, expectedVersion);
? { error: false, text: logMessages.success.programVersionSatisfies(program, usedVersion, expectedVersion) }
: { error: true, text: logMessages.error.changeProgramVersion(program, usedVersion, expectedVersion) };
};

export const getValidNodeVersionLog = async (usedNodeVersion: ILogMessage) => {
const nodeVersionFromFile = await getNodeVersionFromFile('.node-version');
return nodeVersionFromFile.error
? nodeVersionFromFile
: getValidVersionLog('node', usedNodeVersion.text, nodeVersionFromFile.text);
};

export const processVersionArgument = async (versionArgument: string) => {
const [program, expectedVersionCli] = versionArgument.split('=').map((item) => item.trim());
const expectedVersion =
program === 'node' && !expectedVersionCli
? await getNodeVersionFromFile('.node-version')
: { error: false, text: expectedVersionCli };
if (expectedVersion.error) {
return expectedVersion.text;
}
const usedVersion = await getInstalledVersion(program);
return usedVersion.error ? usedVersion.text : getValidVersionLog(program, usedVersion.text, expectedVersion.text);
if (program === 'node' && !expectedVersionCli) {
return getValidNodeVersionLog(usedVersion);
}
if (usedVersion.error) {
return usedVersion;
}
if (!expectedVersionCli) {
return { error: false, text: logMessages.warning.specifyProgramVersion(program, usedVersion.text) };
}
return getValidVersionLog(program, usedVersion.text, expectedVersionCli);
};

export const getVersionCheckers = async (versionArguments: string[]) => {
const versionChecks = versionArguments.map(async (versionArgument) => processVersionArgument(versionArgument));
const usedNodeVersion = await getInstalledVersion('node');
const usedNPMVersion = await getInstalledVersion('npm');
const versionChecks: Promise<ILogMessage>[] = versionArguments.map(async (versionArgument) =>
processVersionArgument(versionArgument)
);
const usedNodeVersion: ILogMessage = await getInstalledVersion('node');
const usedNPMVersion: ILogMessage = await getInstalledVersion('npm');
if (usedNodeVersion.error || usedNPMVersion.error) {
const usedVersionError = usedNodeVersion.error ? usedNodeVersion.text : usedNPMVersion.text;
const usedVersionError: ILogMessage = usedNodeVersion.error
? { error: true, text: usedNodeVersion.text }
: { error: true, text: usedNPMVersion.text };
versionChecks.push(Promise.resolve(usedVersionError));
return versionChecks;
}
Expand Down

0 comments on commit 9cefd4b

Please sign in to comment.