"use strict";
exports.id = 2944;
exports.ids = [2944];
exports.modules = {

/***/ 35445:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.readManifest = readManifest;
exports.generateOPAFiles = generateOPAFiles;
exports.generatePageObjectFile = generatePageObjectFile;
const node_path_1 = __webpack_require__(76760);
const mem_fs_1 = __webpack_require__(64812);
const mem_fs_editor_1 = __webpack_require__(90718);
const types_1 = __webpack_require__(30420);
const i18n_1 = __webpack_require__(60577);
const project_access_1 = __webpack_require__(20787);
/**
 * Reads the manifest for an app.
 *
 * @param fs - a reference to a mem-fs editor
 * @param basePath - the root folder of the app
 * @returns the manifest object. An exception is thrown if the manifest cannot be read.
 */
function readManifest(fs, basePath) {
    const manifest = fs.readJSON((0, node_path_1.join)(basePath, project_access_1.DirName.Webapp, project_access_1.FileName.Manifest));
    if (!manifest) {
        throw new types_1.ValidationError((0, i18n_1.t)('error.cannotReadManifest', {
            filePath: (0, node_path_1.join)(basePath, project_access_1.DirName.Webapp, project_access_1.FileName.Manifest)
        }));
    }
    return manifest;
}
/**
 * Retrieves the application type of the main datasource (FreeStyle, FE V2 or FE V4).
 *
 * @param manifest - the app descriptor of the app
 * @returns {{ applicationType: string, hideFilterBar: boolean }} An object containing the application type and hideFilterBar flag. An exception is thrown if it can't be found or if it's not supported
 */
function getAppTypeAndHideFilterBarFromManifest(manifest) {
    const appTargets = manifest['sap.ui5']?.routing?.targets;
    let hideFilterBar = false;
    let isFEV4 = false;
    for (const targetKey in appTargets) {
        const target = appTargets[targetKey];
        if (target.type === 'Component' && target.name && target.name in types_1.SupportedPageTypes) {
            isFEV4 = true;
            if (types_1.SupportedPageTypes[target.name] === 'ListReport') {
                hideFilterBar = target.options?.settings?.hideFilterBar ?? false;
            }
        }
    }
    if (!isFEV4) {
        throw new types_1.ValidationError((0, i18n_1.t)('error.badApplicationType'));
    }
    return { applicationType: 'v4', hideFilterBar }; // For the time being, only FE V4 is supported
}
/**
 * Retrieves appID and appPath from the manifest.
 *
 * @param manifest - the app descriptor of the app
 * @param forcedAppID - the appID in case we don't want to read it from the manifest
 * @returns appID and appPath
 */
function getAppFromManifest(manifest, forcedAppID) {
    const appID = forcedAppID ?? manifest['sap.app']?.id;
    const appPath = appID?.split('.').join('/');
    if (!appID || !appPath) {
        throw new types_1.ValidationError((0, i18n_1.t)('error.cannotReadAppID'));
    }
    return { appID, appPath };
}
/**
 * Create the page configuration object from the app descriptor and the target key.
 *
 * @param manifest - the app descriptor of the app
 * @param targetKey - the key of the target in the manifest
 * @param forcedAppID - the appID in case we don't want to read it from the manifest
 * @returns Page configuration object, or undefined if the target type is not supported
 */
function createPageConfig(manifest, targetKey, forcedAppID) {
    const appTargets = manifest['sap.ui5']?.routing?.targets;
    const target = appTargets && appTargets[targetKey];
    const { appID, appPath } = getAppFromManifest(manifest, forcedAppID);
    if (target?.type === 'Component' &&
        target?.name &&
        target.name in types_1.SupportedPageTypes &&
        target?.id &&
        (target?.options?.settings?.entitySet || target?.options?.settings?.contextPath)) {
        const pageConfig = {
            appPath,
            appID,
            targetKey,
            componentID: target.id,
            template: types_1.SupportedPageTypes[target.name],
            isStartup: false
        };
        if (target.options.settings.contextPath) {
            pageConfig.contextPath = target.options.settings.contextPath;
        }
        else if (target.options.settings.entitySet) {
            pageConfig.entitySet = target.options.settings.entitySet;
        }
        return pageConfig;
    }
    else {
        return undefined;
    }
}
/**
 * Create the configuration object from the app descriptor.
 *
 * @param manifest - the app descriptor of the target app
 * @param opaConfig - parameters for the generation
 * @param opaConfig.scriptName - the name of the OPA journey file. If not specified, 'FirstJourney' will be used
 * @param opaConfig.htmlTarget - the name of the html file that will be used in the OPA journey file. If not specified, 'index.html' will be used
 * @param opaConfig.appID - the appID. If not specified, will be read from the manifest in sap.app/id
 * @param hideFilterBar - whether the filter bar should be hidden in the generated tests
 * @returns OPA test configuration object
 */
function createConfig(manifest, opaConfig, hideFilterBar) {
    // General application info
    const { appID, appPath } = getAppFromManifest(manifest, opaConfig.appID);
    const config = {
        appID,
        appPath,
        pages: [],
        opaJourneyFileName: opaConfig.scriptName ?? 'FirstJourney',
        htmlTarget: opaConfig.htmlTarget ?? 'index.html',
        hideFilterBar
    };
    // Identify startup targets from the routes
    const appRoutes = (manifest['sap.ui5']?.routing?.routes ?? []);
    // Find the route with an empty pattern (except for the trailing query part)
    const startupRoute = appRoutes.find((route) => {
        return route.pattern.replace(':?query:', '') === '';
    });
    let startupTargets = startupRoute?.target ?? [];
    if (!Array.isArray(startupTargets)) {
        startupTargets = [startupTargets];
    }
    // Create page configurations in supported cases
    const appTargets = manifest['sap.ui5']?.routing?.targets;
    for (const targetKey in appTargets) {
        const pageConfig = createPageConfig(manifest, targetKey, opaConfig.appID);
        if (pageConfig) {
            pageConfig.isStartup = startupTargets.includes(targetKey);
            config.pages.push(pageConfig);
        }
    }
    return config;
}
/**
 * Finds the initial ListReport page and the first Object page from the app.
 *
 * @param pages - the page configs of the app
 * @param manifest - the app descriptor of the target app
 * @returns the page fonfigs for the LR and the OP if they're found
 */
function findLROP(pages, manifest) {
    const pageLR = pages.find((page) => {
        return page.isStartup && page.template === 'ListReport';
    });
    if (!pageLR) {
        return {};
    }
    const appTargets = manifest['sap.ui5']?.routing?.targets;
    const appRoutes = (manifest['sap.ui5']?.routing?.routes ?? []);
    const target = appTargets?.[pageLR.targetKey];
    if (!target?.options?.settings?.navigation) {
        return { pageLR }; // No navigation from LR
    }
    // Find all targets that can be navigated from the LR page
    const navigatedTargetKeys = [];
    for (const navKey in target.options.settings.navigation) {
        const navObject = target.options.settings.navigation[navKey];
        const navigatedRoute = navObject.detail?.route &&
            appRoutes.find((route) => {
                return route.name === navObject.detail?.route;
            });
        if (Array.isArray(navigatedRoute?.target)) {
            navigatedTargetKeys.push(...navigatedRoute.target);
        }
        else if (navigatedRoute?.target) {
            navigatedTargetKeys.push(navigatedRoute.target);
        }
    }
    // Find the first navigated page that is valid and not the starting LR
    let pageOP;
    for (let i = 0; i < navigatedTargetKeys.length && !pageOP; i++) {
        if (navigatedTargetKeys[i] === pageLR.targetKey) {
            continue; // This can happen in the FCL case where the LR is also part of the route's targets to the OP
        }
        pageOP = pages.find((page) => {
            return page.targetKey === navigatedTargetKeys[i];
        });
    }
    return { pageLR, pageOP };
}
/**
 * Writes a page object in a mem-fs-editor.
 *
 * @param pageConfig - the page configuration object
 * @param rootTemplateDirPath - template root directory
 * @param testOutDirPath - output test directory (.../webapp/test)
 * @param fs - a reference to a mem-fs editor
 */
function writePageObject(pageConfig, rootTemplateDirPath, testOutDirPath, fs) {
    fs.copyTpl((0, node_path_1.join)(rootTemplateDirPath, `integration/pages/${pageConfig.template}.js`), (0, node_path_1.join)(testOutDirPath, `integration/pages/${pageConfig.targetKey}.js`), pageConfig, undefined, {
        globOptions: { dot: true }
    });
}
/**
 * Generate OPA test files for a Fiori elements for OData V4 application.
 * Note: this can potentially overwrite existing files in the webapp/test folder.
 *
 * @param basePath - the absolute target path where the application will be generated
 * @param opaConfig - parameters for the generation
 * @param opaConfig.scriptName - the name of the OPA journey file. If not specified, 'FirstJourney' will be used
 * @param opaConfig.htmlTarget - the name of the html that will be used in OPA journey file. If not specified, 'index.html' will be used
 * @param opaConfig.appID - the appID. If not specified, will be read from the manifest in sap.app/id
 * @param fs - an optional reference to a mem-fs editor
 * @returns Reference to a mem-fs-editor
 */
function generateOPAFiles(basePath, opaConfig, fs) {
    const editor = fs ?? (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
    const manifest = readManifest(editor, basePath);
    const { applicationType, hideFilterBar } = getAppTypeAndHideFilterBarFromManifest(manifest);
    const config = createConfig(manifest, opaConfig, hideFilterBar);
    const rootCommonTemplateDirPath = (0, node_path_1.join)(__dirname, '../templates/common');
    const rootV4TemplateDirPath = (0, node_path_1.join)(__dirname, `../templates/${applicationType}`); // Only v4 is supported for the time being
    const testOutDirPath = (0, node_path_1.join)(basePath, 'webapp/test');
    // Common test files
    editor.copyTpl((0, node_path_1.join)(rootCommonTemplateDirPath), testOutDirPath, 
    // unit tests are not added for Fiori elements app
    { appId: config.appID }, undefined, {
        globOptions: { dot: true }
    });
    // Integration (OPA) test files - version-specific
    editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'opaTests.*.*'), (0, node_path_1.join)(testOutDirPath, 'integration'), config, undefined, {
        globOptions: { dot: true }
    });
    // Pages files (one for each page in the app)
    config.pages.forEach((page) => {
        writePageObject(page, rootV4TemplateDirPath, testOutDirPath, editor);
    });
    // OPA Journey file
    const startPages = config.pages.filter((page) => page.isStartup).map((page) => page.targetKey);
    const LROP = findLROP(config.pages, manifest);
    const journeyParams = {
        startPages,
        startLR: LROP.pageLR?.targetKey,
        navigatedOP: LROP.pageOP?.targetKey,
        hideFilterBar: config.hideFilterBar
    };
    editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration/FirstJourney.js'), (0, node_path_1.join)(testOutDirPath, `integration/${config.opaJourneyFileName}.js`), journeyParams, undefined, {
        globOptions: { dot: true }
    });
    // Journey Runner
    editor.copyTpl((0, node_path_1.join)(rootV4TemplateDirPath, 'integration', 'pages', 'JourneyRunner.js'), (0, node_path_1.join)(testOutDirPath, 'integration', 'pages', 'JourneyRunner.js'), config, undefined, {
        globOptions: { dot: true }
    });
    return editor;
}
/**
 * Generate a page object file for a Fiori elements for OData V4 application.
 * Note: this doesn't modify other existing files in the webapp/test folder.
 *
 * @param basePath - the absolute target path where the application will be generated
 * @param pageObjectParameters - parameters for the page
 * @param pageObjectParameters.targetKey - the key of the target in the manifest file corresponding to the page
 * @param pageObjectParameters.appID - the appID. If not specified, will be read from the manifest in sap.app/id
 * @param fs - an optional reference to a mem-fs editor
 * @returns Reference to a mem-fs-editor
 */
