@midscene/test 1.12.5-beta-20260908034641.0 → 1.12.5

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.
@@ -115,12 +115,12 @@ const renderWorkflowLocations = (location)=>{
115
115
  sections.push(`**Config file:** ${location.configPath ? `\`${location.configPath}\`` : 'No config file loaded.'}`);
116
116
  for (const project of location.projects){
117
117
  const patterns = project.files.include.map((pattern)=>`\`${pattern}\``).join(', ');
118
- const projectLabel = location.projects.length > 1 || 'default' !== project.name ? ` (Execution Project: ${project.name})` : '';
118
+ const projectLabel = location.projects.length > 1 ? ` (Execution Project: ${project.name})` : '';
119
119
  const excludes = project.files.exclude?.length ? `; excludes: ${project.files.exclude.map((pattern)=>`\`${pattern}\``).join(', ')}` : '';
120
120
  sections.push(`**Case files:** ${patterns}${projectLabel}${excludes}`);
121
121
  }
122
122
  }
123
- sections.push('Files matching `files.exclude` are not selected. Midscene Test always ignores `.git/`, `.midscene/`, `midscene_run/`, and `node_modules/` directories, including nested ones.', 'Call a Node in `cases[].steps`, or in the Workflow Document lifecycle steps `beforeAll`, `beforeEach`, `afterEach`, and `afterAll`. Root Nodes are shared defaults; `projects[].nodes` adds local Nodes and overrides same-named root Nodes only within that Execution Project. Each Execution Project supplies its own runtime environment.');
123
+ sections.push('Files matching `files.exclude` are not selected. Midscene Test always ignores `.git/`, `.midscene/`, `midscene_run/`, and `node_modules/` directories, including nested ones.', 'Call a Node in `cases[].steps`, or in the Workflow Document lifecycle steps `beforeAll`, `beforeEach`, `afterEach`, and `afterAll`. Node registrations are shared across the Test Project; each Execution Project supplies its own runtime environment.');
124
124
  return sections.join('\n\n');
125
125
  };
126
126
  const WORKFLOW_STEP_CONTRACT = `## Workflow Step Contract
@@ -609,7 +609,7 @@ const validateNonNegativeInteger = (value, fallback, label)=>{
609
609
  if (!Number.isInteger(value) || value < 0) throw new TypeError(`Midscene config ${label} must be a non-negative integer.`);
610
610
  return value;
611
611
  };
612
- const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
612
+ const validateExecutionProjects = (value, defaultSetup)=>{
613
613
  if (void 0 === value) {
614
614
  let platform = 'web';
615
615
  if (void 0 !== defaultSetup) {
@@ -635,8 +635,7 @@ const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
635
635
  exclude: []
636
636
  }),
637
637
  retry: 0,
638
- variables: Object.freeze({}),
639
- nodes: new NodeRegistry(globalNodes.definitions())
638
+ variables: Object.freeze({})
640
639
  })
641
640
  ])
642
641
  };
@@ -651,7 +650,6 @@ const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
651
650
  'name',
652
651
  'platform',
653
652
  'setup',
654
- 'nodes',
655
653
  'files',
656
654
  'tags',
657
655
  'retry',
