NodeUtils.spec.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. import { IBlockStatementNode } from "../src/interfaces/nodes/IBlockStatementNode";
  2. import { IFunctionDeclarationNode } from "../src/interfaces/nodes/IFunctionDeclarationNode";
  3. import { IIdentifierNode } from "../src/interfaces/nodes/IIdentifierNode";
  4. import { IIfStatementNode } from "../src/interfaces/nodes/IIfStatementNode";
  5. import { ILiteralNode } from "../src/interfaces/nodes/ILiteralNode";
  6. import { INode } from "../src/interfaces/nodes/INode";
  7. import { IProgramNode } from "../src/interfaces/nodes/IProgramNode";
  8. import { NodeType } from "../src/enums/NodeType";
  9. import { NodeUtils } from '../src/NodeUtils';
  10. const assert: any = require('chai').assert;
  11. function getProgramNode (bodyNodes: INode[] = []): IProgramNode {
  12. return {
  13. type: NodeType.Program,
  14. body: bodyNodes
  15. };
  16. }
  17. function getBlockStatementNode (bodyNodes: INode[] = []): IBlockStatementNode {
  18. return {
  19. type: NodeType.BlockStatement,
  20. body: bodyNodes
  21. };
  22. }
  23. function getFunctionDeclarationNode (blockStatementNode: IBlockStatementNode): IFunctionDeclarationNode {
  24. return {
  25. type: NodeType.FunctionDeclaration,
  26. id: {
  27. type: NodeType.Identifier,
  28. name: 'test'
  29. },
  30. params: [],
  31. body: blockStatementNode,
  32. generator: false,
  33. expression: false
  34. };
  35. }
  36. function getIfStatementNode (blockStatementNode: IBlockStatementNode): IIfStatementNode {
  37. return {
  38. type: 'IfStatement',
  39. test: {
  40. type: 'Literal',
  41. value: true,
  42. raw: 'true'
  43. },
  44. consequent: blockStatementNode,
  45. alternate: null
  46. };
  47. }
  48. function getIdentifierNode (): IIdentifierNode {
  49. return {
  50. type: NodeType.Identifier,
  51. name: 'identifier',
  52. };
  53. }
  54. function getLiteralNode (): ILiteralNode {
  55. return {
  56. type: NodeType.Literal,
  57. value: 'string',
  58. raw: `'string'`,
  59. 'x-verbatim-property': `'string'`
  60. };
  61. }
  62. describe('NodeUtils', () => {
  63. describe('addXVerbatimPropertyToLiterals (node: INode): void', () => {
  64. let literalNode: any,
  65. expectedLiteralNode: any;
  66. beforeEach(() => {
  67. literalNode = getLiteralNode();
  68. expectedLiteralNode = Object.assign({}, literalNode);
  69. expectedLiteralNode['x-verbatim-property'] = `'string'`;
  70. NodeUtils.addXVerbatimPropertyToLiterals(literalNode);
  71. });
  72. it('should add `x-verbatim-property` to `Literal` node', () => {
  73. assert.deepEqual(literalNode, expectedLiteralNode);
  74. });
  75. });
  76. describe('appendNode (blockScopeBody: INode[], node: INode): void', () => {
  77. let blockStatementNode: IBlockStatementNode,
  78. expectedBlockStatementNode: IBlockStatementNode,
  79. identifierNode: IIdentifierNode;
  80. beforeEach(() => {
  81. identifierNode = getIdentifierNode();
  82. blockStatementNode = getBlockStatementNode();
  83. expectedBlockStatementNode = Object.assign({}, blockStatementNode);
  84. expectedBlockStatementNode.body.push(identifierNode);
  85. NodeUtils.appendNode(blockStatementNode.body, identifierNode);
  86. });
  87. it('should append given node to a `BlockStatement` node body', () => {
  88. assert.deepEqual(blockStatementNode, expectedBlockStatementNode);
  89. });
  90. it('should does not change `BlockStatement` node body if given node is not a valid Node', () => {
  91. assert.doesNotChange(
  92. () => NodeUtils.appendNode(blockStatementNode.body, <INode>null),
  93. blockStatementNode,
  94. 'body'
  95. );
  96. assert.doesNotChange(
  97. () => NodeUtils.appendNode(blockStatementNode.body, <INode>{}),
  98. blockStatementNode,
  99. 'body'
  100. );
  101. });
  102. });
  103. describe('getBlockStatementNodeByIndex (node: INode, index: number = 0): INode', () => {
  104. let blockStatementNode: IBlockStatementNode,
  105. identifierNode: IIdentifierNode,
  106. literalNode: ILiteralNode;
  107. beforeEach(() => {
  108. identifierNode = getIdentifierNode();
  109. literalNode = getLiteralNode();
  110. blockStatementNode = getBlockStatementNode([
  111. identifierNode,
  112. literalNode
  113. ]);
  114. });
  115. it('should return block-statement child node of given node if that node has block-statement', () => {
  116. assert.deepEqual(NodeUtils.getBlockStatementNodeByIndex(blockStatementNode), identifierNode);
  117. assert.deepEqual(NodeUtils.getBlockStatementNodeByIndex(blockStatementNode, 1), literalNode);
  118. });
  119. it('should throw a `ReferenceError` if index is out of boundaries', () => {
  120. assert.throws(() => NodeUtils.getBlockStatementNodeByIndex(blockStatementNode, 2), ReferenceError);
  121. });
  122. it('should throw a `TypeError` if node have no a block-statement', () => {
  123. assert.throws(() => NodeUtils.getBlockStatementNodeByIndex(identifierNode, 1), TypeError);
  124. });
  125. });
  126. describe('getBlockScopeOfNode (node: INode, depth: number = 0): TNodeWithBlockStatement', () => {
  127. let functionDeclarationBlockStatementNode: IBlockStatementNode,
  128. ifStatementBlockStatementNode1: IBlockStatementNode,
  129. ifStatementBlockStatementNode2: IBlockStatementNode,
  130. ifStatementNode1: IIfStatementNode,
  131. ifStatementNode2: IIfStatementNode,
  132. identifierNode: IIdentifierNode,
  133. functionDeclarationNode: IFunctionDeclarationNode,
  134. literalNode1: ILiteralNode,
  135. literalNode2: ILiteralNode,
  136. programNode: IProgramNode;
  137. beforeEach(() => {
  138. identifierNode = getIdentifierNode();
  139. literalNode1 = getLiteralNode();
  140. literalNode2 = getLiteralNode();
  141. ifStatementBlockStatementNode2 = getBlockStatementNode([
  142. literalNode1,
  143. literalNode2
  144. ]);
  145. ifStatementNode2 = getIfStatementNode(ifStatementBlockStatementNode2);
  146. ifStatementBlockStatementNode1 = getBlockStatementNode([
  147. ifStatementNode2
  148. ]);
  149. ifStatementNode1 = getIfStatementNode(ifStatementBlockStatementNode1);
  150. functionDeclarationBlockStatementNode = getBlockStatementNode([
  151. identifierNode,
  152. ifStatementNode1
  153. ]);
  154. functionDeclarationNode = getFunctionDeclarationNode(functionDeclarationBlockStatementNode);
  155. programNode = getProgramNode([
  156. functionDeclarationNode
  157. ]);
  158. programNode['parentNode'] = programNode;
  159. functionDeclarationNode['parentNode'] = programNode;
  160. functionDeclarationBlockStatementNode['parentNode'] = functionDeclarationNode;
  161. identifierNode['parentNode'] = functionDeclarationBlockStatementNode;
  162. ifStatementNode1['parentNode'] = functionDeclarationBlockStatementNode;
  163. ifStatementBlockStatementNode1['parentNode'] = ifStatementNode1;
  164. ifStatementNode2['parentNode'] = ifStatementBlockStatementNode1;
  165. ifStatementBlockStatementNode2['parentNode'] = ifStatementNode2;
  166. literalNode1['parentNode'] = ifStatementBlockStatementNode2;
  167. });
  168. it('should return block-scope node for given node', () => {
  169. assert.deepEqual(NodeUtils.getBlockScopeOfNode(identifierNode), functionDeclarationBlockStatementNode);
  170. assert.deepEqual(NodeUtils.getBlockScopeOfNode(identifierNode, 1), programNode);
  171. assert.deepEqual(NodeUtils.getBlockScopeOfNode(functionDeclarationNode), programNode);
  172. assert.deepEqual(NodeUtils.getBlockScopeOfNode(functionDeclarationBlockStatementNode), programNode);
  173. assert.deepEqual(NodeUtils.getBlockScopeOfNode(programNode), programNode);
  174. assert.deepEqual(NodeUtils.getBlockScopeOfNode(literalNode1), functionDeclarationBlockStatementNode);
  175. });
  176. it('should throw a `ReferenceError` if node has no `parentNode` property', () => {
  177. assert.throws(() => NodeUtils.getBlockScopeOfNode(literalNode2), ReferenceError);
  178. });
  179. });
  180. describe('insertNodeAtIndex (blockScopeBody: INode[], node: INode, index: number): void', () => {
  181. let blockStatementNode: IBlockStatementNode,
  182. expectedBlockStatementNode: IBlockStatementNode,
  183. identifierNode: IIdentifierNode,
  184. literalNode: ILiteralNode;
  185. beforeEach(() => {
  186. identifierNode = getIdentifierNode();
  187. literalNode = getLiteralNode();
  188. blockStatementNode = getBlockStatementNode([
  189. identifierNode
  190. ]);
  191. expectedBlockStatementNode = Object.assign({}, blockStatementNode);
  192. expectedBlockStatementNode['body'].push(literalNode);
  193. NodeUtils.insertNodeAtIndex(blockStatementNode.body, literalNode, 1);
  194. });
  195. it('should insert given node in `BlockStatement` node body at index', () => {
  196. assert.deepEqual(blockStatementNode, expectedBlockStatementNode);
  197. });
  198. it('should does not change `BlockStatement` node body if given node is not a valid Node', () => {
  199. assert.doesNotChange(
  200. () => NodeUtils.insertNodeAtIndex(blockStatementNode.body, <INode>null, 1),
  201. blockStatementNode,
  202. 'body'
  203. );
  204. assert.doesNotChange(
  205. () => NodeUtils.insertNodeAtIndex(blockStatementNode.body, <INode>{}, 1),
  206. blockStatementNode,
  207. 'body'
  208. );
  209. });
  210. });
  211. describe('parentize (node: INode): void', () => {
  212. let blockStatementNode: IBlockStatementNode,
  213. identifierNode: IIdentifierNode,
  214. literalNode: ILiteralNode,
  215. programNode: IProgramNode;
  216. beforeEach(() => {
  217. identifierNode = getIdentifierNode();
  218. literalNode = getLiteralNode();
  219. blockStatementNode = getBlockStatementNode([
  220. identifierNode,
  221. literalNode
  222. ]);
  223. programNode = getProgramNode([
  224. blockStatementNode
  225. ]);
  226. NodeUtils.parentize(blockStatementNode);
  227. });
  228. it('should parentize given AST-tree', () => {
  229. assert.deepEqual(blockStatementNode['parentNode'], programNode);
  230. assert.deepEqual(identifierNode['parentNode'], blockStatementNode);
  231. assert.deepEqual(literalNode['parentNode'], blockStatementNode);
  232. });
  233. });
  234. describe('prependNode (blockScopeBody: INode[], node: INode): void', () => {
  235. let blockStatementNode: IBlockStatementNode,
  236. expectedBlockStatementNode: IBlockStatementNode,
  237. identifierNode: IIdentifierNode,
  238. literalNode: ILiteralNode;
  239. beforeEach(() => {
  240. identifierNode = getIdentifierNode();
  241. literalNode = getLiteralNode();
  242. blockStatementNode = getBlockStatementNode([
  243. identifierNode
  244. ]);
  245. expectedBlockStatementNode = Object.assign({}, blockStatementNode);
  246. expectedBlockStatementNode['body'].unshift(literalNode);
  247. NodeUtils.prependNode(blockStatementNode.body, literalNode);
  248. });
  249. it('should prepend given node to a `BlockStatement` node body', () => {
  250. assert.deepEqual(blockStatementNode, expectedBlockStatementNode);
  251. });
  252. it('should does not change `BlockStatement` node body if given node is not a valid Node', () => {
  253. assert.doesNotChange(
  254. () => NodeUtils.prependNode(blockStatementNode.body, <INode>null),
  255. blockStatementNode,
  256. 'body'
  257. );
  258. assert.doesNotChange(
  259. () => NodeUtils.prependNode(blockStatementNode.body, <INode>{}),
  260. blockStatementNode,
  261. 'body'
  262. );
  263. });
  264. });
  265. });