Skip to content

Commit

Permalink
test: reference tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bradzacher committed May 3, 2020
1 parent 887fcf0 commit b7f4005
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 15 deletions.
1 change: 1 addition & 0 deletions packages/experimental-utils/src/ts-estree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export {
TSESTree,
} from '@typescript-eslint/typescript-estree/dist/ts-estree';
export { simpleTraverse } from '@typescript-eslint/typescript-estree/dist/simple-traverse';
export { visitorKeys } from '@typescript-eslint/typescript-estree/dist/visitor-keys';
export { ParserServices } from '@typescript-eslint/typescript-estree/dist/parser-options';
8 changes: 6 additions & 2 deletions packages/scope-manager/src/analyze.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
import {
TSESLint,
TSESTree,
visitorKeys,
} from '@typescript-eslint/experimental-utils';
import { Referencer, ReferencerOptions } from './referencer';
import { ScopeManager } from './ScopeManager';

Expand Down Expand Up @@ -41,7 +45,7 @@ const DEFAULT_OPTIONS: Options = {
impliedStrict: false,
sourceType: 'script',
ecmaVersion: 2018,
childVisitorKeys: null,
childVisitorKeys: visitorKeys,
fallback: 'iteration',
};

Expand Down
1 change: 1 addition & 0 deletions packages/scope-manager/tests/serializers/Reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Reference } from '../../src/referencer/Reference';
const serializer = createSerializer(Reference, [
'identifier',
'init',
'isTypeReference',
'resolved',
'writeExpr',
]);
Expand Down
110 changes: 110 additions & 0 deletions packages/scope-manager/tests/types/reference-type.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils';
import { getSpecificNode, parseAndAnalyze } from '../util';

describe('referencing a type - positive', () => {
it('records a reference when a type is referenced from a type', () => {
const { ast, scopeManager } = parseAndAnalyze(`
type TypeDecl = string;
type OtherType = TypeDecl;
`);
const node = getSpecificNode(
ast,
AST_NODE_TYPES.TSTypeAliasDeclaration,
n => (n.id.name === 'TypeDecl' ? n : null),
);
const variable = scopeManager.getDeclaredVariables(node)[0];

// there should be one reference from the declaration itself
expect(variable.references).toHaveLength(2);
expect(variable.references[0].identifier.parent).toBe(node);

// and one reference from the usage
const otherTypeDecl = getSpecificNode(
ast,
AST_NODE_TYPES.TSTypeAliasDeclaration,
n => (n.id.name === 'OtherType' ? n : null),
);
expect(variable.references[1].identifier.parent?.parent).toBe(
otherTypeDecl,
);
});

it('records a reference when a dual value-type is referenced from a type', () => {
const { ast, scopeManager } = parseAndAnalyze(`
class Class {}
type Type = Class;
`);
const node = getSpecificNode(ast, AST_NODE_TYPES.ClassDeclaration);
const variable = scopeManager.getDeclaredVariables(node)[0];

// there should be one reference from the type declaration
expect(variable.references).toHaveLength(1);
const otherTypeDecl = getSpecificNode(
ast,
AST_NODE_TYPES.TSTypeAliasDeclaration,
);
expect(variable.references[0].identifier.parent?.parent).toBe(
otherTypeDecl,
);
});

it('records a reference when a generic type parameter is referenced from its type', () => {
const { ast, scopeManager } = parseAndAnalyze(`
type TypeDecl<TypeParam> = TypeParam;
`);
const node = getSpecificNode(ast, AST_NODE_TYPES.TSTypeParameter);
const variable = scopeManager.getDeclaredVariables(node)[0];

// there should be one reference from the declaration itself
expect(variable.references).toHaveLength(2);
expect(variable.references[0].identifier.parent).toBe(node);

// and one reference from the usage
const usage = getSpecificNode(ast, AST_NODE_TYPES.TSTypeReference);
expect(variable.references[1].identifier.parent).toBe(usage);
});
});