function generatePageObjectFile(basePath, pageObjectParameters, fs) {
    const editor = fs || (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
    const manifest = readManifest(editor, basePath);
    const { applicationType } = getAppTypeAndHideFilterBarFromManifest(manifest);
    const pageConfig = createPageConfig(manifest, pageObjectParameters.targetKey, pageObjectParameters.appID);
    if (pageConfig) {
        const rootTemplateDirPath = (0, node_path_1.join)(__dirname, `../templates/${applicationType}`); // Only v4 is supported for the time being
        const testOutDirPath = (0, node_path_1.join)(basePath, 'webapp/test');
        writePageObject(pageConfig, rootTemplateDirPath, testOutDirPath, editor);
    }
    else {
        throw new types_1.ValidationError((0, i18n_1.t)('error.cannotGeneratePageFile', {
            targetKey: pageObjectParameters.targetKey
        }));
    }
    return editor;
}
//# sourceMappingURL=fiori-elements-opa-writer.js.map

/***/ }),

/***/ 53591:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.generateFreestyleOPAFiles = generateFreestyleOPAFiles;
const node_path_1 = __webpack_require__(76760);
const mem_fs_1 = __webpack_require__(64812);
const mem_fs_editor_1 = __webpack_require__(90718);
const project_access_1 = __webpack_require__(20787);
const i18n_1 = __webpack_require__(60577);
const ui5_application_writer_1 = __webpack_require__(57727);
/**
 * Updates tsconfig.json to include paths for unit and integration tests.
 *
 * @param {Editor} fs - The file system editor instance.
 * @param {string} destinationRoot - The root directory where tsconfig.json exists.
 * @param log
 */
