UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

203 lines (183 loc) 7.98 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /* global QUnit */ /* eslint-disable no-eval */ // put qunit-coverage last so library files don't get measured sap.ui.define([ "sap/base/Log", "sap/base/util/ObjectPath", "sap/base/future", "sap/ui/test/opaQunit", "sap/ui/test/Opa5", "sap/ui/test/gherkin/GherkinTestGenerator", "sap/ui/test/gherkin/dataTableUtils", "sap/ui/test/gherkin/StepDefinitions", "sap/ui/test/launchers/componentLauncher", "sap/ui/test/launchers/iFrameLauncher", "sap/ui/qunit/qunit-junit", "sap/ui/qunit/qunit-coverage" ], function(Log, ObjectPath, future, opaTest, Opa5, GherkinTestGenerator, dataTableUtils, StepDefinitions, componentLauncher, iFrameLauncher) { "use strict"; QUnit.config.urlConfig.splice(0, 0, { id: "closeFrame", label: "Close Frame", tooltip: "Closes the application-under-test's frame after all tests have executed", value: "true" }); QUnit.done(function() { if (new URLSearchParams(window.location.search).has("closeFrame")) { Opa5.emptyQueue(); } }); /** * Dynamically generates and executes Opa5 tests based on a Gherkin feature file and step definitions. * * Logs activity to Opa5, and some debug information to the console with the prefix "[GHERKIN]" * * @author Jonathan Benn * @alias sap.ui.test.gherkin.opa5TestHarness * @namespace * @static * @since 1.40 * @public */ var opa5TestHarness = { // for testability these need to be accessible outside of public 'test' function's scope _oOpa5: new Opa5(), _opaTest: opaTest, _fnAlternateTestStepGenerator: function(oStep) { // Automatically generates test steps from Opa Page Objects, only used when args.generateMissingSteps is true var sContext = oStep.keyword; var sFinalFunction = oStep.text; var aMatch = oStep.text.match(/(.*?)\s*:\s*(.*)/); if (aMatch) { sContext += "." + dataTableUtils.normalization.camelCase(aMatch[1]); sFinalFunction = aMatch[2]; } sFinalFunction = dataTableUtils.normalization.camelCase(sFinalFunction); var sToEval = sContext + "." + sFinalFunction + "();"; var func; if ( /^(Given|When|Then)(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*$/.test(sContext) && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(sFinalFunction) ) { func = function(Given, When, Then) { Log.info("[GHERKIN] Generated Step: " + sToEval); var oContext = ObjectPath.get(sContext, {Given: Given, When: When, Then: Then}); if ( oContext && typeof oContext[sFinalFunction] === "function" ) { oContext[sFinalFunction](); } else { throw new TypeError(sContext + "." + sFinalFunction + " is not a function"); } }; } else { func = function(Given, When, Then) { future.errorThrows("[GHERKIN]: Deprecated Step Generation method (eval) detected! Replace the following with an OPA5 page object call: " + sToEval); /** * @deprecated */ eval(sToEval); }; } return { isMatch: true, text: oStep.text, regex: /Generated Step/, parameters: [], func: func, _sToEval: sToEval // exposing this for testability }; }, /** * Dynamically generates Opa5 tests * * If a test step is missing and args.generateMissingSteps is true then the Gherkin step will be converted into Opa * Page Object code and executed. The text will be converted to camelCase and have any non-alphanumeric character * removed. Here are two pertinent examples: * * (1) The simple step "Given I start my app" will be converted into the call "Given.iStartMyApp();" * * (2) The step "Then on page 1: I should see the page 1 text" will become the call * "Then.onPage1.iShouldSeeThePage1Text();" * * Chaining function calls, such as "Then.iStartMyApp().and.iCloseMyApp()" is not possible at this time. * * @param {object} args - the arguments to the function * @param {string} args.featurePath - the path to the Gherkin feature file to parse, as an SAPUI5 module path. The * ".feature" extension is assumed and should not be included. See * {@link jQuery.sap.registerModulePath} * @param {function} [args.steps] - the constructor function of type {@link sap.ui.test.gherkin.StepDefinitions}. * If this parameter is ommitted then args.generateMissingSteps must be explicitly * set to true. * @param {boolean} [args.generateMissingSteps=false] - When true: if a Gherkin step cannot be matched to a step * definition then it will be assumed that the user wants to * convert the step into an Opa Page Object call. * @public * @throws {Error} if any parameters are invalid * @function * @static */ test: function(args) { // args is mandatory if (!args || typeof args !== "object") { throw new Error("opa5TestHarness.test: input all arguments via a single object"); } if (typeof args.featurePath !== "string" && !(args.featurePath instanceof String)) { throw new Error("opa5TestHarness.test: parameter 'featurePath' must be a valid string"); } if (args.steps && ((typeof args.steps !== "function") || !((new args.steps())._generateTestStep))) { throw new Error("opa5TestHarness.test: if specified, parameter 'steps' must be a valid StepDefinitions constructor"); } if (!args.steps && (args.generateMissingSteps !== true)) { throw new Error("opa5TestHarness.test: if parameter 'generateMissingSteps' is not true then parameter 'steps' must be a valid StepDefinitions constructor"); } if (args.generateMissingSteps && (typeof args.generateMissingSteps !== "boolean")) { throw new Error("opa5TestHarness.test: if specified, parameter 'generateMissingSteps' must be a valid boolean"); } // if the user did not input a StepDefinitions constructor if (!args.steps) { // then use a default StepDefinitions constructor args.steps = StepDefinitions; } var fnTestStepGenerator = (args.generateMissingSteps) ? this._fnAlternateTestStepGenerator : null; var oTestGenerator = new GherkinTestGenerator(args.featurePath, args.steps, fnTestStepGenerator); var oFeatureTest = oTestGenerator.generate(); QUnit.module(oFeatureTest.name, { beforeEach: function() { oTestGenerator.setUp(); }, afterEach: function() { if (this._oOpa5.hasAppStarted()) { this._oOpa5.iTeardownMyApp(); } oTestGenerator.tearDown(); }.bind(this) }); Log.info("[GHERKIN] Running feature: '" + oFeatureTest.name + "'"); oFeatureTest.testScenarios.forEach(function(oTestScenario) { var fnTestFunction = (!oFeatureTest.skip && !oTestScenario.skip) ? this._opaTest : QUnit.skip; fnTestFunction(oTestScenario.name, function(Given, When, Then) { Log.info("[GHERKIN] Running scenario: '" + oTestScenario.name + "'"); oTestScenario.testSteps.forEach(function(oTestStep) { // Put test execution inside a waitFor so that they are executed in order, even if the user fails to put // a waitFor statement in one of the test steps this._oOpa5.waitFor({ viewName: "", success: function() { Log.info("[GHERKIN] Running step: text='" + oTestStep.text + "' regex='" + oTestStep.regex + "'"); Opa5.assert.ok(oTestStep.isMatch, oTestStep.text); if (oTestStep.isMatch) { QUnit.config.current.assertions.pop(); // don't break QUnit expect() behaviour } oTestStep.parameters = (oTestStep.parameters || []).concat([Given, When, Then]); oTestGenerator.execute(oTestStep, Opa5.assert); } }); }.bind(this)); }.bind(this)); }.bind(this)); } }; return opa5TestHarness; }, /* bExport= */ true);