describe('referencing a type - negative', () => {
it('does not record a reference when a value is referenced from a type', () => {
const { ast, scopeManager } = parseAndAnalyze(`
const value = 1;
type Type = value;
`);
const node = getSpecificNode(ast, AST_NODE_TYPES.VariableDeclarator);
const variable = scopeManager.getDeclaredVariables(node)[0];

// there should be one reference from the declaration itself
expect(variable.references).toHaveLength(1);
expect(variable.references[0].identifier.parent).toBe(node);
});

it('does not record a reference when a type is referenced from a value', () => {
const { ast, scopeManager } = parseAndAnalyze(`
type Type = value;
const value = 1;
`);
const node = getSpecificNode(ast, AST_NODE_TYPES.TSTypeAliasDeclaration);
const variable = scopeManager.getDeclaredVariables(node)[0];

// there should be one reference from the declaration itself
expect(variable.references).toHaveLength(1);
expect(variable.references[0].identifier.parent).toBe(node);
});

it.todo(
'does not record a reference when a type is referenced from outside its declaring type',
// () => {
// const { ast, scopeManager } = parseAndAnalyze(`
// type TypeDecl<TypeParam> = T;
// type Other = TypeParam;
// `);
// const node = getSpecificNode(ast, AST_NODE_TYPES.TSTypeParameter);
// const variable = scopeManager.getDeclaredVariables(node)[0];

// // there should be one reference from the declaration itself
// expect(variable.references).toHaveLength(1);
// expect(variable.references[0].identifier.parent).toBe(node);
// },
);
});
19 changes: 12 additions & 7 deletions packages/scope-manager/tests/types/variable-definition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ describe('variable definition', () => {
references: Array [
Reference$1 {
identifier: Identifier<"TypeDecl">,
isTypeReference: true,
resolved: Variable$1,
},
],
Expand Down Expand Up @@ -68,8 +69,9 @@ describe('variable definition', () => {
],
name: "InterfaceDecl",
references: Array [
Reference$2 {
Reference$1 {
identifier: Identifier<"InterfaceDecl">,
isTypeReference: true,
resolved: Variable$1,
},
],
Expand Down Expand Up @@ -101,6 +103,7 @@ describe('variable definition', () => {
references: Array [
Reference$2 {
identifier: Identifier<"TypeParam">,
isTypeReference: true,
resolved: Variable$2,
},
],
Expand All @@ -120,9 +123,9 @@ describe('variable definition', () => {
);
expect(scopeManager.getDeclaredVariables(node)).toMatchInlineSnapshot(`
Array [
Variable$2 {
Variable$3 {
defs: Array [
TypeDefinition$2 {
TypeDefinition$3 {
name: Identifier<"Inferred">,
node: TSTypeParameter$1 {
name: Identifier<"Inferred">,
Expand All @@ -134,13 +137,15 @@ describe('variable definition', () => {
],
name: "Inferred",
references: Array [
Reference$4 {
Reference$5 {
identifier: Identifier<"Inferred">,
resolved: Variable$2,
isTypeReference: true,
resolved: Variable$3,
},
Reference$5 {
Reference$6 {
identifier: Identifier<"Inferred">,
resolved: Variable$2,
isTypeReference: true,
resolved: Variable$3,
},
],
},
Expand Down
17 changes: 13 additions & 4 deletions packages/scope-manager/tests/util/getSpecificNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,20 @@ function getSpecificNode<
cb?: (node: TNode) => TReturnType | null | undefined,
): TReturnType {
let node: TReturnType | null | undefined = null;
simpleTraverse(ast, {
[selector](n: TNode) {
node = cb ? cb(n) : ((n as never) as TReturnType);
simpleTraverse(
ast,
{
[selector](n: TNode) {
const res = cb ? cb(n) : ((n as never) as TReturnType);
if (res) {
// the callback shouldn't match multiple nodes or else tests may behave weirdly
expect(node).toBeFalsy();
node = res;
}
},
},
});
true,
);

expect(node).not.toBeFalsy();
return node!;
Expand Down
14 changes: 12 additions & 2 deletions packages/typescript-estree/src/simple-traverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,22 @@ type SimpleTraverseOptions =
class SimpleTraverser {
private readonly allVisitorKeys = visitorKeys;
private readonly selectors: SimpleTraverseOptions;
private readonly setParentPointers: boolean;

constructor(selectors: SimpleTraverseOptions) {
constructor(selectors: SimpleTraverseOptions, setParentPointers = false) {
this.selectors = selectors;
this.setParentPointers = setParentPointers;
}

traverse(node: unknown, parent: TSESTree.Node | undefined): void {
if (!isValidNode(node)) {
return;
}

if (this.setParentPointers) {
node.parent = parent;
}

if ('enter' in this.selectors) {
this.selectors.enter(node, parent);
} else if (node.type in this.selectors) {
Expand Down Expand Up @@ -66,6 +72,10 @@ class SimpleTraverser {
export function simpleTraverse(
startingNode: TSESTree.Node,
options: SimpleTraverseOptions,
setParentPointers = false,
): void {
new SimpleTraverser(options).traverse(startingNode, undefined);
new SimpleTraverser(options, setParentPointers).traverse(
startingNode,
undefined,
);
}

0 comments on commit b7f4005

Please sign in to comment.