// Copyright 2018-2025 the Deno authors. MIT license. import { assertEquals } from "./test_util.ts"; import { assertSnapshot } from "@std/testing/snapshot"; // TODO(@marvinhagemeister) Remove once we land "official" types export interface LintReportData { // deno-lint-ignore no-explicit-any node: any; message: string; } // TODO(@marvinhagemeister) Remove once we land "official" types interface LintContext { id: string; } // TODO(@marvinhagemeister) Remove once we land "official" types // deno-lint-ignore no-explicit-any type LintVisitor = Record void>; // TODO(@marvinhagemeister) Remove once we land "official" types interface LintRule { create(ctx: LintContext): LintVisitor; destroy?(): void; } // TODO(@marvinhagemeister) Remove once we land "official" types interface LintPlugin { name: string; rules: Record; } function runLintPlugin(plugin: LintPlugin, fileName: string, source: string) { // deno-lint-ignore no-explicit-any return (Deno as any)[(Deno as any).internal].runLintPlugin( plugin, fileName, source, ); } function testPlugin( source: string, rule: LintRule, ) { const plugin = { name: "test-plugin", rules: { testRule: rule, }, }; return runLintPlugin(plugin, "source.tsx", source); } interface VisitResult { selector: string; kind: "enter" | "exit"; // deno-lint-ignore no-explicit-any node: any; } function testVisit( source: string, ...selectors: string[] ): VisitResult[] { const result: VisitResult[] = []; testPlugin(source, { create() { const visitor: LintVisitor = {}; for (const s of selectors) { visitor[s] = (node) => { result.push({ kind: s.endsWith(":exit") ? "exit" : "enter", selector: s, node, }); }; } return visitor; }, }); return result; } async function testSnapshot( t: Deno.TestContext, source: string, ...selectors: string[] ) { const log: unknown[] = []; testPlugin(source, { create() { const visitor: LintVisitor = {}; for (const s of selectors) { visitor[s] = (node) => { log.push(node[Symbol.for("Deno.lint.toJsValue")]()); }; } return visitor; }, }); assertEquals(log.length > 0, true); await assertSnapshot(t, log[0]); } Deno.test("Plugin - visitor enter/exit", () => { const enter = testVisit( "foo", "Identifier", ); assertEquals(enter[0].node.type, "Identifier"); const exit = testVisit( "foo", "Identifier:exit", ); assertEquals(exit[0].node.type, "Identifier"); const both = testVisit("foo", "Identifier", "Identifier:exit"); assertEquals(both.map((t) => t.selector), ["Identifier", "Identifier:exit"]); }); Deno.test("Plugin - visitor descendant", () => { let result = testVisit( "if (false) foo; if (false) bar()", "IfStatement CallExpression", ); assertEquals(result[0].node.type, "CallExpression"); assertEquals(result[0].node.callee.name, "bar"); result = testVisit( "if (false) foo; foo()", "IfStatement IfStatement", ); assertEquals(result, []); result = testVisit( "if (false) foo; foo()", "* CallExpression", ); assertEquals(result[0].node.type, "CallExpression"); }); Deno.test("Plugin - visitor child combinator", () => { let result = testVisit( "if (false) foo; if (false) { bar; }", "IfStatement > ExpressionStatement > Identifier", ); assertEquals(result[0].node.name, "foo"); result = testVisit( "if (false) foo; foo()", "IfStatement IfStatement", ); assertEquals(result, []); }); Deno.test("Plugin - visitor next sibling", () => { const result = testVisit( "if (false) foo; if (false) bar;", "IfStatement + IfStatement Identifier", ); assertEquals(result[0].node.name, "bar"); }); Deno.test("Plugin - visitor subsequent sibling", () => { const result = testVisit( "if (false) foo; if (false) bar; if (false) baz;", "IfStatement ~ IfStatement Identifier", ); assertEquals(result.map((r) => r.node.name), ["bar", "baz"]); }); Deno.test("Plugin - visitor attr", () => { let result = testVisit( "for (const a of b) {}", "[await]", ); assertEquals(result[0].node.await, false); result = testVisit( "for await (const a of b) {}", "[await=true]", ); assertEquals(result[0].node.await, true); result = testVisit( "for await (const a of b) {}", "ForOfStatement[await=true]", ); assertEquals(result[0].node.await, true); result = testVisit( "for (const a of b) {}", "ForOfStatement[await != true]", ); assertEquals(result[0].node.await, false); result = testVisit( "async function *foo() {}", "FunctionDeclaration[async=true][generator=true]", ); assertEquals(result[0].node.type, "FunctionDeclaration"); result = testVisit( "foo", "[name='foo']", ); assertEquals(result[0].node.name, "foo"); }); Deno.test("Plugin - visitor attr to check type", () => { let result = testVisit( "foo", "Identifier[type]", ); assertEquals(result[0].node.type, "Identifier"); result = testVisit( "foo", "Identifier[type='Identifier']", ); assertEquals(result[0].node.type, "Identifier"); }); Deno.test("Plugin - visitor attr non-existing", () => { const result = testVisit( "foo", "[non-existing]", ); assertEquals(result, []); }); Deno.test("Plugin - visitor attr length special case", () => { let result = testVisit( "foo(1); foo(1, 2);", "CallExpression[arguments.length=2]", ); assertEquals(result[0].node.arguments.length, 2); result = testVisit( "foo(1); foo(1, 2);", "CallExpression[arguments.length>1]", ); assertEquals(result[0].node.arguments.length, 2); result = testVisit( "foo(1); foo(1, 2);", "CallExpression[arguments.length<2]", ); assertEquals(result[0].node.arguments.length, 1); result = testVisit( "foo(1); foo(1, 2);", "CallExpression[arguments.length<=3]", ); assertEquals(result[0].node.arguments.length, 1); assertEquals(result[1].node.arguments.length, 2); result = testVisit( "foo(1); foo(1, 2);", "CallExpression[arguments.length>=1]", ); assertEquals(result[0].node.arguments.length, 1); assertEquals(result[1].node.arguments.length, 2); }); Deno.test("Plugin - visitor :first-child", () => { const result = testVisit( "{ foo; bar }", "BlockStatement ExpressionStatement:first-child Identifier", ); assertEquals(result[0].node.name, "foo"); }); Deno.test("Plugin - visitor :last-child", () => { const result = testVisit( "{ foo; bar }", "BlockStatement ExpressionStatement:last-child Identifier", ); assertEquals(result[0].node.name, "bar"); }); Deno.test("Plugin - visitor :nth-child", () => { let result = testVisit( "{ foo; bar; baz; foobar; }", "BlockStatement ExpressionStatement:nth-child(2) Identifier", ); assertEquals(result[0].node.name, "bar"); result = testVisit( "{ foo; bar; baz; foobar; }", "BlockStatement ExpressionStatement:nth-child(2n) Identifier", ); assertEquals(result[0].node.name, "foo"); assertEquals(result[1].node.name, "baz"); result = testVisit( "{ foo; bar; baz; foobar; }", "BlockStatement ExpressionStatement:nth-child(2n + 1) Identifier", ); assertEquals(result[0].node.name, "bar"); assertEquals(result[1].node.name, "foobar"); result = testVisit( "{ foo; bar; baz; foobar; }", "BlockStatement *:nth-child(2n + 1 of ExpressionStatement) Identifier", ); assertEquals(result[0].node.name, "bar"); assertEquals(result[1].node.name, "foobar"); }); Deno.test("Plugin - Program", async (t) => { await testSnapshot(t, "", "Program"); }); Deno.test("Plugin - ImportDeclaration", async (t) => { await testSnapshot(t, 'import "foo";', "ImportDeclaration"); await testSnapshot(t, 'import foo from "foo";', "ImportDeclaration"); await testSnapshot(t, 'import * as foo from "foo";', "ImportDeclaration"); await testSnapshot( t, 'import { foo, bar as baz } from "foo";', "ImportDeclaration", ); await testSnapshot( t, 'import foo from "foo" with { type: "json" };', "ImportDeclaration", ); }); Deno.test("Plugin - ExportNamedDeclaration", async (t) => { await testSnapshot(t, 'export { foo } from "foo";', "ExportNamedDeclaration"); await testSnapshot( t, 'export { bar as baz } from "foo";', "ExportNamedDeclaration", ); await testSnapshot( t, 'export { foo } from "foo" with { type: "json" };', "ExportNamedDeclaration", ); }); Deno.test("Plugin - ExportDefaultDeclaration", async (t) => { await testSnapshot( t, "export default function foo() {}", "ExportDefaultDeclaration", ); await testSnapshot( t, "export default function () {}", "ExportDefaultDeclaration", ); await testSnapshot( t, "export default class Foo {}", "ExportDefaultDeclaration", ); await testSnapshot( t, "export default class {}", "ExportDefaultDeclaration", ); await testSnapshot(t, "export default bar;", "ExportDefaultDeclaration"); await testSnapshot( t, "export default interface Foo {};", "ExportDefaultDeclaration", ); }); Deno.test("Plugin - ExportAllDeclaration", async (t) => { await testSnapshot(t, 'export * from "foo";', "ExportAllDeclaration"); await testSnapshot(t, 'export * as foo from "foo";', "ExportAllDeclaration"); await testSnapshot( t, 'export * from "foo" with { type: "json" };', "ExportAllDeclaration", ); }); Deno.test("Plugin - TSExportAssignment", async (t) => { await testSnapshot(t, "export = foo;", "TSExportAssignment"); }); Deno.test("Plugin - TSNamespaceExportDeclaration", async (t) => { await testSnapshot( t, "export as namespace A;", "TSNamespaceExportDeclaration", ); }); Deno.test("Plugin - TSImportEqualsDeclaration", async (t) => { await testSnapshot(t, "import a = b", "TSImportEqualsDeclaration"); await testSnapshot( t, 'import a = require("foo")', "TSImportEqualsDeclaration", ); }); Deno.test("Plugin - BlockStatement", async (t) => { await testSnapshot(t, "{ foo; }", "BlockStatement"); }); Deno.test("Plugin - BreakStatement", async (t) => { await testSnapshot(t, "while (false) break;", "BreakStatement"); await testSnapshot(t, "foo: while (false) break foo;", "BreakStatement"); }); Deno.test("Plugin - ContinueStatement", async (t) => { await testSnapshot(t, "continue;", "ContinueStatement"); await testSnapshot(t, "continue foo;", "ContinueStatement"); }); Deno.test("Plugin - DebuggerStatement", async (t) => { await testSnapshot(t, "debugger;", "DebuggerStatement"); }); Deno.test("Plugin - DoWhileStatement", async (t) => { await testSnapshot(t, "do {} while (foo);", "DoWhileStatement"); }); Deno.test("Plugin - ExpressionStatement", async (t) => { await testSnapshot(t, "foo;", "ExpressionStatement"); }); Deno.test("Plugin - ForInStatement", async (t) => { await testSnapshot(t, "for (a in b) {}", "ForInStatement"); }); Deno.test("Plugin - ForOfStatement", async (t) => { await testSnapshot(t, "for (a of b) {}", "ForOfStatement"); await testSnapshot(t, "for await (a of b) {}", "ForOfStatement"); }); Deno.test("Plugin - ForStatement", async (t) => { await testSnapshot(t, "for (;;) {}", "ForStatement"); await testSnapshot(t, "for (a; b; c) {}", "ForStatement"); }); Deno.test("Plugin - IfStatement", async (t) => { await testSnapshot(t, "if (foo) {}", "IfStatement"); await testSnapshot(t, "if (foo) {} else {}", "IfStatement"); }); Deno.test("Plugin - LabeledStatement", async (t) => { await testSnapshot(t, "foo: {};", "LabeledStatement"); }); Deno.test("Plugin - ReturnStatement", async (t) => { await testSnapshot(t, "return", "ReturnStatement"); await testSnapshot(t, "return foo;", "ReturnStatement"); }); Deno.test("Plugin - SwitchStatement", async (t) => { await testSnapshot( t, `switch (foo) { case foo: case bar: break; default: {} }`, "SwitchStatement", ); }); Deno.test("Plugin - ThrowStatement", async (t) => { await testSnapshot(t, "throw foo;", "ThrowStatement"); }); Deno.test("Plugin - TryStatement", async (t) => { await testSnapshot(t, "try {} catch {};", "TryStatement"); await testSnapshot(t, "try {} catch (e) {};", "TryStatement"); await testSnapshot(t, "try {} finally {};", "TryStatement"); }); Deno.test("Plugin - WhileStatement", async (t) => { await testSnapshot(t, "while (foo) {}", "WhileStatement"); }); Deno.test("Plugin - WithStatement", async (t) => { await testSnapshot(t, "with ([]) {}", "WithStatement"); }); Deno.test("Plugin - ArrayExpression", async (t) => { await testSnapshot(t, "[[],,[]]", "ArrayExpression"); }); Deno.test("Plugin - ArrowFunctionExpression", async (t) => { await testSnapshot(t, "() => {}", "ArrowFunctionExpression"); await testSnapshot(t, "async () => {}", "ArrowFunctionExpression"); await testSnapshot( t, "(a: number, ...b: any[]): any => {}", "ArrowFunctionExpression", ); }); Deno.test("Plugin - AssignmentExpression", async (t) => { await testSnapshot(t, "a = b", "AssignmentExpression"); await testSnapshot(t, "a = a ??= b", "AssignmentExpression"); }); Deno.test("Plugin - AwaitExpression", async (t) => { await testSnapshot(t, "await foo;", "AwaitExpression"); }); Deno.test("Plugin - BinaryExpression", async (t) => { await testSnapshot(t, "a > b", "BinaryExpression"); await testSnapshot(t, "a >= b", "BinaryExpression"); await testSnapshot(t, "a < b", "BinaryExpression"); await testSnapshot(t, "a <= b", "BinaryExpression"); await testSnapshot(t, "a == b", "BinaryExpression"); await testSnapshot(t, "a === b", "BinaryExpression"); await testSnapshot(t, "a != b", "BinaryExpression"); await testSnapshot(t, "a !== b", "BinaryExpression"); await testSnapshot(t, "a << b", "BinaryExpression"); await testSnapshot(t, "a >> b", "BinaryExpression"); await testSnapshot(t, "a >>> b", "BinaryExpression"); await testSnapshot(t, "a + b", "BinaryExpression"); await testSnapshot(t, "a - b", "BinaryExpression"); await testSnapshot(t, "a * b", "BinaryExpression"); await testSnapshot(t, "a / b", "BinaryExpression"); await testSnapshot(t, "a % b", "BinaryExpression"); await testSnapshot(t, "a | b", "BinaryExpression"); await testSnapshot(t, "a ^ b", "BinaryExpression"); await testSnapshot(t, "a & b", "BinaryExpression"); await testSnapshot(t, "a in b", "BinaryExpression"); await testSnapshot(t, "a ** b", "BinaryExpression"); }); Deno.test("Plugin - CallExpression", async (t) => { await testSnapshot(t, "foo();", "CallExpression"); await testSnapshot(t, "foo(a, ...b);", "CallExpression"); await testSnapshot(t, "foo?.();", "CallExpression"); await testSnapshot(t, "foo();", "CallExpression"); }); Deno.test("Plugin - ChainExpression", async (t) => { await testSnapshot(t, "a?.b", "ChainExpression"); }); Deno.test("Plugin - ClassExpression", async (t) => { await testSnapshot(t, "a = class {}", "ClassExpression"); await testSnapshot(t, "a = class Foo {}", "ClassExpression"); await testSnapshot(t, "a = class Foo extends Bar {}", "ClassExpression"); await testSnapshot( t, "a = class Foo extends Bar implements Baz, Baz2 {}", "ClassExpression", ); await testSnapshot(t, "a = class Foo {}", "ClassExpression"); await testSnapshot(t, "a = class { foo() {} }", "ClassExpression"); await testSnapshot(t, "a = class { #foo() {} }", "ClassExpression"); await testSnapshot(t, "a = class { foo: number }", "ClassExpression"); await testSnapshot(t, "a = class { foo = bar }", "ClassExpression"); await testSnapshot( t, "a = class { constructor(public foo: string) {} }", "ClassExpression", ); await testSnapshot(t, "a = class { #foo: number = bar }", "ClassExpression"); await testSnapshot(t, "a = class { static foo = bar }", "ClassExpression"); await testSnapshot( t, "a = class { static foo; static { foo = bar } }", "ClassExpression", ); }); Deno.test("Plugin - ConditionalExpression", async (t) => { await testSnapshot(t, "a ? b : c", "ConditionalExpression"); }); Deno.test("Plugin - FunctionExpression", async (t) => { await testSnapshot(t, "a = function () {}", "FunctionExpression"); await testSnapshot(t, "a = function foo() {}", "FunctionExpression"); await testSnapshot( t, "a = function (a?: number, ...b: any[]): any {}", "FunctionExpression", ); await testSnapshot(t, "a = async function* () {}", "FunctionExpression"); }); Deno.test("Plugin - Identifier", async (t) => { await testSnapshot(t, "a", "Identifier"); }); Deno.test("Plugin - ImportExpression", async (t) => { await testSnapshot( t, "import('foo', { with: { type: 'json' } })", "ImportExpression", ); }); Deno.test("Plugin - LogicalExpression", async (t) => { await testSnapshot(t, "a && b", "LogicalExpression"); await testSnapshot(t, "a || b", "LogicalExpression"); await testSnapshot(t, "a ?? b", "LogicalExpression"); }); Deno.test("Plugin - MemberExpression", async (t) => { await testSnapshot(t, "a.b", "MemberExpression"); await testSnapshot(t, "a['b']", "MemberExpression"); }); Deno.test("Plugin - MetaProperty", async (t) => { await testSnapshot(t, "import.meta", "MetaProperty"); }); Deno.test("Plugin - NewExpression", async (t) => { await testSnapshot(t, "new Foo()", "NewExpression"); await testSnapshot(t, "new Foo(a, ...b)", "NewExpression"); }); Deno.test("Plugin - ObjectExpression", async (t) => { await testSnapshot(t, "a = {}", "ObjectExpression"); await testSnapshot(t, "a = { a }", "ObjectExpression"); await testSnapshot(t, "a = { b: c, [c]: d }", "ObjectExpression"); }); Deno.test("Plugin - PrivateIdentifier", async (t) => { await testSnapshot(t, "class Foo { #foo = foo }", "PrivateIdentifier"); }); Deno.test("Plugin - SequenceExpression", async (t) => { await testSnapshot(t, "(a, b)", "SequenceExpression"); }); Deno.test("Plugin - Super", async (t) => { await testSnapshot( t, "class Foo extends Bar { constructor() { super(); } }", "Super", ); }); Deno.test("Plugin - TaggedTemplateExpression", async (t) => { await testSnapshot(t, "foo`foo ${bar} baz`", "TaggedTemplateExpression"); }); Deno.test("Plugin - TemplateLiteral", async (t) => { await testSnapshot(t, "`foo ${bar} baz`", "TemplateLiteral"); }); Deno.test("Plugin - ThisExpression", async (t) => { await testSnapshot(t, "this", "ThisExpression"); }); Deno.test("Plugin - TSAsExpression", async (t) => { await testSnapshot(t, "a as b", "TSAsExpression"); await testSnapshot(t, "a as const", "TSAsExpression"); }); Deno.test("Plugin - TSNonNullExpression", async (t) => { await testSnapshot(t, "a!", "TSNonNullExpression"); }); Deno.test("Plugin - TSSatisfiesExpression", async (t) => { await testSnapshot(t, "a satisfies b", "TSSatisfiesExpression"); }); Deno.test("Plugin - UnaryExpression", async (t) => { await testSnapshot(t, "typeof a", "UnaryExpression"); await testSnapshot(t, "void 0", "UnaryExpression"); await testSnapshot(t, "-a", "UnaryExpression"); await testSnapshot(t, "+a", "UnaryExpression"); }); Deno.test("Plugin - UpdateExpression", async (t) => { await testSnapshot(t, "a++", "UpdateExpression"); await testSnapshot(t, "++a", "UpdateExpression"); await testSnapshot(t, "a--", "UpdateExpression"); await testSnapshot(t, "--a", "UpdateExpression"); }); Deno.test("Plugin - YieldExpression", async (t) => { await testSnapshot(t, "function* foo() { yield bar; }", "YieldExpression"); }); Deno.test("Plugin - Literal", async (t) => { await testSnapshot(t, "1", "Literal"); await testSnapshot(t, "'foo'", "Literal"); await testSnapshot(t, '"foo"', "Literal"); await testSnapshot(t, "true", "Literal"); await testSnapshot(t, "false", "Literal"); await testSnapshot(t, "null", "Literal"); await testSnapshot(t, "1n", "Literal"); await testSnapshot(t, "/foo/g", "Literal"); }); Deno.test("Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr", async (t) => { await testSnapshot(t, "
", "JSXElement"); await testSnapshot(t, "
", "JSXElement"); await testSnapshot(t, "
", "JSXElement"); await testSnapshot(t, '
', "JSXElement"); await testSnapshot(t, "
", "JSXElement"); await testSnapshot(t, "
foo{2}
", "JSXElement"); await testSnapshot(t, "", "JSXElement"); await testSnapshot(t, "
", "JSXElement"); await testSnapshot(t, "", "JSXElement"); await testSnapshot(t, " />", "JSXElement"); }); Deno.test("Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment", async (t) => { await testSnapshot(t, "<>", "JSXFragment"); await testSnapshot(t, "<>foo{2}", "JSXFragment"); }); Deno.test("Plugin - TSAsExpression", async (t) => { await testSnapshot(t, "a as any", "TSAsExpression"); await testSnapshot(t, '"foo" as const', "TSAsExpression"); }); Deno.test("Plugin - TSEnumDeclaration", async (t) => { await testSnapshot(t, "enum Foo {}", "TSEnumDeclaration"); await testSnapshot(t, "const enum Foo {}", "TSEnumDeclaration"); await testSnapshot(t, "enum Foo { A, B }", "TSEnumDeclaration"); await testSnapshot(t, 'enum Foo { "a-b" }', "TSEnumDeclaration"); await testSnapshot( t, "enum Foo { A = 1, B = 2, C = A | B }", "TSEnumDeclaration", ); }); Deno.test("Plugin - TSInterface", async (t) => { await testSnapshot(t, "interface A {}", "TSInterface"); await testSnapshot(t, "interface A {}", "TSInterface"); await testSnapshot(t, "interface A extends Foo, Bar {}", "TSInterface"); await testSnapshot(t, "interface A { foo: any, bar?: any }", "TSInterface"); await testSnapshot( t, "interface A { readonly [key: string]: any }", "TSInterface", ); await testSnapshot(t, "interface A { readonly a: any }", "TSInterface"); await testSnapshot(t, "interface A { (a: T): T }", "TSInterface"); await testSnapshot(t, "interface A { new (a: T): T }", "TSInterface"); await testSnapshot(t, "interface A { a: new (a: T) => T }", "TSInterface"); await testSnapshot(t, "interface A { get a(): string }", "TSInterface"); await testSnapshot(t, "interface A { set a(v: string) }", "TSInterface"); await testSnapshot( t, "interface A { a(arg?: any, ...args: any[]): any }", "TSInterface", ); }); Deno.test("Plugin - TSSatisfiesExpression", async (t) => { await testSnapshot(t, "const a = {} satisfies A", "TSSatisfiesExpression"); }); Deno.test("Plugin - TSTypeAliasDeclaration", async (t) => { await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); await testSnapshot(t, "declare type A = any", "TSTypeAliasDeclaration"); }); Deno.test("Plugin - TSNonNullExpression", async (t) => { await testSnapshot(t, "a!", "TSNonNullExpression"); }); Deno.test("Plugin - TSUnionType", async (t) => { await testSnapshot(t, "type A = B | C", "TSUnionType"); }); Deno.test("Plugin - TSIntersectionType", async (t) => { await testSnapshot(t, "type A = B & C", "TSIntersectionType"); }); Deno.test("Plugin - TSModuleDeclaration", async (t) => { await testSnapshot(t, "module A {}", "TSModuleDeclaration"); await testSnapshot( t, "declare module A { export function A(): void }", "TSModuleDeclaration", ); }); Deno.test("Plugin - TSModuleDeclaration + TSModuleBlock", async (t) => { await testSnapshot(t, "module A {}", "TSModuleDeclaration"); await testSnapshot( t, "namespace A { namespace B {} }", "TSModuleDeclaration", ); }); Deno.test("Plugin - TSQualifiedName", async (t) => { await testSnapshot(t, "type A = a.b;", "TSQualifiedName"); }); Deno.test("Plugin - TSTypeLiteral", async (t) => { await testSnapshot(t, "type A = { a: 1 };", "TSTypeLiteral"); }); Deno.test("Plugin - TSOptionalType", async (t) => { await testSnapshot(t, "type A = [number?]", "TSOptionalType"); }); Deno.test("Plugin - TSRestType", async (t) => { await testSnapshot(t, "type A = [...number[]]", "TSRestType"); }); Deno.test("Plugin - TSConditionalType", async (t) => { await testSnapshot( t, "type A = B extends C ? number : string;", "TSConditionalType", ); }); Deno.test("Plugin - TSInferType", async (t) => { await testSnapshot( t, "type A = T extends Array ? Item : T;", "TSInferType", ); }); Deno.test("Plugin - TSTypeOperator", async (t) => { await testSnapshot(t, "type A = keyof B", "TSTypeOperator"); await testSnapshot(t, "declare const sym1: unique symbol;", "TSTypeOperator"); await testSnapshot(t, "type A = readonly []", "TSTypeOperator"); }); Deno.test("Plugin - TSMappedType", async (t) => { await testSnapshot( t, "type A = { [P in keyof T]: boolean; };", "TSMappedType", ); await testSnapshot( t, "type A = { readonly [P in keyof T]: []; };", "TSMappedType", ); await testSnapshot( t, "type A = { -readonly [P in keyof T]: []; };", "TSMappedType", ); await testSnapshot( t, "type A = { +readonly [P in keyof T]: []; };", "TSMappedType", ); await testSnapshot( t, "type A = { [P in keyof T]?: boolean; };", "TSMappedType", ); await testSnapshot( t, "type A = { [P in keyof T]-?: boolean; };", "TSMappedType", ); await testSnapshot( t, "type A = { [P in keyof T]+?: boolean; };", "TSMappedType", ); }); Deno.test("Plugin - TSLiteralType", async (t) => { await testSnapshot(t, "type A = true", "TSLiteralType"); await testSnapshot(t, "type A = false", "TSLiteralType"); await testSnapshot(t, "type A = 1", "TSLiteralType"); await testSnapshot(t, 'type A = "foo"', "TSLiteralType"); }); Deno.test("Plugin - TSTemplateLiteralType", async (t) => { await testSnapshot(t, "type A = `a ${string}`", "TSTemplateLiteralType"); }); Deno.test("Plugin - TSTupleType + TSArrayType", async (t) => { await testSnapshot(t, "type A = [number]", "TSTupleType"); await testSnapshot(t, "type A = [x: number]", "TSTupleType"); await testSnapshot(t, "type A = [x: number]", "TSTupleType"); await testSnapshot(t, "type A = [...x: number[]]", "TSTupleType"); }); Deno.test("Plugin - TSArrayType", async (t) => { await testSnapshot(t, "type A = number[]", "TSArrayType"); }); Deno.test("Plugin - TSTypeQuery", async (t) => { await testSnapshot(t, "type A = typeof B", "TSTypeQuery"); }); Deno.test("Plugin - TS keywords", async (t) => { await testSnapshot(t, "type A = any", "TSAnyKeyword"); await testSnapshot(t, "type A = bigint", "TSBigIntKeyword"); await testSnapshot(t, "type A = boolean", "TSBooleanKeyword"); await testSnapshot(t, "type A = intrinsic", "TSIntrinsicKeyword"); await testSnapshot(t, "type A = never", "TSNeverKeyword"); await testSnapshot(t, "type A = null", "TSNullKeyword"); await testSnapshot(t, "type A = number", "TSNumberKeyword"); await testSnapshot(t, "type A = object", "TSObjectKeyword"); await testSnapshot(t, "type A = string", "TSStringKeyword"); await testSnapshot(t, "type A = symbol", "TSSymbolKeyword"); await testSnapshot(t, "type A = undefined", "TSUndefinedKeyword"); await testSnapshot(t, "type A = unknown", "TSUnknownKeyword"); await testSnapshot(t, "type A = void", "TSVoidKeyword"); });