|
1 | 1 | import { expect, it, rs } from '@rstest/core'; |
2 | | -import type { StackState } from '../../../src/types'; |
| 2 | +import type { |
| 3 | + DetectionSnapshot, |
| 4 | + StackContext, |
| 5 | + StackState, |
| 6 | +} from '../../../src/types'; |
3 | 7 | import type { RslintOptions } from '../../../src/stacks/lint/Rslint'; |
| 8 | +import type { ResolvedCoreRuntime } from '../../../src/stacks/lint/CoreResolver'; |
4 | 9 | import { registerEditorProxy } from '../../../src/stacks/lint/worker/index'; |
5 | 10 |
|
6 | 11 | let refreshOutcome: |
7 | 12 | 'missing' | 'broken' | 'fixed' | 'changed' | 'changed-once' = 'missing'; |
8 | 13 |
|
9 | | -rs.mock('vscode', () => ({ |
10 | | - RelativePattern: class {}, |
11 | | - workspace: { |
12 | | - createFileSystemWatcher: () => ({ |
13 | | - onDidCreate() {}, |
14 | | - onDidChange() {}, |
15 | | - onDidDelete() {}, |
16 | | - }), |
| 14 | +rs.mock('vscode', () => { |
| 15 | + const api = { |
| 16 | + RelativePattern: class {}, |
| 17 | + workspace: { |
| 18 | + textDocuments: [], |
| 19 | + onDidChangeWorkspaceFolders: () => ({ dispose() {} }), |
| 20 | + onDidOpenTextDocument: () => ({ dispose() {} }), |
| 21 | + onDidCloseTextDocument: () => ({ dispose() {} }), |
| 22 | + createFileSystemWatcher: () => ({ |
| 23 | + onDidCreate() {}, |
| 24 | + onDidChange() {}, |
| 25 | + onDidDelete() {}, |
| 26 | + }), |
| 27 | + }, |
| 28 | + env: {}, |
| 29 | + }; |
| 30 | + return { ...api, default: api }; |
| 31 | +}); |
| 32 | +let runtimeFactory: (resolved: ResolvedCoreRuntime) => Rslint; |
| 33 | +rs.mock('../../../src/stacks/lint/RuntimeManager', () => ({ |
| 34 | + RuntimeManager: class { |
| 35 | + constructor( |
| 36 | + _router: unknown, |
| 37 | + _resolver: unknown, |
| 38 | + create: typeof runtimeFactory, |
| 39 | + ) { |
| 40 | + runtimeFactory = create; |
| 41 | + } |
| 42 | + initialize() {} |
| 43 | + clearResolutionCache() {} |
| 44 | + async reconcileOpenDocuments() {} |
17 | 45 | }, |
18 | | - env: {}, |
| 46 | +})); |
| 47 | +rs.mock('../../../src/stacks/lint/CoreResolver', () => ({ |
| 48 | + CoreResolver: class {}, |
| 49 | +})); |
| 50 | +rs.mock('../../../src/stacks/lint/ruleDocumentationProviders', () => ({ |
| 51 | + registerRuleDocumentationProviders: () => [], |
19 | 52 | })); |
20 | 53 | rs.mock('../../../src/shared/nodeExecutableSetting', () => ({ |
21 | 54 | getConfiguredNodeExecutable: () => undefined, |
@@ -89,6 +122,64 @@ rs.mock('vscode-languageclient/node', () => ({ |
89 | 122 | })); |
90 | 123 |
|
91 | 124 | import { Rslint } from '../../../src/stacks/lint/Rslint'; |
| 125 | +import { createRslintController } from '../../../src/stacks/lint'; |
| 126 | + |
| 127 | +it('updates a surviving bridge runtime attribution before the next config failure', async () => { |
| 128 | + const folder = { |
| 129 | + name: 'project', |
| 130 | + uri: { fsPath: '/project', toString: () => 'file:///project' }, |
| 131 | + }; |
| 132 | + const snapshot = (configPath: string): DetectionSnapshot => { |
| 133 | + const entry = { |
| 134 | + folder, |
| 135 | + rootRstackConfigPath: configPath, |
| 136 | + stacks: { rslint: { mode: 'bridged' } }, |
| 137 | + }; |
| 138 | + return { |
| 139 | + forFolder: () => entry, |
| 140 | + foldersFor: () => [entry], |
| 141 | + } as unknown as DetectionSnapshot; |
| 142 | + }; |
| 143 | + let onDetection!: (snapshot: DetectionSnapshot) => void; |
| 144 | + const warnings: string[] = []; |
| 145 | + const states: StackState[] = []; |
| 146 | + const controller = createRslintController(); |
| 147 | + await controller.register({ |
| 148 | + detection: snapshot('/project/rstack.config.js'), |
| 149 | + onDidChangeDetection: (listener: typeof onDetection) => { |
| 150 | + onDetection = listener; |
| 151 | + return { dispose() {} }; |
| 152 | + }, |
| 153 | + output: { warn: (message: string) => warnings.push(message) }, |
| 154 | + status: { report: (state: StackState) => states.push(state) }, |
| 155 | + } as unknown as StackContext); |
| 156 | + const shimPath = '/project/node_modules/rstack/dist/rslintConfig.js'; |
| 157 | + const runtime = runtimeFactory({ |
| 158 | + key: 'bridge', |
| 159 | + workspaceFolder: folder, |
| 160 | + installation: { |
| 161 | + mode: 'bridged', |
| 162 | + packageDirectory: '/project/core', |
| 163 | + shimPath, |
| 164 | + }, |
| 165 | + } as unknown as ResolvedCoreRuntime); |
| 166 | + |
| 167 | + onDetection(snapshot('/project/rstack.config.ts')); |
| 168 | + // Deliver the next worker verdict to the same runtime, not a replacement. |
| 169 | + ( |
| 170 | + runtime as unknown as { handleConfigDependencyStatus(value: unknown): void } |
| 171 | + ).handleConfigDependencyStatus({ |
| 172 | + kind: 'missing', |
| 173 | + failure: { configPath: shimPath, cause: "Cannot find package 'missing'" }, |
| 174 | + }); |
| 175 | + expect(states.at(-1)).toMatchObject({ |
| 176 | + kind: 'disabled', |
| 177 | + reason: expect.stringContaining('rstack.config.ts'), |
| 178 | + }); |
| 179 | + expect(warnings).toHaveLength(1); |
| 180 | + expect(warnings[0]).toContain('Cannot load rstack.config.ts:'); |
| 181 | + expect(warnings[0]).not.toContain('rstack.config.js'); |
| 182 | +}); |
92 | 183 |
|
93 | 184 | function createRuntime() { |
94 | 185 | const states: StackState[] = []; |
|
0 commit comments