mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
617bffa95e
* test(deploy): skip segment cache memory pressure suite * test(deploy): skip instant navigation suite
188 lines
5.8 KiB
JavaScript
188 lines
5.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Generates a filtered deploy-tests manifest from the Next.js repo.
|
|
*
|
|
* Usage:
|
|
* node scripts/nextjs-deploy-manifest.mjs <nextjs-dir> <output-json-path> \
|
|
* [--filter pages|app|all] [--match <path>]...
|
|
*
|
|
* Filters:
|
|
* pages — exclude test/e2e/app-dir/** (Pages Router only)
|
|
* app — include only test/e2e/app-dir/** (App Router only)
|
|
* all — pass through the full manifest unfiltered (default)
|
|
*
|
|
* --match narrows further to specific suites by path prefix. Repeatable, or
|
|
* comma-separated. A directory prefix like `test/e2e/edge-async-local-storage`
|
|
* keeps every suite under it; a full test file path keeps just that file.
|
|
* Applied AFTER --filter, so `--filter app --match test/e2e/app-dir/foo`
|
|
* works as expected.
|
|
*/
|
|
|
|
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
|
|
function printUsage() {
|
|
console.error(
|
|
"Usage: node scripts/nextjs-deploy-manifest.mjs <nextjs-dir> <output-json-path> [--filter pages|app|all] [--match <path>]...",
|
|
);
|
|
}
|
|
|
|
const TEST_FILE_RE = /\.test\.(t|j)sx?$/;
|
|
|
|
function matchToInclude(match) {
|
|
// Already a test file path: include verbatim.
|
|
if (TEST_FILE_RE.test(match)) return match;
|
|
// Directory prefix: glob every test file underneath.
|
|
const trimmed = match.replace(/\/+$/, "");
|
|
return `${trimmed}/**/*.test.{t,j}s{,x}`;
|
|
}
|
|
|
|
function suiteMatchesAny(suite, matches) {
|
|
for (const m of matches) {
|
|
const trimmed = m.replace(/\/+$/, "");
|
|
if (suite === trimmed) return true;
|
|
if (suite.startsWith(`${trimmed}/`)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isAppDirSuite(suite) {
|
|
return suite.startsWith("test/e2e/app-dir/");
|
|
}
|
|
|
|
/**
|
|
* Suites that live outside test/e2e/app-dir/ but exercise App Router
|
|
* behavior. Excluded from the "pages" filter so they don't gate Pages
|
|
* Router-only runs.
|
|
*/
|
|
const APP_ROUTER_NON_APP_DIR_SUITES = ["test/e2e/next-form/default/next-form-prefetch.test.ts"];
|
|
|
|
const CUSTOM_DEPLOY_ADAPTER_EXCLUDES = [
|
|
"test/e2e/app-dir/instant-navigation-testing-api/instant-navigation-testing-api.test.ts",
|
|
"test/e2e/app-dir/segment-cache/memory-pressure/segment-cache-memory-pressure.test.ts",
|
|
];
|
|
|
|
const CUSTOM_DEPLOY_ADAPTER_SUITE_OVERRIDES = {
|
|
// Custom deploy adapters snapshot cliOutput during setup, before this test emits runtime logs.
|
|
"test/e2e/repeated-forward-slashes-error/repeated-forward-slashes-error.test.ts": {
|
|
failed: [
|
|
"repeated-forward-slashes-error should log error when href has repeated forward-slashes",
|
|
],
|
|
},
|
|
};
|
|
|
|
async function main() {
|
|
const positionals = [];
|
|
let filter = "all";
|
|
const matches = [];
|
|
|
|
// Simple arg parsing: positionals + optional --filter / --match
|
|
for (let i = 2; i < process.argv.length; i++) {
|
|
const arg = process.argv[i];
|
|
if (arg === "--filter" && i + 1 < process.argv.length) {
|
|
filter = process.argv[++i];
|
|
} else if (arg === "--match" && i + 1 < process.argv.length) {
|
|
for (const part of process.argv[++i].split(",")) {
|
|
const trimmed = part.trim();
|
|
if (trimmed) matches.push(trimmed);
|
|
}
|
|
} else if (!arg.startsWith("-")) {
|
|
positionals.push(arg);
|
|
}
|
|
}
|
|
|
|
const [nextjsDirArg, outputPathArg] = positionals;
|
|
|
|
if (!nextjsDirArg || !outputPathArg) {
|
|
printUsage();
|
|
process.exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
if (!["pages", "app", "all"].includes(filter)) {
|
|
console.error(`Invalid --filter value: ${filter}. Must be pages, app, or all.`);
|
|
process.exitCode = 1;
|
|
return;
|
|
}
|
|
|
|
const nextjsDir = path.resolve(nextjsDirArg);
|
|
const outputPath = path.resolve(outputPathArg);
|
|
const sourcePath = path.join(nextjsDir, "test", "deploy-tests-manifest.json");
|
|
const source = JSON.parse(await fs.readFile(sourcePath, "utf8"));
|
|
|
|
if (source.version !== 2) {
|
|
throw new Error(`Expected Next.js deploy manifest version 2, got ${source.version}`);
|
|
}
|
|
|
|
let suites = { ...source.suites };
|
|
for (const [suite, override] of Object.entries(CUSTOM_DEPLOY_ADAPTER_SUITE_OVERRIDES)) {
|
|
const current = suites[suite] ?? {};
|
|
suites[suite] = {
|
|
...current,
|
|
failed: Array.from(new Set([...(current.failed ?? []), ...(override.failed ?? [])])),
|
|
};
|
|
}
|
|
let extraExcludes = [...CUSTOM_DEPLOY_ADAPTER_EXCLUDES];
|
|
|
|
if (filter === "pages") {
|
|
suites = Object.fromEntries(Object.entries(suites).filter(([suite]) => !isAppDirSuite(suite)));
|
|
extraExcludes.push("test/e2e/app-dir/**/*", ...APP_ROUTER_NON_APP_DIR_SUITES);
|
|
} else if (filter === "app") {
|
|
suites = Object.fromEntries(Object.entries(suites).filter(([suite]) => isAppDirSuite(suite)));
|
|
}
|
|
|
|
let include = source.rules?.include?.length
|
|
? source.rules.include
|
|
: ["test/e2e/**/*.test.{t,j}s{,x}"];
|
|
|
|
if (matches.length > 0) {
|
|
suites = Object.fromEntries(
|
|
Object.entries(suites).filter(([suite]) => suiteMatchesAny(suite, matches)),
|
|
);
|
|
// Narrow include to just the matched paths so the runner doesn't scan
|
|
// the rest of test/e2e/. Excludes from --filter still apply.
|
|
include = matches.map(matchToInclude);
|
|
}
|
|
|
|
const exclude = Array.from(new Set([...(source.rules?.exclude ?? []), ...extraExcludes]));
|
|
|
|
const manifest = {
|
|
version: 2,
|
|
suites,
|
|
rules: {
|
|
include,
|
|
exclude,
|
|
},
|
|
};
|
|
|
|
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
await fs.writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
|
|
const totalSuites = Object.keys(source.suites ?? {}).length;
|
|
const appDirSuites = Object.keys(source.suites ?? {}).filter(isAppDirSuite).length;
|
|
const outputSuites = Object.keys(suites).length;
|
|
|
|
console.log(`Wrote ${outputPath}`);
|
|
console.log(
|
|
JSON.stringify(
|
|
{
|
|
source: sourcePath,
|
|
filter,
|
|
matches,
|
|
totalSuites,
|
|
outputSuites,
|
|
appDirSuites,
|
|
nonAppDirSuites: totalSuites - appDirSuites,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error?.stack || error);
|
|
process.exitCode = 1;
|
|
});
|