@@ -662,17 +660,10 @@ const validateExecutionProjects = (value, defaultSetup, globalNodes)=>{
662
660
  names.add(candidate.name);
663
661
  const platform = validatePlatform(candidate.platform, `${label}.platform`);
664
662
  const files = validateTestFileSelection(candidate.files, `${label}.files`);
665
- if (void 0 !== candidate.nodes && !Array.isArray(candidate.nodes)) throw new TypeError(`Midscene config ${label}.nodes must be an array.`);
666
- const localNodes = new NodeRegistry(candidate.nodes);
667
- const nodes = new NodeRegistry([
668
- ...globalNodes.definitions().filter((node)=>!localNodes.has(node.name)),
669
- ...localNodes.definitions()
670
- ]);
671
663
  return Object.freeze({
672
664
  projectId: projectIdFromIndex(index),
673
665
  name: candidate.name,
674
666
  platform,
675
- nodes,
676
667
  ...files ? {
677
668
  files
678
669
  } : {},
@@ -714,8 +705,7 @@ const validateOutput = (value)=>{
714
705
  });
715
706
  };
716
707
  const validateTestProjectDefinition = (definition)=>{
717
- if (!isRecord(definition)) throw new TypeError('Midscene config must default export an object.');
718
- if (void 0 !== definition.nodes && !Array.isArray(definition.nodes)) throw new TypeError('Midscene config nodes must be an array.');
708
+ if (!isRecord(definition) || !('nodes' in definition) || !Array.isArray(definition.nodes)) throw new TypeError('Midscene config must default export an object with a nodes array.');
719
709
  if ('setupWorkflow' in definition) throw new TypeError('Midscene config setupWorkflow is no longer supported. Use setup or projects[].setup instead.');
720
710
  if ('setupDocument' in definition) throw new TypeError('Midscene config setupDocument is not supported. Use setup or projects[].setup for resources, and beforeAll/afterAll for document lifecycle steps.');
721
711
  if ('root' in definition) throw new TypeError('Midscene config root is not supported. Pass the Test Project directory to midscene-test instead.');
@@ -728,8 +718,8 @@ const validateTestProjectDefinition = (definition)=>{
728
718
  'output',
729
719
  'nodes'
730
720
  ], 'root');
721
+ const resolvedProjects = validateExecutionProjects(definition.projects, definition.setup);
731
722
  const nodes = new NodeRegistry(definition.nodes);
732
- const resolvedProjects = validateExecutionProjects(definition.projects, definition.setup, nodes);
733
723
  return {
734
724
  ...resolvedProjects,
735
725
  test: validateTestOptions(definition.test),
@@ -1934,7 +1924,7 @@ const selectProjects = (projects, names)=>{
1934
1924
  if (unknown) throw new Error(`Unknown Midscene project: ${unknown}`);
1935
1925
  return projects.filter((project)=>requested.has(project.name));
1936
1926
  };
1937
- const prepareProject = (project, projectRoot, runDir)=>{
1927
+ const prepareProject = (project, projectRoot, resolveNode, runDir)=>{
1938
1928
  const fileSelection = project.files ?? DEFAULT_TEST_FILE_SELECTION;
1939
1929
  const files = discoverTestFiles(projectRoot, fileSelection);
1940
1930
  const sources = files.map((absolutePath)=>({
@@ -1956,7 +1946,7 @@ const prepareProject = (project, projectRoot, runDir)=>{
1956
1946
  }
1957
1947
  for (const source of sources)try {
1958
1948
  const collected = collectWorkflowDocument(source, {
1959
- resolveNode: project.nodes.get.bind(project.nodes),
1949
+ resolveNode,
1960
1950
  variables: project.variables,
1961
1951
  env: process.env
1962
1952
  });
@@ -2000,7 +1990,7 @@ async function runTestProject(options = {}) {
2000
1990
  });
2001
1991
  (0, external_node_fs_.mkdirSync)(runDir);
2002
1992
  const selectedProjects = selectProjects(definition.projects, options.projectNames);
2003
- const preparedProjects = selectedProjects.map((project)=>prepareProject(project, projectRoot, runDir));
1993
+ const preparedProjects = selectedProjects.map((project)=>prepareProject(project, projectRoot, definition.resolveNode, runDir));
2004
1994
  const progress = options.onProgress ?? (()=>{});
2005
1995
  const totalDocuments = preparedProjects.reduce((total, prepared)=>total + prepared.documents.length, 0);
2006
1996
  const totalCases = preparedProjects.reduce((total, prepared)=>total + prepared.selectedCaseCount, 0);
@@ -2093,7 +2083,7 @@ async function runTestProject(options = {}) {
2093
2083
  }
2094
2084
  projectProgress(` [document ${documentIndex + 1}/${prepared.documents.length}] ${document.sourcePath}`);
2095
2085
  const execution = await runWorkflowDocument(document, {
2096
- resolveNode: project.nodes.require.bind(project.nodes),
2086
+ resolveNode: definition.nodes.require.bind(definition.nodes),
2097
2087
  project,
2098
2088
  projectContext: runtime.context,
2099
2089
  retry: project.retry,
@@ -2232,7 +2222,7 @@ const parseTestCliArgs = (args, cwd = process.cwd())=>{
2232
2222
  } else throw new Error(`Unknown option: ${arg}`);
2233
2223
  }
2234
2224
  if ('nodes' === command && resultDir) throw new Error('--result-dir is not supported by nodes.');
2235
- if ('nodes' === command && projectNames.length > 1) throw new Error('nodes accepts only one --project name.');
2225
+ if ('nodes' === command && projectNames.length > 0) throw new Error('--project is not supported by nodes.');
2236
2226
  return {
2237
2227
  ...command ? {
2238
2228
  command
@@ -2263,15 +2253,10 @@ const runNodesCommand = async (options, io)=>{
2263
2253
  const configPath = options.configPath ? (0, external_node_path_.resolve)(configSearchRoot, options.configPath) : discoverTestConfig(configSearchRoot);
2264
2254
  if (options.configPath && (!configPath || !(0, external_node_fs_.existsSync)(configPath))) throw new Error(`Midscene config does not exist: ${configPath}`);
2265
2255
  const project = await loadTestProject(configPath);
2266
- const projectName = options.projectNames?.[0];
2267
- const selectedProjects = projectName ? project.projects.filter((candidate)=>candidate.name === projectName) : project.projects;
2268
- if (0 === selectedProjects.length) throw new Error(`Unknown Midscene project: ${projectName}`);
2269
- const registry = selectedProjects[0].nodes;
2270
- if (selectedProjects.some((candidate)=>candidate.nodes.names().length !== registry.names().length || candidate.nodes.definitions().some((node)=>registry.get(node.name) !== node))) throw new Error('Projects have different Nodes. Use nodes --project <name> to select one.');
2271
- const nodes = sortNodesForReference(registry.definitions());
2256
+ const nodes = sortNodesForReference(project.nodes.definitions());
2272
2257
  const document = renderNodeReference(nodes, {
2273
2258
  configPath: configPath ? (0, external_node_path_.relative)(configSearchRoot, configPath).split(external_node_path_.sep).join('/') : void 0,
2274
- projects: selectedProjects.map((executionProject)=>({
2259
+ projects: project.projects.map((executionProject)=>({
2275
2260
  name: executionProject.name,
2276
2261
  files: executionProject.files ?? DEFAULT_TEST_FILE_SELECTION
2277
2262
  }))
@@ -2279,7 +2264,6 @@ const runNodesCommand = async (options, io)=>{
2279
2264
  for (const warning of document.warnings)io.error(`midscene-test nodes: ${warning}`);
2280
2265
  const referencePath = (0, external_node_path_.resolve)(configSearchRoot, 'midscene-node-reference.md');
2281
2266
  (0, external_node_fs_.writeFileSync)(referencePath, document.markdown);
2282
- if (projectName) io.log(`Execution Project: ${projectName}`);
2283
2267
  io.log(`Registered Nodes (${nodes.length}):`);
2284
2268
  if (0 === nodes.length) io.log('No nodes are registered by the current Test Project.');
2285
2269
  for (const node of nodes)io.log(`- ${node.name}: ${node.description?.trim() || "Description not declared."}`);