vscode/.eslint-plugin-local/tests/code-no-observable-get-in-reactive-context-test.ts
Henning Dieterichs dace6b32b3
Implements code-no-observable-get-in-reactive-context (#267526)
* Implements code-no-observable-get-in-reactive-context

* Improves code

* Fixes lint warnings
2025-09-20 20:29:58 +02:00

203 lines
5.9 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Test file to verify the code-no-observable-get-in-reactive-context ESLint rule works correctly
import { observableValue, derived, autorun } from '../../src/vs/base/common/observable.js';
export function testValidUsage() {
const obs = observableValue('test', 0);
// Valid: Using .read(reader) in derived
const validDerived = derived(reader => {
const value = obs.read(reader);
return value * 2;
});
// Valid: Using .read(reader) in autorun
autorun(rdr => {
const value = validDerived.read(rdr);
console.log('Value:', value);
});
// Valid: Using .get() outside reactive context
const outsideValue = obs.get();
console.log('Outside value:', outsideValue);
}
export function testInvalidUsage() {
const obs = observableValue('test', 0);
// Invalid: Using .get() in derived instead of .read(reader)
const invalidDerived = derived(rdr => {
// This should use obs.read(reader) instead
// eslint-disable-next-line local/code-no-observable-get-in-reactive-context
const value = obs.get();
// Use reader for something valid to avoid unused var warning
const validValue = obs.read(rdr);
obs.read(undefined);
return value * 2 + validValue;
});
// Invalid: Using .get() in autorun instead of .read(reader)
autorun(reader => {
// This should use invalidDerived.read(reader) instead
// eslint-disable-next-line local/code-no-observable-get-in-reactive-context
const value = invalidDerived.get();
// Use reader for something valid to avoid unused var warning
const validValue = obs.read(reader);
console.log('Value:', value, validValue);
});
// Invalid: Using .get() in derivedWithStore
derived(reader => {
// eslint-disable-next-line local/code-no-observable-get-in-reactive-context
const value = obs.get();
reader.store.add({ dispose: () => { } });
return value;
});
}
export function testComplexCases() {
const obs1 = observableValue('test1', 0);
const obs2 = observableValue('test2', 10);
// Invalid: Using .get() in conditional within derived
derived(reader => {
const initial = obs1.read(reader);
if (initial > 0) {
// eslint-disable-next-line local/code-no-observable-get-in-reactive-context
return obs2.get();
}
return initial;
});
// Invalid: Using .get() in nested function call within autorun
autorun(reader => {
const process = () => {
// eslint-disable-next-line local/code-no-observable-get-in-reactive-context
return obs1.get() + obs2.get();
};
// Use reader for something valid to avoid unused var warning
const validValue = obs1.read(reader);
const result = process();
console.log('Result:', result, validValue);
});
// Invalid: Using .get() in try-catch within derived
derived(reader => {
try {
// eslint-disable-next-line local/code-no-observable-get-in-reactive-context
const value = obs1.get();
// Use reader for something valid to avoid unused var warning
const validValue = obs2.read(reader);
return value * 2 + validValue;
} catch (e) {
return obs2.read(reader);
}
});
}
export function testValidComplexCases() {
const obs1 = observableValue('test1', 0);
const obs2 = observableValue('test2', 10);
// Valid: Proper usage with .read(reader)
derived(reader => {
const value1 = obs1.read(reader);
const value2 = obs2.read(undefined);
if (value1 > 0) {
return value2;
}
return value1;
});
// Valid: Using .get() outside reactive context
function processValues() {
const val1 = obs1.get();
const val2 = obs2.get();
return val1 + val2;
}
// Valid: Mixed usage - .read(reader) inside reactive, .get() outside
autorun(reader => {
const reactiveValue = obs1.read(reader);
const outsideValue = processValues();
console.log('Values:', reactiveValue, outsideValue);
});
}
export function testEdgeCases() {
const obs = observableValue('test', 0);
// Valid: Function with no reader parameter
derived(() => {
const value = obs.get();
return value;
});
// Invalid: Function with differently named parameter (now also flagged)
derived(_someOtherName => {
// eslint-disable-next-line local/code-no-observable-get-in-reactive-context
const value = obs.get();
return value;
});
// Invalid: Correctly named reader parameter
derived(reader => {
// eslint-disable-next-line local/code-no-observable-get-in-reactive-context
const value = obs.get();
// Use reader for something valid to avoid unused var warning
const validValue = obs.read(reader);
return value + validValue;
});
}
export function testQuickFixScenarios() {
const obs = observableValue('test', 0);
const obs2 = observableValue('test2', 10);
// These examples show what the quick fix should transform:
// Example 1: Simple case with 'reader' parameter name
derived(_reader => {
const value = obs.read(undefined); // This should be the auto-fix result
return value;
});
// Example 2: Different parameter name
derived(rdr => {
// Before fix: obs2.get()
// After fix: obs2.read(rdr)
const value = obs2.read(rdr); // This should be the auto-fix result
return value;
});
// Example 3: Complex expression
derived(ctx => {
// Before fix: (someCondition ? obs : obs2).get()
// After fix: (someCondition ? obs : obs2).read(ctx)
const someCondition = true;
const value = (someCondition ? obs : obs2).read(ctx); // This should be the auto-fix result
return value;
});
// Example 4: Multiple calls in same function
autorun(reader => {
// Before fix: obs.get() and obs2.get()
// After fix: obs.read(reader) and obs2.read(reader)
const val1 = obs.read(reader); // This should be the auto-fix result
const val2 = obs2.read(reader); // This should be the auto-fix result
console.log(val1, val2);
});
}