@fluid-internal/mocha-test-setup 3.0.2 → 3.2.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/.mocharc.cjs ADDED
@@ -0,0 +1,19 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ "use strict";
7
+
8
+ // This package's own self-tests don't use its own `getFluidTestMochaConfig()` (unlike its consumers):
9
+ // that helper wires up `mocha-multi-reporters` and `FluidXunitReporter`'s JUnit-compatible XML output,
10
+ // which these straightforward unit tests don't need, and using it here would mean this package
11
+ // `require`-ing itself as a dependency of its own test run.
12
+ module.exports = {
13
+ require: ["source-map-support/register"],
14
+ recursive: true,
15
+ spec: "lib/test/**/*.spec.js",
16
+ "unhandled-rejections": "strict",
17
+ // Fail the test run if no tests are found/run, consistent with `getFluidTestMochaConfig`'s consumers.
18
+ "fail-zero": true,
19
+ };
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @fluid-internal/mocha-test-setup
2
2
 
3
+ ## 3.2.0
4
+
5
+ Dependency updates only.
6
+
7
+ ## 3.1.0
8
+
9
+ Dependency updates only.
10
+
3
11
  ## 3.0.0
4
12
 
5
13
  Dependency updates only.
package/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # @fluid-internal/mocha-test-setup
2
2
 
3
- <!-- AUTO-GENERATED-CONTENT:START (LIBRARY_README_HEADER) -->
4
-
3
+ <!-- markdown-magic:begin {"transform":"library-readme-header","headingLevel":2} -->
5
4
  <!-- prettier-ignore-start -->
6
5
  <!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
7
6
 
@@ -10,8 +9,7 @@
10
9
  **We do not guarantee the stability of this package or its APIs.**
11
10
 
12
11
  <!-- prettier-ignore-end -->
13
-
14
- <!-- AUTO-GENERATED-CONTENT:END -->
12
+ <!-- markdown-magic:end -->
15
13
 
16
14
  This package has a few main purposes:
17
15
 
