|
1 | | -import { describe, it, expect, jest } from '@jest/globals'; |
| 1 | +import { describe, it, expect, jest, beforeEach } from '@jest/globals'; |
2 | 2 |
|
3 | 3 | import { ConfigProvider } from '../../src/providers/config-provider'; |
4 | | -import * as cosmi from 'cosmiconfig'; |
5 | 4 |
|
6 | | -jest.mock('cosmiconfig'); |
| 5 | +import * as yml from 'yaml'; |
| 6 | +import * as vsce from 'vscode'; |
| 7 | + |
| 8 | +jest.mock('yaml'); |
| 9 | +jest.mock('vscode'); |
7 | 10 |
|
8 | 11 | describe('Config-Provider', () => { |
| 12 | + let provider: ConfigProvider; |
| 13 | + const mockFs: any = {}; |
| 14 | + const mockWorkspace: any = {}; |
| 15 | + const mockUri = (path: string) => ({ fsPath: path, path }); |
| 16 | + |
| 17 | + beforeEach(() => { |
| 18 | + provider = new ConfigProvider(); |
| 19 | + jest.clearAllMocks(); |
| 20 | + // Mock VSCE |
| 21 | + mockFs.stat = jest.fn(); |
| 22 | + mockFs.readFile = jest.fn(); |
| 23 | + mockFs.writeFile = jest.fn(); |
| 24 | + (vsce.workspace.fs as any).stat = mockFs.stat; |
| 25 | + (vsce.workspace.fs as any).readFile = mockFs.readFile; |
| 26 | + (vsce.workspace.fs as any).writeFile = mockFs.writeFile; |
| 27 | + if (vsce.Uri) { |
| 28 | + (vsce.Uri as any).file = mockUri; |
| 29 | + } |
| 30 | + // Mock YAML |
| 31 | + jest.spyOn(yml, 'parse').mockImplementation(() => jest.fn()); |
| 32 | + jest.spyOn(yml, 'Document').mockImplementation((c: any) => c); |
| 33 | + // TextEncoder |
| 34 | + (global as any).TextEncoder = class { |
| 35 | + encode(str: string) { |
| 36 | + return Buffer.from(str); |
| 37 | + } |
| 38 | + }; |
| 39 | + }); |
| 40 | + |
9 | 41 | it('should be defined', () => { |
10 | 42 | expect(ConfigProvider).toBeDefined(); |
11 | 43 | }); |
12 | 44 |
|
13 | | - it('should not error when no config', async () => { |
14 | | - const cosmiMock = { |
15 | | - load: jest.fn(), |
16 | | - search: jest.fn(), |
17 | | - }; |
18 | | - const cosmiSpy = jest.spyOn(cosmi, 'cosmiconfig'); |
19 | | - cosmiSpy.mockReturnValue(cosmiMock as any); |
| 45 | + it('discover: finds JSON config', async () => { |
| 46 | + mockFs.stat.mockResolvedValueOnce(true); |
| 47 | + mockFs.readFile.mockResolvedValueOnce(Buffer.from('{"foo":1}')); |
| 48 | + (yml as any).parse.mockReturnValue({ foo: 1 }); |
| 49 | + const config = await provider.discover('/path'); |
| 50 | + expect(config).toBeDefined(); |
| 51 | + expect(config.config).toEqual({ foo: 1 }); |
| 52 | + }); |
20 | 53 |
|
21 | | - const configProvider = new ConfigProvider(); |
22 | | - await expect(configProvider.loadConfig()).resolves.toStrictEqual({}); |
| 54 | + it('discover: finds YAML config', async () => { |
| 55 | + mockFs.stat.mockRejectedValueOnce(new Error('not found')); |
| 56 | + mockFs.stat.mockRejectedValueOnce(new Error('not found')); |
| 57 | + mockFs.stat.mockResolvedValueOnce(true); |
| 58 | + mockFs.readFile.mockResolvedValueOnce(Buffer.from('bar: 2')); |
| 59 | + (yml as any).parse.mockReturnValue({ bar: 2 }); |
| 60 | + const config = await provider.discover('/path'); |
| 61 | + expect(config).toBeDefined(); |
| 62 | + expect(config.config).toEqual({ bar: 2 }); |
23 | 63 | }); |
24 | 64 |
|
25 | | - it('should load config when directly passed from settings', async () => { |
26 | | - const cosmiMock = { |
27 | | - load: jest.fn().mockImplementation(() => ({ |
28 | | - config: { |
29 | | - rules: { |
30 | | - FlowName: { |
31 | | - severity: 'error', |
32 | | - }, |
33 | | - }, |
34 | | - exceptions: {}, |
35 | | - }, |
36 | | - filepath: '', |
37 | | - })), |
38 | | - search: jest.fn(), |
39 | | - }; |
40 | | - const cosmiSpy = jest.spyOn(cosmi, 'cosmiconfig'); |
41 | | - cosmiSpy.mockReturnValue(cosmiMock as any); |
| 65 | + it('discover: creates new config if not found', async () => { |
| 66 | + mockFs.stat.mockRejectedValue(new Error('not found')); |
| 67 | + mockFs.writeFile.mockResolvedValueOnce(undefined); |
| 68 | + const config = await provider.discover('/path'); |
| 69 | + expect(config).toBeDefined(); |
| 70 | + expect(config.fspath).toContain('/path/.flow-scanner.yml'); |
| 71 | + expect(mockFs.writeFile).toHaveBeenCalled(); |
| 72 | + }); |
42 | 73 |
|
43 | | - const configProvider = new ConfigProvider(); |
44 | | - await expect( |
45 | | - configProvider.loadConfig('some config') |
46 | | - ).resolves.toStrictEqual({ |
47 | | - rules: { |
48 | | - FlowName: { |
49 | | - severity: 'error', |
50 | | - }, |
51 | | - }, |
52 | | - exceptions: {}, |
53 | | - }); |
54 | | - expect(cosmiMock.search).not.toHaveBeenCalled(); |
| 74 | + it('writeConfigFile: writes a new config file', async () => { |
| 75 | + mockFs.writeFile.mockResolvedValueOnce(undefined); |
| 76 | + const result = await (provider as any).writeConfigFile( |
| 77 | + 'flow-scanner', |
| 78 | + '/base' |
| 79 | + ); |
| 80 | + expect(result.fspath).toContain('/base/.flow-scanner.yml'); |
| 81 | + expect(mockFs.writeFile).toHaveBeenCalled(); |
55 | 82 | }); |
56 | 83 |
|
57 | | - it('should resolve a config via workspace directory', async () => { |
58 | | - const cosmiMock = { |
59 | | - load: jest.fn(), |
60 | | - search: jest.fn().mockImplementation(() => ({ |
61 | | - config: { |
62 | | - rules: { |
63 | | - APIVersion: { |
64 | | - severity: 'error', |
65 | | - }, |
66 | | - }, |
67 | | - exceptions: {}, |
68 | | - }, |
69 | | - filepath: '', |
70 | | - })), |
71 | | - }; |
72 | | - const cosmiSpy = jest.spyOn(cosmi, 'cosmiconfig'); |
73 | | - cosmiSpy.mockReturnValue(cosmiMock as any); |
| 84 | + it('attemptToReadConfig: returns config if file exists', async () => { |
| 85 | + mockFs.stat.mockResolvedValueOnce(true); |
| 86 | + mockFs.readFile.mockResolvedValueOnce(Buffer.from('{"a":3}')); |
| 87 | + const parser = jest.fn().mockReturnValue({ a: 3 }); |
| 88 | + const result = await (provider as any).attemptToReadConfig( |
| 89 | + '/foo', |
| 90 | + ['bar.json'], |
| 91 | + parser |
| 92 | + ); |
| 93 | + expect(result).toBeDefined(); |
| 94 | + expect(result.config).toEqual({ a: 3 }); |
| 95 | + }); |
| 96 | + |
| 97 | + it('attemptToReadConfig: returns null if file does not exist', async () => { |
| 98 | + mockFs.stat.mockRejectedValue(new Error('not found')); |
| 99 | + const parser = jest.fn(); |
| 100 | + const result = await (provider as any).attemptToReadConfig( |
| 101 | + '/foo', |
| 102 | + ['bar.json'], |
| 103 | + parser |
| 104 | + ); |
| 105 | + expect(result).toBeUndefined(); |
| 106 | + }); |
74 | 107 |
|
75 | | - const configProvider = new ConfigProvider(); |
76 | | - await expect(configProvider.loadConfig()).resolves.toStrictEqual({ |
77 | | - rules: { |
78 | | - APIVersion: { |
79 | | - severity: 'error', |
80 | | - }, |
81 | | - }, |
82 | | - exceptions: {}, |
83 | | - }); |
84 | | - expect(cosmiMock.load).not.toHaveBeenCalled(); |
| 108 | + it('loadConfig: returns config from discover', async () => { |
| 109 | + const fakeConfig = { rules: { test: { severity: 'error' } } }; |
| 110 | + jest |
| 111 | + .spyOn(provider, 'discover') |
| 112 | + .mockResolvedValueOnce({ fspath: '/f', config: fakeConfig }); |
| 113 | + const result = await provider.loadConfig('/some'); |
| 114 | + expect(result).toEqual(fakeConfig); |
85 | 115 | }); |
86 | 116 | }); |
0 commit comments