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 rollupObject 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 { expect } from 'chai';
17import mocha from 'mocha';
18import * as ts from 'typescript';
19import path from 'path';
20import { hasDecorator } from '../../../lib/utils';
21import { hasArkDecorator } from '../../../lib/fast_build/ark_compiler/utils';
22import {
23  processSendableClass,
24  processSendableFunction,
25  processSendableType
26} from '../../../lib/process_sendable';
27import { COMPONENT_SENDABLE_DECORATOR } from '../../../lib/pre_define';
28
29const SENDABLE_WITHOUT_CONSTRUCTOR_CODE: string =
30`
31@Sendable
32class sendableClass {}
33`
34
35const SENDABLE_WITHOUT_CONSTRUCTOR_CODE_EXPECT: string =
36'"use strict";\n' +
37'class sendableClass {\n' +
38'    constructor() {\n' +
39'        "use sendable";\n' +
40'    }\n' +
41'}\n' +
42'//# sourceMappingURL=sendableTest.js.map'
43
44const SENDABLE_WITH_CONSTRUCTOR_CODE: string =
45`
46@Sendable
47class sendableClass {
48  constructor() {}
49}
50`
51
52const SENDABLE_WITH_CONSTRUCTOR_CODE_EXPECT: string =
53'"use strict";\n' +
54'class sendableClass {\n' +
55'    constructor() {\n' +
56'        "use sendable";\n' +
57'    }\n' +
58'}\n' +
59'//# sourceMappingURL=sendableTest.js.map'
60
61const SENDABLE_WITH_SUPER_CLASS_CODE: string =
62`@Sendable
63class SuperClass {}
64
65@Sendable
66class SubClass extends SuperClass {}`
67
68const SENDABLE_WITH_SUPER_CLASS_CODE_EXPECT: string =
69'"use strict";\n' +
70'class SuperClass {\n' +
71'    constructor() {\n' +
72'        "use sendable";\n' +
73'    }\n' +
74'}\n' +
75'class SubClass extends SuperClass {\n' +
76'    constructor(...args) {\n' +
77'        "use sendable";\n' +
78'        super(...args);\n' +
79'    }\n' +
80'}\n' +
81'//# sourceMappingURL=sendableTest.js.map'
82
83const SENDABLE_WITH_SUPER_CLASS_AND_CONSTRUCTOR_CODE: string =
84 ` @Sendable
85class SuperClass {}
86
87@Sendable
88class SubClass extends SuperClass {
89  constructor() {
90    super();
91  }
92}`
93
94const SENDABLE_WITH_SUPER_CLASS_AND_CONSTRUCTOR_CODE_EXPECT: string =
95'"use strict";\n' +
96'class SuperClass {\n' +
97'    constructor() {\n' +
98'        "use sendable";\n' +
99'    }\n' +
100'}\n' +
101'class SubClass extends SuperClass {\n' +
102'    constructor() {\n' +
103'        "use sendable";\n' +
104'        super();\n' +
105'    }\n' +
106'}\n' +
107'//# sourceMappingURL=sendableTest.js.map'
108
109const SENDABLE_WITH_OVERLOADED_CONSTRUCTOR_CODE: string =
110`@Sendable
111class SuperClass {
112    constructor(name) {
113        this.name = name;
114    }
115}
116
117@Sendable
118class SubClass extends SuperClass {
119
120}`
121
122const SENDABLE_WITH_OVERLOADED_CONSTRUCTOR_CODE_EXPECT: string =
123'"use strict";\n' +
124'class SuperClass {\n' +
125'    constructor(name) {\n' +
126'        "use sendable";\n' +
127'        this.name = name;\n' +
128'    }\n' +
129'}\n' +
130'class SubClass extends SuperClass {\n' +
131'    constructor(...args) {\n' +
132'        "use sendable";\n' +
133'        super(...args);\n' +
134'    }\n' +
135'}\n' +
136'//# sourceMappingURL=sendableTest.js.map';
137
138const SENDABLE_WITH_NO_PARENT_CONSTRUCTOR_CODE: string =
139`@Sendable
140class SuperClass {
141}
142
143@Sendable
144class SubClass extends SuperClass {
145    age: number;
146    constructor()
147    constructor(age: number)
148    constructor(age?: number) {
149        super();
150        this.age = age !== undefined ? age : 0;
151    }
152}`
153
154const SENDABLE_WITH_NO_PARENT_CONSTRUCTOR_CODE_EXPECT: string =
155'"use strict";\n' +
156'class SuperClass {\n' +
157'    constructor() {\n' +
158'        "use sendable";\n' +
159'    }\n' +
160'}\n' +
161'class SubClass extends SuperClass {\n' +
162'    constructor(age) {\n' +
163'        "use sendable";\n' +
164'        super();\n' +
165'        this.age = age !== undefined ? age : 0;\n' +
166'    }\n' +
167'}\n' +
168'//# sourceMappingURL=sendableTest.js.map'
169
170const SENDABLE_FUNCTION_WITH_BODY: string = 
171`
172@Sendable
173function sendableFunction() {}
174`
175
176const SENDABLE_FUNCTION_WITH_BODY_EXPECT: string = 
177`"use strict";
178function sendableFunction() {
179    "use sendable";
180}
181//# sourceMappingURL=sendableTest.js.map`
182
183const SENDABLE_FUNCTION_WITHOUT_BODY: string = 
184`
185@Sendable
186function sendableFunction()
187`
188
189const SENDABLE_FUNCTION_WITHOUT_BODY_EXPECT: string = 
190`"use strict";
191//# sourceMappingURL=sendableTest.js.map`
192
193const SENDABLE_TYPE: string = 
194`
195@Sendable
196type SendableFunctionType = () => void
197`
198
199const SENDABLE_TYPE_EXPECT: string = 
200`"use strict";
201//# sourceMappingURL=sendableTest.js.map`
202
203const compilerOptions = ts.readConfigFile(
204  path.resolve(__dirname, '../../../tsconfig.json'), ts.sys.readFile).config.compilerOptions;
205
206function processSendable(): Function {
207  return (context: ts.TransformationContext) => {
208    const visitor: ts.Visitor = node => {
209      if (ts.isClassDeclaration(node) && hasDecorator(node, COMPONENT_SENDABLE_DECORATOR)) {
210        return processSendableClass(node);
211      }
212      if (ts.isFunctionDeclaration(node) && hasArkDecorator(node, COMPONENT_SENDABLE_DECORATOR)) {
213        return processSendableFunction(node);
214      }
215      if (ts.isTypeAliasDeclaration(node) && hasArkDecorator(node, COMPONENT_SENDABLE_DECORATOR)) {
216        return processSendableType(node);
217      }
218      return node;
219    };
220
221    return (node: ts.SourceFile) => {
222      return ts.visitEachChild(node, visitor, context);
223    };
224  }
225}
226
227mocha.describe('process sendable decorator', function () {
228  mocha.it('1-1: process sendable class without constrcutor tests', function () {
229    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_WITHOUT_CONSTRUCTOR_CODE, {
230      compilerOptions: compilerOptions,
231      fileName: "sendableTest.ts",
232      transformers: { before: [ processSendable() ] }
233    });
234    expect(result.outputText == SENDABLE_WITHOUT_CONSTRUCTOR_CODE_EXPECT).to.be.true;
235  });
236  mocha.it('1-2: process sendable class with constrcutor tests', function () {
237    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_WITH_CONSTRUCTOR_CODE, {
238      compilerOptions: compilerOptions,
239      fileName: "sendableTest.ts",
240      transformers: { before: [ processSendable() ] }
241    });
242    expect(result.outputText == SENDABLE_WITH_CONSTRUCTOR_CODE_EXPECT).to.be.true;
243  });
244  mocha.it('1-3: process sendable subclass with super class tests', function () {
245    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_WITH_SUPER_CLASS_CODE, {
246      compilerOptions: compilerOptions,
247      fileName: "sendableTest.ts",
248      transformers: { before: [ processSendable() ] }
249    });
250    expect(result.outputText == SENDABLE_WITH_SUPER_CLASS_CODE_EXPECT).to.be.true;
251  });
252  mocha.it('1-4: process sendable subclass with super class and constructor tests', function () {
253    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_WITH_SUPER_CLASS_AND_CONSTRUCTOR_CODE, {
254      compilerOptions: compilerOptions,
255      fileName: "sendableTest.ts",
256      transformers: { before: [ processSendable() ] }
257    });
258    expect(result.outputText == SENDABLE_WITH_SUPER_CLASS_AND_CONSTRUCTOR_CODE_EXPECT).to.be.true;
259  });
260  mocha.it('1-5: process sendable subclass with super class and constructor tests', function () {
261    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_WITH_OVERLOADED_CONSTRUCTOR_CODE, {
262      compilerOptions: compilerOptions,
263      fileName: "sendableTest.ts",
264      transformers: { before: [ processSendable() ] }
265    });
266    expect(result.outputText == SENDABLE_WITH_OVERLOADED_CONSTRUCTOR_CODE_EXPECT).to.be.true;
267  });
268  mocha.it('1-6: process sendable subclass with no explicit constructor in super class tests', function () {
269    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_WITH_NO_PARENT_CONSTRUCTOR_CODE, {
270      compilerOptions: compilerOptions,
271      fileName: "sendableTest.ts",
272      transformers: { before: [ processSendable() ] }
273    });
274    expect(result.outputText == SENDABLE_WITH_NO_PARENT_CONSTRUCTOR_CODE_EXPECT).to.be.true;
275  });
276  mocha.it('2-1: process sendable function with body tests', function () {
277    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_FUNCTION_WITH_BODY, {
278      compilerOptions: compilerOptions,
279      fileName: "sendableTest.ts",
280      transformers: { before: [ processSendable() ] }
281    });
282    expect(result.outputText == SENDABLE_FUNCTION_WITH_BODY_EXPECT).to.be.true;
283  });
284  mocha.it('2-2: process sendable function without body tests', function () {
285    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_FUNCTION_WITHOUT_BODY, {
286      compilerOptions: compilerOptions,
287      fileName: "sendableTest.ts",
288      transformers: { before: [ processSendable() ] }
289    });
290    expect(result.outputText == SENDABLE_FUNCTION_WITHOUT_BODY_EXPECT).to.be.true;
291  });
292  mocha.it('3-1: process sendable type tests', function () {
293    const result: ts.TranspileOutput = ts.transpileModule(SENDABLE_TYPE, {
294      compilerOptions: compilerOptions,
295      fileName: "sendableTest.ts",
296      transformers: { before: [ processSendable() ] }
297    });
298    expect(result.outputText == SENDABLE_TYPE_EXPECT).to.be.true;
299  });
300})