/
githubmirror
/
next.js
Обзор
Документация
Войти
/
githubmirror
/
next.js
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
canary
scripts/get-changed-tests.mjs
223 строки
6 KB
Tim Neutkens
Convert test/integration to isolated tests (#93247)
11 май 2026, 14:55
Не верифицирован
11 май 2026, 14:55
8141dcf
Код
Авторство
О чём код?
// @ts-check import fs from 'fs/promises' import execa from 'execa' import { createRequire } from 'module' import path from 'path' import { getDiffRevision, getGitInfo } from './git-info.mjs' const require = createRequire(import.meta.url) const glob = require('glob') const { getTestFilterFromManifest, mergeManifests, } = require('../test/get-test-filter') const DEFAULT_DEPLOY_TESTS_MANIFEST_PATH = 'test/deploy-tests-manifest.json' const TEST_FILE_REGEX = /^test\/.*?\.test\.(js|ts|tsx)$/ function normalizePath(file) { return file.replace(/\\/g, '/') } function getExcludedCases(suite = {}) { return new Set([...(suite.failed ?? []), ...(suite.flakey ?? [])]) } function getExternalTestsFilterPaths() { const externalTestsFilters = process.env.NEXT_EXTERNAL_TESTS_FILTERS ?? DEFAULT_DEPLOY_TESTS_MANIFEST_PATH const manifestPaths = new Map() for (const manifestPath of externalTestsFilters.split(',')) { if (!manifestPath.trim()) { continue } const absolutePath = path.resolve(process.cwd(), manifestPath) const repoRelativePath = normalizePath( path.relative(process.cwd(), absolutePath) ) manifestPaths.set(repoRelativePath, { absolutePath, repoRelativePath, }) } return [...manifestPaths.values()] } function getDeployTestsFromManifest(manifest) { const testFilter = getTestFilterFromManifest(manifest) if (!testFilter) { return [] } const tests = glob .sync('test/e2e/**/*.test.{js,ts,tsx}', { cwd: process.cwd(), ignore: '**/node_modules/**', nodir: true, }) .map((file) => ({ file: normalizePath(file), excludedCases: [] })) return testFilter(tests).map((test) => test.file) } export function getDeployManifestChangedTests( currentManifest, previousManifest ) { if (!currentManifest) { return [] } const previousVersion2Manifest = previousManifest ?? { version: 2, suites: {}, rules: { include: [], exclude: [] }, } const previousIncludedTests = new Set( getDeployTestsFromManifest(previousVersion2Manifest) ) const changedTests = new Set() for (const file of getDeployTestsFromManifest(currentManifest)) { const currentExcludedCases = getExcludedCases( currentManifest.suites?.[file] ) const previousExcludedCases = getExcludedCases( previousVersion2Manifest.suites?.[file] ) if (!previousIncludedTests.has(file)) { changedTests.add(file) continue } for (const testCase of previousExcludedCases) { if (!currentExcludedCases.has(testCase)) { changedTests.add(file) break } } } return [...changedTests] } /** * Detects changed tests files by comparing the current branch with `origin/canary` * Returns tests separated by test mode (dev/prod), as well as the corresponding commit hash * that the current branch is pointing to */ export default async function getChangedTests() { /** @type import('execa').Options */ const EXECA_OPTS = { shell: true } const { branchName, remoteUrl, commitSha, isCanary } = await getGitInfo() if (isCanary) { console.log(`Skipping flake detection for canary`) return { devTests: [], prodTests: [], deployTests: [], commitSha } } const diffRevision = await getDiffRevision() const changesResult = await execa( `git diff ${diffRevision} --name-only`, EXECA_OPTS ).catch((err) => { console.error(err) return { stdout: '', stderr: '' } }) console.log( { branchName, remoteUrl, isCanary, commitSha, }, `\ngit diff:\n${changesResult.stderr}\n${changesResult.stdout}` ) const changedFiles = changesResult.stdout.split('\n') // run each test 3 times in each test mode (if E2E) with no-retrying // and if any fail it's flakey const devTests = new Set() const prodTests = new Set() const deployTests = new Set() for (let file of changedFiles) { // normalize slashes file = normalizePath(file) const fileExists = await fs .access(path.join(process.cwd(), file), fs.constants.F_OK) .then(() => true) .catch(() => false) if (fileExists && file.match(TEST_FILE_REGEX)) { if (file.startsWith('test/e2e/')) { devTests.add(file) prodTests.add(file) deployTests.add(file) } else if (file.startsWith('test/production')) { prodTests.add(file) } else if (file.startsWith('test/development')) { devTests.add(file) } } } const externalTestsFilterPaths = getExternalTestsFilterPaths() if ( externalTestsFilterPaths.some(({ repoRelativePath }) => changedFiles.includes(repoRelativePath) ) ) { const currentManifest = mergeManifests( await Promise.all( externalTestsFilterPaths.map(async ({ absolutePath }) => JSON.parse(await fs.readFile(absolutePath, 'utf8')) ) ) ) const previousManifest = mergeManifests( ( await Promise.all( externalTestsFilterPaths.map(async ({ repoRelativePath }) => { const previousManifestOutput = await execa( `git show ${diffRevision}:${repoRelativePath}`, EXECA_OPTS ).catch(() => null) return previousManifestOutput?.stdout ? JSON.parse(previousManifestOutput.stdout) : null }) ) ).filter(Boolean) ) for (const file of getDeployManifestChangedTests( currentManifest, previousManifest )) { deployTests.add(file) } } const detectedTests = { devTests: [...devTests], prodTests: [...prodTests], deployTests: [...deployTests], } console.log('Detected tests:', JSON.stringify(detectedTests, null, 2)) return { ...detectedTests, commitSha } }