@@ -187,8 +185,7 @@ to the module with that implementation should **not** be specified with the `--r
187
185
  This package has logic that needs the custom logger to be imported/executed at a particular time, and we ensure that
188
186
  by requiring that the path be provided through that environment variable.
189
187
 
190
- <!-- AUTO-GENERATED-CONTENT:START (README_FOOTER) -->
191
-
188
+ <!-- markdown-magic:begin {"transform":"readme-footer","headingLevel":2} -->
192
189
  <!-- prettier-ignore-start -->
193
190
  <!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
194
191
 
@@ -196,16 +193,16 @@ by requiring that the path be provided through that environment variable.
196
193
 
197
194
  You can [contribute](https://github.com/microsoft/FluidFramework/blob/main/CONTRIBUTING.md) to Fluid Framework in these ways:
198
195
 
199
- - Answer questions in [GitHub Discussions](https://github.com/microsoft/FluidFramework/discussions).
200
- - [Submit bug reports](https://github.com/microsoft/FluidFramework/issues) and help verify fixes.
201
- - Review [source code changes](https://github.com/microsoft/FluidFramework/pulls).
202
- - [Contribute bug fixes](https://github.com/microsoft/FluidFramework/blob/main/CONTRIBUTING.md).
196
+ - Answer questions in [GitHub Discussions](https://github.com/microsoft/FluidFramework/discussions).
197
+ - [Submit bug reports](https://github.com/microsoft/FluidFramework/issues) and help verify fixes.
198
+ - Review [source code changes](https://github.com/microsoft/FluidFramework/pulls).
199
+ - [Contribute bug fixes](https://github.com/microsoft/FluidFramework/blob/main/CONTRIBUTING.md).
203
200
 
204
201
  For detailed instructions, read the [repo documentation](https://github.com/microsoft/FluidFramework/blob/main/docs/content/Home.md).
205
202
 
206
203
  This project follows the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
207
204
  For more information, read the [Code of Conduct frequently asked questions](https://opensource.microsoft.com/codeofconduct/faq/).
208
- For questions or comments, contact [opencode@microsoft.com](mailto:opencode@microsoft.com).
205
+ For questions or comments, contact <opencode@microsoft.com>.
209
206
 
210
207
  This project may contain Microsoft trademarks or logos for Microsoft projects, products, or services.
211
208
  Use of these trademarks or logos must follow Microsoft’s [Trademark & Brand Guidelines](https://www.microsoft.com/trademarks).
@@ -226,5 +223,4 @@ Use of these trademarks or logos must follow Microsoft's [Trademark & Brand Guid
226
223
  Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
227
224
 
228
225
  <!-- prettier-ignore-end -->
229
-
230
- <!-- AUTO-GENERATED-CONTENT:END -->
226
+ <!-- markdown-magic:end -->
@@ -1 +1 @@
1
- {"version":3,"file":"mocharcCommon.d.ts","sourceRoot":"","sources":["../src/mocharcCommon.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACtC,UAAU,EAAE,MAAM,EAClB,yBAAyB,CAAC,EAAE,MAAM,EAAE,EACpC,gBAAgB,CAAC,EAAE,MAAM,GACvB,oBAAoB,CAyGtB"}
1
+ {"version":3,"file":"mocharcCommon.d.ts","sourceRoot":"","sources":["../src/mocharcCommon.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACtC,UAAU,EAAE,MAAM,EAClB,yBAAyB,CAAC,EAAE,MAAM,EAAE,EACpC,gBAAgB,CAAC,EAAE,MAAM,GACvB,oBAAoB,CAgHtB"}
@@ -91,8 +91,14 @@ export function getFluidTestMochaConfig(packageDir, additionalRequiredModules, t
91
91
  console.log(`Writing test results relative to package to nyc/${outputFilePrefix}junit-report.xml`);
92
92
  }
93
93
  const suiteName = reportPrefix !== undefined ? `${packageJson.name} - ${reportPrefix}` : packageJson.name;
94
+ // This must be the exact module specifier mocha-multi-reporters will `require(...)` to load the reporter
95
+ // (see its source: https://github.com/stevemao/mocha-multi-reporters/blob/master/lib/MultiReporters.js),
96
+ // i.e. it can't be shortened to some arbitrary friendly name. It also determines the reporter-specific
97
+ // option key mocha-multi-reporters expects below (and in test-config.json): it camelCases this exact
98
+ // string and appends "ReporterOptions" (e.g. "@foo/bar/baz" -> "fooBarBazReporterOptions").
99
+ const xunitReporterName = "@fluid-internal/mocha-test-setup/xunit-reporter";
94
100
  config["reporter-options"] = [
95
- `configFile=${path.join(import.meta.dirname, "..", "test-config.json")},cmrOutput=xunit+output+${outputFilePrefix}:xunit+suiteName+${suiteName}`,
101
+ `configFile=${path.join(import.meta.dirname, "..", "test-config.json")},cmrOutput=${xunitReporterName}+output+${outputFilePrefix}:${xunitReporterName}+suiteName+${suiteName}`,
96
102
  ];
97
103
  }
98
104
  if (process.env.FLUID_TEST_TIMEOUT !== undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"mocharcCommon.js","sourceRoot":"","sources":["../src/mocharcCommon.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Mocha configuration object returned by {@link getFluidTestMochaConfig}.\n */\nexport interface FluidTestMochaConfig {\n\trecursive: boolean;\n\trequire: string[];\n\t\"unhandled-rejections\": string;\n\t\"fail-zero\": boolean;\n\tignore: string[];\n\t\"node-option\": string[];\n\tspec: string;\n\texit?: boolean;\n\ttimeout?: number | string;\n\tfgrep?: string[];\n\treporter?: string;\n\treporterOptions?: string[];\n\t\"reporter-options\"?: string[];\n\t\"forbid-only\"?: boolean;\n}\n\n/**\n * Get the mocha configuration for running tests using the conventions followed in the Fluid Framework repository.\n *\n * @param packageDir - the directory of the package, typically set using `__dirname`\n * @param additionalRequiredModules - modules to require in addition to the standard set.\n * @param testReportPrefix - prefix for the test output report file names.\n * @remarks\n * Additional configuration can be provided via environment variables: see {@link file://./README.md}.\n *\n * Users desiring exact control over the `spec` from the CLI should delete or replace the spec from the returned config, since mocha's behavior is to extend it, not override it.\n */\nexport function getFluidTestMochaConfig(\n\tpackageDir: string,\n\tadditionalRequiredModules?: string[],\n\ttestReportPrefix?: string,\n): FluidTestMochaConfig {\n\tconst requiredModules = [\n\t\t// General mocha setup e.g. suppresses console.log,\n\t\t// This has to be before others (except logger) so that registerMochaTestWrapperFuncs is available\n\t\t\"@fluid-internal/mocha-test-setup\",\n\t\t\"source-map-support/register\",\n\t\t...(additionalRequiredModules ?? []),\n\t];\n\n\tif (process.env.FLUID_TEST_LOGGER_PKG_SPECIFIER !== undefined) {\n\t\t// Inject implementation of createTestLogger, put it first before mocha-test-setup\n\t\trequiredModules.unshift(process.env.FLUID_TEST_LOGGER_PKG_SPECIFIER);\n\t}\n\n\tlet reportPrefix = testReportPrefix;\n\tlet defaultSpec = \"lib/test\";\n\tif (process.env.FLUID_TEST_MODULE_SYSTEM === \"CJS\") {\n\t\tdefaultSpec = \"dist/test\";\n\t\tconst testVariant = process.env.FLUID_TEST_VARIANT;\n\t\tprocess.env.FLUID_TEST_VARIANT = testVariant !== undefined ? `CJS,${testVariant}` : \"CJS\";\n\t\treportPrefix = reportPrefix !== undefined ? `${reportPrefix}-CJS` : \"CJS\";\n\t}\n\n\tconst config: FluidTestMochaConfig = {\n\t\t\"recursive\": true,\n\t\t\"require\": requiredModules,\n\t\t\"unhandled-rejections\": \"strict\",\n\t\t// Fail the test run if no tests are found/run. This catches cases where test files fail to\n\t\t// load silently (e.g. due to broken imports), which would otherwise produce a green \"0 passing\"\n\t\t// result with exit code 0.\n\t\t\"fail-zero\": true,\n\t\tignore: [\n\t\t\t// Ignore \"tools\" which are scripts intended to be run, not part of the test suite.\n\t\t\t\"**/*.tool.{js,cjs,mjs}\",\n\t\t],\n\t\t\"node-option\": [\n\t\t\t// Allow test-only indexes to be imported. Search the FF repo for package.json files with this condition to see example usage.\n\t\t\t\"conditions=allow-ff-test-exports\",\n\t\t\t// Performance tests benefit from having access to GC, and memory tests require it.\n\t\t\t// Exposing it here avoids all packages which do perf testing from having to expose it.\n\t\t\t// Note that since \"node-option\" is explicitly set,\n\t\t\t// these must be provided here and not via mocha's --v8-expose-gc.\n\t\t\t\"expose-gc\",\n\t\t],\n\t\tspec: process.env.MOCHA_SPEC ?? defaultSpec,\n\t};\n\n\t// This approach to checking for this flag is consistent with some (but not all) others in this file,\n\t// but not with tools/benchmark/src/Configuration.ts.\n\t// For now, undefined, \"1\" and \"true\" should work consistently across all known use, and \"1\" is the main form we explicitly use.\n\t// All these should be made to match, see AB#69054.\n\tif (process.env.FLUID_TEST_PERF_MODE !== undefined) {\n\t\tif (process.env.SILENT_TEST_OUTPUT === undefined) {\n\t\t\tconsole.log(`Running performance tests...`);\n\t\t}\n\n\t\t// Some perf tests are often quite slow, and we don't want to lose the data by hitting a timeout.\n\t\t// This is 1000 seconds, so ~16 minutes.\n\t\t// This can be overridden by setting FLUID_TEST_TIMEOUT to a different value if desired.\n\t\tconfig.timeout = 1_000_000;\n\n\t\t// If there is no filter specified, limit to benchmarks.\n\t\t// If mocha allowed multiple filters to all be applied to further narrow results, we would do this unconditionally.\n\t\tif (!(process.argv.includes(\"--fgrep\") || process.argv.includes(\"--grep\"))) {\n\t\t\tconfig.fgrep = [\"@Benchmark\"];\n\t\t}\n\n\t\tconfig.reporter = \"@fluid-tools/benchmark/dist/mocha/Reporter.js\";\n\t\tif (!process.argv.includes(\"--reporterOptions\")) {\n\t\t\t// If report options were not specified, default to:\n\t\t\tconfig.reporterOptions = [\"reportFile=./benchmarkOutput.json\"];\n\t\t}\n\t} else {\n\t\tconst packageJsonPath = path.join(packageDir, \"package.json\");\n\t\tconst packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as {\n\t\t\tname: string;\n\t\t};\n\t\tconfig.reporter = `mocha-multi-reporters`;\n\t\t// See https://www.npmjs.com/package/mocha-multi-reporters#cmroutput-option\n\t\tconst outputFilePrefix = reportPrefix !== undefined ? `${reportPrefix}-` : \"\";\n\t\tif (process.env.SILENT_TEST_OUTPUT === undefined) {\n\t\t\tconsole.log(\n\t\t\t\t`Writing test results relative to package to nyc/${outputFilePrefix}junit-report.xml`,\n\t\t\t);\n\t\t}\n\t\tconst suiteName =\n\t\t\treportPrefix !== undefined ? `${packageJson.name} - ${reportPrefix}` : packageJson.name;\n\t\tconfig[\"reporter-options\"] = [\n\t\t\t`configFile=${path.join(\n\t\t\t\timport.meta.dirname,\n\t\t\t\t\"..\",\n\t\t\t\t\"test-config.json\",\n\t\t\t)},cmrOutput=xunit+output+${outputFilePrefix}:xunit+suiteName+${suiteName}`,\n\t\t];\n\t}\n\n\tif (process.env.FLUID_TEST_TIMEOUT !== undefined) {\n\t\tconfig.timeout = process.env.FLUID_TEST_TIMEOUT;\n\t}\n\n\tif (process.env.FLUID_TEST_FORBID_ONLY !== undefined) {\n\t\tconfig[\"forbid-only\"] = true;\n\t}\n\n\treturn config;\n}\n"]}
