Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added 'info' task using envinfo #1632

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@
"svg2js": "0.0.4-alpha1",
"tslib": "2.5.2",
"type-fest": "4.14.0",
"zod": "3.23.8"
"zod": "3.23.8",
"envinfo": "7.13.0"
},
"devDependencies": {
"@types/envinfo": "7.8.4"
},
"peerDependencies": {
"@rnv/config-templates": "^1.0.0-rc.21"
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/enums/taskName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const RnvTaskName = {
link: 'link',
unlink: 'unlink',
publish: 'publish',
status: 'status',
switch: 'switch',
targetLaunch: 'target launch',
targetList: 'target list',
Expand Down
4 changes: 2 additions & 2 deletions packages/engine-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import taskHooksList from './tasks/hooks/taskHooksList';
import taskHooksRun from './tasks/hooks/taskHooksRun';
import taskHooksPipes from './tasks/hooks/taskHooksPipes';
import taskClean from './tasks/global/taskClean';
import taskStatus from './tasks/global/taskStatus';
import taskInfo from './tasks/global/taskInfo';
import taskConfig from './tasks/global/taskConfig';
import taskHelp from './tasks/global/taskHelp';
import taskNew from './tasks/bootstrap/taskNew';
Expand Down Expand Up @@ -63,7 +63,7 @@ const Engine = createRnvEngine({
taskHooksRun,
taskHooksPipes,
taskClean,
taskStatus,
taskInfo,
taskConfig,
taskHelp,
taskNew,
Expand Down
179 changes: 179 additions & 0 deletions packages/engine-core/src/tasks/global/__tests__/taskInfo.test.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ElenaDiachenko consider changing the scope of tests.

instead of testing what could be considered an internal structure of task:

import { _checkAndConfigureTargetSdk, _formatObject, _getCliVersions } from '../taskInfo';

import task itself

import taskInfo from '../taskInfo';

and create test case scenarios around it.

that way what is tested (taskInfo) is treated as blackbox because nobody will use _checkAndConfigureTargetSdk, _formatObject, _getCliVersions directly (those should be non-expoted methods internal to task).
the method you want to test in this case would be taskClean.fn() because that's the public api called by anybody who uses this task

example:
https://github.com/flexn-io/renative/blob/main/packages/engine-core/src/tasks/global/__tests__/taskClean.test.ts#L35

Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { createRnvContext, execCLI, getContext, logDebug, logToSummary, logError } from '@rnv/core';
import envinfo from 'envinfo';
import semver from 'semver';
import taskInfo from '../taskInfo';

jest.mock('@rnv/core');
jest.mock('semver');
jest.mock('envinfo', () => ({
run: jest.fn(),
}));

beforeEach(() => {
createRnvContext();
jest.resetAllMocks();
jest.resetModules();
});

describe('taskInfo tests', () => {
it('Execute task.rnv.info', async () => {
// GIVEN
const ctx = getContext();
ctx.cli = {
webosAres: '/path/to/webosAres',
tizen: '/path/to/tizen',
};
const mockEnvInfo = { System: { OS: 'macOS', CPU: 'Intel' } };
jest.mocked(envinfo.run).mockResolvedValue(JSON.stringify(mockEnvInfo));

const mockCheckAndConfigure = jest.fn();
jest.mock(
'@rnv/sdk-tizen',
() => ({
checkAndConfigureTizenSdks: mockCheckAndConfigure,
}),
{ virtual: true }
);
jest.mock(
'@rnv/sdk-webos',
() => ({
checkAndConfigureWebosSdks: mockCheckAndConfigure,
}),
{ virtual: true }
);

const mockExecCLI = jest.mocked(execCLI).mockResolvedValue('1.0.0');
semver.coerce = jest.fn().mockReturnValue({ version: '1.0.0' });

// WHEN
await expect(
taskInfo.fn?.({
ctx,
taskName: 'MOCK_taskName',
originTaskName: 'MOCK_originTaskName',
parentTaskName: 'MOCK_parentTaskName',
shouldSkip: false,
})
).resolves.toEqual(true);
// THEN
expect(envinfo.run).toHaveBeenCalled();
expect(mockCheckAndConfigure).toHaveBeenCalledTimes(2);
expect(mockExecCLI).toHaveBeenCalledTimes(2);
expect(logToSummary).toHaveBeenCalledWith(expect.stringContaining('System:\n OS: macOS\n CPU: Intel\n'));
expect(logToSummary).toHaveBeenCalledWith(expect.stringContaining('WEBOS CLI: 1.0.0 - /path/to/webosAres\n'));
expect(logToSummary).toHaveBeenCalledWith(expect.stringContaining('TIZEN CLI: 1.0.0 - /path/to/tizen\n'));
});
it('should handle errors when getting environment info', async () => {
// GIVEN
const ctx = getContext();
const mockError = new Error('Test error');
jest.mocked(envinfo.run).mockRejectedValue(mockError);
//WHEN
await expect(
taskInfo.fn?.({
ctx,
taskName: 'MOCK_taskName',
originTaskName: 'MOCK_originTaskName',
parentTaskName: 'MOCK_parentTaskName',
shouldSkip: false,
})
).resolves.toEqual(true);
//THEN
expect(logError).toHaveBeenCalledWith(mockError);
expect(logToSummary).toHaveBeenCalledWith(expect.objectContaining(mockError));
});
it('should handle errors during SDK configuration', async () => {
// GIVEN
const ctx = getContext();
const mockEnvInfo = { System: { OS: 'macOS', CPU: 'Intel' } };
jest.mocked(envinfo.run).mockResolvedValue(JSON.stringify(mockEnvInfo));
const cliVersionOutput = '1.0.0';
ctx.cli = {
webosAres: '/path/to/webosAres',
tizen: '/path/to/tizen',
};
jest.mocked(execCLI).mockResolvedValue(cliVersionOutput);
semver.coerce = jest.fn().mockReturnValue({ version: '1.0.0' });
jest.mock(
'@rnv/sdk-tizen',
() => ({
checkAndConfigureTizenSdks: jest.fn().mockRejectedValue(new Error('Tizen SDK error')),
}),
{ virtual: true }
);
jest.mock(
'@rnv/sdk-webos',
() => ({
checkAndConfigureWebosSdks: jest.fn(),
}),
{ virtual: true }
);

// WHEN
await expect(
taskInfo.fn?.({
ctx,
taskName: 'MOCK_taskName',
originTaskName: 'MOCK_originTaskName',
parentTaskName: 'MOCK_parentTaskName',
shouldSkip: false,
})
).resolves.toEqual(true);
// THEN
expect(envinfo.run).toHaveBeenCalled();
expect(execCLI).toHaveBeenCalledTimes(2);
expect(logDebug).toHaveBeenCalledWith('Error configuring sdk-tizen SDK: ', new Error('Tizen SDK error'));
expect(logDebug).not.toHaveBeenCalledWith('Error configuring sdk-webos SDK: ', new Error('WebOS SDK error'));
expect(logToSummary).toHaveBeenCalledWith(expect.stringContaining('System:\n OS: macOS\n CPU: Intel\n'));
expect(logToSummary).toHaveBeenCalledWith(expect.stringContaining('WEBOS CLI: 1.0.0 - /path/to/webosAres\n'));
});
it('should handle error and log it if module is not found', async () => {
// GIVEN
const ctx = getContext();
const mockEnvInfo = { System: { OS: 'macOS', CPU: 'Intel' } };
jest.mocked(envinfo.run).mockResolvedValue(JSON.stringify(mockEnvInfo));
const cliVersionOutput = '1.0.0';
const errorMessage = `Cannot find module '@rnv/sdk-tizen'`;
ctx.cli = {
webosAres: '/path/to/webosAres',
};
jest.mocked(execCLI).mockResolvedValue(cliVersionOutput);
semver.coerce = jest.fn().mockReturnValue({ version: '1.0.0' });
jest.mock(
'@rnv/sdk-tizen',
() => {
throw new Error(errorMessage);
},
{ virtual: true }
);
jest.mock(
'@rnv/sdk-webos',
() => ({
checkAndConfigureWebosSdks: jest.fn(),
}),
{ virtual: true }
);

// WHEN
await expect(
taskInfo.fn?.({
ctx,
taskName: 'MOCK_taskName',
originTaskName: 'MOCK_originTaskName',
parentTaskName: 'MOCK_parentTaskName',
shouldSkip: false,
})
).resolves.toEqual(true);
// THEN
expect(envinfo.run).toHaveBeenCalled();
expect(execCLI).toHaveBeenCalledTimes(1);
expect(logDebug).toHaveBeenCalledWith(
`Error configuring sdk-tizen SDK: `,
expect.objectContaining({
message: expect.stringContaining(errorMessage),
})
);
expect(logToSummary).toHaveBeenCalledWith(expect.stringContaining('System:\n OS: macOS\n CPU: Intel\n'));
expect(logToSummary).toHaveBeenCalledWith(expect.stringContaining('WEBOS CLI: 1.0.0 - /path/to/webosAres\n'));
});
});
102 changes: 102 additions & 0 deletions packages/engine-core/src/tasks/global/taskInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { createTask, RnvTaskName, getContext, execCLI, logError, logDebug, logToSummary } from '@rnv/core';
import envinfo from 'envinfo';
import semver from 'semver';

export default createTask({
description: 'Get relevant version info about OS, toolchain and libraries',
fn: async () => {
const parsedInfo = await _getEnvironmentInfo();
await _checkAndConfigureSdks();
await _getCliVersions(parsedInfo);
logToSummary(_formatObject(parsedInfo));
return true;
},
task: RnvTaskName.info,
isGlobalScope: true,
});

const _checkAndConfigureSdks = async () => {
const moduleConfigs = [
{ moduleName: 'sdk-tizen', configureFunction: 'checkAndConfigureTizenSdks' },
{ moduleName: 'sdk-webos', configureFunction: 'checkAndConfigureWebosSdks' },
];
for (const config of moduleConfigs) {
const { moduleName, configureFunction } = config;
await _checkAndConfigureTargetSdk(moduleName, configureFunction);
}
};

const _checkAndConfigureTargetSdk = async (moduleName: string, configureFunction: string): Promise<void> => {
try {
const SDKModule = require(`@rnv/${moduleName}`);
await SDKModule[configureFunction]();
} catch (e) {
logDebug(`Error configuring ${moduleName} SDK: `, e);
}
};

const _getCliVersions = async (parsedInfo: any) => {
const c = getContext();
const cliVersions: { [key: string]: { version: string; path: string } } = {};

const addCliVersion = async (cli: string, command: string, path: string, cliName: string) => {
try {
const cliVersionOutput = await execCLI(cli, command);
const cliVersionNumber = semver.coerce(cliVersionOutput)?.version;
if (cliVersionNumber) {
cliVersions[`${cliName.replace('-', ' ').toUpperCase()}`] = { version: cliVersionNumber, path };
}
} catch (e) {
logDebug(`Error getting version for ${cliName}: `, e);
}
};

if (c.cli.webosAres) {
await addCliVersion('webosAres', '--version', c.cli.webosAres, 'WEBOS CLI');
}
if (c.cli.tizen) {
await addCliVersion('tizen', 'version', c.cli.tizen, 'TIZEN CLI');
}
if (Object.keys(cliVersions).length) {
parsedInfo.CLI = cliVersions;
}
};
const _formatObject = (obj: any, indent = 0) => {
let formattedString = '';
if (indent === 0) formattedString += '\n';
for (const key in obj) {
if (obj[key] && typeof obj[key] === 'object' && obj[key].version) {
formattedString +=
' '.repeat(indent) + `${key}: ${obj[key].version} ${obj[key].path ? `- ${obj[key].path}` : ''}\n`;
} else if (Array.isArray(obj[key])) {
formattedString += ' '.repeat(indent) + `${key}: ${obj[key].join(', ')}\n`;
} else if (typeof obj[key] === 'object') {
formattedString += ' '.repeat(indent) + `${key}:\n`;
formattedString += _formatObject(obj[key], indent + 2);
} else {
formattedString += ' '.repeat(indent) + `${key}: ${obj[key]}\n`;
}
}
return formattedString;
};

const _getEnvironmentInfo = async () => {
try {
const output = await envinfo.run(
{
System: ['OS', 'CPU', 'Memory', 'Shell'],
Binaries: ['Node', 'Yarn', 'npm', 'Watchman'],
Managers: ['CocoaPods'],
Languages: ['Ruby', 'Java'],
IDEs: ['Xcode', 'Android Studio'],
SDKs: ['iOS SDK', 'Android SDK'],
npmPackages: ['react', 'react-native', '@react-native-community/cli', 'rnv'],
npmGlobalPackages: ['*react-native*', 'rnv', 'lerna'],
},
{ json: true, duplicates: false }
);
return JSON.parse(output);
} catch (e) {
logError(e);
}
};
10 changes: 0 additions & 10 deletions packages/engine-core/src/tasks/global/taskStatus.ts

This file was deleted.

Loading
Loading