<commit_before>/**
 * Copyright 2013-2017 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see http://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
 *
 *      http://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.
 */
const chalk = require('chalk');
const BaseGenerator = require('../generator-base');
const cleanup = require('../cleanup');
const prompts = require('./prompts');
const packagejs = require('../../package.json');
const constants = require('../generator-constants');

module.exports = class extends BaseGenerator {
    constructor(args, opts) {
        super(args, opts);

        this.configOptions = {};
        // This adds support for a `--skip-client` flag
        this.option('skip-client', {
            desc: 'Skip the client-side application generation',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--skip-server` flag
        this.option('skip-server', {
            desc: 'Skip the server-side application generation',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--skip-git` flag
        this.option('skip-git', {
            desc: 'Skip the git initialization',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--skip-user-management` flag
        this.option('skip-user-management', {
            desc: 'Skip the user management module during app generation',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--[no-]i18n` flag
        this.option('i18n', {
            desc: 'Disable or enable i18n when skipping client side generation, has no effect otherwise',
            type: Boolean,
            defaults: true
        });

        // This adds support for a `--with-entities` flag
        this.option('with-entities', {
            desc: 'Regenerate the existing entities if any',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--skip-checks` flag
        this.option('skip-checks', {
            desc: 'Check the status of the required tools',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--jhi-prefix` flag
        this.option('jhi-prefix', {
            desc: 'Add prefix before services, controllers and states name',
            type: String,
            defaults: 'jhi'
        });

        // This adds support for a `--npm` flag
        this.option('npm', {
            desc: 'Use npm instead of yarn',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--auth` flag
        this.option('auth', {
            desc: 'Provide authentication type for the application when skipping server',
            type: String
        });

        // This adds support for a `--db` flag
        this.option('db', {
            desc: 'Provide DB name for the application when skipping server',
            type: String
        });

        this.currentQuestion = 0;
        this.totalQuestions = constants.QUESTIONS;
        this.skipClient = this.configOptions.skipClient = this.options['skip-client'] || this.config.get('skipClient');
        this.skipServer = this.configOptions.skipServer = this.options['skip-server'] || this.config.get('skipServer');
        this.skipUserManagement = this.configOptions.skipUserManagement = this.options['skip-user-management'] || this.config.get('skipUserManagement');
        this.jhiPrefix = this.configOptions.jhiPrefix || this.config.get('jhiPrefix') || this.options['jhi-prefix'];
        this.withEntities = this.options['with-entities'];
        this.skipChecks = this.options['skip-checks'];
        this.useYarn = this.configOptions.useYarn = !this.options.npm;
        this.isDebugEnabled = this.configOptions.isDebugEnabled = this.options.debug;
    }

    get initializing() {
        return {
            displayLogo() {
                this.printJHipsterLogo();
            },

            checkJava() {
                this.checkJava();
            },

            checkNode() {
                this.checkNode();
            },

            checkGit() {
                this.checkGit();
            },

            checkGitConnection() {
                this.checkGitConnection();
            },

            checkYarn() {
                this.checkYarn();
            },

            checkForNewVersion() {
                if (!this.skipChecks) {
                    this.checkForNewVersion();
                }
            },

            validate() {
                if (this.skipServer && this.skipClient) {
                    this.error(chalk.red(`You can not pass both ${chalk.yellow('--skip-client')} and ${chalk.yellow('--skip-server')} together`));
                }
            },

            setupconsts() {
                this.applicationType = this.config.get('applicationType');
                if (!this.applicationType) {
                    this.applicationType = 'monolith';
                }
                this.baseName = this.config.get('baseName');
                this.jhipsterVersion = packagejs.version;
                if (this.jhipsterVersion === undefined) {
                    this.jhipsterVersion = this.config.get('jhipsterVersion');
                }
                this.otherModules = this.config.get('otherModules');
                this.testFrameworks = this.config.get('testFrameworks');
                this.enableTranslation = this.config.get('enableTranslation');
                this.nativeLanguage = this.config.get('nativeLanguage');
                this.languages = this.config.get('languages');
                const configFound = this.baseName !== undefined && this.applicationType !== undefined;
                if (configFound) {
                    this.existingProject = true;
                    // If translation is not defined, it is enabled by default
                    if (this.enableTranslation === undefined) {
                        this.enableTranslation = true;
                    }
                }
                this.clientPackageManager = this.config.get('clientPackageManager');
                if (!this.clientPackageManager) {
                    if (this.useYarn) {
                        this.clientPackageManager = 'yarn';
                    } else {
                        this.clientPackageManager = 'npm';
                    }
                }
            }
        };
    }

    get prompting() {
        return {
            askForInsightOptIn: prompts.askForInsightOptIn,
            askForApplicationType: prompts.askForApplicationType,
            askForModuleName: prompts.askForModuleName
        };
    }

    get configuring() {
        return {
            setup() {
                this.configOptions.skipI18nQuestion = true;
                this.configOptions.baseName = this.baseName;
                this.configOptions.logo = false;
                this.configOptions.otherModules = this.otherModules;
                this.configOptions.lastQuestion = this.currentQuestion;
                this.generatorType = 'app';
                if (this.applicationType === 'microservice') {
                    this.skipClient = true;
                    this.generatorType = 'server';
                    this.skipUserManagement = this.configOptions.skipUserManagement = true;
                }
                if (this.applicationType === 'uaa') {
                    this.skipClient = true;
                    this.generatorType = 'server';
                    this.skipUserManagement = this.configOptions.skipUserManagement = false;
                    this.authenticationType = this.configOptions.authenticationType = 'uaa';
                }
                if (this.skipClient) {
                    // defaults to use when skipping client
                    this.generatorType = 'server';
                    this.configOptions.enableTranslation = this.options.i18n;
                }
                if (this.skipServer) {
                    // defaults to use when skipping server
                    this.generatorType = 'client';
                    this.configOptions.databaseType = this.getDBTypeFromDBValue(this.options.db);
                    this.configOptions.devDatabaseType = this.options.db;
                    this.configOptions.prodDatabaseType = this.options.db;
                    this.configOptions.authenticationType = this.options.auth;
                }
                this.configOptions.clientPackageManager = this.clientPackageManager;
            },

            composeServer() {
                if (this.skipServer) return;

                this.composeWith(require.resolve('../server'), {
                    'client-hook': !this.skipClient,
                    configOptions: this.configOptions,
                    force: this.options.force,
                    debug: this.isDebugEnabled
                });
            },

            composeClient() {
                if (this.skipClient) return;

                this.composeWith(require.resolve('../client'), {
                    'skip-install': this.options['skip-install'],
                    configOptions: this.configOptions,
                    force: this.options.force,
                    debug: this.isDebugEnabled
                });
            },

            askFori18n: prompts.askFori18n
        };
    }

    get default() {
        return {
            askForTestOpts: prompts.askForTestOpts,

            askForMoreModules: prompts.askForMoreModules,

            setSharedConfigOptions() {
                this.configOptions.lastQuestion = this.currentQuestion;
                this.configOptions.totalQuestions = this.totalQuestions;
                this.configOptions.testFrameworks = this.testFrameworks;
                this.configOptions.enableTranslation = this.enableTranslation;
                this.configOptions.nativeLanguage = this.nativeLanguage;
                this.configOptions.languages = this.languages;
                this.configOptions.clientPackageManager = this.clientPackageManager;
            },

            insight() {
                const insight = this.insight();
                insight.trackWithEvent('generator', 'app');
                insight.track('app/applicationType', this.applicationType);
                insight.track('app/testFrameworks', this.testFrameworks);
                insight.track('app/otherModules', this.otherModules);
                insight.track('app/clientPackageManager', this.clientPackageManager);
            },

            composeLanguages() {
                if (this.skipI18n) return;
                this.composeLanguagesSub(this, this.configOptions, this.generatorType);
            },

            saveConfig() {
                this.config.set('jhipsterVersion', packagejs.version);
                this.config.set('applicationType', this.applicationType);
                this.config.set('baseName', this.baseName);
                this.config.set('testFrameworks', this.testFrameworks);
                this.config.set('jhiPrefix', this.jhiPrefix);
                this.config.set('otherModules', this.otherModules);
                if (this.skipClient) this.config.set('skipClient', true);
                if (this.skipServer) this.config.set('skipServer', true);
                if (this.skipUserManagement) this.config.set('skipUserManagement', true);
                this.config.set('enableTranslation', this.enableTranslation);
                if (this.enableTranslation) {
                    this.config.set('nativeLanguage', this.nativeLanguage);
                    this.config.set('languages', this.languages);
                }
                this.config.set('clientPackageManager', this.clientPackageManager);
            }
        };
    }

    get writing() {
        return {
            cleanup() {
                cleanup.cleanupOldFiles(this, this.javaDir, this.testDir);
            },

            regenerateEntities() {
                if (this.withEntities) {
                    this.getExistingEntities().forEach((entity) => {
                        this.composeWith(require.resolve('../entity'), {
                            regenerate: true,
                            'skip-install': true,
                            force: this.options.force,
                            debug: this.isDebugEnabled,
                            arguments: [entity.name]
                        });
                    });
                }
            }
        };
    }

    get end() {
        return {
            localInstall() {
                if (this.skipClient) {
                    if (this.otherModules === undefined) {
                        this.otherModules = [];
                    }
                    // Generate a package.json file containing the current version
                    // of the generator as dependency
                    this.template('_skipClientApp.package.json', 'package.json');

                    if (!this.options['skip-install']) {
                        if (this.clientPackageManager === 'yarn') {
                            this.log(chalk.bold(`\nInstalling generator-jhipster@${this.jhipsterVersion} locally using yarn`));
                            this.yarnInstall();
                        } else if (this.clientPackageManager === 'npm') {
                            this.log(chalk.bold(`\nInstalling generator-jhipster@${this.jhipsterVersion} locally using npm`));
                            this.npmInstall();
                        }
                    }
                }

                if (!this.options['skip-git']) {
                    this.isGitInstalled((code) => {
                        if (code === 0) {
                            this.gitExec('rev-parse --is-inside-work-tree', (err, gitDir) => {
                                if (!gitDir) {
                                    this.gitExec('init', () => {
                                        this.log('git init successful');
                                        this.gitExec(['add -A'], () => {
                                            this.log('git add successful');
                                            this.gitExec([`commit -am "Initial application generated by Jhipster-${this.jhipsterVersion}"`], () => this.log('git commit successful'));
                                        });
                                    });
                                }
                            });
                        } else {
                            this.warning('The generated application could not be added to Git, as Git is not installed on your system');
                        }
                    });
                }
            },

            afterRunHook() {
                try {
                    const modules = this.getModuleHooks();
                    if (modules.length > 0) {
                        this.log(`\n${chalk.bold.green('Running post run module hooks\n')}`);
                        // run through all post app creation module hooks
                        this.callHooks('app', 'post', {
                            appConfig: this.configOptions,
                            force: this.options.force
                        });
                    }
                } catch (err) {
                    this.log(`\n${chalk.bold.red('Running post run module hooks failed. No modification done to the generated app.')}`);
                    this.debug('Error:', err);
                }
            }
        };
    }
};
<commit_msg>removing array<commit_after>/**
 * Copyright 2013-2017 the original author or authors from the JHipster project.
 *
 * This file is part of the JHipster project, see http://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
 *
 *      http://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.
 */
const chalk = require('chalk');
const BaseGenerator = require('../generator-base');
const cleanup = require('../cleanup');
const prompts = require('./prompts');
const packagejs = require('../../package.json');
const constants = require('../generator-constants');

module.exports = class extends BaseGenerator {
    constructor(args, opts) {
        super(args, opts);

        this.configOptions = {};
        // This adds support for a `--skip-client` flag
        this.option('skip-client', {
            desc: 'Skip the client-side application generation',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--skip-server` flag
        this.option('skip-server', {
            desc: 'Skip the server-side application generation',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--skip-git` flag
        this.option('skip-git', {
            desc: 'Skip the git initialization',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--skip-user-management` flag
        this.option('skip-user-management', {
            desc: 'Skip the user management module during app generation',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--[no-]i18n` flag
        this.option('i18n', {
            desc: 'Disable or enable i18n when skipping client side generation, has no effect otherwise',
            type: Boolean,
            defaults: true
        });

        // This adds support for a `--with-entities` flag
        this.option('with-entities', {
            desc: 'Regenerate the existing entities if any',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--skip-checks` flag
        this.option('skip-checks', {
            desc: 'Check the status of the required tools',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--jhi-prefix` flag
        this.option('jhi-prefix', {
            desc: 'Add prefix before services, controllers and states name',
            type: String,
            defaults: 'jhi'
        });

        // This adds support for a `--npm` flag
        this.option('npm', {
            desc: 'Use npm instead of yarn',
            type: Boolean,
            defaults: false
        });

        // This adds support for a `--auth` flag
        this.option('auth', {
            desc: 'Provide authentication type for the application when skipping server',
            type: String
        });

        // This adds support for a `--db` flag
        this.option('db', {
            desc: 'Provide DB name for the application when skipping server',
            type: String
        });

        this.currentQuestion = 0;
        this.totalQuestions = constants.QUESTIONS;
        this.skipClient = this.configOptions.skipClient = this.options['skip-client'] || this.config.get('skipClient');
        this.skipServer = this.configOptions.skipServer = this.options['skip-server'] || this.config.get('skipServer');
        this.skipUserManagement = this.configOptions.skipUserManagement = this.options['skip-user-management'] || this.config.get('skipUserManagement');
        this.jhiPrefix = this.configOptions.jhiPrefix || this.config.get('jhiPrefix') || this.options['jhi-prefix'];
        this.withEntities = this.options['with-entities'];
        this.skipChecks = this.options['skip-checks'];
        this.useYarn = this.configOptions.useYarn = !this.options.npm;
        this.isDebugEnabled = this.configOptions.isDebugEnabled = this.options.debug;
    }

    get initializing() {
        return {
            displayLogo() {
                this.printJHipsterLogo();
            },

            checkJava() {
                this.checkJava();
            },

            checkNode() {
                this.checkNode();
            },

            checkGit() {
                this.checkGit();
            },

            checkGitConnection() {
                this.checkGitConnection();
            },

            checkYarn() {
                this.checkYarn();
            },

            checkForNewVersion() {
                if (!this.skipChecks) {
                    this.checkForNewVersion();
                }
            },

            validate() {
                if (this.skipServer && this.skipClient) {
                    this.error(chalk.red(`You can not pass both ${chalk.yellow('--skip-client')} and ${chalk.yellow('--skip-server')} together`));
                }
            },

            setupconsts() {
                this.applicationType = this.config.get('applicationType');
                if (!this.applicationType) {
                    this.applicationType = 'monolith';
                }
                this.baseName = this.config.get('baseName');
                this.jhipsterVersion = packagejs.version;
                if (this.jhipsterVersion === undefined) {
                    this.jhipsterVersion = this.config.get('jhipsterVersion');
                }
                this.otherModules = this.config.get('otherModules');
                this.testFrameworks = this.config.get('testFrameworks');
                this.enableTranslation = this.config.get('enableTranslation');
                this.nativeLanguage = this.config.get('nativeLanguage');
                this.languages = this.config.get('languages');
                const configFound = this.baseName !== undefined && this.applicationType !== undefined;
                if (configFound) {
                    this.existingProject = true;
                    // If translation is not defined, it is enabled by default
                    if (this.enableTranslation === undefined) {
                        this.enableTranslation = true;
                    }
                }
                this.clientPackageManager = this.config.get('clientPackageManager');
                if (!this.clientPackageManager) {
                    if (this.useYarn) {
                        this.clientPackageManager = 'yarn';
                    } else {
                        this.clientPackageManager = 'npm';
                    }
                }
            }
        };
    }

    get prompting() {
        return {
            askForInsightOptIn: prompts.askForInsightOptIn,
            askForApplicationType: prompts.askForApplicationType,
            askForModuleName: prompts.askForModuleName
        };
    }

    get configuring() {
        return {
            setup() {
                this.configOptions.skipI18nQuestion = true;
                this.configOptions.baseName = this.baseName;
                this.configOptions.logo = false;
                this.configOptions.otherModules = this.otherModules;
                this.configOptions.lastQuestion = this.currentQuestion;
                this.generatorType = 'app';
                if (this.applicationType === 'microservice') {
                    this.skipClient = true;
                    this.generatorType = 'server';
                    this.skipUserManagement = this.configOptions.skipUserManagement = true;
                }
                if (this.applicationType === 'uaa') {
                    this.skipClient = true;
                    this.generatorType = 'server';
                    this.skipUserManagement = this.configOptions.skipUserManagement = false;
                    this.authenticationType = this.configOptions.authenticationType = 'uaa';
                }
                if (this.skipClient) {
                    // defaults to use when skipping client
                    this.generatorType = 'server';
                    this.configOptions.enableTranslation = this.options.i18n;
                }
                if (this.skipServer) {
                    // defaults to use when skipping server
                    this.generatorType = 'client';
                    this.configOptions.databaseType = this.getDBTypeFromDBValue(this.options.db);
                    this.configOptions.devDatabaseType = this.options.db;
                    this.configOptions.prodDatabaseType = this.options.db;
                    this.configOptions.authenticationType = this.options.auth;
                }
                this.configOptions.clientPackageManager = this.clientPackageManager;
            },

            composeServer() {
                if (this.skipServer) return;

                this.composeWith(require.resolve('../server'), {
                    'client-hook': !this.skipClient,
                    configOptions: this.configOptions,
                    force: this.options.force,
                    debug: this.isDebugEnabled
                });
            },

            composeClient() {
                if (this.skipClient) return;

                this.composeWith(require.resolve('../client'), {
                    'skip-install': this.options['skip-install'],
                    configOptions: this.configOptions,
                    force: this.options.force,
                    debug: this.isDebugEnabled
                });
            },

            askFori18n: prompts.askFori18n
        };
    }

    get default() {
        return {
            askForTestOpts: prompts.askForTestOpts,

            askForMoreModules: prompts.askForMoreModules,

            setSharedConfigOptions() {
                this.configOptions.lastQuestion = this.currentQuestion;
                this.configOptions.totalQuestions = this.totalQuestions;
                this.configOptions.testFrameworks = this.testFrameworks;
                this.configOptions.enableTranslation = this.enableTranslation;
                this.configOptions.nativeLanguage = this.nativeLanguage;
                this.configOptions.languages = this.languages;
                this.configOptions.clientPackageManager = this.clientPackageManager;
            },

            insight() {
                const insight = this.insight();
                insight.trackWithEvent('generator', 'app');
                insight.track('app/applicationType', this.applicationType);
                insight.track('app/testFrameworks', this.testFrameworks);
                insight.track('app/otherModules', this.otherModules);
                insight.track('app/clientPackageManager', this.clientPackageManager);
            },

            composeLanguages() {
                if (this.skipI18n) return;
                this.composeLanguagesSub(this, this.configOptions, this.generatorType);
            },

            saveConfig() {
                this.config.set('jhipsterVersion', packagejs.version);
                this.config.set('applicationType', this.applicationType);
                this.config.set('baseName', this.baseName);
                this.config.set('testFrameworks', this.testFrameworks);
                this.config.set('jhiPrefix', this.jhiPrefix);
                this.config.set('otherModules', this.otherModules);
                if (this.skipClient) this.config.set('skipClient', true);
                if (this.skipServer) this.config.set('skipServer', true);
                if (this.skipUserManagement) this.config.set('skipUserManagement', true);
                this.config.set('enableTranslation', this.enableTranslation);
                if (this.enableTranslation) {
                    this.config.set('nativeLanguage', this.nativeLanguage);
                    this.config.set('languages', this.languages);
                }
                this.config.set('clientPackageManager', this.clientPackageManager);
            }
        };
    }

    get writing() {
        return {
            cleanup() {
                cleanup.cleanupOldFiles(this, this.javaDir, this.testDir);
            },

            regenerateEntities() {
                if (this.withEntities) {
                    this.getExistingEntities().forEach((entity) => {
                        this.composeWith(require.resolve('../entity'), {
                            regenerate: true,
                            'skip-install': true,
                            force: this.options.force,
                            debug: this.isDebugEnabled,
                            arguments: [entity.name]
                        });
                    });
                }
            }
        };
    }

    get end() {
        return {
            localInstall() {
                if (this.skipClient) {
                    if (this.otherModules === undefined) {
                        this.otherModules = [];
                    }
                    // Generate a package.json file containing the current version
                    // of the generator as dependency
                    this.template('_skipClientApp.package.json', 'package.json');

                    if (!this.options['skip-install']) {
                        if (this.clientPackageManager === 'yarn') {
                            this.log(chalk.bold(`\nInstalling generator-jhipster@${this.jhipsterVersion} locally using yarn`));
                            this.yarnInstall();
                        } else if (this.clientPackageManager === 'npm') {
                            this.log(chalk.bold(`\nInstalling generator-jhipster@${this.jhipsterVersion} locally using npm`));
                            this.npmInstall();
                        }
                    }
                }

                if (!this.options['skip-git']) {
                    this.isGitInstalled((code) => {
                        if (code === 0) {
                            this.gitExec('rev-parse --is-inside-work-tree', (err, gitDir) => {
                                if (!gitDir) {
                                    this.gitExec('init', () => {
                                        this.log('git init successful');
                                        this.gitExec('add -A', () => {
                                            this.log('git add successful');
                                            this.gitExec(`commit -am "Initial application generated by Jhipster-${this.jhipsterVersion}"`, () => this.log('git commit successful'));
                                        });
                                    });
                                }
                            });
                        } else {
                            this.warning('The generated application could not be added to Git, as Git is not installed on your system');
                        }
                    });
                }
            },

            afterRunHook() {
                try {
                    const modules = this.getModuleHooks();
                    if (modules.length > 0) {
                        this.log(`\n${chalk.bold.green('Running post run module hooks\n')}`);
                        // run through all post app creation module hooks
                        this.callHooks('app', 'post', {
                            appConfig: this.configOptions,
                            force: this.options.force
                        });
                    }
                } catch (err) {
                    this.log(`\n${chalk.bold.red('Running post run module hooks failed. No modification done to the generated app.')}`);
                    this.debug('Error:', err);
                }
            }
        };
    }
};
<|endoftext|>


<commit_before>/* eslint-env jest */
import getCurrentCampaignConfig from '../getCurrentCampaignConfig'
import createCampaignConfiguration from '../createCampaignConfiguration'

jest.mock('../createCampaignConfiguration')

describe('getCurrentCampaignConfig', () => {
  it('returns an object with the expected required properties', () => {
    expect.assertions(1)
    expect(getCurrentCampaignConfig()).toMatchObject({
      campaignId: expect.any(String),
      content: {
        titleMarkdown: expect.any(String),
        descriptionMarkdown: expect.any(String),
      },
      getCharityData: expect.any(Function),
      incrementNewUserCount: expect.any(Function),
      incrementTabCount: expect.any(Function),
      isLive: expect.any(Boolean),
      showCountdownTimer: expect.any(Boolean),
      showHeartsDonationButton: expect.any(Boolean),
      showProgressBar: expect.any(Boolean),
      time: {
        start: expect.any(String),
        end: expect.any(String),
      },
    })
  })
})

// Test the transformNumberSourceValue function, which is unique
// to each campaign.
describe('getCurrentCampaignConfig: transformNumberSourceValue', () => {
  it('behaves as expected for the current campaign', () => {
    getCurrentCampaignConfig()
    const configInput = createCampaignConfiguration.mock.calls[0][0]
    if (!configInput.goal || !configInput.goal.transformNumberSourceValue) {
      // The input does not define a goal.transformNumberSourceValue,
      // so there is no need to test it.
      expect(true).toBe(true)
      return
    }
    const transformFunc = configInput.goal.transformNumberSourceValue

    // Test campaign-specific transform logic here.
    expect(transformFunc(0)).toEqual(0)
    expect(transformFunc(1)).toEqual(10)
    expect(transformFunc(1.23)).toEqual(10)
    expect(transformFunc(49)).toEqual(490)
    expect(transformFunc(50)).toEqual(500)
    expect(transformFunc(4902)).toEqual(49020)
  })
})
<commit_msg>Update test<commit_after>/* eslint-env jest */
import getCurrentCampaignConfig from '../getCurrentCampaignConfig'
import createCampaignConfiguration from '../createCampaignConfiguration'

jest.mock('../createCampaignConfiguration')

describe('getCurrentCampaignConfig', () => {
  it('returns an object with the expected required properties', () => {
    expect.assertions(1)
    expect(getCurrentCampaignConfig()).toMatchObject({
      campaignId: expect.any(String),
      content: {
        titleMarkdown: expect.any(String),
        descriptionMarkdown: expect.any(String),
      },
      getCharityData: expect.any(Function),
      incrementNewUserCount: expect.any(Function),
      incrementTabCount: expect.any(Function),
      isLive: expect.any(Boolean),
      showCountdownTimer: expect.any(Boolean),
      showHeartsDonationButton: expect.any(Boolean),
      showProgressBar: expect.any(Boolean),
      time: {
        start: expect.any(String),
        end: expect.any(String),
      },
    })
  })
})

// Test the transformNumberSourceValue function, which is unique
// to each campaign.
describe('getCurrentCampaignConfig: transformNumberSourceValue', () => {
  it('behaves as expected for the current campaign', () => {
    getCurrentCampaignConfig()
    const configInput = createCampaignConfiguration.mock.calls[0][0]
    if (!configInput.goal || !configInput.goal.transformNumberSourceValue) {
      // The input does not define a goal.transformNumberSourceValue,
      // so there is no need to test it.
      expect(true).toBe(true)
      return
    }
    const transformFunc = configInput.goal.transformNumberSourceValue

    // Test campaign-specific transform logic here.
    expect(transformFunc(0)).toEqual(0)
    expect(transformFunc(1)).toEqual(10)
    expect(transformFunc(1.26)).toEqual(12)
    expect(transformFunc(49)).toEqual(490)
    expect(transformFunc(50)).toEqual(500)
    expect(transformFunc(4902)).toEqual(49020)
  })
})
<|endoftext|>


<commit_before>CATARSE.ExploreIndexView = Backbone.View.extend({

  initialize: function() {
    _.bindAll(this, "render", "ProjectView", "ProjectsView", "initializeView", "recommended", "expiring", "recent", "successful", "category", "search", "updateSearch")
    CATARSE.router.route(":name", "category", this.category)
    CATARSE.router.route("recommended", "recommended", this.recommended)
    CATARSE.router.route("expiring", "expiring", this.expiring)
    CATARSE.router.route("recent", "recent", this.recent)
    CATARSE.router.route("successful", "successful", this.successful)
    CATARSE.router.route("search/*search", "search", this.search)
    CATARSE.router.route("", "index", this.index)
    this.render()
    _this = this;
  },

  ProjectView: CATARSE.ModelView.extend({
    template: _.template(this.$('#project_template').html())
  }),

  ProjectsView: CATARSE.PaginatedView.extend({
    template: _.template(this.$('#projects_template').html()),
    emptyTemplate: _.template(this.$('#empty_projects_template').html())
  }),

  search: function(search){
    if(this.$('.section_header .replaced_header')) {
      this.$('.section_header .replaced_header').remove();
    }
    this.$('.section_header .original_title').fadeOut(300, function() {
      $('.section_header').append('

');
      $('.section_header .replaced_header').html('
Explore / '+encodeURIComponent(search)+'
');
    })
    this.selectItem("")
    this.initializeView({
      meta_sort: "explore",
      name_or_headline_or_about_or_user_name_or_user_address_city_contains: search
    })
    var input = this.$('#search')
    if(input.val() != search)
      input.val(search)
  },

  updateSearch: function(){
    var search = encodeURIComponent(this.$('#search').val())
    CATARSE.router.navigate("search/" + encodeURIComponent(search), true)
  },

  index: function(){
    _this.changeReplacedTitle()
    _this.selectItem("recommended")
    _this.initializeView({
      recommended: true,
      not_expired: true,
      meta_sort: "explore"
    })
  },

  recommended: function(){
    this.replaceTitleBy("recommended")
    this.selectItem("recommended")
    this.initializeView({
      recommended: true,
      not_expired: true,
      meta_sort: "explore"
    })
  },

  expiring: function(){
    this.replaceTitleBy("expiring")
    this.selectItem("expiring")
    this.initializeView({
      expiring: true,
      meta_sort: "expires_at"
    })
  },

  recent: function(){
    this.replaceTitleBy("recent")
    this.selectItem("recent")
    this.initializeView({
      recent: true,
      not_expired: true,
      meta_sort: "created_at.desc"
    })
  },

  successful: function(){
    this.replaceTitleBy("successful")
    this.selectItem("successful")
    this.initializeView({
      successful: true,
      meta_sort: "expires_at.desc"
    })
  },

  category: function(name){
    this.replaceTitleBy(name)
    this.selectItem(name)
    this.initializeView({
      category_id_equals: this.selectedItem.data("id"),
      meta_sort: "explore"
    })
  },

  initializeView: function(search){
    if(this.projectsView)
      this.projectsView.destroy()
    this.projectsView = new this.ProjectsView({
      modelView: this.ProjectView,
      collection: new CATARSE.Projects({search: search}),
      loading: this.$("#loading"),
      el: this.$("#explore_results .results")
    })
  },

  changeReplacedTitle: function() {
    if(this.$('.section_header .replaced_header')) {
      this.$('.section_header .replaced_header').fadeOut(300, function(){
        $(this).remove();
        $('.section_header .original_title').fadeIn(300);
      });
    }
  },

  replaceTitleBy: function(name) {
    if(this.$('.section_header .replaced_header')) {
      this.$('.section_header .replaced_header').remove();
    }
    this.$('.section_header .original_title').fadeOut(300, function() {
      $('.section_header').append('

');
      $('.section_header .replaced_header').html('
Explore '+$('.sidebar a[href=#' + name + ']').text()+'
');
    })
  },

  selectItem: function(name) {
    this.selectedItem = $('.sidebar a[href=#' + name + ']')
    $('.sidebar .selected').removeClass('selected')
    this.selectedItem.addClass('selected')
  },

  render: function(){
    this.$('#header .search input').timedKeyup(this.updateSearch, 1000)
  }

})
<commit_msg>fixed URI encoding in search, now search with special chars works in chorme<commit_after>CATARSE.ExploreIndexView = Backbone.View.extend({

  initialize: function() {
    _.bindAll(this, "render", "ProjectView", "ProjectsView", "initializeView", "recommended", "expiring", "recent", "successful", "category", "search", "updateSearch")
    CATARSE.router.route(":name", "category", this.category)
    CATARSE.router.route("recommended", "recommended", this.recommended)
    CATARSE.router.route("expiring", "expiring", this.expiring)
    CATARSE.router.route("recent", "recent", this.recent)
    CATARSE.router.route("successful", "successful", this.successful)
    CATARSE.router.route("search/*search", "search", this.search)
    CATARSE.router.route("", "index", this.index)
    this.render()
    _this = this;
  },

  ProjectView: CATARSE.ModelView.extend({
    template: _.template(this.$('#project_template').html())
  }),

  ProjectsView: CATARSE.PaginatedView.extend({
    template: _.template(this.$('#projects_template').html()),
    emptyTemplate: _.template(this.$('#empty_projects_template').html())
  }),

  search: function(search){
    search = decodeURIComponent(search);
    if(this.$('.section_header .replaced_header')) {
      this.$('.section_header .replaced_header').remove();
    }
    this.$('.section_header .original_title').fadeOut(300, function() {
      $('.section_header').append('

');
      $('.section_header .replaced_header').html('
Explore / '+ search +'
');
    })
    this.selectItem("")
    this.initializeView({
      meta_sort: "explore",
      name_or_headline_or_about_or_user_name_or_user_address_city_contains: search
    })
    var input = this.$('#search')
    if(input.val() != search)
      input.val(search)
  },

  updateSearch: function(){
    var search = encodeURIComponent(this.$('#search').val())
    CATARSE.router.navigate("search/" + encodeURIComponent(search), true)
  },

  index: function(){
    _this.changeReplacedTitle()
    _this.selectItem("recommended")
    _this.initializeView({
      recommended: true,
      not_expired: true,
      meta_sort: "explore"
    })
  },

  recommended: function(){
    this.replaceTitleBy("recommended")
    this.selectItem("recommended")
    this.initializeView({
      recommended: true,
      not_expired: true,
      meta_sort: "explore"
    })
  },

  expiring: function(){
    this.replaceTitleBy("expiring")
    this.selectItem("expiring")
    this.initializeView({
      expiring: true,
      meta_sort: "expires_at"
    })
  },

  recent: function(){
    this.replaceTitleBy("recent")
    this.selectItem("recent")
    this.initializeView({
      recent: true,
      not_expired: true,
      meta_sort: "created_at.desc"
    })
  },

  successful: function(){
    this.replaceTitleBy("successful")
    this.selectItem("successful")
    this.initializeView({
      successful: true,
      meta_sort: "expires_at.desc"
    })
  },

  category: function(name){
    this.replaceTitleBy(name)
    this.selectItem(name)
    this.initializeView({
      category_id_equals: this.selectedItem.data("id"),
      meta_sort: "explore"
    })
  },

  initializeView: function(search){
    if(this.projectsView)
      this.projectsView.destroy()
    this.projectsView = new this.ProjectsView({
      modelView: this.ProjectView,
      collection: new CATARSE.Projects({search: search}),
      loading: this.$("#loading"),
      el: this.$("#explore_results .results")
    })
  },

  changeReplacedTitle: function() {
    if(this.$('.section_header .replaced_header')) {
      this.$('.section_header .replaced_header').fadeOut(300, function(){
        $(this).remove();
        $('.section_header .original_title').fadeIn(300);
      });
    }
  },

  replaceTitleBy: function(name) {
    if(this.$('.section_header .replaced_header')) {
      this.$('.section_header .replaced_header').remove();
    }
    this.$('.section_header .original_title').fadeOut(300, function() {
      $('.section_header').append('

');
      $('.section_header .replaced_header').html('
Explore '+$('.sidebar a[href=#' + name + ']').text()+'
');
    })
  },

  selectItem: function(name) {
    this.selectedItem = $('.sidebar a[href=#' + name + ']')
    $('.sidebar .selected').removeClass('selected')
    this.selectedItem.addClass('selected')
  },

  render: function(){
    this.$('#header .search input').timedKeyup(this.updateSearch, 1000)
  }

})
<|endoftext|>


<commit_before>/* ************************************************************************

    License: MIT Licence

    Dependencies : [
        a.js
        plugin/translate.js
    ]

    Events : []

    Description:
        Manipulate HTML form by with a simple system.

************************************************************************ */

/**
 * Manipulate HTML form by with a simple system.
 *
 * Examples: here
 *
 * @class form
 * @static
 * @namespace a
*/
a.form = (function() {
    'use strict';

    // HTML/HTML5 input type allowed
    var typePatternList  = ['text', 'search', 'url',
                                    'tel', 'email', 'password'],
        minMaxStepList   = ['number', 'range', 'date',
                                    'datetime', 'datetime-local',
                                    'month', 'time', 'week'],
        typeRequiredList = typePatternList.concat(minMaxStepList,
                                    ['number', 'checkbox', 'radio', 'file']),
        typeMultipleList = ['email', 'file'],
        typeList = minMaxStepList.concat(typePatternList,
                                    ['color', 'checkbox', 'file',
                                    'hidden', 'radio']);

    /**
     * Get the field key from given input.
     *
     * @method getFieldKey
     * @private
     *
     * @param e {DOMElement}                The element o search value inside
     * @return {String}                     The value found
    */
    function getFieldKey(e) {
        var el   = a.dom.el(e),
            name = el.attribute('name');

        // Search the good attribute in case of problem
        if(a.isNone(name) || name === '') {
            name = el.attribute('id');
            // Should never appear... But we provide it in case of trouble
            if(a.isNone(name) || name === '') {
                name = el.attribute('class');
            }
        }

        return name;
    };

    /**
     * Get the field value for given input.
     *
     * @method getFieldValue
     * @private
     *
     * @param e {DOMElement}                The element to search value inside
     * @return {String}                     The value found
    */
    function getFieldValue(e) {
        var type    = e.type || '',
            tagName = e.tagName.toLowerCase();

        if(tagName === 'input' || tagName === 'textarea') {
            return (type === 'checkbox') ? e.checked : e.value;
        } else if(tagName === 'select') {
            if(e.options[e.selectedIndex]) {
                return e.options[e.selectedIndex].value
            }
            return null;
        }
    };

    /**
     * From a given dom, get the list of revelant elements inside.
     *
     * @method getFieldList
     * @private
     *
     * @param dom {a.dom}                   The dom element to search inside
     * @return {Array}                      The element list inside DOM
    */
    function getFieldList(dom) {
        // dom must be a a.dom element
        var elements = dom.tag(['input', 'textarea', 'select']).getElements();

        // We remove input who are not listed in typeList
        // LIKE: submit/reset should not appear in this list
        var i = elements.length;
        while(i--) {
            var el = elements[i];
            if(el.type &&
                    (  el.type == 'submit'
                    || el.type == 'reset'
                    || el.type == 'image'
                    ) ) {
                elements.splice(i, 1);
            }
        }

        // Now filtering is done, we can send back all elements
        return elements;
    };

    /**
     * Raise an error on input.
     *
     * @method validateError
     * @private
     *
     * @param el {DOMElement}               The element where comes from error
     * @param id {String}                   The element id/name/class
     * @param name {String | null}          The name (like min, pattern, ...)
     *                                      which is not valid, can be null
     * @param value {String | null}         The current input value
     *                                      (can be used as parameter)
     * @return {Object}                     A validate object with everything
     *                                      inside if possible
    */
    function validateError(el, id, name, value) {
        // First : we need to get error element and translate if possible
        var error = '';

        // Retrieve error tag
        if(!a.isNone(name) && name !== '') {
            error = el['data-error-' + name] || null;
        }
        if(a.isNone(error) || error === '') {
            error = el['data-error'] || null;
        }

        if(a.isNone(error) || error === '') {
            var errorMessage  = 'A data-error tag has not been setted for ';
                errorMessage += id + ' with value ' + value + 'n can\'t ';
                errorMessage += 'proceed error message';
            a.console.warn(errorMessage, 3);
        }

        // Translate error if possible
        error = a.translate.get(error, {
            name:  name,
            value: value
        });

        // Returning an object with all needed data inside
        return {
            el:    el,
            id:    id,
            error: error
        };
    };

    return {
        /**
         * Allow to skip HTML5 form-novalidate tag or not (boolean)
         *
         * @property skipNoValidate
         * @type Boolean
         * @default false
        */
        skipNoValidate: false,

        /**
         * Get the list of element stored into given form.
         *
         * @method get
         *
         * @param dom {Object}              The dom element to search inside
         *                                  - It has to be a valid a.dom.el
         *                                  input
         * @return {Object}                 The list of input tags existing
        */
        get: function(dom) {
            dom = a.dom.el(dom);
            var inputList  = getFieldList(dom),
                outputList = {};

            var i = inputList.length;
            while(i--) {
                var input = inputList[i];

                var name  = a.trim(getFieldKey(input)),
                    value = getFieldValue(input);

                // We don't continue if we don't find any data on element
                if(a.isNone(name) || !name) {
                    continue;
                }

                var parse = false;

                // We got a special case with input radio type
                if(!a.isNone(input) && input.type === 'radio') {
                    // Only checked one are validated
                    if(input.checked) {
                        parse = true;
                    }
                } else {
                    parse = true;
                }

                if(parse) {
                    // Name is a multiple value one (using [] at the end)
                    if(name.substr(name.length - 2) === '[]') {
                        if(!a.isArray(outputList[name])) {
                            outputList[name] = [];
                        }
                        value = (value) ? value: null;
                        outputList[name].push(value);
                    } else {
                       outputList[name] = (value) ? value: null;
                   }
                }
            };

            a.console.log('a.form.get: found element list:', 3);
            a.console.log(outputList, 3);
            return outputList;
        },

        /**
         * Validate a form
         * Note : multiple tester (email, file) is not supported
         * Note : date field (date, datetime, datetime-local,
         * month, time, week) are not supported
         * Note : tel/file field are not supported
         *
         * @method validate
         *
         * @param dom {Object}              The dom element to search inside
         *                                  - It has to be a valid a.dom.el
         *                                  input
         * @return {Array}                  An array with all errors listed
         *                                  inside, an empty array if there
         *                                  is no error to show
        */
        validate: function(dom) {
            dom = a.dom.el(dom);
            // On form tag, the "novalidate" allow to not validate a form
            if(this.skipNoValidate === false &&
                    !a.isNone(dom.get(0).novalidate)) {
                return [];
            }

            var inputList    = getFieldList(dom),
                // Store all errors appearing
                errorList    = [],
                // TODO : after todos are ended, remove this...
                allowedTypes = ['number', 'range', 'text', 'search',
                                        'url', 'email', 'password',
                                        'color', 'checkbox',
                                        'hidden', 'radio'],
                // Pretty basic url tester
                urlTester    = new RegExp(
                    '^[a-z]{2,}:\\/\\/([a-z0-9\\/\\.\\-_~+;:&=]{2,})$', 'gi'),
                // Pretty basic email tester
                emailTester  = new RegExp('^.{2,}@.*\\.[a-z0-9]{2,}$', 'gi'),
                colorTester  = new RegExp('^#([a-f]{3}|[a-f]{6})$', 'gi');

            /*
             * required : at least one char
                (text, search, url, tel, email, password, date, datetime,
                datetime-local, month, time, week, number, checkbox,
                radio, file)
             * pattern : a regex to test (Use title like a helper),
                (text, search, url, tel, email, password)
             * multiple : the user is allowed to enter more than one element
                (only for email, file)
             * min/max : min/max value
                (number, range, date, datetime, datetime-local,
                month, time, week)
             * step : multiplier
                (number, range, date, datetime, datetime-local,
                month, time, week)
            */
            var i = inputList.length;
            while(i--) {
                // Does only work for input tags
                var el      = inputList[i],
                    tagName = el.tagName.toLowerCase();

                // form novalidate : we must not validate
                // this element (including all select)
                if(tagName == 'select'
                    || !a.isNone(el.novalidate)) {
                    continue;
                }

                var type     = el.type,
                    name    = getFieldKey(el),
                    value    = el.value,

                    required = el.required,
                    pattern  = el.pattern,
                    multiple = el.multiple,
                    min      = el.min,
                    max      = el.max,
                    step     = el.step;

                // Double check float data
                min  = (a.isNone(min) || min == '')   ? null :
                                                            parseFloat(min);
                max  = (a.isNone(max) || max == '')   ? null :
                                                            parseFloat(max);
                step = (a.isNone(step) || step == '') ? null :
                                                            parseFloat(step);

                // Check input type does existing in allowed type list
                if(tagName == 'input'
                        && !a.contains(allowedTypes, type)
                        && !a.isNone(type)) {
                    var errorSupport =  'Type ' + type + ' for input ' + name;
                        errorSupport += ' not recognized or not supported';
                    a.console.warn(errorSupport, 3);
                    continue;
                }

                // Now checking type
                if( (type == 'number' || type == 'range')
                        && !a.isNumber(value) ) {
                    errorList.push(validateError(el, name, null, value));
                    continue;
                }
                if(type == 'url' && !urlTester.test(value) ) {
                    errorList.push(validateError(el, name, null, value));
                    continue;
                }
                if(type == 'email' && !emailTester.test(value) ) {
                    errorList.push(validateError(el, name, null, value));
                    continue;
                }
                if(type == 'color' && !colorTester.test(value) ) {
                    errorList.push(validateError(el, name, null, value));
                    continue;
                }

                // Required test
                if( required !== null
                    && a.contains(typeRequiredList, type)
                    && (value === '' || a.isNone(value)) ) {
                    errorList.push(validateError(el, name, 'required', value));
                    continue;
                }

                // Pattern test
                if( pattern !== null
                     && (tagName === "textarea"
                        ||(a.contains(typePatternList, type))
                        || a.isNone(type)
                        )
                ) {
                    var reg = new RegExp(pattern);
                    if(!reg.test(value)) {
                        errorList.push(validateError(
                                            el, name, 'pattern', value));
                        continue;
                    }
                }

                // Min/max/step test
                if( (min !== null || max != null || step != null)
                    && a.contains(minMaxStepList, type) ) {

                    var pval = parseFloat(value);
                    if( min !== null && pval < min ) {
                        errorList.push(validateError(el, name, 'min', value));
                        continue;
                    }
                    if( max !== null && pval > max ) {
                        errorList.push(validateError(el, name, 'max', value));
                        continue;
                    }
                    if( step !== null && pval % step !== 0 ) {
                        errorList.push(validateError(el, name, 'step', value));
                        continue;
                    }
                }
            }

            a.console.log('a.form.validate: found error list:', 3);
            a.console.log(errorList, 3);
            return errorList;
        },

        /**
         * Validate and get the form content.
         *
         * @method validateAndGet
         *
         * @param dom {Object}              The dom element to search inside
         *                                  - It has to be a valid a.dom.el
         *                                  input
         * @return {Object}                 An object with error (boolean),
         *                                  errorList (Array)
         *                                  and contentList (Array)
        */
        validateAndGet: function(dom) {
            var obj = {
                errorList   : this.validate(dom),
                error       : false,
                contentList : this.get(dom)
            };
            if(obj.errorList.length > 0) {
                obj.error = true;
            }
            return obj;
        }
    };

})();<commit_msg>Correct a tiny bug on html checkbox type.<commit_after>/* ************************************************************************

    License: MIT Licence

    Dependencies : [
        a.js
        plugin/translate.js
    ]

    Events : []

    Description:
        Manipulate HTML form by with a simple system.

************************************************************************ */

/**
 * Manipulate HTML form by with a simple system.
 *
 * Examples: here
 *
 * @class form
 * @static
 * @namespace a
*/
a.form = (function() {
    'use strict';

    // HTML/HTML5 input type allowed
    var typePatternList  = ['text', 'search', 'url',
                                    'tel', 'email', 'password'],
        minMaxStepList   = ['number', 'range', 'date',
                                    'datetime', 'datetime-local',
                                    'month', 'time', 'week'],
        typeRequiredList = typePatternList.concat(minMaxStepList,
                                    ['number', 'checkbox', 'radio', 'file']),
        typeMultipleList = ['email', 'file'],
        typeList = minMaxStepList.concat(typePatternList,
                                    ['color', 'checkbox', 'file',
                                    'hidden', 'radio']);

    /**
     * Get the field key from given input.
     *
     * @method getFieldKey
     * @private
     *
     * @param e {DOMElement}                The element o search value inside
     * @return {String}                     The value found
    */
    function getFieldKey(e) {
        var el   = a.dom.el(e),
            name = el.attribute('name');

        // Search the good attribute in case of problem
        if(a.isNone(name) || name === '') {
            name = el.attribute('id');
            // Should never appear... But we provide it in case of trouble
            if(a.isNone(name) || name === '') {
                name = el.attribute('class');
            }
        }

        return name;
    };

    /**
     * Get the field value for given input.
     *
     * @method getFieldValue
     * @private
     *
     * @param e {DOMElement}                The element to search value inside
     * @return {String}                     The value found
    */
    function getFieldValue(e) {
        var type    = e.type || '',
            tagName = e.tagName.toLowerCase();

        if(tagName === 'input' || tagName === 'textarea') {
            return (type === 'checkbox') ? e.checked : e.value;
        } else if(tagName === 'select') {
            if(e.options[e.selectedIndex]) {
                return e.options[e.selectedIndex].value
            }
            return null;
        }
    };

    /**
     * From a given dom, get the list of revelant elements inside.
     *
     * @method getFieldList
     * @private
     *
     * @param dom {a.dom}                   The dom element to search inside
     * @return {Array}                      The element list inside DOM
    */
    function getFieldList(dom) {
        // dom must be a a.dom element
        var elements = dom.tag(['input', 'textarea', 'select']).getElements();

        // We remove input who are not listed in typeList
        // LIKE: submit/reset should not appear in this list
        var i = elements.length;
        while(i--) {
            var el = elements[i];
            if(el.type &&
                    (  el.type == 'submit'
                    || el.type == 'reset'
                    || el.type == 'image'
                    ) ) {
                elements.splice(i, 1);
            }
        }

        // Now filtering is done, we can send back all elements
        return elements;
    };

    /**
     * Raise an error on input.
     *
     * @method validateError
     * @private
     *
     * @param el {DOMElement}               The element where comes from error
     * @param id {String}                   The element id/name/class
     * @param name {String | null}          The name (like min, pattern, ...)
     *                                      which is not valid, can be null
     * @param value {String | null}         The current input value
     *                                      (can be used as parameter)
     * @return {Object}                     A validate object with everything
     *                                      inside if possible
    */
    function validateError(el, id, name, value) {
        // First : we need to get error element and translate if possible
        var error = '';

        // Retrieve error tag
        if(!a.isNone(name) && name !== '') {
            error = el['data-error-' + name] || null;
        }
        if(a.isNone(error) || error === '') {
            error = el['data-error'] || null;
        }

        if(a.isNone(error) || error === '') {
            var errorMessage  = 'A data-error tag has not been setted for ';
                errorMessage += id + ' with value ' + value + 'n can\'t ';
                errorMessage += 'proceed error message';
            a.console.warn(errorMessage, 3);
        }

        // Translate error if possible
        error = a.translate.get(error, {
            name:  name,
            value: value
        });

        // Returning an object with all needed data inside
        return {
            el:    el,
            id:    id,
            error: error
        };
    };

    return {
        /**
         * Allow to skip HTML5 form-novalidate tag or not (boolean)
         *
         * @property skipNoValidate
         * @type Boolean
         * @default false
        */
        skipNoValidate: false,

        /**
         * Get the list of element stored into given form.
         *
         * @method get
         *
         * @param dom {Object}              The dom element to search inside
         *                                  - It has to be a valid a.dom.el
         *                                  input
         * @return {Object}                 The list of input tags existing
        */
        get: function(dom) {
            dom = a.dom.el(dom);
            var inputList  = getFieldList(dom),
                outputList = {};

            var i = inputList.length;
            while(i--) {
                var input = inputList[i];

                var name  = a.trim(getFieldKey(input)),
                    value = getFieldValue(input);

                // We don't continue if we don't find any data on element
                if(a.isNone(name) || !name) {
                    continue;
                }

                var parse = false;

                // We got a special case with input radio type
                if(!a.isNone(input) && input.type === 'radio') {
                    // Only checked one are validated
                    if(input.checked) {
                        parse = true;
                    }
                } else if(!a.isNone(input) && input.type === 'checkbox') {
                    parse = false;
                    outputList[name] = (input.checked) ? true: false;
                } else {
                    parse = true;
                }

                if(parse) {
                    // Name is a multiple value one (using [] at the end)
                    if(name.substr(name.length - 2) === '[]') {
                        if(!a.isArray(outputList[name])) {
                            outputList[name] = [];
                        }
                        value = (value) ? value: null;
                        outputList[name].push(value);
                    } else {
                       outputList[name] = (value) ? value: null;
                   }
                }
            };

            a.console.log('a.form.get: found element list:', 3);
            a.console.log(outputList, 3);
            return outputList;
        },

        /**
         * Validate a form
         * Note : multiple tester (email, file) is not supported
         * Note : date field (date, datetime, datetime-local,
         * month, time, week) are not supported
         * Note : tel/file field are not supported
         *
         * @method validate
         *
         * @param dom {Object}              The dom element to search inside
         *                                  - It has to be a valid a.dom.el
         *                                  input
         * @return {Array}                  An array with all errors listed
         *                                  inside, an empty array if there
         *                                  is no error to show
        */
        validate: function(dom) {
            dom = a.dom.el(dom);
            // On form tag, the "novalidate" allow to not validate a form
            if(this.skipNoValidate === false &&
                    !a.isNone(dom.get(0).novalidate)) {
                return [];
            }

            var inputList    = getFieldList(dom),
                // Store all errors appearing
                errorList    = [],
                // TODO : after todos are ended, remove this...
                allowedTypes = ['number', 'range', 'text', 'search',
                                        'url', 'email', 'password',
                                        'color', 'checkbox',
                                        'hidden', 'radio'],
                // Pretty basic url tester
                urlTester    = new RegExp(
                    '^[a-z]{2,}:\\/\\/([a-z0-9\\/\\.\\-_~+;:&=]{2,})$', 'gi'),
                // Pretty basic email tester
                emailTester  = new RegExp('^.{2,}@.*\\.[a-z0-9]{2,}$', 'gi'),
                colorTester  = new RegExp('^#([a-f]{3}|[a-f]{6})$', 'gi');

            /*
             * required : at least one char
                (text, search, url, tel, email, password, date, datetime,
                datetime-local, month, time, week, number, checkbox,
                radio, file)
             * pattern : a regex to test (Use title like a helper),
                (text, search, url, tel, email, password)
             * multiple : the user is allowed to enter more than one element
                (only for email, file)
             * min/max : min/max value
                (number, range, date, datetime, datetime-local,
                month, time, week)
             * step : multiplier
                (number, range, date, datetime, datetime-local,
                month, time, week)
            */
            var i = inputList.length;
            while(i--) {
                // Does only work for input tags
                var el      = inputList[i],
                    tagName = el.tagName.toLowerCase();

                // form novalidate : we must not validate
                // this element (including all select)
                if(tagName == 'select'
                    || !a.isNone(el.novalidate)) {
                    continue;
                }

                var type     = el.type,
                    name    = getFieldKey(el),
                    value    = el.value,

                    required = el.required,
                    pattern  = el.pattern,
                    multiple = el.multiple,
                    min      = el.min,
                    max      = el.max,
                    step     = el.step;

                // Double check float data
                min  = (a.isNone(min) || min == '')   ? null :
                                                            parseFloat(min);
                max  = (a.isNone(max) || max == '')   ? null :
                                                            parseFloat(max);
                step = (a.isNone(step) || step == '') ? null :
                                                            parseFloat(step);

                // Check input type does existing in allowed type list
                if(tagName == 'input'
                        && !a.contains(allowedTypes, type)
                        && !a.isNone(type)) {
                    var errorSupport =  'Type ' + type + ' for input ' + name;
                        errorSupport += ' not recognized or not supported';
                    a.console.warn(errorSupport, 3);
                    continue;
                }

                // Now checking type
                if( (type == 'number' || type == 'range')
                        && !a.isNumber(value) ) {
                    errorList.push(validateError(el, name, null, value));
                    continue;
                }
                if(type == 'url' && !urlTester.test(value) ) {
                    errorList.push(validateError(el, name, null, value));
                    continue;
                }
                if(type == 'email' && !emailTester.test(value) ) {
                    errorList.push(validateError(el, name, null, value));
                    continue;
                }
                if(type == 'color' && !colorTester.test(value) ) {
                    errorList.push(validateError(el, name, null, value));
                    continue;
                }

                // Required test
                if( required !== null
                    && a.contains(typeRequiredList, type)
                    && (value === '' || a.isNone(value)) ) {
                    errorList.push(validateError(el, name, 'required', value));
                    continue;
                }

                // Pattern test
                if( pattern !== null
                     && (tagName === "textarea"
                        ||(a.contains(typePatternList, type))
                        || a.isNone(type)
                        )
                ) {
                    var reg = new RegExp(pattern);
                    if(!reg.test(value)) {
                        errorList.push(validateError(
                                            el, name, 'pattern', value));
                        continue;
                    }
                }

                // Min/max/step test
                if( (min !== null || max != null || step != null)
                    && a.contains(minMaxStepList, type) ) {

                    var pval = parseFloat(value);
                    if( min !== null && pval < min ) {
                        errorList.push(validateError(el, name, 'min', value));
                        continue;
                    }
                    if( max !== null && pval > max ) {
                        errorList.push(validateError(el, name, 'max', value));
                        continue;
                    }
                    if( step !== null && pval % step !== 0 ) {
                        errorList.push(validateError(el, name, 'step', value));
                        continue;
                    }
                }
            }

            a.console.log('a.form.validate: found error list:', 3);
            a.console.log(errorList, 3);
            return errorList;
        },

        /**
         * Validate and get the form content.
         *
         * @method validateAndGet
         *
         * @param dom {Object}              The dom element to search inside
         *                                  - It has to be a valid a.dom.el
         *                                  input
         * @return {Object}                 An object with error (boolean),
         *                                  errorList (Array)
         *                                  and contentList (Array)
        */
        validateAndGet: function(dom) {
            var obj = {
                errorList   : this.validate(dom),
                error       : false,
                contentList : this.get(dom)
            };
            if(obj.errorList.length > 0) {
                obj.error = true;
            }
            return obj;
        }
    };

})();<|endoftext|>


<commit_before>/**
 * @file MaterialDatePickerTextField component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import TextField from '../TextField';
import Theme from '../Theme';

import Util from '../_vendors/Util';

class DatePickerTextField extends Component {

    static Type = TextField.Type;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: ''
        };

    }

    /**
     * public
     */
    focus = () => {
        this.refs.textField.focus();
    };

    /**
     * public
     */
    blur = () => {
        this.refs.textField.blur();
    };

    triggerChangeHandler = value => {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    };

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value || nextProps.popupVisible !== this.props.popupVisible) {
            this.setState({
                value: nextProps.value,
                isFocus: nextProps.popupVisible
            });
        }
    }

    render() {

        const {
                className, theme, disabled, required, popupVisible,
                ...restProps
            } = this.props,
            {value} = this.state,

            fieldClassName = classNames('date-picker-text-field', {
                activated: popupVisible,
                [className]: className
            });

        return (
            <TextField className={fieldClassName}
                       {...restProps}
                       ref="textField"
                       theme={theme}
                       value={value}
                       disabled={disabled}
                       required={required}
                       onChange={this.triggerChangeHandler}/>
        );

    }
}

DatePickerTextField.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * Specifies the type of input to display such as "password" or "text".
     */
    type: PropTypes.oneOf(Util.enumerateValue(TextField.Type)),

    /**
     * The name of the text field.
     */
    name: PropTypes.string,

    /**
     * The placeholder of the text field.
     */
    placeholder: PropTypes.string,

    /**
     * The value of the text field.Type can be string or number.
     */
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Disables the textField if set to true.
     */
    disabled: PropTypes.bool,

    /**
     * If true,the textField will can't editable.
     */
    readOnly: PropTypes.bool,

    /**
     * If true,the textField will autoFocus.
     */
    autoFocus: PropTypes.bool,

    /**
     * The message of the textField.
     */
    infoMsg: PropTypes.string,

    /**
     * If true,clearButton will display when the textField is not empty.
     */
    clearButtonVisible: PropTypes.bool,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * If true,passwordButton will display.
     */
    passwordButtonVisible: PropTypes.bool,

    // valid
    /**
     * If true,the textField must be required.
     */
    required: PropTypes.bool,

    /**
     * The maximum length of textField to enter.
     */
    maxLength: PropTypes.number,

    /**
     * Maximum number of textField that can be entered.
     */
    max: PropTypes.number,

    /**
     * Minimum number of textField that can be entered.
     */
    min: PropTypes.number,

    /**
     * The matching rule of textField.
     */
    pattern: PropTypes.object,

    /**
     * The invalid message of textField.
     */
    patternInvalidMsg: PropTypes.string,

    /**
     *
     */
    autoComplete: PropTypes.string,

    /**
     *
     */
    autoCorrect: PropTypes.string,

    /**
     *
     */
    autoCapitalize: PropTypes.string,

    /**
     *
     */
    spellCheck: PropTypes.string,

    /**
     * Callback function fired when the textField is changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when the press enter.
     */
    onPressEnter: PropTypes.func,

    /**
     * Callback function fired when textField valid.
     */
    onValid: PropTypes.func,

    /**
     * Callback function fired when textField invalid.
     */
    onInvalid: PropTypes.func,

    /**
     * Callback function fired when textField focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when textField blur.
     */
    onBlur: PropTypes.func,

    /**
     * Callback function fired when textField clear.
     */
    onClear: PropTypes.func,

    /**
     * Callback function fired when password visible.
     */
    onPasswordVisible: PropTypes.func,

    /**
     * Callback function fired when password invisible.
     */
    onPasswordInvisible: PropTypes.func,

    /**
     * the datePicker is open if set to true.
     */
    popupVisible: PropTypes.bool,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

DatePickerTextField.defaultProps = {

    theme: Theme.DEFAULT,

    type: 'text',
    name: '',

    placeholder: '',
    value: '',
    disabled: false,
    readOnly: false,
    autoFocus: false,
    popupVisible: false,

    clearButtonVisible: true,
    passwordButtonVisible: true,

    // valid
    required: false,
    patternInvalidMsg: '',

    autoComplete: 'off',
    autoCorrect: 'off',
    autoCapitalize: 'off',
    spellCheck: 'false',

    fieldMsgVisible: false

};

export default DatePickerTextField;<commit_msg>update DatePickerTextField<commit_after>/**
 * @file MaterialDatePickerTextField component
 * @author sunday(sunday.wei@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import TextField from '../TextField';
import Theme from '../Theme';

import Util from '../_vendors/Util';

class DatePickerTextField extends Component {

    static Type = TextField.Type;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: ''
        };

    }

    /**
     * public
     */
    focus = () => {
        this.refs.textField.focus();
    };

    /**
     * public
     */
    blur = () => {
        this.refs.textField.blur();
    };

    triggerChangeHandler = value => {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    };

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value || nextProps.popupVisible !== this.props.popupVisible) {
            this.setState({
                value: nextProps.value,
                isFocus: nextProps.popupVisible
            });
        }
    }

    render() {

        const {

                className, theme, disabled, required,

                // not passing down these props
                popupVisible,

                ...restProps

            } = this.props,
            {value} = this.state,

            fieldClassName = classNames('date-picker-text-field', {
                [className]: className
            });

        return (
            <TextField className={fieldClassName}
                       {...restProps}
                       ref="textField"
                       theme={theme}
                       value={value}
                       disabled={disabled}
                       required={required}
                       onChange={this.triggerChangeHandler}/>
        );

    }
}

DatePickerTextField.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * Specifies the type of input to display such as "password" or "text".
     */
    type: PropTypes.oneOf(Util.enumerateValue(TextField.Type)),

    /**
     * The name of the text field.
     */
    name: PropTypes.string,

    /**
     * The placeholder of the text field.
     */
    placeholder: PropTypes.string,

    /**
     * The value of the text field.Type can be string or number.
     */
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Disables the textField if set to true.
     */
    disabled: PropTypes.bool,

    /**
     * If true,the textField will can't editable.
     */
    readOnly: PropTypes.bool,

    /**
     * If true,the textField will autoFocus.
     */
    autoFocus: PropTypes.bool,

    /**
     * The message of the textField.
     */
    infoMsg: PropTypes.string,

    /**
     * If true,clearButton will display when the textField is not empty.
     */
    clearButtonVisible: PropTypes.bool,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * If true,passwordButton will display.
     */
    passwordButtonVisible: PropTypes.bool,

    // valid
    /**
     * If true,the textField must be required.
     */
    required: PropTypes.bool,

    /**
     * The maximum length of textField to enter.
     */
    maxLength: PropTypes.number,

    /**
     * Maximum number of textField that can be entered.
     */
    max: PropTypes.number,

    /**
     * Minimum number of textField that can be entered.
     */
    min: PropTypes.number,

    /**
     * The matching rule of textField.
     */
    pattern: PropTypes.object,

    /**
     * The invalid message of textField.
     */
    patternInvalidMsg: PropTypes.string,

    /**
     *
     */
    autoComplete: PropTypes.string,

    /**
     *
     */
    autoCorrect: PropTypes.string,

    /**
     *
     */
    autoCapitalize: PropTypes.string,

    /**
     *
     */
    spellCheck: PropTypes.string,

    /**
     * Callback function fired when the textField is changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when the press enter.
     */
    onPressEnter: PropTypes.func,

    /**
     * Callback function fired when textField valid.
     */
    onValid: PropTypes.func,

    /**
     * Callback function fired when textField invalid.
     */
    onInvalid: PropTypes.func,

    /**
     * Callback function fired when textField focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when textField blur.
     */
    onBlur: PropTypes.func,

    /**
     * Callback function fired when textField clear.
     */
    onClear: PropTypes.func,

    /**
     * Callback function fired when password visible.
     */
    onPasswordVisible: PropTypes.func,

    /**
     * Callback function fired when password invisible.
     */
    onPasswordInvisible: PropTypes.func,

    /**
     * the datePicker is open if set to true.
     */
    popupVisible: PropTypes.bool,

    onMouseOver: PropTypes.func,
    onMouseOut: PropTypes.func

};

DatePickerTextField.defaultProps = {

    theme: Theme.DEFAULT,

    type: 'text',
    name: '',

    placeholder: '',
    value: '',
    disabled: false,
    readOnly: false,
    autoFocus: false,
    popupVisible: false,

    clearButtonVisible: true,
    passwordButtonVisible: true,

    // valid
    required: false,
    patternInvalidMsg: '',

    autoComplete: 'off',
    autoCorrect: 'off',
    autoCapitalize: 'off',
    spellCheck: 'false',

    fieldMsgVisible: false

};

export default DatePickerTextField;<|endoftext|>


<commit_before>const dotenv = require(`dotenv`)
dotenv.config({
  path: `../backend/uclapi/.env`,
})

const webpack = require(`webpack`)
const path = require(`path`)
const TerserPlugin = require(`terser-webpack-plugin`)
const OptimizeCSSAssetsPlugin = require(`optimize-css-assets-webpack-plugin`)
const BundleTracker = require(`webpack-bundle-tracker`)

const os = require(`os`)

const entryPointsPathPrefix = `./src/pages`

const {
  AWS_S3_STATICS,
  AWS_S3_BUCKET_NAME,
  AWS_S3_BUCKET_PATH,
} = process.env

const publicPath = AWS_S3_STATICS === `True`
  ? `https://${AWS_S3_BUCKET_NAME}.s3.amazonaws.com/${AWS_S3_BUCKET_PATH}`
  : `/static/`

module.exports = {
  mode: `production`,
  optimization: {
    minimize: true,
    minimizer: [],  // This list is built below as per platform requirements
    splitChunks: {
      name: false,
      cacheGroups: {
        vendors: {
          chunks: `all`,
          name: `vendors`,
          test: /[\\/]node_modules[\\/]/,
        },
      },
    },
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(`production`),
      'process.env.SENTRY_DSN_REACT': JSON.stringify(process.env.SENTRY_DSN_REACT),
    }),
    new BundleTracker({
      filename: `../backend/uclapi/static/webpack-stats.json`,
    }),
    new webpack.HashedModuleIdsPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: `babel-loader`,
        },
      },
      {
        test: /\.scss|css$/,
        use: [
          { loader: `style-loader` },
          { loader: `css-loader` },
          { loader: `sass-loader` },
        ],
      },
      {
        test: /\.(jpg|png|svg|jpeg)$/,
        loader: `url-loader`,
      },
    ],
  },
  resolve: {
    alias: {
      'Images': path.resolve(__dirname, `./src/images`),
      'Layout': path.resolve(__dirname, `./src/components/layout`),
      'Dashboard': path.resolve(__dirname, `./src/components/dashboard`),
      'Styles': path.resolve(__dirname, `./src/sass`),
    },
    extensions: [`.js`, `.json`, `.jsx`],
  },
  entry: {
    index: entryPointsPathPrefix + `/Home`,
    documentation: entryPointsPathPrefix + `/Documentation.jsx`,
    about: entryPointsPathPrefix + `/AboutPage.jsx`,
    dashboard: entryPointsPathPrefix + `/Dashboard`,
    marketplace: entryPointsPathPrefix + `/Marketplace`,
    authorise: entryPointsPathPrefix + `/Authorise`,
    warning: entryPointsPathPrefix + `/Warning.jsx`,
    404: entryPointsPathPrefix + `/404.jsx`,
    500: entryPointsPathPrefix + `/500.jsx`,
    settings: entryPointsPathPrefix + `/AppSettings.jsx`,
    vendors: [`react`],
  },
  output: {
    path: path.resolve(__dirname, `../backend/uclapi/static/`),
    publicPath,
    filename: `[name]-[contenthash].js`,
  },
}

// Do not enable parallelisation for Windows Subsystem for Linux
// https://github.com/webpack-contrib/uglifyjs-webpack-plugin/issues/302
// https://stackoverflow.com/a/44356310/5297057
if (os.platform() == `linux` && os.release().indexOf(`Microsoft`) != -1) {
  module.exports.optimization.minimizer.push(
    new TerserPlugin({
      cache: true,
      sourceMap: true,
    })
  )
} else {
  module.exports.optimization.minimizer.push(
    new TerserPlugin({
      cache: true,
      sourceMap: true,
      parallel: true,
    })
  )
}
module.exports.optimization.minimizer.push(
  new OptimizeCSSAssetsPlugin({})
)
<commit_msg>Update webpack.prod.js (#3178)<commit_after>const dotenv = require(`dotenv`)
dotenv.config({
  path: `../backend/uclapi/.env`,
})

const webpack = require(`webpack`)
const path = require(`path`)
const TerserPlugin = require(`terser-webpack-plugin`)
const OptimizeCSSAssetsPlugin = require(`optimize-css-assets-webpack-plugin`)
const BundleTracker = require(`webpack-bundle-tracker`)

const os = require(`os`)

const entryPointsPathPrefix = `./src/pages`

const {
  AWS_S3_STATICS,
  AWS_S3_BUCKET_NAME,
  AWS_S3_BUCKET_PATH,
} = process.env

const publicPath = AWS_S3_STATICS === `true`
  ? `https://${AWS_S3_BUCKET_NAME}.s3.amazonaws.com/${AWS_S3_BUCKET_PATH}`
  : `/static/`

module.exports = {
  mode: `production`,
  optimization: {
    minimize: true,
    minimizer: [],  // This list is built below as per platform requirements
    splitChunks: {
      name: false,
      cacheGroups: {
        vendors: {
          chunks: `all`,
          name: `vendors`,
          test: /[\\/]node_modules[\\/]/,
        },
      },
    },
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(`production`),
      'process.env.SENTRY_DSN_REACT': JSON.stringify(process.env.SENTRY_DSN_REACT),
    }),
    new BundleTracker({
      filename: `../backend/uclapi/static/webpack-stats.json`,
    }),
    new webpack.HashedModuleIdsPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: `babel-loader`,
        },
      },
      {
        test: /\.scss|css$/,
        use: [
          { loader: `style-loader` },
          { loader: `css-loader` },
          { loader: `sass-loader` },
        ],
      },
      {
        test: /\.(jpg|png|svg|jpeg)$/,
        loader: `url-loader`,
      },
    ],
  },
  resolve: {
    alias: {
      'Images': path.resolve(__dirname, `./src/images`),
      'Layout': path.resolve(__dirname, `./src/components/layout`),
      'Dashboard': path.resolve(__dirname, `./src/components/dashboard`),
      'Styles': path.resolve(__dirname, `./src/sass`),
    },
    extensions: [`.js`, `.json`, `.jsx`],
  },
  entry: {
    index: entryPointsPathPrefix + `/Home`,
    documentation: entryPointsPathPrefix + `/Documentation.jsx`,
    about: entryPointsPathPrefix + `/AboutPage.jsx`,
    dashboard: entryPointsPathPrefix + `/Dashboard`,
    marketplace: entryPointsPathPrefix + `/Marketplace`,
    authorise: entryPointsPathPrefix + `/Authorise`,
    warning: entryPointsPathPrefix + `/Warning.jsx`,
    404: entryPointsPathPrefix + `/404.jsx`,
    500: entryPointsPathPrefix + `/500.jsx`,
    settings: entryPointsPathPrefix + `/AppSettings.jsx`,
    vendors: [`react`],
  },
  output: {
    path: path.resolve(__dirname, `../backend/uclapi/static/`),
    publicPath,
    filename: `[name]-[contenthash].js`,
  },
}

// Do not enable parallelisation for Windows Subsystem for Linux
// https://github.com/webpack-contrib/uglifyjs-webpack-plugin/issues/302
// https://stackoverflow.com/a/44356310/5297057
if (os.platform() == `linux` && os.release().indexOf(`Microsoft`) != -1) {
  module.exports.optimization.minimizer.push(
    new TerserPlugin({
      cache: true,
      sourceMap: true,
    })
  )
} else {
  module.exports.optimization.minimizer.push(
    new TerserPlugin({
      cache: true,
      sourceMap: true,
      parallel: true,
    })
  )
}
module.exports.optimization.minimizer.push(
  new OptimizeCSSAssetsPlugin({})
)
<|endoftext|>