• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { describe, it } from 'mocha';
17import { expect } from 'chai';
18import { NodeUtils, collectReservedNameForObf, hasExportModifier } from '../../../src/utils/NodeUtils';
19import * as ts from 'typescript'
20import sinon from 'sinon';
21import { MergedConfig } from '../../../src/initialization/ConfigResolver';
22import { UnobfuscationCollections } from '../../../src/utils/CommonCollections';
23
24type Mutable<T extends object> = { -readonly [K in keyof T]: T[K] }
25
26describe('test for NodeUtils', function () {
27    describe('test for isPropertyDeclarationNode', function () {
28        it('should return false if node has no parent', function () {
29            const node = ts.factory.createIdentifier('name');
30            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.false;
31        })
32        it('should return ture when node.parent is PropertyAssignment', function () {
33            const node = ts.factory.createIdentifier('name');
34            const parent = ts.factory.createPropertyAssignment(node, ts.factory.createNumericLiteral('1'));
35            (node as Mutable<ts.Node>).parent = parent;
36            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
37        })
38        it('should return ture when node.parent is ComputedPropertyName', function () {
39            const node = ts.factory.createIdentifier('name');
40            const parent = ts.factory.createComputedPropertyName(node);
41            (node as Mutable<ts.Node>).parent = parent;
42            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
43        })
44        it('should return ture when node.parent is BindingElement', function () {
45            const node = ts.factory.createIdentifier('name');
46            const parent = ts.factory.createBindingElement(undefined, node, 'bindingElement');
47            (node as Mutable<ts.Node>).parent = parent;
48            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
49        })
50        it('should return ture when node.parent is PropertySignature', function () {
51            const node = ts.factory.createIdentifier('name');
52            const parent = ts.factory.createPropertySignature(undefined, node, undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword));
53            (node as Mutable<ts.Node>).parent = parent;
54            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
55        })
56        it('should return ture when node.parent is MethodSignature', function () {
57            const node = ts.factory.createIdentifier('name');
58            const parent = ts.factory.createMethodSignature(undefined, node, undefined, undefined, [], undefined);
59            (node as Mutable<ts.Node>).parent = parent;
60            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
61        })
62        it('should return ture when node.parent is EnumMember', function () {
63            const node = ts.factory.createIdentifier('name');
64            const parent = ts.factory.createEnumMember(node);
65            (node as Mutable<ts.Node>).parent = parent;
66            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
67        })
68        it('should return ture when node.parent is PropertyDeclaration', function () {
69            const node = ts.factory.createIdentifier('name');
70            const parent = ts.factory.createPropertyDeclaration(undefined, undefined, node, undefined, undefined, undefined);
71            (node as Mutable<ts.Node>).parent = parent;
72            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
73        })
74        it('should return ture when node.parent is MethodDeclaration', function () {
75            const node = ts.factory.createIdentifier('name');
76            const parent = ts.factory.createMethodDeclaration(undefined, undefined, undefined, node, undefined, undefined, [], undefined, undefined);
77            (node as Mutable<ts.Node>).parent = parent;
78            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
79        })
80        it('should return ture when node.parent is SetAccessorDeclaration', function () {
81            const node = ts.factory.createIdentifier('name');
82            const parent = ts.factory.createSetAccessorDeclaration(undefined, undefined, node, [], undefined);
83            (node as Mutable<ts.Node>).parent = parent;
84            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
85        })
86        it('should return ture when node.parent is GetAccessorDeclaration', function () {
87            const node = ts.factory.createIdentifier('name');
88            const parent = ts.factory.createGetAccessorDeclaration(undefined, undefined, node, [], undefined, undefined);
89            (node as Mutable<ts.Node>).parent = parent;
90            expect(NodeUtils.isPropertyDeclarationNode(node)).to.be.true;
91        })
92    })
93    describe('test for isPropertyOrElementAccessNode', function () {
94        let isPropertyAccessNodeStub;
95        let isElementAccessNodeStub;
96
97        beforeEach(function () {
98            isPropertyAccessNodeStub = sinon.stub(NodeUtils, 'isPropertyAccessNode').returns(false);
99            isElementAccessNodeStub = sinon.stub(NodeUtils, 'isElementAccessNode').returns(false);
100        });
101
102        afterEach(function () {
103            isPropertyAccessNodeStub.restore();
104            isElementAccessNodeStub.restore();
105        });
106
107        it('should return true when node is a PropertyAccessNode', function () {
108            const node = ts.factory.createIdentifier('name');
109            isPropertyAccessNodeStub.returns(true);
110            expect(NodeUtils.isPropertyOrElementAccessNode(node)).to.be.true;
111        })
112        it('should return true when node is a isElementAccessNode', function () {
113            const node = ts.factory.createIdentifier('name');
114            isElementAccessNodeStub.returns(true);
115            expect(NodeUtils.isPropertyOrElementAccessNode(node)).to.be.true;
116        })
117        it('should return false when both isPropertyAccessNode and isElementAccessNode return false', function () {
118            const node = ts.factory.createIdentifier('name');
119            expect(NodeUtils.isPropertyOrElementAccessNode(node)).to.be.false;
120        })
121    })
122    describe('test for isPropertyAccessNode', function () {
123        let isInClassDeclarationStub;
124        let isInExpressionStub;
125
126        beforeEach(function () {
127            isInClassDeclarationStub = sinon.stub(NodeUtils, 'isInClassDeclaration').returns(false);
128            isInExpressionStub = sinon.stub(NodeUtils, 'isInExpression').returns(false);
129        });
130
131        afterEach(function () {
132            isInClassDeclarationStub.restore();
133            isInExpressionStub.restore();
134        });
135
136        it('should return false if node has no parent', function () {
137            const node = ts.factory.createIdentifier('name');
138            expect(NodeUtils.isPropertyAccessNode(node)).to.be.false;
139        })
140        it('should return true if isPropertyAccessExpression and parent.name equals to node', function () {
141            const node = ts.factory.createIdentifier('name');
142            const parent = ts.factory.createPropertyAccessExpression(node, node);
143            (node as Mutable<ts.Node>).parent = parent;
144            expect(NodeUtils.isPropertyAccessNode(node)).to.be.true;
145        })
146        it('should return isInExpression(parent) if isPrivateIdentifier and isInClassDeclaration', function () {
147            const node = ts.factory.createPrivateIdentifier("#name");
148            const parent = ts.factory.createIdentifier('parent');
149            (node as Mutable<ts.Node>).parent = parent;
150            isInClassDeclarationStub.returns(true);
151            isInExpressionStub.returns(true)
152            expect(NodeUtils.isPropertyAccessNode(node)).to.be.true;
153            isInExpressionStub.returns(false)
154            expect(NodeUtils.isPropertyAccessNode(node)).to.be.false;
155        })
156        it('should return true if isQualifiedName and parent.right equals to node', function () {
157            const node = ts.factory.createIdentifier('name');
158            const parent = ts.factory.createQualifiedName(node, node);
159            (node as Mutable<ts.Node>).parent = parent;
160            expect(NodeUtils.isPropertyAccessNode(node)).to.be.true;
161        })
162    })
163    describe('test for isInClassDeclaration', function () {
164        it('should return flase when node is undefined', function () {
165            expect(NodeUtils.isInClassDeclarationForTest(undefined)).to.be.false;
166        })
167        it('should return true when node is ClassDeclaration', function () {
168            const node = ts.factory.createClassDeclaration(undefined, undefined, undefined, undefined, undefined, []);
169            expect(NodeUtils.isInClassDeclarationForTest(node)).to.be.true;
170        })
171        it('should return true when node is ClassExpression', function () {
172            const node = ts.factory.createClassExpression(undefined, undefined, undefined, undefined, undefined, []);
173            expect(NodeUtils.isInClassDeclarationForTest(node)).to.be.true;
174        })
175        it('should return true when node.parent is isInClassDeclaration', function () {
176            const node = ts.factory.createIdentifier('name');
177            const parent = ts.factory.createClassExpression(undefined, undefined, undefined, undefined, undefined, []);
178            (node as Mutable<ts.Node>).parent = parent;
179            expect(NodeUtils.isInClassDeclarationForTest(node)).to.be.true;
180        })
181    })
182    describe('test for isInClassDeclaration', function () {
183        it('should return flase when node is undefined', function () {
184            expect(NodeUtils.isInClassDeclarationForTest(undefined)).to.be.false;
185        })
186        it('should return true when node is ClassDeclaration', function () {
187            const node = ts.factory.createClassDeclaration(undefined, undefined, undefined, undefined, undefined, []);
188            expect(NodeUtils.isInClassDeclarationForTest(node)).to.be.true;
189        })
190        it('should return true when node is ClassExpression', function () {
191            const node = ts.factory.createClassExpression(undefined, undefined, undefined, undefined, undefined, []);
192            expect(NodeUtils.isInClassDeclarationForTest(node)).to.be.true;
193        })
194        it('should return true when node.parent is isInClassDeclaration', function () {
195            const node = ts.factory.createIdentifier('name');
196            const parent = ts.factory.createClassExpression(undefined, undefined, undefined, undefined, undefined, []);
197            (node as Mutable<ts.Node>).parent = parent;
198            expect(NodeUtils.isInClassDeclarationForTest(node)).to.be.true;
199        })
200    })
201    describe('test for isInExpression', function () {
202        it('should return flase when node is undefined', function () {
203            expect(NodeUtils.isInExpressionForTest(undefined)).to.be.false;
204        })
205        it('should return isInOperator(node) when node is not undefined', function () {
206            let isInOperatorStub = sinon.stub(NodeUtils, 'isInOperator').returns(false);
207            const node = ts.factory.createIdentifier('name');
208            expect(NodeUtils.isInExpressionForTest(node)).to.be.false;
209            isInOperatorStub.returns(true);
210            expect(NodeUtils.isInExpressionForTest(node)).to.be.true;
211            isInOperatorStub.restore();
212        })
213    })
214    describe('test for isInOperator', function () {
215        it('should return true when node is binary expression and operator is InKeyword', function () {
216            const name = ts.factory.createIdentifier('name');
217            const node = ts.factory.createBinaryExpression(name, ts.SyntaxKind.InKeyword, name);
218            expect(NodeUtils.isInOperatorForTest(node)).to.be.true;
219        })
220        it('should return false when node is not binary expression', function () {
221            const node = ts.factory.createIdentifier('name');
222            expect(NodeUtils.isInOperatorForTest(node)).to.be.false;
223        })
224        it('should return false when operator is not Inkeyword', function () {
225            const name = ts.factory.createIdentifier('name');
226            const node = ts.factory.createBinaryExpression(name, ts.SyntaxKind.PlusEqualsToken, name);
227            expect(NodeUtils.isInOperatorForTest(node)).to.be.false;
228        })
229    })
230
231    describe('test for isElementAccessNode', function () {
232        it('should return false if node has no parent', function () {
233            const node = ts.factory.createIdentifier('name');
234            expect(NodeUtils.isElementAccessNode(node)).to.be.false;
235        })
236        it('should return true if isElementAccessExpression and parent argumentExpression equals to node', function () {
237            const node = ts.factory.createIdentifier('name');
238            const parent = ts.factory.createElementAccessExpression(node, node);
239            (node as Mutable<ts.Node>).parent = parent;
240            expect(NodeUtils.isElementAccessNode(node)).to.be.true;
241        })
242    })
243
244    describe('test for isIndexedAccessNode', function () {
245        it('should return false if node has no parent', function () {
246            const node = ts.factory.createIdentifier('name');
247            expect(NodeUtils.isIndexedAccessNode(node)).to.be.false;
248        })
249        it('should return false if isIndexedAccessNode but parent literalType not equals to node', function () {
250            const node1 = ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("name"));
251            const node2 = ts.factory.createLiteralTypeNode(ts.factory.createNumericLiteral(1));
252            const parent = ts.factory.createIndexedAccessTypeNode(node2, node2);
253            (node1 as Mutable<ts.Node>).parent = parent;
254            (node2 as Mutable<ts.Node>).parent = parent;
255            expect(NodeUtils.isIndexedAccessNode(node1)).to.be.false;
256            expect(NodeUtils.isIndexedAccessNode(node2)).to.be.true;
257        })
258        it('should return true if isIndexedAccessNode and parent literalType equals to node', function () {
259            const node1 = ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("name"));
260            const node2 = ts.factory.createLiteralTypeNode(ts.factory.createNumericLiteral(1));
261            const parent1 = ts.factory.createIndexedAccessTypeNode(node1, node1);
262            const parent2 = ts.factory.createIndexedAccessTypeNode(node2, node2);
263            (node1 as Mutable<ts.Node>).parent = parent1;
264            (node2 as Mutable<ts.Node>).parent = parent2;
265            expect(NodeUtils.isIndexedAccessNode(node1)).to.be.true;
266            expect(NodeUtils.isIndexedAccessNode(node2)).to.be.true;
267        })
268    })
269
270    describe('test for isClassPropertyInConstructorParams', function () {
271        it('should return false if node is not an Identifier', function () {
272            const node = ts.factory.createRegularExpressionLiteral('name');
273            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
274        })
275        it('should return false when node has no parent', function () {
276            const node = ts.factory.createIdentifier('name');
277            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
278        })
279        it('should return false when node parent is not a parameter', function () {
280            const node = ts.factory.createIdentifier('name');
281            const parent = ts.factory.createElementAccessExpression(node, node);
282            (node as Mutable<ts.Node>).parent = parent;
283            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
284        })
285        it('should return false when modifiers is undefined', function () {
286            const node = ts.factory.createIdentifier('name');
287            const parent = ts.factory.createParameterDeclaration([], undefined, node, undefined, undefined, undefined);
288            (node as Mutable<ts.Node>).parent = parent;
289            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
290        })
291        it('should return false when modifiers length is 0 or modifier is ParameterPropertyModifier', function () {
292            const node = ts.factory.createIdentifier('name');
293            const parent = ts.factory.createParameterDeclaration([ts.factory.createModifier(ts.SyntaxKind.AbstractKeyword)], undefined, node, undefined, undefined, undefined);
294            (node as Mutable<ts.Node>).parent = parent;
295            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
296        })
297        it('should return true when node parent parent is ConstructorDeclaration', function () {
298            const node = ts.factory.createIdentifier('name');
299            const parent = ts.factory.createParameterDeclaration([ts.factory.createModifier(ts.SyntaxKind.PublicKeyword)], undefined, node, undefined, undefined, undefined);
300            const parentParent = ts.factory.createConstructorDeclaration(undefined, [ts.factory.createModifier(ts.SyntaxKind.AbstractKeyword)], [], undefined);
301            (parent as Mutable<ts.Node>).parent = parentParent;
302            (node as Mutable<ts.Node>).parent = parent;
303            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.true;
304        })
305    })
306
307    describe('test for isClassPropertyInConstructorBody', function () {
308        it('should return false if node is not an Identifier', function () {
309            const node = ts.factory.createRegularExpressionLiteral('name');
310            expect(NodeUtils.isClassPropertyInConstructorBody(node, new Set)).to.be.false;
311        })
312        it('shound return true when node parent is ConstructorDeclaration and constructorParams has id', function () {
313            const node = ts.factory.createIdentifier('name');
314            const set: Set<string> = new Set();
315            set.add('name');
316            const parent = ts.factory.createConstructorDeclaration(undefined, [ts.factory.createModifier(ts.SyntaxKind.AbstractKeyword)], [], undefined);
317            (node as Mutable<ts.Node>).parent = parent;
318            expect(NodeUtils.isClassPropertyInConstructorBody(node, set)).to.be.true;
319        })
320        it('should return false when curNode is not a ConstructorDeclaration or id does not exist in constructorParams', function () {
321            const node = ts.factory.createIdentifier('name');
322            const set: Set<string> = new Set();
323            set.add('test');
324            const parent = ts.factory.createElementAccessExpression(node, node);
325            (node as Mutable<ts.Node>).parent = parent;
326            expect(NodeUtils.isClassPropertyInConstructorBody(node, set)).to.be.false;
327        })
328    })
329
330    describe('test for isPropertyNode', function () {
331        it('should return true when node is PropertyOrElementAccessNode', function () {
332            const node = ts.factory.createIdentifier('name');
333            const parent = ts.factory.createElementAccessExpression(node, node);
334            (node as Mutable<ts.Node>).parent = parent;
335            expect(NodeUtils.isPropertyNode(node)).to.be.true;
336        })
337        it('should return true when node is a PropertyDeclarationNode', function () {
338            const node = ts.factory.createIdentifier('name');
339            const parent = ts.factory.createPropertyAssignment(node, node);
340            (node as Mutable<ts.Node>).parent = parent;
341            expect(NodeUtils.isPropertyNode(node)).to.be.true;
342        })
343        it('should return true when node is a IndexedAccessNode', function () {
344            const node = ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral("name"));
345            const parent = ts.factory.createIndexedAccessTypeNode(node, node);
346            (node as Mutable<ts.Node>).parent = parent;
347            expect(NodeUtils.isPropertyNode(node)).to.be.true;
348        })
349    })
350
351    describe('test for isObjectBindingPatternAssignment', function () {
352        it('should return false when node is not VariableDeclaration', function () {
353            const node = ts.factory.createObjectBindingPattern([]);
354            const parent = ts.factory.createParameterDeclaration(undefined, undefined, undefined, node, undefined, undefined, undefined);
355            (node as Mutable<ts.Node>).parent = parent;
356            expect(NodeUtils.isObjectBindingPatternAssignment(node)).to.be.false;
357        })
358        it('should return true when node parent initializer is CallExpression', function () {
359            const node = ts.factory.createObjectBindingPattern([]);
360            const parent = ts.factory.createVariableDeclaration(node, undefined, undefined, undefined);
361            const initializer = ts.factory.createCallExpression(ts.factory.createIdentifier('name'), undefined, undefined);
362            (parent as Mutable<ts.VariableDeclaration>).initializer = initializer;
363            (node as Mutable<ts.Node>).parent = parent;
364            expect(NodeUtils.isObjectBindingPatternAssignment(node)).to.be.true;
365        })
366    })
367
368    describe('test for isDeclarationFile', function () {
369        it('should return false when sourceFile is not a declarationFile', function () {
370            const endOfFileToken = ts.factory.createToken(ts.SyntaxKind.EndOfFileToken);
371            const sourceFile = ts.factory.createSourceFile([], endOfFileToken, ts.NodeFlags.AwaitContext);
372            expect(NodeUtils.isDeclarationFile(sourceFile)).to.be.false;
373        })
374    })
375
376    describe('test for getSourceFileOfNode', function () {
377        it('should return node when node kind is SyntaxKind.SourceFile', function () {
378            const node = ts.factory.createIdentifier('name');
379            const kind = ts.SyntaxKind.SourceFile;
380            (node as Mutable<ts.Node>).kind = kind;
381            expect(NodeUtils.getSourceFileOfNode(node)).to.equal(node);
382        })
383        it('should return node parent when node kind is not SyntaxKind.SourceFile', function () {
384            const node = ts.factory.createIdentifier('name');
385            const kind = ts.SyntaxKind.SymbolKeyword;
386            (node as Mutable<ts.Node>).kind = kind;
387            const endOfFileToken = ts.factory.createToken(ts.SyntaxKind.EndOfFileToken);
388            const parent = ts.factory.createSourceFile([], endOfFileToken, ts.NodeFlags.AwaitContext);
389            (node as Mutable<ts.Node>).parent = parent;
390            expect(NodeUtils.getSourceFileOfNode(node)).to.equal(node.parent);
391        })
392    })
393
394    describe('test for isDETSFile', function () {
395        it('should return true when node fileName end with .d.ets', function () {
396            const node = ts.factory.createIdentifier('name');
397            const kind = ts.SyntaxKind.SourceFile;
398            (node as Mutable<ts.Node>).kind = kind;
399            const sourceFile = NodeUtils.getSourceFileOfNode(node);
400            sourceFile.fileName = 'a.d.ets';
401            expect(NodeUtils.isDETSFile(node)).to.be.true;
402        })
403    })
404
405    describe('test for isNewTargetNode', function () {
406        it('should return true when node parent is MetaProperty and node parent keywordToken is yntaxKind.NewKeyword and node escapedText is target', function () {
407            const node = ts.factory.createIdentifier('target');
408            const parent = ts.factory.createMetaProperty(ts.SyntaxKind.NewKeyword, node);
409            (node as Mutable<ts.Identifier>).parent = parent;
410            expect(NodeUtils.isNewTargetNode(node)).to.be.true;
411        })
412        it('should return false when node is not a new target node', function () {
413            const node = ts.factory.createIdentifier('name');
414            const parent = ts.factory.createMetaProperty(ts.SyntaxKind.ImportKeyword, node);
415            (node as Mutable<ts.Identifier>).parent = parent;
416            expect(NodeUtils.isNewTargetNode(node)).to.be.false;
417        })
418    })
419
420    describe('test for collectReservedNameForObf', function () {
421        process.env.compileTool = 'rollup';
422        const ENUM_TEST1: string =
423            'enum ANIMAL {\n' +
424            ' CAT,\n' +
425            ' DOG = CAT + 1,\n' +
426            ' GOOSE = DOG + 1,\n' +
427            ' DUCK = GOOSE + 1,\n' +
428            '}';
429        it('test1 collectReservedNameForObf-enum: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = false', function () {
430            const arkConfig = new MergedConfig();
431            arkConfig.options.enablePropertyObfuscation = true;
432            const result: ts.TranspileOutput = ts.transpileModule(ENUM_TEST1, {
433                compilerOptions: {
434                    "target": ts.ScriptTarget.ES2021
435                },
436                fileName: "enum.ts",
437                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
438            });
439            expect(UnobfuscationCollections.reservedEnum.has('CAT')).to.be.true;
440            expect(UnobfuscationCollections.reservedEnum.has('DOG')).to.be.true;
441            expect(UnobfuscationCollections.reservedEnum.has('GOOSE')).to.be.true;
442            expect(UnobfuscationCollections.reservedEnum.has('DUCK')).to.be.false;
443            UnobfuscationCollections.clear();
444        })
445
446        const ENUM_TEST2: string =
447            'let test = 1;\n' +
448            'enum ANIMAL {\n' +
449            ' CAT,\n' +
450            ' DOG = (CAT + 1) + test,\n' +
451            '}';
452        it('test2 collectReservedNameForObf-enum: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = false', function () {
453            const arkConfig = new MergedConfig();
454            arkConfig.options.enablePropertyObfuscation = true;
455            const result: ts.TranspileOutput = ts.transpileModule(ENUM_TEST2, {
456                compilerOptions: {
457                    "target": ts.ScriptTarget.ES2021
458                },
459                fileName: "enum.ts",
460                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
461            });
462            expect(UnobfuscationCollections.reservedEnum.has('CAT')).to.be.true;
463            expect(UnobfuscationCollections.reservedEnum.has('test')).to.be.true;
464            expect(UnobfuscationCollections.reservedEnum.has('DOG')).to.be.false;
465            UnobfuscationCollections.clear();
466        })
467
468        const ENUM_TEST3: string =
469            'class TEST{\n' +
470            ' prop1 = 1\n' +
471            '}\n' +
472            'let myclass = new TEST();\n' +
473            'enum TEST1{\n' +
474            ' AAA,\n' +
475            ' BBB = AAA + myclass.prop1\n' +
476            '}';
477        it('test3 collectReservedNameForObf-enum: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = false', function () {
478            const arkConfig = new MergedConfig();
479            arkConfig.options.enablePropertyObfuscation = true;
480            const result: ts.TranspileOutput = ts.transpileModule(ENUM_TEST3, {
481                compilerOptions: {
482                    "target": ts.ScriptTarget.ES2021
483                },
484                fileName: "enum.ts",
485                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
486            });
487            expect(UnobfuscationCollections.reservedEnum.has('AAA')).to.be.true;
488            expect(UnobfuscationCollections.reservedEnum.has('myclass')).to.be.true;
489            expect(UnobfuscationCollections.reservedEnum.has('BBB')).to.be.false;
490            expect(UnobfuscationCollections.reservedEnum.has('prop1')).to.be.false;
491            UnobfuscationCollections.clear();
492        })
493
494        it('test4 collectReservedNameForObf-enum: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = true', function () {
495            const arkConfig = new MergedConfig();
496            arkConfig.options.enablePropertyObfuscation = true;
497            const result: ts.TranspileOutput = ts.transpileModule(ENUM_TEST3, {
498                compilerOptions: {
499                    "target": ts.ScriptTarget.ES2021
500                },
501                fileName: "enum.js",
502                transformers: { before: [collectReservedNameForObf(arkConfig, true)] }
503            });
504            expect(UnobfuscationCollections.reservedEnum.size === 0).to.be.true;
505            UnobfuscationCollections.clear();
506        })
507
508        const STRUCT_TEST1: string =
509            'class ViewPU {}\n' +
510            'export class Retransmission1 extends ViewPU {\n' +
511            ' constructor() {\n' +
512            '  super();\n' +
513            ' }\n' +
514            ' scroller1: string;\n' +
515            ' controller1: number;\n' +
516            ' callMethod1(): void {};\n' +
517            '}\n' +
518            'class Retransmission2 extends ViewPU {\n' +
519            ' constructor() {\n' +
520            '  super();\n' +
521            ' }\n' +
522            ' scroller2: string;\n' +
523            ' controller2: number;\n' +
524            ' callMethod2(): void {};\n' +
525            '}';
526        it('test5 collectReservedNameForObf-struct: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = false', function () {
527            const arkConfig = new MergedConfig();
528            arkConfig.options.enablePropertyObfuscation = true;
529            const result: ts.TranspileOutput = ts.transpileModule(STRUCT_TEST1, {
530                compilerOptions: {
531                    "target": ts.ScriptTarget.ES2021
532                },
533                fileName: "enum.ts",
534                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
535            });
536            expect(UnobfuscationCollections.reservedStruct.has('scroller1')).to.be.true;
537            expect(UnobfuscationCollections.reservedStruct.has('controller1')).to.be.true;
538            expect(UnobfuscationCollections.reservedStruct.has('callMethod1')).to.be.true;
539            expect(UnobfuscationCollections.reservedStruct.has('scroller2')).to.be.true;
540            expect(UnobfuscationCollections.reservedStruct.has('controller2')).to.be.true;
541            expect(UnobfuscationCollections.reservedStruct.has('callMethod2')).to.be.true;
542            UnobfuscationCollections.clear();
543        })
544
545        it('test6 collectReservedNameForObf-struct: -disable-obfuscation', function () {
546            const arkConfig = new MergedConfig();
547            arkConfig.options.disableObfuscation = true;
548            arkConfig.options.enablePropertyObfuscation = true;
549            const result: ts.TranspileOutput = ts.transpileModule(STRUCT_TEST1, {
550                compilerOptions: {
551                    "target": ts.ScriptTarget.ES2021
552                },
553                fileName: "enum.ts",
554                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
555            });
556            expect(UnobfuscationCollections.reservedStruct.size === 0).to.be.true;
557            UnobfuscationCollections.clear();
558        })
559    })
560
561  describe('test for hasExportModifier', function () {
562    it('should return false if has no modifier', function () {
563      const fileContent = `
564      let a = 1;
565    `;
566      const sourceFile: ts.SourceFile = ts.createSourceFile('demo.ts', fileContent, ts.ScriptTarget.ES2015, true);
567      expect(hasExportModifier(sourceFile.statements[0])).to.be.false;
568    })
569    it('should return false if has no export modifier', function () {
570      const fileContent = `
571      declare const a = 1;
572    `;
573      const sourceFile: ts.SourceFile = ts.createSourceFile('demo.ts', fileContent, ts.ScriptTarget.ES2015, true);
574      expect(hasExportModifier(sourceFile.statements[0])).to.be.false;
575    })
576    it('should return true if has export modifier', function () {
577      const fileContent = `
578      export const a = 1;
579    `;
580      const sourceFile: ts.SourceFile = ts.createSourceFile('demo.ts', fileContent, ts.ScriptTarget.ES2015, true);
581      expect(hasExportModifier(sourceFile.statements[0])).to.be.true;
582    })
583  })
584
585  describe('test for isObjectLiteralInAnnotation', function () {
586    it('should return false when node is not ObjectLiteral', function () {
587        const node = ts.factory.createObjectBindingPattern([]);
588        expect(NodeUtils.isObjectLiteralInAnnotation(node, '.d.ets')).to.be.false;
589    })
590    it('should return true if annotation is in .d.ets files', function () {
591        const node = ts.factory.createObjectLiteralExpression(
592            [ts.factory.createPropertyAssignment(
593              ts.factory.createIdentifier("a"),
594              ts.factory.createNumericLiteral("1")
595            )],
596            false
597        );
598        const expr = ts.factory.createCallExpression(
599            ts.factory.createIdentifier("Anno1"),
600            undefined,
601            [node]
602        );
603        (node as Mutable<ts.Node>).parent = expr;
604        const decorator = ts.factory.createDecorator(expr);
605        (expr as Mutable<ts.Node>).parent = decorator;
606        expect(NodeUtils.isObjectLiteralInAnnotation(node, '.d.ets')).to.be.true;
607    })
608    it('should return true if annotation is in .ts files and with annotationPrefix', function () {
609        const node = ts.factory.createObjectLiteralExpression(
610            [ts.factory.createPropertyAssignment(
611              ts.factory.createIdentifier("a"),
612              ts.factory.createNumericLiteral("1")
613            )],
614            false
615        );
616        const expr = ts.factory.createCallExpression(
617            ts.factory.createIdentifier("__$$ETS_ANNOTATION$$__Anno1"),
618            undefined,
619            [node]
620        );
621        (node as Mutable<ts.Node>).parent = expr;
622        const decorator = ts.factory.createDecorator(expr);
623        (expr as Mutable<ts.Node>).parent = decorator;
624        expect(NodeUtils.isObjectLiteralInAnnotation(node, '.ts')).to.be.true;
625    })
626})
627})