🤖 Tests: AppsService
This commit is contained in:
parent
96555d884b
commit
20b32526ef
13 changed files with 2290 additions and 22 deletions
1892
package-lock.json
generated
Normal file
1892
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
9
packages/system-api/__mocks__/child_process.ts
Normal file
9
packages/system-api/__mocks__/child_process.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
const childProcess: { execFile: typeof execFile } = jest.genMockFromModule('child_process');
|
||||
|
||||
const execFile = (path: string, args: string[], thing: any, callback: Function) => {
|
||||
callback();
|
||||
};
|
||||
|
||||
childProcess.execFile = execFile;
|
||||
|
||||
module.exports = childProcess;
|
86
packages/system-api/__mocks__/fs.ts
Normal file
86
packages/system-api/__mocks__/fs.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import path from 'path';
|
||||
const fs: {
|
||||
__createMockFiles: typeof createMockFiles;
|
||||
readFileSync: typeof readFileSync;
|
||||
existsSync: typeof existsSync;
|
||||
writeFileSync: typeof writeFileSync;
|
||||
mkdirSync: typeof mkdirSync;
|
||||
rmSync: typeof rmSync;
|
||||
readdirSync: typeof readdirSync;
|
||||
copyFileSync: typeof copyFileSync;
|
||||
} = jest.genMockFromModule('fs');
|
||||
|
||||
let mockFiles = Object.create(null);
|
||||
|
||||
const createMockFiles = (newMockFiles: Record<string, string>) => {
|
||||
mockFiles = Object.create(null);
|
||||
|
||||
// Create folder tree
|
||||
for (const file in newMockFiles) {
|
||||
const dir = path.dirname(file);
|
||||
|
||||
if (!mockFiles[dir]) {
|
||||
mockFiles[dir] = [];
|
||||
}
|
||||
|
||||
mockFiles[dir].push(path.basename(file));
|
||||
mockFiles[file] = newMockFiles[file];
|
||||
}
|
||||
};
|
||||
|
||||
const readFileSync = (p: string) => {
|
||||
return mockFiles[p];
|
||||
};
|
||||
|
||||
const existsSync = (p: string) => {
|
||||
return mockFiles[p] !== undefined;
|
||||
};
|
||||
|
||||
const writeFileSync = (p: string, data: any) => {
|
||||
mockFiles[p] = data;
|
||||
};
|
||||
|
||||
const mkdirSync = (p: string) => {
|
||||
mockFiles[p] = Object.create(null);
|
||||
};
|
||||
|
||||
const rmSync = (p: string, options: { recursive: boolean }) => {
|
||||
if (options.recursive) {
|
||||
delete mockFiles[p];
|
||||
} else {
|
||||
delete mockFiles[p][Object.keys(mockFiles[p])[0]];
|
||||
}
|
||||
};
|
||||
|
||||
const readdirSync = (p: string) => {
|
||||
const files: string[] = [];
|
||||
|
||||
const depth = p.split('/').length;
|
||||
|
||||
Object.keys(mockFiles).forEach((file) => {
|
||||
if (file.startsWith(p)) {
|
||||
const fileDepth = file.split('/').length;
|
||||
|
||||
if (fileDepth === depth + 1) {
|
||||
files.push(file.split('/').pop() || '');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
const copyFileSync = (source: string, destination: string) => {
|
||||
mockFiles[destination] = mockFiles[source];
|
||||
};
|
||||
|
||||
fs.readdirSync = readdirSync;
|
||||
fs.existsSync = existsSync;
|
||||
fs.readFileSync = readFileSync;
|
||||
fs.writeFileSync = writeFileSync;
|
||||
fs.mkdirSync = mkdirSync;
|
||||
fs.rmSync = rmSync;
|
||||
fs.copyFileSync = copyFileSync;
|
||||
fs.__createMockFiles = createMockFiles;
|
||||
|
||||
module.exports = fs;
|
|
@ -3,5 +3,5 @@ module.exports = {
|
|||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/__tests__/**/*.test.ts'],
|
||||
setupFiles: ['dotenv/config'],
|
||||
setupFiles: ['<rootDir>/tests/dotenv-config.ts'],
|
||||
};
|
||||
|
|
|
@ -36,7 +36,8 @@
|
|||
"passport-http-bearer": "^1.0.1",
|
||||
"public-ip": "^5.0.0",
|
||||
"systeminformation": "^5.11.9",
|
||||
"tcp-port-used": "^1.0.2"
|
||||
"tcp-port-used": "^1.0.2",
|
||||
"mock-fs": "^5.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
|
@ -46,6 +47,7 @@
|
|||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^27.5.0",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/passport": "^1.0.7",
|
||||
"@types/passport-http-bearer": "^1.0.37",
|
||||
"@types/tcp-port-used": "^1.0.1",
|
||||
|
|
|
@ -7,11 +7,7 @@ interface IConfig {
|
|||
CLIENT_URLS: string[];
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
dotenv.config({ path: '.env.test' });
|
||||
} else {
|
||||
dotenv.config();
|
||||
}
|
||||
dotenv.config();
|
||||
|
||||
const { NODE_ENV = 'development', ROOT_FOLDER = '', JWT_SECRET = '', INTERNAL_IP = '' } = process.env;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ export type Maybe<T> = T | null | undefined;
|
|||
|
||||
export interface AppConfig {
|
||||
id: string;
|
||||
available: boolean;
|
||||
port: number;
|
||||
name: string;
|
||||
requirements?: {
|
||||
|
|
|
@ -1,7 +1,258 @@
|
|||
import AppsService from '../apps.service';
|
||||
import fs from 'fs';
|
||||
import config from '../../../config';
|
||||
import { AppConfig, FieldTypes } from '../../../config/types';
|
||||
import childProcess from 'child_process';
|
||||
|
||||
jest.mock('fs');
|
||||
jest.mock('child_process');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
const testApp: Partial<AppConfig> = {
|
||||
id: 'test-app',
|
||||
port: 3000,
|
||||
available: true,
|
||||
form_fields: {
|
||||
test: {
|
||||
type: FieldTypes.text,
|
||||
label: 'Test field',
|
||||
required: true,
|
||||
env_variable: 'TEST_FIELD',
|
||||
},
|
||||
test2: {
|
||||
type: FieldTypes.text,
|
||||
label: 'Test field 2',
|
||||
required: false,
|
||||
env_variable: 'TEST_FIELD_2',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const testApp2: Partial<AppConfig> = {
|
||||
available: true,
|
||||
id: 'test-app2',
|
||||
};
|
||||
|
||||
const MOCK_FILE_EMPTY = {
|
||||
[`${config.ROOT_FOLDER}/apps/test-app/config.json`]: JSON.stringify(testApp),
|
||||
[`${config.ROOT_FOLDER}/.env`]: 'TEST=test',
|
||||
[`${config.ROOT_FOLDER}/state/apps.json`]: '{"installed": ""}',
|
||||
};
|
||||
|
||||
const MOCK_FILE_INSTALLED = {
|
||||
[`${config.ROOT_FOLDER}/apps/test-app/config.json`]: JSON.stringify(testApp),
|
||||
[`${config.ROOT_FOLDER}/apps/test-app2/config.json`]: JSON.stringify(testApp2),
|
||||
[`${config.ROOT_FOLDER}/.env`]: 'TEST=test',
|
||||
[`${config.ROOT_FOLDER}/state/apps.json`]: '{"installed": "test-app"}',
|
||||
[`${config.ROOT_FOLDER}/app-data/test-app`]: '',
|
||||
[`${config.ROOT_FOLDER}/app-data/test-app/app.env`]: 'TEST=test\nAPP_PORT=3000\nTEST_FIELD=test',
|
||||
};
|
||||
|
||||
describe('Install app', () => {
|
||||
it('Should throw when app is not available', () => {
|
||||
expect(AppsService.installApp('not-available', {})).rejects.toThrow('App not-available not available');
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(MOCK_FILE_EMPTY);
|
||||
});
|
||||
|
||||
it('Should correctly generate env file for app', async () => {
|
||||
await AppsService.installApp('test-app', { test: 'test' });
|
||||
|
||||
const envFile = fs.readFileSync(`${config.ROOT_FOLDER}/app-data/test-app/app.env`).toString();
|
||||
|
||||
expect(envFile.trim()).toBe('TEST=test\nAPP_PORT=3000\nTEST_FIELD=test');
|
||||
});
|
||||
|
||||
it('Should add app to state file', async () => {
|
||||
await AppsService.installApp('test-app', { test: 'test' });
|
||||
|
||||
const stateFile = JSON.parse(fs.readFileSync(`${config.ROOT_FOLDER}/state/apps.json`).toString());
|
||||
|
||||
expect(stateFile.installed).toBe(' test-app');
|
||||
});
|
||||
|
||||
it('Should correctly run app script', async () => {
|
||||
const spy = jest.spyOn(childProcess, 'execFile');
|
||||
|
||||
await AppsService.installApp('test-app', { test: 'test' });
|
||||
|
||||
expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['install', 'test-app'], {}, expect.any(Function)]);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('Should start app if already installed', async () => {
|
||||
const spy = jest.spyOn(childProcess, 'execFile');
|
||||
|
||||
await AppsService.installApp('test-app', { test: 'test' });
|
||||
await AppsService.installApp('test-app', { test: 'test' });
|
||||
|
||||
expect(spy.mock.calls.length).toBe(2);
|
||||
expect(spy.mock.calls[0]).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['install', 'test-app'], {}, expect.any(Function)]);
|
||||
expect(spy.mock.calls[1]).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['start', 'test-app'], {}, expect.any(Function)]);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('Should throw if required form fields are missing', async () => {
|
||||
await expect(AppsService.installApp('test-app', {})).rejects.toThrowError('Variable test is required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Uninstall app', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(MOCK_FILE_INSTALLED);
|
||||
});
|
||||
|
||||
it('Should correctly remove app from state file', async () => {
|
||||
await AppsService.uninstallApp('test-app');
|
||||
|
||||
const stateFile = JSON.parse(fs.readFileSync(`${config.ROOT_FOLDER}/state/apps.json`).toString());
|
||||
|
||||
expect(stateFile.installed).toBe('');
|
||||
});
|
||||
|
||||
it('Should correctly run app script', async () => {
|
||||
const spy = jest.spyOn(childProcess, 'execFile');
|
||||
|
||||
await AppsService.uninstallApp('test-app');
|
||||
|
||||
expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['uninstall', 'test-app'], {}, expect.any(Function)]);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('Should throw if app is not installed', async () => {
|
||||
await expect(AppsService.uninstallApp('test-app-2')).rejects.toThrowError('App test-app-2 not installed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Start app', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(MOCK_FILE_INSTALLED);
|
||||
});
|
||||
|
||||
it('Should correctly run app script', async () => {
|
||||
const spy = jest.spyOn(childProcess, 'execFile');
|
||||
|
||||
await AppsService.startApp('test-app');
|
||||
|
||||
expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['start', 'test-app'], {}, expect.any(Function)]);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('Should throw if app is not installed', async () => {
|
||||
await expect(AppsService.startApp('test-app-2')).rejects.toThrowError('App test-app-2 not installed');
|
||||
});
|
||||
|
||||
it('Should restart if app is already running', async () => {
|
||||
const spy = jest.spyOn(childProcess, 'execFile');
|
||||
|
||||
await AppsService.startApp('test-app');
|
||||
expect(spy.mock.calls.length).toBe(1);
|
||||
await AppsService.startApp('test-app');
|
||||
expect(spy.mock.calls.length).toBe(2);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('Should throw if app is not installed', async () => {
|
||||
await expect(AppsService.startApp('test-app-2')).rejects.toThrowError('App test-app-2 not installed');
|
||||
});
|
||||
|
||||
it('Regenerate env file', async () => {
|
||||
fs.writeFile(`${config.ROOT_FOLDER}/app-data/test-app/app.env`, 'TEST=test\nAPP_PORT=3000', () => {});
|
||||
|
||||
await AppsService.startApp('test-app');
|
||||
|
||||
const envFile = fs.readFileSync(`${config.ROOT_FOLDER}/app-data/test-app/app.env`).toString();
|
||||
|
||||
expect(envFile.trim()).toBe('TEST=test\nAPP_PORT=3000\nTEST_FIELD=test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Stop app', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(MOCK_FILE_INSTALLED);
|
||||
});
|
||||
|
||||
it('Should correctly run app script', async () => {
|
||||
const spy = jest.spyOn(childProcess, 'execFile');
|
||||
|
||||
await AppsService.stopApp('test-app');
|
||||
|
||||
expect(spy.mock.lastCall).toEqual([`${config.ROOT_FOLDER}/scripts/app.sh`, ['stop', 'test-app'], {}, expect.any(Function)]);
|
||||
});
|
||||
|
||||
it('Should throw if app is not installed', async () => {
|
||||
await expect(AppsService.stopApp('test-app-2')).rejects.toThrowError('App test-app-2 not installed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update app config', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(MOCK_FILE_INSTALLED);
|
||||
});
|
||||
|
||||
it('Should correctly update app config', async () => {
|
||||
await AppsService.updateAppConfig('test-app', { test: 'test', test2: 'test2' });
|
||||
|
||||
const envFile = fs.readFileSync(`${config.ROOT_FOLDER}/app-data/test-app/app.env`).toString();
|
||||
|
||||
expect(envFile.trim()).toBe('TEST=test\nAPP_PORT=3000\nTEST_FIELD=test\nTEST_FIELD_2=test2');
|
||||
});
|
||||
|
||||
it('Should throw if app is not installed', async () => {
|
||||
await expect(AppsService.updateAppConfig('test-app-2', { test: 'test' })).rejects.toThrowError('App test-app-2 not installed');
|
||||
});
|
||||
|
||||
it('Should throw if required form fields are missing', async () => {
|
||||
await expect(AppsService.updateAppConfig('test-app', {})).rejects.toThrowError('Variable test is required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Get app config', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(MOCK_FILE_INSTALLED);
|
||||
});
|
||||
|
||||
it('Should correctly get app config', async () => {
|
||||
const appconfig = await AppsService.getAppInfo('test-app');
|
||||
|
||||
expect(appconfig).toEqual({ ...testApp, installed: true, status: 'stopped' });
|
||||
});
|
||||
|
||||
it('Should have installed false if app is not installed', async () => {
|
||||
const appconfig = await AppsService.getAppInfo('test-app2');
|
||||
|
||||
expect(appconfig).toEqual({ ...testApp2, installed: false, status: 'stopped' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('List apps', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(MOCK_FILE_INSTALLED);
|
||||
});
|
||||
|
||||
it('Should correctly list apps', async () => {
|
||||
const apps = await AppsService.listApps();
|
||||
|
||||
expect(apps).toEqual([
|
||||
{ ...testApp, installed: true, status: 'stopped' },
|
||||
{ ...testApp2, installed: false, status: 'stopped' },
|
||||
]);
|
||||
expect(apps.length).toBe(2);
|
||||
expect(apps[0].id).toBe('test-app');
|
||||
expect(apps[1].id).toBe('test-app2');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import portUsed from 'tcp-port-used';
|
||||
import p from 'p-iteration';
|
||||
import { AppConfig } from '../../config/types';
|
||||
import { fileExists, readFile, readJsonFile, runScript, writeFile } from '../fs/fs.helpers';
|
||||
import { fileExists, readdirSync, readFile, readJsonFile, runScript, writeFile } from '../fs/fs.helpers';
|
||||
import InternalIp from 'internal-ip';
|
||||
|
||||
type AppsState = { installed: string };
|
||||
|
@ -96,7 +96,7 @@ export const ensureAppState = (appName: string, installed: boolean) => {
|
|||
}
|
||||
} else {
|
||||
if (state.installed.indexOf(appName) !== -1) {
|
||||
state.installed = state.installed.replace(` ${appName}`, '');
|
||||
state.installed = state.installed.replace(`${appName}`, '');
|
||||
writeFile('/state/apps.json', JSON.stringify(state));
|
||||
}
|
||||
}
|
||||
|
@ -124,3 +124,21 @@ export const generateEnvFile = (appName: string, form: Record<string, string>) =
|
|||
export const getStateFile = (): AppsState => {
|
||||
return readJsonFile('/state/apps.json');
|
||||
};
|
||||
|
||||
export const getAvailableApps = (): string[] => {
|
||||
const apps: string[] = [];
|
||||
|
||||
const appsDir = readdirSync('/apps');
|
||||
|
||||
appsDir.forEach((app) => {
|
||||
if (fileExists(`/apps/${app}/config.json`)) {
|
||||
const configFile: AppConfig = readJsonFile(`/apps/${app}/config.json`);
|
||||
|
||||
if (configFile.available) {
|
||||
apps.push(app);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return apps;
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import si from 'systeminformation';
|
||||
import { appNames } from '../../config/apps';
|
||||
import { AppConfig } from '../../config/types';
|
||||
import { createFolder, fileExists, readJsonFile } from '../fs/fs.helpers';
|
||||
import { checkAppExists, checkAppRequirements, checkEnvFile, ensureAppState, generateEnvFile, getInitalFormValues, getStateFile, runAppScript } from './apps.helpers';
|
||||
import { checkAppExists, checkAppRequirements, checkEnvFile, ensureAppState, generateEnvFile, getAvailableApps, getInitalFormValues, getStateFile, runAppScript } from './apps.helpers';
|
||||
|
||||
const startApp = async (appName: string): Promise<void> => {
|
||||
checkAppExists(appName);
|
||||
|
@ -19,12 +18,6 @@ const startApp = async (appName: string): Promise<void> => {
|
|||
};
|
||||
|
||||
const installApp = async (id: string, form: Record<string, string>): Promise<void> => {
|
||||
const appIsAvailable = appNames.includes(id);
|
||||
|
||||
if (!appIsAvailable) {
|
||||
throw new Error(`App ${id} not available`);
|
||||
}
|
||||
|
||||
const appExists = fileExists(`/app-data/${id}`);
|
||||
|
||||
if (appExists) {
|
||||
|
@ -46,10 +39,12 @@ const installApp = async (id: string, form: Record<string, string>): Promise<voi
|
|||
// Run script
|
||||
await runAppScript(['install', id]);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const listApps = async (): Promise<AppConfig[]> => {
|
||||
const apps: AppConfig[] = appNames
|
||||
const apps: AppConfig[] = getAvailableApps()
|
||||
.map((app) => {
|
||||
try {
|
||||
return readJsonFile(`/apps/${app}/config.json`);
|
||||
|
|
|
@ -11,6 +11,8 @@ export const readJsonFile = (path: string): any => {
|
|||
|
||||
export const readFile = (path: string): string => fs.readFileSync(getAbsolutePath(path)).toString();
|
||||
|
||||
export const readdirSync = (path: string): string[] => fs.readdirSync(getAbsolutePath(path));
|
||||
|
||||
export const fileExists = (path: string): boolean => fs.existsSync(getAbsolutePath(path));
|
||||
|
||||
export const writeFile = (path: string, data: any) => fs.writeFileSync(getAbsolutePath(path), data);
|
||||
|
|
3
packages/system-api/tests/dotenv-config.ts
Normal file
3
packages/system-api/tests/dotenv-config.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import * as dotenv from 'dotenv';
|
||||
|
||||
dotenv.config({ path: '.env.test' });
|
|
@ -86,6 +86,7 @@ importers:
|
|||
'@types/express': ^4.17.13
|
||||
'@types/jest': ^27.5.0
|
||||
'@types/jsonwebtoken': ^8.5.8
|
||||
'@types/mock-fs': ^4.13.1
|
||||
'@types/passport': ^1.0.7
|
||||
'@types/passport-http-bearer': ^1.0.37
|
||||
'@types/tcp-port-used': ^1.0.1
|
||||
|
@ -109,6 +110,7 @@ importers:
|
|||
internal-ip: ^6.0.0
|
||||
jest: ^28.1.0
|
||||
jsonwebtoken: ^8.5.1
|
||||
mock-fs: ^5.1.2
|
||||
node-port-scanner: ^3.0.1
|
||||
nodemon: ^2.0.15
|
||||
p-iteration: ^1.1.8
|
||||
|
@ -131,6 +133,7 @@ importers:
|
|||
helmet: 5.0.2
|
||||
internal-ip: 6.2.0
|
||||
jsonwebtoken: 8.5.1
|
||||
mock-fs: 5.1.2
|
||||
node-port-scanner: 3.0.1
|
||||
p-iteration: 1.1.8
|
||||
passport: 0.5.2
|
||||
|
@ -147,6 +150,7 @@ importers:
|
|||
'@types/express': 4.17.13
|
||||
'@types/jest': 27.5.0
|
||||
'@types/jsonwebtoken': 8.5.8
|
||||
'@types/mock-fs': 4.13.1
|
||||
'@types/passport': 1.0.7
|
||||
'@types/passport-http-bearer': 1.0.37
|
||||
'@types/tcp-port-used': 1.0.1
|
||||
|
@ -2188,6 +2192,12 @@ packages:
|
|||
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
|
||||
dev: true
|
||||
|
||||
/@types/mock-fs/4.13.1:
|
||||
resolution: {integrity: sha512-m6nFAJ3lBSnqbvDZioawRvpLXSaPyn52Srf7OfzjubYbYX8MTUdIgDxQl0wEapm4m/pNYSd9TXocpQ0TvZFlYA==}
|
||||
dependencies:
|
||||
'@types/node': 17.0.31
|
||||
dev: true
|
||||
|
||||
/@types/node/17.0.31:
|
||||
resolution: {integrity: sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==}
|
||||
|
||||
|
@ -3968,8 +3978,6 @@ packages:
|
|||
dependencies:
|
||||
debug: 3.2.7
|
||||
resolve: 1.22.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/eslint-import-resolver-typescript/2.4.0_l3k33lf43msdtqtpwrwceacqke:
|
||||
|
@ -6080,6 +6088,11 @@ packages:
|
|||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/mock-fs/5.1.2:
|
||||
resolution: {integrity: sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
dev: false
|
||||
|
||||
/ms/2.0.0:
|
||||
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
|
||||
|
||||
|
|
Loading…
Reference in a new issue