1
+ {"version":3,"file":"mocharcCommon.js","sourceRoot":"","sources":["../src/mocharcCommon.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Mocha configuration object returned by {@link getFluidTestMochaConfig}.\n */\nexport interface FluidTestMochaConfig {\n\trecursive: boolean;\n\trequire: string[];\n\t\"unhandled-rejections\": string;\n\t\"fail-zero\": boolean;\n\tignore: string[];\n\t\"node-option\": string[];\n\tspec: string;\n\texit?: boolean;\n\ttimeout?: number | string;\n\tfgrep?: string[];\n\treporter?: string;\n\treporterOptions?: string[];\n\t\"reporter-options\"?: string[];\n\t\"forbid-only\"?: boolean;\n}\n\n/**\n * Get the mocha configuration for running tests using the conventions followed in the Fluid Framework repository.\n *\n * @param packageDir - the directory of the package, typically set using `__dirname`\n * @param additionalRequiredModules - modules to require in addition to the standard set.\n * @param testReportPrefix - prefix for the test output report file names.\n * @remarks\n * Additional configuration can be provided via environment variables: see {@link file://./README.md}.\n *\n * Users desiring exact control over the `spec` from the CLI should delete or replace the spec from the returned config, since mocha's behavior is to extend it, not override it.\n */\nexport function getFluidTestMochaConfig(\n\tpackageDir: string,\n\tadditionalRequiredModules?: string[],\n\ttestReportPrefix?: string,\n): FluidTestMochaConfig {\n\tconst requiredModules = [\n\t\t// General mocha setup e.g. suppresses console.log,\n\t\t// This has to be before others (except logger) so that registerMochaTestWrapperFuncs is available\n\t\t\"@fluid-internal/mocha-test-setup\",\n\t\t\"source-map-support/register\",\n\t\t...(additionalRequiredModules ?? []),\n\t];\n\n\tif (process.env.FLUID_TEST_LOGGER_PKG_SPECIFIER !== undefined) {\n\t\t// Inject implementation of createTestLogger, put it first before mocha-test-setup\n\t\trequiredModules.unshift(process.env.FLUID_TEST_LOGGER_PKG_SPECIFIER);\n\t}\n\n\tlet reportPrefix = testReportPrefix;\n\tlet defaultSpec = \"lib/test\";\n\tif (process.env.FLUID_TEST_MODULE_SYSTEM === \"CJS\") {\n\t\tdefaultSpec = \"dist/test\";\n\t\tconst testVariant = process.env.FLUID_TEST_VARIANT;\n\t\tprocess.env.FLUID_TEST_VARIANT = testVariant !== undefined ? `CJS,${testVariant}` : \"CJS\";\n\t\treportPrefix = reportPrefix !== undefined ? `${reportPrefix}-CJS` : \"CJS\";\n\t}\n\n\tconst config: FluidTestMochaConfig = {\n\t\t\"recursive\": true,\n\t\t\"require\": requiredModules,\n\t\t\"unhandled-rejections\": \"strict\",\n\t\t// Fail the test run if no tests are found/run. This catches cases where test files fail to\n\t\t// load silently (e.g. due to broken imports), which would otherwise produce a green \"0 passing\"\n\t\t// result with exit code 0.\n\t\t\"fail-zero\": true,\n\t\tignore: [\n\t\t\t// Ignore \"tools\" which are scripts intended to be run, not part of the test suite.\n\t\t\t\"**/*.tool.{js,cjs,mjs}\",\n\t\t],\n\t\t\"node-option\": [\n\t\t\t// Allow test-only indexes to be imported. Search the FF repo for package.json files with this condition to see example usage.\n\t\t\t\"conditions=allow-ff-test-exports\",\n\t\t\t// Performance tests benefit from having access to GC, and memory tests require it.\n\t\t\t// Exposing it here avoids all packages which do perf testing from having to expose it.\n\t\t\t// Note that since \"node-option\" is explicitly set,\n\t\t\t// these must be provided here and not via mocha's --v8-expose-gc.\n\t\t\t\"expose-gc\",\n\t\t],\n\t\tspec: process.env.MOCHA_SPEC ?? defaultSpec,\n\t};\n\n\t// This approach to checking for this flag is consistent with some (but not all) others in this file,\n\t// but not with tools/benchmark/src/Configuration.ts.\n\t// For now, undefined, \"1\" and \"true\" should work consistently across all known use, and \"1\" is the main form we explicitly use.\n\t// All these should be made to match, see AB#69054.\n\tif (process.env.FLUID_TEST_PERF_MODE !== undefined) {\n\t\tif (process.env.SILENT_TEST_OUTPUT === undefined) {\n\t\t\tconsole.log(`Running performance tests...`);\n\t\t}\n\n\t\t// Some perf tests are often quite slow, and we don't want to lose the data by hitting a timeout.\n\t\t// This is 1000 seconds, so ~16 minutes.\n\t\t// This can be overridden by setting FLUID_TEST_TIMEOUT to a different value if desired.\n\t\tconfig.timeout = 1_000_000;\n\n\t\t// If there is no filter specified, limit to benchmarks.\n\t\t// If mocha allowed multiple filters to all be applied to further narrow results, we would do this unconditionally.\n\t\tif (!(process.argv.includes(\"--fgrep\") || process.argv.includes(\"--grep\"))) {\n\t\t\tconfig.fgrep = [\"@Benchmark\"];\n\t\t}\n\n\t\tconfig.reporter = \"@fluid-tools/benchmark/dist/mocha/Reporter.js\";\n\t\tif (!process.argv.includes(\"--reporterOptions\")) {\n\t\t\t// If report options were not specified, default to:\n\t\t\tconfig.reporterOptions = [\"reportFile=./benchmarkOutput.json\"];\n\t\t}\n\t} else {\n\t\tconst packageJsonPath = path.join(packageDir, \"package.json\");\n\t\tconst packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as {\n\t\t\tname: string;\n\t\t};\n\t\tconfig.reporter = `mocha-multi-reporters`;\n\t\t// See https://www.npmjs.com/package/mocha-multi-reporters#cmroutput-option\n\n\t\tconst outputFilePrefix = reportPrefix !== undefined ? `${reportPrefix}-` : \"\";\n\t\tif (process.env.SILENT_TEST_OUTPUT === undefined) {\n\t\t\tconsole.log(\n\t\t\t\t`Writing test results relative to package to nyc/${outputFilePrefix}junit-report.xml`,\n\t\t\t);\n\t\t}\n\t\tconst suiteName =\n\t\t\treportPrefix !== undefined ? `${packageJson.name} - ${reportPrefix}` : packageJson.name;\n\t\t// This must be the exact module specifier mocha-multi-reporters will `require(...)` to load the reporter\n\t\t// (see its source: https://github.com/stevemao/mocha-multi-reporters/blob/master/lib/MultiReporters.js),\n\t\t// i.e. it can't be shortened to some arbitrary friendly name. It also determines the reporter-specific\n\t\t// option key mocha-multi-reporters expects below (and in test-config.json): it camelCases this exact\n\t\t// string and appends \"ReporterOptions\" (e.g. \"@foo/bar/baz\" -> \"fooBarBazReporterOptions\").\n\t\tconst xunitReporterName = \"@fluid-internal/mocha-test-setup/xunit-reporter\";\n\t\tconfig[\"reporter-options\"] = [\n\t\t\t`configFile=${path.join(\n\t\t\t\timport.meta.dirname,\n\t\t\t\t\"..\",\n\t\t\t\t\"test-config.json\",\n\t\t\t)},cmrOutput=${xunitReporterName}+output+${outputFilePrefix}:${xunitReporterName}+suiteName+${suiteName}`,\n\t\t];\n\t}\n\n\tif (process.env.FLUID_TEST_TIMEOUT !== undefined) {\n\t\tconfig.timeout = process.env.FLUID_TEST_TIMEOUT;\n\t}\n\n\tif (process.env.FLUID_TEST_FORBID_ONLY !== undefined) {\n\t\tconfig[\"forbid-only\"] = true;\n\t}\n\n\treturn config;\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluid-internal/mocha-test-setup";
8
- export declare const pkgVersion = "3.0.2";
8
+ export declare const pkgVersion = "3.2.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluid-internal/mocha-test-setup";
8
- export const pkgVersion = "3.0.2";
8
+ export const pkgVersion = "3.2.0";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,kCAAkC,CAAC;AAC1D,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluid-internal/mocha-test-setup\";\nexport const pkgVersion = \"3.0.2\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,kCAAkC,CAAC;AAC1D,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluid-internal/mocha-test-setup\";\nexport const pkgVersion = \"3.2.0\";\n"]}
@@ -0,0 +1,67 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ /**
6
+ * Walks up from `startDir` looking for the repo root, identified by the presence of
7
+ * `pnpm-workspace.yaml`. Falls back to `startDir` if no such ancestor is found (e.g. if the workspace
8
+ * layout ever changes).
9
+ *
10
+ * @remarks Exported (in addition to being used internally) so tests can independently compute the
11
+ * expected `classname` for a given file without duplicating this logic.
12
+ */
13
+ export declare function findRepoRoot(startDir: string): string;
14
+ /**
15
+ * The subset of a Mocha `Test` that {@link FluidXunitReporter} cares about.
16
+ */
17
+ interface JUnitTestLike {
18
+ /**
19
+ * Absolute path of the file the test is defined in, as set by Mocha.
20
+ */
21
+ file?: string;
22
+ /**
23
+ * The `Suite` (or nested `Suite`s) the test is defined in. `xunit`'s `test` method relies on
24
+ * `parent.fullTitle()`, and mocha's own `isPending()` implementation recurses into `parent.isPending()`,
25
+ * so the whole object (not just `fullTitle`) needs to be preserved when overriding it below.
26
+ */
27
+ parent: object;
28
+ /**
29
+ * The test's fully qualified title: its own title, prefixed by its ancestor `describe` blocks' titles.
30
+ */
31
+ fullTitle(): string;
32
+ }
33
+ /**
34
+ * Minimal typing for mocha's built-in `xunit` reporter constructor and the `test` instance method
35
+ * {@link FluidXunitReporter} overrides.
36
+ */
37
+ interface XUnitReporterInstance {
38
+ test(test: JUnitTestLike, options?: unknown): void;
39
+ }
40
+ declare const XUnitReporterCtor: new (runner: unknown, options: unknown) => XUnitReporterInstance;
41
+ /**
42
+ * A JUnit reporter for Mocha, based on mocha's built-in `xunit` reporter, that fixes two issues with the
43
+ * default `xunit` output when many packages' reports are viewed together in Azure DevOps's Tests tab.
44
+ *
45
+ * `name` (the `<testcase>`'s display title in Azure DevOps): `xunit` sets this to the test's own,
46
+ * non-fully-qualified title (e.g. `"increments"`), which frequently collides with other tests of the same
47
+ * name defined under different `describe` blocks or files. This override sets it to the fully qualified
48
+ * title instead (the full `describe`/`it` chain), which is unique.
49
+ *
50
+ * `classname` (used by Azure DevOps's JUnit importer to populate the "Test file" grouping — see
51
+ * `AutomatedTestStorage` in
52
+ * {@link https://github.com/microsoft/azure-pipelines-agent/blob/master/src/Agent.Worker/TestResults/Legacy/JunitResultReader.cs | JunitResultReader.cs}):
53
+ * `xunit` sets this to the fully qualified title as well, so that grouping showed test titles instead of
54
+ * file names. This override sets it to the path (relative to the repo root) of the file containing the
55
+ * test.
56
+ *
57
+ * All other behavior — notably, `xunit`'s single flat `<testsuite>` per report file — is unchanged, so
58
+ * this reporter is not subject to the Azure DevOps "Test Run" naming regression that `mocha-junit-reporter`
59
+ * (which emits one `<testsuite>` per `describe` block, rather than one per file) causes: Azure DevOps's
60
+ * JUnit importer falls back to a generic `JUnit_<file name>` run title whenever a report file contains more
61
+ * than one `<testsuite>` element, unconditionally, regardless of file or suite naming.
62
+ */
63
+ export declare class FluidXunitReporter extends XUnitReporterCtor {
64
+ test(test: JUnitTestLike, options?: unknown): void;
65
+ }
66
+ export {};
67
+ //# sourceMappingURL=xunitReporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xunitReporter.d.ts","sourceRoot":"","sources":["../src/xunitReporter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAcrD;AAID;;GAEG;AACH,UAAU,aAAa;IACtB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,SAAS,IAAI,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,UAAU,qBAAqB;IAC9B,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CACnD;AAED,QAAA,MAAM,iBAAiB,EAAuC,KAC7D,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,OAAO,KACZ,qBAAqB,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,kBAAmB,SAAQ,iBAAiB;IACjD,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI;CAkBzD"}
@@ -0,0 +1,74 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { existsSync } from "node:fs";
6
+ import path from "node:path";
7
+ import Mocha from "mocha";
8
+ /**
9
+ * Walks up from `startDir` looking for the repo root, identified by the presence of
10
+ * `pnpm-workspace.yaml`. Falls back to `startDir` if no such ancestor is found (e.g. if the workspace
11
+ * layout ever changes).
12
+ *
13
+ * @remarks Exported (in addition to being used internally) so tests can independently compute the
14
+ * expected `classname` for a given file without duplicating this logic.
15
+ */
16
+ export function findRepoRoot(startDir) {
17
+ let dir = startDir;
18
+ // eslint-disable-next-line no-constant-condition -- terminates via the `parent === dir` root check below
19
+ while (true) {
20
+ if (existsSync(path.join(dir, "pnpm-workspace.yaml"))) {
21
+ return dir;
22
+ }
23
+ const parent = path.dirname(dir);
24
+ if (parent === dir) {
25
+ // Reached the filesystem root without finding a workspace marker.
26
+ return startDir;
27
+ }
28
+ dir = parent;
29
+ }
30
+ }
31
+ const repoRoot = findRepoRoot(process.cwd());
32
+ const XUnitReporterCtor = Mocha.reporters.XUnit;
33
+ /**
34
+ * A JUnit reporter for Mocha, based on mocha's built-in `xunit` reporter, that fixes two issues with the
35
+ * default `xunit` output when many packages' reports are viewed together in Azure DevOps's Tests tab.
36
+ *
37
+ * `name` (the `<testcase>`'s display title in Azure DevOps): `xunit` sets this to the test's own,
38
+ * non-fully-qualified title (e.g. `"increments"`), which frequently collides with other tests of the same
39
+ * name defined under different `describe` blocks or files. This override sets it to the fully qualified
40
+ * title instead (the full `describe`/`it` chain), which is unique.
41
+ *
42
+ * `classname` (used by Azure DevOps's JUnit importer to populate the "Test file" grouping — see
43
+ * `AutomatedTestStorage` in
44
+ * {@link https://github.com/microsoft/azure-pipelines-agent/blob/master/src/Agent.Worker/TestResults/Legacy/JunitResultReader.cs | JunitResultReader.cs}):
45
+ * `xunit` sets this to the fully qualified title as well, so that grouping showed test titles instead of
46
+ * file names. This override sets it to the path (relative to the repo root) of the file containing the
47
+ * test.
48
+ *
49
+ * All other behavior — notably, `xunit`'s single flat `<testsuite>` per report file — is unchanged, so
50
+ * this reporter is not subject to the Azure DevOps "Test Run" naming regression that `mocha-junit-reporter`
51
+ * (which emits one `<testsuite>` per `describe` block, rather than one per file) causes: Azure DevOps's
52
+ * JUnit importer falls back to a generic `JUnit_<file name>` run title whenever a report file contains more
53
+ * than one `<testsuite>` element, unconditionally, regardless of file or suite naming.
54
+ */
55
+ export class FluidXunitReporter extends XUnitReporterCtor {
56
+ test(test, options) {
57
+ const classname = test.file === undefined ? test.fullTitle() : path.relative(repoRoot, test.file);
58
+ // `xunit`'s `test` method reads `test.title` for `name` and `test.parent.fullTitle()` for
59
+ // `classname`. Wrap `test` and `test.parent` (via prototypal inheritance, so unrelated
60
+ // properties/methods they're relied on for — `file`, `duration`, `state`, `err`, `isPending()`
61
+ // (which recurses into `parent.isPending()`) — still resolve correctly) to present the values
62
+ // wanted here, without mutating the shared `test`/`test.parent` objects that other reporters
63
+ // running in the same `mocha-multi-reporters` process also read.
64
+ const wrappedParent = Object.create(test.parent, {
65
+ fullTitle: { value: () => classname, enumerable: true },
66
+ });
67
+ const wrapped = Object.create(test, {
68
+ title: { value: test.fullTitle(), enumerable: true },
69
+ parent: { value: wrappedParent, enumerable: true },
70
+ });
71
+ super.test(wrapped, options);
72
+ }
73
+ }
74
+ //# sourceMappingURL=xunitReporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xunitReporter.js","sourceRoot":"","sources":["../src/xunitReporter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC5C,IAAI,GAAG,GAAG,QAAQ,CAAC;IACnB,yGAAyG;IACzG,OAAO,IAAI,EAAE,CAAC;QACb,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC;YACvD,OAAO,GAAG,CAAC;QACZ,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACpB,kEAAkE;YAClE,OAAO,QAAQ,CAAC;QACjB,CAAC;QACD,GAAG,GAAG,MAAM,CAAC;IACd,CAAC;AACF,CAAC;AAED,MAAM,QAAQ,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAgC7C,MAAM,iBAAiB,GAAG,KAAK,CAAC,SAAS,CAAC,KAGhB,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,kBAAmB,SAAQ,iBAAiB;IACjD,IAAI,CAAC,IAAmB,EAAE,OAAiB;QACjD,MAAM,SAAS,GACd,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACjF,0FAA0F;QAC1F,uFAAuF;QACvF,+FAA+F;QAC/F,8FAA8F;QAC9F,6FAA6F;QAC7F,iEAAiE;QACjE,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE;YAChD,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE;SACvD,CAA4B,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;YACnC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;YACpD,MAAM,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,IAAI,EAAE;SAClD,CAAkB,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;CACD","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport Mocha from \"mocha\";\n\n/**\n * Walks up from `startDir` looking for the repo root, identified by the presence of\n * `pnpm-workspace.yaml`. Falls back to `startDir` if no such ancestor is found (e.g. if the workspace\n * layout ever changes).\n *\n * @remarks Exported (in addition to being used internally) so tests can independently compute the\n * expected `classname` for a given file without duplicating this logic.\n */\nexport function findRepoRoot(startDir: string): string {\n\tlet dir = startDir;\n\t// eslint-disable-next-line no-constant-condition -- terminates via the `parent === dir` root check below\n\twhile (true) {\n\t\tif (existsSync(path.join(dir, \"pnpm-workspace.yaml\"))) {\n\t\t\treturn dir;\n\t\t}\n\t\tconst parent = path.dirname(dir);\n\t\tif (parent === dir) {\n\t\t\t// Reached the filesystem root without finding a workspace marker.\n\t\t\treturn startDir;\n\t\t}\n\t\tdir = parent;\n\t}\n}\n\nconst repoRoot = findRepoRoot(process.cwd());\n\n/**\n * The subset of a Mocha `Test` that {@link FluidXunitReporter} cares about.\n */\ninterface JUnitTestLike {\n\t/**\n\t * Absolute path of the file the test is defined in, as set by Mocha.\n\t */\n\tfile?: string;\n\n\t/**\n\t * The `Suite` (or nested `Suite`s) the test is defined in. `xunit`'s `test` method relies on\n\t * `parent.fullTitle()`, and mocha's own `isPending()` implementation recurses into `parent.isPending()`,\n\t * so the whole object (not just `fullTitle`) needs to be preserved when overriding it below.\n\t */\n\tparent: object;\n\n\t/**\n\t * The test's fully qualified title: its own title, prefixed by its ancestor `describe` blocks' titles.\n\t */\n\tfullTitle(): string;\n}\n\n/**\n * Minimal typing for mocha's built-in `xunit` reporter constructor and the `test` instance method\n * {@link FluidXunitReporter} overrides.\n */\ninterface XUnitReporterInstance {\n\ttest(test: JUnitTestLike, options?: unknown): void;\n}\n\nconst XUnitReporterCtor = Mocha.reporters.XUnit as unknown as new (\n\trunner: unknown,\n\toptions: unknown,\n) => XUnitReporterInstance;\n\n/**\n * A JUnit reporter for Mocha, based on mocha's built-in `xunit` reporter, that fixes two issues with the\n * default `xunit` output when many packages' reports are viewed together in Azure DevOps's Tests tab.\n *\n * `name` (the `<testcase>`'s display title in Azure DevOps): `xunit` sets this to the test's own,\n * non-fully-qualified title (e.g. `\"increments\"`), which frequently collides with other tests of the same\n * name defined under different `describe` blocks or files. This override sets it to the fully qualified\n * title instead (the full `describe`/`it` chain), which is unique.\n *\n * `classname` (used by Azure DevOps's JUnit importer to populate the \"Test file\" grouping — see\n * `AutomatedTestStorage` in\n * {@link https://github.com/microsoft/azure-pipelines-agent/blob/master/src/Agent.Worker/TestResults/Legacy/JunitResultReader.cs | JunitResultReader.cs}):\n * `xunit` sets this to the fully qualified title as well, so that grouping showed test titles instead of\n * file names. This override sets it to the path (relative to the repo root) of the file containing the\n * test.\n *\n * All other behavior — notably, `xunit`'s single flat `<testsuite>` per report file — is unchanged, so\n * this reporter is not subject to the Azure DevOps \"Test Run\" naming regression that `mocha-junit-reporter`\n * (which emits one `<testsuite>` per `describe` block, rather than one per file) causes: Azure DevOps's\n * JUnit importer falls back to a generic `JUnit_<file name>` run title whenever a report file contains more\n * than one `<testsuite>` element, unconditionally, regardless of file or suite naming.\n */\nexport class FluidXunitReporter extends XUnitReporterCtor {\n\tpublic test(test: JUnitTestLike, options?: unknown): void {\n\t\tconst classname =\n\t\t\ttest.file === undefined ? test.fullTitle() : path.relative(repoRoot, test.file);\n\t\t// `xunit`'s `test` method reads `test.title` for `name` and `test.parent.fullTitle()` for\n\t\t// `classname`. Wrap `test` and `test.parent` (via prototypal inheritance, so unrelated\n\t\t// properties/methods they're relied on for — `file`, `duration`, `state`, `err`, `isPending()`\n\t\t// (which recurses into `parent.isPending()`) — still resolve correctly) to present the values\n\t\t// wanted here, without mutating the shared `test`/`test.parent` objects that other reporters\n\t\t// running in the same `mocha-multi-reporters` process also read.\n\t\tconst wrappedParent = Object.create(test.parent, {\n\t\t\tfullTitle: { value: () => classname, enumerable: true },\n\t\t}) as JUnitTestLike[\"parent\"];\n\t\tconst wrapped = Object.create(test, {\n\t\t\ttitle: { value: test.fullTitle(), enumerable: true },\n\t\t\tparent: { value: wrappedParent, enumerable: true },\n\t\t}) as JUnitTestLike;\n\t\tsuper.test(wrapped, options);\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-internal/mocha-test-setup",
3
- "version": "3.0.2",
3
+ "version": "3.2.0",
4
4
  "description": "Utilities for Fluid tests",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -19,11 +19,15 @@