function writeOPATsconfigJsonUpdates(fs, destinationRoot, log) {
    try {
        const tsconfig = fs.readJSON((0, node_path_1.join)(destinationRoot, project_access_1.FileName.Tsconfig)) ?? {};
        tsconfig.compilerOptions = tsconfig.compilerOptions || {};
        tsconfig.compilerOptions.paths = tsconfig.compilerOptions.paths || {};
        tsconfig.compilerOptions.paths['unit/*'] = ['./webapp/test/unit/*'];
        tsconfig.compilerOptions.paths['integration/*'] = ['./webapp/test/integration/*'];
        fs.writeJSON((0, node_path_1.join)(destinationRoot, project_access_1.FileName.Tsconfig), tsconfig);
    }
    catch (error) {
        log?.error((0, i18n_1.t)('error.errorWritingTsConfig', {
            error: error
        }));
    }
}
/**
 * Gets the template UI5 version based on the provided UI5 version.
 *
 * @param ui5Version - The UI5 version.
 * @returns template UI5 version.
 */
function getTemplateUi5Version(ui5Version) {
    if (!ui5Version) {
        return ui5_application_writer_1.ui5LtsVersion_1_120;
    }
    return (0, ui5_application_writer_1.compareUI5VersionGte)(ui5Version, ui5_application_writer_1.ui5LtsVersion_1_120) ? ui5_application_writer_1.ui5LtsVersion_1_120 : ui5_application_writer_1.ui5LtsVersion_1_71;
}
/**
 * Filters files based on the template UI5 version.
 *
 * @param files - Array of file paths.
 * @param templateUi5Version - The current template Ui5 Version.
 * @returns Files that either are testsuite files or reside in the current template UI5 version folder.
 */
