import { expect } from "chai"; import { partial, cloneDeep, find } from "lodash"; import { UI5SemanticModel, UI5Aggregation, } from "@ui5-language-assistant/semantic-model-types"; import { generateModel } from "@ui5-language-assistant/test-utils"; import { generate } from "@ui5-language-assistant/semantic-model"; import { validations, buildMessage, } from "@ui5-language-assistant/user-facing-text"; import { validators } from "../../../src/api"; import { assertNoIssues as assertNoIssuesBase, assertSingleIssue as assertSingleIssueBase, } from "../../test-utils"; const { INVALID_AGGREGATION_TYPE } = validations; describe("the type aggregation validation", () => { let ui5SemanticModel: UI5SemanticModel; before(async () => { ui5SemanticModel = await generateModel({ version: "1.74.0", modelGenerator: generate, }); }); context("true positive scenarios", () => { let assertSingleIssue: (xmlSnippet: string, message: string) => void; before(() => { assertSingleIssue = partial( assertSingleIssueBase, ui5SemanticModel, { element: [validators.validateAggregationType], }, "InvalidAggregationType", "error" ); }); it("will detect mismatch of class to explicit aggregation of 'UI5Class' type", () => { assertSingleIssue( `<mvc:View xmlns:uxap="sap.uxap" xmlns:m="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.ui.commons"> <m:Panel> <m:headerToolbar> <🢂Button🢀></Button> </m:headerToolbar> </m:Panel> </mvc:View>`, buildMessage( INVALID_AGGREGATION_TYPE.msg, "Button", "headerToolbar", "Toolbar" ) ); }); it("will detect mismatch of class to default aggregation of 'UI5Interface' type", () => { assertSingleIssue( `<mvc:View xmlns:uxap="sap.uxap" xmlns:m="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.ui.commons"> <m:Panel> <🢂ToolbarSeparator🢀></ToolbarSeparator> </m:Panel> </mvc:View>`, buildMessage( INVALID_AGGREGATION_TYPE.msg, "ToolbarSeparator", "content", "Control" ) ); }); it("will detect mismatch of class to explicit aggregation of 'UI5Class' type", () => { assertSingleIssue( `<mvc:View xmlns:uxap="sap.uxap" xmlns:m="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.ui.commons"> <m:Page> <m:footer> <!-- The class "Toolbar" is under the aggregation "footer" and must match the type "IBar" --> <🢂Toolbar🢀></Toolbar> </m:footer> </m:Page> </mvc:View>`, buildMessage(INVALID_AGGREGATION_TYPE.msg, "Toolbar", "footer", "IBar") ); }); }); context("negative edge cases", () => { let assertNoIssues: (xmlSnippet: string) => void; before(() => { assertNoIssues = partial(assertNoIssuesBase, ui5SemanticModel, { element: [validators.validateAggregationType], }); }); it("will not detect an issue when the class is under the default aggregation and matching its type", () => { assertNoIssues( `<mvc:View xmlns:uxap="sap.uxap" xmlns:m="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.ui.commons"> <m:Shell></m:Shell> </mvc:View>` ); }); it("will not detect an issue when the class is under non-ui5 aggregation", () => { assertNoIssues( `<mvc:View xmlns:uxap="sap.uxap" xmlns:m="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.ui.commons"> <UnknownTag> <Toolbar></Toolbar> </UnknownTag> </mvc:View>` ); }); it("will not detect an issue when the class is under explicit aggregation when the aggregartion type is not a UI5Class or UI5Interface", () => { const clonedModel = cloneDeep(ui5SemanticModel); const viewClass = clonedModel.classes["sap.ui.core.mvc.View"]; const contentAggregation = find( viewClass.aggregations, (_) => _.name === "content" ) as UI5Aggregation; expect(contentAggregation).to.exist; contentAggregation.type = undefined; viewClass.aggregations = [contentAggregation]; const xmlSnippet = ` <mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"> <mvc:content> <Shell></Shell> </mvc:content> </mvc:View>`; assertNoIssuesBase( clonedModel, { element: [validators.validateAggregationType], }, xmlSnippet ); }); it("will not detect an issue when the class is a `sap.ui.core.Fragment", () => { assertNoIssues( `<mvc:View xmlns:uxap="sap.uxap" xmlns:m="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core"> <m:Page> <m:content> <core:Fragment fragmentName="Fragment1" type="XML" /> </m:content> </m:Page> </mvc:View>` ); }); }); });