19
19
  "./mocharc-common": {
20
20
  "require": "./mocharc-common-cjswrapper.cjs",
21
21
  "default": "./lib/mocharcCommon.js"
22
+ },
23
+ "./xunit-reporter": {
24
+ "require": "./xunit-reporter-cjswrapper.cjs",
25
+ "default": "./lib/xunitReporter.js"
22
26
  }
23
27
  },
24
28
  "dependencies": {
25
- "@fluid-internal/test-driver-definitions": "~3.0.2",
26
- "@fluidframework/core-interfaces": "~3.0.2",
29
+ "@fluid-internal/test-driver-definitions": "~3.2.0",
30
+ "@fluidframework/core-interfaces": "~3.2.0",
27
31
  "mocha": "^11.7.5",
28
32
  "source-map-support": "^0.5.21"
29
33
  },
@@ -37,12 +41,14 @@
37
41
  "@microsoft/api-extractor": "7.58.1",
38
42
  "@types/mocha": "^10.0.10",
39
43
  "@types/node": "~22.19.17",
44
+ "@types/xml2js": "^0.4.14",
40
45
  "concurrently": "^10.0.3",
41
46
  "copyfiles": "^2.4.1",
42
47
  "eslint": "~9.39.1",
43
48
  "jiti": "^2.6.1",