function filterByUi5Version(files, templateUi5Version) {
    return files.filter((filePath) => {
        // Always include testsuite files.
        if (filePath.includes('testsuite.qunit')) {
            return true;
        }
        // For all other files, include only those in the current UI5 version directory.
        return filePath.includes(`${node_path_1.sep}${templateUi5Version}${node_path_1.sep}`);
    });
}
/**
 * Filters files based on the TypeScript setting.
 *
 * @param files - Array of file paths.
 * @param isTypeScript - If true, include .ts files; if false, include .js files.
 * @returns Files filtered based on the file extension.
 */
function filterByTypeScript(files, isTypeScript) {
    return files.filter((filePath) => {
        if (filePath.endsWith('.ts')) {
            return isTypeScript;
        }
        if (filePath.endsWith('.js')) {
            return !isTypeScript;
        }
        // Keep all .html file types
        return true;
    });
}
/**
 * Determines the destination file path based on the provided file path.
 *
 * @param {string} filePath - The original file path.
 * @param {string} freestyleTemplateDir - The directory file path to be removed from the path.
 * @param {string} commonTemplateDir - The directory file to be removed for common templates path.
 * @param {string} templateUi5Version - The UI5 version to be replaced in the path.
 * @returns {string} - The transformed destination file path.
 */
