@tevm/ts-plugin
Version:
A typescript plugin for tevm
141 lines (130 loc) • 4.8 kB
text/typescript
import ts from 'typescript/lib/tsserverlibrary.js'
import { type MockedFunction, describe, expect, it, vi } from 'vitest'
import { findContractDefinitionFileNameFromTevmNode } from './findContractDefinitionFileNameFromTevmNode.js'
import { findNode } from './findNode.js'
const mockContractFile = '/path/to/ContractDefinitionFile.sol'
// Mock TypeScript's LanguageService
const mockLanguageService = {
getDefinitionAtPosition: vi.fn((fileName: string, position: number) => [
{
fileName: mockContractFile,
},
]),
} as unknown as ts.LanguageService
describe('findContractDefinitionFileNameFromTevmNode', () => {
it('should find contract definition file name', () => {
const fileText = `import { viemClient } from './viemClient';
import { MyContract } from './MyContract.sol';
const readCall = viemClient.readContract(MyContract.read().someProperty());
const writeCall = MyContract.write().someWrite
const eventCall = MyContract.events().someEvent(5, 'foo')
`
const sourceFile = ts.createSourceFile(
'test.ts',
fileText,
{ languageVersion: ts.ScriptTarget.ESNext },
true,
ts.ScriptKind.TS,
)
const somePropertyNode = findNode(sourceFile, fileText.indexOf('someProperty()'))
const someWriteNode = findNode(sourceFile, fileText.indexOf('someWrite'))
const someEventNode = findNode(sourceFile, fileText.indexOf('someEvent'))
if (!somePropertyNode || !someWriteNode || !someEventNode) {
throw new Error('node is not valid')
}
expect(findContractDefinitionFileNameFromTevmNode(somePropertyNode, mockLanguageService, 'test.ts', ts)).toBe(
mockContractFile,
)
expect((mockLanguageService.getDefinitionAtPosition as MockedFunction<any>).mock.lastCall).toMatchInlineSnapshot(`
[
"test.ts",
131,
]
`)
expect(findContractDefinitionFileNameFromTevmNode(someWriteNode, mockLanguageService, 'test.ts', ts)).toBe(
mockContractFile,
)
expect((mockLanguageService.getDefinitionAtPosition as MockedFunction<any>).mock.lastCall).toMatchInlineSnapshot(`
[
"test.ts",
184,
]
`)
expect(findContractDefinitionFileNameFromTevmNode(someEventNode, mockLanguageService, 'test.ts', ts)).toBe(
mockContractFile,
)
expect((mockLanguageService.getDefinitionAtPosition as MockedFunction<any>).mock.lastCall).toMatchInlineSnapshot(`
[
"test.ts",
231,
]
`)
})
it('should return null if the node does not match criteria', () => {
const fileText = `
const notAContract = {
somethingElse: 42
};
notAContract.test0;
test1;
notAContract.test2();
notAContract.notread.test3();
write.eventreadwrite().test4();
write().test5(test4);
noContractInScope.read.test6();
`
const sourceFile = ts.createSourceFile('test.ts', fileText, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS)
;['test0', 'test1', 'test2', 'test3', 'test4', 'test5', 'test6'].forEach((testcase) => {
const node = findNode(sourceFile, fileText.indexOf(testcase))
if (!node) {
throw new Error('node is not valid')
}
const contractDefinitionFileName = findContractDefinitionFileNameFromTevmNode(
node,
mockLanguageService,
'test.ts',
ts,
)
expect(contractDefinitionFileName).toBeNull()
})
})
it('should handle no definition existing', () => {
const fileText = `
foo.read().test();
`
const sourceFile = ts.createSourceFile('test.ts', fileText, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS)
const node = findNode(sourceFile, fileText.indexOf('test'))
if (!node) {
throw new Error('node is not valid')
}
mockLanguageService.getDefinitionAtPosition = vi.fn(() => [])
let contractDefinitionFileName = findContractDefinitionFileNameFromTevmNode(
node,
mockLanguageService,
'test.ts',
ts,
)
expect(contractDefinitionFileName).toBeNull()
mockLanguageService.getDefinitionAtPosition = vi.fn(() => null as any)
contractDefinitionFileName = findContractDefinitionFileNameFromTevmNode(node, mockLanguageService, 'test.ts', ts)
expect(contractDefinitionFileName).toBeNull()
})
it('should return null if definition file is not a solidity file', () => {
const fileText = `import { MyContract } from './MyContract.js';
const res = MyContract.read().myMethod();
`
const sourceFile = ts.createSourceFile('test.ts', fileText, ts.ScriptTarget.ES2015, true, ts.ScriptKind.TS)
const node = findNode(sourceFile, fileText.indexOf('myMethod'))
if (!node) {
throw new Error('node is not valid')
}
mockLanguageService.getDefinitionAtPosition = () => [{ fileName: 'foo.js' }] as any
const contractDefinitionFileName = findContractDefinitionFileNameFromTevmNode(
node,
mockLanguageService,
'test.ts',
ts,
)
expect(contractDefinitionFileName).toBeNull()
})
})