44
49
  "rimraf": "^6.1.3",
45
- "typescript": "~6.0.3"
50
+ "typescript": "~6.0.3",
51
+ "xml2js": "^0.6.2"
46
52
  },
47
53
  "fluidBuild": {
48
54
  "_tasks_comment": "build:cjs is configured to depend on build:esm task as all CommonJS consumers expect mocha support which is implemented in ESM. Building ESM when CommonJS is requested ensures the implementation expectation is satisfied.",
@@ -67,7 +73,7 @@
67
73
  "build:docs": "api-extractor run --local --config api-extractor/api-extractor-model.json",
68
74
  "build:esm": "tsc --project ./tsconfig.json",
69
75
  "build:genver": "gen-version",
70
- "check:are-the-types-wrong": "attw --pack . --profile esm-only --exclude-entrypoints ./mocharc-common",
76
+ "check:are-the-types-wrong": "attw --pack . --profile esm-only --exclude-entrypoints ./mocharc-common ./xunit-reporter",
71
77
  "check:biome": "biome check .",
72
78
  "check:exports": "concurrently \"npm:check:exports:*\"",
73
79
  "check:exports:bundle-release-tags": "api-extractor run --config api-extractor/api-extractor-lint-bundle.json",
@@ -76,12 +82,15 @@
76
82
  "check:types:inexactOptionalPropertyTypes": "tsc --project ./tsconfig.json --noEmit --exactOptionalPropertyTypes false --skipLibCheck --emitDeclarationOnly false --tsBuildInfoFile ./tsconfig.inexactOptionalPropertyTypes.tsbuildinfo",
77
83
  "ci:build:api-reports": "api-extractor run --config api-extractor/api-extractor-report.json",
78
84
  "ci:build:docs": "api-extractor run --config api-extractor/api-extractor-model.json",
79
- "clean": "rimraf --glob dist lib \"**/*.tsbuildinfo\" \"**/*.build.log\" _api-extractor-temp",
85
+ "clean": "rimraf --glob dist lib \"**/*.tsbuildinfo\" \"**/*.build.log\" _api-extractor-temp nyc",
80
86
  "eslint": "eslint --quiet --format stylish src",
81
87
  "eslint:fix": "eslint --quiet --format stylish src --fix --fix-type problem,suggestion,layout",
82
88
  "format": "npm run format:biome",
83
89
  "format:biome": "biome check . --write",
84
90
  "lint": "fluid-build . --task lint",
85
- "lint:fix": "fluid-build . --task eslint:fix --task format"
91
+ "lint:fix": "fluid-build . --task eslint:fix --task format",
92
+ "test": "npm run test:mocha",
93
+ "test:mocha": "npm run test:mocha:esm",
94
+ "test:mocha:esm": "mocha"
86
95
  }