function getDestFilePath(filePath, freestyleTemplateDir, commonTemplateDir, templateUi5Version) {
    if (filePath.includes(freestyleTemplateDir)) {
        return filePath.replace(freestyleTemplateDir, '').replace(`${node_path_1.sep}${templateUi5Version}${node_path_1.sep}`, node_path_1.sep);
    }
    else if (filePath.includes(commonTemplateDir)) {
        return filePath.replace(commonTemplateDir, '');
    }
    else {
        return filePath;
    }
}
/**
 * Generates and copies freestyle test files based on configuration.
 *
 * @param {string} basePath - The base directory path.
 * @param {FFOPAConfig} opaConfig - Configuration object.
 * @param {Editor} fs - Optional file system editor instance.
 * @param {Logger} log - Optional logger instance.
 * @returns {Editor} - The modified file system editor.
 */
async function generateFreestyleOPAFiles(basePath, opaConfig, fs, log) {
    const fsEditor = fs ?? (0, mem_fs_editor_1.create)((0, mem_fs_1.create)());
    const { enableTypeScript, ui5Version, viewName, appId } = opaConfig;
    const freestyleTemplateDir = (0, node_path_1.join)(__dirname, '../templates/freestyle/webapp/test');
    const commonTemplateDir = (0, node_path_1.join)(__dirname, '../templates/common');
    const testOutDir = (0, node_path_1.join)(basePath, 'webapp/test');
    const templateUi5Version = getTemplateUi5Version(ui5Version);
    const appIdWithSlash = appId.replace(/[.]/g, '/'); // Replace all dots with slashes
    const navigationIntent = appId.replace(/[./\\\-\s]/g, ''); // Remove all dots, slashes, dashes, and spaces
    const templateFiles = await (0, project_access_1.getFilePaths)(freestyleTemplateDir);
    const isTypeScript = Boolean(enableTypeScript);
    const templateFilteredFiles = filterByUi5Version(templateFiles, templateUi5Version);
    const filteredFiles = filterByTypeScript(templateFilteredFiles, isTypeScript);
    // copy common templates
    const commonFiles = await (0, project_access_1.getFilePaths)(commonTemplateDir);
    const filteredCommonFiles = commonFiles.filter((filePath) => filePath.endsWith('.html'));
    filteredFiles.push(...filteredCommonFiles);
    const config = {
        ...opaConfig,
        viewNamePage: `${viewName}Page`,
        appIdWithSlash,
        navigationIntent,
        ui5Theme: opaConfig.ui5Theme ?? ''
    };
    // Rename files:
    // - viewName.js files are renamed to include the view name in their file path
    // - viewName.ts files are renamed with the view name appended with 'Page'
    const renameMap = {
        [(0, node_path_1.join)('/integration/pages/viewName.js')]: (0, node_path_1.join)(`integration/pages/${viewName}.js`),
        [(0, node_path_1.join)('/integration/pages/viewName.ts')]: (0, node_path_1.join)(`integration/pages/${viewName}Page.ts`),
        [(0, node_path_1.join)('/unit/controller/viewName.controller.js')]: (0, node_path_1.join)(`unit/controller/${viewName}.controller.js`),
        [(0, node_path_1.join)('/unit/controller/viewName.controller.ts')]: (0, node_path_1.join)(`unit/controller/${viewName}Page.controller.ts`)
    };
    // copy templates
    let freestyleTestTemplatesCopied = false;
    try {
        filteredFiles.forEach((filePath) => {
            // remove template UI5 version from the path
            const destFilePath = getDestFilePath(filePath, freestyleTemplateDir, commonTemplateDir, templateUi5Version);
            const destinationFilePath = (0, node_path_1.join)(testOutDir, renameMap?.[destFilePath] ?? destFilePath);
            fsEditor.copyTpl(filePath, destinationFilePath, config, undefined, {
                globOptions: { dot: true }
            });
        });
        freestyleTestTemplatesCopied = true;
    }
    catch (error) {
        log?.error((0, i18n_1.t)('error.errorCopyingFreestyleTestTemplates', {
            error: error
        }));
    }
    if (freestyleTestTemplatesCopied && isTypeScript) {
        writeOPATsconfigJsonUpdates(fsEditor, basePath, log);
    }
    return fsEditor;
}
//# sourceMappingURL=fiori-freestyle-opa-writer.js.map

