UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

281 lines (249 loc) 11.5 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /* globals QUnit */ sap.ui.define([ "sap/base/util/ObjectPath", "sap/ui/base/DataType", "sap/ui/core/Lib", "sap/ui/test/generic/GenericTestCollection", "require" ], function(ObjectPath, DataType, Lib, GenericTestCollection, require) { "use strict"; /** * @typedef {object} sap.ui.test.generic.ClassInfo * @property {string} className The class name * @property {sap.ui.core.Control|sap.ui.core.Element} [fnClass=undefined] The loaded class * @property {Error} [error=undefined] The error that might occur * * @private **/ /** * @namespace sap.ui.test.generic.Utils * @private * @ui5-restricted SAPUI5 Distribution Layer Libraries */ var Utils = { /** * @param {string} sClassName The class name which should be loaded * @param {string} sContainingModuleName Name of the module that contains the class. If not given, it is assumed that a module with the same name as the class (dots replaced by slashes) exports the class. * When given, the module is loaded and the class is retrieved via its global name * @return {Promise<sap.ui.test.generic.ClassInfo>} Returns a promise resolving with a <code>sap.ui.test.generic.ClassInfo</code> object. */ loadClass: function(sClassName, sContainingModuleName) { return new Promise(function(resolve) { var sModuleName = sContainingModuleName ? sContainingModuleName : sClassName.replace(/\./g, "/"); require([sModuleName], function(Class) { if (sContainingModuleName) { Class = ObjectPath.get(sClassName); } resolve({ className: sClassName, fnClass: Class, error: undefined }); }, function(err) { var Class = ObjectPath.get(sClassName); resolve({ className: sClassName, fnClass: Class, error: err }); }); }); }, /** * Loads all control classes for the given library. * * @param {object} [mTestParams] Test specific parameters * @param {string} [mTestParams.library] The library name * @param {sap.ui.test.generic.GenericTestCollection.ObjectCapabilities} [mTestParams.objectCapabilities] The capabilities of the controls * @param {object} [mOptions] Object that holds further configs. * @param {boolean} [mOptions.includeElements=false] Whether the library's elements should be considered as well. * @return {Promise<sap.ui.test.generic.ClassInfo[]>} Returns a promise resolving with an array of <code>sap.ui.test.generic.ClassInfo</code> objects. */ loadAllControls: function(mTestParams, mOptions) { var sLibName = mTestParams.library; var mCapabilities = mTestParams.objectCapabilities || {}; var aLoadedElementsAndControls = []; mOptions = mOptions || {}; var aLoadClassPromises = []; var loadControls = function () { return Lib.load(sLibName).then(function (library) { var aClasses = library ? library.controls : []; if (mOptions.includeElements) { aClasses = aClasses.concat(library.elements); } aClasses = aClasses.filter(function (sControlName) { // Check if there are new controls or elements available in library after the previous loading return !aLoadedElementsAndControls.includes(sControlName); }); if (aClasses.length > 0) { aClasses.forEach(function(sClass) { var sModuleName = mCapabilities[sClass] && mCapabilities[sClass].moduleName; aLoadClassPromises.push(Utils.loadClass(sClass, sModuleName)); aLoadedElementsAndControls.push(sClass); }); return Promise.all(aLoadClassPromises).then(loadControls); } return Promise.all(aLoadClassPromises); }); }; return loadControls().then(function (aClassInfo) { var i; var aClassInfoError = aClassInfo.filter(function (oClassInfo) { return oClassInfo.error !== undefined; }); aClassInfo = aClassInfo.filter(function (oClassInfo) { return !!oClassInfo.fnClass; }); if (aClassInfoError.length) { QUnit.test("Controls only available in global namespace", function (assert) { for (i = 0; i < aClassInfoError.length; i++) { assert.ok(false, aClassInfoError[i].className + " seems to be only available using global namespace." + " If the class exists, but is not a module of its own, then specify the module that contains the class within the testsuite config."); } }); } return aClassInfo; }); }, /** * Create a control or element based on the given object capabilities * * @param {sap.ui.core.Control|sap.ui.core.Element} Class The control or element class to instantiate * @param {sap.ui.test.generic.GenericTestCollection.ObjectCapabilities} oObjectCapabilities The capabilities of the correspodning control * @param {map} mSettings Settings which should be used to create the control or element * @return {sap.ui.core.Control|sap.ui.core.Element} Instance of the control or element */ createControlOrElement: function (Class, oObjectCapabilities, mSettings) { if (oObjectCapabilities && oObjectCapabilities.create) { return oObjectCapabilities.create(Class, mSettings); } else { return new Class(mSettings); } }, /** * Tries to fill all control properties with string values * * @param {sap.ui.core.Control} oControl The control whose properties get filled * @param {sap.ui.test.generic.GenericTestCollection.ObjectCapabilities} oObjectCapabilities The capabilities of the corresponding control */ fillControlProperties: function(oControl, oObjectCapabilities) { const mProperties = oControl.getMetadata().getAllProperties(); const mPropertyCapabilities = oObjectCapabilities && oObjectCapabilities.properties || {}; let vValueToSet = "test"; // just try a string as default, with some frequently happening exceptions for (const sPropertyName in mProperties) { const oProperty = mProperties[sPropertyName]; let sPropertyCapability; // Check if property should be skipped because of known issues or if a specific value should be used for setting the property if (mPropertyCapabilities[sPropertyName]) { if (GenericTestCollection.ExcludeReason.hasOwnProperty(mPropertyCapabilities[sPropertyName])) { sPropertyCapability = mPropertyCapabilities[sPropertyName]; } else { vValueToSet = mPropertyCapabilities[sPropertyName]; } } if (sPropertyCapability !== GenericTestCollection.ExcludeReason.NotChangeableAfterInit && sPropertyCapability !== GenericTestCollection.ExcludeReason.SetterNeedsSpecificSettings && sPropertyCapability !== GenericTestCollection.ExcludeReason.OnlyChangeableViaBinding) { try { const oType = DataType.getType(oProperty.type); vValueToSet = oType?.getDefaultValue(); if (oProperty.type === "int") { vValueToSet = 100; } oControl[oProperty._sMutator](vValueToSet); } catch (e) { // type check error, ignore // QUnit.assert.ok(true, "INFO: Failed to fill property: '" + sPropertyName + "' to control '" + oControl.getMetadata().getName() + "'"); } } } try { oControl.setTooltip("test"); // seems not to be a property... } catch (error) { // This case currently only happens in sap.ui.webc libraries // They can't handle a string as tooltip properly therefore // setting tooltip on these controls fails // Adding this try catch here instead of exception in order // to avoid an entry for each of the controls in the // corresponding config files // The libraries where not tested before the introduction // of the new generic tests for libraries. Therefore the // issue was not 'visible' before QUnit.assert.ok(true, "WARNING: setTooltip is not able to handle strings"); } }, fillControlAggregations: function(oControl, mObjectCapabilities) { var oMetadata = oControl.getMetadata(), oObjectCapabilities = mObjectCapabilities[oMetadata.getName()], mAggregations = oMetadata.getAggregations(); return Promise.all( Object.values(mAggregations).map(function(oAggregation) { var sAggregationType = oAggregation.type; // Specific handling for frequently used abstract and interface classes switch (oAggregation.type) { case "sap.ui.core.Control": // Use sap.m.Text in case aggregation is of type sap.ui.core.Control sAggregationType = "sap.m.Text"; break; case "sap.ui.core.Element": // Use sap.ui.core.Icon in case aggregation is of type sap.ui.core.Element sAggregationType = "sap.ui.core.Icon"; break; case "sap.ui.core.Toolbar": // Toolbar is interface - Special treatment because of legacy coding in old // DuplicateIdCheck qunit sAggregationType = "sap.m.Toolbar"; break; default: sAggregationType = oAggregation.type; break; } // Check if aggregation should be skipped because of known issues or if a specific control should be used for the aggregation if (oObjectCapabilities && oObjectCapabilities.aggregations && oObjectCapabilities.aggregations[oAggregation.name]) { if (GenericTestCollection.ExcludeReason.hasOwnProperty(oObjectCapabilities.aggregations[oAggregation.name])) { var sAggregationCapability = oObjectCapabilities.aggregations[oAggregation.name]; if (sAggregationCapability === GenericTestCollection.ExcludeReason.NotChangeableAfterInit || sAggregationCapability === GenericTestCollection.ExcludeReason.SetterNeedsSpecificSettings) { return Promise.resolve(undefined); // Skip this aggregation because it can't be added generically } } else { sAggregationType = oObjectCapabilities.aggregations[oAggregation.name]; } } var sModuleName = mObjectCapabilities[sAggregationType] && mObjectCapabilities[sAggregationType].moduleName ? mObjectCapabilities[sAggregationType].moduleName : undefined; if (DataType.isInterfaceType(sAggregationType)) { return Promise.resolve(undefined); // Can't handle interface types generically } return Utils.loadClass(sAggregationType, sModuleName).then(function(oClassInfo) { var oElement; if (!oClassInfo.fnClass) { QUnit.assert.ok(false, "No class of type " + sAggregationType + " for aggregation '" + oAggregation.name + "' of " + oControl + " could be loaded. Does this class exist? Is it properly implemented?"); return; } if (oClassInfo.fnClass.getMetadata().isAbstract() ) { // we also shouldn't instantiate abstract classes return; } else { // A specific Control or Element type. Try to add a working instance. oElement = new oClassInfo.fnClass(); } // In case we were able to instantiate a suitable Element, add it into the aggregation. if (oElement) { oControl[oAggregation._sMutator](oElement); } return oElement; }).catch(function () { // error while creating or adding a child element: not a problem, just reducing test coverage a bit // QUnit.assert.ok(true, "INFO: Failed to add aggregation '" + oAggregation.name + "' of type '" + oAggregation.type + "' to control '" + oControl.getMetadata().getName() + "'"); }); }) ); } }; return Utils; });