87
96
  }
@@ -120,6 +120,7 @@ export function getFluidTestMochaConfig(
120
120
  };
121
121
  config.reporter = `mocha-multi-reporters`;
122
122
  // See https://www.npmjs.com/package/mocha-multi-reporters#cmroutput-option
123
+
123
124
  const outputFilePrefix = reportPrefix !== undefined ? `${reportPrefix}-` : "";
124
125
  if (process.env.SILENT_TEST_OUTPUT === undefined) {
125
126
  console.log(
@@ -128,12 +129,18 @@ export function getFluidTestMochaConfig(
128
129
  }
129
130
  const suiteName =
130
131
  reportPrefix !== undefined ? `${packageJson.name} - ${reportPrefix}` : packageJson.name;
132
+ // This must be the exact module specifier mocha-multi-reporters will `require(...)` to load the reporter
133
+ // (see its source: https://github.com/stevemao/mocha-multi-reporters/blob/master/lib/MultiReporters.js),
134
+ // i.e. it can't be shortened to some arbitrary friendly name. It also determines the reporter-specific
135
+ // option key mocha-multi-reporters expects below (and in test-config.json): it camelCases this exact
136
+ // string and appends "ReporterOptions" (e.g. "@foo/bar/baz" -> "fooBarBazReporterOptions").
137
+ const xunitReporterName = "@fluid-internal/mocha-test-setup/xunit-reporter";
131
138
  config["reporter-options"] = [
132
139
  `configFile=${path.join(
133
140
  import.meta.dirname,
134
141
  "..",
135
142
  "test-config.json",
136
- )},cmrOutput=xunit+output+${outputFilePrefix}:xunit+suiteName+${suiteName}`,
143
+ )},cmrOutput=${xunitReporterName}+output+${outputFilePrefix}:${xunitReporterName}+suiteName+${suiteName}`,
137
144
  ];
138
145
  }
139
146
 
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluid-internal/mocha-test-setup";
9
- export const pkgVersion = "3.0.2";
9
+ export const pkgVersion = "3.2.0";
@@ -0,0 +1,113 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { existsSync } from "node:fs";
7
+ import path from "node:path";
8
+
9
+ import Mocha from "mocha";
10
+
11
+ /**
12
+ * Walks up from `startDir` looking for the repo root, identified by the presence of
13
+ * `pnpm-workspace.yaml`. Falls back to `startDir` if no such ancestor is found (e.g. if the workspace
14
+ * layout ever changes).
15
+ *
16
+ * @remarks Exported (in addition to being used internally) so tests can independently compute the
17
+ * expected `classname` for a given file without duplicating this logic.
18
+ */
19
+ export function findRepoRoot(startDir: string): string {
20
+ let dir = startDir;
21
+ // eslint-disable-next-line no-constant-condition -- terminates via the `parent === dir` root check below
22
+ while (true) {
23
+ if (existsSync(path.join(dir, "pnpm-workspace.yaml"))) {
24
+ return dir;
25
+ }
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir) {
28
+ // Reached the filesystem root without finding a workspace marker.
29
+ return startDir;
30
+ }
31
+ dir = parent;
32
+ }
33
+ }
34
+
35
+ const repoRoot = findRepoRoot(process.cwd());
36
+
37
+ /**
38
+ * The subset of a Mocha `Test` that {@link FluidXunitReporter} cares about.
39
+ */
40
+ interface JUnitTestLike {
41
+ /**
42
+ * Absolute path of the file the test is defined in, as set by Mocha.
43
+ */
44
+ file?: string;
45
+
46
+ /**
47
+ * The `Suite` (or nested `Suite`s) the test is defined in. `xunit`'s `test` method relies on
48
+ * `parent.fullTitle()`, and mocha's own `isPending()` implementation recurses into `parent.isPending()`,
49
+ * so the whole object (not just `fullTitle`) needs to be preserved when overriding it below.
50
+ */
51
+ parent: object;
52
+
53
+ /**
54
+ * The test's fully qualified title: its own title, prefixed by its ancestor `describe` blocks' titles.
55
+ */
56
+ fullTitle(): string;
57
+ }
58
+
59
+ /**
60
+ * Minimal typing for mocha's built-in `xunit` reporter constructor and the `test` instance method
61
+ * {@link FluidXunitReporter} overrides.
62
+ */
63
+ interface XUnitReporterInstance {
64
+ test(test: JUnitTestLike, options?: unknown): void;
65
+ }
66
+
67
+ const XUnitReporterCtor = Mocha.reporters.XUnit as unknown as new (
68
+ runner: unknown,
69
+ options: unknown,
70
+ ) => XUnitReporterInstance;
71
+
72
+ /**
73
+ * A JUnit reporter for Mocha, based on mocha's built-in `xunit` reporter, that fixes two issues with the
74
+ * default `xunit` output when many packages' reports are viewed together in Azure DevOps's Tests tab.
75
+ *
76
+ * `name` (the `<testcase>`'s display title in Azure DevOps): `xunit` sets this to the test's own,
77
+ * non-fully-qualified title (e.g. `"increments"`), which frequently collides with other tests of the same
78
+ * name defined under different `describe` blocks or files. This override sets it to the fully qualified
79
+ * title instead (the full `describe`/`it` chain), which is unique.
80
+ *
81
+ * `classname` (used by Azure DevOps's JUnit importer to populate the "Test file" grouping — see
82
+ * `AutomatedTestStorage` in
83
+ * {@link https://github.com/microsoft/azure-pipelines-agent/blob/master/src/Agent.Worker/TestResults/Legacy/JunitResultReader.cs | JunitResultReader.cs}):
84
+ * `xunit` sets this to the fully qualified title as well, so that grouping showed test titles instead of
85
+ * file names. This override sets it to the path (relative to the repo root) of the file containing the
86
+ * test.
87
+ *
88
+ * All other behavior — notably, `xunit`'s single flat `<testsuite>` per report file — is unchanged, so
89
+ * this reporter is not subject to the Azure DevOps "Test Run" naming regression that `mocha-junit-reporter`
90
+ * (which emits one `<testsuite>` per `describe` block, rather than one per file) causes: Azure DevOps's
91
+ * JUnit importer falls back to a generic `JUnit_<file name>` run title whenever a report file contains more
92
+ * than one `<testsuite>` element, unconditionally, regardless of file or suite naming.
93
+ */
94
+ export class FluidXunitReporter extends XUnitReporterCtor {
95
+ public test(test: JUnitTestLike, options?: unknown): void {
96
+ const classname =
97
+ test.file === undefined ? test.fullTitle() : path.relative(repoRoot, test.file);
98
+ // `xunit`'s `test` method reads `test.title` for `name` and `test.parent.fullTitle()` for
99
+ // `classname`. Wrap `test` and `test.parent` (via prototypal inheritance, so unrelated
100
+ // properties/methods they're relied on for — `file`, `duration`, `state`, `err`, `isPending()`
101
+ // (which recurses into `parent.isPending()`) — still resolve correctly) to present the values
102
+ // wanted here, without mutating the shared `test`/`test.parent` objects that other reporters
103
+ // running in the same `mocha-multi-reporters` process also read.
104
+ const wrappedParent = Object.create(test.parent, {
105
+ fullTitle: { value: () => classname, enumerable: true },
106
+ }) as JUnitTestLike["parent"];
107
+ const wrapped = Object.create(test, {
108
+ title: { value: test.fullTitle(), enumerable: true },
109
+ parent: { value: wrappedParent, enumerable: true },
110
+ }) as JUnitTestLike;
111
+ super.test(wrapped, options);
112
+ }
113
+ }
package/test-config.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "reporterEnabled": "spec,xunit",
3
- "xunitReporterOptions": {
2
+ "reporterEnabled": "spec,@fluid-internal/mocha-test-setup/xunit-reporter",
3
+ "fluidInternalMochaTestSetupXunitReporterReporterOptions": {
4
4
  "output": "nyc/{id}junit-report.xml",
5
5
  "suiteName": "{id}"
6
6
  }
@@ -0,0 +1,15 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ // This file exists to preserve CommonJS default export compatibility for the xunit-reporter entrypoint,
7
+ // since `mocha-multi-reporters` loads reporters via a plain CommonJS `require()`.
8
+ // Once all consumers are using named imports, this file can be removed and all conditions can point
9
+ // directly to the ESM file. attw xunit-reporter exclusion can also be removed at that time.
10
+
11
+ "use strict";
12
+
13
+ const { FluidXunitReporter } = require("./lib/xunitReporter.js");
14
+
15
+ module.exports = FluidXunitReporter;