/***/ }),

/***/ 60577:
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {


var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.i18n = void 0;
exports.initI18n = initI18n;
exports.t = t;
const i18next_1 = __importDefault(__webpack_require__(68801));
const ui5_test_writer_i18n_json_1 = __importDefault(__webpack_require__(5125));
const NS = 'ui5-test-writer';
exports.i18n = i18next_1.default.createInstance();
/**
 * Initialize i18next with the translations for this module.
 */
async function initI18n() {
    await exports.i18n.init({
        resources: {
            en: {
                [NS]: ui5_test_writer_i18n_json_1.default
            }
        },
        lng: 'en',
        fallbackLng: 'en',
        defaultNS: NS,
        ns: [NS]
    });
}
/**
 * Helper function facading the call to i18next.
 *
 * @param key i18n key
 * @param options additional options
 * @returns {string} localized string stored for the given key
 */
function t(key, options) {
    return exports.i18n.t(key, options);
}
initI18n().catch(() => {
    // Ignore any errors since the write will still work
});
//# sourceMappingURL=i18n.js.map

/***/ }),

/***/ 53069:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.generateFreestyleOPAFiles = exports.generateOPAFiles = void 0;
var fiori_elements_opa_writer_1 = __webpack_require__(35445);
Object.defineProperty(exports, "generateOPAFiles", ({ enumerable: true, get: function () { return fiori_elements_opa_writer_1.generateOPAFiles; } }));
var fiori_freestyle_opa_writer_1 = __webpack_require__(53591);
Object.defineProperty(exports, "generateFreestyleOPAFiles", ({ enumerable: true, get: function () { return fiori_freestyle_opa_writer_1.generateFreestyleOPAFiles; } }));
//# sourceMappingURL=index.js.map

/***/ }),

/***/ 30420:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ValidationError = exports.SupportedPageTypes = void 0;
exports.SupportedPageTypes = {
    'sap.fe.templates.ListReport': 'ListReport',
    'sap.fe.templates.ObjectPage': 'ObjectPage',
    'sap.fe.core.fpm': 'FPM'
};
/**
 * General validation error thrown if app config options contain invalid combinations
 */
class ValidationError extends Error {
    /**
     * ValidationError constructor.
     *
     * @param message - the error message
     */
    constructor(message) {
        super(`Validation error: ${message}`);
        this.name = this.constructor.name;
    }
}
exports.ValidationError = ValidationError;
//# sourceMappingURL=types.js.map

/***/ }),

/***/ 5125:
/***/ ((module) => {

module.exports = /*#__PURE__*/JSON.parse('{"error":{"cannotReadManifest":"Cannot read the `manifest.json` file: {{ filePath }}.","cannotReadAppID":"Cannot read `appID` in the `manifest.json` file.","badApplicationType":"Cannot determine the application type from the `manifest.json` file or it uses an unsupported type.","cannotGeneratePageFile":"Cannot generate the page file for target: {{ targetKey }}.","errorCopyingFreestyleTestTemplates":"An error occurred when copying freestyle test templates: {{ error }}","errorWritingTsConfig":"An error occurred when writing the `tsconfig.json` file: {{ error }}"}}');

/***/ })

};
;