!176 @builder 增加ut测试

Merge pull request !176 from 蒲亚军/master
This commit is contained in:
openharmony_ci
2022-01-18 11:57:43 +00:00
committed by Gitee
7 changed files with 416 additions and 14 deletions
+3 -1
View File
@@ -35,13 +35,14 @@ export const COMPONENT_PROVIDE_DECORATOR: string = '@Provide';
export const COMPONENT_CONSUME_DECORATOR: string = '@Consume';
export const COMPONENT_OBJECT_LINK_DECORATOR: string = '@ObjectLink';
export const COMPONENT_WATCH_DECORATOR: string = '@Watch';
export const COMPONENT_BUILDERPARAM_DECORATOR: string = '@BuilderParam';
export const INNER_COMPONENT_DECORATORS: Set<string> = new Set([COMPONENT_DECORATOR_ENTRY,
COMPONENT_DECORATOR_PREVIEW, COMPONENT_DECORATOR_COMPONENT, COMPONENT_DECORATOR_CUSTOM_DIALOG]);
export const INNER_COMPONENT_MEMBER_DECORATORS: Set<string> = new Set([COMPONENT_STATE_DECORATOR,
COMPONENT_PROP_DECORATOR, COMPONENT_LINK_DECORATOR, COMPONENT_STORAGE_PROP_DECORATOR,
COMPONENT_STORAGE_LINK_DECORATOR, COMPONENT_PROVIDE_DECORATOR, COMPONENT_CONSUME_DECORATOR,
COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_WATCH_DECORATOR]);
COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_WATCH_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR]);
export const COMPONENT_OBSERVED_DECORATOR: string = '@Observed';
export const COMPONENT_BUILDER_DECORATOR: string = '@Builder';
@@ -181,6 +182,7 @@ export const GEOMETRY_VIEW: string = 'GeometryView';
export const MODULE_SHARE_PATH: string = 'src' + path.sep + 'ets' + path.sep + 'share';
export const BUILD_SHARE_PATH: string = '../share';
export const CHILD: string = 'child';
export const THIS: string = 'this';
export const STYLES: string = 'Styles';
export const VISUAL_STATE: string = 'visualState';
+41 -5
View File
@@ -45,6 +45,7 @@ import {
COMPONENT_TRANSITION_NAME,
COMPONENT_DEBUGLINE_FUNCTION,
ATTRIBUTE_STATESTYLES,
CHILD,
THIS,
VISUAL_STATE,
VIEW_STACK_PROCESSOR,
@@ -76,6 +77,7 @@ import {
componentInfo,
createFunction
} from './utils';
import { builderParamObjectCollection } from './process_component_member';
import { projectConfig } from '../main';
import { transformLog, contextGlobal } from './process_ui_syntax';
import { props } from './compile_info';
@@ -157,20 +159,25 @@ function validateRootNode(node: ts.MethodDeclaration, log: LogInfo[]): boolean {
export function processComponentChild(node: ts.Block | ts.SourceFile, newStatements: ts.Statement[],
log: LogInfo[]): void {
if (node.statements.length) {
node.statements.forEach((item, index) => {
node.statements.forEach((item, index, array) => {
if (ts.isExpressionStatement(item)) {
const name: string = getName(item);
switch (getComponentType(item, log, name)) {
case ComponentType.innerComponent:
processInnerComponent(item, index, Array.from(node.statements), newStatements, log, name);
processInnerComponent(item, index, Array.from(node.statements),
newStatements, log, name);
break;
case ComponentType.customComponent:
if (index + 1 < array.length && ts.isBlock(array[index + 1])) {
item = processBlockChange(item,
array[index + 1] as ts.Block, log)
}
processCustomComponent(item, newStatements, log);
break;
case ComponentType.forEachComponent:
processForEachComponent(item, newStatements, log);
break;
case ComponentType.customBuilderMethod:
case ComponentType.customBuilderMethod || ComponentType.builderParamMethod:
newStatements.push(item);
break;
}
@@ -188,6 +195,31 @@ export function processComponentChild(node: ts.Block | ts.SourceFile, newStateme
}
}
function processBlockChange(node: ts.ExpressionStatement, nextNode: ts.Block,
log: LogInfo[]): ts.block {
// @ts-ignore
const newBlock: ts.Block = processComponentBlock(nextNode, false, log);
const arrowNode: ts.ArrowFunction = ts.factory.createArrowFunction(undefined, undefined,
[], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), newBlock);
const newPropertyAssignment:ts.PropertyAssignment = ts.factory.createPropertyAssignment(
ts.factory.createIdentifier(CHILD), arrowNode);
// @ts-ignore
let argumentsArray: ts.ObjectLiteralExpression[] = node.express.arguments;
if (arguments && arguments.length < 1) {
argumentsArray = [ts.factory.createObjectLiteralExpression([newPropertyAssignment], true)]
} else {
// @ts-ignore
argumentsArray = [ts.factory.createObjectLiteralExpression(
// @ts-ignore
node.express.arguments[0].properties.concat([newPropertyAssignment]), true)]
}
// @ts-ignore
node = ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression(node.expression,
// @ts-ignore
node.expression.expression, node.expression.expression,typeArguments, argumentsArray))
return node;
}
function processInnerComponent(node: ts.ExpressionStatement, index: number, arr: ts.Statement[],
newStatements: ts.Statement[], log: LogInfo[], name: string): void {
const res: CreateResult = createComponent(node, COMPONENT_CREATE_FUNCTION);
@@ -817,7 +849,8 @@ enum ComponentType {
innerComponent,
customComponent,
forEachComponent,
customBuilderMethod
customBuilderMethod,
builderParamMethod
}
function getComponentType(node: ts.ExpressionStatement, log: LogInfo[],
@@ -831,7 +864,10 @@ function getComponentType(node: ts.ExpressionStatement, log: LogInfo[],
return ComponentType.forEachComponent;
} else if (CUSTOM_BUILDER_METHOD.has(name)) {
return ComponentType.customBuilderMethod;
} else if (!isAttributeNode(node)) {
} else if (builderParamObjectCollection.get(componentCollection.currentClassName) &&
builderParamObjectCollection.get(componentCollection.currentClassName).has(name)) {
return ComponentType.builderParamMethod;
}else if (!isAttributeNode(node)) {
log.push({
type: LogType.ERROR,
message: `'${node.getText()}' does not meet UI component syntax.`,
+21 -1
View File
@@ -37,7 +37,8 @@ import {
BUILDER_ATTR_NAME,
BUILDER_ATTR_BIND,
COMPONENT_STYLES_DECORATOR,
STYLES
STYLES,
CUSTOM_COMPONENT_EARLIER_CREATE_CHILD
} from './pre_define';
import {
BUILDIN_STYLE_NAMES,
@@ -211,6 +212,9 @@ function processBuildMember(node: ts.MethodDeclaration, context: ts.Transformati
const buildNode: ts.MethodDeclaration = processComponentBuild(node, log);
return ts.visitNode(buildNode, visitBuild);
function visitBuild(node: ts.Node): ts.Node {
if (isCustomComponentNode(node)) {
return node;
}
if (isGeometryView(node)) {
node = processGeometryView(node as ts.ExpressionStatement, log);
}
@@ -283,6 +287,22 @@ function getParsedBuilderAttrArgument(node: ts.PropertyAccessExpression | ts.Ide
return newObjectNode;
}
function isCustomComponentNode(node:ts.NewExpression | ts.ExpressionStatement): boolean {
if ((ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.escapedText
&& componentCollection.customComponents.has(node.expression.escapedText.toString())) ||
// @ts-ignore
(ts.isExpressionStatement(node) && node.expression && node.expression.expression &&
// @ts-ignore
node.expression.expression.expression && node.expression.expression.expression.escapedText &&
// @ts-ignore
node.expression.expression.expression.escapedText.toString().startsWith(
CUSTOM_COMPONENT_EARLIER_CREATE_CHILD))) {
return true;
}else {
return false;
}
}
function isGeometryView(node: ts.Node): boolean {
if (ts.isExpressionStatement(node) && ts.isCallExpression(node.expression)) {
const call: ts.CallExpression = node.expression;
+27 -5
View File
@@ -51,7 +51,8 @@ import {
JS_DIALOG,
CUSTOM_DIALOG_CONTROLLER_BUILDER,
BASE_COMPONENT_NAME,
COMPONENT_CREATE_FUNCTION
COMPONENT_CREATE_FUNCTION,
COMPONENT_BUILDERPARAM_DECORATOR
} from './pre_define';
import {
forbiddenUseStateType,
@@ -98,7 +99,9 @@ export const mandatoryToInitViaParamDecorators: Set<string> =
new Set([...propAndLinkDecorators, COMPONENT_OBJECT_LINK_DECORATOR]);
export const setUpdateParamsDecorators: Set<string> =
new Set([...observedPropertyDecorators, COMPONENT_PROP_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR]);
new Set([...observedPropertyDecorators, COMPONENT_PROP_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR,
COMPONENT_BUILDERPARAM_DECORATOR
]);
export const immutableDecorators: Set<string> =
new Set([COMPONENT_STORAGE_PROP_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR]);
@@ -110,6 +113,8 @@ export const decoratorParamSet: Set<string> = new Set();
export const stateObjectCollection: Set<string> = new Set();
export const builderParamObjectCollection: Map<string, Set<string>> = new Map();
export class UpdateResult {
private itemUpdate: boolean = false;
private ctorUpdate: boolean = false;
@@ -263,7 +268,8 @@ function processPropertyNodeDecorator(parentName: ts.Identifier, node: ts.Proper
if (node.questionToken && mandatoryToInitViaParamDecorators.has(decoratorName)) {
validateHasIllegalQuestionToken(name, decoratorName, log);
}
if (!isSimpleType(node.type, program)) {
if (!isSimpleType(node.type, program) &&
decoratorName !== COMPONENT_BUILDERPARAM_DECORATOR) {
stateObjectCollection.add(name.escapedText.toString());
}
if (decoratorName === COMPONENT_WATCH_DECORATOR &&
@@ -405,6 +411,16 @@ function createUpdateParams(name: ts.Identifier, decorator: string): ts.Statemen
case COMPONENT_OBJECT_LINK_DECORATOR:
updateParamsNode = createUpdateParamsWithSet(name);
break;
case COMPONENT_BUILDERPARAM_DECORATOR:
if (decorator === COMPONENT_BUILDERPARAM_DECORATOR) {
if (!builderParamObjectCollection.get(componentCollection.currentClassName)) {
builderParamObjectCollection.set(componentCollection.currentClassName, new Set([]));
}
builderParamObjectCollection.get(componentCollection.currentClassName)
.add(name.escapedText.toString())
}
updateParamsNode = createUpdateParamsWithoutIf(name, 1);
break;
}
return updateParamsNode;
}
@@ -419,9 +435,15 @@ function createUpdateParamsWithIf(name: ts.Identifier): ts.IfStatement {
createUpdateParamsWithoutIf(name)], true), undefined);
}
function createUpdateParamsWithoutIf(name: ts.Identifier): ts.ExpressionStatement {
function createUpdateParamsWithoutIf(name: ts.Identifier, isAdd: number = 0): ts.ExpressionStatement {
let textName: string;
if (isAdd) {
textName = `__${name.getText()}`;
} else {
textName = name.getText();
}
return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
createPropertyAccessExpressionWithThis(name.getText()),
createPropertyAccessExpressionWithThis(textName),
ts.factory.createToken(ts.SyntaxKind.EqualsToken),
createPropertyAccessExpressionWithParams(name.getText())));
}
+9 -2
View File
@@ -105,7 +105,10 @@ function validateCustomComponentPrams(node: ts.ExpressionStatement, name: string
ts.isObjectLiteralExpression(nodeArguments[0])) {
const nodeArgument: ts.ObjectLiteralExpression = nodeArguments[0] as ts.ObjectLiteralExpression;
nodeArgument.properties.forEach(item => {
curChildProps.add(item.name.getText());
if (item.name && item.name.escapedText) {
// @ts-ignore
curChildProps.add(item.name.escapedText.toString());
}
if (isThisProperty(item, propertySet)) {
validateStateManagement(item, name, log);
if (isNonThisProperty(item, linkSet)) {
@@ -157,7 +160,11 @@ function validateStateManagement(node: ts.ObjectLiteralElementLike, customCompon
function checkFromParentToChild(node: ts.ObjectLiteralElementLike, customComponentName: string,
log: LogInfo[]): void {
const propertyName: string = node.name.getText();
let propertyName: string;
if (node.name && node.name.escapedText) {
// @ts-ignore
propertyName = node.name.escapedText.toString();
}
const curPropertyKind: string = getPropertyDecoratorKind(propertyName, customComponentName);
if (curPropertyKind) {
if (isInitFromParent(node)) {
+149
View File
@@ -0,0 +1,149 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
exports.source = `
@Component
struct CustomContainer {
header: string = "";
footer: string = "";
@BuilderParam child: () => any;
build() {
Column() {
Text(this.header)
this.child()
Text(this.footer)
}
}
}
@Builder function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
Text(label2)
}
}
@Entry
@Component
struct CustomContainerUser {
build() {
Column() {
CustomContainer({header: "Header", footer: "Footer"}){
Column() {
Text("content1")
.width(50)
Text("content2")
}
specificParam("content3", "content4")
}
}
}
}
`
exports.expectResult =
`class CustomContainer extends View {
constructor(compilerAssignedUniqueChildId, parent, params) {
super(compilerAssignedUniqueChildId, parent);
this.header = ""
this.footer = ""
this.updateWithValueParams(params);
}
updateWithValueParams(params) {
if (params.header !== undefined) {
this.header = params.header;
}
if (params.footer !== undefined) {
this.footer = params.footer;
}
this.__child = params.child;
}
aboutToBeDeleted() {
this.__child.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id());
}
get child(){
return this.__child.get();
}
set child(newValue) {
this.__child.set(newValue)
}
render() {
Column.create();
Text.create(this.header);
Text.pop();
this.child();
Text.create(this.footer);
Text.pop();
Column.pop();
}
}
function specificParam(label1, label2) {
Column.create();
Text.create(label1);
Text.pop();
Text.create(label1);
Text.pop();
Column.pop();
}
class CustomContainerUser {
constructor(compilerAssignedUniqueChildId, parent, params) {
super(compilerAssignedUniqueChildId, parent);
this.updateWithValueParams(params);
}
updateWithValueParams(params) {
}
aboutToBeDeleted() {
SubscriberManager.Get().delete(this.id());
}
render() {
Column.create();
let earlierCreatedChild_2 = this.findChildById("2");
if (earlierCreatedChild_2 == undefined) {
View.create(new CustomContainer("2", this, {
header: "Header", footer: "Footer",
child: () => {
Column.create();
Text.create("content1");
Text.width(50)
Text.pop();
Text.create("content2");
Text.pop();
Column.pop();
specificParam("content3", "content4)
}
}));
}
else {
earlierCreatedChild_2.updateWithValueParams({
header: "Header", footer: "Footer",
child: () => {
Column.create();
Text.create("content1");
Text.width(50)
Text.pop();
Text.create("content2");
Text.pop();
Column.pop();
specificParam("content3", "content4)
}}
});
View.create(earlierCreatedChild_2);
}
Column.pop();
}
}
loadDocument(new MyComponent("1", undefined, {}));
`
+166
View File
@@ -0,0 +1,166 @@
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
exports.source = `
@Component
struct CustomContainer {
header: string = "";
footer: string = "";
@BuilderParam child: () => any;
build() {
Column() {
Text(this.header)
this.child()
Text(this.footer)
}
}
}
@Builder function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
Text(label2)
}
}
@Entry
@Component
struct CustomContainerUser {
@Builder specificChild() {
Column() {
Text("My content1")
Text("My content1")
}
}
build() {
Column() {
CustomContainer({
header: "Header",
footer: "Footer",
child: this.specificChild
})
CustomContainer({
header: "Header",
footer: "Footer",
child: specificParam("content3", "content4")
})
}
}
}
`
exports.expectResult =
`class CustomContainer extends View {
constructor(compilerAssignedUniqueChildId, parent, params) {
super(compilerAssignedUniqueChildId, parent);
this.header = ""
this.footer = ""
this.updateWithValueParams(params);
}
updateWithValueParams(params) {
if (params.header !== undefined) {
this.header = params.header;
}
if (params.footer !== undefined) {
this.footer = params.footer;
}
this.__child = params.child;
}
aboutToBeDeleted() {
this.__child.aboutToBeDeleted();
SubscriberManager.Get().delete(this.id());
}
get child(){
return this.__child.get();
}
set child(newValue) {
this.__child.set(newValue)
}
render() {
Column.create();
Text.create(this.header);
Text.pop();
this.child();
Text.create(this.footer);
Text.pop();
Column.pop();
}
}
function specificParam(label1, label2) {
Column.create();
Text.create(label1);
Text.pop();
Text.create(label1);
Text.pop();
Column.pop();
}
class CustomContainerUser {
constructor(compilerAssignedUniqueChildId, parent, params) {
super(compilerAssignedUniqueChildId, parent);
this.updateWithValueParams(params);
}
updateWithValueParams(params) {
}
aboutToBeDeleted() {
SubscriberManager.Get().delete(this.id());
}
specificChild() {
Column.create();
Text.create("My content1");
Text.pop();
Text.create("My content2");
Text.pop();
Column.pop();
}
render() {
Column.create();
let earlierCreatedChild_2 = this.findChildById("2");
if (earlierCreatedChild_2 == undefined) {
View.create(new CustomContainer("2", this, {
header: "Header",
footer: "Footer",
child: this.specificChild
}));
}
else {
earlierCreatedChild_2.updateWithValueParams({
header: "Header",
footer: "Footer",
child: this.specificChild
});
View.create(earlierCreatedChild_2);
}
let earlierCreatedChild_3 = this.findChildById("3");
if (earlierCreatedChild_3 == undefined) {
View.create(new CustomContainer("3", this, {
header: "Header",
footer: "Footer",
child: specificParam("content3", "content4")
}));
}
else {
earlierCreatedChild_3.updateWithValueParams({
header: "Header",
footer: "Footer",
child: specificParam("content3", "content4")
});
View.create(earlierCreatedChild_3);
}
Column.pop();
}
}
loadDocument(new MyComponent("1", undefined, {}));
`