Skip to content
This repository was archived by the owner on Oct 7, 2025. It is now read-only.
Merged
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
76 changes: 58 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@
},
"dependencies": {
"convert-array-to-csv": "^2.0.0",
"cosmiconfig": "^9.0.0",
"lightning-flow-scanner-core": "4.45.0",
"tabulator-tables": "^6.3.1",
"uuid": "^11.0.5",
Expand Down
8 changes: 8 additions & 0 deletions src/commands/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { CacheProvider } from '../providers/cache-provider';
import { testdata } from '../store/testdata';
import { OutputChannel } from '../providers/outputChannel';

const { USE_NEW_CONFIG: isUseNewConfig } = process.env;

export default class Commands {
constructor(private context: vscode.ExtensionContext) {}

Expand All @@ -30,6 +32,10 @@ export default class Commands {
}

private async configRules() {
if (isUseNewConfig) {
await this.ruleConfiguration();
return;
}
const allRules: core.IRuleDefinition[] = [
...core.getBetaRules(),
...core.getRules(),
Expand Down Expand Up @@ -94,6 +100,8 @@ export default class Commands {
);
}

private async ruleConfiguration() {}

private async debugView() {
let results = testdata as unknown as core.ScanResult[];
await CacheProvider.instance.set('results', results);
Expand Down
28 changes: 28 additions & 0 deletions src/providers/config-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { cosmiconfig, CosmiconfigResult } from 'cosmiconfig';
import { IRulesConfig } from 'lightning-flow-scanner-core';

export class ConfigProvider {
public async loadConfig(configPath?: string): Promise<IRulesConfig> {
const moduleName = 'flow-scanner';
const searchPlaces = [
'package.json',
`.${moduleName}.yaml`,
`.${moduleName}.yml`,
`.${moduleName}.json`,
`config/.${moduleName}.yaml`,
`config/.${moduleName}.yml`,
`.flow-scanner`,
];
const explorer = cosmiconfig(moduleName, {
searchPlaces,
});
let explorerResults: CosmiconfigResult;
if (configPath) {
// Forced config file name
explorerResults = await explorer.load(configPath);
}
// Let cosmiconfig look for a config file
explorerResults = explorerResults ?? (await explorer.search());
return explorerResults?.config ?? {};
}
}
14 changes: 10 additions & 4 deletions tests/commands/handlers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,25 @@ describe('Commands', () => {
CacheProvider.instance = instanceMock as any;

const outputMock = { logChannel: { debug: jest.fn() } };
const outputSpy = jest.spyOn(OutputChannel, 'getInstance'); //.getInstance = outputMock as any;
const outputSpy = jest.spyOn(OutputChannel, 'getInstance');
outputSpy.mockReturnValue(outputMock as any);

const extensionContext = jest.fn();
const command = new cmd.default(
extensionContext as unknown as ExtensionContext
);

await command['configRules']();
await expect(async () => await command['configRules']()).not.toThrow();
});

it('should read from configuration', async () => {});
it('should read from configuration', async () => {
const command = new cmd.default({} as any);
await expect(command['ruleConfiguration']()).resolves.not.toThrow();
});

it('should write to configuration', async () => {});
it('should write to configuration', async () => {
const command = new cmd.default({} as any);
await expect(command['ruleConfiguration']()).resolves.not.toThrow();
});
});
});
86 changes: 86 additions & 0 deletions tests/providers/config-provider.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { describe, it, expect, jest } from '@jest/globals';

import { ConfigProvider } from '../../src/providers/config-provider';
import * as cosmi from 'cosmiconfig';

jest.mock('cosmiconfig');

describe('Config-Provider', () => {
it('should be defined', () => {
expect(ConfigProvider).toBeDefined();
});

it('should not error when no config', async () => {
const cosmiMock = {
load: jest.fn(),
search: jest.fn(),
};
const cosmiSpy = jest.spyOn(cosmi, 'cosmiconfig');
cosmiSpy.mockReturnValue(cosmiMock as any);

const configProvider = new ConfigProvider();
await expect(configProvider.loadConfig()).resolves.toStrictEqual({});
});

it('should load config when directly passed from settings', async () => {
const cosmiMock = {
load: jest.fn().mockImplementation(() => ({
config: {
rules: {
FlowName: {
severity: 'error',
},
},
exceptions: {},
},
filepath: '',
})),
search: jest.fn(),
};
const cosmiSpy = jest.spyOn(cosmi, 'cosmiconfig');
cosmiSpy.mockReturnValue(cosmiMock as any);

const configProvider = new ConfigProvider();
await expect(
configProvider.loadConfig('some config')
).resolves.toStrictEqual({
rules: {
FlowName: {
severity: 'error',
},
},
exceptions: {},
});
expect(cosmiMock.search).not.toHaveBeenCalled();
});

it('should resolve a config via workspace directory', async () => {
const cosmiMock = {
load: jest.fn(),
search: jest.fn().mockImplementation(() => ({
config: {
rules: {
APIVersion: {
severity: 'error',
},
},
exceptions: {},
},
filepath: '',
})),
};
const cosmiSpy = jest.spyOn(cosmi, 'cosmiconfig');
cosmiSpy.mockReturnValue(cosmiMock as any);

const configProvider = new ConfigProvider();
await expect(configProvider.loadConfig()).resolves.toStrictEqual({
rules: {
APIVersion: {
severity: 'error',
},
},
exceptions: {},
});
expect(cosmiMock.load).not.toHaveBeenCalled();
});
});