From 1f66d156b5fdfe0c5ecf137c97ce1008452372a2 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Mon, 20 Jan 2025 03:12:52 -0500 Subject: [PATCH] test: update tests --- lib/binding_web/test/helper.ts | 10 +- lib/binding_web/test/language.test.ts | 8 +- lib/binding_web/test/node.test.ts | 366 ++++++++++++-------------- lib/binding_web/test/parser.test.ts | 56 ++-- lib/binding_web/test/query.test.ts | 36 +-- lib/binding_web/test/tree.test.ts | 30 +-- 6 files changed, 234 insertions(+), 272 deletions(-) diff --git a/lib/binding_web/test/helper.ts b/lib/binding_web/test/helper.ts index 2c4ecdbd..15a4d02c 100644 --- a/lib/binding_web/test/helper.ts +++ b/lib/binding_web/test/helper.ts @@ -1,12 +1,6 @@ -import { type Parser as ParserType, type Language as LanguageType } from '../src'; +import { Parser, Language } from '../src'; import path from 'path'; -// @ts-expect-error We're intentionally importing ../tree-sitter.js -import { Parser as ParserImpl, Language as LanguageImpl } from '..'; - -const Parser = ParserImpl as typeof ParserType; -const Language = LanguageImpl as typeof LanguageType; - // https://github.com/tree-sitter/tree-sitter/blob/master/xtask/src/fetch.rs#L15 export type LanguageName = 'bash' | 'c' | 'cpp' | 'embedded-template' | 'go' | 'html' | 'java' | 'javascript' | 'jsdoc' | 'json' | 'php' | 'python' | 'ruby' | 'rust' | 'typescript' | 'tsx'; @@ -16,8 +10,6 @@ function languageURL(name: LanguageName): string { } export default Parser.init().then(async () => ({ - Parser, - Language, languageURL, C: await Language.load(languageURL('c')), EmbeddedTemplate: await Language.load(languageURL('embedded-template')), diff --git a/lib/binding_web/test/language.test.ts b/lib/binding_web/test/language.test.ts index 126aa872..9d5c49de 100644 --- a/lib/binding_web/test/language.test.ts +++ b/lib/binding_web/test/language.test.ts @@ -1,8 +1,8 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import helper from './helper'; -import type { Parser as ParserType, LookaheadIterator, Language } from '../src'; +import type { LookaheadIterator, Language } from '../src'; +import { Parser } from '../src'; -let Parser: typeof ParserType; let JavaScript: Language; let Rust: Language; @@ -136,10 +136,10 @@ describe('Lookahead iterator', () => { let state: number; beforeAll(async () => { - ({ Parser, JavaScript } = await helper); + ({ JavaScript } = await helper); const parser = new Parser(); parser.setLanguage(JavaScript); - const tree = parser.parse('function fn() {}'); + const tree = parser.parse('function fn() {}')!; parser.delete(); const cursor = tree.walk(); expect(cursor.gotoFirstChild()).toBe(true); diff --git a/lib/binding_web/test/node.test.ts b/lib/binding_web/test/node.test.ts index a5d08d3b..51303808 100644 --- a/lib/binding_web/test/node.test.ts +++ b/lib/binding_web/test/node.test.ts @@ -1,8 +1,8 @@ import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest'; -import type { Parser as ParserType, Language, Tree, Node } from '../src'; +import type { Language, Tree, Node } from '../src'; +import { Parser } from '../src'; import helper from './helper'; -let Parser: typeof ParserType; let C: Language; let JavaScript: Language; let JSON: Language; @@ -40,11 +40,11 @@ function getAllNodes(tree: Tree): Node[] { } describe('Node', () => { - let parser: ParserType; + let parser: Parser; let tree: Tree | null; beforeAll(async () => { - ({ Parser, C, EmbeddedTemplate, JavaScript, JSON, Python } = await helper); + ({ C, EmbeddedTemplate, JavaScript, JSON, Python } = await helper); }); beforeEach(() => { @@ -55,31 +55,24 @@ describe('Node', () => { afterEach(() => { parser.delete(); - tree?.delete(); + tree!.delete(); }); describe('.children', () => { it('returns an array of child nodes', () => { - tree = parser.parse('x10 + 1000'); + tree = parser.parse('x10 + 1000')!; expect(tree.rootNode.children).toHaveLength(1); - const sumNode = tree.rootNode.firstChild?.firstChild; - expect(sumNode?.children.map(child => child?.type)).toEqual([ - 'identifier', - '+', - 'number' - ]); + const sumNode = tree.rootNode.firstChild!.firstChild!; + expect(sumNode.children.map(child => child!.type)).toEqual(['identifier', '+', 'number' ]); }); }); describe('.namedChildren', () => { it('returns an array of named child nodes', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild?.firstChild; + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; expect(tree.rootNode.namedChildren).toHaveLength(1); - expect(sumNode?.namedChildren.map(child => child?.type)).toEqual([ - 'identifier', - 'number' - ]); + expect(sumNode.namedChildren.map(child => child!.type)).toEqual(['identifier', 'number']); }); }); @@ -96,13 +89,13 @@ describe('Node', () => { elif four: d()`; - tree = parser.parse(source); - const node = tree.rootNode.firstChild; - expect(node?.type).toBe('if_statement'); - const alternatives = node?.childrenForFieldName('alternative'); - const alternativeTexts = alternatives?.map(n => { - const condition = n?.childForFieldName('condition'); - return source.slice(condition?.startIndex, condition?.endIndex); + tree = parser.parse(source)!; + const node = tree.rootNode.firstChild!; + expect(node.type).toBe('if_statement'); + const alternatives = node.childrenForFieldName('alternative'); + const alternativeTexts = alternatives.map(n => { + const condition = n!.childForFieldName('condition')!; + return source.slice(condition.startIndex, condition.endIndex); }); expect(alternativeTexts).toEqual(['two', 'three', 'four']); }); @@ -110,30 +103,30 @@ describe('Node', () => { describe('.startIndex and .endIndex', () => { it('returns the character index where the node starts/ends in the text', () => { - tree = parser.parse('a👍👎1 / b👎c👎'); - const quotientNode = tree.rootNode.firstChild?.firstChild; + tree = parser.parse('a👍👎1 / b👎c👎')!; + const quotientNode = tree.rootNode.firstChild!.firstChild!; - expect(quotientNode?.startIndex).toBe(0); - expect(quotientNode?.endIndex).toBe(15); - expect(quotientNode?.children.map(child => child?.startIndex)).toEqual([0, 7, 9]); - expect(quotientNode?.children.map(child => child?.endIndex)).toEqual([6, 8, 15]); + expect(quotientNode.startIndex).toBe(0); + expect(quotientNode.endIndex).toBe(15); + expect(quotientNode.children.map(child => child!.startIndex)).toEqual([0, 7, 9]); + expect(quotientNode.children.map(child => child!.endIndex)).toEqual([6, 8, 15]); }); }); describe('.startPosition and .endPosition', () => { it('returns the row and column where the node starts/ends in the text', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild?.firstChild; - expect(sumNode?.type).toBe('binary_expression'); + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; + expect(sumNode.type).toBe('binary_expression'); - expect(sumNode?.startPosition).toEqual({ row: 0, column: 0 }); - expect(sumNode?.endPosition).toEqual({ row: 0, column: 10 }); - expect(sumNode?.children.map((child) => child?.startPosition)).toEqual([ + expect(sumNode.startPosition).toEqual({ row: 0, column: 0 }); + expect(sumNode.endPosition).toEqual({ row: 0, column: 10 }); + expect(sumNode.children.map((child) => child!.startPosition)).toEqual([ { row: 0, column: 0 }, { row: 0, column: 4 }, { row: 0, column: 6 }, ]); - expect(sumNode?.children.map((child) => child?.endPosition)).toEqual([ + expect(sumNode.children.map((child) => child!.endPosition)).toEqual([ { row: 0, column: 3 }, { row: 0, column: 5 }, { row: 0, column: 10 }, @@ -141,9 +134,9 @@ describe('Node', () => { }); it('handles characters that occupy two UTF16 code units', () => { - tree = parser.parse('a👍👎1 /\n b👎c👎'); - const sumNode = tree.rootNode.firstChild?.firstChild; - expect(sumNode?.children.map(child => [child?.startPosition, child?.endPosition])).toEqual([ + tree = parser.parse('a👍👎1 /\n b👎c👎')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; + expect(sumNode.children.map(child => [child!.startPosition, child!.endPosition])).toEqual([ [{ row: 0, column: 0 }, { row: 0, column: 6 }], [{ row: 0, column: 7 }, { row: 0, column: 8 }], [{ row: 1, column: 1 }, { row: 1, column: 7 }] @@ -153,75 +146,75 @@ describe('Node', () => { describe('.parent', () => { it('returns the node\'s parent', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild; - const variableNode = sumNode?.firstChild; - expect(sumNode?.id).not.toBe(variableNode?.id); - expect(sumNode?.id).toBe(variableNode?.parent?.id); - expect(tree.rootNode.id).toBe(sumNode?.parent?.id); + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!; + const variableNode = sumNode.firstChild!; + expect(sumNode.id).not.toBe(variableNode.id); + expect(sumNode.id).toBe(variableNode.parent!.id); + expect(tree.rootNode.id).toBe(sumNode.parent!.id); }); }); describe('.child(), .firstChild, .lastChild', () => { it('returns null when the node has no children', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild?.firstChild; - const variableNode = sumNode?.firstChild; - expect(variableNode?.firstChild).toBeNull(); - expect(variableNode?.lastChild).toBeNull(); - expect(variableNode?.firstNamedChild).toBeNull(); - expect(variableNode?.lastNamedChild).toBeNull(); - expect(variableNode?.child(1)).toBeNull(); + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; + const variableNode = sumNode.firstChild!; + expect(variableNode.firstChild).toBeNull(); + expect(variableNode.lastChild).toBeNull(); + expect(variableNode.firstNamedChild).toBeNull(); + expect(variableNode.lastNamedChild).toBeNull(); + expect(variableNode.child(1)).toBeNull(); }); }); describe('.childForFieldName()', () => { it('returns node for the given field name', () => { - tree = parser.parse('class A { b() {} }'); + tree = parser.parse('class A { b() {} }')!; - const classNode = tree.rootNode.firstChild; - expect(classNode?.type).toBe('class_declaration'); + const classNode = tree.rootNode.firstChild!; + expect(classNode.type).toBe('class_declaration'); - const classNameNode = classNode?.childForFieldName('name'); - expect(classNameNode?.type).toBe('identifier'); - expect(classNameNode?.text).toBe('A'); + const classNameNode = classNode.childForFieldName('name')!; + expect(classNameNode.type).toBe('identifier'); + expect(classNameNode.text).toBe('A'); - const bodyNode = classNode?.childForFieldName('body'); - expect(bodyNode?.type).toBe('class_body'); - expect(bodyNode?.text).toBe('{ b() {} }'); + const bodyNode = classNode.childForFieldName('body')!; + expect(bodyNode.type).toBe('class_body'); + expect(bodyNode.text).toBe('{ b() {} }'); - const methodNode = bodyNode?.firstNamedChild; - expect(methodNode?.type).toBe('method_definition'); - expect(methodNode?.text).toBe('b() {}'); + const methodNode = bodyNode.firstNamedChild!; + expect(methodNode.type).toBe('method_definition'); + expect(methodNode.text).toBe('b() {}'); }); }); describe('.nextSibling and .previousSibling', () => { it('returns the node\'s next and previous sibling', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild?.firstChild; - expect(sumNode?.children[1]?.id).toBe(sumNode?.children[0]?.nextSibling?.id); - expect(sumNode?.children[2]?.id).toBe(sumNode?.children[1]?.nextSibling?.id); - expect(sumNode?.children[0]?.id).toBe(sumNode?.children[1]?.previousSibling?.id); - expect(sumNode?.children[1]?.id).toBe(sumNode?.children[2]?.previousSibling?.id); + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; + expect(sumNode.children[1]!.id).toBe(sumNode.children[0]!.nextSibling!.id); + expect(sumNode.children[2]!.id).toBe(sumNode.children[1]!.nextSibling!.id); + expect(sumNode.children[0]!.id).toBe(sumNode.children[1]!.previousSibling!.id); + expect(sumNode.children[1]!.id).toBe(sumNode.children[2]!.previousSibling!.id); }); }); describe('.nextNamedSibling and .previousNamedSibling', () => { it('returns the node\'s next and previous named sibling', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild?.firstChild; - expect(sumNode?.namedChildren[1]?.id).toBe(sumNode?.namedChildren[0]?.nextNamedSibling?.id); - expect(sumNode?.namedChildren[0]?.id).toBe(sumNode?.namedChildren[1]?.previousNamedSibling?.id); + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; + expect(sumNode.namedChildren[1]!.id).toBe(sumNode.namedChildren[0]!.nextNamedSibling!.id); + expect(sumNode.namedChildren[0]!.id).toBe(sumNode.namedChildren[1]!.previousNamedSibling!.id); }); }); describe('.descendantForIndex(min, max)', () => { it('returns the smallest node that spans the given range', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild?.firstChild; - expect(sumNode?.descendantForIndex(1, 2)?.type).toBe('identifier'); - expect(sumNode?.descendantForIndex(4, 4)?.type).toBe('+'); + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; + expect(sumNode.descendantForIndex(1, 2)!.type).toBe('identifier'); + expect(sumNode.descendantForIndex(4, 4)!.type).toBe('+'); expect(() => { // @ts-expect-error Testing invalid arguments @@ -237,28 +230,20 @@ describe('Node', () => { describe('.namedDescendantForIndex', () => { it('returns the smallest named node that spans the given range', () => { - tree = parser.parse('x10 + 1000'); + tree = parser.parse('x10 + 1000')!; const sumNode = tree.rootNode.firstChild!; - expect(sumNode.descendantForIndex(1, 2)?.type).toBe('identifier'); - expect(sumNode.descendantForIndex(4, 4)?.type).toBe('+'); + expect(sumNode.descendantForIndex(1, 2)!.type).toBe('identifier'); + expect(sumNode.descendantForIndex(4, 4)!.type).toBe('+'); }); }); describe('.descendantForPosition', () => { it('returns the smallest node that spans the given range', () => { - tree = parser.parse('x10 + 1000'); + tree = parser.parse('x10 + 1000')!; const sumNode = tree.rootNode.firstChild!; - expect( - sumNode.descendantForPosition( - { row: 0, column: 1 }, - { row: 0, column: 2 } - )?.type - ).toBe('identifier'); - - expect( - sumNode.descendantForPosition({ row: 0, column: 4 })?.type - ).toBe('+'); + expect(sumNode.descendantForPosition({ row: 0, column: 1 }, { row: 0, column: 2 })!.type).toBe('identifier'); + expect(sumNode.descendantForPosition({ row: 0, column: 4 })!.type).toBe('+'); expect(() => { // @ts-expect-error Testing invalid arguments @@ -274,75 +259,67 @@ describe('Node', () => { describe('.namedDescendantForPosition(min, max)', () => { it('returns the smallest named node that spans the given range', () => { - tree = parser.parse('x10 + 1000'); + tree = parser.parse('x10 + 1000')!; const sumNode = tree.rootNode.firstChild!; - expect( - sumNode.namedDescendantForPosition( - { row: 0, column: 1 }, - { row: 0, column: 2 }, - )?.type - ).toBe('identifier') - - expect( - sumNode.namedDescendantForPosition({ row: 0, column: 4 })?.type - ).toBe('binary_expression'); + expect(sumNode.namedDescendantForPosition({ row: 0, column: 1 }, { row: 0, column: 2 })!.type).toBe('identifier') + expect(sumNode.namedDescendantForPosition({ row: 0, column: 4 })!.type).toBe('binary_expression'); }); }); describe('.hasError', () => { it('returns true if the node contains an error', () => { - tree = parser.parse('1 + 2 * * 3'); + tree = parser.parse('1 + 2 * * 3')!; const node = tree.rootNode; expect(node.toString()).toBe( '(program (expression_statement (binary_expression left: (number) right: (binary_expression left: (number) (ERROR) right: (number)))))' ); - const sum = node.firstChild?.firstChild; - expect(sum?.hasError).toBe(true); - expect(sum?.children[0]?.hasError).toBe(false); - expect(sum?.children[1]?.hasError).toBe(false); - expect(sum?.children[2]?.hasError).toBe(true); + const sum = node.firstChild!.firstChild!; + expect(sum.hasError).toBe(true); + expect(sum.children[0]!.hasError).toBe(false); + expect(sum.children[1]!.hasError).toBe(false); + expect(sum.children[2]!.hasError).toBe(true); }); }); describe('.isError', () => { it('returns true if the node is an error', () => { - tree = parser.parse('2 * * 3'); + tree = parser.parse('2 * * 3')!; const node = tree.rootNode; expect(node.toString()).toBe( '(program (expression_statement (binary_expression left: (number) (ERROR) right: (number))))' ); - const multi = node.firstChild?.firstChild; - expect(multi?.hasError).toBe(true); - expect(multi?.children[0]?.isError).toBe(false); - expect(multi?.children[1]?.isError).toBe(false); - expect(multi?.children[2]?.isError).toBe(true); - expect(multi?.children[3]?.isError).toBe(false); + const multi = node.firstChild!.firstChild!; + expect(multi.hasError).toBe(true); + expect(multi.children[0]!.isError).toBe(false); + expect(multi.children[1]!.isError).toBe(false); + expect(multi.children[2]!.isError).toBe(true); + expect(multi.children[3]!.isError).toBe(false); }); }); describe('.isMissing', () => { it('returns true if the node was inserted via error recovery', () => { - tree = parser.parse('(2 ||)'); + tree = parser.parse('(2 ||)')!; const node = tree.rootNode; expect(node.toString()).toBe( '(program (expression_statement (parenthesized_expression (binary_expression left: (number) right: (MISSING identifier)))))' ); - const sum = node.firstChild?.firstChild?.firstNamedChild; - expect(sum?.type).toBe('binary_expression'); - expect(sum?.hasError).toBe(true); - expect(sum?.children[0]?.isMissing).toBe(false); - expect(sum?.children[1]?.isMissing).toBe(false); - expect(sum?.children[2]?.isMissing).toBe(true); + const sum = node.firstChild!.firstChild!.firstNamedChild!; + expect(sum.type).toBe('binary_expression'); + expect(sum.hasError).toBe(true); + expect(sum.children[0]!.isMissing).toBe(false); + expect(sum.children[1]!.isMissing).toBe(false); + expect(sum.children[2]!.isMissing).toBe(true); }); }); describe('.isExtra', () => { it('returns true if the node is an extra node like comments', () => { - tree = parser.parse('foo(/* hi */);'); + tree = parser.parse('foo(/* hi */);')!; const node = tree.rootNode; const commentNode = node.descendantForIndex(7, 7)!; @@ -362,15 +339,15 @@ describe('Node', () => { }).forEach(([method, _parse]) => { it(`returns the text of a node generated by ${method}`, () => { const [numeratorSrc, denominatorSrc] = text.split(/\s*\/\s+/); - tree = parser.parse(_parse); - const quotientNode = tree.rootNode.firstChild?.firstChild; - const [numerator, slash, denominator] = quotientNode!.children; + tree = parser.parse(_parse)!; + const quotientNode = tree.rootNode.firstChild!.firstChild!; + const [numerator, slash, denominator] = quotientNode.children; expect(tree.rootNode.text).toBe(text); - expect(denominator?.text).toBe(denominatorSrc); - expect(quotientNode?.text).toBe(text); - expect(numerator?.text).toBe(numeratorSrc); - expect(slash?.text).toBe('/'); + expect(denominator!.text).toBe(denominatorSrc); + expect(quotientNode.text).toBe(text); + expect(numerator!.text).toBe(numeratorSrc); + expect(slash!.text).toBe('/'); }); }); }); @@ -378,7 +355,7 @@ describe('Node', () => { describe('.descendantCount', () => { it('returns the number of descendants', () => { parser.setLanguage(JSON); - tree = parser.parse(JSON_EXAMPLE); + tree = parser.parse(JSON_EXAMPLE)!; const valueNode = tree.rootNode; const allNodes = getAllNodes(tree); @@ -400,7 +377,7 @@ describe('Node', () => { it('tests a single node tree', () => { parser.setLanguage(EmbeddedTemplate); - tree = parser.parse('hello'); + tree = parser.parse('hello')!; const nodes = getAllNodes(tree); expect(nodes).toHaveLength(2); @@ -420,19 +397,19 @@ describe('Node', () => { describe('.rootNodeWithOffset', () => { it('returns the root node of the tree, offset by the given byte offset', () => { - tree = parser.parse(' if (a) b'); + tree = parser.parse(' if (a) b')!; const node = tree.rootNodeWithOffset(6, { row: 2, column: 2 }); expect(node.startIndex).toBe(8); expect(node.endIndex).toBe(16); expect(node.startPosition).toEqual({ row: 2, column: 4 }); expect(node.endPosition).toEqual({ row: 2, column: 12 }); - let child = node.firstChild?.child(2); - expect(child?.type).toBe('expression_statement'); - expect(child?.startIndex).toBe(15); - expect(child?.endIndex).toBe(16); - expect(child?.startPosition).toEqual({ row: 2, column: 11 }); - expect(child?.endPosition).toEqual({ row: 2, column: 12 }); + let child = node.firstChild!.child(2)!; + expect(child.type).toBe('expression_statement'); + expect(child.startIndex).toBe(15); + expect(child.endIndex).toBe(16); + expect(child.startPosition).toEqual({ row: 2, column: 11 }); + expect(child.endPosition).toEqual({ row: 2, column: 12 }); const cursor = node.walk(); cursor.gotoFirstChild(); @@ -451,40 +428,35 @@ describe('Node', () => { const text = '10 / 5'; it('returns node parse state ids', () => { - tree = parser.parse(text); - const quotientNode = tree.rootNode.firstChild?.firstChild; - const [numerator, slash, denominator] = quotientNode!.children; + tree = parser.parse(text)!; + const quotientNode = tree.rootNode.firstChild!.firstChild!; + const [numerator, slash, denominator] = quotientNode.children; expect(tree.rootNode.parseState).toBe(0); // parse states will change on any change to the grammar so test that it // returns something instead - expect(numerator?.parseState).toBeGreaterThan(0); - expect(slash?.parseState).toBeGreaterThan(0); - expect(denominator?.parseState).toBeGreaterThan(0); + expect(numerator!.parseState).toBeGreaterThan(0); + expect(slash!.parseState).toBeGreaterThan(0); + expect(denominator!.parseState).toBeGreaterThan(0); }); it('returns next parse state equal to the language', () => { - tree = parser.parse(text); - const quotientNode = tree.rootNode.firstChild?.firstChild; - quotientNode?.children.forEach((node) => { - expect(node?.nextParseState).toBe( - JavaScript.nextState(node!.parseState, node!.grammarId) - ); + tree = parser.parse(text)!; + const quotientNode = tree.rootNode.firstChild!.firstChild!; + quotientNode.children.forEach((node) => { + expect(node!.nextParseState).toBe(JavaScript.nextState(node!.parseState, node!.grammarId)); }); }); }); describe('.descendantsOfType', () => { it('finds all descendants of a given type in the given range', () => { - tree = parser.parse('a + 1 * b * 2 + c + 3'); - const outerSum = tree.rootNode.firstChild?.firstChild; + tree = parser.parse('a + 1 * b * 2 + c + 3')!; + const outerSum = tree.rootNode.firstChild!.firstChild!; - const descendants = outerSum?.descendantsOfType('number', { row: 0, column: 2 }, { row: 0, column: 15 }) ?? []; - expect(descendants.map(node => node?.startIndex)).toEqual([4, 12]); - expect(descendants.map(node => node?.endPosition)).toEqual([ - { row: 0, column: 5 }, - { row: 0, column: 13 }, - ]); + const descendants = outerSum.descendantsOfType('number', { row: 0, column: 2 }, { row: 0, column: 15 }); + expect(descendants.map(node => node!.startIndex)).toEqual([4, 12]); + expect(descendants.map(node => node!.endPosition)).toEqual([{ row: 0, column: 5 }, { row: 0, column: 13 }]); }); }); @@ -492,57 +464,57 @@ describe('Node', () => { describe('.firstChildForIndex(index)', () => { it('returns the first child that contains or starts after the given index', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild?.firstChild; + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; - expect(sumNode?.firstChildForIndex(0)?.type).toBe('identifier'); - expect(sumNode?.firstChildForIndex(1)?.type).toBe('identifier'); - expect(sumNode?.firstChildForIndex(3)?.type).toBe('+'); - expect(sumNode?.firstChildForIndex(5)?.type).toBe('number'); + expect(sumNode.firstChildForIndex(0)!.type).toBe('identifier'); + expect(sumNode.firstChildForIndex(1)!.type).toBe('identifier'); + expect(sumNode.firstChildForIndex(3)!.type).toBe('+'); + expect(sumNode.firstChildForIndex(5)!.type).toBe('number'); }); }); describe('.firstNamedChildForIndex(index)', () => { it('returns the first child that contains or starts after the given index', () => { - tree = parser.parse('x10 + 1000'); - const sumNode = tree.rootNode.firstChild?.firstChild; + tree = parser.parse('x10 + 1000')!; + const sumNode = tree.rootNode.firstChild!.firstChild!; - expect(sumNode?.firstNamedChildForIndex(0)?.type).toBe('identifier'); - expect(sumNode?.firstNamedChildForIndex(1)?.type).toBe('identifier'); - expect(sumNode?.firstNamedChildForIndex(3)?.type).toBe('number'); + expect(sumNode.firstNamedChildForIndex(0)!.type).toBe('identifier'); + expect(sumNode.firstNamedChildForIndex(1)!.type).toBe('identifier'); + expect(sumNode.firstNamedChildForIndex(3)!.type).toBe('number'); }); }); describe('.equals(other)', () => { it('returns true if the nodes are the same', () => { - tree = parser.parse('1 + 2'); + tree = parser.parse('1 + 2')!; - const sumNode = tree.rootNode.firstChild?.firstChild; - const node1 = sumNode?.firstChild; - const node2 = sumNode?.firstChild; - expect(node1?.equals(node2!)).toBe(true); + const sumNode = tree.rootNode.firstChild!.firstChild!; + const node1 = sumNode.firstChild!; + const node2 = sumNode.firstChild!; + expect(node1.equals(node2)).toBe(true); }); it('returns false if the nodes are not the same', () => { - tree = parser.parse('1 + 2'); + tree = parser.parse('1 + 2')!; - const sumNode = tree.rootNode.firstChild?.firstChild; - const node1 = sumNode?.firstChild; - const node2 = node1?.nextSibling; - expect(node1?.equals(node2!)).toBe(false); + const sumNode = tree.rootNode.firstChild!.firstChild!; + const node1 = sumNode.firstChild!; + const node2 = node1.nextSibling!; + expect(node1.equals(node2)).toBe(false); }); }); describe('.fieldNameForChild(index)', () => { it('returns the field of a child or null', () => { parser.setLanguage(C); - tree = parser.parse('int w = x + /* y is special! */ y;'); + tree = parser.parse('int w = x + /* y is special! */ y;')!; const translationUnitNode = tree.rootNode; const declarationNode = translationUnitNode.firstChild; const binaryExpressionNode = declarationNode! .childForFieldName('declarator')! - .childForFieldName('value'); + .childForFieldName('value')!; // ------------------- // left: (identifier) 0 @@ -551,26 +523,26 @@ describe('Node', () => { // right: (identifier) 3 // ------------------- - expect(binaryExpressionNode?.fieldNameForChild(0)).toBe('left'); - expect(binaryExpressionNode?.fieldNameForChild(1)).toBe('operator'); + expect(binaryExpressionNode.fieldNameForChild(0)).toBe('left'); + expect(binaryExpressionNode.fieldNameForChild(1)).toBe('operator'); // The comment should not have a field name, as it's just an extra - expect(binaryExpressionNode?.fieldNameForChild(2)).toBeNull(); - expect(binaryExpressionNode?.fieldNameForChild(3)).toBe('right'); + expect(binaryExpressionNode.fieldNameForChild(2)).toBeNull(); + expect(binaryExpressionNode.fieldNameForChild(3)).toBe('right'); // Negative test - Not a valid child index - expect(binaryExpressionNode?.fieldNameForChild(4)).toBeNull(); + expect(binaryExpressionNode.fieldNameForChild(4)).toBeNull(); }); }); describe('.fieldNameForNamedChild(index)', () => { it('returns the field of a named child or null', () => { parser.setLanguage(C); - tree = parser.parse('int w = x + /* y is special! */ y;'); + tree = parser.parse('int w = x + /* y is special! */ y;')!; const translationUnitNode = tree.rootNode; const declarationNode = translationUnitNode.firstNamedChild; const binaryExpressionNode = declarationNode! .childForFieldName('declarator')! - .childForFieldName('value'); + .childForFieldName('value')!; // ------------------- // left: (identifier) 0 @@ -579,13 +551,13 @@ describe('Node', () => { // right: (identifier) 2 // ------------------- - expect(binaryExpressionNode?.fieldNameForNamedChild(0)).toBe('left'); + expect(binaryExpressionNode.fieldNameForNamedChild(0)).toBe('left'); // The comment should not have a field name, as it's just an extra - expect(binaryExpressionNode?.fieldNameForNamedChild(1)).toBeNull(); + expect(binaryExpressionNode.fieldNameForNamedChild(1)).toBeNull(); // The operator is not a named child, so the named child at index 2 is the right child - expect(binaryExpressionNode?.fieldNameForNamedChild(2)).toBe('right'); + expect(binaryExpressionNode.fieldNameForNamedChild(2)).toBe('right'); // Negative test - Not a valid child index - expect(binaryExpressionNode?.fieldNameForNamedChild(3)).toBeNull(); + expect(binaryExpressionNode.fieldNameForNamedChild(3)).toBeNull(); }); }); }); diff --git a/lib/binding_web/test/parser.test.ts b/lib/binding_web/test/parser.test.ts index 04cc1461..1186e02c 100644 --- a/lib/binding_web/test/parser.test.ts +++ b/lib/binding_web/test/parser.test.ts @@ -1,19 +1,18 @@ import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest'; import helper, { type LanguageName } from './helper'; -import type { ParseState, Tree, Parser as ParserType, Language as LanguageType } from '../src'; +import type { ParseState, Tree } from '../src'; +import { Parser, Language } from '../src'; -let Parser: typeof ParserType; -let Language: typeof LanguageType; -let JavaScript: LanguageType; -let HTML: LanguageType; -let JSON: LanguageType; +let JavaScript: Language; +let HTML: Language; +let JSON: Language; let languageURL: (name: LanguageName) => string; describe('Parser', () => { - let parser: ParserType; + let parser: Parser; beforeAll(async () => { - ({ Parser, Language, JavaScript, HTML, JSON, languageURL } = await helper); + ({ JavaScript, HTML, JSON, languageURL } = await helper); }); beforeEach(() => { @@ -49,7 +48,7 @@ describe('Parser', () => { it('calls the given callback for each parse event', () => { const debugMessages: string[] = []; parser.setLogger((message) => debugMessages.push(message)); - parser.parse('a + b + c'); + parser.parse('a + b + c')!; expect(debugMessages).toEqual(expect.arrayContaining([ 'skip character:\' \'', 'consume character:\'b\'', @@ -70,7 +69,7 @@ describe('Parser', () => { const debugMessages: string[] = []; parser.setLogger((message) => debugMessages.push(message)); parser.setLogger(false); - parser.parse('a + b * c'); + parser.parse('a + b * c')!; expect(debugMessages).toHaveLength(0); }); @@ -92,7 +91,7 @@ describe('Parser', () => { it('parses the text within a range', () => { parser.setLanguage(HTML); const sourceCode = 'hi'; - const htmlTree = parser.parse(sourceCode); + const htmlTree = parser.parse(sourceCode)!; const scriptContentNode = htmlTree.rootNode.child(1)!.child(1)!; expect(scriptContentNode.type).toBe('raw_text'); @@ -115,7 +114,7 @@ describe('Parser', () => { sourceCode, null, { includedRanges: ranges } - ); + )!; expect(parser.getIncludedRanges()).toEqual(ranges); expect(jsTree.rootNode.toString()).toBe( @@ -131,7 +130,7 @@ describe('Parser', () => { it('parses the text within multiple ranges', () => { parser.setLanguage(JavaScript); const sourceCode = 'html `
Hello, ${name.toUpperCase()}, it\'s ${now()}.
`'; - const jsTree = parser.parse(sourceCode); + const jsTree = parser.parse(sourceCode)!; const templateStringNode = jsTree.rootNode.descendantForIndex( sourceCode.indexOf('`<'), sourceCode.indexOf('>`') @@ -165,7 +164,7 @@ describe('Parser', () => { }, ]; - const htmlTree = parser.parse(sourceCode, null, { includedRanges: htmlRanges }); + const htmlTree = parser.parse(sourceCode, null, { includedRanges: htmlRanges })!; expect(htmlTree.rootNode.toString()).toBe( '(document (element' + @@ -212,7 +211,7 @@ describe('Parser', () => { endPosition: { row: 10, column: 12 + endIndex }, }; - const htmlTree = parser.parse(sourceCode, null, { includedRanges: [rangeToParse] }); + const htmlTree = parser.parse(sourceCode, null, { includedRanges: [rangeToParse] })!; expect(htmlTree.getIncludedRanges()[0]).toEqual(rangeToParse); @@ -236,13 +235,13 @@ describe('Parser', () => { it('reads from the given input', () => { const parts = ['first', '_', 'second', '_', 'third']; - tree = parser.parse(() => parts.shift()); + tree = parser.parse(() => parts.shift())!; expect(tree.rootNode.toString()).toBe('(program (expression_statement (identifier)))'); }); it('stops reading when the input callback returns something that\'s not a string', () => { const parts = ['abc', 'def', 'ghi', {}, {}, {}, 'second-word', ' ']; - tree = parser.parse(() => parts.shift() as string); + tree = parser.parse(() => parts.shift() as string)!; expect(tree.rootNode.toString()).toBe('(program (expression_statement (identifier)))'); expect(tree.rootNode.endIndex).toBe(9); expect(parts).toHaveLength(2); @@ -261,14 +260,14 @@ describe('Parser', () => { const repeatCount = 10000; const inputString = `[${Array(repeatCount).fill('0').join(',')}]`; - tree = parser.parse(inputString); + tree = parser.parse(inputString)!; expect(tree.rootNode.type).toBe('program'); expect(tree.rootNode.firstChild!.firstChild!.namedChildCount).toBe(repeatCount); }); it('can use the bash parser', { timeout: 5000 }, async () => { parser.setLanguage(await Language.load(languageURL('bash'))); - tree = parser.parse('FOO=bar echo < err.txt > hello.txt \nhello${FOO}\nEOF'); + tree = parser.parse('FOO=bar echo < err.txt > hello.txt \nhello${FOO}\nEOF')!; expect(tree.rootNode.toString()).toBe( '(program ' + '(redirected_statement ' + @@ -285,7 +284,7 @@ describe('Parser', () => { it('can use the c++ parser', { timeout: 5000 }, async () => { parser.setLanguage(await Language.load(languageURL('cpp'))); - tree = parser.parse('const char *s = R"EOF(HELLO WORLD)EOF";'); + tree = parser.parse('const char *s = R"EOF(HELLO WORLD)EOF";')!; expect(tree.rootNode.toString()).toBe( '(translation_unit (declaration ' + '(type_qualifier) ' + @@ -298,7 +297,7 @@ describe('Parser', () => { it('can use the HTML parser', { timeout: 5000 }, async () => { parser.setLanguage(await Language.load(languageURL('html'))); - tree = parser.parse('
'); + tree = parser.parse('
')!; expect(tree.rootNode.toString()).toBe( '(document (element (start_tag (tag_name)) (element (start_tag (tag_name)) ' + '(element (start_tag (tag_name)) (end_tag (tag_name))) (end_tag (tag_name))) (end_tag (tag_name))))' @@ -307,7 +306,7 @@ describe('Parser', () => { it('can use the python parser', { timeout: 5000 }, async () => { parser.setLanguage(await Language.load(languageURL('python'))); - tree = parser.parse('class A:\n def b():\n c()'); + tree = parser.parse('class A:\n def b():\n c()')!; expect(tree.rootNode.toString()).toBe( '(module (class_definition ' + 'name: (identifier) ' + @@ -323,7 +322,7 @@ describe('Parser', () => { it('can use the rust parser', { timeout: 5000 }, async () => { parser.setLanguage(await Language.load(languageURL('rust'))); - tree = parser.parse('const x: &\'static str = r###"hello"###;'); + tree = parser.parse('const x: &\'static str = r###"hello"###;')!; expect(tree.rootNode.toString()).toBe( '(source_file (const_item ' + 'name: (identifier) ' + @@ -334,7 +333,7 @@ describe('Parser', () => { it('can use the typescript parser', { timeout: 5000 }, async () => { parser.setLanguage(await Language.load(languageURL('typescript'))); - tree = parser.parse('a()\nb()\n[c]'); + tree = parser.parse('a()\nb()\n[c]')!; expect(tree.rootNode.toString()).toBe( '(program ' + '(expression_statement (call_expression function: (identifier) arguments: (arguments))) ' + @@ -348,7 +347,7 @@ describe('Parser', () => { it('can use the tsx parser', { timeout: 5000 }, async () => { parser.setLanguage(await Language.load(languageURL('tsx'))); - tree = parser.parse('a()\nb()\n[c]'); + tree = parser.parse('a()\nb()\n[c]')!; expect(tree.rootNode.toString()).toBe( '(program ' + '(expression_statement (call_expression function: (identifier) arguments: (arguments))) ' + @@ -384,7 +383,7 @@ describe('Parser', () => { endPosition: { row: 0, column: end2 }, }, ], - }); + })!; expect(tree.rootNode.toString()).toBe( '(program ' + @@ -408,12 +407,11 @@ describe('Parser', () => { return false; }; - expect(() => parser.parse( + expect(parser.parse( (offset) => offset === 0 ? '[' : ',0', null, { progressCallback }, - ) - ).toThrowError(); + )).toBeNull(); }); }); }); diff --git a/lib/binding_web/test/query.test.ts b/lib/binding_web/test/query.test.ts index f0bd96c1..a3ca76d2 100644 --- a/lib/binding_web/test/query.test.ts +++ b/lib/binding_web/test/query.test.ts @@ -1,17 +1,17 @@ import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest'; -import type { Parser as ParserType, Language, Tree, Query, QueryMatch, QueryCapture } from '../src'; +import type { Language, Tree, QueryMatch, QueryCapture } from '../src'; +import { Parser, Query } from '../src'; import helper from './helper'; -let Parser: typeof ParserType; let JavaScript: Language; describe('Query', () => { - let parser: ParserType; + let parser: Parser; let tree: Tree | null; let query: Query | null; beforeAll(async () => { - ({ Parser, JavaScript } = await helper); + ({ JavaScript } = await helper); }); beforeEach(() => { @@ -65,7 +65,7 @@ describe('Query', () => { describe('.matches', () => { it('returns all of the matches for the given query', () => { - tree = parser.parse('function one() { two(); function three() {} }'); + tree = parser.parse('function one() { two(); function three() {} }')!; query = JavaScript.query(` (function_declaration name: (identifier) @fn-def) (call_expression function: (identifier) @fn-ref) @@ -79,7 +79,7 @@ describe('Query', () => { }); it('can search in specified ranges', () => { - tree = parser.parse('[a, b,\nc, d,\ne, f,\ng, h]'); + tree = parser.parse('[a, b,\nc, d,\ne, f,\ng, h]')!; query = JavaScript.query('(identifier) @element'); const matches = query.matches( tree.rootNode, @@ -104,7 +104,7 @@ describe('Query', () => { gross(3, []); hiccup([]); gaff(5); - `); + `)!; // Find all calls to functions beginning with 'g', where one argument // is an array literal. @@ -125,7 +125,7 @@ describe('Query', () => { it('handles multiple matches where the first one is filtered', () => { tree = parser.parse(` const a = window.b; - `); + `)!; query = JavaScript.query(` ((identifier) @variable.builtin @@ -151,7 +151,7 @@ describe('Query', () => { const no = function pq() {} }, }); - `); + `)!; query = JavaScript.query(` (pair key: _ @method.def @@ -192,7 +192,7 @@ describe('Query', () => { toad const ab = require('./ab'); new Cd(EF); - `); + `)!; query = JavaScript.query(` ((identifier) @variable @@ -228,7 +228,7 @@ describe('Query', () => { ab = abc + 1; def = de + 1; ghi = ghi + 1; - `); + `)!; query = JavaScript.query(` ( @@ -248,7 +248,7 @@ describe('Query', () => { }); it('handles patterns with properties', () => { - tree = parser.parse(`a(b.c);`); + tree = parser.parse(`a(b.c);`)!; query = JavaScript.query(` ((call_expression (identifier) @func) (#set! foo) @@ -285,7 +285,7 @@ describe('Query', () => { hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, hello, ]; - `); + `)!; query = JavaScript.query(` (array (identifier) @pre (identifier) @post) @@ -300,7 +300,7 @@ describe('Query', () => { /// foo /// bar /// baz - `); + `)!; const expectCount = (tree: Tree, queryText: string, expectedCount: number) => { query = JavaScript.query(queryText); @@ -427,7 +427,7 @@ describe('Query', () => { `); const source = 'function foo() { return 1; }'; - const tree = parser.parse(source); + const tree = parser.parse(source)!; let matches = query.matches(tree.rootNode); expect(formatMatches(matches)).toEqual([ @@ -463,7 +463,7 @@ describe('Query', () => { describe('Set a timeout', () => { it('returns less than the expected matches', () => { - tree = parser.parse('function foo() while (true) { } }\n'.repeat(1000)); + tree = parser.parse('function foo() while (true) { } }\n'.repeat(1000))!; query = JavaScript.query( '(function_declaration name: (identifier) @function)' ); @@ -528,7 +528,7 @@ describe('Query', () => { query.disablePattern(2); const source = 'class A { constructor() {} } function b() { return 1; }'; - tree = parser.parse(source); + tree = parser.parse(source)!; const matches = query.matches(tree.rootNode); expect(formatMatches(matches)).toEqual([ { @@ -542,7 +542,7 @@ describe('Query', () => { describe('Executes with a timeout', () => { it('Returns less than the expected matches', () => { - tree = parser.parse('function foo() while (true) { } }\n'.repeat(1000)); + tree = parser.parse('function foo() while (true) { } }\n'.repeat(1000))!; query = JavaScript.query( '(function_declaration) @function' ); diff --git a/lib/binding_web/test/tree.test.ts b/lib/binding_web/test/tree.test.ts index 711a039f..3cf40eab 100644 --- a/lib/binding_web/test/tree.test.ts +++ b/lib/binding_web/test/tree.test.ts @@ -1,8 +1,8 @@ import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest'; -import type { Parser as ParserType, Point, Language, Tree, Edit, TreeCursor } from '../src'; +import type { Point, Language, Tree, Edit, TreeCursor } from '../src'; +import { Parser } from '../src'; import helper from './helper'; -let Parser: typeof ParserType; let JavaScript: Language; interface CursorState { @@ -15,11 +15,11 @@ interface CursorState { } describe('Tree', () => { - let parser: ParserType; + let parser: Parser; let tree: Tree; beforeAll(async () => { - ({ Parser, JavaScript } = await helper); + ({ JavaScript } = await helper); }); beforeEach(() => { @@ -38,7 +38,7 @@ describe('Tree', () => { it('updates the positions of nodes', () => { input = 'abc + cde'; - tree = parser.parse(input); + tree = parser.parse(input)!; expect(tree.rootNode.toString()).toBe( '(program (expression_statement (binary_expression left: (identifier) right: (identifier))))' ); @@ -63,7 +63,7 @@ describe('Tree', () => { expect(variableNode2!.startIndex).toBe(9); expect(variableNode2!.endIndex).toBe(12); - tree = parser.parse(input, tree); + tree = parser.parse(input, tree)!; expect(tree.rootNode.toString()).toBe( '(program (expression_statement (binary_expression left: (binary_expression left: (identifier) right: (identifier)) right: (identifier))))' ); @@ -72,7 +72,7 @@ describe('Tree', () => { it('handles non-ascii characters', () => { input = 'αβδ + cde'; - tree = parser.parse(input); + tree = parser.parse(input)!; expect(tree.rootNode.toString()).toBe( '(program (expression_statement (binary_expression left: (identifier) right: (identifier))))' ); @@ -86,7 +86,7 @@ describe('Tree', () => { variableNode = tree.rootNode.firstChild!.firstChild!.lastChild; expect(variableNode!.startIndex).toBe(input.indexOf('cde')); - tree = parser.parse(input, tree); + tree = parser.parse(input, tree)!; expect(tree.rootNode.toString()).toBe( '(program (expression_statement (binary_expression left: (binary_expression left: (identifier) right: (identifier)) right: (identifier))))' ); @@ -96,7 +96,7 @@ describe('Tree', () => { describe('.getChangedRanges(previous)', () => { it('reports the ranges of text whose syntactic meaning has changed', () => { let sourceCode = 'abcdefg + hij'; - tree = parser.parse(sourceCode); + tree = parser.parse(sourceCode)!; expect(tree.rootNode.toString()).toBe( '(program (expression_statement (binary_expression left: (identifier) right: (identifier))))' @@ -112,7 +112,7 @@ describe('Tree', () => { newEndPosition: { row: 0, column: 5 }, }); - const tree2 = parser.parse(sourceCode, tree); + const tree2 = parser.parse(sourceCode, tree)!; expect(tree2.rootNode.toString()).toBe( '(program (expression_statement (binary_expression left: (binary_expression left: (identifier) right: (identifier)) right: (identifier))))' ); @@ -131,7 +131,7 @@ describe('Tree', () => { }); it('throws an exception if the argument is not a tree', () => { - tree = parser.parse('abcdefg + hij'); + tree = parser.parse('abcdefg + hij')!; expect(() => { tree.getChangedRanges({} as Tree); @@ -147,7 +147,7 @@ describe('Tree', () => { }); it('returns a cursor that can be used to walk the tree', () => { - tree = parser.parse('a * b + c / d'); + tree = parser.parse('a * b + c / d')!; cursor = tree.walk(); assertCursorState(cursor, { @@ -306,7 +306,7 @@ describe('Tree', () => { }); it('keeps track of the field name associated with each node', () => { - tree = parser.parse('a.b();'); + tree = parser.parse('a.b();')!; cursor = tree.walk(); cursor.gotoFirstChild(); cursor.gotoFirstChild(); @@ -334,7 +334,7 @@ describe('Tree', () => { }); it('returns a cursor that can be reset anywhere in the tree', () => { - tree = parser.parse('a * b + c / d'); + tree = parser.parse('a * b + c / d')!; cursor = tree.walk(); const root = tree.rootNode.firstChild; @@ -369,7 +369,7 @@ describe('Tree', () => { it('creates another tree that remains stable if the original tree is edited', () => { input = 'abc + cde'; - tree = parser.parse(input); + tree = parser.parse(input)!; expect(tree.rootNode.toString()).toBe( '(program (expression_statement (binary_expression left: (identifier) right: (identifier))))' );