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 } 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        })
241    })
242
243    describe('test for isClassPropertyInConstructorParams', function () {
244        it('should return false if node is not an Identifier', function () {
245            const node = ts.factory.createRegularExpressionLiteral('name');
246            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
247        })
248        it('should return false when node has no parent', function () {
249            const node = ts.factory.createIdentifier('name');
250            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
251        })
252        it('should return false when node parent is not a parameter', function () {
253            const node = ts.factory.createIdentifier('name');
254            const parent = ts.factory.createElementAccessExpression(node, node);
255            (node as Mutable<ts.Node>).parent = parent;
256            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
257        })
258        it('should return false when modifiers is undefined', function () {
259            const node = ts.factory.createIdentifier('name');
260            const parent = ts.factory.createParameterDeclaration([], undefined, node, undefined, undefined, undefined);
261            (node as Mutable<ts.Node>).parent = parent;
262            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
263        })
264        it('should return false when modifiers length is 0 or modifier is ParameterPropertyModifier', function () {
265            const node = ts.factory.createIdentifier('name');
266            const parent = ts.factory.createParameterDeclaration([ts.factory.createModifier(ts.SyntaxKind.AbstractKeyword)], undefined, node, undefined, undefined, undefined);
267            (node as Mutable<ts.Node>).parent = parent;
268            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.false;
269        })
270        it('should return true when node parent parent is ConstructorDeclaration', function () {
271            const node = ts.factory.createIdentifier('name');
272            const parent = ts.factory.createParameterDeclaration([ts.factory.createModifier(ts.SyntaxKind.PublicKeyword)], undefined, node, undefined, undefined, undefined);
273            const parentParent = ts.factory.createConstructorDeclaration(undefined, [ts.factory.createModifier(ts.SyntaxKind.AbstractKeyword)], [], undefined);
274            (parent as Mutable<ts.Node>).parent = parentParent;
275            (node as Mutable<ts.Node>).parent = parent;
276            expect(NodeUtils.isClassPropertyInConstructorParams(node)).to.be.true;
277        })
278    })
279
280    describe('test for isClassPropertyInConstructorBody', function () {
281        it('should return false if node is not an Identifier', function () {
282            const node = ts.factory.createRegularExpressionLiteral('name');
283            expect(NodeUtils.isClassPropertyInConstructorBody(node, new Set)).to.be.false;
284        })
285        it('shound return true when node parent is ConstructorDeclaration and constructorParams has id', function () {
286            const node = ts.factory.createIdentifier('name');
287            const set: Set<string> = new Set();
288            set.add('name');
289            const parent = ts.factory.createConstructorDeclaration(undefined, [ts.factory.createModifier(ts.SyntaxKind.AbstractKeyword)], [], undefined);
290            (node as Mutable<ts.Node>).parent = parent;
291            expect(NodeUtils.isClassPropertyInConstructorBody(node, set)).to.be.true;
292        })
293        it('should return false when curNode is not a ConstructorDeclaration or id does not exist in constructorParams', function () {
294            const node = ts.factory.createIdentifier('name');
295            const set: Set<string> = new Set();
296            set.add('test');
297            const parent = ts.factory.createElementAccessExpression(node, node);
298            (node as Mutable<ts.Node>).parent = parent;
299            expect(NodeUtils.isClassPropertyInConstructorBody(node, set)).to.be.false;
300        })
301    })
302
303    describe('test for isPropertyNode', function () {
304        it('should return true when node is PropertyOrElementAccessNode', function () {
305            const node = ts.factory.createIdentifier('name');
306            const parent = ts.factory.createElementAccessExpression(node, node);
307            (node as Mutable<ts.Node>).parent = parent;
308            expect(NodeUtils.isPropertyNode(node)).to.be.true;
309        })
310        it('should return true when node is a PropertyDeclarationNode', function () {
311            const node = ts.factory.createIdentifier('name');
312            const parent = ts.factory.createPropertyAssignment(node, node);
313            (node as Mutable<ts.Node>).parent = parent;
314            expect(NodeUtils.isPropertyNode(node)).to.be.true;
315        })
316    })
317
318    describe('test for isObjectBindingPatternAssignment', function () {
319        it('should return false when node is not VariableDeclaration', function () {
320            const node = ts.factory.createObjectBindingPattern([]);
321            const parent = ts.factory.createParameterDeclaration(undefined, undefined, undefined, node, undefined, undefined, undefined);
322            (node as Mutable<ts.Node>).parent = parent;
323            expect(NodeUtils.isObjectBindingPatternAssignment(node)).to.be.false;
324        })
325        it('should return true when node parent initializer is CallExpression', function () {
326            const node = ts.factory.createObjectBindingPattern([]);
327            const parent = ts.factory.createVariableDeclaration(node, undefined, undefined, undefined);
328            const initializer = ts.factory.createCallExpression(ts.factory.createIdentifier('name'), undefined, undefined);
329            (parent as Mutable<ts.VariableDeclaration>).initializer = initializer;
330            (node as Mutable<ts.Node>).parent = parent;
331            expect(NodeUtils.isObjectBindingPatternAssignment(node)).to.be.true;
332        })
333    })
334
335    describe('test for isDeclarationFile', function () {
336        it('should return false when sourceFile is not a declarationFile', function () {
337            const endOfFileToken = ts.factory.createToken(ts.SyntaxKind.EndOfFileToken);
338            const sourceFile = ts.factory.createSourceFile([], endOfFileToken, ts.NodeFlags.AwaitContext);
339            expect(NodeUtils.isDeclarationFile(sourceFile)).to.be.false;
340        })
341    })
342
343    describe('test for getSourceFileOfNode', function () {
344        it('should return node when node kind is SyntaxKind.SourceFile', function () {
345            const node = ts.factory.createIdentifier('name');
346            const kind = ts.SyntaxKind.SourceFile;
347            (node as Mutable<ts.Node>).kind = kind;
348            expect(NodeUtils.getSourceFileOfNode(node)).to.equal(node);
349        })
350        it('should return node parent when node kind is not SyntaxKind.SourceFile', function () {
351            const node = ts.factory.createIdentifier('name');
352            const kind = ts.SyntaxKind.SymbolKeyword;
353            (node as Mutable<ts.Node>).kind = kind;
354            const endOfFileToken = ts.factory.createToken(ts.SyntaxKind.EndOfFileToken);
355            const parent = ts.factory.createSourceFile([], endOfFileToken, ts.NodeFlags.AwaitContext);
356            (node as Mutable<ts.Node>).parent = parent;
357            expect(NodeUtils.getSourceFileOfNode(node)).to.equal(node.parent);
358        })
359    })
360
361    describe('test for isDETSFile', function () {
362        it('should return true when node fileName end with .d.ets', function () {
363            const node = ts.factory.createIdentifier('name');
364            const kind = ts.SyntaxKind.SourceFile;
365            (node as Mutable<ts.Node>).kind = kind;
366            const sourceFile = NodeUtils.getSourceFileOfNode(node);
367            sourceFile.fileName = 'a.d.ets';
368            expect(NodeUtils.isDETSFile(node)).to.be.true;
369        })
370    })
371
372    describe('test for isNewTargetNode', function () {
373        it('should return true when node parent is MetaProperty and node parent keywordToken is yntaxKind.NewKeyword and node escapedText is target', function () {
374            const node = ts.factory.createIdentifier('target');
375            const parent = ts.factory.createMetaProperty(ts.SyntaxKind.NewKeyword, node);
376            (node as Mutable<ts.Identifier>).parent = parent;
377            expect(NodeUtils.isNewTargetNode(node)).to.be.true;
378        })
379        it('should return false when node is not a new target node', function () {
380            const node = ts.factory.createIdentifier('name');
381            const parent = ts.factory.createMetaProperty(ts.SyntaxKind.ImportKeyword, node);
382            (node as Mutable<ts.Identifier>).parent = parent;
383            expect(NodeUtils.isNewTargetNode(node)).to.be.false;
384        })
385    })
386
387    describe('test for collectReservedNameForObf', function () {
388        process.env.compileTool = 'rollup';
389        const ENUM_TEST1: string =
390            'enum ANIMAL {\n' +
391            ' CAT,\n' +
392            ' DOG = CAT + 1,\n' +
393            ' GOOSE = DOG + 1,\n' +
394            ' DUCK = GOOSE + 1,\n' +
395            '}';
396        it('test1 collectReservedNameForObf-enum: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = false', function () {
397            const arkConfig = new MergedConfig();
398            arkConfig.options.enablePropertyObfuscation = true;
399            const result: ts.TranspileOutput = ts.transpileModule(ENUM_TEST1, {
400                compilerOptions: {
401                    "target": ts.ScriptTarget.ES2021
402                },
403                fileName: "enum.ts",
404                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
405            });
406            expect(UnobfuscationCollections.reservedEnum.has('CAT')).to.be.true;
407            expect(UnobfuscationCollections.reservedEnum.has('DOG')).to.be.true;
408            expect(UnobfuscationCollections.reservedEnum.has('GOOSE')).to.be.true;
409            expect(UnobfuscationCollections.reservedEnum.has('DUCK')).to.be.false;
410            UnobfuscationCollections.clear();
411        })
412
413        const ENUM_TEST2: string =
414            'let test = 1;\n' +
415            'enum ANIMAL {\n' +
416            ' CAT,\n' +
417            ' DOG = (CAT + 1) + test,\n' +
418            '}';
419        it('test2 collectReservedNameForObf-enum: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = false', function () {
420            const arkConfig = new MergedConfig();
421            arkConfig.options.enablePropertyObfuscation = true;
422            const result: ts.TranspileOutput = ts.transpileModule(ENUM_TEST2, {
423                compilerOptions: {
424                    "target": ts.ScriptTarget.ES2021
425                },
426                fileName: "enum.ts",
427                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
428            });
429            expect(UnobfuscationCollections.reservedEnum.has('CAT')).to.be.true;
430            expect(UnobfuscationCollections.reservedEnum.has('test')).to.be.true;
431            expect(UnobfuscationCollections.reservedEnum.has('DOG')).to.be.false;
432            UnobfuscationCollections.clear();
433        })
434
435        const ENUM_TEST3: string =
436            'class TEST{\n' +
437            ' prop1 = 1\n' +
438            '}\n' +
439            'let myclass = new TEST();\n' +
440            'enum TEST1{\n' +
441            ' AAA,\n' +
442            ' BBB = AAA + myclass.prop1\n' +
443            '}';
444        it('test3 collectReservedNameForObf-enum: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = false', function () {
445            const arkConfig = new MergedConfig();
446            arkConfig.options.enablePropertyObfuscation = true;
447            const result: ts.TranspileOutput = ts.transpileModule(ENUM_TEST3, {
448                compilerOptions: {
449                    "target": ts.ScriptTarget.ES2021
450                },
451                fileName: "enum.ts",
452                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
453            });
454            expect(UnobfuscationCollections.reservedEnum.has('AAA')).to.be.true;
455            expect(UnobfuscationCollections.reservedEnum.has('myclass')).to.be.true;
456            expect(UnobfuscationCollections.reservedEnum.has('BBB')).to.be.false;
457            expect(UnobfuscationCollections.reservedEnum.has('prop1')).to.be.false;
458            UnobfuscationCollections.clear();
459        })
460
461        it('test4 collectReservedNameForObf-enum: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = true', function () {
462            const arkConfig = new MergedConfig();
463            arkConfig.options.enablePropertyObfuscation = true;
464            const result: ts.TranspileOutput = ts.transpileModule(ENUM_TEST3, {
465                compilerOptions: {
466                    "target": ts.ScriptTarget.ES2021
467                },
468                fileName: "enum.js",
469                transformers: { before: [collectReservedNameForObf(arkConfig, true)] }
470            });
471            expect(UnobfuscationCollections.reservedEnum.size === 0).to.be.true;
472            UnobfuscationCollections.clear();
473        })
474
475        const STRUCT_TEST1: string =
476            'class ViewPU {}\n' +
477            'export class Retransmission1 extends ViewPU {\n' +
478            ' constructor() {\n' +
479            '  super();\n' +
480            ' }\n' +
481            ' scroller1: string;\n' +
482            ' controller1: number;\n' +
483            ' callMethod1(): void {};\n' +
484            '}\n' +
485            'class Retransmission2 extends ViewPU {\n' +
486            ' constructor() {\n' +
487            '  super();\n' +
488            ' }\n' +
489            ' scroller2: string;\n' +
490            ' controller2: number;\n' +
491            ' callMethod2(): void {};\n' +
492            '}';
493        it('test5 collectReservedNameForObf-struct: -enable-property-obfuscation, shouldETSOrTSFileTransformToJS = false', function () {
494            const arkConfig = new MergedConfig();
495            arkConfig.options.enablePropertyObfuscation = true;
496            const result: ts.TranspileOutput = ts.transpileModule(STRUCT_TEST1, {
497                compilerOptions: {
498                    "target": ts.ScriptTarget.ES2021
499                },
500                fileName: "enum.ts",
501                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
502            });
503            expect(UnobfuscationCollections.reservedStruct.has('scroller1')).to.be.true;
504            expect(UnobfuscationCollections.reservedStruct.has('controller1')).to.be.true;
505            expect(UnobfuscationCollections.reservedStruct.has('callMethod1')).to.be.true;
506            expect(UnobfuscationCollections.reservedStruct.has('scroller2')).to.be.true;
507            expect(UnobfuscationCollections.reservedStruct.has('controller2')).to.be.true;
508            expect(UnobfuscationCollections.reservedStruct.has('callMethod2')).to.be.true;
509            UnobfuscationCollections.clear();
510        })
511
512        it('test6 collectReservedNameForObf-struct: -disable-obfuscation', function () {
513            const arkConfig = new MergedConfig();
514            arkConfig.options.disableObfuscation = true;
515            arkConfig.options.enablePropertyObfuscation = true;
516            const result: ts.TranspileOutput = ts.transpileModule(STRUCT_TEST1, {
517                compilerOptions: {
518                    "target": ts.ScriptTarget.ES2021
519                },
520                fileName: "enum.ts",
521                transformers: { before: [collectReservedNameForObf(arkConfig, false)] }
522            });
523            expect(UnobfuscationCollections.reservedStruct.size === 0).to.be.true;
524            UnobfuscationCollections.clear();
525        })
526    })
527})