/**
 * Copyright 2013-2026 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see https://www.jhipster.tech/
 * for more information.
 *
 * 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
 *
 *      https://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.
 */
import { clientFrameworkTypes } from "../../lib/jhipster/index.js";
import { mutateData, stringHashCode } from "../../lib/utils/index.js";
import BaseApplicationGenerator from "../base-application/index.js";
import { createFaker } from "../base-application/support/index.js";
import { generateTestEntity } from "../client/support/index.js";
import { CLIENT_MAIN_SRC_DIR } from "../generator-constants.js";
import { cypressEntityFiles, cypressFiles } from "./files.js";
const { ANGULAR } = clientFrameworkTypes;
const WAIT_TIMEOUT = 3 * 60000;
export default class CypressGenerator extends BaseApplicationGenerator {
    angularSchematic = false;
    constructor(args, options, features) {
        super(args, options, { ...features, loadCommand: ['jhipster:server'] });
    }
    async beforeQueue() {
        if (!this.fromBlueprint) {
            await this.composeWithBlueprints();
        }
        if (!this.delegateToBlueprint) {
            await this.dependsOnBootstrap('client');
        }
    }
    get prompting() {
        return this.asPromptingTaskGroup({
            async askForCypressOptions({ control }) {
                if (control.existingProject && !this.options.askAnswered)
                    return;
                await this.prompt([
                    {
                        when: this.jhipsterConfig.clientFramework === ANGULAR,
                        type: 'confirm',
                        name: 'cypressCoverage',
                        message: 'Would you like to generate code coverage for Cypress tests? [Experimental]',
                    },
                    {
                        type: 'confirm',
                        name: 'cypressAudit',
                        message: 'Would you like to audit Cypress tests?',
                    },
                ], this.config);
            },
        });
    }
    get [BaseApplicationGenerator.PROMPTING]() {
        return this.delegateTasksToBlueprint(() => this.prompting);
    }
    get configuring() {
        return this.asConfiguringTaskGroup({
            setCypressTestFramework({ control }) {
                if (control.isJhipsterVersionLessThan('9.0.0-beta.4')) {
                    // cypressAudit is set to false by default since next release following 9.0.0-beta.3.
                    this.jhipsterConfig.cypressAudit ??= true;
                }
            },
        });
    }
    get [BaseApplicationGenerator.CONFIGURING]() {
        return this.delegateTasksToBlueprint(() => this.configuring);
    }
    get preparing() {
        return this.asPreparingTaskGroup({
            loadPackageJson({ application }) {
                this.loadNodeDependenciesFromPackageJson(application.nodeDependencies, this.fetchFromInstalledJHipster('cypress', 'resources', 'package.json'));
            },
            prepareForTemplates({ applicationDefaults }) {
                applicationDefaults({
                    cypressAudit: false,
                    cypressCoverage: false,
                    cypressDir: ({ clientTestDir }) => (clientTestDir ? `${clientTestDir}cypress/` : 'cypress/'),
                    cypressTemporaryDir: ({ temporaryDir }) => (temporaryDir ? `${temporaryDir}cypress/` : '.cypress/'),
                    cypressBootstrapEntities: true,
                    cypressCoverageWebpack: data => Boolean(data.cypressCoverage && data.clientFrameworkAngular && data.clientBundlerWebpack),
                });
            },
            npmScripts({ application }) {
                const { devServerPort, devServerPortProxy: devServerPortE2e = devServerPort } = application;
                this.angularSchematic = Boolean(application.clientFrameworkAngular && application.clientBundlerEsbuild);
                Object.assign(application.clientPackageJsonScripts, {
                    cypress: 'cypress open --e2e',
                    e2e: 'npm run e2e:cypress:headed --',
                    'e2e:cypress': 'cypress run --e2e --browser chrome',
                    'e2e:cypress:headed': 'npm run e2e:cypress -- --headed',
                    'e2e:cypress:record': 'npm run e2e:cypress -- --record',
                    'e2e:headless': 'npm run e2e:cypress --',
                });
                // Scripts that handle server and client concurrently should be added to the root package.json
                Object.assign(application.packageJsonScripts, {
                    'ci:e2e:run': 'concurrently -k -s first -n application,e2e -c red,blue npm:ci:e2e:server:start npm:e2e:headless',
                    'ci:e2e:dev': `concurrently -k -s first -n application,e2e -c red,blue npm:app:start npm:e2e:headless`,
                    'e2e:dev': `concurrently -k -s first -n application,e2e -c red,blue npm:app:start npm:e2e`,
                    'e2e:devserver': this.angularSchematic ?
                        `concurrently -k -s first -n backend,e2e -c red,blue npm:backend:start "npm run ci:server:await && ng e2e --configuration ${application.cypressCoverage ? 'coverage' : 'run'}"`
                        : `concurrently -k -s first -n backend,frontend,e2e -c red,yellow,blue npm:backend:start npm:start "wait-on -t ${WAIT_TIMEOUT} http-get://127.0.0.1:${devServerPortE2e} && npm run e2e:headless -- -c baseUrl=http://localhost:${devServerPortE2e}"`,
                });
                if (application.clientRootDir) {
                    // Add scripts to map to client package.json
                    Object.assign(application.packageJsonScripts, {
                        'e2e:headless': `npm run -w ${application.clientRootDir} e2e:headless`,
                    });
                }
                else if (application.backendTypeJavaAny) {
                    Object.assign(application.clientPackageJsonScripts, {
                        'pree2e:headless': 'npm run ci:server:await',
                    });
                }
            },
        });
    }
    get [BaseApplicationGenerator.PREPARING]() {
        return this.delegateTasksToBlueprint(() => this.preparing);
    }
    get postPreparingEachEntity() {
        return this.asPreparingEachEntityTaskGroup({
            prepareForTemplates({ entity }) {
                mutateData(entity, {
                    workaroundEntityCannotBeEmpty: false,
                    workaroundInstantReactiveMariaDB: false,
                    generateEntityCypress: ({ builtInUserManagement, skipClient }) => !skipClient || builtInUserManagement,
                });
            },
        });
    }
    get [BaseApplicationGenerator.POST_PREPARING_EACH_ENTITY]() {
        return this.delegateTasksToBlueprint(() => this.postPreparingEachEntity);
    }
    get writing() {
        return this.asWritingTaskGroup({
            async cleanup({ control, application: { authenticationTypeOauth2, generateUserManagement, cypressDir } }) {
                if (control.isJhipsterVersionLessThan('7.0.0-beta.1')) {
                    this.removeFile(`${cypressDir}support/keycloak-oauth2.ts`);
                    this.removeFile(`${cypressDir}fixtures/users/user.json`);
                }
                if (control.isJhipsterVersionLessThan('7.8.2')) {
                    this.removeFile('cypress.json');
                    this.removeFile('cypress-audits.json');
                    this.removeFile(`${cypressDir}integration/administration/administration.spec.ts`);
                    this.removeFile(`${cypressDir}integration/lighthouse.audits.ts`);
                    if (!authenticationTypeOauth2) {
                        this.removeFile(`${cypressDir}integration/account/login-page.spec.ts`);
                    }
                    if (generateUserManagement) {
                        this.removeFile(`${cypressDir}integration/account/register-page.spec.ts`);
                        this.removeFile(`${cypressDir}integration/account/settings-page.spec.ts`);
                        this.removeFile(`${cypressDir}integration/account/password-page.spec.ts`);
                        this.removeFile(`${cypressDir}integration/account/reset-password-page.spec.ts`);
                    }
                }
                await control.cleanupFiles({ '8.6.1': [`${cypressDir}.eslintrc.json`] });
            },
            async writeFiles({ application }) {
                const faker = await createFaker();
                faker.seed(stringHashCode(application.baseName));
                const context = { ...application, faker };
                return this.writeFiles({
                    sections: cypressFiles,
                    context,
                });
            },
        });
    }
    get [BaseApplicationGenerator.WRITING]() {
        return this.delegateTasksToBlueprint(() => this.writing);
    }
    get writingEntities() {
        return this.asWritingEntitiesTaskGroup({
            cleanupCypressEntityFiles({ application: { cypressDir }, control, entities }) {
                for (const entity of entities) {
                    if (control.isJhipsterVersionLessThan('7.8.2')) {
                        this.removeFile(`${cypressDir}integration/entity/${entity.entityFileName}.spec.ts`);
                    }
                }
            },
            async writeCypressEntityFiles({ application, entities }) {
                for (const entity of entities.filter(entity => entity.generateEntityCypress && !entity.embedded && !entity.builtInUser && !entity.entityClientModelOnly)) {
                    const context = { ...application, ...entity };
                    await this.writeFiles({
                        sections: cypressEntityFiles,
                        context,
                    });
                }
            },
        });
    }
    get [BaseApplicationGenerator.WRITING_ENTITIES]() {
        return this.delegateTasksToBlueprint(() => this.writingEntities);
    }
    get postWriting() {
        return this.asPostWritingTaskGroup({
            packageJson({ application }) {
                const clientPackageJson = this.createStorage(this.destinationPath(application.clientRootDir, 'package.json'));
                clientPackageJson.merge({
                    devDependencies: {
                        cypress: application.nodeDependencies.cypress,
                        'cypress-terminal-report': null,
                        'eslint-plugin-cypress': application.nodeDependencies['eslint-plugin-cypress'],
                    },
                });
                if (application.cypressCoverage && this.angularSchematic) {
                    clientPackageJson.merge({
                        devDependencies: {
                            'cypress-monocart-coverage': null,
                        },
                        scripts: {
                            'pree2e:cypress:coverage': 'npm run ci:server:await',
                            'e2e:cypress:coverage': 'ng e2e --configuration coverage',
                        },
                    });
                }
            },
            configureAudits({ application }) {
                if (!application.cypressAudit)
                    return;
                const clientPackageJson = this.createStorage(this.destinationPath(application.clientRootDir, 'package.json'));
                clientPackageJson.merge({
                    devDependencies: {
                        lighthouse: application.nodeDependencies.lighthouse,
                        'cypress-audit': application.nodeDependencies['cypress-audit'],
                    },
                    scripts: {
                        'cypress:audits': 'cypress open --e2e --config-file cypress-audits.config.ts',
                        'e2e:cypress:audits:headless': 'npm run e2e:cypress -- --config-file cypress-audits.config.ts',
                        'e2e:cypress:audits': 'cypress run --e2e --browser chrome --config-file cypress-audits.config.ts',
                    },
                });
            },
            configureWebpackCoverage({ application, source }) {
                const { cypressCoverageWebpack, clientFrameworkAngular, clientRootDir, dasherizedBaseName } = application;
                if (!cypressCoverageWebpack)
                    return;
                const clientPackageJson = this.createStorage(this.destinationPath(application.clientRootDir, 'package.json'));
                clientPackageJson.merge({
                    devDependencies: {
                        '@cypress/code-coverage': application.nodeDependencies['@cypress/code-coverage'],
                        'babel-loader': application.nodeDependencies['babel-loader'],
                        'babel-plugin-istanbul': application.nodeDependencies['babel-plugin-istanbul'],
                        nyc: application.nodeDependencies.nyc,
                    },
                    scripts: {
                        'clean-coverage': 'rimraf .nyc_output coverage',
                        'pree2e:cypress:coverage': 'npm run clean-coverage && npm run ci:server:await',
                        'e2e:cypress:coverage': 'npm run e2e:cypress:headed',
                        'poste2e:cypress:coverage': 'nyc report',
                        'prewebapp:instrumenter': 'npm run clean-www && npm run clean-coverage',
                        'webapp:instrumenter': 'ng build --configuration instrumenter',
                    },
                });
                if (clientFrameworkAngular) {
                    // Add 'ng build --configuration instrumenter' support
                    this.createStorage(`${clientRootDir}angular.json`).setPath(`projects.${dasherizedBaseName}.architect.build.configurations.instrumenter`, {});
                    source.addWebpackConfig?.({
                        config: String.raw `targetOptions.configuration === 'instrumenter'
      ? {
          module: {
            rules: [
              {
                test: /\.(js|ts)$/,
                use: [
                  {
                    loader: 'babel-loader',
                    options: {
                      plugins: ['istanbul'],
                    },
                  }
                ],
                enforce: 'post',
                include: path.resolve(__dirname, '../${CLIENT_MAIN_SRC_DIR}'),
                exclude: [/\.(e2e|spec)\.ts$/, /node_modules/, /(ngfactory|ngstyle)\.js/],
              },
            ],
          },
        }
      : {}`,
                    });
                }
            },
            cypressSchematics({ application, source }) {
                const { applicationTypeMicroservice, dasherizedBaseName, clientRootDir, gatewayServerPort, serverPort } = application;
                if (!this.angularSchematic)
                    return;
                source.mergeClientPackageJson?.({
                    devDependencies: {
                        '@cypress/schematic': null,
                    },
                });
                this.mergeDestinationJson(`${clientRootDir}angular.json`, {
                    projects: {
                        [application.dasherizedBaseName]: {
                            architect: {
                                e2e: {
                                    builder: '@cypress/schematic:cypress',
                                    options: { browser: 'chrome' },
                                    configurations: {
                                        baseHref: {
                                            headless: true,
                                            baseUrl: `http://localhost:${applicationTypeMicroservice ? gatewayServerPort : serverPort}`,
                                        },
                                        open: {
                                            watch: true,
                                            devServerTarget: `${dasherizedBaseName}:serve`,
                                        },
                                        openProduction: {
                                            watch: true,
                                            devServerTarget: `${dasherizedBaseName}:serve:production`,
                                        },
                                        run: {
                                            headless: true,
                                            devServerTarget: `${dasherizedBaseName}:serve`,
                                        },
                                        runProduction: {
                                            headless: true,
                                            devServerTarget: `${dasherizedBaseName}:serve:production`,
                                        },
                                    },
                                    defaultConfiguration: 'open',
                                },
                            },
                        },
                    },
                });
            },
            configureV8Coverage({ application }) {
                const { cypressCoverage, clientRootDir, dasherizedBaseName } = application;
                if (!cypressCoverage || !this.angularSchematic)
                    return;
                this.mergeDestinationJson(`${clientRootDir}angular.json`, {
                    projects: {
                        [dasherizedBaseName]: {
                            architect: {
                                e2e: {
                                    configurations: {
                                        coverage: {
                                            headless: true,
                                            devServerTarget: `${dasherizedBaseName}:serve:development`,
                                            env: { CYPRESS_COVERAGE: true },
                                        },
                                        openCoverage: {
                                            watch: true,
                                            devServerTarget: `${dasherizedBaseName}:serve:development`,
                                            env: { CYPRESS_COVERAGE: true },
                                        },
                                    },
                                },
                            },
                        },
                    },
                });
            },
            mavenProfile({ source }) {
                source.addMavenProfile?.({
                    id: 'e2e',
                    content: `
            <properties>
                <profile.e2e>,e2e</profile.e2e>
            </properties>
            <build>
                <finalName>e2e</finalName>
            </build>
          `,
                });
            },
        });
    }
    get [BaseApplicationGenerator.POST_WRITING]() {
        return this.delegateTasksToBlueprint(() => this.postWriting);
    }
    generateTestEntity(fields) {
        return generateTestEntity(fields);
    }
}
