@gnuechtel/nx-cucumber 2.0.0-beta.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -51
- package/migrations.json +10 -0
- package/package.json +6 -9
- package/src/adapters/registry.js +6 -1
- package/src/adapters/registry.js.map +1 -1
- package/src/core/cucumber-run-metadata-formatter.js +111 -0
- package/src/generators/project/files/all/cucumber.js.template +17 -5
- package/src/generators/project/files/playwright/run-e2e.js.template +112 -0
- package/src/generators/project/project.js +72 -29
- package/src/generators/project/project.js.map +1 -1
- package/src/index.d.ts +1 -4
- package/src/index.js +3 -6
- package/src/index.js.map +1 -1
- package/src/migrations/update-2-0-0/migrate-cucumber-api-to-inferred-tasks.d.ts +20 -0
- package/src/migrations/update-2-0-0/migrate-cucumber-api-to-inferred-tasks.js +222 -0
- package/src/migrations/update-2-0-0/migrate-cucumber-api-to-inferred-tasks.js.map +1 -0
- package/src/plugin/create-nodes.d.ts +14 -0
- package/src/plugin/create-nodes.js +4 -0
- package/src/plugin/create-nodes.js.map +1 -1
- package/src/core/cucumber-report-html.d.ts +0 -11
- package/src/core/cucumber-report-html.js +0 -58
- package/src/core/cucumber-report-html.js.map +0 -1
- package/src/core/web-server.d.ts +0 -29
- package/src/core/web-server.js +0 -139
- package/src/core/web-server.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate-cucumber-api-to-inferred-tasks.js","sourceRoot":"","sources":["../../../../../../libs/nx-cucumber/src/migrations/update-2-0-0/migrate-cucumber-api-to-inferred-tasks.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n logger,\n NxJsonConfiguration,\n ProjectConfiguration,\n readNxJson,\n Tree,\n updateNxJson,\n updateProjectConfiguration,\n getProjects,\n} from '@nx/devkit';\n\nconst LEGACY_EXECUTOR = '@gnuechtel/nx-cucumber:cucumber-api';\nconst REPORT_PATH = 'tmp/nx-cucumber-migration-report.json';\nconst PLUGIN_NAME = '@gnuechtel/nx-cucumber';\n\ninterface LegacyOptions {\n testRunner?: 'core' | 'playwright';\n featuresDirectory?: string;\n tags?: string;\n cucumberTimeout?: number;\n tsConfig?: string;\n baseUrl?: string;\n browser?: string;\n headed?: boolean;\n debug?: boolean;\n disableAnimations?: boolean;\n disableCoverage?: boolean;\n disableScreenshotAttachments?: boolean;\n disableScreenshotFiles?: boolean;\n enableSuccessfulScreenshotFiles?: boolean;\n disableVideoAttachments?: boolean;\n disableVideoFiles?: boolean;\n disableJavaScript?: boolean;\n additionalPlaywrightConfiguration?: Record<string, unknown>;\n viewport?: Record<string, unknown>;\n videoSize?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\ninterface MigratedProjectReport {\n project: string;\n projectRoot: string;\n removedTargets: string[];\n createdCucumberConfig: boolean;\n manualFollowUps: string[];\n}\n\ninterface MigrationReport {\n dryRun: boolean;\n pluginAdded: boolean;\n removedLegacyTargetDefaults: boolean;\n migratedProjects: MigratedProjectReport[];\n}\n\ninterface MigrationOptions {\n dryRun?: boolean;\n}\n\nfunction isDryRunRequested(): boolean {\n const arg = process.argv.some((v) => v === '--dry-run' || v === '--dryRun');\n const env = `${process.env['NX_CUCUMBER_MIGRATION_DRY_RUN'] || ''}`.toLowerCase();\n return arg || env === '1' || env === 'true' || env === 'yes';\n}\n\nfunction normalizePath(p: string): string {\n return p.replace(/\\\\/g, '/');\n}\n\nfunction resolveTsNodeProject(projectRoot: string, tsConfig?: string): string {\n if (!tsConfig) return `${projectRoot}/tsconfig.json`;\n if (tsConfig.includes('/')) return normalizePath(tsConfig);\n return `${projectRoot}/${tsConfig}`;\n}\n\nfunction hasNxCucumberPlugin(nxJson: NxJsonConfiguration | null): boolean {\n if (!nxJson) return false;\n return (\n nxJson.plugins?.some((plugin) =>\n typeof plugin === 'string' ? plugin === PLUGIN_NAME : plugin?.plugin === PLUGIN_NAME\n ) ?? false\n );\n}\n\nfunction getLegacyTargetNames(project: ProjectConfiguration): string[] {\n return Object.entries(project.targets ?? {})\n .filter(([, target]) => target.executor === LEGACY_EXECUTOR)\n .map(([targetName]) => targetName);\n}\n\nfunction pickLegacyOptions(project: ProjectConfiguration): LegacyOptions {\n const targets = project.targets ?? {};\n const legacyTarget =\n targets['e2e-cucumber']?.executor === LEGACY_EXECUTOR\n ? targets['e2e-cucumber']\n : Object.values(targets).find((target) => target.executor === LEGACY_EXECUTOR);\n return (legacyTarget?.options ?? {}) as LegacyOptions;\n}\n\nfunction extractWorldParameters(options: LegacyOptions): Record<string, unknown> {\n const worldParameters: Record<string, unknown> = {};\n const allowedKeys = [\n 'baseUrl',\n 'browser',\n 'headed',\n 'debug',\n 'disableAnimations',\n 'disableCoverage',\n 'disableScreenshotAttachments',\n 'disableScreenshotFiles',\n 'enableSuccessfulScreenshotFiles',\n 'disableVideoAttachments',\n 'disableVideoFiles',\n 'disableJavaScript',\n 'additionalPlaywrightConfiguration',\n 'viewport',\n 'videoSize',\n ];\n\n for (const key of allowedKeys) {\n if (options[key] !== undefined) {\n worldParameters[key] = options[key];\n }\n }\n return worldParameters;\n}\n\nfunction createWorldParametersLiteral(worldParameters: Record<string, unknown>): string {\n const lines = ['{', ' outputDirectory: outputDir,'];\n for (const [key, value] of Object.entries(worldParameters)) {\n lines.push(` ${key}: ${JSON.stringify(value)},`);\n }\n lines.push('}');\n return lines.join('\\n');\n}\n\nfunction createCucumberConfigContent(projectRoot: string, options: LegacyOptions): string {\n const outputDir = `dist/e2e/${projectRoot}`;\n const featuresDirectory = normalizePath(options.featuresDirectory || `${projectRoot}/features`);\n const testRunner = options.testRunner === 'playwright' ? 'playwright' : 'core';\n const tsNodeProject = resolveTsNodeProject(\n projectRoot,\n typeof options.tsConfig === 'string' ? options.tsConfig : undefined\n );\n\n const requireEntries: string[] = [`'${projectRoot}/src/**/*.ts'`];\n if (testRunner === 'playwright') {\n requireEntries.push(`'node_modules/@gnuechtel/nx-cucumber/src/playwright/playwright-world-default.js'`);\n requireEntries.push(`'node_modules/@gnuechtel/nx-cucumber/src/playwright/playwright-hooks.js'`);\n }\n\n const worldParametersLiteral = createWorldParametersLiteral(extractWorldParameters(options));\n\n const lines: string[] = [\n `process.env.TS_NODE_PROJECT = '${tsNodeProject}';`,\n `const outputDir = 'dist/e2e/${projectRoot}';`,\n `const { pathToFileURL } = require('url');`,\n `const runMetadataFormatter = pathToFileURL(require.resolve('@gnuechtel/nx-cucumber/src/core/cucumber-run-metadata-formatter.js')).href;`,\n `const worldParameters = ${worldParametersLiteral};`,\n 'module.exports = {',\n ' default: {',\n ` paths: ['${featuresDirectory}/**/*.feature'],`,\n ` requireModule: ['ts-node/register', 'tsconfig-paths/register'],`,\n ` require: [${requireEntries.join(', ')}],`,\n ` tags: '${typeof options.tags === 'string' ? options.tags : 'not @ignore'}',`,\n ' format: [',\n ` 'summary',`,\n ` 'progress-bar',`,\n ` \\`json:\\${outputDir}/reports/cucumber-json/cucumber-test-report.json\\`,`,\n ` \\`message:\\${outputDir}/reports/cucumber-messages/cucumber-messages.ndjson\\`,`,\n ` \\`html:\\${outputDir}/reports/html/cucumber-test-report-builtin.html\\`,`,\n ` [runMetadataFormatter, \\`\\${outputDir}/reports/metadata/cucumber-run-metadata.json\\`],`,\n ` \\`junit:\\${outputDir}/reports/test-results/cucumber-test-report.xml\\`,`,\n ' ],',\n ' formatOptions: {',\n ' nxCucumberRunMetadata: {',\n ' worldParameters,',\n ' },',\n ' },',\n ' worldParameters,',\n ...(typeof options.cucumberTimeout === 'number' ? [` timeout: ${options.cucumberTimeout},`] : []),\n ' publishQuiet: true,',\n ' },',\n '};',\n '',\n ];\n return lines.join('\\n');\n}\n\nfunction shouldFlagWatchTarget(targets: ProjectConfiguration['targets']): boolean {\n const watchCommand = targets?.['e2e-watch']?.options?.['commands'];\n if (!Array.isArray(watchCommand)) return false;\n return watchCommand.some((cmd) => typeof cmd === 'string' && cmd.includes('watch-message'));\n}\n\nfunction shouldRemoveLegacyE2eRunCommand(targets: ProjectConfiguration['targets']): boolean {\n const e2e = targets?.['e2e'];\n if (!e2e || e2e.executor !== 'nx:run-commands') return false;\n const command = e2e.options?.['command'];\n return typeof command === 'string' && command.includes('e2e-cucumber');\n}\n\nexport async function migrateCucumberApiToInferredTasks(\n tree: Tree,\n options: MigrationOptions = {}\n): Promise<MigrationReport> {\n const dryRun = options.dryRun ?? isDryRunRequested();\n const nxJson = (readNxJson(tree) ?? {}) as NxJsonConfiguration;\n const report: MigrationReport = {\n dryRun,\n pluginAdded: false,\n removedLegacyTargetDefaults: false,\n migratedProjects: [],\n };\n\n const hadPlugin = hasNxCucumberPlugin(nxJson);\n if (!hadPlugin) {\n report.pluginAdded = true;\n if (!dryRun) {\n nxJson.plugins ??= [];\n nxJson.plugins.push(PLUGIN_NAME);\n updateNxJson(tree, nxJson);\n }\n }\n\n if (nxJson.targetDefaults?.[LEGACY_EXECUTOR]) {\n report.removedLegacyTargetDefaults = true;\n if (!dryRun) {\n delete nxJson.targetDefaults[LEGACY_EXECUTOR];\n updateNxJson(tree, nxJson);\n }\n }\n\n for (const [projectName, projectConfig] of getProjects(tree).entries()) {\n const legacyTargets = getLegacyTargetNames(projectConfig);\n const removeLegacyRunCommandTarget = shouldRemoveLegacyE2eRunCommand(projectConfig.targets);\n const removeLegacyWatchTarget = shouldFlagWatchTarget(projectConfig.targets);\n if (legacyTargets.length === 0 && !removeLegacyRunCommandTarget && !removeLegacyWatchTarget) continue;\n\n const manualFollowUps: string[] = [];\n const removedTargets = [...legacyTargets];\n if (removeLegacyRunCommandTarget && !removedTargets.includes('e2e')) {\n removedTargets.push('e2e');\n }\n if (removeLegacyWatchTarget && !removedTargets.includes('e2e-watch')) {\n removedTargets.push('e2e-watch');\n }\n\n const cucumberConfigPath = `${projectConfig.root}/cucumber.js`;\n const hasCucumberConfig = tree.exists(cucumberConfigPath);\n const legacyOptions = pickLegacyOptions(projectConfig);\n\n if (hasCucumberConfig) {\n manualFollowUps.push(\n `Existing ${cucumberConfigPath} was kept. Verify tags/paths/worldParameters still reflect prior executor options.`\n );\n }\n\n const updatedProject: ProjectConfiguration = {\n ...projectConfig,\n targets: {\n ...(projectConfig.targets ?? {}),\n },\n };\n\n for (const targetName of removedTargets) {\n delete updatedProject.targets?.[targetName];\n }\n\n if (!dryRun) {\n updateProjectConfiguration(tree, projectName, updatedProject);\n if (!hasCucumberConfig) {\n tree.write(cucumberConfigPath, createCucumberConfigContent(projectConfig.root, legacyOptions));\n }\n }\n\n report.migratedProjects.push({\n project: projectName,\n projectRoot: projectConfig.root,\n removedTargets,\n createdCucumberConfig: !hasCucumberConfig,\n manualFollowUps,\n });\n }\n\n if (!dryRun) {\n tree.write(REPORT_PATH, JSON.stringify(report, null, 2));\n }\n\n logger.info(\n `[nx-cucumber migration] migrated projects: ${report.migratedProjects.length}, pluginAdded: ${report.pluginAdded}, dryRun: ${report.dryRun}`\n );\n if (report.migratedProjects.length > 0) {\n for (const project of report.migratedProjects) {\n logger.info(\n `[nx-cucumber migration] ${project.project}: removed targets [${project.removedTargets.join(\n ', '\n )}], created cucumber.js: ${project.createdCucumberConfig}`\n );\n for (const followUp of project.manualFollowUps) {\n logger.warn(`[nx-cucumber migration] ${project.project}: ${followUp}`);\n }\n }\n }\n if (!dryRun) {\n logger.info(`[nx-cucumber migration] report written to ${REPORT_PATH}`);\n }\n\n return report;\n}\n\nexport default async function migrateCucumberApiToInferredTasksEntry(tree: Tree): Promise<void> {\n await migrateCucumberApiToInferredTasks(tree);\n}\n"]}
|
|
@@ -8,6 +8,7 @@ export interface NxCucumberPluginOptions {
|
|
|
8
8
|
/** Override the parallel target name (default: 'e2e-parallel') */
|
|
9
9
|
e2eParallelTargetName?: string;
|
|
10
10
|
}
|
|
11
|
+
export declare function readTargetsCache(cachePath: string): Record<string, TargetsResult>;
|
|
11
12
|
interface TargetsResult {
|
|
12
13
|
targets: Record<string, TargetConfiguration>;
|
|
13
14
|
metadata: ProjectConfiguration['metadata'];
|
|
@@ -16,5 +17,18 @@ interface TargetsResult {
|
|
|
16
17
|
* Builds Nx targets from an adapter definition for a specific project.
|
|
17
18
|
*/
|
|
18
19
|
export declare function buildTargetsForAdapter(adapter: AdapterDefinition, projectRoot: string, configFilePath: string, namedInputs: Record<string, unknown[]>, options?: NxCucumberPluginOptions): TargetsResult;
|
|
20
|
+
/**
|
|
21
|
+
* Checks whether the given directory is a valid Nx project
|
|
22
|
+
* (contains project.json or package.json).
|
|
23
|
+
*/
|
|
24
|
+
export declare function isProjectRoot(workspaceRoot: string, projectRoot: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Reads user-defined targets from project.json to avoid conflicts.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getUserDefinedTargets(workspaceRoot: string, projectRoot: string): Set<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Removes targets that conflict with user-defined targets in project.json.
|
|
31
|
+
*/
|
|
32
|
+
export declare function filterConflictingTargets(result: TargetsResult, userTargets: Set<string>): TargetsResult;
|
|
19
33
|
export declare const createNodesV2: CreateNodesV2<NxCucumberPluginOptions>;
|
|
20
34
|
export {};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createNodesV2 = void 0;
|
|
4
|
+
exports.readTargetsCache = readTargetsCache;
|
|
4
5
|
exports.buildTargetsForAdapter = buildTargetsForAdapter;
|
|
6
|
+
exports.isProjectRoot = isProjectRoot;
|
|
7
|
+
exports.getUserDefinedTargets = getUserDefinedTargets;
|
|
8
|
+
exports.filterConflictingTargets = filterConflictingTargets;
|
|
5
9
|
const devkit_1 = require("@nx/devkit");
|
|
6
10
|
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
|
|
7
11
|
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-nodes.js","sourceRoot":"","sources":["../../../../../libs/nx-cucumber/src/plugin/create-nodes.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CreateNodesResultV2,\n CreateNodesV2,\n ProjectConfiguration,\n TargetConfiguration,\n createNodesFromFiles,\n readJsonFile,\n writeJsonFile,\n} from '@nx/devkit';\nimport { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';\nimport { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';\nimport { existsSync, readdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { hashObject } from 'nx/src/devkit-internals';\nimport { workspaceDataDirectory } from 'nx/src/utils/cache-directory';\nimport { AdapterDefinition, AdapterTargetConfig, AdapterTargetName, ADAPTER_TARGET_NAMES } from '../adapters/contract';\nimport { buildDiscoveryGlob, findAdapterForConfigFile, getBuiltinAdapters } from '../adapters/registry';\n\nexport interface NxCucumberPluginOptions {\n /** Override the primary e2e target name (default: 'e2e') */\n e2eTargetName?: string;\n /** Override the UI mode target name (default: 'e2e-ui') */\n e2eUiTargetName?: string;\n /** Override the parallel target name (default: 'e2e-parallel') */\n e2eParallelTargetName?: string;\n}\n\nconst DEFAULT_TARGET_NAMES: Record<AdapterTargetName, string> = {\n e2e: 'e2e',\n 'e2e-ui': 'e2e-ui',\n 'e2e-parallel': 'e2e-parallel',\n};\n\nfunction resolveTargetName(key: AdapterTargetName, options?: NxCucumberPluginOptions): string {\n if (key === 'e2e' && options?.e2eTargetName) return options.e2eTargetName;\n if (key === 'e2e-ui' && options?.e2eUiTargetName) return options.e2eUiTargetName;\n if (key === 'e2e-parallel' && options?.e2eParallelTargetName) return options.e2eParallelTargetName;\n return DEFAULT_TARGET_NAMES[key];\n}\n\nfunction readTargetsCache(cachePath: string): Record<string, TargetsResult> {\n return process.env['NX_CACHE_PROJECT_GRAPH'] !== 'false' && existsSync(cachePath) ? readJsonFile(cachePath) : {};\n}\n\nfunction writeTargetsToCache(cachePath: string, results: Record<string, TargetsResult>): void {\n writeJsonFile(cachePath, results);\n}\n\ninterface TargetsResult {\n targets: Record<string, TargetConfiguration>;\n metadata: ProjectConfiguration['metadata'];\n}\n\n/**\n * Builds Nx targets from an adapter definition for a specific project.\n */\nexport function buildTargetsForAdapter(\n adapter: AdapterDefinition,\n projectRoot: string,\n configFilePath: string,\n namedInputs: Record<string, unknown[]>,\n options?: NxCucumberPluginOptions\n): TargetsResult {\n const targets: Record<string, TargetConfiguration> = {};\n const targetGroup: string[] = [];\n\n const defaultInputs = 'production' in namedInputs ? ['production', '^production'] : ['default', '^default'];\n\n for (const key of ADAPTER_TARGET_NAMES) {\n const adapterTarget: AdapterTargetConfig | undefined = adapter.targets[key];\n if (!adapterTarget) continue;\n\n const targetName = resolveTargetName(key, options);\n const defaultOutputs = [`{workspaceRoot}/dist/e2e/${projectRoot}`];\n\n targets[targetName] = {\n command: `${adapterTarget.command} --config ${configFilePath}`,\n options: {\n ...(adapterTarget.defaultOptions ?? {}),\n },\n cache: adapterTarget.continuous ? false : true,\n inputs: adapterTarget.inputs ?? [...defaultInputs, { externalDependencies: adapter.technologies }],\n outputs: adapterTarget.outputs ?? defaultOutputs,\n ...(adapterTarget.dependsOn ? { dependsOn: adapterTarget.dependsOn } : {}),\n ...(adapterTarget.continuous ? { continuous: true } : {}),\n metadata: {\n technologies: adapter.technologies,\n description: `Run ${adapter.name} ${key} tests`,\n help: {\n command: `${adapterTarget.command} --help`,\n example: {},\n },\n },\n };\n\n targetGroup.push(targetName);\n }\n\n return {\n targets,\n metadata: {\n targetGroups: {\n [adapter.name]: targetGroup,\n },\n },\n };\n}\n\n/**\n * Checks whether the given directory is a valid Nx project\n * (contains project.json or package.json).\n */\nfunction isProjectRoot(workspaceRoot: string, projectRoot: string): boolean {\n const dir = join(workspaceRoot, projectRoot);\n if (!existsSync(dir)) return false;\n const files = readdirSync(dir);\n return files.includes('project.json') || files.includes('package.json');\n}\n\n/**\n * Reads user-defined targets from project.json to avoid conflicts.\n */\nfunction getUserDefinedTargets(workspaceRoot: string, projectRoot: string): Set<string> {\n const projectJsonPath = join(workspaceRoot, projectRoot, 'project.json');\n if (!existsSync(projectJsonPath)) return new Set();\n try {\n const config = readJsonFile<ProjectConfiguration>(projectJsonPath);\n return new Set(Object.keys(config.targets ?? {}));\n } catch {\n return new Set();\n }\n}\n\n/**\n * Removes targets that conflict with user-defined targets in project.json.\n */\nfunction filterConflictingTargets(result: TargetsResult, userTargets: Set<string>): TargetsResult {\n if (userTargets.size === 0) return result;\n\n const filtered: Record<string, TargetConfiguration> = {};\n const filteredGroup: string[] = [];\n\n for (const [name, target] of Object.entries(result.targets)) {\n if (!userTargets.has(name)) {\n filtered[name] = target;\n filteredGroup.push(name);\n }\n }\n\n const groupKey = Object.keys(result.metadata?.targetGroups ?? {})[0];\n\n return {\n targets: filtered,\n metadata: groupKey ? { targetGroups: { [groupKey]: filteredGroup } } : result.metadata,\n };\n}\n\nexport const createNodesV2: CreateNodesV2<NxCucumberPluginOptions> = [\n createDiscoveryGlob(),\n async (configFiles, options, context): Promise<CreateNodesResultV2> => {\n const adapters = getBuiltinAdapters();\n if (adapters.length === 0) return [];\n\n const optionsHash = hashObject(options ?? {});\n const cachePath = join(workspaceDataDirectory, `nx-cucumber-${optionsHash}.hash`);\n const targetsCache = readTargetsCache(cachePath);\n\n try {\n return await createNodesFromFiles(\n async (configFilePath, opts, ctx) => {\n const projectRoot = dirname(configFilePath);\n\n if (!isProjectRoot(ctx.workspaceRoot, projectRoot)) {\n return {};\n }\n\n const adapter = findAdapterForConfigFile(configFilePath, adapters);\n if (!adapter) return {};\n\n const hash = await calculateHashForCreateNodes(projectRoot, opts ?? {}, ctx);\n\n if (!targetsCache[hash]) {\n const namedInputs = getNamedInputs(projectRoot, ctx);\n const result = buildTargetsForAdapter(adapter, projectRoot, configFilePath, namedInputs, opts ?? undefined);\n const userTargets = getUserDefinedTargets(ctx.workspaceRoot, projectRoot);\n targetsCache[hash] = filterConflictingTargets(result, userTargets);\n }\n\n const { targets, metadata } = targetsCache[hash];\n\n return {\n projects: {\n [projectRoot]: {\n root: projectRoot,\n targets,\n metadata,\n },\n },\n };\n },\n configFiles,\n options,\n context\n );\n } finally {\n writeTargetsToCache(cachePath, targetsCache);\n }\n },\n];\n\n/**\n * Builds the glob at module load time from registered adapters.\n * Returns a no-match pattern if no adapters are registered yet.\n */\nfunction createDiscoveryGlob(): string {\n const adapters = getBuiltinAdapters();\n return buildDiscoveryGlob(adapters) ?? '**/__nx-cucumber-no-adapters__';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"create-nodes.js","sourceRoot":"","sources":["../../../../../libs/nx-cucumber/src/plugin/create-nodes.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CreateNodesResultV2,\n CreateNodesV2,\n ProjectConfiguration,\n TargetConfiguration,\n createNodesFromFiles,\n readJsonFile,\n writeJsonFile,\n} from '@nx/devkit';\nimport { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';\nimport { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';\nimport { existsSync, readdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { hashObject } from 'nx/src/devkit-internals';\nimport { workspaceDataDirectory } from 'nx/src/utils/cache-directory';\nimport { AdapterDefinition, AdapterTargetConfig, AdapterTargetName, ADAPTER_TARGET_NAMES } from '../adapters/contract';\nimport { buildDiscoveryGlob, findAdapterForConfigFile, getBuiltinAdapters } from '../adapters/registry';\n\nexport interface NxCucumberPluginOptions {\n /** Override the primary e2e target name (default: 'e2e') */\n e2eTargetName?: string;\n /** Override the UI mode target name (default: 'e2e-ui') */\n e2eUiTargetName?: string;\n /** Override the parallel target name (default: 'e2e-parallel') */\n e2eParallelTargetName?: string;\n}\n\nconst DEFAULT_TARGET_NAMES: Record<AdapterTargetName, string> = {\n e2e: 'e2e',\n 'e2e-ui': 'e2e-ui',\n 'e2e-parallel': 'e2e-parallel',\n};\n\nfunction resolveTargetName(key: AdapterTargetName, options?: NxCucumberPluginOptions): string {\n if (key === 'e2e' && options?.e2eTargetName) return options.e2eTargetName;\n if (key === 'e2e-ui' && options?.e2eUiTargetName) return options.e2eUiTargetName;\n if (key === 'e2e-parallel' && options?.e2eParallelTargetName) return options.e2eParallelTargetName;\n return DEFAULT_TARGET_NAMES[key];\n}\n\nexport function readTargetsCache(cachePath: string): Record<string, TargetsResult> {\n return process.env['NX_CACHE_PROJECT_GRAPH'] !== 'false' && existsSync(cachePath) ? readJsonFile(cachePath) : {};\n}\n\nfunction writeTargetsToCache(cachePath: string, results: Record<string, TargetsResult>): void {\n writeJsonFile(cachePath, results);\n}\n\ninterface TargetsResult {\n targets: Record<string, TargetConfiguration>;\n metadata: ProjectConfiguration['metadata'];\n}\n\n/**\n * Builds Nx targets from an adapter definition for a specific project.\n */\nexport function buildTargetsForAdapter(\n adapter: AdapterDefinition,\n projectRoot: string,\n configFilePath: string,\n namedInputs: Record<string, unknown[]>,\n options?: NxCucumberPluginOptions\n): TargetsResult {\n const targets: Record<string, TargetConfiguration> = {};\n const targetGroup: string[] = [];\n\n const defaultInputs = 'production' in namedInputs ? ['production', '^production'] : ['default', '^default'];\n\n for (const key of ADAPTER_TARGET_NAMES) {\n const adapterTarget: AdapterTargetConfig | undefined = adapter.targets[key];\n if (!adapterTarget) continue;\n\n const targetName = resolveTargetName(key, options);\n const defaultOutputs = [`{workspaceRoot}/dist/e2e/${projectRoot}`];\n\n targets[targetName] = {\n command: `${adapterTarget.command} --config ${configFilePath}`,\n options: {\n ...(adapterTarget.defaultOptions ?? {}),\n },\n cache: adapterTarget.continuous ? false : true,\n inputs: adapterTarget.inputs ?? [...defaultInputs, { externalDependencies: adapter.technologies }],\n outputs: adapterTarget.outputs ?? defaultOutputs,\n ...(adapterTarget.dependsOn ? { dependsOn: adapterTarget.dependsOn } : {}),\n ...(adapterTarget.continuous ? { continuous: true } : {}),\n metadata: {\n technologies: adapter.technologies,\n description: `Run ${adapter.name} ${key} tests`,\n help: {\n command: `${adapterTarget.command} --help`,\n example: {},\n },\n },\n };\n\n targetGroup.push(targetName);\n }\n\n return {\n targets,\n metadata: {\n targetGroups: {\n [adapter.name]: targetGroup,\n },\n },\n };\n}\n\n/**\n * Checks whether the given directory is a valid Nx project\n * (contains project.json or package.json).\n */\nexport function isProjectRoot(workspaceRoot: string, projectRoot: string): boolean {\n const dir = join(workspaceRoot, projectRoot);\n if (!existsSync(dir)) return false;\n const files = readdirSync(dir);\n return files.includes('project.json') || files.includes('package.json');\n}\n\n/**\n * Reads user-defined targets from project.json to avoid conflicts.\n */\nexport function getUserDefinedTargets(workspaceRoot: string, projectRoot: string): Set<string> {\n const projectJsonPath = join(workspaceRoot, projectRoot, 'project.json');\n if (!existsSync(projectJsonPath)) return new Set();\n try {\n const config = readJsonFile<ProjectConfiguration>(projectJsonPath);\n return new Set(Object.keys(config.targets ?? {}));\n } catch {\n return new Set();\n }\n}\n\n/**\n * Removes targets that conflict with user-defined targets in project.json.\n */\nexport function filterConflictingTargets(result: TargetsResult, userTargets: Set<string>): TargetsResult {\n if (userTargets.size === 0) return result;\n\n const filtered: Record<string, TargetConfiguration> = {};\n const filteredGroup: string[] = [];\n\n for (const [name, target] of Object.entries(result.targets)) {\n if (!userTargets.has(name)) {\n filtered[name] = target;\n filteredGroup.push(name);\n }\n }\n\n const groupKey = Object.keys(result.metadata?.targetGroups ?? {})[0];\n\n return {\n targets: filtered,\n metadata: groupKey ? { targetGroups: { [groupKey]: filteredGroup } } : result.metadata,\n };\n}\n\nexport const createNodesV2: CreateNodesV2<NxCucumberPluginOptions> = [\n createDiscoveryGlob(),\n async (configFiles, options, context): Promise<CreateNodesResultV2> => {\n const adapters = getBuiltinAdapters();\n if (adapters.length === 0) return [];\n\n const optionsHash = hashObject(options ?? {});\n const cachePath = join(workspaceDataDirectory, `nx-cucumber-${optionsHash}.hash`);\n const targetsCache = readTargetsCache(cachePath);\n\n try {\n return await createNodesFromFiles(\n async (configFilePath, opts, ctx) => {\n const projectRoot = dirname(configFilePath);\n\n if (!isProjectRoot(ctx.workspaceRoot, projectRoot)) {\n return {};\n }\n\n const adapter = findAdapterForConfigFile(configFilePath, adapters);\n if (!adapter) return {};\n\n const hash = await calculateHashForCreateNodes(projectRoot, opts ?? {}, ctx);\n\n if (!targetsCache[hash]) {\n const namedInputs = getNamedInputs(projectRoot, ctx);\n const result = buildTargetsForAdapter(adapter, projectRoot, configFilePath, namedInputs, opts ?? undefined);\n const userTargets = getUserDefinedTargets(ctx.workspaceRoot, projectRoot);\n targetsCache[hash] = filterConflictingTargets(result, userTargets);\n }\n\n const { targets, metadata } = targetsCache[hash];\n\n return {\n projects: {\n [projectRoot]: {\n root: projectRoot,\n targets,\n metadata,\n },\n },\n };\n },\n configFiles,\n options,\n context\n );\n } finally {\n writeTargetsToCache(cachePath, targetsCache);\n }\n },\n];\n\n/**\n * Builds the glob at module load time from registered adapters.\n * Returns a no-match pattern if no adapters are registered yet.\n */\nfunction createDiscoveryGlob(): string {\n const adapters = getBuiltinAdapters();\n return buildDiscoveryGlob(adapters) ?? '**/__nx-cucumber-no-adapters__';\n}\n"]}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export interface BeautifulReportOptions {
|
|
2
|
-
jsonDir: string;
|
|
3
|
-
outputDir: string;
|
|
4
|
-
reportTitle?: string;
|
|
5
|
-
metadata?: Record<string, string>;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Generates a beautiful HTML report from Cucumber JSON output.
|
|
9
|
-
* Requires cucumber-html-reporter to be installed.
|
|
10
|
-
*/
|
|
11
|
-
export declare function createBeautifulHtmlReport(options: BeautifulReportOptions): boolean;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* Utility to generate beautiful HTML report from Cucumber JSON results.
|
|
4
|
-
* Requires optional peer dependency: cucumber-html-reporter
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.createBeautifulHtmlReport = createBeautifulHtmlReport;
|
|
8
|
-
const tslib_1 = require("tslib");
|
|
9
|
-
const fs = tslib_1.__importStar(require("fs"));
|
|
10
|
-
const shared_util_1 = require("@gnuechtel/shared-util");
|
|
11
|
-
/**
|
|
12
|
-
* Generates a beautiful HTML report from Cucumber JSON output.
|
|
13
|
-
* Requires cucumber-html-reporter to be installed.
|
|
14
|
-
*/
|
|
15
|
-
function createBeautifulHtmlReport(options) {
|
|
16
|
-
const { jsonDir, outputDir, reportTitle, metadata } = options;
|
|
17
|
-
if (!fs.existsSync(outputDir)) {
|
|
18
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
19
|
-
}
|
|
20
|
-
if (!fs.existsSync(jsonDir)) {
|
|
21
|
-
console.error(`Cucumber JSON directory ${jsonDir} does not exist`);
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
try {
|
|
25
|
-
const reporter = loadCucumberHtmlReporter();
|
|
26
|
-
reporter.generate({
|
|
27
|
-
name: reportTitle || 'Cucumber Feature Report',
|
|
28
|
-
brandTitle: ' ',
|
|
29
|
-
theme: 'bootstrap',
|
|
30
|
-
columnLayout: 1,
|
|
31
|
-
jsonDir,
|
|
32
|
-
output: `${outputDir}/cucumber-test-report.html`,
|
|
33
|
-
reportSuiteAsScenarios: true,
|
|
34
|
-
launchReport: false,
|
|
35
|
-
...(metadata ? { metadata } : {}),
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
catch (e) {
|
|
39
|
-
console.error(`Could not create cucumber HTML report: ${(0, shared_util_1.toErrorWithMessage)(e).message}`);
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
function loadCucumberHtmlReporter() {
|
|
45
|
-
try {
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
47
|
-
return require('cucumber-html-reporter');
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
const requiresInstall = error instanceof Error &&
|
|
51
|
-
(error.message.includes('Cannot find module') || error.message.includes('Cannot find package'));
|
|
52
|
-
if (requiresInstall) {
|
|
53
|
-
throw new Error('Beautiful HTML report generation requires "cucumber-html-reporter". Install it with: npm i -D cucumber-html-reporter');
|
|
54
|
-
}
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
//# sourceMappingURL=cucumber-report-html.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cucumber-report-html.js","sourceRoot":"","sources":["../../../../../libs/nx-cucumber/src/core/cucumber-report-html.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAoBH,8DA+BC;;AAjDD,+CAAyB;AACzB,wDAA4D;AAa5D;;;GAGG;AACH,SAAgB,yBAAyB,CAAC,OAA+B;IACvE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAE9D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,2BAA2B,OAAO,iBAAiB,CAAC,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,wBAAwB,EAAE,CAAC;QAC5C,QAAQ,CAAC,QAAQ,CAAC;YAChB,IAAI,EAAE,WAAW,IAAI,yBAAyB;YAC9C,UAAU,EAAE,GAAG;YACf,KAAK,EAAE,WAAW;YAClB,YAAY,EAAE,CAAC;YACf,OAAO;YACP,MAAM,EAAE,GAAG,SAAS,4BAA4B;YAChD,sBAAsB,EAAE,IAAI;YAC5B,YAAY,EAAE,KAAK;YACnB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,0CAA0C,IAAA,gCAAkB,EAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACzF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,wBAAwB;IAC/B,IAAI,CAAC;QACH,8DAA8D;QAC9D,OAAO,OAAO,CAAC,wBAAwB,CAAyB,CAAC;IACnE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,eAAe,GACnB,KAAK,YAAY,KAAK;YACtB,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAElG,IAAI,eAAe,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,sHAAsH,CACvH,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC","sourcesContent":["/*\n * Utility to generate beautiful HTML report from Cucumber JSON results.\n * Requires optional peer dependency: cucumber-html-reporter\n */\n\nimport * as fs from 'fs';\nimport { toErrorWithMessage } from '@gnuechtel/shared-util';\n\ntype CucumberHtmlReporter = {\n generate(options: Record<string, unknown>): void;\n};\n\nexport interface BeautifulReportOptions {\n jsonDir: string;\n outputDir: string;\n reportTitle?: string;\n metadata?: Record<string, string>;\n}\n\n/**\n * Generates a beautiful HTML report from Cucumber JSON output.\n * Requires cucumber-html-reporter to be installed.\n */\nexport function createBeautifulHtmlReport(options: BeautifulReportOptions): boolean {\n const { jsonDir, outputDir, reportTitle, metadata } = options;\n\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n if (!fs.existsSync(jsonDir)) {\n console.error(`Cucumber JSON directory ${jsonDir} does not exist`);\n return false;\n }\n\n try {\n const reporter = loadCucumberHtmlReporter();\n reporter.generate({\n name: reportTitle || 'Cucumber Feature Report',\n brandTitle: ' ',\n theme: 'bootstrap',\n columnLayout: 1,\n jsonDir,\n output: `${outputDir}/cucumber-test-report.html`,\n reportSuiteAsScenarios: true,\n launchReport: false,\n ...(metadata ? { metadata } : {}),\n });\n } catch (e: unknown) {\n console.error(`Could not create cucumber HTML report: ${toErrorWithMessage(e).message}`);\n return false;\n }\n\n return true;\n}\n\nfunction loadCucumberHtmlReporter(): CucumberHtmlReporter {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n return require('cucumber-html-reporter') as CucumberHtmlReporter;\n } catch (error) {\n const requiresInstall =\n error instanceof Error &&\n (error.message.includes('Cannot find module') || error.message.includes('Cannot find package'));\n\n if (requiresInstall) {\n throw new Error(\n 'Beautiful HTML report generation requires \"cucumber-html-reporter\". Install it with: npm i -D cucumber-html-reporter'\n );\n }\n\n throw error;\n }\n}\n"]}
|
package/src/core/web-server.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export interface WebServerConfig {
|
|
2
|
-
command: string;
|
|
3
|
-
url: string;
|
|
4
|
-
reuseExistingServer?: boolean;
|
|
5
|
-
timeout?: number;
|
|
6
|
-
/** Working directory for the server process. Defaults to the current working directory. */
|
|
7
|
-
cwd?: string;
|
|
8
|
-
/** Environment variables for the server process. Defaults to the current process environment. */
|
|
9
|
-
env?: NodeJS.ProcessEnv;
|
|
10
|
-
/**
|
|
11
|
-
* stdio mode for the server process. Defaults to 'ignore'.
|
|
12
|
-
*
|
|
13
|
-
* Keep the default 'ignore' when calling startWebServer at module load time or from any
|
|
14
|
-
* synchronous context. waitForUrl uses Atomics.wait which blocks the entire Node.js event
|
|
15
|
-
* loop - stdout data handlers cannot fire, so the pipe buffer fills up. On Windows the
|
|
16
|
-
* pipe buffer is only 4 KB: Vite fills it during startup output, blocks on write, and
|
|
17
|
-
* never starts the HTTP server, causing an indefinite hang.
|
|
18
|
-
*
|
|
19
|
-
* Use 'pipe' only when the event loop is free to process I/O during the wait (e.g. when
|
|
20
|
-
* called from an async BeforeAll hook without Atomics.wait).
|
|
21
|
-
*/
|
|
22
|
-
stdio?: 'pipe' | 'ignore';
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Starts a web server before Cucumber runs and stops it when the process exits.
|
|
26
|
-
* Mirrors Playwright's webServer config pattern.
|
|
27
|
-
*/
|
|
28
|
-
export declare function startWebServer(config: WebServerConfig): void;
|
|
29
|
-
export declare function stopWebServer(): void;
|
package/src/core/web-server.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.startWebServer = startWebServer;
|
|
4
|
-
exports.stopWebServer = stopWebServer;
|
|
5
|
-
const child_process_1 = require("child_process");
|
|
6
|
-
const shared_util_1 = require("@gnuechtel/shared-util");
|
|
7
|
-
let serverProcess;
|
|
8
|
-
let serverPort;
|
|
9
|
-
/**
|
|
10
|
-
* Starts a web server before Cucumber runs and stops it when the process exits.
|
|
11
|
-
* Mirrors Playwright's webServer config pattern.
|
|
12
|
-
*/
|
|
13
|
-
function startWebServer(config) {
|
|
14
|
-
const { command, url, reuseExistingServer = true, timeout = 60000, cwd, env, stdio = 'ignore' } = config;
|
|
15
|
-
if (reuseExistingServer && isUrlReachable(url)) {
|
|
16
|
-
console.log(`Reusing existing server at ${url}`);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
serverPort = extractPort(url);
|
|
20
|
-
console.log(`Starting web server: ${command}`);
|
|
21
|
-
serverProcess = (0, child_process_1.spawn)(command, {
|
|
22
|
-
shell: true,
|
|
23
|
-
stdio,
|
|
24
|
-
detached: true,
|
|
25
|
-
...(cwd ? { cwd } : {}),
|
|
26
|
-
...(env ? { env } : {}),
|
|
27
|
-
});
|
|
28
|
-
if (stdio === 'pipe') {
|
|
29
|
-
serverProcess.stdout?.on('data', (data) => {
|
|
30
|
-
process.stdout.write(`[serve] ${data}`);
|
|
31
|
-
});
|
|
32
|
-
serverProcess.stderr?.on('data', (data) => {
|
|
33
|
-
process.stderr.write(`[serve] ${data}`);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
serverProcess.unref();
|
|
37
|
-
process.on('exit', stopWebServerOnProcessExit);
|
|
38
|
-
process.on('SIGINT', stopWebServer);
|
|
39
|
-
process.on('SIGTERM', stopWebServer);
|
|
40
|
-
console.log(`Waiting for ${url}...`);
|
|
41
|
-
waitForUrl(url, timeout);
|
|
42
|
-
console.log(`Server ready at ${url}`);
|
|
43
|
-
if (stdio === 'pipe') {
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
-
serverProcess.stdout?.unref?.();
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
-
serverProcess.stderr?.unref?.();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function stopWebServer() {
|
|
51
|
-
if (!serverProcess || serverProcess.killed)
|
|
52
|
-
return;
|
|
53
|
-
const pid = serverProcess.pid;
|
|
54
|
-
const port = serverPort;
|
|
55
|
-
serverProcess = undefined;
|
|
56
|
-
serverPort = undefined;
|
|
57
|
-
if (pid)
|
|
58
|
-
(0, shared_util_1.killProcessTree)(pid);
|
|
59
|
-
if (process.platform === 'win32' && port) {
|
|
60
|
-
killProcessOnPort(port);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function stopWebServerOnProcessExit() {
|
|
64
|
-
if (!serverProcess || serverProcess.killed)
|
|
65
|
-
return;
|
|
66
|
-
const pid = serverProcess.pid;
|
|
67
|
-
serverProcess = undefined;
|
|
68
|
-
serverPort = undefined;
|
|
69
|
-
if (!pid)
|
|
70
|
-
return;
|
|
71
|
-
try {
|
|
72
|
-
// Avoid synchronous process-tree cleanup on process exit to prevent rare
|
|
73
|
-
// Windows deadlocks while the Node.js process is shutting down.
|
|
74
|
-
process.kill(pid, 'SIGTERM');
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
// ignore - process may have already exited
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function waitForUrl(url, timeout) {
|
|
81
|
-
const interval = 1000;
|
|
82
|
-
const deadline = Date.now() + timeout;
|
|
83
|
-
while (Date.now() < deadline) {
|
|
84
|
-
if (isUrlReachable(url))
|
|
85
|
-
return;
|
|
86
|
-
if (serverProcess?.exitCode !== null && serverProcess?.exitCode !== undefined) {
|
|
87
|
-
throw new Error(`Web server exited with code ${serverProcess.exitCode} before becoming ready`);
|
|
88
|
-
}
|
|
89
|
-
sleepSync(interval);
|
|
90
|
-
}
|
|
91
|
-
throw new Error(`Timed out after ${timeout}ms waiting for ${url}`);
|
|
92
|
-
}
|
|
93
|
-
function sleepSync(ms) {
|
|
94
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
95
|
-
}
|
|
96
|
-
function isUrlReachable(url) {
|
|
97
|
-
if (process.platform === 'win32') {
|
|
98
|
-
return isUrlReachableOnWindows(url);
|
|
99
|
-
}
|
|
100
|
-
// Let the child process exit itself after 2s instead of relying on spawnSync timeout.
|
|
101
|
-
// On Windows this path is avoided because GitLab Windows runners showed rare hangs with
|
|
102
|
-
// node+fetch probes. The PowerShell probe below has a parent timeout as a hard guard.
|
|
103
|
-
const result = (0, child_process_1.spawnSync)('node', [
|
|
104
|
-
'-e',
|
|
105
|
-
`setTimeout(()=>process.exit(1),2000);fetch('${url}').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))`,
|
|
106
|
-
], { stdio: 'ignore' });
|
|
107
|
-
return result.status === 0;
|
|
108
|
-
}
|
|
109
|
-
function isUrlReachableOnWindows(url) {
|
|
110
|
-
const escapedUrl = url.replace(/'/g, "''");
|
|
111
|
-
const result = (0, child_process_1.spawnSync)('powershell', [
|
|
112
|
-
'-NoProfile',
|
|
113
|
-
'-NonInteractive',
|
|
114
|
-
'-Command',
|
|
115
|
-
`$ProgressPreference='SilentlyContinue'; try { $r = Invoke-WebRequest -UseBasicParsing -Uri '${escapedUrl}' -TimeoutSec 2; if ($r.StatusCode -ge 200 -and $r.StatusCode -lt 400) { exit 0 } else { exit 1 } } catch { exit 1 }`,
|
|
116
|
-
], { stdio: 'ignore', timeout: 10000 });
|
|
117
|
-
return result.status === 0;
|
|
118
|
-
}
|
|
119
|
-
function extractPort(url) {
|
|
120
|
-
try {
|
|
121
|
-
const parsed = new URL(url);
|
|
122
|
-
const port = parseInt(parsed.port, 10);
|
|
123
|
-
if (port > 0)
|
|
124
|
-
return port;
|
|
125
|
-
return parsed.protocol === 'https:' ? 443 : 80;
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function killProcessOnPort(port) {
|
|
132
|
-
(0, child_process_1.spawnSync)('powershell', [
|
|
133
|
-
'-NoProfile',
|
|
134
|
-
'-NonInteractive',
|
|
135
|
-
'-Command',
|
|
136
|
-
`$conns = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue; foreach ($c in $conns) { taskkill /F /T /PID $c.OwningProcess 2>$null }`,
|
|
137
|
-
], { stdio: 'ignore', timeout: 10000 });
|
|
138
|
-
}
|
|
139
|
-
//# sourceMappingURL=web-server.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"web-server.js","sourceRoot":"","sources":["../../../../../libs/nx-cucumber/src/core/web-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync, spawn } from 'child_process';\nimport { killProcessTree } from '@gnuechtel/shared-util';\n\nexport interface WebServerConfig {\n command: string;\n url: string;\n reuseExistingServer?: boolean;\n timeout?: number;\n /** Working directory for the server process. Defaults to the current working directory. */\n cwd?: string;\n /** Environment variables for the server process. Defaults to the current process environment. */\n env?: NodeJS.ProcessEnv;\n /**\n * stdio mode for the server process. Defaults to 'ignore'.\n *\n * Keep the default 'ignore' when calling startWebServer at module load time or from any\n * synchronous context. waitForUrl uses Atomics.wait which blocks the entire Node.js event\n * loop - stdout data handlers cannot fire, so the pipe buffer fills up. On Windows the\n * pipe buffer is only 4 KB: Vite fills it during startup output, blocks on write, and\n * never starts the HTTP server, causing an indefinite hang.\n *\n * Use 'pipe' only when the event loop is free to process I/O during the wait (e.g. when\n * called from an async BeforeAll hook without Atomics.wait).\n */\n stdio?: 'pipe' | 'ignore';\n}\n\nlet serverProcess: ReturnType<typeof spawn> | undefined;\nlet serverPort: number | undefined;\n\n/**\n * Starts a web server before Cucumber runs and stops it when the process exits.\n * Mirrors Playwright's webServer config pattern.\n */\nexport function startWebServer(config: WebServerConfig): void {\n const { command, url, reuseExistingServer = true, timeout = 60000, cwd, env, stdio = 'ignore' } = config;\n\n if (reuseExistingServer && isUrlReachable(url)) {\n console.log(`Reusing existing server at ${url}`);\n return;\n }\n\n serverPort = extractPort(url);\n\n console.log(`Starting web server: ${command}`);\n serverProcess = spawn(command, {\n shell: true,\n stdio,\n detached: true,\n ...(cwd ? { cwd } : {}),\n ...(env ? { env } : {}),\n });\n\n if (stdio === 'pipe') {\n serverProcess.stdout?.on('data', (data: Buffer) => {\n process.stdout.write(`[serve] ${data}`);\n });\n serverProcess.stderr?.on('data', (data: Buffer) => {\n process.stderr.write(`[serve] ${data}`);\n });\n }\n\n serverProcess.unref();\n\n process.on('exit', stopWebServerOnProcessExit);\n process.on('SIGINT', stopWebServer);\n process.on('SIGTERM', stopWebServer);\n\n console.log(`Waiting for ${url}...`);\n waitForUrl(url, timeout);\n console.log(`Server ready at ${url}`);\n\n if (stdio === 'pipe') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (serverProcess.stdout as any)?.unref?.();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (serverProcess.stderr as any)?.unref?.();\n }\n}\n\nexport function stopWebServer(): void {\n if (!serverProcess || serverProcess.killed) return;\n const pid = serverProcess.pid;\n const port = serverPort;\n serverProcess = undefined;\n serverPort = undefined;\n if (pid) killProcessTree(pid);\n if (process.platform === 'win32' && port) {\n killProcessOnPort(port);\n }\n}\n\nfunction stopWebServerOnProcessExit(): void {\n if (!serverProcess || serverProcess.killed) return;\n const pid = serverProcess.pid;\n serverProcess = undefined;\n serverPort = undefined;\n if (!pid) return;\n try {\n // Avoid synchronous process-tree cleanup on process exit to prevent rare\n // Windows deadlocks while the Node.js process is shutting down.\n process.kill(pid, 'SIGTERM');\n } catch {\n // ignore - process may have already exited\n }\n}\n\nfunction waitForUrl(url: string, timeout: number): void {\n const interval = 1000;\n const deadline = Date.now() + timeout;\n while (Date.now() < deadline) {\n if (isUrlReachable(url)) return;\n if (serverProcess?.exitCode !== null && serverProcess?.exitCode !== undefined) {\n throw new Error(`Web server exited with code ${serverProcess.exitCode} before becoming ready`);\n }\n sleepSync(interval);\n }\n throw new Error(`Timed out after ${timeout}ms waiting for ${url}`);\n}\n\nfunction sleepSync(ms: number): void {\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n}\n\nfunction isUrlReachable(url: string): boolean {\n if (process.platform === 'win32') {\n return isUrlReachableOnWindows(url);\n }\n\n // Let the child process exit itself after 2s instead of relying on spawnSync timeout.\n // On Windows this path is avoided because GitLab Windows runners showed rare hangs with\n // node+fetch probes. The PowerShell probe below has a parent timeout as a hard guard.\n const result = spawnSync(\n 'node',\n [\n '-e',\n `setTimeout(()=>process.exit(1),2000);fetch('${url}').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))`,\n ],\n { stdio: 'ignore' }\n );\n return result.status === 0;\n}\n\nfunction isUrlReachableOnWindows(url: string): boolean {\n const escapedUrl = url.replace(/'/g, \"''\");\n const result = spawnSync(\n 'powershell',\n [\n '-NoProfile',\n '-NonInteractive',\n '-Command',\n `$ProgressPreference='SilentlyContinue'; try { $r = Invoke-WebRequest -UseBasicParsing -Uri '${escapedUrl}' -TimeoutSec 2; if ($r.StatusCode -ge 200 -and $r.StatusCode -lt 400) { exit 0 } else { exit 1 } } catch { exit 1 }`,\n ],\n { stdio: 'ignore', timeout: 10000 }\n );\n return result.status === 0;\n}\n\nfunction extractPort(url: string): number | undefined {\n try {\n const parsed = new URL(url);\n const port = parseInt(parsed.port, 10);\n if (port > 0) return port;\n return parsed.protocol === 'https:' ? 443 : 80;\n } catch {\n return undefined;\n }\n}\n\nfunction killProcessOnPort(port: number): void {\n spawnSync(\n 'powershell',\n [\n '-NoProfile',\n '-NonInteractive',\n '-Command',\n `$conns = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue; foreach ($c in $conns) { taskkill /F /T /PID $c.OwningProcess 2>$null }`,\n ],\n { stdio: 'ignore', timeout: 10000 }\n );\n}\n"]}
|